/* $Id: autoremove.c,v 1.24 2009/05/08 10:03:03 imil Exp $ */ /* * Copyright (c) 2009 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Emile "iMil" Heitor . * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* autoremove.c: cleanup orphan dependencies */ #include "pkg_dry.h" #define AUTOREMOVE_WARNING "\ in order to remove packages from the autoremove list, flag those with the -k modifier." static int removenb = 0; static Plisthead *keeplisthead; static int deptreecount(char *fullpkg, const char *query) { Deptreehead deptreehead; Pkgdeptree *pdp; Pkglist *pkglist; char *pkgname; int count = 0; /* first check the package itself is not keep-able */ SLIST_FOREACH(pkglist, keeplisthead, next) if (strncmp(fullpkg, pkglist->pkgname, strlen(fullpkg)) == 0) return 1; SLIST_INIT(&deptreehead); XSTRDUP(pkgname, fullpkg); trunc_str(pkgname, '-', STR_BACKWARD); full_dep_tree(pkgname, query, &deptreehead, __func__, 0); SLIST_FOREACH(pdp, &deptreehead, next) if (pdp->pkgkeep) count++; free_deptree(&deptreehead); XFREE(pkgname); return count; } /* * recursively find unneeded dependencies over 1st autoremove line */ void find_useless(Deptreehead *removehead) { Deptreehead remtreehead; Pkgdeptree *rdp, *premove, *premadd, *testdp; Plisthead *plisthead; Pkglist *mapplist; char *pkgname; int keepcount, exists; if ((plisthead = rec_pkglist(LOCAL_PKGS_QUERY)) == NULL) return; /* empty local pkg list, should not happen */ /* record all dependency tree for 1st autoremove level */ SLIST_FOREACH(premove, removehead, next) { XSTRDUP(pkgname, premove->depname); trunc_str(pkgname, '-', STR_BACKWARD); SLIST_INIT(&remtreehead); /* returns dependencies */ full_dep_tree(pkgname, LOCAL_DIRECT_DEPS, &remtreehead, __func__, 0); /* browse full dependencies for autoremove list */ SLIST_FOREACH(rdp, &remtreehead, next) { if ((mapplist = map_pkg_to_dep(plisthead, rdp->depname)) == NULL) continue; /* database corruption, should not happen */ XSTRDUP(pkgname, mapplist->pkgname); if (pkgname == NULL) /* database corruption, empty mapping */ continue; /* how many reverse dependencies keep flag */ keepcount = deptreecount(pkgname, LOCAL_REVERSE_DEPS); /* if there are keep packages, this dependency * is needed by another non-autoremovable package */ if (keepcount == 0) { exists = 0; /* check if package is already recorded as to-remove */ SLIST_FOREACH(testdp, removehead, next) { if (testdp->depname == NULL || pkgname == NULL) /* database corruption */ continue; if (strncmp(testdp->depname, pkgname, strlen(testdp->depname)) == 0) exists = 1; } if (exists) { XFREE(pkgname); continue; } XMALLOC(premadd, sizeof(Pkgdeptree)); XSTRDUP(premadd->depname, pkgname); premadd->matchname = NULL; /* safety */ premadd->level = 0; SLIST_INSERT_HEAD(removehead, premadd, next); removenb++; XFREE(pkgname); } /* keepcount == 0, no keep packages depends on this package */ } /* premove SLIST_FOREACH */ XFREE(pkgname); free_deptree(&remtreehead); } free_pkglist(plisthead); XFREE(plisthead); } void pkg_dry_autoremove() { Plisthead *plisthead; Pkglist *pkglist; Deptreehead deptreehead, removehead, *orderedhead; Pkgdeptree *premove; char *pkgname, *p, *toremove = NULL; /* test if there's any keep package */ if ((plisthead = rec_pkglist(KEEP_LOCAL_PKGS)) == NULL) errx(EXIT_FAILURE, "no packages have been installed with %s", getprogname()); free_pkglist(plisthead); XFREE(plisthead); if ((plisthead = rec_pkglist(NOKEEP_LOCAL_PKGS)) == NULL) return; SLIST_INIT(&removehead); /* browse local packages not marked as "keep" */ SLIST_FOREACH(pkglist, plisthead, next) { XSTRDUP(pkgname, pkglist->pkgname); if ((p = strrchr(pkgname, '-')) == NULL) return; /* should not happen */ *p = '\0'; SLIST_INIT(&deptreehead); full_dep_tree(pkgname, LOCAL_REVERSE_DEPS, &deptreehead, __func__, 0); if (SLIST_EMPTY(&deptreehead)) { /* empty dep list */ XMALLOC(premove, sizeof(Pkgdeptree)); XSTRDUP(premove->depname, pkglist->pkgname); premove->matchname = NULL; /* safety */ premove->level = 0; SLIST_INSERT_HEAD(&removehead, premove, next); removenb++; } free_deptree(&deptreehead); } free_pkglist(plisthead); XFREE(plisthead); if ((keeplisthead = rec_pkglist(KEEP_LOCAL_PKGS)) == NULL) return; find_useless(&removehead); free_pkglist(keeplisthead); XFREE(keeplisthead); orderedhead = order_remove(&removehead); if (!SLIST_EMPTY(orderedhead)) { SLIST_FOREACH(premove, orderedhead, next) toremove = action_list(toremove, premove->depname); /* we want this action to be confirmed */ yesflag = 0; printf("%s\n", AUTOREMOVE_WARNING); printf("%d packages to be autoremoved: %s\n", removenb, toremove); if (check_yesno()) { SLIST_FOREACH(premove, orderedhead, next) { printf("removing %s...\n", premove->depname); #ifdef DEBUG printf("%s -f %s\n", PKG_DELETE, premove->depname); #else fexec(PKG_DELETE, "-f", premove->depname, NULL); #endif } update_db(LOCAL_SUMMARY, NULL); } } XFREE(toremove); free_deptree(orderedhead); XFREE(orderedhead); } void show_pkg_keep(void) { Plisthead *plisthead; Pkglist *pkglist; plisthead = rec_pkglist(KEEP_LOCAL_PKGS); if (plisthead == NULL) { printf("keep-list is empty.\n"); return; } SLIST_FOREACH(pkglist, plisthead, next) printf("%s is marked as non-autoremovable\n", pkglist->pkgname); free_pkglist(plisthead); XFREE(plisthead); } static char * match_keep(char *pkgkeep, char *pkgname, char *real_pkg) { int pkglen; pkglen = strlen(pkgname); if (strlen(pkgkeep) == pkglen && strncmp(pkgname, pkgkeep, pkglen) == 0) { XFREE(pkgname); XSTRDUP(pkgname, real_pkg); return pkgname; } return NULL; } /* flag packages in pkgargs as non or autoremovable */ void pkg_keep(int type, char **pkgargs) { Plisthead *plisthead; Pkglist *pkglist; char **pkeep, *pkgname, query[BUFSIZ]; plisthead = rec_pkglist(LOCAL_PKGS_QUERY); if (plisthead == NULL) /* no packages recorded */ return; /* parse packages by their command line names */ for (pkeep = pkgargs; *pkeep != NULL; pkeep++) { pkgname = NULL; /* find real package name */ SLIST_FOREACH(pkglist, plisthead, next) { /* PKGNAME match */ if (exact_pkgfmt(*pkeep)) /* argument was a full package name */ trunc_str(*pkeep, '-', STR_BACKWARD); XSTRDUP(pkgname, pkglist->pkgname); trunc_str(pkgname, '-', STR_BACKWARD); if ((pkgname = match_keep(*pkeep, pkgname, pkglist->pkgname)) != NULL) break; XFREE(pkgname); } /* SLIST pkglist */ if (pkgname != NULL) { switch (type) { case KEEP: printf("marking %s as non auto-removeable\n", pkgname); snprintf(query, BUFSIZ, KEEP_PKG, pkgname); break; case UNKEEP: printf("marking %s as auto-removeable\n", pkgname); snprintf(query, BUFSIZ, UNKEEP_PKG, pkgname); break; } drydb_doquery(query, NULL, NULL); XFREE(pkgname); } else printf("no such package %s installed\n", *pkeep); } /* for (pkeep) */ free_pkglist(plisthead); XFREE(plisthead); }