perform.c revision 38583
1#ifndef lint
2static const char rcsid[] =
3	"$Id: perform.c,v 1.50 1998/07/17 14:56:31 eivind Exp $";
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 add module.
23 *
24 */
25
26#include <err.h>
27#include "lib.h"
28#include "add.h"
29
30#include <signal.h>
31#include <sys/wait.h>
32
33static int pkg_do(char *);
34static int sanity_check(char *);
35static char LogDir[FILENAME_MAX];
36static int zapLogDir;		/* Should we delete LogDir? */
37
38int
39pkg_perform(char **pkgs)
40{
41    int i, err_cnt = 0;
42
43    signal(SIGINT, cleanup);
44    signal(SIGHUP, cleanup);
45
46    if (AddMode == SLAVE)
47	err_cnt = pkg_do(NULL);
48    else {
49	for (i = 0; pkgs[i]; i++)
50	    err_cnt += pkg_do(pkgs[i]);
51    }
52    return err_cnt;
53}
54
55static Package Plist;
56static char *Home;
57
58/*
59 * This is seriously ugly code following.  Written very fast!
60 * [And subsequently made even worse..  Sigh!  This code was just born
61 * to be hacked, I guess.. :) -jkh]
62 */
63static int
64pkg_do(char *pkg)
65{
66    char pkg_fullname[FILENAME_MAX];
67    char playpen[FILENAME_MAX];
68    char extract_contents[FILENAME_MAX];
69    char *where_to, *tmp, *extract;
70    FILE *cfile;
71    int code;
72    PackingList p;
73    struct stat sb;
74    int inPlace;
75
76    code = 0;
77    zapLogDir = 0;
78    LogDir[0] = '\0';
79    strcpy(playpen, FirstPen);
80    inPlace = 0;
81
82    /* Are we coming in for a second pass, everything already extracted? */
83    if (!pkg) {
84	fgets(playpen, FILENAME_MAX, stdin);
85	playpen[strlen(playpen) - 1] = '\0'; /* pesky newline! */
86	if (chdir(playpen) == FAIL) {
87	    warnx("pkg_add in SLAVE mode can't chdir to %s", playpen);
88	    return 1;
89	}
90	read_plist(&Plist, stdin);
91	where_to = playpen;
92    }
93    /* Nope - do it now */
94    else {
95	/* Is it an ftp://foo.bar.baz/file.tgz specification? */
96	if (isURL(pkg)) {
97	    if (!(Home = fileGetURL(NULL, pkg))) {
98		warnx("unable to fetch `%s' by URL", pkg);
99		return 1;
100	    }
101	    where_to = Home;
102	    strcpy(pkg_fullname, pkg);
103	    cfile = fopen(CONTENTS_FNAME, "r");
104	    if (!cfile) {
105		warnx(
106		"unable to open table of contents file `%s' - not a package?",
107		CONTENTS_FNAME);
108		goto bomb;
109	    }
110	    read_plist(&Plist, cfile);
111	    fclose(cfile);
112	}
113	else {
114	    strcpy(pkg_fullname, pkg);		/* copy for sanity's sake, could remove pkg_fullname */
115	    if (strcmp(pkg, "-")) {
116		if (stat(pkg_fullname, &sb) == FAIL) {
117		    warnx("can't stat package file '%s'", pkg_fullname);
118		    goto bomb;
119		}
120		sprintf(extract_contents, "--fast-read %s", CONTENTS_FNAME);
121		extract = extract_contents;
122	    }
123	    else {
124		extract = NULL;
125		sb.st_size = 100000;	/* Make up a plausible average size */
126	    }
127	    Home = make_playpen(playpen, sb.st_size * 4);
128	    if (!Home)
129		errx(1, "unable to make playpen for %d bytes", sb.st_size * 4);
130	    where_to = Home;
131	    /* Since we can call ourselves recursively, keep notes on where we came from */
132	    if (!getenv("_TOP"))
133		setenv("_TOP", Home, 1);
134	    if (unpack(pkg_fullname, extract)) {
135		warnx(
136	"unable to extract table of contents file from `%s' - not a package?",
137		pkg_fullname);
138		goto bomb;
139	    }
140	    cfile = fopen(CONTENTS_FNAME, "r");
141	    if (!cfile) {
142		warnx(
143	"unable to open table of contents file `%s' - not a package?",
144		CONTENTS_FNAME);
145		goto bomb;
146	    }
147	    read_plist(&Plist, cfile);
148	    fclose(cfile);
149
150	    /* Extract directly rather than moving?  Oh goodie! */
151	    if (find_plist_option(&Plist, "extract-in-place")) {
152		if (Verbose)
153		    printf("Doing in-place extraction for %s\n", pkg_fullname);
154		p = find_plist(&Plist, PLIST_CWD);
155		if (p) {
156		    if (!isdir(p->name) && !Fake) {
157			if (Verbose)
158			    printf("Desired prefix of %s does not exist, creating..\n", p->name);
159			vsystem("mkdir -p %s", p->name);
160			if (chdir(p->name) == -1) {
161			    warn("unable to change directory to `%s'", p->name);
162			    goto bomb;
163			}
164		    }
165		    where_to = p->name;
166		    inPlace = 1;
167		}
168		else {
169		    warnx(
170		"no prefix specified in `%s' - this is a bad package!",
171			pkg_fullname);
172		    goto bomb;
173		}
174	    }
175
176	    /*
177	     * Apply a crude heuristic to see how much space the package will
178	     * take up once it's unpacked.  I've noticed that most packages
179	     * compress an average of 75%, so multiply by 4 for good measure.
180	     */
181
182	    if (!inPlace && min_free(playpen) < sb.st_size * 4) {
183		warnx("projected size of %d exceeds available free space.\n"
184"Please set your PKG_TMPDIR variable to point to a location with more\n"
185		       "free space and try again", sb.st_size * 4);
186		warnx("not extracting %s\ninto %s, sorry!",
187			pkg_fullname, where_to);
188		goto bomb;
189	    }
190
191	    /* If this is a direct extract and we didn't want it, stop now */
192	    if (inPlace && Fake)
193		goto success;
194
195	    /* Finally unpack the whole mess */
196	    if (unpack(pkg_fullname, NULL)) {
197		warnx("unable to extract `%s'!", pkg_fullname);
198		goto bomb;
199	    }
200	}
201
202	/* Check for sanity and dependencies */
203	if (sanity_check(pkg))
204	    goto bomb;
205
206	/* If we're running in MASTER mode, just output the plist and return */
207	if (AddMode == MASTER) {
208	    printf("%s\n", where_playpen());
209	    write_plist(&Plist, stdout);
210	    return 0;
211	}
212    }
213
214    /*
215     * If we have a prefix, delete the first one we see and add this
216     * one in place of it.
217     */
218    if (Prefix) {
219	delete_plist(&Plist, FALSE, PLIST_CWD, NULL);
220	add_plist_top(&Plist, PLIST_CWD, Prefix);
221    }
222
223    setenv(PKG_PREFIX_VNAME, (p = find_plist(&Plist, PLIST_CWD)) ? p->name : ".", 1);
224    /* Protect against old packages with bogus @name fields */
225    PkgName = (p = find_plist(&Plist, PLIST_NAME)) ? p->name : "anonymous";
226
227    /* See if we're already registered */
228    sprintf(LogDir, "%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR, PkgName);
229    if (isdir(LogDir) && !Force) {
230	warnx("package `%s' already recorded as installed", PkgName);
231	code = 1;
232	goto success;	/* close enough for government work */
233    }
234
235    /* Now check the packing list for dependencies */
236    for (p = Plist.head; p ; p = p->next) {
237	if (p->type != PLIST_PKGDEP)
238	    continue;
239	if (Verbose)
240	    printf("Package `%s' depends on `%s'.\n", PkgName, p->name);
241	if (vsystem("pkg_info -e %s", p->name)) {
242	    char path[FILENAME_MAX], *cp = NULL;
243
244	    if (!Fake) {
245		if (!isURL(pkg) && !getenv("PKG_ADD_BASE")) {
246		    snprintf(path, FILENAME_MAX, "%s/%s.tgz", getenv("_TOP"), p->name);
247		    if (fexists(path))
248			cp = path;
249		    else
250			cp = fileFindByPath(pkg, p->name);
251		    if (cp) {
252			if (Verbose)
253			    printf("Loading it from %s.\n", cp);
254			if (vsystem("pkg_add %s%s", Verbose ? "-v " : "", cp)) {
255			    warnx("autoload of dependency `%s' failed%s",
256				cp, Force ? " (proceeding anyway)" : "!");
257			    if (!Force)
258				++code;
259			}
260		    }
261		}
262		else if ((cp = fileGetURL(pkg, p->name)) != NULL) {
263		    if (Verbose)
264			printf("Finished loading %s over FTP.\n", p->name);
265		    if (!fexists("+CONTENTS")) {
266			warnx("autoloaded package %s has no +CONTENTS file?",
267				p->name);
268			if (!Force)
269			    ++code;
270		    }
271		    else if (vsystem("(pwd; cat +CONTENTS) | pkg_add %s-S", Verbose ? "-v " : "")) {
272			warnx("pkg_add of dependency `%s' failed%s",
273				p->name, Force ? " (proceeding anyway)" : "!");
274			if (!Force)
275			    ++code;
276		    }
277		    else if (Verbose)
278			printf("\t`%s' loaded successfully.\n", p->name);
279		    /* Nuke the temporary playpen */
280		    leave_playpen();
281		}
282	    }
283	    else {
284		if (Verbose)
285		    printf("and was not found%s.\n", Force ? " (proceeding anyway)" : "");
286		else
287		    printf("Package dependency %s for %s not found%s\n", p->name, pkg,
288			   Force ? " (proceeding anyway)" : "!");
289		if (!Force)
290		    ++code;
291	    }
292	}
293	else if (Verbose)
294	    printf(" - already installed.\n");
295    }
296
297    if (code != 0)
298	goto bomb;
299
300    /* Look for the requirements file */
301    if (fexists(REQUIRE_FNAME)) {
302	vsystem("chmod +x %s", REQUIRE_FNAME);	/* be sure */
303	if (Verbose)
304	    printf("Running requirements file first for %s..\n", PkgName);
305	if (!Fake && vsystem("./%s %s INSTALL", REQUIRE_FNAME, PkgName)) {
306	    warnx("package %s fails requirements %s", pkg_fullname,
307		   Force ? "installing anyway" : "- not installed");
308	    if (!Force) {
309		code = 1;
310		goto success;	/* close enough for government work */
311	    }
312	}
313    }
314
315    /* If we're really installing, and have an installation file, run it */
316    if (!NoInstall && fexists(INSTALL_FNAME)) {
317	vsystem("chmod +x %s", INSTALL_FNAME);	/* make sure */
318	if (Verbose)
319	    printf("Running install with PRE-INSTALL for %s..\n", PkgName);
320	if (!Fake && vsystem("./%s %s PRE-INSTALL", INSTALL_FNAME, PkgName)) {
321	    warnx("install script returned error status");
322	    unlink(INSTALL_FNAME);
323	    code = 1;
324	    goto success;		/* nothing to uninstall yet */
325	}
326    }
327
328    /* Now finally extract the entire show if we're not going direct */
329    if (!inPlace && !Fake)
330	extract_plist(".", &Plist);
331
332    if (!Fake && fexists(MTREE_FNAME)) {
333	if (Verbose)
334	    printf("Running mtree for %s..\n", PkgName);
335	p = find_plist(&Plist, PLIST_CWD);
336	if (Verbose)
337	    printf("mtree -U -f %s -d -e -p %s\n", MTREE_FNAME, p ? p->name : "/");
338	if (!Fake) {
339	    if (vsystem("/usr/sbin/mtree -U -f %s -d -e -p %s", MTREE_FNAME, p ? p->name : "/"))
340		warnx("mtree returned a non-zero status - continuing");
341	}
342	unlink(MTREE_FNAME);
343    }
344
345    /* Run the installation script one last time? */
346    if (!NoInstall && fexists(INSTALL_FNAME)) {
347	if (Verbose)
348	    printf("Running install with POST-INSTALL for %s..\n", PkgName);
349	if (!Fake && vsystem("./%s %s POST-INSTALL", INSTALL_FNAME, PkgName)) {
350	    warnx("install script returned error status");
351	    unlink(INSTALL_FNAME);
352	    code = 1;
353	    goto fail;
354	}
355	unlink(INSTALL_FNAME);
356    }
357
358    /* Time to record the deed? */
359    if (!NoRecord && !Fake) {
360	char contents[FILENAME_MAX];
361	FILE *cfile;
362
363	umask(022);
364	if (getuid() != 0)
365	    warnx("not running as root - trying to record install anyway");
366	if (!PkgName) {
367	    warnx("no package name! can't record package, sorry");
368	    code = 1;
369	    goto success;	/* well, partial anyway */
370	}
371	sprintf(LogDir, "%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR, PkgName);
372	zapLogDir = 1;
373	if (Verbose)
374	    printf("Attempting to record package into %s..\n", LogDir);
375	if (make_hierarchy(LogDir)) {
376	    warnx("can't record package into '%s', you're on your own!",
377		   LogDir);
378	    bzero(LogDir, FILENAME_MAX);
379	    code = 1;
380	    goto success;	/* close enough for government work */
381	}
382	/* Make sure pkg_info can read the entry */
383	vsystem("chmod a+rx %s", LogDir);
384	if (fexists(DEINSTALL_FNAME))
385	    move_file(".", DEINSTALL_FNAME, LogDir);
386	if (fexists(REQUIRE_FNAME))
387	    move_file(".", REQUIRE_FNAME, LogDir);
388	sprintf(contents, "%s/%s", LogDir, CONTENTS_FNAME);
389	cfile = fopen(contents, "w");
390	if (!cfile) {
391	    warnx("can't open new contents file '%s'! can't register pkg",
392		contents);
393	    goto success; /* can't log, but still keep pkg */
394	}
395	write_plist(&Plist, cfile);
396	fclose(cfile);
397	move_file(".", DESC_FNAME, LogDir);
398	move_file(".", COMMENT_FNAME, LogDir);
399	if (fexists(DISPLAY_FNAME))
400	    move_file(".", DISPLAY_FNAME, LogDir);
401	for (p = Plist.head; p ; p = p->next) {
402	    if (p->type != PLIST_PKGDEP)
403		continue;
404	    if (Verbose)
405		printf("Attempting to record dependency on package `%s'\n", p->name);
406	    sprintf(contents, "%s/%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR,
407	    	    basename_of(p->name), REQUIRED_BY_FNAME);
408	    cfile = fopen(contents, "a");
409	    if (!cfile)
410		warnx("can't open dependency file '%s'!\n"
411		       "dependency registration is incomplete", contents);
412	    else {
413		fprintf(cfile, "%s\n", PkgName);
414		if (fclose(cfile) == EOF)
415		    warnx("cannot properly close file %s", contents);
416	    }
417	}
418	if (Verbose)
419	    printf("Package %s registered in %s\n", PkgName, LogDir);
420    }
421
422    if ((p = find_plist(&Plist, PLIST_DISPLAY)) != NULL) {
423	FILE *fp;
424	char buf[BUFSIZ];
425
426	snprintf(buf, sizeof buf, "%s/%s", LogDir, p->name);
427	fp = fopen(buf, "r");
428	if (fp) {
429	    putc('\n', stdout);
430	    while (fgets(buf, sizeof(buf), fp))
431		fputs(buf, stdout);
432	    putc('\n', stdout);
433	    (void) fclose(fp);
434	} else
435	    warnx("cannot open %s as display file", buf);
436    }
437
438    goto success;
439
440 bomb:
441    code = 1;
442    goto success;
443
444 fail:
445    /* Nuke the whole (installed) show, XXX but don't clean directories */
446    if (!Fake)
447	delete_package(FALSE, FALSE, &Plist);
448
449 success:
450    /* delete the packing list contents */
451    free_plist(&Plist);
452    leave_playpen();
453    return code;
454}
455
456static int
457sanity_check(char *pkg)
458{
459    int code = 0;
460
461    if (!fexists(CONTENTS_FNAME)) {
462	warnx("package %s has no CONTENTS file!", pkg);
463	code = 1;
464    }
465    else if (!fexists(COMMENT_FNAME)) {
466	warnx("package %s has no COMMENT file!", pkg);
467	code = 1;
468    }
469    else if (!fexists(DESC_FNAME)) {
470	warnx("package %s has no DESC file!", pkg);
471	code = 1;
472    }
473    return code;
474}
475
476void
477cleanup(int signo)
478{
479    static int in_cleanup = 0;
480
481    if (!in_cleanup) {
482	in_cleanup = 1;
483    	if (signo)
484		printf("Signal %d received, cleaning up..\n", signo);
485    	if (!Fake && zapLogDir && LogDir[0])
486		vsystem("%s -rf %s", REMOVE_CMD, LogDir);
487    	leave_playpen();
488    }
489    exit(1);
490}
491