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