#ifdef HAVE_CONFIG_H
#include "pkg_config.h"
#endif
#ifdef HAVE_CAPSICUM
#include <sys/capsicum.h>
#endif
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <pkg.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "pkgcli.h"
enum sign {
LT,
LE,
GT,
GE,
EQ
};
void
usage_info(void)
{
fprintf(stderr, "Usage: pkg info <pkg-name>\n");
fprintf(stderr, " pkg info -a\n");
fprintf(stderr, " pkg info [-AbBDdefIklOqRrs] [-Cgix] <pkg-name>\n");
fprintf(stderr, " pkg info [-AbBDdfIlqRrs] -F <pkg-file>\n\n");
fprintf(stderr, "For more information see 'pkg help info'.\n");
}
int
exec_info(int argc, char **argv)
{
struct pkgdb *db = NULL;
struct pkgdb_it *it = NULL;
int query_flags;
struct pkg *pkg = NULL;
uint64_t opt = INFO_TAG_NAMEVER;
match_t match = MATCH_GLOB;
char *pkgname;
char *pkgversion = NULL, *pkgversion2 = NULL;
const char *file = NULL;
int ch, fd;
int ret = EPKG_OK;
int retcode = 0;
bool gotone = false;
int i, j;
int sign = 0;
int sign2 = 0;
int open_flags = 0;
bool pkg_exists = false;
bool origin_search = false;
bool e_flag = false;
#ifdef HAVE_CAPSICUM
cap_rights_t rights;
#endif
struct option longopts[] = {
{ "all", no_argument, NULL, 'a' },
{ "annotations", no_argument, NULL, 'A' },
{ "provided-shlibs", no_argument, NULL, 'b' },
{ "required-shlibs", no_argument, NULL, 'B' },
{ "case-sensitive", no_argument, NULL, 'C' },
{ "dependencies", no_argument, NULL, 'd' },
{ "pkg-message", no_argument, NULL, 'D' },
{ "exists", no_argument, NULL, 'e' },
{ "show-name-only", no_argument, NULL, 'E' },
{ "full", no_argument, NULL, 'f' },
{ "file", required_argument, NULL, 'F' },
{ "glob", no_argument, NULL, 'g' },
{ "case-insensitive", no_argument, NULL, 'i' },
{ "comment", no_argument, NULL, 'I' },
{ "locked", no_argument, NULL, 'k' },
{ "list-files", no_argument, NULL, 'l' },
{ "origin", no_argument, NULL, 'o' },
{ "by-origin", no_argument, NULL, 'O' },
{ "prefix", no_argument, NULL, 'p' },
{ "quiet", no_argument, NULL, 'q' },
{ "required-by", no_argument, NULL, 'r' },
{ "raw", no_argument, NULL, 'R' },
{ "size", no_argument, NULL, 's' },
{ "regex", no_argument, NULL, 'x' },
{ "raw-format", required_argument, NULL, 1 },
{ NULL, 0, NULL, 0 },
};
while ((ch = getopt_long(argc, argv, "+aAbBCdDeEfF:giIkloOpqrRsx", longopts, NULL)) != -1) {
switch (ch) {
case 'a':
match = MATCH_ALL;
break;
case 'A':
opt |= INFO_ANNOTATIONS;
break;
case 'b':
opt |= INFO_SHLIBS_PROVIDED;
break;
case 'B':
opt |= INFO_SHLIBS_REQUIRED;
break;
case 'C':
pkgdb_set_case_sensitivity(true);
break;
case 'd':
opt |= INFO_DEPS;
break;
case 'D':
opt |= INFO_MESSAGE;
break;
case 'e':
pkg_exists = true;;
retcode = 1;
break;
case 'E':
e_flag = true;
break;
case 'f':
opt |= INFO_FULL;
break;
case 'F':
file = optarg;
break;
case 'g':
match = MATCH_GLOB;
break;
case 'i':
pkgdb_set_case_sensitivity(false);
break;
case 'I':
opt |= INFO_COMMENT;
break;
case 'k':
opt |= INFO_LOCKED;
break;
case 'l':
opt |= INFO_FILES;
break;
case 'o':
opt |= INFO_ORIGIN;
break;
case 'O':
origin_search = true;
break;
case 'p':
opt |= INFO_PREFIX;
break;
case 'q':
quiet = true;
break;
case 'r':
opt |= INFO_RDEPS;
break;
case 'R':
opt |= INFO_RAW;
break;
case 's':
opt |= INFO_FLATSIZE;
break;
case 'x':
match = MATCH_REGEX;
break;
case 1:
if (STRIEQ(optarg, "json"))
opt |= INFO_RAW_JSON;
else if (STRIEQ(optarg, "json-compact"))
opt |= INFO_RAW_JSON_COMPACT;
else if (STRIEQ(optarg, "yaml"))
opt |= INFO_RAW_YAML;
else if (STRIEQ(optarg, "ucl"))
opt |= INFO_RAW_UCL;
else
errx(EXIT_FAILURE, "Invalid format '%s' for the "
"raw output, expecting json, json-compact "
"or yaml", optarg);
break;
default:
usage_info();
return(EXIT_FAILURE);
}
}
if (argc == 1 || (argc == 2 && quiet))
match = MATCH_ALL;
argc -= optind;
argv += optind;
if (argc == 0 && file == NULL && match != MATCH_ALL) {
if (origin_search)
return (EXIT_SUCCESS);
usage_info();
return (EXIT_FAILURE);
}
if (!origin_search && (opt & INFO_ALL) == 0 && match == MATCH_ALL &&
!quiet)
opt |= INFO_COMMENT;
if (origin_search) {
if (quiet) {
opt = INFO_TAG_NAMEVER;
quiet = false;
} else {
opt = INFO_TAG_NAMEVER|INFO_COMMENT;
}
}
if (match == MATCH_ALL && opt == INFO_TAG_NAMEVER)
quiet = false;
if (opt & INFO_RAW) {
if ((opt & (INFO_RAW_JSON|INFO_RAW_JSON_COMPACT|INFO_RAW_UCL)) == 0)
opt |= INFO_RAW_YAML;
}
if (file != NULL) {
if ((fd = open(file, O_RDONLY)) == -1) {
warn("Unable to open %s", file);
return (EXIT_FAILURE);
}
pkg_drop_privileges();
#ifdef HAVE_CAPSICUM
cap_rights_init(&rights, CAP_READ, CAP_FSTAT);
if (cap_rights_limit(fd, &rights) < 0 && errno != ENOSYS ) {
warn("cap_rights_limit() failed");
close(fd);
return (EXIT_FAILURE);
}
#ifndef PKG_COVERAGE
if (cap_enter() < 0 && errno != ENOSYS) {
warn("cap_enter() failed");
close(fd);
return (EXIT_FAILURE);
}
#endif
#endif
if (opt == INFO_TAG_NAMEVER)
opt |= INFO_FULL;
if ((opt & (INFO_RAW | INFO_FILES |
INFO_DIRS)) == 0)
open_flags = PKG_OPEN_MANIFEST_COMPACT;
if (pkg_open_fd(&pkg, fd, open_flags) != EPKG_OK) {
close(fd);
return (1);
}
print_info(NULL, pkg, opt);
close(fd);
pkg_free(pkg);
return (EXIT_SUCCESS);
}
ret = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_LOCAL);
if (ret == EPKG_ENOACCESS) {
warnx("Insufficient privileges to query the package database");
return (EXIT_FAILURE);
} else if (ret == EPKG_ENODB) {
if (match == MATCH_ALL)
return (EXIT_SUCCESS);
if (origin_search)
return (EXIT_SUCCESS);
if (!quiet)
warnx("No packages installed");
return (EXIT_FAILURE);
} else if (ret != EPKG_OK)
return (EXIT_FAILURE);
ret = pkgdb_open(&db, PKGDB_DEFAULT);
if (ret != EPKG_OK)
return (EXIT_FAILURE);
pkg_drop_privileges();
if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
pkgdb_close(db);
warnx("Cannot get a read lock on a database, it is locked by another process");
return (EXIT_FAILURE);
}
i = 0;
do {
gotone = false;
pkgname = argv[i];
if (argc > 0 && pkgname[strlen(pkgname) -1] == '/')
pkgname[strlen(pkgname) -1] = '\0';
if (argc > 0) {
j=0;
while (pkgname[j] != '\0') {
if (pkgname[j] == '<') {
if (pkgversion) {
pkgversion2 = pkgname + j;
sign2 = LT;
pkgversion2[0] = '\0';
pkgversion2++;
if (pkgversion2[0] == '=') {
pkgversion2++;
sign2=LE;
j++;
}
} else {
pkgversion = pkgname + j;
sign = LT;
pkgversion[0] = '\0';
pkgversion++;
if (pkgversion[0] == '=') {
pkgversion++;
sign=LE;
j++;
}
}
} else if (pkgname[j] == '>') {
if (pkgversion) {
pkgversion2 = pkgname + j;
sign2 = GT;
pkgversion2[0] = '\0';
pkgversion2++;
if (pkgversion2[0] == '=') {
pkgversion2++;
sign2=GE;
j++;
}
} else {
pkgversion = pkgname + j;
sign = GT;
pkgversion[0] = '\0';
pkgversion++;
if (pkgversion[0] == '=') {
pkgversion++;
sign=GE;
j++;
}
}
} else if (pkgname[j] == '=') {
if (pkgversion) {
pkgversion2 = pkgname + j;
sign2 = EQ;
pkgversion2[0] = '\0';
pkgversion2++;
if (pkgversion2[0] == '=') {
pkgversion2++;
sign2=EQ;
j++;
}
} else {
pkgversion = pkgname + j;
sign = EQ;
pkgversion[0] = '\0';
pkgversion++;
if (pkgversion[0] == '=') {
pkgversion++;
sign=EQ;
j++;
}
}
}
j++;
}
}
if (match != MATCH_ALL && pkgname[0] == '\0') {
fprintf(stderr, "Pattern must not be empty.\n");
i++;
continue;
}
if ((it = pkgdb_query(db, pkgname, match)) == NULL) {
goto cleanup;
}
if (origin_search)
gotone = true;
if (argc == 1 && !origin_search && !quiet && !e_flag &&
match == MATCH_GLOB &&
strcspn(pkgname, "*[]{}()") == strlen(pkgname) &&
opt == INFO_TAG_NAMEVER && !quiet)
opt |= INFO_FULL;
query_flags = info_flags(opt, false);
while ((ret = pkgdb_it_next(it, &pkg, query_flags)) == EPKG_OK) {
gotone = true;
const char *version;
pkg_get(pkg, PKG_ATTR_VERSION, &version);
if (pkgversion != NULL) {
switch (pkg_version_cmp(version, pkgversion)) {
case -1:
if (sign != LT && sign != LE) {
gotone = false;
continue;
}
break;
case 0:
if (sign != LE &&
sign != GE &&
sign != EQ) {
gotone = false;
continue;
}
break;
case 1:
if (sign != GT && sign != GE) {
gotone = false;
continue;
}
break;
}
}
if (pkgversion2 != NULL) {
switch (pkg_version_cmp(version, pkgversion2)) {
case -1:
if (sign2 != LT && sign2 != LE) {
gotone = false;
continue;
}
break;
case 0:
if (sign2 != LE &&
sign2 != GE &&
sign2 != EQ) {
gotone = false;
continue;
}
break;
case 1:
if (sign2 != GT && sign2 != GE) {
gotone = false;
continue;
}
break;
}
}
if (pkg_exists)
retcode = EXIT_SUCCESS;
else
print_info(db, pkg, opt);
}
if (ret != EPKG_END) {
retcode = EXIT_FAILURE;
}
if (retcode == EXIT_SUCCESS && !gotone && match != MATCH_ALL) {
if (!quiet)
warnx("No package(s) matching %s", argv[i]);
retcode = EXIT_FAILURE;
}
pkgdb_it_free(it);
i++;
} while (i < argc);
cleanup:
pkg_free(pkg);
pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
pkgdb_close(db);
return (retcode);
}