diff --git a/usr.sbin/pkg_install/create/perform.c b/usr.sbin/pkg_install/create/perform.c index 252a4570cd1c..30678bbb2838 100644 --- a/usr.sbin/pkg_install/create/perform.c +++ b/usr.sbin/pkg_install/create/perform.c @@ -27,6 +27,7 @@ static const char rcsid[] = #include "create.h" #include +#include #include #include #include diff --git a/usr.sbin/pkg_install/delete/delete.h b/usr.sbin/pkg_install/delete/delete.h index faa2e0649c47..a9736426e507 100644 --- a/usr.sbin/pkg_install/delete/delete.h +++ b/usr.sbin/pkg_install/delete/delete.h @@ -28,6 +28,7 @@ extern Boolean CleanDirs; extern Boolean Interactive; extern Boolean NoDeInstall; extern Boolean Force; +extern Boolean Recursive; extern char *Directory; extern char *PkgName; extern match_t MatchType; diff --git a/usr.sbin/pkg_install/delete/main.c b/usr.sbin/pkg_install/delete/main.c index 2dba240b5469..5538838e4598 100644 --- a/usr.sbin/pkg_install/delete/main.c +++ b/usr.sbin/pkg_install/delete/main.c @@ -30,12 +30,13 @@ static const char rcsid[] = #include "lib.h" #include "delete.h" -static char Options[] = "adDfGhinp:vx"; +static char Options[] = "adDfGhinp:rvx"; char *Prefix = NULL; Boolean CleanDirs = FALSE; Boolean Interactive = FALSE; Boolean NoDeInstall = FALSE; +Boolean Recursive = FALSE; match_t MatchType = MATCH_GLOB; static void usage __P((void)); @@ -93,6 +94,10 @@ main(int argc, char **argv) Interactive = TRUE; break; + case 'r': + Recursive = TRUE; + break; + case 'h': case '?': default: @@ -148,7 +153,7 @@ static void usage() { fprintf(stderr, "%s\n%s\n", - "usage: pkg_delete [-dDfGinvx] [-p prefix] pkg-name ...", + "usage: pkg_delete [-dDfGinrvx] [-p prefix] pkg-name ...", " pkg_delete -a [flags]"); exit(1); } diff --git a/usr.sbin/pkg_install/delete/perform.c b/usr.sbin/pkg_install/delete/perform.c index 21815f6e65e0..dfe7c91ed862 100644 --- a/usr.sbin/pkg_install/delete/perform.c +++ b/usr.sbin/pkg_install/delete/perform.c @@ -36,10 +36,11 @@ static char LogDir[FILENAME_MAX]; int pkg_perform(char **pkgs) { - char **matched; - int i; + char **matched, **rb, **rbtmp; + int errcode, i, j; int err_cnt = 0; - int errcode; + struct reqr_by_entry *rb_entry; + struct reqr_by_head *rb_list; if (MatchType != MATCH_EXACT) { matched = matchinstalled(MatchType, pkgs, &errcode); @@ -65,6 +66,40 @@ pkg_perform(char **pkgs) err_cnt += sortdeps(pkgs); for (i = 0; pkgs[i]; i++) { + if (Recursive == TRUE) { + errcode = requiredby(pkgs[i], &rb_list, FALSE, TRUE); + if (errcode < 0) { + err_cnt++; + } else if (errcode > 0) { + /* + * Copy values from the rb_list queue into argv-like NULL + * terminated list because requiredby() uses some static + * storage, while pkg_do() below will call this function, + * thus blowing our rb_list away. + */ + rbtmp = rb = alloca((errcode + 1) * sizeof(*rb)); + if (rb == NULL) { + warnx("%s(): alloca() failed", __FUNCTION__); + err_cnt++; + continue; + } + STAILQ_FOREACH(rb_entry, rb_list, link) { + *rbtmp = alloca(strlen(rb_entry->pkgname) + 1); + if (*rbtmp == NULL) { + warnx("%s(): alloca() failed", __FUNCTION__); + err_cnt++; + continue; + } + strcpy(*rbtmp, rb_entry->pkgname); + rbtmp++; + } + *rbtmp = NULL; + + err_cnt += sortdeps(rb); + for (j = 0; rb[j]; j++) + err_cnt += pkg_do(rb[j]); + } + } err_cnt += pkg_do(pkgs[i]); } @@ -80,13 +115,14 @@ pkg_do(char *pkg) FILE *cfile; char home[FILENAME_MAX]; PackingList p; - char *tmp; int len; /* support for separate pre/post install scripts */ int new_m = 0; char pre_script[FILENAME_MAX] = DEINSTALL_FNAME; char post_script[FILENAME_MAX]; char pre_arg[FILENAME_MAX], post_arg[FILENAME_MAX]; + struct reqr_by_entry *rb_entry; + struct reqr_by_head *rb_list; if (!pkg || !(len = strlen(pkg))) return 1; @@ -127,18 +163,14 @@ pkg_do(char *pkg) /* Not reached */ } - if (!isemptyfile(REQUIRED_BY_FNAME)) { - char buf[512]; + if (requiredby(pkg, &rb_list, FALSE, TRUE) < 0) + return 1; + if (!STAILQ_EMPTY(rb_list)) { warnx("package '%s' is required by these other packages\n" - "and may not be deinstalled%s:", - pkg, Force ? " (but I'll delete it anyway)" : "" ); - cfile = fopen(REQUIRED_BY_FNAME, "r"); - if (cfile) { - while (fgets(buf, sizeof(buf), cfile)) - fprintf(stderr, "%s", buf); - fclose(cfile); - } else - warnx("cannot open requirements file '%s'", REQUIRED_BY_FNAME); + "and may not be deinstalled%s:", + pkg, Force ? " (but I'll delete it anyway)" : ""); + STAILQ_FOREACH(rb_entry, rb_list, link) + fprintf(stderr, "%s\n", rb_entry->pkgname); if (!Force) return 1; } @@ -283,54 +315,44 @@ cleanup(int sig) static void undepend(PackingList p, char *pkgname) { - char fname[FILENAME_MAX], ftmp[FILENAME_MAX]; - char fbuf[FILENAME_MAX]; - FILE *fp, *fpwr; - char *tmp; - int s; + char fname[FILENAME_MAX], ftmp[FILENAME_MAX]; + FILE *fpwr; + int s; + struct reqr_by_entry *rb_entry; + struct reqr_by_head *rb_list; - sprintf(fname, "%s/%s/%s", LOG_DIR, p->name, REQUIRED_BY_FNAME); - fp = fopen(fname, "r"); - if (fp == NULL) { - warnx("couldn't open dependency file '%s'", fname); - return; - } - sprintf(ftmp, "%s.XXXXXX", fname); - s = mkstemp(ftmp); - if (s == -1) { - fclose(fp); - warnx("couldn't open temp file '%s'", ftmp); - return; - } - fpwr = fdopen(s, "w"); - if (fpwr == NULL) { - close(s); - fclose(fp); - warnx("couldn't fdopen temp file '%s'", ftmp); - remove(ftmp); - return; - } - while (fgets(fbuf, sizeof(fbuf), fp) != NULL) { - if (fbuf[strlen(fbuf)-1] == '\n') - fbuf[strlen(fbuf)-1] = '\0'; - if (strcmp(fbuf, pkgname)) /* 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; - } - if (fclose(fpwr) == EOF) { - warnx("error closing temp file '%s'", ftmp); - remove(ftmp); - return; - } - if (rename(ftmp, fname) == -1) - warnx("error renaming '%s' to '%s'", ftmp, fname); - remove(ftmp); /* just in case */ - return; + + if (requiredby(p->name, &rb_list, Verbose, FALSE) <= 0) + return; + snprintf(fname, sizeof(fname), "%s/%s/%s", LOG_DIR, p->name, + REQUIRED_BY_FNAME); + snprintf(ftmp, sizeof(ftmp), "%s.XXXXXX", fname); + s = mkstemp(ftmp); + if (s == -1) { + warnx("couldn't open temp file '%s'", ftmp); + return; + } + fpwr = fdopen(s, "w"); + if (fpwr == NULL) { + close(s); + warnx("couldn't fdopen temp file '%s'", ftmp); + goto cleanexit; + } + STAILQ_FOREACH(rb_entry, rb_list, link) + if (strcmp(rb_entry->pkgname, pkgname)) /* no match */ + fputs(rb_entry->pkgname, fpwr), putc('\n', fpwr); + if (fchmod(s, 0644) == FAIL) { + warnx("error changing permission of temp file '%s'", ftmp); + fclose(fpwr); + goto cleanexit; + } + if (fclose(fpwr) == EOF) { + warnx("error closing temp file '%s'", ftmp); + goto cleanexit; + } + if (rename(ftmp, fname) == -1) + warnx("error renaming '%s' to '%s'", ftmp, fname); +cleanexit: + remove(ftmp); + return; } - diff --git a/usr.sbin/pkg_install/delete/pkg_delete.1 b/usr.sbin/pkg_install/delete/pkg_delete.1 index c998bb375586..947939b8c480 100644 --- a/usr.sbin/pkg_install/delete/pkg_delete.1 +++ b/usr.sbin/pkg_install/delete/pkg_delete.1 @@ -25,7 +25,7 @@ .Nd a utility for deleting previously installed software package distributions .Sh SYNOPSIS .Nm -.Op Fl dDfGinvx +.Op Fl dDfGinrvx .Op Fl p Ar prefix .Ar pkg-name ... .Nm @@ -119,6 +119,9 @@ provided, in that case .Nm deletes all packages that match at least one regular expression from the list. +.It Fl r +Recursive removal. In addition to specified packages, delete all +packages that depend on those packages as well. .El .Sh TECHNICAL DETAILS .Nm diff --git a/usr.sbin/pkg_install/info/perform.c b/usr.sbin/pkg_install/info/perform.c index a4aa8292ff84..5074b1b5b8ec 100644 --- a/usr.sbin/pkg_install/info/perform.c +++ b/usr.sbin/pkg_install/info/perform.c @@ -143,8 +143,6 @@ pkg_do(char *pkg) } /* It's not an ininstalled package, try and find it among the installed */ else { - char *tmp; - sprintf(log_dir, "%s/%s", LOG_DIR, pkg); if (!fexists(log_dir)) { warnx("can't find package '%s' installed or in a file!", pkg); diff --git a/usr.sbin/pkg_install/lib/deps.c b/usr.sbin/pkg_install/lib/deps.c index 802d6c8de790..cd5220957ee1 100644 --- a/usr.sbin/pkg_install/lib/deps.c +++ b/usr.sbin/pkg_install/lib/deps.c @@ -82,29 +82,98 @@ sortdeps(char **pkgs) int chkifdepends(char *pkgname1, char *pkgname2) { - FILE *fp; - char fname[FILENAME_MAX]; - char fbuf[FILENAME_MAX]; - char *tmp; - int retval; + char pkgdir[FILENAME_MAX]; + int errcode; + struct reqr_by_entry *rb_entry; + struct reqr_by_head *rb_list; - sprintf(fname, "%s/%s/%s", LOG_DIR, pkgname2, REQUIRED_BY_FNAME); + /* Check that pkgname2 is actually installed */ + snprintf(pkgdir, sizeof(pkgdir), "%s/%s", LOG_DIR, pkgname2); + if (!isdir(pkgdir)) + return 0; + + errcode = requiredby(pkgname2, &rb_list, FALSE, TRUE); + if (errcode < 0) + return errcode; + + STAILQ_FOREACH(rb_entry, rb_list, link) + if (strcmp(rb_entry->pkgname, pkgname1) == 0) /* match */ + return 1; + + return 0; +} + +/* + * Load +REQUIRED_BY file and return a list with names of + * packages that require package reffered to by `pkgname'. + * + * Optionally check that packages listed there are actually + * installed and filter out those that don't (filter == TRUE). + * + * strict argument controls whether the caller want warnings + * to be emitted when there are some non-fatal conditions, + * i.e. package doesn't have +REQUIRED_BY file or some packages + * listed in +REQUIRED_BY don't exist. + * + * Result returned in the **list, while return value is equal + * to the number of entries in the resulting list. Print error + * message and return -1 on error. + */ +int +requiredby(const char *pkgname, struct reqr_by_head **list, Boolean strict, Boolean filter) +{ + FILE *fp; + char fbuf[FILENAME_MAX], fname[FILENAME_MAX], pkgdir[FILENAME_MAX]; + int retval; + struct reqr_by_entry *rb_entry; + static struct reqr_by_head rb_list = STAILQ_HEAD_INITIALIZER(rb_list); + + *list = &rb_list; + /* Deallocate any previously allocated space */ + while (!STAILQ_EMPTY(&rb_list)) { + rb_entry = STAILQ_FIRST(&rb_list); + STAILQ_REMOVE_HEAD(&rb_list, link); + free(rb_entry); + } + + snprintf(fname, sizeof(fname), "%s/%s", LOG_DIR, pkgname); + if (!isdir(fname)) { + if (strict == TRUE) + warnx("no such package '%s' installed", pkgname); + return -1; + } + + snprintf(fname, sizeof(fname), "%s/%s", fname, REQUIRED_BY_FNAME); fp = fopen(fname, "r"); if (fp == NULL) { - /* Probably pkgname2 doesn't have any packages that depend on it */ + /* Probably pkgname doesn't have any packages that depend on it */ + if (strict == TRUE) + warnx("couldn't open dependency file '%s'", fname); return 0; } retval = 0; while (fgets(fbuf, sizeof(fbuf), fp) != NULL) { - if (fbuf[strlen(fbuf)-1] == '\n') - fbuf[strlen(fbuf)-1] = '\0'; - if (strcmp(fbuf, pkgname1) == 0) { /* match */ - retval = 1; + if (fbuf[strlen(fbuf) - 1] == '\n') + fbuf[strlen(fbuf) - 1] = '\0'; + snprintf(pkgdir, sizeof(pkgdir), "%s/%s", LOG_DIR, fbuf); + if (filter == TRUE && !isdir(pkgdir)) { + if (strict == TRUE) + warnx("package '%s' is recorded in the '%s' but isn't " + "actually installed", fbuf, fname); + continue; + } + retval++; + rb_entry = malloc(sizeof(*rb_entry)); + if (rb_entry == NULL) { + warnx("%s(): malloc() failed", __FUNCTION__); + retval = -1; break; } + strlcpy(rb_entry->pkgname, fbuf, sizeof(rb_entry->pkgname)); + STAILQ_INSERT_TAIL(&rb_list, rb_entry, link); } - fclose(fp); + return retval; } diff --git a/usr.sbin/pkg_install/lib/lib.h b/usr.sbin/pkg_install/lib/lib.h index fc41c66fdfbc..ee709cd4a7b2 100644 --- a/usr.sbin/pkg_install/lib/lib.h +++ b/usr.sbin/pkg_install/lib/lib.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -111,6 +112,12 @@ struct _pack { }; typedef struct _pack Package; +struct reqr_by_entry { + STAILQ_ENTRY(reqr_by_entry) link; + char pkgname[PATH_MAX]; +}; +STAILQ_HEAD(reqr_by_head, reqr_by_entry); + /* Prototypes */ /* Misc */ int vsystem(const char *, ...); @@ -183,6 +190,7 @@ char **matchinstalled(match_t, char **, int *); /* Dependencies */ int sortdeps(char **); int chkifdepends(char *, char *); +int requiredby(const char *, struct reqr_by_head **, Boolean, Boolean); /* Externs */ extern Boolean Verbose;