perform.c revision 154102
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 154102 2006-01-07 22:10:58Z krion $");
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_recursive(const char *, const char *);
38static int create_from_installed(const char *, const char *, const char *);
39
40static char *home;
41
42int
43pkg_perform(char **pkgs)
44{
45    char *pkg = *pkgs;		/* Only one arg to create */
46    char *cp;
47    FILE *pkg_in, *fp;
48    Package plist;
49    int len;
50    const char *suf;
51
52    /* Preliminary setup */
53    if (InstalledPkg == NULL)
54	sanity_check();
55    if (Verbose && !PlistOnly)
56	printf("Creating package %s\n", pkg);
57
58    /* chop suffix off if already specified, remembering if we want to compress  */
59    len = strlen(pkg);
60    if (len > 4) {
61	if (!strcmp(&pkg[len - 4], ".tbz")) {
62	    Zipper = BZIP2;
63	    pkg[len - 4] = '\0';
64	}
65	else if (!strcmp(&pkg[len - 4], ".tgz")) {
66	    Zipper = GZIP;
67	    pkg[len - 4] = '\0';
68	}
69	else if (!strcmp(&pkg[len - 4], ".tar")) {
70	    Zipper = NONE;
71	    pkg[len - 4] = '\0';
72	}
73    }
74    if (Zipper == BZIP2) {
75	suf = "tbz";
76	setenv("BZIP2", "--best", 0);
77    } else if (Zipper == GZIP) {
78	suf = "tgz";
79	setenv("GZIP", "-9", 0);
80    } else
81	suf = "tar";
82
83    if (InstalledPkg != NULL) {
84	char *pkgglob[] = { InstalledPkg, NULL };
85	char **matched, **pkgs;
86	int i, error;
87
88	pkgs = pkgglob;
89	if (MatchType != MATCH_EXACT) {
90		matched = matchinstalled(MatchType, pkgs, &error);
91		if (!error && matched != NULL)
92			pkgs = matched;
93		else if (MatchType != MATCH_GLOB)
94	    		errx(1, "no packages match pattern");
95	}
96	/*
97	 * Is there is only one installed package matching the pattern,
98	 * we need to respect the optional pkg-filename parameter.  If,
99	 * however, the pattern matches several packages, this parameter
100	 * makes no sense and is ignored.
101	 */
102	if (pkgs[1] == NULL) {
103	    if (pkg == InstalledPkg)
104		pkg = *pkgs;
105	    InstalledPkg = *pkgs;
106	    if (!Recursive)
107		return (create_from_installed(InstalledPkg, pkg, suf));
108	    return (create_from_installed_recursive(pkg, suf));
109	}
110	for (i = 0; pkgs[i] != NULL; i++) {
111	    InstalledPkg = pkg = pkgs[i];
112	    if (!Recursive)
113		create_from_installed(pkg, pkg, suf);
114	    else
115	        create_from_installed_recursive(pkg, suf);
116	}
117	return TRUE;
118    }
119
120    get_dash_string(&Comment);
121    get_dash_string(&Desc);
122    if (!strcmp(Contents, "-"))
123	pkg_in = stdin;
124    else {
125	pkg_in = fopen(Contents, "r");
126	if (!pkg_in) {
127	    cleanup(0);
128	    errx(2, "%s: unable to open contents file '%s' for input",
129		__func__, Contents);
130	}
131    }
132    plist.head = plist.tail = NULL;
133
134    /* Stick the dependencies, if any, at the top */
135    if (Pkgdeps) {
136	char **deps, *deporigin;
137	int i;
138	int ndeps = 0;
139
140	if (Verbose && !PlistOnly)
141	    printf("Registering depends:");
142
143	/* Count number of dependencies */
144	for (cp = Pkgdeps; cp != NULL && *cp != '\0';
145			   cp = strpbrk(++cp, " \t\n")) {
146	    ndeps++;
147	}
148
149	if (ndeps != 0) {
150	    /* Create easy to use NULL-terminated list */
151	    deps = alloca(sizeof(*deps) * ndeps + 1);
152	    if (deps == NULL) {
153		errx(2, "%s: alloca() failed", __func__);
154		/* Not reached */
155	    }
156	    for (i = 0; Pkgdeps;) {
157		cp = strsep(&Pkgdeps, " \t\n");
158		if (*cp) {
159		    deps[i] = cp;
160		    i++;
161		}
162	    }
163	    ndeps = i;
164	    deps[ndeps] = NULL;
165
166	    sortdeps(deps);
167	    for (i = 0; i < ndeps; i++) {
168		deporigin = strchr(deps[i], ':');
169		if (deporigin != NULL) {
170		    *deporigin = '\0';
171		    add_plist_top(&plist, PLIST_DEPORIGIN, ++deporigin);
172		}
173		add_plist_top(&plist, PLIST_PKGDEP, deps[i]);
174		if (Verbose && !PlistOnly)
175		    printf(" %s", deps[i]);
176	    }
177	}
178
179	if (Verbose && !PlistOnly)
180	    printf(".\n");
181    }
182
183    /* Put the conflicts directly after the dependencies, if any */
184    if (Conflicts) {
185	if (Verbose && !PlistOnly)
186	    printf("Registering conflicts:");
187	while (Conflicts) {
188	   cp = strsep(&Conflicts, " \t\n");
189	   if (*cp) {
190		add_plist(&plist, PLIST_CONFLICTS, cp);
191		if (Verbose && !PlistOnly)
192		    printf(" %s", cp);
193	   }
194	}
195	if (Verbose && !PlistOnly)
196	    printf(".\n");
197    }
198
199    /* If a SrcDir override is set, add it now */
200    if (SrcDir) {
201	if (Verbose && !PlistOnly)
202	    printf("Using SrcDir value of %s\n", SrcDir);
203	add_plist(&plist, PLIST_SRC, SrcDir);
204    }
205
206    /* Slurp in the packing list */
207    read_plist(&plist, pkg_in);
208
209    /* Prefix should add an @cwd to the packing list */
210    if (Prefix)
211	add_plist_top(&plist, PLIST_CWD, Prefix);
212
213    /* Add the origin if asked, at the top */
214    if (Origin)
215	add_plist_top(&plist, PLIST_ORIGIN, Origin);
216
217    /*
218     * Run down the list and see if we've named it, if not stick in a name
219     * at the top.
220     */
221    if (find_plist(&plist, PLIST_NAME) == NULL)
222	add_plist_top(&plist, PLIST_NAME, basename(pkg));
223
224    if (asprintf(&cp, "PKG_FORMAT_REVISION:%d.%d", PLIST_FMT_VER_MAJOR,
225		 PLIST_FMT_VER_MINOR) == -1) {
226	errx(2, "%s: asprintf() failed", __func__);
227    }
228    add_plist_top(&plist, PLIST_COMMENT, cp);
229    free(cp);
230
231    /*
232     * We're just here for to dump out a revised plist for the FreeBSD ports
233     * hack.  It's not a real create in progress.
234     */
235    if (PlistOnly) {
236	check_list(home, &plist);
237	write_plist(&plist, stdout);
238	exit(0);
239    }
240
241    /* Make a directory to stomp around in */
242    home = make_playpen(PlayPen, 0);
243    signal(SIGINT, cleanup);
244    signal(SIGHUP, cleanup);
245
246    /* Make first "real contents" pass over it */
247    check_list(home, &plist);
248    (void) umask(022);	/*
249			 * Make sure gen'ed directories, files don't have
250			 * group or other write bits.
251			 */
252    /* copy_plist(home, &plist); */
253    /* mark_plist(&plist); */
254
255    /* Now put the release specific items in */
256    add_plist(&plist, PLIST_CWD, ".");
257    write_file(COMMENT_FNAME, Comment);
258    add_plist(&plist, PLIST_IGNORE, NULL);
259    add_plist(&plist, PLIST_FILE, COMMENT_FNAME);
260    add_cksum(&plist, plist.tail, COMMENT_FNAME);
261    write_file(DESC_FNAME, Desc);
262    add_plist(&plist, PLIST_IGNORE, NULL);
263    add_plist(&plist, PLIST_FILE, DESC_FNAME);
264    add_cksum(&plist, plist.tail, DESC_FNAME);
265
266    if (Install) {
267	copy_file(home, Install, INSTALL_FNAME);
268	add_plist(&plist, PLIST_IGNORE, NULL);
269	add_plist(&plist, PLIST_FILE, INSTALL_FNAME);
270	add_cksum(&plist, plist.tail, INSTALL_FNAME);
271    }
272    if (PostInstall) {
273	copy_file(home, PostInstall, POST_INSTALL_FNAME);
274	add_plist(&plist, PLIST_IGNORE, NULL);
275	add_plist(&plist, PLIST_FILE, POST_INSTALL_FNAME);
276	add_cksum(&plist, plist.tail, POST_INSTALL_FNAME);
277    }
278    if (DeInstall) {
279	copy_file(home, DeInstall, DEINSTALL_FNAME);
280	add_plist(&plist, PLIST_IGNORE, NULL);
281	add_plist(&plist, PLIST_FILE, DEINSTALL_FNAME);
282	add_cksum(&plist, plist.tail, DEINSTALL_FNAME);
283    }
284    if (PostDeInstall) {
285	copy_file(home, PostDeInstall, POST_DEINSTALL_FNAME);
286	add_plist(&plist, PLIST_IGNORE, NULL);
287	add_plist(&plist, PLIST_FILE, POST_DEINSTALL_FNAME);
288	add_cksum(&plist, plist.tail, POST_DEINSTALL_FNAME);
289    }
290    if (Require) {
291	copy_file(home, Require, REQUIRE_FNAME);
292	add_plist(&plist, PLIST_IGNORE, NULL);
293	add_plist(&plist, PLIST_FILE, REQUIRE_FNAME);
294	add_cksum(&plist, plist.tail, REQUIRE_FNAME);
295    }
296    if (Display) {
297	copy_file(home, Display, DISPLAY_FNAME);
298	add_plist(&plist, PLIST_IGNORE, NULL);
299	add_plist(&plist, PLIST_FILE, DISPLAY_FNAME);
300	add_cksum(&plist, plist.tail, DISPLAY_FNAME);
301	add_plist(&plist, PLIST_DISPLAY, DISPLAY_FNAME);
302    }
303    if (Mtree) {
304	copy_file(home, Mtree, MTREE_FNAME);
305	add_plist(&plist, PLIST_IGNORE, NULL);
306	add_plist(&plist, PLIST_FILE, MTREE_FNAME);
307	add_cksum(&plist, plist.tail, MTREE_FNAME);
308	add_plist(&plist, PLIST_MTREE, MTREE_FNAME);
309    }
310
311    /* Finally, write out the packing list */
312    fp = fopen(CONTENTS_FNAME, "w");
313    if (!fp) {
314	cleanup(0);
315	errx(2, "%s: can't open file %s for writing",
316	    __func__, CONTENTS_FNAME);
317    }
318    write_plist(&plist, fp);
319    if (fclose(fp)) {
320	cleanup(0);
321	errx(2, "%s: error while closing %s",
322	    __func__, CONTENTS_FNAME);
323    }
324
325    /* And stick it into a tar ball */
326    make_dist(home, pkg, suf, &plist);
327
328    /* Cleanup */
329    free(Comment);
330    free(Desc);
331    free_plist(&plist);
332    leave_playpen();
333    return TRUE;	/* Success */
334}
335
336static void
337make_dist(const char *homedir, const char *pkg, const char *suff, Package *plist)
338{
339    char tball[FILENAME_MAX];
340    PackingList p;
341    int ret;
342    const char *args[50];	/* Much more than enough. */
343    int nargs = 0;
344    int pipefds[2];
345    FILE *totar;
346    pid_t pid;
347    const char *cname;
348    char *prefix = NULL;
349
350
351    args[nargs++] = "tar";	/* argv[0] */
352
353    if (*pkg == '/')
354	snprintf(tball, FILENAME_MAX, "%s.%s", pkg, suff);
355    else
356	snprintf(tball, FILENAME_MAX, "%s/%s.%s", homedir, pkg, suff);
357
358    args[nargs++] = "-c";
359    args[nargs++] = "-f";
360    args[nargs++] = tball;
361    if (strchr(suff, 'z')) {	/* Compress/gzip/bzip2? */
362	if (Zipper == BZIP2) {
363	    args[nargs++] = "-j";
364	    cname = "bzip'd ";
365	}
366	else {
367	    args[nargs++] = "-z";
368	    cname = "gzip'd ";
369	}
370    } else {
371	cname = "";
372    }
373    if (Dereference)
374	args[nargs++] = "-h";
375    if (ExcludeFrom) {
376	args[nargs++] = "-X";
377	args[nargs++] = ExcludeFrom;
378    }
379    args[nargs++] = "-T";	/* Take filenames from file instead of args. */
380    args[nargs++] = "-";	/* Use stdin for the file. */
381    args[nargs] = NULL;
382
383    if (Verbose)
384	printf("Creating %star ball in '%s'\n", cname, tball);
385
386    /* Set up a pipe for passing the filenames, and fork off a tar process. */
387    if (pipe(pipefds) == -1) {
388	cleanup(0);
389	errx(2, "%s: cannot create pipe", __func__);
390    }
391    if ((pid = fork()) == -1) {
392	cleanup(0);
393	errx(2, "%s: cannot fork process for tar", __func__);
394    }
395    if (pid == 0) {	/* The child */
396	dup2(pipefds[0], 0);
397	close(pipefds[0]);
398	close(pipefds[1]);
399	execv("/usr/bin/tar", (char * const *)(uintptr_t)args);
400	cleanup(0);
401	errx(2, "%s: failed to execute tar command", __func__);
402    }
403
404    /* Meanwhile, back in the parent process ... */
405    close(pipefds[0]);
406    if ((totar = fdopen(pipefds[1], "w")) == NULL) {
407	cleanup(0);
408	errx(2, "%s: fdopen failed", __func__);
409    }
410
411    fprintf(totar, "%s\n", CONTENTS_FNAME);
412    fprintf(totar, "%s\n", COMMENT_FNAME);
413    fprintf(totar, "%s\n", DESC_FNAME);
414
415    if (Install)
416	fprintf(totar, "%s\n", INSTALL_FNAME);
417    if (PostInstall)
418	fprintf(totar, "%s\n", POST_INSTALL_FNAME);
419    if (DeInstall)
420	fprintf(totar, "%s\n", DEINSTALL_FNAME);
421    if (PostDeInstall)
422	fprintf(totar, "%s\n", POST_DEINSTALL_FNAME);
423    if (Require)
424	fprintf(totar, "%s\n", REQUIRE_FNAME);
425    if (Display)
426	fprintf(totar, "%s\n", DISPLAY_FNAME);
427    if (Mtree)
428	fprintf(totar, "%s\n", MTREE_FNAME);
429
430    for (p = plist->head; p; p = p->next) {
431	if (p->type == PLIST_FILE)
432	    fprintf(totar, "%s\n", p->name);
433	else if (p->type == PLIST_CWD && p->name == NULL)
434	    fprintf(totar, "-C\n%s\n", prefix);
435	else if (p->type == PLIST_CWD && BaseDir && p->name && p->name[0] == '/')
436	    fprintf(totar, "-C\n%s%s\n", BaseDir, p->name);
437	else if (p->type == PLIST_CWD || p->type == PLIST_SRC)
438	    fprintf(totar, "-C\n%s\n", p->name);
439	else if (p->type == PLIST_IGNORE)
440	     p = p->next;
441	if (p->type == PLIST_CWD && !prefix)
442	    prefix = p->name;
443
444    }
445
446    fclose(totar);
447    wait(&ret);
448    /* assume either signal or bad exit is enough for us */
449    if (ret) {
450	cleanup(0);
451	errx(2, "%s: tar command failed with code %d", __func__, ret);
452    }
453}
454
455static void
456sanity_check()
457{
458    if (!Comment) {
459	cleanup(0);
460	errx(2, "%s: required package comment string is missing (-c comment)",
461	    __func__);
462    }
463    if (!Desc) {
464	cleanup(0);
465	errx(2,	"%s: required package description string is missing (-d desc)",
466	    __func__);
467    }
468    if (!Contents) {
469	cleanup(0);
470	errx(2,	"%s: required package contents list is missing (-f [-]file)",
471	    __func__);
472    }
473}
474
475
476/* Clean up those things that would otherwise hang around */
477void
478cleanup(int sig)
479{
480    int in_cleanup = 0;
481
482    if (!in_cleanup) {
483	in_cleanup = 1;
484    	leave_playpen();
485    }
486    if (sig)
487	exit(1);
488}
489
490static int
491create_from_installed_recursive(const char *pkg, const char *suf)
492{
493    FILE *fp;
494    Package plist;
495    PackingList p;
496    char tmp[PATH_MAX];
497    int rval;
498
499    if (!create_from_installed(InstalledPkg, pkg, suf))
500	return FALSE;
501    snprintf(tmp, sizeof(tmp), "%s/%s/%s", LOG_DIR, InstalledPkg, CONTENTS_FNAME);
502    if (!fexists(tmp)) {
503	warnx("can't find package '%s' installed!", InstalledPkg);
504	return FALSE;
505    }
506    /* Suck in the contents list */
507    plist.head = plist.tail = NULL;
508    fp = fopen(tmp, "r");
509    if (!fp) {
510	warnx("unable to open %s file", tmp);
511	return FALSE;
512    }
513    read_plist(&plist, fp);
514    fclose(fp);
515    rval = TRUE;
516    for (p = plist.head; p ; p = p->next) {
517	if (p->type != PLIST_PKGDEP)
518	    continue;
519	if (Verbose)
520	    printf("Creating package %s\n", p->name);
521	if (!create_from_installed(p->name, p->name, suf)) {
522	    rval = FALSE;
523	    break;
524	}
525    }
526    free_plist(&plist);
527    return rval;
528}
529
530static int
531create_from_installed(const char *ipkg, const char *pkg, const char *suf)
532{
533    FILE *fp;
534    Package plist;
535    char homedir[MAXPATHLEN], log_dir[FILENAME_MAX];
536
537    snprintf(log_dir, sizeof(log_dir), "%s/%s", LOG_DIR, ipkg);
538    if (!fexists(log_dir)) {
539	warnx("can't find package '%s' installed!", ipkg);
540	return FALSE;
541    }
542    getcwd(homedir, sizeof(homedir));
543    if (chdir(log_dir) == FAIL) {
544	warnx("can't change directory to '%s'!", log_dir);
545	return FALSE;
546    }
547    /* Suck in the contents list */
548    plist.head = plist.tail = NULL;
549    fp = fopen(CONTENTS_FNAME, "r");
550    if (!fp) {
551	warnx("unable to open %s file", CONTENTS_FNAME);
552	return FALSE;
553    }
554    read_plist(&plist, fp);
555    fclose(fp);
556
557    Install = isfile(INSTALL_FNAME) ? (char *)INSTALL_FNAME : NULL;
558    PostInstall = isfile(POST_INSTALL_FNAME) ?
559	(char *)POST_INSTALL_FNAME : NULL;
560    DeInstall = isfile(DEINSTALL_FNAME) ? (char *)DEINSTALL_FNAME : NULL;
561    PostDeInstall = isfile(POST_DEINSTALL_FNAME) ?
562	(char *)POST_DEINSTALL_FNAME : NULL;
563    Require = isfile(REQUIRE_FNAME) ? (char *)REQUIRE_FNAME : NULL;
564    Display = isfile(DISPLAY_FNAME) ? (char *)DISPLAY_FNAME : NULL;
565    Mtree = isfile(MTREE_FNAME) ?  (char *)MTREE_FNAME : NULL;
566
567    make_dist(homedir, pkg, suf, &plist);
568
569    free_plist(&plist);
570    if (chdir(homedir) == FAIL) {
571	warnx("can't change directory to '%s'!", homedir);
572	return FALSE;
573    }
574    return TRUE;
575}
576