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