perform.c revision 96392
1/*
2 * FreeBSD install - a package for the installation and maintainance
3 * of non-core utilities.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * Jordan K. Hubbard
15 * 18 July 1993
16 *
17 * This is the main body of the create module.
18 *
19 */
20
21#include <sys/cdefs.h>
22__FBSDID("$FreeBSD: head/usr.sbin/pkg_install/create/perform.c 96392 2002-05-11 04:17:55Z alfred $");
23
24#include "lib.h"
25#include "create.h"
26
27#include <err.h>
28#include <libgen.h>
29#include <signal.h>
30#include <stdlib.h>
31#include <sys/syslimits.h>
32#include <sys/wait.h>
33#include <unistd.h>
34
35static void sanity_check(void);
36static void make_dist(const char *, const char *, const char *, Package *);
37static int create_from_installed(const char *, const char *);
38
39static char *home;
40
41int
42pkg_perform(char **pkgs)
43{
44    char *pkg = *pkgs;		/* Only one arg to create */
45    char *cp;
46    FILE *pkg_in, *fp;
47    Package plist;
48    int len;
49    const char *suf;
50
51    /* Preliminary setup */
52    if (InstalledPkg == NULL)
53	sanity_check();
54    if (Verbose && !PlistOnly)
55	printf("Creating package %s\n", pkg);
56
57    /* chop suffix off if already specified, remembering if we want to compress  */
58    len = strlen(pkg);
59    if (len > 4) {
60	if (!strcmp(&pkg[len - 4], ".tgz")) {
61	    Zipper = GZIP;
62	    pkg[len - 4] = '\0';
63	}
64	else if (!strcmp(&pkg[len - 4], ".tar")) {
65	    Zipper = NONE;
66	    pkg[len - 4] = '\0';
67	}
68	else if (!strcmp(&pkg[len - 4], ".tbz")) {
69	    Zipper = BZIP2;
70	    pkg[len - 4] = '\0';
71	}
72	else if ((len > 5) && (!strcmp(&pkg[len - 5], ".tbz2"))) {
73	    Zipper = BZIP2;
74	    pkg[len - 5] = '\0';
75	}
76    }
77    if (Zipper == BZIP2) {
78	suf = "tbz2";
79	setenv("BZIP2", "-9", 0);
80    } else if (Zipper == GZIP) {
81	suf = "tgz";
82	setenv("GZIP", "-9", 0);
83    } else
84	suf = "tar";
85
86    if (InstalledPkg != NULL)
87	return (create_from_installed(pkg, suf));
88
89    get_dash_string(&Comment);
90    get_dash_string(&Desc);
91    if (!strcmp(Contents, "-"))
92	pkg_in = stdin;
93    else {
94	pkg_in = fopen(Contents, "r");
95	if (!pkg_in) {
96	    cleanup(0);
97	    errx(2, "%s: unable to open contents file '%s' for input",
98		__func__, Contents);
99	}
100    }
101    plist.head = plist.tail = NULL;
102
103    /* Stick the dependencies, if any, at the top */
104    if (Pkgdeps) {
105	char **deps, *deporigin;
106	int i;
107	int ndeps = 0;
108
109	if (Verbose && !PlistOnly)
110	    printf("Registering depends:");
111
112	/* Count number of dependencies */
113	for (cp = Pkgdeps; cp != NULL && *cp != '\0';
114			   cp = strpbrk(++cp, " \t\n")) {
115	    ndeps++;
116	}
117
118	if (ndeps != 0) {
119	    /* Create easy to use NULL-terminated list */
120	    deps = alloca(sizeof(*deps) * ndeps + 1);
121	    if (deps == NULL) {
122		errx(2, "%s: alloca() failed", __func__);
123		/* Not reached */
124	    }
125	    for (i = 0; Pkgdeps;) {
126		cp = strsep(&Pkgdeps, " \t\n");
127		if (*cp) {
128		    deps[i] = cp;
129		    i++;
130		}
131	    }
132	    ndeps = i;
133	    deps[ndeps] = NULL;
134
135	    sortdeps(deps);
136	    for (i = 0; i < ndeps; i++) {
137		deporigin = strchr(deps[i], ':');
138		if (deporigin != NULL) {
139		    *deporigin = '\0';
140		    add_plist_top(&plist, PLIST_DEPORIGIN, ++deporigin);
141		}
142		add_plist_top(&plist, PLIST_PKGDEP, deps[i]);
143		if (Verbose && !PlistOnly)
144		    printf(" %s", deps[i]);
145	    }
146	}
147
148	if (Verbose && !PlistOnly)
149	    printf(".\n");
150    }
151
152    /* If a SrcDir override is set, add it now */
153    if (SrcDir) {
154	if (Verbose && !PlistOnly)
155	    printf("Using SrcDir value of %s\n", SrcDir);
156	add_plist(&plist, PLIST_SRC, SrcDir);
157    }
158
159    /* Slurp in the packing list */
160    read_plist(&plist, pkg_in);
161
162    /* Prefix should add an @cwd to the packing list */
163    if (Prefix)
164	add_plist_top(&plist, PLIST_CWD, Prefix);
165
166    /* Add the origin if asked, at the top */
167    if (Origin)
168	add_plist_top(&plist, PLIST_ORIGIN, Origin);
169
170    /*
171     * Run down the list and see if we've named it, if not stick in a name
172     * at the top.
173     */
174    if (find_plist(&plist, PLIST_NAME) == NULL)
175	add_plist_top(&plist, PLIST_NAME, basename(pkg));
176
177    if (asprintf(&cp, "PKG_FORMAT_REVISION:%d.%d", PLIST_FMT_VER_MAJOR,
178		 PLIST_FMT_VER_MINOR) == -1) {
179	errx(2, "%s: asprintf() failed", __func__);
180    }
181    add_plist_top(&plist, PLIST_COMMENT, cp);
182    free(cp);
183
184    /*
185     * We're just here for to dump out a revised plist for the FreeBSD ports
186     * hack.  It's not a real create in progress.
187     */
188    if (PlistOnly) {
189	check_list(home, &plist);
190	write_plist(&plist, stdout);
191	exit(0);
192    }
193
194    /* Make a directory to stomp around in */
195    home = make_playpen(PlayPen, 0);
196    signal(SIGINT, cleanup);
197    signal(SIGHUP, cleanup);
198
199    /* Make first "real contents" pass over it */
200    check_list(home, &plist);
201    (void) umask(022);	/*
202			 * Make sure gen'ed directories, files don't have
203			 * group or other write bits.
204			 */
205    /* copy_plist(home, &plist); */
206    /* mark_plist(&plist); */
207
208    /* Now put the release specific items in */
209    add_plist(&plist, PLIST_CWD, ".");
210    write_file(COMMENT_FNAME, Comment);
211    add_plist(&plist, PLIST_IGNORE, NULL);
212    add_plist(&plist, PLIST_FILE, COMMENT_FNAME);
213    write_file(DESC_FNAME, Desc);
214    add_plist(&plist, PLIST_IGNORE, NULL);
215    add_plist(&plist, PLIST_FILE, DESC_FNAME);
216
217    if (Install) {
218	copy_file(home, Install, INSTALL_FNAME);
219	add_plist(&plist, PLIST_IGNORE, NULL);
220	add_plist(&plist, PLIST_FILE, INSTALL_FNAME);
221    }
222    if (PostInstall) {
223	copy_file(home, PostInstall, POST_INSTALL_FNAME);
224	add_plist(&plist, PLIST_IGNORE, NULL);
225	add_plist(&plist, PLIST_FILE, POST_INSTALL_FNAME);
226    }
227    if (DeInstall) {
228	copy_file(home, DeInstall, DEINSTALL_FNAME);
229	add_plist(&plist, PLIST_IGNORE, NULL);
230	add_plist(&plist, PLIST_FILE, DEINSTALL_FNAME);
231    }
232    if (PostDeInstall) {
233	copy_file(home, PostDeInstall, POST_DEINSTALL_FNAME);
234	add_plist(&plist, PLIST_IGNORE, NULL);
235	add_plist(&plist, PLIST_FILE, POST_DEINSTALL_FNAME);
236    }
237    if (Require) {
238	copy_file(home, Require, REQUIRE_FNAME);
239	add_plist(&plist, PLIST_IGNORE, NULL);
240	add_plist(&plist, PLIST_FILE, REQUIRE_FNAME);
241    }
242    if (Display) {
243	copy_file(home, Display, DISPLAY_FNAME);
244	add_plist(&plist, PLIST_IGNORE, NULL);
245	add_plist(&plist, PLIST_FILE, DISPLAY_FNAME);
246	add_plist(&plist, PLIST_DISPLAY, DISPLAY_FNAME);
247    }
248    if (Mtree) {
249	copy_file(home, Mtree, MTREE_FNAME);
250	add_plist(&plist, PLIST_IGNORE, NULL);
251	add_plist(&plist, PLIST_FILE, MTREE_FNAME);
252	add_plist(&plist, PLIST_MTREE, MTREE_FNAME);
253    }
254
255    /* Finally, write out the packing list */
256    fp = fopen(CONTENTS_FNAME, "w");
257    if (!fp) {
258	cleanup(0);
259	errx(2, "%s: can't open file %s for writing",
260	    __func__, CONTENTS_FNAME);
261    }
262    write_plist(&plist, fp);
263    if (fclose(fp)) {
264	cleanup(0);
265	errx(2, "%s: error while closing %s",
266	    __func__, CONTENTS_FNAME);
267    }
268
269    /* And stick it into a tar ball */
270    make_dist(home, pkg, suf, &plist);
271
272    /* Cleanup */
273    free(Comment);
274    free(Desc);
275    free_plist(&plist);
276    leave_playpen();
277    return TRUE;	/* Success */
278}
279
280static void
281make_dist(const char *homedir, const char *pkg, const char *suff, Package *plist)
282{
283    char tball[FILENAME_MAX];
284    PackingList p;
285    int ret;
286    const char *args[50];	/* Much more than enough. */
287    int nargs = 0;
288    int pipefds[2];
289    FILE *totar;
290    pid_t pid;
291    const char *cname;
292
293    args[nargs++] = "tar";	/* argv[0] */
294
295    if (*pkg == '/')
296	snprintf(tball, FILENAME_MAX, "%s.%s", pkg, suff);
297    else
298	snprintf(tball, FILENAME_MAX, "%s/%s.%s", homedir, pkg, suff);
299
300    args[nargs++] = "-c";
301    args[nargs++] = "-f";
302    args[nargs++] = tball;
303    if (strchr(suff, 'z')) {	/* Compress/gzip/bzip2? */
304	if (Zipper == BZIP2) {
305	    args[nargs++] = "-j";
306	    cname = "bzip'd ";
307	}
308	else {
309	    args[nargs++] = "-z";
310	    cname = "gzip'd ";
311	}
312    } else {
313	cname = "";
314    }
315    if (Dereference)
316	args[nargs++] = "-h";
317    if (ExcludeFrom) {
318	args[nargs++] = "-X";
319	args[nargs++] = ExcludeFrom;
320    }
321    args[nargs++] = "-T";	/* Take filenames from file instead of args. */
322    args[nargs++] = "-";	/* Use stdin for the file. */
323    args[nargs] = NULL;
324
325    if (Verbose)
326	printf("Creating %star ball in '%s'\n", cname, tball);
327
328    /* Set up a pipe for passing the filenames, and fork off a tar process. */
329    if (pipe(pipefds) == -1) {
330	cleanup(0);
331	errx(2, "%s: cannot create pipe", __func__);
332    }
333    if ((pid = fork()) == -1) {
334	cleanup(0);
335	errx(2, "%s: cannot fork process for tar", __func__);
336    }
337    if (pid == 0) {	/* The child */
338	dup2(pipefds[0], 0);
339	close(pipefds[0]);
340	close(pipefds[1]);
341	execv("/usr/bin/tar", (char * const *)(uintptr_t)args);
342	cleanup(0);
343	errx(2, "%s: failed to execute tar command", __func__);
344    }
345
346    /* Meanwhile, back in the parent process ... */
347    close(pipefds[0]);
348    if ((totar = fdopen(pipefds[1], "w")) == NULL) {
349	cleanup(0);
350	errx(2, "%s: fdopen failed", __func__);
351    }
352
353    fprintf(totar, "%s\n", CONTENTS_FNAME);
354    fprintf(totar, "%s\n", COMMENT_FNAME);
355    fprintf(totar, "%s\n", DESC_FNAME);
356
357    if (Install)
358	fprintf(totar, "%s\n", INSTALL_FNAME);
359    if (PostInstall)
360	fprintf(totar, "%s\n", POST_INSTALL_FNAME);
361    if (DeInstall)
362	fprintf(totar, "%s\n", DEINSTALL_FNAME);
363    if (PostDeInstall)
364	fprintf(totar, "%s\n", POST_DEINSTALL_FNAME);
365    if (Require)
366	fprintf(totar, "%s\n", REQUIRE_FNAME);
367    if (Display)
368	fprintf(totar, "%s\n", DISPLAY_FNAME);
369    if (Mtree)
370	fprintf(totar, "%s\n", MTREE_FNAME);
371
372    for (p = plist->head; p; p = p->next) {
373	if (p->type == PLIST_FILE)
374	    fprintf(totar, "%s\n", p->name);
375	else if (p->type == PLIST_CWD || p->type == PLIST_SRC)
376	    fprintf(totar, "-C\n%s\n", p->name);
377	else if (p->type == PLIST_IGNORE)
378	     p = p->next;
379    }
380
381    fclose(totar);
382    wait(&ret);
383    /* assume either signal or bad exit is enough for us */
384    if (ret) {
385	cleanup(0);
386	errx(2, "%s: tar command failed with code %d", __func__, ret);
387    }
388}
389
390static void
391sanity_check()
392{
393    if (!Comment) {
394	cleanup(0);
395	errx(2, "%s: required package comment string is missing (-c comment)",
396	    __func__);
397    }
398    if (!Desc) {
399	cleanup(0);
400	errx(2,	"%s: required package description string is missing (-d desc)",
401	    __func__);
402    }
403    if (!Contents) {
404	cleanup(0);
405	errx(2,	"%s: required package contents list is missing (-f [-]file)",
406	    __func__);
407    }
408}
409
410
411/* Clean up those things that would otherwise hang around */
412void
413cleanup(int sig)
414{
415    int in_cleanup = 0;
416
417    if (!in_cleanup) {
418	in_cleanup = 1;
419    	leave_playpen();
420    }
421    if (sig)
422	exit(1);
423}
424
425static int
426create_from_installed(const char *pkg, const char *suf)
427{
428    FILE *fp;
429    Package plist;
430    char homedir[MAXPATHLEN], log_dir[FILENAME_MAX];
431
432    snprintf(log_dir, sizeof(log_dir), "%s/%s", LOG_DIR, InstalledPkg);
433    if (!fexists(log_dir)) {
434	warnx("can't find package '%s' installed!", InstalledPkg);
435	return FALSE;
436    }
437    getcwd(homedir, sizeof(homedir));
438    if (chdir(log_dir) == FAIL) {
439	warnx("can't change directory to '%s'!", log_dir);
440	return FALSE;
441    }
442    /* Suck in the contents list */
443    plist.head = plist.tail = NULL;
444    fp = fopen(CONTENTS_FNAME, "r");
445    if (!fp) {
446	warnx("unable to open %s file", CONTENTS_FNAME);
447	return FALSE;
448    }
449    read_plist(&plist, fp);
450    fclose(fp);
451
452    Install = isfile(INSTALL_FNAME) ? INSTALL_FNAME : NULL;
453    PostInstall = isfile(POST_INSTALL_FNAME) ? POST_INSTALL_FNAME : NULL;
454    DeInstall = isfile(DEINSTALL_FNAME) ? DEINSTALL_FNAME : NULL;
455    PostDeInstall = isfile(POST_DEINSTALL_FNAME) ? POST_DEINSTALL_FNAME : NULL;
456    Require = isfile(REQUIRE_FNAME) ? REQUIRE_FNAME : NULL;
457    Display = isfile(DISPLAY_FNAME) ? DISPLAY_FNAME : NULL;
458    Mtree =isfile(MTREE_FNAME) ?  MTREE_FNAME : NULL;
459
460    make_dist(homedir, pkg, suf, &plist);
461
462    free_plist(&plist);
463    return TRUE;
464}
465