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