diff options
Diffstat (limited to 'pkgtools/pkg_install/files/delete/perform.c')
-rw-r--r-- | pkgtools/pkg_install/files/delete/perform.c | 697 |
1 files changed, 697 insertions, 0 deletions
diff --git a/pkgtools/pkg_install/files/delete/perform.c b/pkgtools/pkg_install/files/delete/perform.c new file mode 100644 index 00000000000..cb7bfa04d70 --- /dev/null +++ b/pkgtools/pkg_install/files/delete/perform.c @@ -0,0 +1,697 @@ +/* $NetBSD: perform.c,v 1.1.1.1 2002/12/20 18:14:08 schmonz Exp $ */ + +#if 0 +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static const char *rcsid = "from FreeBSD Id: perform.c,v 1.15 1997/10/13 15:03:52 jkh Exp"; +#else +__RCSID("$NetBSD: perform.c,v 1.1.1.1 2002/12/20 18:14:08 schmonz Exp $"); +#endif +#endif +#endif + +/* + * FreeBSD install - a package for the installation and maintainance + * of non-core utilities. + * + * 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. + * + * Jordan K. Hubbard + * 18 July 1993 + * + * This is the main body of the delete module. + * + */ +/* + * Copyright (c) 1999 Christian E. Hopps + * All rights reserved. + * + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + * + * Added the require find and require delete code + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_ERR_H +#include <err.h> +#endif + +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif + +#include "lib.h" +#include "delete.h" + + +/* In which direction to search in require_find() */ +typedef enum { + FIND_UP, FIND_DOWN +} rec_find_t; + +static int require_find_recursive_up(lpkg_t *); +static int require_find_recursive_down(lpkg_t *, package_t *); +static int require_find(char *, rec_find_t); +static int require_delete(char *, int); +static void require_print(void); +static int undepend(const char *, void *); + +static char LogDir[FILENAME_MAX]; +static char linebuf[FILENAME_MAX]; +static char pkgdir[FILENAME_MAX]; + +static package_t Plist; + +static lpkg_head_t lpfindq; +static lpkg_head_t lpdelq; + +static void +sanity_check(char *pkg) +{ + if (!fexists(CONTENTS_FNAME)) { + cleanup(0); + errx(2, "installed package %s has no %s file!", + pkg, CONTENTS_FNAME); + } +} + +void +cleanup(int sig) +{ + /* Nothing to do */ + if (sig) /* in case this is ever used as a signal handler */ + exit(1); +} + +/* + * deppkgname is the pkg from which's +REQUIRED_BY file we are + * about to remove pkg2delname. This function is called from + * findmatchingname(), deppkgname is expanded from a (possible) pattern. + */ +static int +undepend(const char *deppkgname, void *vp) +{ + char *pkg2delname = vp; + char fname[FILENAME_MAX], ftmp[FILENAME_MAX]; + char fbuf[FILENAME_MAX]; + FILE *fp, *fpwr; + char *tmp; + int s; + + (void) snprintf(fname, sizeof(fname), "%s/%s/%s", + (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR, + deppkgname, REQUIRED_BY_FNAME); + fp = fopen(fname, "r"); + if (fp == NULL) { + warnx("couldn't open dependency file `%s'", fname); + return 0; + } + (void) snprintf(ftmp, sizeof(ftmp), "%s.XXXXXX", fname); + s = mkstemp(ftmp); + if (s == -1) { + fclose(fp); + warnx("couldn't open temp file `%s'", ftmp); + return 0; + } + fpwr = fdopen(s, "w"); + if (fpwr == NULL) { + close(s); + fclose(fp); + warnx("couldn't fdopen temp file `%s'", ftmp); + remove(ftmp); + return 0; + } + while (fgets(fbuf, sizeof(fbuf), fp) != NULL) { + if (fbuf[strlen(fbuf) - 1] == '\n') + fbuf[strlen(fbuf) - 1] = '\0'; + if (strcmp(fbuf, pkg2delname)) /* no match */ + fputs(fbuf, fpwr), putc('\n', fpwr); + } + (void) fclose(fp); + if (fchmod(s, 0644) == FAIL) { + warnx("error changing permission of temp file `%s'", ftmp); + fclose(fpwr); + remove(ftmp); + return 0; + } + if (fclose(fpwr) == EOF) { + warnx("error closing temp file `%s'", ftmp); + remove(ftmp); + return 0; + } + if (rename(ftmp, fname) == -1) + warnx("error renaming `%s' to `%s'", ftmp, fname); + remove(ftmp); /* just in case */ + + return 0; +} + +/* + * Delete from directory 'home' all packages on lpkg_list. + * If tryall is set, ignore errors from pkg_delete(1). + */ +int +require_delete(char *home, int tryall) +{ + lpkg_t *lpp; + int rv, fail; + char *tmp; + int oldcwd; + + /* save cwd */ + oldcwd = open(".", O_RDONLY, 0); + if (oldcwd == -1) + err(1, "cannot open \".\""); + + (void) snprintf(pkgdir, sizeof(pkgdir), "%s", + (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR); + + /* walk list of things to delete */ + fail = 0; + lpp = TAILQ_FIRST(&lpdelq); + for (; lpp; lpp = TAILQ_NEXT(lpp, lp_link)) { + int rm_installed; /* delete expanded pkg, not @pkgdep value */ + char installed[FILENAME_MAX]; + + /* go to the db dir */ + if (chdir(pkgdir) == FAIL) { + warnx("unable to change directory to %s, deinstall failed (1)", + pkgdir); + fail = 1; + break; + } + + /* look to see if package was already deleted */ + rm_installed = 0; + if (ispkgpattern(lpp->lp_name)) { + if (findmatchingname(".", lpp->lp_name, note_whats_installed, installed) != 1) { + warnx("%s appears to have been deleted", lpp->lp_name); + continue; + } + rm_installed = 1; + } else { + if (!fexists(lpp->lp_name)) { + warnx("%s appears to have been deleted", lpp->lp_name); + continue; + } + } + + /* return home for execution of command */ + if (chdir(home) == FAIL) { + warnx("unable to change directory to %s, deinstall failed (2)", home); + fail = 1; + break; + } + + if (Verbose) + printf("deinstalling %s\n", rm_installed?installed:lpp->lp_name); + + /* delete the package */ + if (Fake) + rv = 0; + else + rv = vsystem("%s %s %s %s %s %s %s %s %s", ProgramPath, + Prefix ? "-p" : "", + Prefix ? Prefix : "", + Verbose ? "-v" : "", + Force ? "-f" : "", + NoDeInstall ? "-D" : "", + CleanDirs ? "-d" : "", + Fake ? "-n" : "", + rm_installed?installed:lpp->lp_name); + + /* check for delete failure */ + if (rv && !tryall) { + fail = 1; + warnx("had problem removing %s%s", rm_installed?installed:lpp->lp_name, + Force ? ", continuing" : ""); + if (!Force) + break; + } + } + + /* cleanup list */ + while ((lpp = TAILQ_FIRST(&lpdelq))) { + TAILQ_REMOVE(&lpdelq, lpp, lp_link); + free_lpkg(lpp); + } + + /* return to the log dir */ + if (fchdir(oldcwd) == FAIL) { + warnx("unable to change to previous directory, deinstall failed"); + fail = 1; + } + close(oldcwd); + + return (fail); +} + +/* + * Recursively find all packages "up" the tree (follow +REQUIRED_BY). + * Return 1 on errors + */ +int +require_find_recursive_up(lpkg_t *thislpp) +{ + lpkg_head_t reqq; + lpkg_t *lpp = NULL; + FILE *cfile; + char *nl, *tmp; + + /* see if we are on the find queue -- circular dependency */ + if ((lpp = find_on_queue(&lpfindq, thislpp->lp_name))) { + warnx("circular dependency found for pkg %s", lpp->lp_name); + return (1); + } + + TAILQ_INIT(&reqq); + + (void) snprintf(pkgdir, sizeof(pkgdir), "%s/%s", + (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR, thislpp->lp_name); + + /* change to package's dir */ + if (chdir(pkgdir) == FAIL) { + warnx("unable to change directory to %s! deinstall failed", pkgdir); + return (1); + } + + /* terminate recursion if no required by's */ + if (isemptyfile(REQUIRED_BY_FNAME)) + return (0); + + /* get packages that directly require us */ + cfile = fopen(REQUIRED_BY_FNAME, "r"); + if (!cfile) { + warnx("cannot open requirements file `%s'", REQUIRED_BY_FNAME); + return (1); + } + while (fgets(linebuf, sizeof(linebuf), cfile)) { + if ((nl = strrchr(linebuf, '\n'))) + *nl = 0; + lpp = alloc_lpkg(linebuf); + TAILQ_INSERT_TAIL(&reqq, lpp, lp_link); + } + fclose(cfile); + + /* put ourselves on the top of the find queue */ + TAILQ_INSERT_HEAD(&lpfindq, thislpp, lp_link); + + while ((lpp = TAILQ_FIRST(&reqq))) { + /* remove a direct req from our queue */ + TAILQ_REMOVE(&reqq, lpp, lp_link); + + /* find direct required requires */ + if (require_find_recursive_up(lpp)) + goto fail; + + /* all requires taken care of, add to tail of delete queue + * if not already there */ + if (find_on_queue(&lpdelq, lpp->lp_name)) + free_lpkg(lpp); + else + TAILQ_INSERT_TAIL(&lpdelq, lpp, lp_link); + } + + /* take ourselves off the find queue */ + TAILQ_REMOVE(&lpfindq, thislpp, lp_link); + + return (0); + +fail: + while ((lpp = TAILQ_FIRST(&reqq))) { + TAILQ_REMOVE(&reqq, lpp, lp_link); + free_lpkg(lpp); + } + return (1); +} + +/* + * Recursively find all packages "down" the tree (follow @pkgdep). + * Return 1 on errors + */ +int +require_find_recursive_down(lpkg_t *thislpp, package_t *plist) +{ + plist_t *p; + lpkg_t *lpp, *lpp2; + lpkg_head_t reqq; + int rc, fail = 0; + + /* see if we are on the find queue -- circular dependency */ + if ((lpp = find_on_queue(&lpfindq, thislpp->lp_name))) { + warnx("circular dependency found for pkg %s", lpp->lp_name); + return (1); + } + + TAILQ_INIT(&reqq); + + /* width-first scan */ + /* first enqueue all @pkgdep's to lpdelq, then (further below) + * go in recursively */ + for (p = plist->head; p; p = p->next) { + switch (p->type) { + case PLIST_PKGDEP: + lpp = alloc_lpkg(p->name); + TAILQ_INSERT_TAIL(&reqq, lpp, lp_link); + + lpp2 = find_on_queue(&lpdelq, p->name); + if (lpp2) { + TAILQ_REMOVE(&lpdelq, lpp2, lp_link); + free_lpkg(lpp2); + } + lpp = alloc_lpkg(p->name); + TAILQ_INSERT_TAIL(&lpdelq, lpp, lp_link); + + break; + default: + break; + } + } + + while ((lpp = TAILQ_FIRST(&reqq))) { + FILE *cfile; + package_t rPlist; + char *tmp; + + /* remove a direct req from our queue */ + TAILQ_REMOVE(&reqq, lpp, lp_link); + + /* Reset some state */ + rPlist.head = NULL; + rPlist.tail = NULL; + + /* prepare for recursion */ + chdir ((tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR); + if (ispkgpattern(lpp->lp_name)) { + char installed[FILENAME_MAX]; + if (findmatchingname(".", lpp->lp_name, note_whats_installed, installed) != 1) { + warnx("cannot remove dependency for pkg-pattern %s", lpp->lp_name); + fail = 1; + goto fail; + } + if (chdir(installed) == -1) { + warnx("can't chdir to %s", installed); + fail = 1; + goto fail; + } + sanity_check(installed); + } else { + if (chdir(lpp->lp_name) == -1) { + warnx("cannot remove dependency from %s", lpp->lp_name); + fail = 1; + goto fail; + } + sanity_check(lpp->lp_name); + } + + cfile = fopen(CONTENTS_FNAME, "r"); + if (!cfile) { + warn("unable to open '%s' file", CONTENTS_FNAME); + fail = 1; + goto fail; + } + /* If we have a prefix, add it now */ + if (Prefix) + add_plist(&rPlist, PLIST_CWD, Prefix); + read_plist(&rPlist, cfile); + fclose(cfile); + p = find_plist(&rPlist, PLIST_CWD); + if (!p) { + warnx("package '%s' doesn't have a prefix", lpp->lp_name); + free_plist(&rPlist); + fail = 1; + goto fail; + } + + /* put ourselves on the top of the find queue */ + TAILQ_INSERT_HEAD(&lpfindq, thislpp, lp_link); + + rc = require_find_recursive_down(lpp, &rPlist); + free_plist(&rPlist); + if (rc) { + fail = 1; + goto fail; + } + + /* take ourselves off the find queue */ + TAILQ_REMOVE(&lpfindq, thislpp, lp_link); + free_lpkg(lpp); + } + +fail: + /* Clean out reqq */ + while ((lpp = TAILQ_FIRST(&reqq))) { + TAILQ_REMOVE(&reqq, lpp, lp_link); + free_lpkg(lpp); + } + + return fail; +} + +/* + * Start recursion in the one or other direction. + */ +int +require_find(char *pkg, rec_find_t updown) +{ + lpkg_t *lpp; + int rv = 0; + + TAILQ_INIT(&lpfindq); + TAILQ_INIT(&lpdelq); + + lpp = alloc_lpkg(pkg); + switch (updown) { + case FIND_UP: + rv = require_find_recursive_up(lpp); + break; + case FIND_DOWN: + rv = require_find_recursive_down(lpp, &Plist); + break; + } + free_lpkg(lpp); + + return (rv); +} + +void +require_print(void) +{ + lpkg_t *lpp; + + /* print all but last -- deleting if requested */ + while ((lpp = TAILQ_FIRST(&lpdelq))) { + TAILQ_REMOVE(&lpdelq, lpp, lp_link); + fprintf(stderr, "\t%s\n", lpp->lp_name); + free_lpkg(lpp); + } +} + +/* + * This is seriously ugly code following. Written very fast! + */ +static int +pkg_do(char *pkg) +{ + FILE *cfile; + char home[FILENAME_MAX]; + plist_t *p; + char *tmp; + + /* Reset some state */ + if (Plist.head) + free_plist(&Plist); + + (void) snprintf(LogDir, sizeof(LogDir), "%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR, + pkg); + if (!fexists(LogDir) || !isdir(LogDir)) { + { + /* Check if the given package name matches something + * with 'pkg-[0-9]*' */ + char try[FILENAME_MAX]; + snprintf(try, FILENAME_MAX, "%s-[0-9]*", pkg); + if (findmatchingname(_pkgdb_getPKGDB_DIR(), try, + add_to_list_fn, &pkgs) != 0) { + return 0; /* we've just appended some names to the pkgs list, + * they will be processed after this package. */ + } + } + + /* No match */ + warnx("package '%s' not installed", pkg); + return 1; + } + if (!getcwd(home, FILENAME_MAX)) { + cleanup(0); + errx(2, "unable to get current working directory!"); + } + if (chdir(LogDir) == FAIL) { + warnx("unable to change directory to %s! deinstall failed", LogDir); + return 1; + } + if (!isemptyfile(REQUIRED_BY_FNAME)) { + /* This package is required by others. Either nuke + * them (-r), or stop. */ + if (!Recurse_up) + warnx("package `%s' is required by other packages:", pkg); + else if (Verbose) + printf("Building list of packages that require `%s'" + " to deinstall\n", pkg); + if (require_find(pkg, FIND_UP)) { + if (!Force || Recurse_up) + return (1); + } + chdir(LogDir); /* CWD was changed by require_find() */ + if (!Recurse_up) { + require_print(); + if (!Force) + return 1; + } else + require_delete(home, 0); + } + sanity_check(LogDir); + cfile = fopen(CONTENTS_FNAME, "r"); + if (!cfile) { + warnx("unable to open '%s' file", CONTENTS_FNAME); + return 1; + } + /* If we have a prefix, add it now */ + if (Prefix) + add_plist(&Plist, PLIST_CWD, Prefix); + read_plist(&Plist, cfile); + fclose(cfile); + p = find_plist(&Plist, PLIST_CWD); + if (!p) { + warnx("package '%s' doesn't have a prefix", pkg); + return 1; + } + setenv(PKG_PREFIX_VNAME, p->name, 1); + if (fexists(REQUIRE_FNAME)) { + if (Verbose) + printf("Executing 'require' script.\n"); + vsystem("%s +x %s", CHMOD_CMD, REQUIRE_FNAME); /* be sure */ + if (vsystem("./%s %s DEINSTALL", REQUIRE_FNAME, pkg)) { + warnx("package %s fails requirements %s", pkg, + Force ? "" : "- not deleted"); + if (!Force) + return 1; + } + } + if (!NoDeInstall && fexists(DEINSTALL_FNAME)) { + if (Fake) + printf("Would execute de-install script at this point (arg: DEINSTALL).\n"); + else { + vsystem("%s +x %s", CHMOD_CMD, DEINSTALL_FNAME); /* make sure */ + if (vsystem("./%s %s DEINSTALL", DEINSTALL_FNAME, pkg)) { + warnx("deinstall script returned error status"); + if (!Force) + return 1; + } + } + } + if (!Fake) { + /* Some packages aren't packed right, so we need to just ignore delete_package()'s status. Ugh! :-( */ + if (delete_package(FALSE, CleanDirs, &Plist) == FAIL) + warnx( + "couldn't entirely delete package `%s'\n" + "(perhaps the packing list is incorrectly specified?)", pkg); + } + /* Remove this package from the +REQUIRED_BY list of the packages this depends on */ + for (p = Plist.head; p; p = p->next) { + if (p->type != PLIST_PKGDEP) + continue; + if (Verbose) + printf("Attempting to remove dependency on package `%s'\n", p->name); + if (!Fake) + findmatchingname((tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR, + p->name, undepend, pkg); + } + if (Recurse_down) { + /* Also remove the packages further down, now that there's + * (most likely) nothing left which requires them. */ + if (Verbose) + printf("Building list of packages that `%s' required\n", pkg); + if (require_find(pkg, FIND_DOWN)) + return (1); + + require_delete(home, 1); + } + if (!NoDeInstall && fexists(DEINSTALL_FNAME)) { + if (Fake) + printf("Would execute post-de-install script at this point (arg: POST-DEINSTALL).\n"); + else { + vsystem("chmod +x %s", DEINSTALL_FNAME); /* make sure */ + if (vsystem("./%s %s POST-DEINSTALL", DEINSTALL_FNAME, pkg)) { + warnx("post-deinstall script returned error status"); + if (!Force) + return 1; + } + } + } + /* Change out of LogDir before we remove it. + * Do not fail here, as the package is not yet completely deleted! */ + if (chdir(home) == FAIL) + warnx("Oops - removed current working directory. Oh, well."); + if (!Fake) { + /* Finally nuke the +-files and the pkgdb-dir (/var/db/pkg/foo) */ + if (vsystem("%s -r %s", RM, LogDir)) { + warnx("couldn't remove log entry in %s, deinstall failed", LogDir); + if (!Force) + return 1; + } + } + return 0; +} + +int +pkg_perform(lpkg_head_t *pkghead) +{ + int err_cnt = 0; + int oldcwd; + lpkg_t *lpp; + + /* save cwd */ + oldcwd = open(".", O_RDONLY, 0); + if (oldcwd == -1) + err(1, "cannot open \".\""); + + while ((lpp = TAILQ_FIRST(pkghead))) { + err_cnt += pkg_do(lpp->lp_name); + TAILQ_REMOVE(pkghead, lpp, lp_link); + free_lpkg(lpp); + if (fchdir(oldcwd) == FAIL) + err(1, "unable to change to previous directory"); + } + close(oldcwd); + return err_cnt; +} |