Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/src/info.c
2065 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
#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
usage_info(void)
61
{
62
fprintf(stderr, "Usage: pkg info <pkg-name>\n");
63
fprintf(stderr, " pkg info -a\n");
64
fprintf(stderr, " pkg info [-AbBDdefIklOqRrs] [-Cgix] <pkg-name>\n");
65
fprintf(stderr, " pkg info [-AbBDdfIlqRrs] -F <pkg-file>\n\n");
66
fprintf(stderr, "For more information see 'pkg help info'.\n");
67
}
68
69
/*
70
* list of options
71
* -S <type> : show scripts, type can be pre-install etc: TODO
72
*/
73
74
int
75
exec_info(int argc, char **argv)
76
{
77
struct pkgdb *db = NULL;
78
struct pkgdb_it *it = NULL;
79
int query_flags;
80
struct pkg *pkg = NULL;
81
uint64_t opt = INFO_TAG_NAMEVER;
82
match_t match = MATCH_GLOB;
83
char *pkgname;
84
char *pkgversion = NULL, *pkgversion2 = NULL;
85
const char *file = NULL;
86
int ch, fd;
87
int ret = EPKG_OK;
88
int retcode = 0;
89
bool gotone = false;
90
int i, j;
91
int sign = 0;
92
int sign2 = 0;
93
int open_flags = 0;
94
bool pkg_exists = false;
95
bool origin_search = false;
96
bool e_flag = false;
97
#ifdef HAVE_CAPSICUM
98
cap_rights_t rights;
99
#endif
100
101
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
while ((ch = getopt_long(argc, argv, "+aAbBCdDeEfF:giIkloOpqrRsx", longopts, NULL)) != -1) {
132
switch (ch) {
133
case 'a':
134
match = MATCH_ALL;
135
break;
136
case 'A':
137
opt |= INFO_ANNOTATIONS;
138
break;
139
case 'b':
140
opt |= INFO_SHLIBS_PROVIDED;
141
break;
142
case 'B':
143
opt |= INFO_SHLIBS_REQUIRED;
144
break;
145
case 'C':
146
pkgdb_set_case_sensitivity(true);
147
break;
148
case 'd':
149
opt |= INFO_DEPS;
150
break;
151
case 'D':
152
opt |= INFO_MESSAGE;
153
break;
154
case 'e':
155
pkg_exists = true;;
156
retcode = 1;
157
break;
158
case 'E': /* ports compatibility */
159
e_flag = true;
160
break;
161
case 'f':
162
opt |= INFO_FULL;
163
break;
164
case 'F':
165
file = optarg;
166
break;
167
case 'g':
168
match = MATCH_GLOB;
169
break;
170
case 'i':
171
pkgdb_set_case_sensitivity(false);
172
break;
173
case 'I':
174
opt |= INFO_COMMENT;
175
break;
176
case 'k':
177
opt |= INFO_LOCKED;
178
break;
179
case 'l':
180
opt |= INFO_FILES;
181
break;
182
case 'o':
183
opt |= INFO_ORIGIN;
184
break;
185
case 'O':
186
origin_search = true; /* only for ports compat */
187
break;
188
case 'p':
189
opt |= INFO_PREFIX;
190
break;
191
case 'q':
192
quiet = true;
193
break;
194
case 'r':
195
opt |= INFO_RDEPS;
196
break;
197
case 'R':
198
opt |= INFO_RAW;
199
break;
200
case 's':
201
opt |= INFO_FLATSIZE;
202
break;
203
case 'x':
204
match = MATCH_REGEX;
205
break;
206
case 1:
207
if (STRIEQ(optarg, "json"))
208
opt |= INFO_RAW_JSON;
209
else if (STRIEQ(optarg, "json-compact"))
210
opt |= INFO_RAW_JSON_COMPACT;
211
else if (STRIEQ(optarg, "yaml"))
212
opt |= INFO_RAW_YAML;
213
else if (STRIEQ(optarg, "ucl"))
214
opt |= INFO_RAW_UCL;
215
else
216
errx(EXIT_FAILURE, "Invalid format '%s' for the "
217
"raw output, expecting json, json-compact "
218
"or yaml", optarg);
219
break;
220
default:
221
usage_info();
222
return(EXIT_FAILURE);
223
}
224
}
225
226
if (argc == 1 || (argc == 2 && quiet))
227
match = MATCH_ALL;
228
229
argc -= optind;
230
argv += optind;
231
232
if (argc == 0 && file == NULL && match != MATCH_ALL) {
233
/* which -O bsd.*.mk always execpt clean output */
234
if (origin_search)
235
return (EXIT_SUCCESS);
236
usage_info();
237
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
if (!origin_search && (opt & INFO_ALL) == 0 && match == MATCH_ALL &&
243
!quiet)
244
opt |= INFO_COMMENT;
245
246
/* Special compatibility: handle -O and -q -O */
247
if (origin_search) {
248
if (quiet) {
249
opt = INFO_TAG_NAMEVER;
250
quiet = false;
251
} else {
252
opt = INFO_TAG_NAMEVER|INFO_COMMENT;
253
}
254
}
255
256
if (match == MATCH_ALL && opt == INFO_TAG_NAMEVER)
257
quiet = false;
258
259
if (opt & INFO_RAW) {
260
if ((opt & (INFO_RAW_JSON|INFO_RAW_JSON_COMPACT|INFO_RAW_UCL)) == 0)
261
opt |= INFO_RAW_YAML;
262
}
263
264
if (file != NULL) {
265
if ((fd = open(file, O_RDONLY)) == -1) {
266
warn("Unable to open %s", file);
267
return (EXIT_FAILURE);
268
}
269
270
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
if (opt == INFO_TAG_NAMEVER)
288
opt |= INFO_FULL;
289
290
if ((opt & (INFO_RAW | INFO_FILES |
291
INFO_DIRS)) == 0)
292
open_flags = PKG_OPEN_MANIFEST_COMPACT;
293
294
if (pkg_open_fd(&pkg, fd, open_flags) != EPKG_OK) {
295
close(fd);
296
return (1);
297
}
298
print_info(NULL, pkg, opt);
299
close(fd);
300
pkg_free(pkg);
301
return (EXIT_SUCCESS);
302
}
303
304
ret = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_LOCAL);
305
if (ret == EPKG_ENOACCESS) {
306
warnx("Insufficient privileges to query the package database");
307
return (EXIT_FAILURE);
308
} else if (ret == EPKG_ENODB) {
309
if (match == MATCH_ALL)
310
return (EXIT_SUCCESS);
311
if (origin_search)
312
return (EXIT_SUCCESS);
313
if (!quiet)
314
warnx("No packages installed");
315
return (EXIT_FAILURE);
316
} else if (ret != EPKG_OK)
317
return (EXIT_FAILURE);
318
ret = pkgdb_open(&db, PKGDB_DEFAULT);
319
if (ret != EPKG_OK)
320
return (EXIT_FAILURE);
321
322
pkg_drop_privileges();
323
if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
324
pkgdb_close(db);
325
warnx("Cannot get a read lock on a database, it is locked by another process");
326
return (EXIT_FAILURE);
327
}
328
329
i = 0;
330
do {
331
gotone = false;
332
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
if (argc > 0 && pkgname[strlen(pkgname) -1] == '/')
339
pkgname[strlen(pkgname) -1] = '\0';
340
341
if (argc > 0) {
342
j=0;
343
while (pkgname[j] != '\0') {
344
if (pkgname[j] == '<') {
345
if (pkgversion) {
346
pkgversion2 = pkgname + j;
347
sign2 = LT;
348
pkgversion2[0] = '\0';
349
pkgversion2++;
350
if (pkgversion2[0] == '=') {
351
pkgversion2++;
352
sign2=LE;
353
j++;
354
}
355
} else {
356
pkgversion = pkgname + j;
357
sign = LT;
358
pkgversion[0] = '\0';
359
pkgversion++;
360
if (pkgversion[0] == '=') {
361
pkgversion++;
362
sign=LE;
363
j++;
364
}
365
}
366
} else if (pkgname[j] == '>') {
367
if (pkgversion) {
368
pkgversion2 = pkgname + j;
369
sign2 = GT;
370
pkgversion2[0] = '\0';
371
pkgversion2++;
372
if (pkgversion2[0] == '=') {
373
pkgversion2++;
374
sign2=GE;
375
j++;
376
}
377
} else {
378
pkgversion = pkgname + j;
379
sign = GT;
380
pkgversion[0] = '\0';
381
pkgversion++;
382
if (pkgversion[0] == '=') {
383
pkgversion++;
384
sign=GE;
385
j++;
386
}
387
}
388
} else if (pkgname[j] == '=') {
389
if (pkgversion) {
390
pkgversion2 = pkgname + j;
391
sign2 = EQ;
392
pkgversion2[0] = '\0';
393
pkgversion2++;
394
if (pkgversion2[0] == '=') {
395
pkgversion2++;
396
sign2=EQ;
397
j++;
398
}
399
} else {
400
pkgversion = pkgname + j;
401
sign = EQ;
402
pkgversion[0] = '\0';
403
pkgversion++;
404
if (pkgversion[0] == '=') {
405
pkgversion++;
406
sign=EQ;
407
j++;
408
}
409
}
410
}
411
j++;
412
}
413
}
414
415
if (match != MATCH_ALL && pkgname[0] == '\0') {
416
fprintf(stderr, "Pattern must not be empty.\n");
417
i++;
418
continue;
419
}
420
421
if ((it = pkgdb_query(db, pkgname, match)) == NULL) {
422
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
if (origin_search)
431
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
if (argc == 1 && !origin_search && !quiet && !e_flag &&
441
match == MATCH_GLOB &&
442
strcspn(pkgname, "*[]{}()") == strlen(pkgname) &&
443
opt == INFO_TAG_NAMEVER && !quiet)
444
opt |= INFO_FULL;
445
446
query_flags = info_flags(opt, false);
447
while ((ret = pkgdb_it_next(it, &pkg, query_flags)) == EPKG_OK) {
448
gotone = true;
449
const char *version;
450
451
pkg_get(pkg, PKG_ATTR_VERSION, &version);
452
if (pkgversion != NULL) {
453
switch (pkg_version_cmp(version, pkgversion)) {
454
case -1:
455
if (sign != LT && sign != LE) {
456
gotone = false;
457
continue;
458
}
459
break;
460
case 0:
461
if (sign != LE &&
462
sign != GE &&
463
sign != EQ) {
464
gotone = false;
465
continue;
466
}
467
break;
468
case 1:
469
if (sign != GT && sign != GE) {
470
gotone = false;
471
continue;
472
}
473
break;
474
}
475
}
476
if (pkgversion2 != NULL) {
477
switch (pkg_version_cmp(version, pkgversion2)) {
478
case -1:
479
if (sign2 != LT && sign2 != LE) {
480
gotone = false;
481
continue;
482
}
483
break;
484
case 0:
485
if (sign2 != LE &&
486
sign2 != GE &&
487
sign2 != EQ) {
488
gotone = false;
489
continue;
490
}
491
break;
492
case 1:
493
if (sign2 != GT && sign2 != GE) {
494
gotone = false;
495
continue;
496
}
497
break;
498
}
499
}
500
if (pkg_exists)
501
retcode = EXIT_SUCCESS;
502
else
503
print_info(db, pkg, opt);
504
}
505
if (ret != EPKG_END) {
506
retcode = EXIT_FAILURE;
507
}
508
509
if (retcode == EXIT_SUCCESS && !gotone && match != MATCH_ALL) {
510
if (!quiet)
511
warnx("No package(s) matching %s", argv[i]);
512
retcode = EXIT_FAILURE;
513
}
514
515
pkgdb_it_free(it);
516
517
i++;
518
} while (i < argc);
519
520
cleanup:
521
pkg_free(pkg);
522
523
pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
524
pkgdb_close(db);
525
526
return (retcode);
527
}
528
529