Line data Source code
1 : /*-
2 : * Copyright (c) 2011-2014 Baptiste Daroussin <bapt@FreeBSD.org>
3 : * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
4 : * Copyright (c) 2011 Philippe Pepiot <phil@philpep.org>
5 : * Copyright (c) 2011-2012 Marin Atanasov Nikolov <dnaeon@gmail.com>
6 : * Copyright (c) 2013-2014 Matthew Seaman <matthew@FreeBSD.org>
7 : * All rights reserved.
8 : *
9 : * Redistribution and use in source and binary forms, with or without
10 : * modification, are permitted provided that the following conditions
11 : * are met:
12 : * 1. Redistributions of source code must retain the above copyright
13 : * notice, this list of conditions and the following disclaimer
14 : * in this position and unchanged.
15 : * 2. Redistributions in binary form must reproduce the above copyright
16 : * notice, this list of conditions and the following disclaimer in the
17 : * documentation and/or other materials provided with the distribution.
18 : *
19 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
20 : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
23 : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 : */
30 :
31 : #ifdef HAVE_CONFIG_H
32 : #include "pkg_config.h"
33 : #endif
34 :
35 : #ifdef HAVE_CAPSICUM
36 : #include <sys/capability.h>
37 : #endif
38 :
39 : #include <err.h>
40 : #include <errno.h>
41 : #include <fcntl.h>
42 : #include <getopt.h>
43 : #include <pkg.h>
44 : #include <stdbool.h>
45 : #include <stdio.h>
46 : #include <string.h>
47 : #include <sysexits.h>
48 : #include <unistd.h>
49 :
50 : #include "pkgcli.h"
51 :
52 : enum sign {
53 : LT,
54 : LE,
55 : GT,
56 : GE,
57 : EQ
58 : };
59 :
60 : void
61 0 : usage_info(void)
62 : {
63 0 : fprintf(stderr, "Usage: pkg info <pkg-name>\n");
64 0 : fprintf(stderr, " pkg info -a\n");
65 0 : fprintf(stderr, " pkg info [-AbBDdefIklOqRrs] [-Cgix] <pkg-name>\n");
66 0 : fprintf(stderr, " pkg info [-AbBDdfIlqRrs] -F <pkg-file>\n\n");
67 0 : fprintf(stderr, "For more information see 'pkg help info'.\n");
68 0 : }
69 :
70 : /*
71 : * list of options
72 : * -S <type> : show scripts, type can be pre-install etc: TODO
73 : */
74 :
75 : int
76 7 : exec_info(int argc, char **argv)
77 : {
78 7 : struct pkgdb *db = NULL;
79 7 : struct pkgdb_it *it = NULL;
80 : int query_flags;
81 7 : struct pkg *pkg = NULL;
82 7 : uint64_t opt = INFO_TAG_NAMEVER;
83 7 : match_t match = MATCH_GLOB;
84 : char *pkgname;
85 7 : char *pkgversion = NULL, *pkgversion2 = NULL;
86 7 : const char *file = NULL;
87 : int ch, fd;
88 7 : int ret = EPKG_OK;
89 7 : int retcode = 0;
90 7 : bool gotone = false;
91 : int i, j;
92 7 : int sign = 0;
93 7 : int sign2 = 0;
94 7 : int open_flags = 0;
95 7 : bool pkg_exists = false;
96 7 : bool origin_search = false;
97 7 : bool e_flag = false;
98 7 : struct pkg_manifest_key *keys = NULL;
99 : #ifdef HAVE_CAPSICUM
100 : cap_rights_t rights;
101 : #endif
102 :
103 7 : struct option longopts[] = {
104 : { "all", no_argument, NULL, 'a' },
105 : { "annotations", no_argument, NULL, 'A' },
106 : { "provided-shlibs", no_argument, NULL, 'b' },
107 : { "required-shlibs", no_argument, NULL, 'B' },
108 : { "case-sensitive", no_argument, NULL, 'C' },
109 : { "dependencies", no_argument, NULL, 'd' },
110 : { "pkg-message", no_argument, NULL, 'D' },
111 : { "exists", no_argument, NULL, 'e' },
112 : { "show-name-only", no_argument, NULL, 'E' },
113 : { "full", no_argument, NULL, 'f' },
114 : { "file", required_argument, NULL, 'F' },
115 : { "glob", no_argument, NULL, 'g' },
116 : { "case-insensitive", no_argument, NULL, 'i' },
117 : { "comment", no_argument, NULL, 'I' },
118 : { "locked", no_argument, NULL, 'k' },
119 : { "list-files", no_argument, NULL, 'l' },
120 : { "origin", no_argument, NULL, 'o' },
121 : { "by-origin", no_argument, NULL, 'O' },
122 : { "prefix", no_argument, NULL, 'p' },
123 : { "quiet", no_argument, NULL, 'q' },
124 : { "required-by", no_argument, NULL, 'r' },
125 : { "raw", no_argument, NULL, 'R' },
126 : { "size", no_argument, NULL, 's' },
127 : { "regex", no_argument, NULL, 'x' },
128 : { "raw-format", required_argument, NULL, 1 },
129 : { NULL, 0, NULL, 0 },
130 : };
131 :
132 : /* TODO: exclusive opts ? */
133 22 : while ((ch = getopt_long(argc, argv, "+aAbBCdDeEfF:giIkloOpqrRsx", longopts, NULL)) != -1) {
134 8 : switch (ch) {
135 : case 'a':
136 0 : match = MATCH_ALL;
137 0 : break;
138 : case 'A':
139 5 : opt |= INFO_ANNOTATIONS;
140 5 : break;
141 : case 'b':
142 0 : opt |= INFO_SHLIBS_PROVIDED;
143 0 : break;
144 : case 'B':
145 0 : opt |= INFO_SHLIBS_REQUIRED;
146 0 : break;
147 : case 'C':
148 0 : pkgdb_set_case_sensitivity(true);
149 0 : break;
150 : case 'd':
151 0 : opt |= INFO_DEPS;
152 0 : break;
153 : case 'D':
154 0 : opt |= INFO_MESSAGE;
155 0 : break;
156 : case 'e':
157 0 : pkg_exists = true;;
158 0 : retcode = 1;
159 0 : break;
160 : case 'E': /* ports compatibility */
161 0 : e_flag = true;
162 0 : break;
163 : case 'f':
164 0 : opt |= INFO_FULL;
165 0 : break;
166 : case 'F':
167 0 : file = optarg;
168 0 : break;
169 : case 'g':
170 0 : match = MATCH_GLOB;
171 0 : break;
172 : case 'i':
173 0 : pkgdb_set_case_sensitivity(false);
174 0 : break;
175 : case 'I':
176 0 : opt |= INFO_COMMENT;
177 0 : break;
178 : case 'k':
179 0 : opt |= INFO_LOCKED;
180 0 : break;
181 : case 'l':
182 0 : opt |= INFO_FILES;
183 0 : break;
184 : case 'o':
185 1 : opt |= INFO_ORIGIN;
186 1 : break;
187 : case 'O':
188 0 : origin_search = true; /* only for ports compat */
189 0 : break;
190 : case 'p':
191 0 : opt |= INFO_PREFIX;
192 0 : break;
193 : case 'q':
194 2 : quiet = true;
195 2 : break;
196 : case 'r':
197 0 : opt |= INFO_RDEPS;
198 0 : break;
199 : case 'R':
200 0 : opt |= INFO_RAW;
201 0 : break;
202 : case 's':
203 0 : opt |= INFO_FLATSIZE;
204 0 : break;
205 : case 'x':
206 0 : match = MATCH_REGEX;
207 0 : break;
208 : case 1:
209 0 : if (strcasecmp(optarg, "json") == 0)
210 0 : opt |= INFO_RAW_JSON;
211 0 : else if (strcasecmp(optarg, "json-compact") == 0)
212 0 : opt |= INFO_RAW_JSON_COMPACT;
213 0 : else if (strcasecmp(optarg, "yaml") == 0)
214 0 : opt |= INFO_RAW_YAML;
215 0 : else if (strcasecmp(optarg, "ucl") == 0)
216 0 : opt |= INFO_RAW_UCL;
217 : else
218 0 : errx(EX_USAGE, "Invalid format '%s' for the "
219 : "raw output, expecting json, json-compact "
220 : "or yaml", optarg);
221 0 : break;
222 : default:
223 0 : usage_info();
224 0 : return(EX_USAGE);
225 : }
226 : }
227 :
228 7 : if (argc == 1 || (argc == 2 && quiet))
229 2 : match = MATCH_ALL;
230 :
231 7 : argc -= optind;
232 7 : argv += optind;
233 :
234 7 : if (argc == 0 && file == NULL && match != MATCH_ALL) {
235 : /* which -O bsd.*.mk always execpt clean output */
236 0 : if (origin_search)
237 0 : return (EX_OK);
238 0 : usage_info();
239 0 : return (EX_USAGE);
240 : }
241 :
242 : /* When no other data is requested, default is to print
243 : * 'name-ver comment' For -O, just print name-ver */
244 8 : if (!origin_search && (opt & INFO_ALL) == 0 && match == MATCH_ALL &&
245 1 : !quiet)
246 0 : opt |= INFO_COMMENT;
247 :
248 : /* Special compatibility: handle -O and -q -O */
249 7 : if (origin_search) {
250 0 : if (quiet) {
251 0 : opt = INFO_TAG_NAMEVER;
252 0 : quiet = false;
253 : } else {
254 0 : opt = INFO_TAG_NAMEVER|INFO_COMMENT;
255 : }
256 : }
257 :
258 7 : if (match == MATCH_ALL && opt == INFO_TAG_NAMEVER)
259 1 : quiet = false;
260 :
261 7 : if (file != NULL) {
262 0 : if ((fd = open(file, O_RDONLY)) == -1) {
263 0 : warn("Unable to open %s", file);
264 0 : return (EX_IOERR);
265 : }
266 :
267 : #ifdef HAVE_CAPSICUM
268 0 : cap_rights_init(&rights, CAP_READ, CAP_FSTAT);
269 0 : if (cap_rights_limit(fd, &rights) < 0 && errno != ENOSYS ) {
270 0 : warn("cap_rights_limit() failed");
271 0 : return (EX_SOFTWARE);
272 : }
273 :
274 0 : if (cap_enter() < 0 && errno != ENOSYS) {
275 0 : warn("cap_enter() failed");
276 0 : close(fd);
277 0 : return (EX_SOFTWARE);
278 : }
279 : #endif
280 0 : if (opt == INFO_TAG_NAMEVER)
281 0 : opt |= INFO_FULL;
282 0 : pkg_manifest_keys_new(&keys);
283 0 : if (opt & INFO_RAW) {
284 0 : if ((opt & (INFO_RAW_JSON|INFO_RAW_JSON_COMPACT)) == 0)
285 0 : opt |= INFO_RAW_YAML;
286 : }
287 :
288 0 : if ((opt & (INFO_RAW | INFO_FILES |
289 : INFO_DIRS)) == 0)
290 0 : open_flags = PKG_OPEN_MANIFEST_COMPACT;
291 :
292 0 : if (pkg_open_fd(&pkg, fd, keys, open_flags) != EPKG_OK) {
293 0 : close(fd);
294 0 : return (1);
295 : }
296 0 : pkg_manifest_keys_free(keys);
297 0 : print_info(pkg, opt);
298 0 : close(fd);
299 0 : pkg_free(pkg);
300 0 : return (EX_OK);
301 : }
302 :
303 7 : ret = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_LOCAL);
304 7 : if (ret == EPKG_ENOACCESS) {
305 0 : warnx("Insufficient privileges to query the package database");
306 0 : return (EX_NOPERM);
307 7 : } else if (ret == EPKG_ENODB) {
308 0 : if (match == MATCH_ALL)
309 0 : return (EX_OK);
310 0 : if (origin_search)
311 0 : return (EX_OK);
312 0 : if (!quiet)
313 0 : warnx("No packages installed");
314 0 : return (EX_UNAVAILABLE);
315 7 : } else if (ret != EPKG_OK)
316 0 : return (EX_IOERR);
317 :
318 7 : ret = pkgdb_open(&db, PKGDB_DEFAULT);
319 7 : if (ret != EPKG_OK)
320 0 : return (EX_IOERR);
321 :
322 7 : if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
323 0 : pkgdb_close(db);
324 0 : warnx("Cannot get a read lock on a database, it is locked by another process");
325 0 : return (EX_TEMPFAIL);
326 : }
327 :
328 7 : i = 0;
329 : do {
330 7 : gotone = false;
331 7 : pkgname = argv[i];
332 :
333 : /*
334 : * allow to search for origin with a trailing /
335 : * likes audio/linux-vsound depending on ${PORTSDIR}/audio/sox/
336 : */
337 7 : if (argc > 0 && pkgname[strlen(pkgname) -1] == '/')
338 0 : pkgname[strlen(pkgname) -1] = '\0';
339 :
340 7 : if (argc > 0) {
341 5 : j=0;
342 25 : while (pkgname[j] != '\0') {
343 15 : if (pkgname[j] == '<') {
344 0 : if (pkgversion) {
345 0 : pkgversion2 = pkgname + j;
346 0 : sign2 = LT;
347 0 : pkgversion2[0] = '\0';
348 0 : pkgversion2++;
349 0 : if (pkgversion2[0] == '=') {
350 0 : pkgversion2++;
351 0 : sign=LE;
352 0 : j++;
353 : }
354 : } else {
355 0 : pkgversion = pkgname + j;
356 0 : sign = LT;
357 0 : pkgversion[0] = '\0';
358 0 : pkgversion++;
359 0 : if (pkgversion[0] == '=') {
360 0 : pkgversion++;
361 0 : sign=LE;
362 0 : j++;
363 : }
364 : }
365 15 : } else if (pkgname[j] == '>') {
366 0 : if (pkgversion) {
367 0 : pkgversion2 = pkgname + j;
368 0 : sign2 = GT;
369 0 : pkgversion2[0] = '\0';
370 0 : pkgversion2++;
371 0 : if (pkgversion2[0] == '=') {
372 0 : pkgversion2++;
373 0 : sign=GE;
374 0 : j++;
375 : }
376 : } else {
377 0 : pkgversion = pkgname + j;
378 0 : sign = GT;
379 0 : pkgversion[0] = '\0';
380 0 : pkgversion++;
381 0 : if (pkgversion[0] == '=') {
382 0 : pkgversion++;
383 0 : sign=GE;
384 0 : j++;
385 : }
386 : }
387 15 : } else if (pkgname[j] == '=') {
388 0 : if (pkgversion) {
389 0 : pkgversion2 = pkgname + j;
390 0 : sign2 = EQ;
391 0 : pkgversion2[0] = '\0';
392 0 : pkgversion2++;
393 0 : if (pkgversion2[0] == '=') {
394 0 : pkgversion2++;
395 0 : sign=EQ;
396 0 : j++;
397 : }
398 : } else {
399 0 : pkgversion = pkgname + j;
400 0 : sign = EQ;
401 0 : pkgversion[0] = '\0';
402 0 : pkgversion++;
403 0 : if (pkgversion[0] == '=') {
404 0 : pkgversion++;
405 0 : sign=EQ;
406 0 : j++;
407 : }
408 : }
409 : }
410 15 : j++;
411 : }
412 : }
413 :
414 7 : if (match != MATCH_ALL && pkgname[0] == '\0') {
415 0 : fprintf(stderr, "Pattern must not be empty.\n");
416 0 : i++;
417 0 : continue;
418 : }
419 :
420 7 : if ((it = pkgdb_query(db, pkgname, match)) == NULL) {
421 0 : goto cleanup;
422 : }
423 :
424 : /* this is place for compatibility hacks */
425 :
426 : /* ports infrastructure expects pkg info -q -O to
427 : * always return 0 even if the ports doesn't exists */
428 :
429 7 : if (origin_search)
430 0 : gotone = true;
431 :
432 : /* end of compatibility hacks */
433 :
434 : /*
435 : * only show full version in case of match glob with a
436 : * single argument specified which does not contains
437 : * any glob pattern
438 : */
439 7 : if (argc == 1 && !origin_search && !quiet && !e_flag &&
440 5 : match == MATCH_GLOB &&
441 10 : strcspn(pkgname, "*[]{}()") == strlen(pkgname) &&
442 : opt == INFO_TAG_NAMEVER)
443 0 : opt |= INFO_FULL;
444 :
445 7 : query_flags = info_flags(opt, false);
446 21 : while ((ret = pkgdb_it_next(it, &pkg, query_flags)) == EPKG_OK) {
447 7 : gotone = true;
448 : const char *version;
449 :
450 7 : pkg_get(pkg, PKG_VERSION, &version);
451 7 : if (pkgversion != NULL) {
452 0 : switch (pkg_version_cmp(version, pkgversion)) {
453 : case -1:
454 0 : if (sign != LT && sign != LE) {
455 0 : gotone = false;
456 0 : continue;
457 : }
458 0 : break;
459 : case 0:
460 0 : if (sign != LE &&
461 0 : sign != GE &&
462 : sign != EQ) {
463 0 : gotone = false;
464 0 : continue;
465 : }
466 0 : break;
467 : case 1:
468 0 : if (sign != GT && sign != GE) {
469 0 : gotone = false;
470 0 : continue;
471 : }
472 0 : break;
473 : }
474 : }
475 7 : if (pkgversion2 != NULL) {
476 0 : switch (pkg_version_cmp(version, pkgversion2)) {
477 : case -1:
478 0 : if (sign2 != LT && sign2 != LE) {
479 0 : gotone = false;
480 0 : continue;
481 : }
482 0 : break;
483 : case 0:
484 0 : if (sign2 != LE &&
485 0 : sign2 != GE &&
486 : sign2 != EQ) {
487 0 : gotone = false;
488 0 : continue;
489 : }
490 0 : break;
491 : case 1:
492 0 : if (sign2 != GT && sign2 != GE) {
493 0 : gotone = false;
494 0 : continue;
495 : }
496 0 : break;
497 : }
498 : }
499 7 : if (pkg_exists)
500 0 : retcode = EX_OK;
501 : else
502 7 : print_info(pkg, opt);
503 : }
504 7 : if (ret != EPKG_END) {
505 0 : retcode = EX_IOERR;
506 : }
507 :
508 7 : if (retcode == EX_OK && !gotone && match != MATCH_ALL) {
509 0 : if (!quiet)
510 0 : warnx("No package(s) matching %s", argv[i]);
511 0 : retcode = EX_SOFTWARE;
512 : }
513 :
514 7 : pkgdb_it_free(it);
515 :
516 7 : i++;
517 7 : } while (i < argc);
518 :
519 : cleanup:
520 7 : pkg_free(pkg);
521 :
522 7 : pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
523 7 : pkgdb_close(db);
524 :
525 7 : return (retcode);
526 : }
|