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