Various fixes and improvements:

- fix harmless compiler's warnings (unused variables and missed prototype);
- before refusing to delete package because "there are packages installed
  that require this package" check that packages in question is actually
  installed;
- add new `-r' option to pkg_delete(8), which instructs it to delete not only
  packages specified at command line, but all packages that depend on
  specified packages as well.

MFC after:	2 weeks
This commit is contained in:
Maxim Sobolev 2001-09-19 08:06:48 +00:00
parent 096f9ef53b
commit a09c8e438b
8 changed files with 188 additions and 81 deletions

View File

@ -27,6 +27,7 @@ static const char rcsid[] =
#include "create.h" #include "create.h"
#include <err.h> #include <err.h>
#include <libgen.h>
#include <signal.h> #include <signal.h>
#include <sys/syslimits.h> #include <sys/syslimits.h>
#include <sys/wait.h> #include <sys/wait.h>

View File

@ -28,6 +28,7 @@ extern Boolean CleanDirs;
extern Boolean Interactive; extern Boolean Interactive;
extern Boolean NoDeInstall; extern Boolean NoDeInstall;
extern Boolean Force; extern Boolean Force;
extern Boolean Recursive;
extern char *Directory; extern char *Directory;
extern char *PkgName; extern char *PkgName;
extern match_t MatchType; extern match_t MatchType;

View File

@ -30,12 +30,13 @@ static const char rcsid[] =
#include "lib.h" #include "lib.h"
#include "delete.h" #include "delete.h"
static char Options[] = "adDfGhinp:vx"; static char Options[] = "adDfGhinp:rvx";
char *Prefix = NULL; char *Prefix = NULL;
Boolean CleanDirs = FALSE; Boolean CleanDirs = FALSE;
Boolean Interactive = FALSE; Boolean Interactive = FALSE;
Boolean NoDeInstall = FALSE; Boolean NoDeInstall = FALSE;
Boolean Recursive = FALSE;
match_t MatchType = MATCH_GLOB; match_t MatchType = MATCH_GLOB;
static void usage __P((void)); static void usage __P((void));
@ -93,6 +94,10 @@ main(int argc, char **argv)
Interactive = TRUE; Interactive = TRUE;
break; break;
case 'r':
Recursive = TRUE;
break;
case 'h': case 'h':
case '?': case '?':
default: default:
@ -148,7 +153,7 @@ static void
usage() usage()
{ {
fprintf(stderr, "%s\n%s\n", 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]"); " pkg_delete -a [flags]");
exit(1); exit(1);
} }

View File

@ -36,10 +36,11 @@ static char LogDir[FILENAME_MAX];
int int
pkg_perform(char **pkgs) pkg_perform(char **pkgs)
{ {
char **matched; char **matched, **rb, **rbtmp;
int i; int errcode, i, j;
int err_cnt = 0; int err_cnt = 0;
int errcode; struct reqr_by_entry *rb_entry;
struct reqr_by_head *rb_list;
if (MatchType != MATCH_EXACT) { if (MatchType != MATCH_EXACT) {
matched = matchinstalled(MatchType, pkgs, &errcode); matched = matchinstalled(MatchType, pkgs, &errcode);
@ -65,6 +66,40 @@ pkg_perform(char **pkgs)
err_cnt += sortdeps(pkgs); err_cnt += sortdeps(pkgs);
for (i = 0; pkgs[i]; i++) { 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]); err_cnt += pkg_do(pkgs[i]);
} }
@ -80,13 +115,14 @@ pkg_do(char *pkg)
FILE *cfile; FILE *cfile;
char home[FILENAME_MAX]; char home[FILENAME_MAX];
PackingList p; PackingList p;
char *tmp;
int len; int len;
/* support for separate pre/post install scripts */ /* support for separate pre/post install scripts */
int new_m = 0; int new_m = 0;
char pre_script[FILENAME_MAX] = DEINSTALL_FNAME; char pre_script[FILENAME_MAX] = DEINSTALL_FNAME;
char post_script[FILENAME_MAX]; char post_script[FILENAME_MAX];
char pre_arg[FILENAME_MAX], post_arg[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))) if (!pkg || !(len = strlen(pkg)))
return 1; return 1;
@ -127,18 +163,14 @@ pkg_do(char *pkg)
/* Not reached */ /* Not reached */
} }
if (!isemptyfile(REQUIRED_BY_FNAME)) { if (requiredby(pkg, &rb_list, FALSE, TRUE) < 0)
char buf[512]; return 1;
if (!STAILQ_EMPTY(rb_list)) {
warnx("package '%s' is required by these other packages\n" warnx("package '%s' is required by these other packages\n"
"and may not be deinstalled%s:", "and may not be deinstalled%s:",
pkg, Force ? " (but I'll delete it anyway)" : "" ); pkg, Force ? " (but I'll delete it anyway)" : "");
cfile = fopen(REQUIRED_BY_FNAME, "r"); STAILQ_FOREACH(rb_entry, rb_list, link)
if (cfile) { fprintf(stderr, "%s\n", rb_entry->pkgname);
while (fgets(buf, sizeof(buf), cfile))
fprintf(stderr, "%s", buf);
fclose(cfile);
} else
warnx("cannot open requirements file '%s'", REQUIRED_BY_FNAME);
if (!Force) if (!Force)
return 1; return 1;
} }
@ -283,54 +315,44 @@ cleanup(int sig)
static void static void
undepend(PackingList p, char *pkgname) undepend(PackingList p, char *pkgname)
{ {
char fname[FILENAME_MAX], ftmp[FILENAME_MAX]; char fname[FILENAME_MAX], ftmp[FILENAME_MAX];
char fbuf[FILENAME_MAX]; FILE *fpwr;
FILE *fp, *fpwr; int s;
char *tmp; struct reqr_by_entry *rb_entry;
int s; struct reqr_by_head *rb_list;
sprintf(fname, "%s/%s/%s", LOG_DIR, p->name, REQUIRED_BY_FNAME);
fp = fopen(fname, "r"); if (requiredby(p->name, &rb_list, Verbose, FALSE) <= 0)
if (fp == NULL) { return;
warnx("couldn't open dependency file '%s'", fname); snprintf(fname, sizeof(fname), "%s/%s/%s", LOG_DIR, p->name,
return; REQUIRED_BY_FNAME);
} snprintf(ftmp, sizeof(ftmp), "%s.XXXXXX", fname);
sprintf(ftmp, "%s.XXXXXX", fname); s = mkstemp(ftmp);
s = mkstemp(ftmp); if (s == -1) {
if (s == -1) { warnx("couldn't open temp file '%s'", ftmp);
fclose(fp); return;
warnx("couldn't open temp file '%s'", ftmp); }
return; fpwr = fdopen(s, "w");
} if (fpwr == NULL) {
fpwr = fdopen(s, "w"); close(s);
if (fpwr == NULL) { warnx("couldn't fdopen temp file '%s'", ftmp);
close(s); goto cleanexit;
fclose(fp); }
warnx("couldn't fdopen temp file '%s'", ftmp); STAILQ_FOREACH(rb_entry, rb_list, link)
remove(ftmp); if (strcmp(rb_entry->pkgname, pkgname)) /* no match */
return; fputs(rb_entry->pkgname, fpwr), putc('\n', fpwr);
} if (fchmod(s, 0644) == FAIL) {
while (fgets(fbuf, sizeof(fbuf), fp) != NULL) { warnx("error changing permission of temp file '%s'", ftmp);
if (fbuf[strlen(fbuf)-1] == '\n') fclose(fpwr);
fbuf[strlen(fbuf)-1] = '\0'; goto cleanexit;
if (strcmp(fbuf, pkgname)) /* no match */ }
fputs(fbuf, fpwr), putc('\n', fpwr); if (fclose(fpwr) == EOF) {
} warnx("error closing temp file '%s'", ftmp);
(void) fclose(fp); goto cleanexit;
if (fchmod(s, 0644) == FAIL) { }
warnx("error changing permission of temp file '%s'", ftmp); if (rename(ftmp, fname) == -1)
fclose(fpwr); warnx("error renaming '%s' to '%s'", ftmp, fname);
remove(ftmp); cleanexit:
return; 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;
} }

View File

@ -25,7 +25,7 @@
.Nd a utility for deleting previously installed software package distributions .Nd a utility for deleting previously installed software package distributions
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl dDfGinvx .Op Fl dDfGinrvx
.Op Fl p Ar prefix .Op Fl p Ar prefix
.Ar pkg-name ... .Ar pkg-name ...
.Nm .Nm
@ -119,6 +119,9 @@ provided, in that case
.Nm .Nm
deletes all packages that match at least one deletes all packages that match at least one
regular expression from the list. 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 .El
.Sh TECHNICAL DETAILS .Sh TECHNICAL DETAILS
.Nm .Nm

View File

@ -143,8 +143,6 @@ pkg_do(char *pkg)
} }
/* It's not an ininstalled package, try and find it among the installed */ /* It's not an ininstalled package, try and find it among the installed */
else { else {
char *tmp;
sprintf(log_dir, "%s/%s", LOG_DIR, pkg); sprintf(log_dir, "%s/%s", LOG_DIR, pkg);
if (!fexists(log_dir)) { if (!fexists(log_dir)) {
warnx("can't find package '%s' installed or in a file!", pkg); warnx("can't find package '%s' installed or in a file!", pkg);

View File

@ -82,29 +82,98 @@ sortdeps(char **pkgs)
int int
chkifdepends(char *pkgname1, char *pkgname2) chkifdepends(char *pkgname1, char *pkgname2)
{ {
FILE *fp; char pkgdir[FILENAME_MAX];
char fname[FILENAME_MAX]; int errcode;
char fbuf[FILENAME_MAX]; struct reqr_by_entry *rb_entry;
char *tmp; struct reqr_by_head *rb_list;
int retval;
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"); fp = fopen(fname, "r");
if (fp == NULL) { 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; return 0;
} }
retval = 0; retval = 0;
while (fgets(fbuf, sizeof(fbuf), fp) != NULL) { while (fgets(fbuf, sizeof(fbuf), fp) != NULL) {
if (fbuf[strlen(fbuf)-1] == '\n') if (fbuf[strlen(fbuf) - 1] == '\n')
fbuf[strlen(fbuf)-1] = '\0'; fbuf[strlen(fbuf) - 1] = '\0';
if (strcmp(fbuf, pkgname1) == 0) { /* match */ snprintf(pkgdir, sizeof(pkgdir), "%s/%s", LOG_DIR, fbuf);
retval = 1; 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; break;
} }
strlcpy(rb_entry->pkgname, fbuf, sizeof(rb_entry->pkgname));
STAILQ_INSERT_TAIL(&rb_list, rb_entry, link);
} }
fclose(fp); fclose(fp);
return retval; return retval;
} }

View File

@ -27,6 +27,7 @@
#include <sys/param.h> #include <sys/param.h>
#include <sys/file.h> #include <sys/file.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/queue.h>
#include <ctype.h> #include <ctype.h>
#include <dirent.h> #include <dirent.h>
#include <stdarg.h> #include <stdarg.h>
@ -111,6 +112,12 @@ struct _pack {
}; };
typedef struct _pack Package; 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 */ /* Prototypes */
/* Misc */ /* Misc */
int vsystem(const char *, ...); int vsystem(const char *, ...);
@ -183,6 +190,7 @@ char **matchinstalled(match_t, char **, int *);
/* Dependencies */ /* Dependencies */
int sortdeps(char **); int sortdeps(char **);
int chkifdepends(char *, char *); int chkifdepends(char *, char *);
int requiredby(const char *, struct reqr_by_head **, Boolean, Boolean);
/* Externs */ /* Externs */
extern Boolean Verbose; extern Boolean Verbose;