/* * dpkg - main program for package management * enquiry.c - status enquiry and listing options * * Copyright © 1995,1996 Ian Jackson * Copyright © 2006, 2008-2016 Guillem Jover * Copyright © 2011 Linaro Limited * Copyright © 2011 Raphaël Hertzog * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "main.h" struct audit_problem { bool (*check)(struct pkginfo *, const struct audit_problem *problem); union { int number; const char *string; } value; const char *explanation; }; static bool audit_reinstreq(struct pkginfo *pkg, const struct audit_problem *problem) { return pkg->eflag & PKG_EFLAG_REINSTREQ; } static bool audit_status(struct pkginfo *pkg, const struct audit_problem *problem) { if (pkg->eflag & PKG_EFLAG_REINSTREQ) return false; return (int)pkg->status == problem->value.number; } static bool audit_infofile(struct pkginfo *pkg, const struct audit_problem *problem) { if (pkg->status < PKG_STAT_HALFINSTALLED) return false; return !pkg_infodb_has_file(pkg, &pkg->installed, problem->value.string); } static bool audit_arch(struct pkginfo *pkg, const struct audit_problem *problem) { if (pkg->status < PKG_STAT_HALFINSTALLED) return false; return pkg->installed.arch->type == (enum dpkg_arch_type)problem->value.number; } static const struct audit_problem audit_problems[] = { { .check = audit_reinstreq, .value.number = 0, .explanation = N_( "The following packages are in a mess due to serious problems during\n" "installation. They must be reinstalled for them (and any packages\n" "that depend on them) to function properly:\n") }, { .check = audit_status, .value.number = PKG_STAT_UNPACKED, .explanation = N_( "The following packages have been unpacked but not yet configured.\n" "They must be configured using dpkg --configure or the configure\n" "menu option in dselect for them to work:\n") }, { .check = audit_status, .value.number = PKG_STAT_HALFCONFIGURED, .explanation = N_( "The following packages are only half configured, probably due to problems\n" "configuring them the first time. The configuration should be retried using\n" "dpkg --configure or the configure menu option in dselect:\n") }, { .check = audit_status, .value.number = PKG_STAT_HALFINSTALLED, .explanation = N_( "The following packages are only half installed, due to problems during\n" "installation. The installation can probably be completed by retrying it;\n" "the packages can be removed using dselect or dpkg --remove:\n") }, { .check = audit_status, .value.number = PKG_STAT_TRIGGERSAWAITED, .explanation = N_( "The following packages are awaiting processing of triggers that they\n" "have activated in other packages. This processing can be requested using\n" "dselect or dpkg --configure --pending (or dpkg --triggers-only):\n") }, { .check = audit_status, .value.number = PKG_STAT_TRIGGERSPENDING, .explanation = N_( "The following packages have been triggered, but the trigger processing\n" "has not yet been done. Trigger processing can be requested using\n" "dselect or dpkg --configure --pending (or dpkg --triggers-only):\n") }, { .check = audit_infofile, .value.string = LISTFILE, .explanation = N_( "The following packages are missing the list control file in the\n" "database, they need to be reinstalled:\n") }, { .check = audit_infofile, .value.string = HASHFILE, .explanation = N_( "The following packages are missing the md5sums control file in the\n" "database, they need to be reinstalled:\n") }, { .check = audit_arch, .value.number = DPKG_ARCH_EMPTY, .explanation = N_("The following packages do not have an architecture:\n") }, { .check = audit_arch, .value.number = DPKG_ARCH_ILLEGAL, .explanation = N_("The following packages have an illegal architecture:\n") }, { .check = audit_arch, .value.number = DPKG_ARCH_UNKNOWN, .explanation = N_( "The following packages have an unknown foreign architecture, which will\n" "cause dependency issues on front-ends. This can be fixed by registering\n" "the foreign architecture with dpkg --add-architecture:\n") }, { .check = NULL } }; static void describebriefly(struct pkginfo *pkg) { int maxl, l; const char *pdesc; maxl= 57; l= strlen(pkg->set->name); if (l>20) maxl -= (l-20); pdesc = pkgbin_synopsis(pkg, &pkg->installed, &l); l = min(l, maxl); printf(" %-20s %.*s\n", pkg_name(pkg, pnaw_nonambig), l, pdesc); } static struct pkginfo * pkg_array_mapper(const char *name) { struct pkginfo *pkg; pkg = dpkg_options_parse_pkgname(cipaction, name); if (pkg->status == PKG_STAT_NOTINSTALLED) notice(_("package '%s' is not installed"), pkg_name(pkg, pnaw_nonambig)); return pkg; } int audit(const char *const *argv) { const struct audit_problem *problem; struct pkg_array array; bool head_running = false; int i; modstatdb_open(msdbrw_readonly); if (!*argv) pkg_array_init_from_hash(&array); else pkg_array_init_from_names(&array, pkg_array_mapper, (const char **)argv); pkg_array_sort(&array, pkg_sorter_by_nonambig_name_arch); for (problem = audit_problems; problem->check; problem++) { bool head = false; for (i = 0; i < array.n_pkgs; i++) { struct pkginfo *pkg = array.pkgs[i]; if (!problem->check(pkg, problem)) continue; if (!head_running) { if (modstatdb_is_locked()) puts(_( "Another process has locked the database for writing, and might currently be\n" "modifying it, some of the following problems might just be due to that.\n")); head_running = true; } if (!head) { fputs(gettext(problem->explanation), stdout); head = true; } describebriefly(pkg); } if (head) putchar('\n'); } pkg_array_destroy(&array); m_output(stdout, _("")); return 0; } struct sectionentry { struct sectionentry *next; const char *name; int count; }; static bool yettobeunpacked(struct pkginfo *pkg, const char **thissect) { if (pkg->want != PKG_WANT_INSTALL) return false; switch (pkg->status) { case PKG_STAT_UNPACKED: case PKG_STAT_INSTALLED: case PKG_STAT_HALFCONFIGURED: case PKG_STAT_TRIGGERSPENDING: case PKG_STAT_TRIGGERSAWAITED: return false; case PKG_STAT_NOTINSTALLED: case PKG_STAT_HALFINSTALLED: case PKG_STAT_CONFIGFILES: if (thissect) *thissect = str_is_set(pkg->section) ? pkg->section : C_("section", ""); return true; default: internerr("unknown package status '%d'", pkg->status); } return false; } int unpackchk(const char *const *argv) { int totalcount, sects; struct sectionentry *sectionentries, *se, **sep; struct pkg_hash_iter *iter; struct pkginfo *pkg; const char *thissect; char buf[20]; int width; if (*argv) badusage(_("--%s takes no arguments"), cipaction->olong); modstatdb_open(msdbrw_readonly); totalcount= 0; sectionentries = NULL; sects= 0; iter = pkg_hash_iter_new(); while ((pkg = pkg_hash_iter_next_pkg(iter))) { if (!yettobeunpacked(pkg, &thissect)) continue; for (se= sectionentries; se && strcasecmp(thissect,se->name); se= se->next); if (!se) { se = nfmalloc(sizeof(*se)); for (sep= §ionentries; *sep && strcasecmp(thissect,(*sep)->name) > 0; sep= &(*sep)->next); se->name= thissect; se->count= 0; se->next= *sep; *sep= se; sects++; } se->count++; totalcount++; } pkg_hash_iter_free(iter); if (totalcount == 0) return 0; if (totalcount <= 12) { iter = pkg_hash_iter_new(); while ((pkg = pkg_hash_iter_next_pkg(iter))) { if (!yettobeunpacked(pkg, NULL)) continue; describebriefly(pkg); } pkg_hash_iter_free(iter); } else if (sects <= 12) { for (se= sectionentries; se; se= se->next) { sprintf(buf,"%d",se->count); printf(_(" %d in %s: "),se->count,se->name); width= 70-strlen(se->name)-strlen(buf); while (width > 59) { putchar(' '); width--; } iter = pkg_hash_iter_new(); while ((pkg = pkg_hash_iter_next_pkg(iter))) { const char *pkgname; if (!yettobeunpacked(pkg,&thissect)) continue; if (strcasecmp(thissect,se->name)) continue; pkgname = pkg_name(pkg, pnaw_nonambig); width -= strlen(pkgname); width--; if (width < 4) { printf(" ..."); break; } printf(" %s", pkgname); } pkg_hash_iter_free(iter); putchar('\n'); } } else { printf(P_(" %d package, from the following section:", " %d packages, from the following sections:", totalcount), totalcount); width= 0; for (se= sectionentries; se; se= se->next) { sprintf(buf,"%d",se->count); width -= (6 + strlen(se->name) + strlen(buf)); if (width < 0) { putchar('\n'); width= 73 - strlen(se->name) - strlen(buf); } printf(" %s (%d)",se->name,se->count); } putchar('\n'); } m_output(stdout, _("")); return 0; } static const struct assert_feature { const char *name; const char *desc; const char *version; } assert_features[] = { { .name = "support-predepends", .desc = N_("the Pre-Depends field"), .version = "1.1.0", }, { .name = "working-epoch", .desc = N_("epochs in versions"), .version = "1.4.0.7", }, { .name = "long-filenames", .desc = N_("long filenames in .deb archives"), .version = "1.4.1.17", }, { .name = "multi-conrep", .desc = N_("multiple Conflicts and Replaces"), .version = "1.4.1.19", }, { .name = "multi-arch", .desc = N_("multi-arch fields and semantics"), .version = "1.16.2", }, { .name = "versioned-provides", .desc = N_("versioned relationships in the Provides field"), .version = "1.17.11", }, { .name = "protected-field", .desc = N_("the Protected field"), .version = "1.20.1", }, { .name = NULL, } }; static int assert_version_support(const char *const *argv, const struct assert_feature *feature) { const char *running_version_str; struct dpkg_version running_version = DPKG_VERSION_INIT; struct dpkg_version version = { 0, feature->version, NULL }; struct dpkg_error err = DPKG_ERROR_INIT; if (*argv) badusage(_("--%s takes no arguments"), cipaction->olong); /* * When using the feature asserts, we want to know whether the currently * running dpkg, which we might be running under (say from within a * maintainer script) and which might have a different version, supports * the requested feature. As dpkg is an Essential package, it is expected * to work even when just unpacked, and so its own version is enough. */ running_version_str = getenv("DPKG_RUNNING_VERSION"); /* * If we are not running from within a maintainer script, then that means * once we do, the executed dpkg will support the requested feature, if * we know about it. Always return success in that case. */ if (str_is_unset(running_version_str)) return 0; if (parseversion(&running_version, running_version_str, &err) < 0) ohshit(_("cannot parse dpkg running version '%s': %s"), running_version_str, err.str); if (dpkg_version_relate(&running_version, DPKG_RELATION_GE, &version)) return 0; printf(_("Running version of dpkg does not support %s.\n" " Please upgrade to at least dpkg %s, and then try again.\n"), feature->desc, versiondescribe(&version, vdew_nonambig)); return 1; } const char *assert_feature_name; int assert_feature(const char *const *argv) { const struct assert_feature *feature; if (strcmp(assert_feature_name, "help") == 0) { printf(_("%s assert options - assert whether features are supported:\n"), dpkg_get_progname()); for (feature = assert_features; feature->name; feature++) { printf(" %-19s %-9s %s\n", feature->name, feature->version, gettext(feature->desc)); } exit(0); } for (feature = assert_features; feature->name; feature++) { if (strcmp(feature->name, assert_feature_name) != 0) continue; return assert_version_support(argv, feature); } badusage(_("unknown --%s-"), cipaction->olong); } /** * Print a single package which: * (a) is the target of one or more relevant predependencies. * (b) has itself no unsatisfied pre-dependencies. * * If such a package is present output is the Packages file entry, * which can be massaged as appropriate. * * Exit status: * 0 = a package printed, OK * 1 = no suitable package available * 2 = error */ int predeppackage(const char *const *argv) { static struct varbuf vb; struct pkg_hash_iter *iter; struct pkginfo *pkg = NULL, *startpkg, *trypkg; struct dependency *dep; struct deppossi *possi, *provider; if (*argv) badusage(_("--%s takes no arguments"), cipaction->olong); modstatdb_open(msdbrw_readonly | msdbrw_available_readonly); /* We use clientdata->istobe to detect loops. */ clear_istobes(); dep = NULL; iter = pkg_hash_iter_new(); while (!dep && (pkg = pkg_hash_iter_next_pkg(iter))) { /* Ignore packages user doesn't want. */ if (pkg->want != PKG_WANT_INSTALL) continue; /* Ignore packages not available. */ if (!pkg->archives) continue; pkg->clientdata->istobe = PKG_ISTOBE_PREINSTALL; for (dep= pkg->available.depends; dep; dep= dep->next) { if (dep->type != dep_predepends) continue; if (depisok(dep, &vb, NULL, NULL, true)) continue; /* This will leave dep non-NULL, and so exit the loop. */ break; } pkg->clientdata->istobe = PKG_ISTOBE_NORMAL; /* If dep is NULL we go and get the next package. */ } pkg_hash_iter_free(iter); if (!dep) return 1; /* Not found. */ if (pkg == NULL) internerr("unexpected unfound package"); startpkg= pkg; pkg->clientdata->istobe = PKG_ISTOBE_PREINSTALL; /* OK, we have found an unsatisfied predependency. * Now go and find the first thing we need to install, as a first step * towards satisfying it. */ do { /* We search for a package which would satisfy dep, and put it in pkg. */ for (possi = dep->list, pkg = NULL; !pkg && possi; possi=possi->next) { struct deppossi_pkg_iterator *possi_iter; possi_iter = deppossi_pkg_iter_new(possi, wpb_available); while (!pkg && (trypkg = deppossi_pkg_iter_next(possi_iter))) { if (trypkg->archives && trypkg->clientdata->istobe == PKG_ISTOBE_NORMAL && versionsatisfied(&trypkg->available, possi)) { pkg = trypkg; break; } for (provider = possi->ed->depended.available; !pkg && provider; provider = provider->next) { if (provider->up->type != dep_provides) continue; if (!pkg_virtual_deppossi_satisfied(possi, provider)) continue; trypkg = provider->up->up; if (!trypkg->archives) continue; if (trypkg->clientdata->istobe == PKG_ISTOBE_NORMAL) { pkg = trypkg; break; } } } deppossi_pkg_iter_free(possi_iter); } if (!pkg) { varbuf_reset(&vb); describedepcon(&vb,dep); varbuf_end_str(&vb); notice(_("cannot see how to satisfy pre-dependency:\n %s"), vb.buf); ohshit(_("cannot satisfy pre-dependencies for %.250s (wanted due to %.250s)"), pkgbin_name(dep->up, &dep->up->available, pnaw_nonambig), pkgbin_name(startpkg, &startpkg->available, pnaw_nonambig)); } pkg->clientdata->istobe = PKG_ISTOBE_PREINSTALL; for (dep= pkg->available.depends; dep; dep= dep->next) { if (dep->type != dep_predepends) continue; if (depisok(dep, &vb, NULL, NULL, true)) continue; /* This will leave dep non-NULL, and so exit the loop. */ break; } } while (dep); /* OK, we've found it - pkg has no unsatisfied pre-dependencies! */ write_stanza(stdout, _(""), pkg, &pkg->available); m_output(stdout, _("")); return 0; } int printarch(const char *const *argv) { if (*argv) badusage(_("--%s takes no arguments"), cipaction->olong); printf("%s\n", dpkg_arch_get(DPKG_ARCH_NATIVE)->name); m_output(stdout, _("")); return 0; } int print_foreign_arches(const char *const *argv) { struct dpkg_arch *arch; if (*argv) badusage(_("--%s takes no arguments"), cipaction->olong); dpkg_arch_load_list(); for (arch = dpkg_arch_get_list(); arch; arch = arch->next) { if (arch->type != DPKG_ARCH_FOREIGN) continue; printf("%s\n", arch->name); } m_output(stdout, _("")); return 0; } int validate_pkgname(const char *const *argv) { const char *emsg; if (!argv[0] || argv[1]) badusage(_("--%s takes one argument"), cipaction->olong); emsg = pkg_name_is_illegal(argv[0]); if (emsg) ohshit(_("package name '%s' is invalid: %s"), argv[0], emsg); return 0; } int validate_trigname(const char *const *argv) { const char *emsg; if (!argv[0] || argv[1]) badusage(_("--%s takes one argument"), cipaction->olong); emsg = trig_name_is_illegal(argv[0]); if (emsg) ohshit(_("trigger name '%s' is invalid: %s"), argv[0], emsg); return 0; } int validate_archname(const char *const *argv) { const char *emsg; if (!argv[0] || argv[1]) badusage(_("--%s takes one argument"), cipaction->olong); emsg = dpkg_arch_name_is_illegal(argv[0]); if (emsg) ohshit(_("architecture name '%s' is invalid: %s"), argv[0], emsg); return 0; } int validate_version(const char *const *argv) { struct dpkg_version version; struct dpkg_error err; if (!argv[0] || argv[1]) badusage(_("--%s takes one argument"), cipaction->olong); if (parseversion(&version, argv[0], &err) < 0) { dpkg_error_print(&err, _("version '%s' has bad syntax"), argv[0]); dpkg_error_destroy(&err); return 1; } return 0; } int cmpversions(const char *const *argv) { struct relationinfo { const char *op; /* These values are exit status codes. */ int if_lesser, if_equal, if_greater; int if_none_a, if_none_both, if_none_b; bool obsolete; }; static const struct relationinfo relationinfos[]= { { .op = "le", .if_lesser = EXIT_SUCCESS, .if_equal = EXIT_SUCCESS, .if_greater = EXIT_FAILURE, .if_none_a = EXIT_SUCCESS, .if_none_both = EXIT_SUCCESS, .if_none_b = EXIT_FAILURE, }, { .op = "lt", .if_lesser = EXIT_SUCCESS, .if_equal = EXIT_FAILURE, .if_greater = EXIT_FAILURE, .if_none_a = EXIT_SUCCESS, .if_none_both = EXIT_FAILURE, .if_none_b = EXIT_FAILURE, }, { .op = "eq", .if_lesser = EXIT_FAILURE, .if_equal = EXIT_SUCCESS, .if_greater = EXIT_FAILURE, .if_none_a = EXIT_FAILURE, .if_none_both = EXIT_SUCCESS, .if_none_b = EXIT_FAILURE, }, { .op = "ne", .if_lesser = EXIT_SUCCESS, .if_equal = EXIT_FAILURE, .if_greater = EXIT_SUCCESS, .if_none_a = EXIT_SUCCESS, .if_none_both = EXIT_FAILURE, .if_none_b = EXIT_SUCCESS, }, { .op = "ge", .if_lesser = EXIT_FAILURE, .if_equal = EXIT_SUCCESS, .if_greater = EXIT_SUCCESS, .if_none_a = EXIT_FAILURE, .if_none_both = EXIT_SUCCESS, .if_none_b = EXIT_SUCCESS, }, { .op = "gt", .if_lesser = EXIT_FAILURE, .if_equal = EXIT_FAILURE, .if_greater = EXIT_SUCCESS, .if_none_a = EXIT_FAILURE, .if_none_both = EXIT_FAILURE, .if_none_b = EXIT_SUCCESS, }, /* These treat an empty version as later than any version. */ { .op = "le-nl", .if_lesser = EXIT_SUCCESS, .if_equal = EXIT_SUCCESS, .if_greater = EXIT_FAILURE, .if_none_a = EXIT_FAILURE, .if_none_both = EXIT_SUCCESS, .if_none_b = EXIT_SUCCESS, }, { .op = "lt-nl", .if_lesser = EXIT_SUCCESS, .if_equal = EXIT_FAILURE, .if_greater = EXIT_FAILURE, .if_none_a = EXIT_FAILURE, .if_none_both = EXIT_FAILURE, .if_none_b = EXIT_SUCCESS, }, { .op = "ge-nl", .if_lesser = EXIT_FAILURE, .if_equal = EXIT_SUCCESS, .if_greater = EXIT_SUCCESS, .if_none_a = EXIT_SUCCESS, .if_none_both = EXIT_SUCCESS, .if_none_b = EXIT_FAILURE, }, { .op = "gt-nl", .if_lesser = EXIT_FAILURE, .if_equal = EXIT_FAILURE, .if_greater = EXIT_SUCCESS, .if_none_a = EXIT_SUCCESS, .if_none_both = EXIT_FAILURE, .if_none_b = EXIT_FAILURE, }, /* For compatibility with dpkg control file syntax. */ { .op = "<", .if_lesser = EXIT_SUCCESS, .if_equal = EXIT_SUCCESS, .if_greater = EXIT_FAILURE, .if_none_a = EXIT_SUCCESS, .if_none_both = EXIT_SUCCESS, .if_none_b = EXIT_FAILURE, .obsolete = true, }, { .op = "<=", .if_lesser = EXIT_SUCCESS, .if_equal = EXIT_SUCCESS, .if_greater = EXIT_FAILURE, .if_none_a = EXIT_SUCCESS, .if_none_both = EXIT_SUCCESS, .if_none_b = EXIT_FAILURE, }, { .op = "<<", .if_lesser = EXIT_SUCCESS, .if_equal = EXIT_FAILURE, .if_greater = EXIT_FAILURE, .if_none_a = EXIT_SUCCESS, .if_none_both = EXIT_FAILURE, .if_none_b = EXIT_FAILURE, }, { .op = "=", .if_lesser = EXIT_FAILURE, .if_equal = EXIT_SUCCESS, .if_greater = EXIT_FAILURE, .if_none_a = EXIT_FAILURE, .if_none_both = EXIT_SUCCESS, .if_none_b = EXIT_FAILURE, }, { .op = ">", .if_lesser = EXIT_FAILURE, .if_equal = EXIT_SUCCESS, .if_greater = EXIT_SUCCESS, .if_none_a = EXIT_FAILURE, .if_none_both = EXIT_SUCCESS, .if_none_b = EXIT_SUCCESS, .obsolete = true, }, { .op = ">=", .if_lesser = EXIT_FAILURE, .if_equal = EXIT_SUCCESS, .if_greater = EXIT_SUCCESS, .if_none_a = EXIT_FAILURE, .if_none_both = EXIT_SUCCESS, .if_none_b = EXIT_SUCCESS, }, { .op = ">>", .if_lesser = EXIT_FAILURE, .if_equal = EXIT_FAILURE, .if_greater = EXIT_SUCCESS, .if_none_a = EXIT_FAILURE, .if_none_both = EXIT_FAILURE, .if_none_b = EXIT_SUCCESS, }, { .op = NULL, } }; const struct relationinfo *rip; struct dpkg_version a, b; struct dpkg_error err; int rc; if (!argv[0] || !argv[1] || !argv[2] || argv[3]) badusage(_("--compare-versions takes three arguments:" " ")); for (rip = relationinfos; rip->op && strcmp(rip->op, argv[1]); rip++) ; if (!rip->op) badusage(_("--compare-versions bad relation")); if (rip->obsolete) warning(_("--%s used with obsolete relation operator '%s'"), cipaction->olong, rip->op); if (*argv[0] && strcmp(argv[0],"")) { if (parseversion(&a, argv[0], &err) < 0) { dpkg_error_print(&err, _("version '%s' has bad syntax"), argv[0]); dpkg_error_destroy(&err); } } else { dpkg_version_blank(&a); } if (*argv[2] && strcmp(argv[2],"")) { if (parseversion(&b, argv[2], &err) < 0) { dpkg_error_print(&err, _("version '%s' has bad syntax"), argv[2]); dpkg_error_destroy(&err); } } else { dpkg_version_blank(&b); } if (!dpkg_version_is_informative(&a)) { if (dpkg_version_is_informative(&b)) return rip->if_none_a; else return rip->if_none_both; } else if (!dpkg_version_is_informative(&b)) { return rip->if_none_b; } rc = dpkg_version_compare(&a, &b); debug(dbg_general, "cmpversions a='%s' b='%s' r=%d", versiondescribe_c(&a,vdew_always), versiondescribe_c(&b,vdew_always), rc); if (rc > 0) return rip->if_greater; else if (rc < 0) return rip->if_lesser; else return rip->if_equal; }