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