1327Sjkh/*
2327Sjkh * FreeBSD install - a package for the installation and maintainance
3327Sjkh * of non-core utilities.
4327Sjkh *
5327Sjkh * Redistribution and use in source and binary forms, with or without
6327Sjkh * modification, are permitted provided that the following conditions
7327Sjkh * are met:
8327Sjkh * 1. Redistributions of source code must retain the above copyright
9327Sjkh *    notice, this list of conditions and the following disclaimer.
10327Sjkh * 2. Redistributions in binary form must reproduce the above copyright
11327Sjkh *    notice, this list of conditions and the following disclaimer in the
12327Sjkh *    documentation and/or other materials provided with the distribution.
13327Sjkh *
14327Sjkh * Jordan K. Hubbard
15327Sjkh * 18 July 1993
16327Sjkh *
17327Sjkh * This is the main body of the delete module.
18327Sjkh *
19327Sjkh */
20327Sjkh
2193520Sobrien#include <sys/cdefs.h>
2293520Sobrien__FBSDID("$FreeBSD$");
2393520Sobrien
2430221Scharnier#include <err.h>
25222035Sflz#include "lib.h"
26327Sjkh#include "delete.h"
27327Sjkh
28327Sjkhstatic int pkg_do(char *);
29327Sjkhstatic void sanity_check(char *);
3096613Ssobomaxstatic void undepend(char *, char *);
31327Sjkhstatic char LogDir[FILENAME_MAX];
32327Sjkh
33327Sjkh
34327Sjkhint
35327Sjkhpkg_perform(char **pkgs)
36327Sjkh{
3783663Ssobomax    char **matched, **rb, **rbtmp;
3883663Ssobomax    int errcode, i, j;
3972694Ssobomax    int err_cnt = 0;
4083663Ssobomax    struct reqr_by_entry *rb_entry;
4183663Ssobomax    struct reqr_by_head *rb_list;
42327Sjkh
4373134Ssobomax    if (MatchType != MATCH_EXACT) {
4473134Ssobomax	matched = matchinstalled(MatchType, pkgs, &errcode);
4573134Ssobomax	if (errcode != 0)
4673134Ssobomax	    return 1;
4773134Ssobomax	    /* Not reached */
4873134Ssobomax
4996613Ssobomax	/*
5096613Ssobomax	 * Copy matched[] into pkgs[], because we'll need to use
5196613Ssobomax	 * matchinstalled() later on.
5296613Ssobomax	 */
5396613Ssobomax	if (matched != NULL) {
5496613Ssobomax	    pkgs = NULL;
5596613Ssobomax	    for (i = 0; matched[i] != NULL; i++) {
5696613Ssobomax		pkgs = realloc(pkgs, sizeof(*pkgs) * (i + 2));
5796613Ssobomax		pkgs[i] = strdup(matched[i]);
5896613Ssobomax	    }
5996613Ssobomax	    pkgs[i] = NULL;
6096613Ssobomax	}
6173134Ssobomax	else switch (MatchType) {
6273134Ssobomax	    case MATCH_GLOB:
6373134Ssobomax		break;
6473134Ssobomax	    case MATCH_ALL:
6573134Ssobomax		warnx("no packages installed");
6673134Ssobomax		return 0;
67131275Seik	    case MATCH_EREGEX:
6873134Ssobomax	    case MATCH_REGEX:
6973134Ssobomax		warnx("no packages match pattern(s)");
7073134Ssobomax		return 1;
7173134Ssobomax	    default:
7273134Ssobomax		break;
7373134Ssobomax	}
7473134Ssobomax    }
7573134Ssobomax
7674295Ssobomax    err_cnt += sortdeps(pkgs);
7772694Ssobomax    for (i = 0; pkgs[i]; i++) {
7883663Ssobomax	if (Recursive == TRUE) {
7983663Ssobomax	    errcode = requiredby(pkgs[i], &rb_list, FALSE, TRUE);
8083663Ssobomax	    if (errcode < 0) {
8183663Ssobomax		err_cnt++;
8283663Ssobomax	    } else if (errcode > 0) {
8383663Ssobomax		/*
8483663Ssobomax		 * Copy values from the rb_list queue into argv-like NULL
8583663Ssobomax		 * terminated list because requiredby() uses some static
8683663Ssobomax		 * storage, while pkg_do() below will call this function,
8783663Ssobomax		 * thus blowing our rb_list away.
8883663Ssobomax		 */
8983663Ssobomax		rbtmp = rb = alloca((errcode + 1) * sizeof(*rb));
9083663Ssobomax		if (rb == NULL) {
9196392Salfred		    warnx("%s(): alloca() failed", __func__);
9283663Ssobomax		    err_cnt++;
9383663Ssobomax		    continue;
9483663Ssobomax		}
9583663Ssobomax		STAILQ_FOREACH(rb_entry, rb_list, link) {
9683663Ssobomax		    *rbtmp = alloca(strlen(rb_entry->pkgname) + 1);
9783663Ssobomax		    if (*rbtmp == NULL) {
9896392Salfred			warnx("%s(): alloca() failed", __func__);
9983663Ssobomax			err_cnt++;
10083663Ssobomax			continue;
10183663Ssobomax		    }
10283663Ssobomax		    strcpy(*rbtmp, rb_entry->pkgname);
10383663Ssobomax		    rbtmp++;
10483663Ssobomax		}
10583663Ssobomax		*rbtmp = NULL;
10683663Ssobomax
10783663Ssobomax		err_cnt += sortdeps(rb);
10883663Ssobomax		for (j = 0; rb[j]; j++)
10983663Ssobomax		    err_cnt += pkg_do(rb[j]);
11083663Ssobomax	    }
11183663Ssobomax	}
112327Sjkh	err_cnt += pkg_do(pkgs[i]);
11372694Ssobomax    }
11472694Ssobomax
115327Sjkh    return err_cnt;
116327Sjkh}
117327Sjkh
118327Sjkhstatic Package Plist;
119327Sjkh
120327Sjkh/* This is seriously ugly code following.  Written very fast! */
121327Sjkhstatic int
122327Sjkhpkg_do(char *pkg)
123327Sjkh{
124327Sjkh    FILE *cfile;
125178753Spav    char *deporigin, **deporigins = NULL, **depnames = NULL, ***depmatches, home[FILENAME_MAX];
1261550Sasami    PackingList p;
12796613Ssobomax    int i, len;
128131280Seik    int isinstalled;
12941866Sjkh    /* support for separate pre/post install scripts */
130178103Spav    int new_m = 0, dep_count = 0;
131111486Sdes    const char *pre_script = DEINSTALL_FNAME;
132111486Sdes    const char *post_script, *pre_arg, *post_arg;
13383663Ssobomax    struct reqr_by_entry *rb_entry;
13483663Ssobomax    struct reqr_by_head *rb_list;
135206043Sflz    int fd;
136206043Sflz    struct stat sb;
137327Sjkh
13841080Sjkh    if (!pkg || !(len = strlen(pkg)))
13941080Sjkh	return 1;
14041080Sjkh    if (pkg[len - 1] == '/')
14141080Sjkh	pkg[len - 1] = '\0';
14241080Sjkh
143327Sjkh    /* Reset some state */
144327Sjkh    if (Plist.head)
145327Sjkh	free_plist(&Plist);
146327Sjkh
147131280Seik    sprintf(LogDir, "%s/%s", LOG_DIR, pkg);
148131280Seik
149131280Seik    isinstalled = isinstalledpkg(pkg);
150131280Seik    if (isinstalled == 0) {
15130221Scharnier	warnx("no such package '%s' installed", pkg);
152327Sjkh	return 1;
153131280Seik    } else if (isinstalled < 0) {
154131280Seik	warnx("the package info for package '%s' is corrupt%s",
155131280Seik	      pkg, Force ? " (but I'll delete it anyway)" : " (use -f to force removal)");
156131280Seik	if (!Force)
157131280Seik	    return 1;
158131280Seik    	if (!Fake) {
159131280Seik	    if (vsystem("%s -rf %s", REMOVE_CMD, LogDir)) {
160131280Seik    		warnx("couldn't remove log entry in %s, deinstall failed", LogDir);
161131280Seik	    } else {
162131280Seik    		warnx("couldn't completely deinstall package '%s',\n"
163131280Seik		      "only the log entry in %s was removed", pkg, LogDir);
164131280Seik	    }
165131280Seik	}
166131280Seik	return 0;
167327Sjkh    }
16841866Sjkh
16939068Sjkh    if (!getcwd(home, FILENAME_MAX)) {
17039068Sjkh	cleanup(0);
17196392Salfred	errx(2, "%s: unable to get current working directory!", __func__);
17239068Sjkh    }
17341866Sjkh
174327Sjkh    if (chdir(LogDir) == FAIL) {
17530221Scharnier	warnx("unable to change directory to %s! deinstall failed", LogDir);
176327Sjkh	return 1;
177327Sjkh    }
17841866Sjkh
17973134Ssobomax    if (Interactive == TRUE) {
18073134Ssobomax	int first, ch;
18173134Ssobomax
18273134Ssobomax	(void)fprintf(stderr, "delete %s? ", pkg);
18373134Ssobomax	(void)fflush(stderr);
18473134Ssobomax	first = ch = getchar();
18573134Ssobomax	while (ch != '\n' && ch != EOF)
18673134Ssobomax	    ch = getchar();
18773134Ssobomax	if (first != 'y' && first != 'Y')
18873134Ssobomax	    return 0;
18973134Ssobomax	    /* Not reached */
19073134Ssobomax    }
19173134Ssobomax
19283663Ssobomax    if (requiredby(pkg, &rb_list, FALSE, TRUE) < 0)
19383663Ssobomax	return 1;
19483663Ssobomax    if (!STAILQ_EMPTY(rb_list)) {
19581046Ssobomax	warnx("package '%s' is required by these other packages\n"
19683663Ssobomax	      "and may not be deinstalled%s:",
19783663Ssobomax	      pkg, Force ? " (but I'll delete it anyway)" : "");
19883663Ssobomax	STAILQ_FOREACH(rb_entry, rb_list, link)
19983663Ssobomax	    fprintf(stderr, "%s\n", rb_entry->pkgname);
2004996Sjkh	if (!Force)
2014996Sjkh	    return 1;
2024996Sjkh    }
20341866Sjkh
204327Sjkh    sanity_check(LogDir);
205327Sjkh    cfile = fopen(CONTENTS_FNAME, "r");
20641866Sjkh
207327Sjkh    if (!cfile) {
20830221Scharnier	warnx("unable to open '%s' file", CONTENTS_FNAME);
209327Sjkh	return 1;
210327Sjkh    }
21141866Sjkh
212327Sjkh    /* If we have a prefix, add it now */
213327Sjkh    if (Prefix)
214327Sjkh	add_plist(&Plist, PLIST_CWD, Prefix);
215327Sjkh    read_plist(&Plist, cfile);
216327Sjkh    fclose(cfile);
21723442Sjkh    p = find_plist(&Plist, PLIST_CWD);
21841866Sjkh
21923442Sjkh    if (!p) {
22030221Scharnier	warnx("package '%s' doesn't have a prefix", pkg);
22123442Sjkh	return 1;
22223442Sjkh    }
22341866Sjkh
22423442Sjkh    setenv(PKG_PREFIX_VNAME, p->name, 1);
22541866Sjkh
226206043Sflz    if ((fd = open(REQUIRE_FNAME, O_RDWR)) != -1) {
227206043Sflz	fstat(fd, &sb);
228206043Sflz	fchmod(fd, sb.st_mode | S_IXALL);       /* be sure, chmod a+x */
229206043Sflz	close(fd);
2301550Sasami	if (Verbose)
2311550Sasami	    printf("Executing 'require' script.\n");
2321550Sasami	if (vsystem("./%s %s DEINSTALL", REQUIRE_FNAME, pkg)) {
23330221Scharnier	    warnx("package %s fails requirements %s", pkg,
23430221Scharnier		   Force ? "" : "- not deleted");
2354996Sjkh	    if (!Force)
2364996Sjkh		return 1;
2371550Sasami	}
2381550Sasami    }
23941866Sjkh
24076739Ssobomax    /*
24176739Ssobomax     * Test whether to use the old method of passing tokens to deinstallation
24241866Sjkh     * scripts, and set appropriate variables..
24341866Sjkh     */
24441866Sjkh
24541866Sjkh    if (fexists(POST_DEINSTALL_FNAME)) {
24641866Sjkh	new_m = 1;
247111486Sdes	post_script = POST_DEINSTALL_FNAME;
248111486Sdes	pre_arg = post_arg = "";
249111486Sdes    } else if (fexists(DEINSTALL_FNAME)) {
250111486Sdes	post_script = DEINSTALL_FNAME;
251111486Sdes	pre_arg = "DEINSTALL";
252111486Sdes	post_arg = "POST-DEINSTALL";
25341866Sjkh    } else {
254111486Sdes	post_script = pre_arg = post_arg = NULL;
25541866Sjkh    }
25641866Sjkh
257206043Sflz    if (!NoDeInstall && pre_script != NULL && (fd = open(pre_script, O_RDWR)) != -1) {
258327Sjkh	if (Fake)
259327Sjkh	    printf("Would execute de-install script at this point.\n");
260327Sjkh	else {
261206043Sflz	    fstat(fd, &sb);
262206043Sflz	    fchmod(fd, sb.st_mode | S_IXALL);       /* be sure, chmod a+x */
263206043Sflz	    close(fd);
26441866Sjkh	    if (vsystem("./%s %s %s", pre_script, pkg, pre_arg)) {
26530221Scharnier		warnx("deinstall script returned error status");
2664996Sjkh		if (!Force)
2674996Sjkh		    return 1;
268327Sjkh	    }
269327Sjkh	}
270327Sjkh    }
27141866Sjkh
272131280Seik    for (p = Plist.head; p ; p = p->next) {
273131280Seik	if (p->type != PLIST_PKGDEP)
274131280Seik	    continue;
275173533Skrion	deporigin = (p->next != NULL && p->next->type == PLIST_DEPORIGIN) ? p->next->name :
276131280Seik							 NULL;
277131280Seik	if (Verbose) {
278131280Seik	    printf("Trying to remove dependency on package '%s'", p->name);
279131280Seik	    if (deporigin != NULL)
280131280Seik		printf(" with '%s' origin", deporigin);
281131280Seik	    printf(".\n");
282131280Seik	}
283131280Seik	if (!Fake) {
284178103Spav	    if (deporigin) {
285178103Spav		deporigins = realloc(deporigins, (dep_count + 2) * sizeof(*deporigins));
286178103Spav		depnames = realloc(depnames, (dep_count + 1) * sizeof(*depnames));
287178103Spav		deporigins[dep_count] = deporigin;
288178103Spav		deporigins[dep_count + 1] = NULL;
289178103Spav		depnames[dep_count] = p->name;
290178103Spav		dep_count++;
291178103Spav	    } else {
292178103Spav		undepend(p->name, pkg);
293131280Seik	    }
294131280Seik	}
295131280Seik    }
296131280Seik
297178103Spav    if (dep_count > 0) {
298178103Spav	/* Undepend all the dependencies at once */
299178103Spav	depmatches = matchallbyorigin((const char **)deporigins, NULL);
300178103Spav	free(deporigins);
301178103Spav	if (depmatches) {
302178103Spav	    for (i = 0; i < dep_count; i++) {
303178103Spav		if (depmatches[i]) {
304178753Spav		    char **tmp = depmatches[i];
305178753Spav		    int j;
306178753Spav		    for (j = 0; tmp[j] != NULL; j++)
307178753Spav			undepend(tmp[j], pkg);
308178103Spav		} else if (depnames[i]) {
309178103Spav		    undepend(depnames[i], pkg);
310178103Spav		}
311178103Spav	    }
312178103Spav	}
313178103Spav    }
314178103Spav
31539068Sjkh    if (chdir(home) == FAIL) {
31639068Sjkh	cleanup(0);
31796392Salfred	errx(2, "%s: unable to return to working directory %s!", __func__,
31896388Salfred	    home);
31939068Sjkh    }
32041866Sjkh
32176739Ssobomax    /*
32276739Ssobomax     * Some packages aren't packed right, so we need to just ignore
32376739Ssobomax     * delete_package()'s status.  Ugh! :-(
32476739Ssobomax     */
32573525Sroberto    if (delete_package(FALSE, CleanDirs, &Plist) == FAIL)
32673525Sroberto	warnx(
327225610Spluknet	"couldn't entirely delete package `%s'\n"
328225610Spluknet	"(perhaps the packing list is incorrectly specified?)", pkg);
32941866Sjkh
33041866Sjkh    if (chdir(LogDir) == FAIL) {
33141866Sjkh 	warnx("unable to change directory to %s! deinstall failed", LogDir);
33241866Sjkh 	return 1;
33341866Sjkh    }
33441866Sjkh
335206043Sflz    if (!NoDeInstall && post_script != NULL && (fd = open(post_script, O_RDWR)) != -1) {
33641866Sjkh 	if (Fake)
33741866Sjkh 	    printf("Would execute post-deinstall script at this point.\n");
33841866Sjkh 	else {
339206043Sflz	    fstat(fd, &sb);
340206043Sflz	    fchmod(fd, sb.st_mode | S_IXALL);       /* be sure, chmod a+x */
341206043Sflz	    close(fd);
34241866Sjkh 	    if (vsystem("./%s %s %s", post_script, pkg, post_arg)) {
34341866Sjkh 		warnx("post-deinstall script returned error status");
34441866Sjkh 		if (!Force)
34541866Sjkh 		    return 1;
34641866Sjkh 	    }
34741866Sjkh 	}
34841866Sjkh    }
34941866Sjkh
35041866Sjkh    if (chdir(home) == FAIL) {
35141866Sjkh 	cleanup(0);
35296392Salfred	errx(2, "%s: unable to return to working directory %s!", __func__,
35396388Salfred	    home);
35441866Sjkh    }
35541866Sjkh
35641866Sjkh    if (!Fake) {
35761171Shoek	if (vsystem("%s -r%c %s", REMOVE_CMD, Force ? 'f' : ' ', LogDir)) {
35830221Scharnier	    warnx("couldn't remove log entry in %s, deinstall failed", LogDir);
35917338Sjkh	    if (!Force)
36017338Sjkh		return 1;
36112219Sjkh	}
362327Sjkh    }
363327Sjkh    return 0;
364327Sjkh}
365327Sjkh
366327Sjkhstatic void
367327Sjkhsanity_check(char *pkg)
368327Sjkh{
36939068Sjkh    if (!fexists(CONTENTS_FNAME)) {
37039068Sjkh	cleanup(0);
37196392Salfred	errx(2, "%s: installed package %s has no %s file!", __func__,
37296388Salfred	    pkg, CONTENTS_FNAME);
37339068Sjkh    }
374327Sjkh}
375327Sjkh
376327Sjkhvoid
377327Sjkhcleanup(int sig)
378327Sjkh{
37939068Sjkh    if (sig)
38039068Sjkh	exit(1);
381327Sjkh}
3824996Sjkh
3834996Sjkhstatic void
38496613Ssobomaxundepend(char *p, char *pkgname)
3854996Sjkh{
38683663Ssobomax    char fname[FILENAME_MAX], ftmp[FILENAME_MAX];
38783663Ssobomax    FILE *fpwr;
38883663Ssobomax    int s;
38983663Ssobomax    struct reqr_by_entry *rb_entry;
39083663Ssobomax    struct reqr_by_head *rb_list;
3914996Sjkh
39283663Ssobomax
39396613Ssobomax    if (requiredby(p, &rb_list, Verbose, FALSE) <= 0)
39483663Ssobomax	return;
39596613Ssobomax    snprintf(fname, sizeof(fname), "%s/%s/%s", LOG_DIR, p, REQUIRED_BY_FNAME);
39683663Ssobomax    snprintf(ftmp, sizeof(ftmp), "%s.XXXXXX", fname);
39783663Ssobomax    s = mkstemp(ftmp);
39883663Ssobomax    if (s == -1) {
39983663Ssobomax	warnx("couldn't open temp file '%s'", ftmp);
40083663Ssobomax	return;
40183663Ssobomax    }
40283663Ssobomax    fpwr = fdopen(s, "w");
40383663Ssobomax    if (fpwr == NULL) {
40483663Ssobomax	close(s);
40583663Ssobomax	warnx("couldn't fdopen temp file '%s'", ftmp);
40683663Ssobomax	goto cleanexit;
40783663Ssobomax    }
40883663Ssobomax    STAILQ_FOREACH(rb_entry, rb_list, link)
40983663Ssobomax	if (strcmp(rb_entry->pkgname, pkgname))		/* no match */
41083663Ssobomax	    fputs(rb_entry->pkgname, fpwr), putc('\n', fpwr);
41183663Ssobomax    if (fchmod(s, 0644) == FAIL) {
41283663Ssobomax	warnx("error changing permission of temp file '%s'", ftmp);
41383663Ssobomax	fclose(fpwr);
41483663Ssobomax	goto cleanexit;
41583663Ssobomax    }
41683663Ssobomax    if (fclose(fpwr) == EOF) {
41783663Ssobomax	warnx("error closing temp file '%s'", ftmp);
41883663Ssobomax	goto cleanexit;
41983663Ssobomax    }
42083663Ssobomax    if (rename(ftmp, fname) == -1)
42183663Ssobomax	warnx("error renaming '%s' to '%s'", ftmp, fname);
42283663Ssobomaxcleanexit:
42383663Ssobomax    remove(ftmp);
42483663Ssobomax    return;
4254996Sjkh}
426