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