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