Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.sbin / pkg_install / create / perform.c
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: src/usr.sbin/pkg_install/create/perform.c,v 1.49.2.18 2002/08/31 19:25:54 obrien Exp $");
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
35 static void sanity_check(void);
36 static void make_dist(const char *, const char *, const char *, Package *);
37 static int create_from_installed(const char *, const char *);
38
39 static char *home;
40
41 int
42 pkg_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
276 static void
277 make_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
386 static void
387 sanity_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 */
408 void
409 cleanup(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
421 static int
422 create_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 }