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