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