/* app_info.c - Alpine Package Keeper (APK) * * Copyright (C) 2005-2009 Natanael Copa * Copyright (C) 2008-2011 Timo Teräs * All rights reserved. * * SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include "apk_defines.h" #include "apk_applet.h" #include "apk_package.h" #include "apk_database.h" #include "apk_print.h" struct info_ctx { struct apk_database *db; void (*action)(struct info_ctx *ctx, struct apk_database *db, struct apk_string_array *args); int subaction_mask; int errors; }; static int verbosity = 0; /* These need to stay in sync with the function pointer array in * info_subaction() */ #define APK_INFO_DESC 0x01 #define APK_INFO_URL 0x02 #define APK_INFO_SIZE 0x04 #define APK_INFO_DEPENDS 0x08 #define APK_INFO_PROVIDES 0x10 #define APK_INFO_RDEPENDS 0x20 #define APK_INFO_CONTENTS 0x40 #define APK_INFO_TRIGGERS 0x80 #define APK_INFO_INSTALL_IF 0x100 #define APK_INFO_RINSTALL_IF 0x200 #define APK_INFO_REPLACES 0x400 #define APK_INFO_LICENSE 0x800 static void verbose_print_pkg(struct apk_package *pkg, int minimal_verbosity) { int v = min(verbosity, minimal_verbosity); if (pkg == NULL || v < 1) return; printf("%s", pkg->name->name); if (v > 1) printf("-" BLOB_FMT, BLOB_PRINTF(*pkg->version)); if (v > 2) printf(" - %s", pkg->description); printf("\n"); } static void info_exists(struct info_ctx *ctx, struct apk_database *db, struct apk_string_array *args) { struct apk_name *name; struct apk_dependency dep; struct apk_provider *p; char **parg; int ok; foreach_array_item(parg, args) { apk_blob_t b = APK_BLOB_STR(*parg); apk_blob_pull_dep(&b, db, &dep); if (APK_BLOB_IS_NULL(b) || b.len > 0) continue; name = dep.name; if (name == NULL) continue; ok = apk_dep_is_provided(&dep, NULL); foreach_array_item(p, name->providers) { if (!p->pkg->ipkg) continue; ok = apk_dep_is_provided(&dep, p); if (ok) verbose_print_pkg(p->pkg, 0); break; } if (!ok) ctx->errors++; } } static void info_who_owns(struct info_ctx *ctx, struct apk_database *db, struct apk_string_array *args) { struct apk_out *out = &db->ctx->out; struct apk_package *pkg; struct apk_dependency_array *deps; struct apk_dependency dep; struct apk_ostream *os; const char *via; char **parg, fnbuf[PATH_MAX], buf[PATH_MAX]; apk_blob_t fn; ssize_t r; apk_dependency_array_init(&deps); foreach_array_item(parg, args) { if (*parg[0] != '/' && realpath(*parg, fnbuf)) fn = APK_BLOB_STR(fnbuf); else fn = APK_BLOB_STR(*parg); via = ""; pkg = apk_db_get_file_owner(db, fn); if (pkg == NULL) { r = readlinkat(db->root_fd, *parg, buf, sizeof(buf)); if (r > 0 && r < PATH_MAX && buf[0] == '/') { pkg = apk_db_get_file_owner(db, APK_BLOB_STR(buf)); via = "symlink target "; } } if (pkg == NULL) { apk_err(out, BLOB_FMT ": Could not find owner package", BLOB_PRINTF(fn)); ctx->errors++; continue; } if (verbosity < 1) { dep = (struct apk_dependency) { .name = pkg->name, .version = &apk_atom_null, .result_mask = APK_DEPMASK_ANY, }; apk_deps_add(&deps, &dep); } else { printf(BLOB_FMT " %sis owned by " PKG_VER_FMT "\n", BLOB_PRINTF(fn), via, PKG_VER_PRINTF(pkg)); } } if (verbosity < 1 && deps->num != 0) { os = apk_ostream_to_fd(STDOUT_FILENO); if (!IS_ERR(os)) { apk_deps_write(db, deps, os, APK_BLOB_PTR_LEN(" ", 1)); apk_ostream_write(os, "\n", 1); apk_ostream_close(os); } } apk_dependency_array_free(&deps); } static void info_print_description(struct apk_database *db, struct apk_package *pkg) { if (verbosity > 1) printf("%s: %s", pkg->name->name, pkg->description); else printf(PKG_VER_FMT " description:\n%s\n", PKG_VER_PRINTF(pkg), pkg->description); } static void info_print_url(struct apk_database *db, struct apk_package *pkg) { if (verbosity > 1) printf("%s: %s", pkg->name->name, pkg->url); else printf(PKG_VER_FMT " webpage:\n%s\n", PKG_VER_PRINTF(pkg), pkg->url); } static void info_print_license(struct apk_database *db, struct apk_package *pkg) { if (verbosity > 1) printf("%s: " BLOB_FMT , pkg->name->name, BLOB_PRINTF(*pkg->license)); else printf(PKG_VER_FMT " license:\n" BLOB_FMT "\n", PKG_VER_PRINTF(pkg), BLOB_PRINTF(*pkg->license)); } static void info_print_size(struct apk_database *db, struct apk_package *pkg) { off_t size; const char *size_unit; size_unit = apk_get_human_size(pkg->installed_size, &size); if (verbosity > 1) printf("%s: %lld %s", pkg->name->name, (long long)size, size_unit); else printf(PKG_VER_FMT " installed size:\n%lld %s\n", PKG_VER_PRINTF(pkg), (long long)size, size_unit); } static void info_print_dep_array(struct apk_database *db, struct apk_package *pkg, struct apk_dependency_array *deps, const char *dep_text) { struct apk_dependency *d; apk_blob_t separator = APK_BLOB_STR(verbosity > 1 ? " " : "\n"); char buf[256]; if (verbosity == 1) printf(PKG_VER_FMT " %s:\n", PKG_VER_PRINTF(pkg), dep_text); if (verbosity > 1) printf("%s: ", pkg->name->name); foreach_array_item(d, deps) { apk_blob_t b = APK_BLOB_BUF(buf); apk_blob_push_dep(&b, db, d); apk_blob_push_blob(&b, separator); b = apk_blob_pushed(APK_BLOB_BUF(buf), b); fwrite(b.ptr, b.len, 1, stdout); } } static void info_print_depends(struct apk_database *db, struct apk_package *pkg) { info_print_dep_array(db, pkg, pkg->depends, "depends on"); } static void info_print_provides(struct apk_database *db, struct apk_package *pkg) { info_print_dep_array(db, pkg, pkg->provides, "provides"); } static void print_rdep_pkg(struct apk_package *pkg0, struct apk_dependency *dep0, struct apk_package *pkg, void *pctx) { printf(PKG_VER_FMT "%s", PKG_VER_PRINTF(pkg0), verbosity > 1 ? " " : "\n"); } static void info_print_required_by(struct apk_database *db, struct apk_package *pkg) { if (verbosity == 1) printf(PKG_VER_FMT " is required by:\n", PKG_VER_PRINTF(pkg)); if (verbosity > 1) printf("%s: ", pkg->name->name); apk_pkg_foreach_reverse_dependency( pkg, APK_FOREACH_INSTALLED | APK_DEP_SATISFIES | apk_foreach_genid(), print_rdep_pkg, NULL); } static void info_print_install_if(struct apk_database *db, struct apk_package *pkg) { info_print_dep_array(db, pkg, pkg->install_if, "has auto-install rule"); } static void info_print_rinstall_if(struct apk_database *db, struct apk_package *pkg) { int i, j; char *separator = verbosity > 1 ? " " : "\n"; if (verbosity == 1) printf(PKG_VER_FMT " affects auto-installation of:\n", PKG_VER_PRINTF(pkg)); if (verbosity > 1) printf("%s: ", pkg->name->name); for (i = 0; i < pkg->name->rinstall_if->num; i++) { struct apk_name *name0; struct apk_package *pkg0; /* Check only the package that is installed, and that * it actually has this package in install_if. */ name0 = pkg->name->rinstall_if->item[i]; pkg0 = apk_pkg_get_installed(name0); if (pkg0 == NULL) continue; for (j = 0; j < pkg0->install_if->num; j++) { if (pkg0->install_if->item[j].name != pkg->name) continue; printf(PKG_VER_FMT "%s", PKG_VER_PRINTF(pkg0), separator); break; } } } static void info_print_contents(struct apk_database *db, struct apk_package *pkg) { struct apk_installed_package *ipkg = pkg->ipkg; struct apk_db_dir_instance *diri; struct apk_db_file *file; struct hlist_node *dc, *dn, *fc, *fn; if (verbosity == 1) printf(PKG_VER_FMT " contains:\n", PKG_VER_PRINTF(pkg)); hlist_for_each_entry_safe(diri, dc, dn, &ipkg->owned_dirs, pkg_dirs_list) { hlist_for_each_entry_safe(file, fc, fn, &diri->owned_files, diri_files_list) { if (verbosity > 1) printf("%s: ", pkg->name->name); printf(DIR_FILE_FMT "\n", DIR_FILE_PRINTF(diri->dir, file)); } } } static void info_print_triggers(struct apk_database *db, struct apk_package *pkg) { struct apk_installed_package *ipkg = pkg->ipkg; char **trigger; if (verbosity == 1) printf(PKG_VER_FMT " triggers:\n", PKG_VER_PRINTF(pkg)); foreach_array_item(trigger, ipkg->triggers) { if (verbosity > 1) printf("%s: trigger ", pkg->name->name); printf("%s\n", *trigger); } } static void info_print_replaces(struct apk_database *db, struct apk_package *pkg) { info_print_dep_array(db, pkg, pkg->ipkg->replaces, "replaces"); } static void info_subaction(struct info_ctx *ctx, struct apk_package *pkg) { typedef void (*subaction_t)(struct apk_database *, struct apk_package *); static subaction_t subactions[] = { info_print_description, info_print_url, info_print_size, info_print_depends, info_print_provides, info_print_required_by, info_print_contents, info_print_triggers, info_print_install_if, info_print_rinstall_if, info_print_replaces, info_print_license, }; const int requireipkg = APK_INFO_CONTENTS | APK_INFO_TRIGGERS | APK_INFO_RDEPENDS | APK_INFO_RINSTALL_IF | APK_INFO_REPLACES; int i; for (i = 0; i < ARRAY_SIZE(subactions); i++) { if (!(BIT(i) & ctx->subaction_mask)) continue; if (pkg->ipkg == NULL && (BIT(i) & requireipkg)) continue; subactions[i](ctx->db, pkg); puts(""); } } static void print_name_info(struct apk_database *db, const char *match, struct apk_name *name, void *pctx) { struct info_ctx *ctx = (struct info_ctx *) pctx; struct apk_provider *p; if (name == NULL) { ctx->errors++; return; } foreach_array_item(p, name->providers) info_subaction(ctx, p->pkg); } #define INFO_OPTIONS(OPT) \ OPT(OPT_INFO_all, APK_OPT_SH("a") "all") \ OPT(OPT_INFO_contents, APK_OPT_SH("L") "contents") \ OPT(OPT_INFO_depends, APK_OPT_SH("R") "depends") \ OPT(OPT_INFO_description, APK_OPT_SH("d") "description") \ OPT(OPT_INFO_install_if, "install-if") \ OPT(OPT_INFO_installed, APK_OPT_SH("e") "installed") \ OPT(OPT_INFO_license, "license") \ OPT(OPT_INFO_provides, APK_OPT_SH("P") "provides") \ OPT(OPT_INFO_rdepends, APK_OPT_SH("r") "rdepends") \ OPT(OPT_INFO_replaces, "replaces") \ OPT(OPT_INFO_rinstall_if, "rinstall-if") \ OPT(OPT_INFO_size, APK_OPT_SH("s") "size") \ OPT(OPT_INFO_triggers, APK_OPT_SH("t") "triggers") \ OPT(OPT_INFO_webpage, APK_OPT_SH("w") "webpage") \ OPT(OPT_INFO_who_owns, APK_OPT_SH("W") "who-owns") APK_OPT_APPLET(option_desc, INFO_OPTIONS); static int option_parse_applet(void *pctx, struct apk_ctx *ac, int opt, const char *optarg) { struct info_ctx *ctx = (struct info_ctx *) pctx; ctx->action = NULL; switch (opt) { case OPT_INFO_installed: ctx->action = info_exists; ac->open_flags |= APK_OPENF_NO_REPOS; break; case OPT_INFO_who_owns: ctx->action = info_who_owns; ac->open_flags |= APK_OPENF_NO_REPOS; break; case OPT_INFO_webpage: ctx->subaction_mask |= APK_INFO_URL; break; case OPT_INFO_depends: ctx->subaction_mask |= APK_INFO_DEPENDS; break; case OPT_INFO_provides: ctx->subaction_mask |= APK_INFO_PROVIDES; break; case OPT_INFO_rdepends: ctx->subaction_mask |= APK_INFO_RDEPENDS; break; case OPT_INFO_install_if: ctx->subaction_mask |= APK_INFO_INSTALL_IF; break; case OPT_INFO_rinstall_if: ctx->subaction_mask |= APK_INFO_RINSTALL_IF; break; case OPT_INFO_size: ctx->subaction_mask |= APK_INFO_SIZE; break; case OPT_INFO_description: ctx->subaction_mask |= APK_INFO_DESC; break; case OPT_INFO_contents: ctx->subaction_mask |= APK_INFO_CONTENTS; break; case OPT_INFO_triggers: ctx->subaction_mask |= APK_INFO_TRIGGERS; break; case OPT_INFO_replaces: ctx->subaction_mask |= APK_INFO_REPLACES; break; case OPT_INFO_license: ctx->subaction_mask |= APK_INFO_LICENSE; break; case OPT_INFO_all: ctx->subaction_mask = 0xffffffff; break; default: return -ENOTSUP; } return 0; } static int info_main(void *ctx, struct apk_ctx *ac, struct apk_string_array *args) { struct apk_out *out = &ac->out; struct apk_database *db = ac->db; struct info_ctx *ictx = (struct info_ctx *) ctx; struct apk_installed_package *ipkg; verbosity = apk_out_verbosity(out); ictx->db = db; if (ictx->subaction_mask == 0) ictx->subaction_mask = APK_INFO_DESC | APK_INFO_URL | APK_INFO_SIZE; if (ictx->action != NULL) { ictx->action(ictx, db, args); } else if (args->num > 0) { /* Print info on given names */ apk_name_foreach_matching( db, args, APK_FOREACH_NULL_MATCHES_ALL | apk_foreach_genid(), print_name_info, ctx); } else { /* Print all installed packages */ list_for_each_entry(ipkg, &db->installed.packages, installed_pkgs_list) verbose_print_pkg(ipkg->pkg, 1); } return ictx->errors; } static const struct apk_option_group optgroup_applet = { .desc = option_desc, .parse = option_parse_applet, }; static struct apk_applet apk_info = { .name = "info", .open_flags = APK_OPENF_READ, .context_size = sizeof(struct info_ctx), .optgroups = { &optgroup_global, &optgroup_applet }, .main = info_main, }; APK_DEFINE_APPLET(apk_info);