perform.c revision 72694
1#ifndef lint
2static const char rcsid[] =
3  "$FreeBSD: head/usr.sbin/pkg_install/delete/perform.c 72694 2001-02-19 13:26:13Z sobomax $";
4#endif
5
6/*
7 * FreeBSD install - a package for the installation and maintainance
8 * of non-core utilities.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * Jordan K. Hubbard
20 * 18 July 1993
21 *
22 * This is the main body of the delete module.
23 *
24 */
25
26#include <err.h>
27#include "lib.h"
28#include "delete.h"
29
30static int pkg_do(char *);
31static void sanity_check(char *);
32static void undepend(PackingList, char *);
33static int chkifdepends(char *pkgname1, char *pkgname2);
34static char LogDir[FILENAME_MAX];
35
36
37int
38pkg_perform(char **pkgs)
39{
40    char *tmp;
41    int i, j;
42    int err_cnt = 0;
43    int loop_cnt;
44
45    for (i = 0; pkgs[i]; i++) {
46	/*
47	 * Check to see if any other package in pkgs[i+1:] depends
48	 * on pkgs[i] and deffer removal of pkgs[i] if so.
49	 */
50	loop_cnt = 0;
51	for (j = i + 1; pkgs[j]; j++) {
52	    if (chkifdepends(pkgs[j], pkgs[i]) == 1) {
53		/*
54		 * Try to avoid deadlock if package A depends on B which in
55		 * turn depends on C and C due to an error depends on A.
56		 * Use ugly but simple method, becase it Should Never
57		 * Happen[tm] in the real life anyway.
58		 */
59		if (loop_cnt > 4096) {
60		    warnx("dependency loop detected for package %s", pkgs[j]);
61		    err_cnt++;
62		    break;
63		}
64		loop_cnt++;
65		tmp = pkgs[i];
66		pkgs[i] = pkgs[j];
67		pkgs[j] = tmp;
68		/*
69		 * Another iteration requred to check if new pkgs[i]
70		 * itself has any packages that depend on it
71		 */
72		j--;
73	    }
74	}
75	err_cnt += pkg_do(pkgs[i]);
76    }
77
78    return err_cnt;
79}
80
81static Package Plist;
82
83/* This is seriously ugly code following.  Written very fast! */
84static int
85pkg_do(char *pkg)
86{
87    FILE *cfile;
88    char home[FILENAME_MAX];
89    PackingList p;
90    char *tmp;
91    int len;
92    /* support for separate pre/post install scripts */
93    int new_m = 0;
94    char pre_script[FILENAME_MAX] = DEINSTALL_FNAME;
95    char post_script[FILENAME_MAX];
96    char pre_arg[FILENAME_MAX], post_arg[FILENAME_MAX];
97
98    if (!pkg || !(len = strlen(pkg)))
99	return 1;
100    if (pkg[len - 1] == '/')
101	pkg[len - 1] = '\0';
102
103    /* Reset some state */
104    if (Plist.head)
105	free_plist(&Plist);
106
107    sprintf(LogDir, "%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR,
108    	    pkg);
109
110    if (!fexists(LogDir)) {
111	warnx("no such package '%s' installed", pkg);
112	return 1;
113    }
114
115    if (!getcwd(home, FILENAME_MAX)) {
116	cleanup(0);
117	errx(2, __FUNCTION__ ": unable to get current working directory!");
118    }
119
120    if (chdir(LogDir) == FAIL) {
121	warnx("unable to change directory to %s! deinstall failed", LogDir);
122	return 1;
123    }
124
125    if (!isemptyfile(REQUIRED_BY_FNAME)) {
126	char buf[512];
127	warnx("package `%s' is required by these other packages\n"
128		"and may not be deinstalled%s:",
129		pkg, Force ? " (but I'll delete it anyway)" : "" );
130	cfile = fopen(REQUIRED_BY_FNAME, "r");
131	if (cfile) {
132	    while (fgets(buf, sizeof(buf), cfile))
133		fprintf(stderr, "%s", buf);
134	    fclose(cfile);
135	} else
136	    warnx("cannot open requirements file `%s'", REQUIRED_BY_FNAME);
137	if (!Force)
138	    return 1;
139    }
140
141    sanity_check(LogDir);
142    cfile = fopen(CONTENTS_FNAME, "r");
143
144    if (!cfile) {
145	warnx("unable to open '%s' file", CONTENTS_FNAME);
146	return 1;
147    }
148
149    /* If we have a prefix, add it now */
150    if (Prefix)
151	add_plist(&Plist, PLIST_CWD, Prefix);
152    read_plist(&Plist, cfile);
153    fclose(cfile);
154    p = find_plist(&Plist, PLIST_CWD);
155
156    if (!p) {
157	warnx("package '%s' doesn't have a prefix", pkg);
158	return 1;
159    }
160
161    setenv(PKG_PREFIX_VNAME, p->name, 1);
162
163    if (fexists(REQUIRE_FNAME)) {
164	if (Verbose)
165	    printf("Executing 'require' script.\n");
166	vsystem("chmod +x %s", REQUIRE_FNAME);	/* be sure */
167	if (vsystem("./%s %s DEINSTALL", REQUIRE_FNAME, pkg)) {
168	    warnx("package %s fails requirements %s", pkg,
169		   Force ? "" : "- not deleted");
170	    if (!Force)
171		return 1;
172	}
173    }
174
175    /* Test whether to use the old method of passing tokens to deinstallation
176     * scripts, and set appropriate variables..
177     */
178
179    if (fexists(POST_DEINSTALL_FNAME)) {
180	new_m = 1;
181	sprintf(post_script, "%s", POST_DEINSTALL_FNAME);
182	pre_arg[0] = '\0';
183	post_arg[0] = '\0';
184    } else {
185	if (fexists(DEINSTALL_FNAME)) {
186	    sprintf(post_script, "%s", DEINSTALL_FNAME);
187	    sprintf(pre_arg, "DEINSTALL");
188	    sprintf(post_arg, "POST-DEINSTALL");
189	}
190    }
191
192    if (!NoDeInstall && fexists(pre_script)) {
193	if (Fake)
194	    printf("Would execute de-install script at this point.\n");
195	else {
196	    vsystem("chmod +x %s", pre_script);	/* make sure */
197	    if (vsystem("./%s %s %s", pre_script, pkg, pre_arg)) {
198		warnx("deinstall script returned error status");
199		if (!Force)
200		    return 1;
201	    }
202	}
203    }
204
205    if (chdir(home) == FAIL) {
206	cleanup(0);
207	errx(2, __FUNCTION__ ": unable to return to working directory %s!", home);
208    }
209
210    if (!Fake) {
211	/* Some packages aren't packed right, so we need to just ignore delete_package()'s status.  Ugh! :-( */
212	if (delete_package(FALSE, CleanDirs, &Plist) == FAIL)
213	    warnx(
214	"couldn't entirely delete package (perhaps the packing list is\n"
215	"incorrectly specified?)");
216    }
217
218    if (chdir(LogDir) == FAIL) {
219 	warnx("unable to change directory to %s! deinstall failed", LogDir);
220 	return 1;
221    }
222
223    if (!NoDeInstall && fexists(post_script)) {
224 	if (Fake)
225 	    printf("Would execute post-deinstall script at this point.\n");
226 	else {
227 	    vsystem("chmod +x %s", post_script);	/* make sure */
228 	    if (vsystem("./%s %s %s", post_script, pkg, post_arg)) {
229 		warnx("post-deinstall script returned error status");
230 		if (!Force)
231 		    return 1;
232 	    }
233 	}
234    }
235
236    if (chdir(home) == FAIL) {
237 	cleanup(0);
238	errx(2, __FUNCTION__ ": unable to return to working directory %s!", home);
239    }
240
241    if (!Fake) {
242	if (vsystem("%s -r%c %s", REMOVE_CMD, Force ? 'f' : ' ', LogDir)) {
243	    warnx("couldn't remove log entry in %s, deinstall failed", LogDir);
244	    if (!Force)
245		return 1;
246	}
247    }
248
249    for (p = Plist.head; p ; p = p->next) {
250	if (p->type != PLIST_PKGDEP)
251	    continue;
252	if (Verbose)
253	    printf("Attempting to remove dependency on package `%s'\n", p->name);
254	if (!Fake)
255	    undepend(p, pkg);
256    }
257    return 0;
258}
259
260static void
261sanity_check(char *pkg)
262{
263    if (!fexists(CONTENTS_FNAME)) {
264	cleanup(0);
265	errx(2, __FUNCTION__ ": installed package %s has no %s file!", pkg, CONTENTS_FNAME);
266    }
267}
268
269void
270cleanup(int sig)
271{
272    if (sig)
273	exit(1);
274}
275
276static void
277undepend(PackingList p, char *pkgname)
278{
279     char fname[FILENAME_MAX], ftmp[FILENAME_MAX];
280     char fbuf[FILENAME_MAX];
281     FILE *fp, *fpwr;
282     char *tmp;
283     int s;
284
285     sprintf(fname, "%s/%s/%s",
286	     (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR,
287	     p->name, REQUIRED_BY_FNAME);
288     fp = fopen(fname, "r");
289     if (fp == NULL) {
290	 warnx("couldn't open dependency file `%s'", fname);
291	 return;
292     }
293     sprintf(ftmp, "%s.XXXXXX", fname);
294     s = mkstemp(ftmp);
295     if (s == -1) {
296	 fclose(fp);
297	 warnx("couldn't open temp file `%s'", ftmp);
298	 return;
299     }
300     fpwr = fdopen(s, "w");
301     if (fpwr == NULL) {
302	 close(s);
303	 fclose(fp);
304	 warnx("couldn't fdopen temp file `%s'", ftmp);
305	 remove(ftmp);
306	 return;
307     }
308     while (fgets(fbuf, sizeof(fbuf), fp) != NULL) {
309	 if (fbuf[strlen(fbuf)-1] == '\n')
310	     fbuf[strlen(fbuf)-1] = '\0';
311	 if (strcmp(fbuf, pkgname))		/* no match */
312	     fputs(fbuf, fpwr), putc('\n', fpwr);
313     }
314     (void) fclose(fp);
315     if (fchmod(s, 0644) == FAIL) {
316	 warnx("error changing permission of temp file `%s'", ftmp);
317	 fclose(fpwr);
318	 remove(ftmp);
319	 return;
320     }
321     if (fclose(fpwr) == EOF) {
322	 warnx("error closing temp file `%s'", ftmp);
323	 remove(ftmp);
324	 return;
325     }
326     if (rename(ftmp, fname) == -1)
327	 warnx("error renaming `%s' to `%s'", ftmp, fname);
328     remove(ftmp);			/* just in case */
329     return;
330}
331
332/*
333 * Check to see if pkgname1 depends on pkgname2.
334 * Returns 1 if depends, 0 if not, and -1 if error occured.
335 */
336static int
337chkifdepends(char *pkgname1, char *pkgname2)
338{
339    FILE *fp;
340    char fname[FILENAME_MAX];
341    char fbuf[FILENAME_MAX];
342    char *tmp;
343    int retval;
344
345    sprintf(fname, "%s/%s/%s",
346	    (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR,
347	    pkgname2, REQUIRED_BY_FNAME);
348    fp = fopen(fname, "r");
349    if (fp == NULL) {
350	/* Probably pkgname2 doesn't have any packages that depend on it */
351	return 0;
352    }
353
354    retval = 0;
355    while (fgets(fbuf, sizeof(fbuf), fp) != NULL) {
356	if (fbuf[strlen(fbuf)-1] == '\n')
357	    fbuf[strlen(fbuf)-1] = '\0';
358	if (strcmp(fbuf, pkgname1) == 0) {	/* match */
359	    retval = 1;
360	    break;
361	}
362    }
363
364    fclose(fp);
365    return retval;
366}
367