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