2 * FreeBSD install - a package for the installation and maintenance
3 * of non-core utilities.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
17 * This is the main body of the create module.
21 #include <sys/cdefs.h>
22 __FBSDID("$FreeBSD$");
31 #include <sys/types.h>
33 #include <sys/syslimits.h>
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 *);
43 pkg_perform(char **pkgs)
45 static const char *home;
46 char *pkg = *pkgs; /* Only one arg to create */
53 /* Preliminary setup */
54 if (InstalledPkg == NULL)
56 if (Verbose && !PlistOnly)
57 printf("Creating package %s\n", pkg);
59 /* chop suffix off if already specified, remembering if we want to compress */
62 if (!strcmp(&pkg[len - 4], ".tbz")) {
66 else if (!strcmp(&pkg[len - 4], ".tgz")) {
70 else if (!strcmp(&pkg[len - 4], ".txz")) {
74 else if (!strcmp(&pkg[len - 4], ".tar")) {
79 if (Zipper == BZIP2) {
81 setenv("BZIP2", "--best", 0);
82 } else if (Zipper == GZIP) {
84 setenv("GZIP", "-9", 0);
85 } else if (Zipper == XZ) {
90 if (InstalledPkg != NULL) {
91 char *pkgglob[] = { InstalledPkg, NULL };
92 char **matched, **pkgs;
96 if (MatchType != MATCH_EXACT) {
97 matched = matchinstalled(MatchType, pkgs, &error);
98 if (!error && matched != NULL)
100 else if (MatchType != MATCH_GLOB)
101 errx(1, "no packages match pattern");
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.
109 if (pkgs[1] == NULL) {
110 if (pkg == InstalledPkg)
112 InstalledPkg = *pkgs;
114 return (create_from_installed(InstalledPkg, pkg, suf));
115 return (create_from_installed_recursive(pkg, suf));
117 for (i = 0; pkgs[i] != NULL; i++) {
118 InstalledPkg = pkg = pkgs[i];
120 create_from_installed(pkg, pkg, suf);
122 create_from_installed_recursive(pkg, suf);
127 get_dash_string(&Comment);
128 get_dash_string(&Desc);
129 if (!strcmp(Contents, "-"))
132 pkg_in = fopen(Contents, "r");
135 errx(2, "%s: unable to open contents file '%s' for input",
139 plist.head = plist.tail = NULL;
141 /* Stick the dependencies, if any, at the top */
143 char **deps, *deporigin;
147 if (Verbose && !PlistOnly)
148 printf("Registering depends:");
150 /* Count number of dependencies */
151 for (cp = Pkgdeps; cp != NULL && *cp != '\0';
152 cp = strpbrk(++cp, " \t\n")) {
157 /* Create easy to use NULL-terminated list */
158 deps = alloca(sizeof(*deps) * ndeps + 1);
160 errx(2, "%s: alloca() failed", __func__);
163 for (i = 0; Pkgdeps;) {
164 cp = strsep(&Pkgdeps, " \t\n");
174 for (i = 0; i < ndeps; i++) {
175 deporigin = strchr(deps[i], ':');
176 if (deporigin != NULL) {
178 add_plist_top(&plist, PLIST_DEPORIGIN, ++deporigin);
180 add_plist_top(&plist, PLIST_PKGDEP, deps[i]);
181 if (Verbose && !PlistOnly)
182 printf(" %s", deps[i]);
186 if (Verbose && !PlistOnly)
190 /* Put the conflicts directly after the dependencies, if any */
192 if (Verbose && !PlistOnly)
193 printf("Registering conflicts:");
195 cp = strsep(&Conflicts, " \t\n");
197 add_plist(&plist, PLIST_CONFLICTS, cp);
198 if (Verbose && !PlistOnly)
202 if (Verbose && !PlistOnly)
206 /* If a SrcDir override is set, add it now */
208 if (Verbose && !PlistOnly)
209 printf("Using SrcDir value of %s\n", SrcDir);
210 add_plist(&plist, PLIST_SRC, SrcDir);
213 /* Slurp in the packing list */
214 read_plist(&plist, pkg_in);
216 /* Prefix should add an @cwd to the packing list */
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);
224 add_plist_top(&plist, PLIST_CWD, Prefix);
228 /* Add the origin if asked, at the top */
230 add_plist_top(&plist, PLIST_ORIGIN, Origin);
233 * Run down the list and see if we've named it, if not stick in a name
236 if (find_plist(&plist, PLIST_NAME) == NULL)
237 add_plist_top(&plist, PLIST_NAME, basename(pkg));
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__);
243 add_plist_top(&plist, PLIST_COMMENT, cp);
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.
251 check_list(home, &plist);
252 write_plist(&plist, stdout);
256 /* Make a directory to stomp around in */
257 home = make_playpen(PlayPen, 0);
258 signal(SIGINT, cleanup);
259 signal(SIGHUP, cleanup);
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.
267 /* copy_plist(home, &plist); */
268 /* mark_plist(&plist); */
270 /* Now put the release specific items in */
272 add_plist(&plist, PLIST_CWD, ".");
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);
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);
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);
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);
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);
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);
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);
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);
328 /* Finally, write out the packing list */
329 fp = fopen(CONTENTS_FNAME, "w");
332 errx(2, "%s: can't open file %s for writing",
333 __func__, CONTENTS_FNAME);
335 write_plist(&plist, fp);
338 errx(2, "%s: error while closing %s",
339 __func__, CONTENTS_FNAME);
342 /* And stick it into a tar ball */
343 make_dist(home, pkg, suf, &plist);
350 return TRUE; /* Success */
354 make_dist(const char *homedir, const char *pkg, const char *suff, Package *plist)
357 char tball[FILENAME_MAX];
360 const char *args[50]; /* Much more than enough. */
369 args[nargs++] = "tar"; /* argv[0] */
372 snprintf(tball, FILENAME_MAX, "%s.%s", pkg, suff);
374 snprintf(tball, FILENAME_MAX, "%s/%s.%s", homedir, pkg, suff);
377 * If the package tarball exists already, and we are running in `no
378 * clobber' mode, skip this package.
380 if (stat(tball, &sb) == 0 && Regenerate == FALSE) {
382 printf("Skipping package '%s'. It already exists.\n", tball);
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";
394 else if (Zipper == XZ) {
395 args[nargs++] = "-J";
399 args[nargs++] = "-z";
406 args[nargs++] = "-h";
408 args[nargs++] = "-X";
409 args[nargs++] = ExcludeFrom;
411 args[nargs++] = "-T"; /* Take filenames from file instead of args. */
412 args[nargs++] = "-"; /* Use stdin for the file. */
416 printf("Creating %star ball in '%s'\n", cname, tball);
418 /* Set up a pipe for passing the filenames, and fork off a tar process. */
419 if (pipe(pipefds) == -1) {
421 errx(2, "%s: cannot create pipe", __func__);
423 if ((pid = fork()) == -1) {
425 errx(2, "%s: cannot fork process for tar", __func__);
427 if (pid == 0) { /* The child */
431 execv("/usr/bin/tar", (char * const *)(uintptr_t)args);
433 errx(2, "%s: failed to execute tar command", __func__);
436 /* Meanwhile, back in the parent process ... */
438 if ((totar = fdopen(pipefds[1], "w")) == NULL) {
440 errx(2, "%s: fdopen failed", __func__);
443 fprintf(totar, "%s\n", CONTENTS_FNAME);
444 fprintf(totar, "%s\n", COMMENT_FNAME);
445 fprintf(totar, "%s\n", DESC_FNAME);
448 fprintf(totar, "%s\n", INSTALL_FNAME);
450 fprintf(totar, "%s\n", POST_INSTALL_FNAME);
452 fprintf(totar, "%s\n", DEINSTALL_FNAME);
454 fprintf(totar, "%s\n", POST_DEINSTALL_FNAME);
456 fprintf(totar, "%s\n", REQUIRE_FNAME);
458 fprintf(totar, "%s\n", DISPLAY_FNAME);
460 fprintf(totar, "%s\n", MTREE_FNAME);
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)
473 if (p->type == PLIST_CWD && !prefix)
480 /* assume either signal or bad exit is enough for us */
483 errx(2, "%s: tar command failed with code %d", __func__, ret);
492 errx(2, "%s: required package comment string is missing (-c comment)",
497 errx(2, "%s: required package description string is missing (-d desc)",
502 errx(2, "%s: required package contents list is missing (-f [-]file)",
508 /* Clean up those things that would otherwise hang around */
523 create_from_installed_recursive(const char *pkg, const char *suf)
531 if (!create_from_installed(InstalledPkg, pkg, suf))
533 snprintf(tmp, sizeof(tmp), "%s/%s/%s", LOG_DIR, InstalledPkg, CONTENTS_FNAME);
535 warnx("can't find package '%s' installed!", InstalledPkg);
538 /* Suck in the contents list */
539 plist.head = plist.tail = NULL;
540 fp = fopen(tmp, "r");
542 warnx("unable to open %s file", tmp);
545 read_plist(&plist, fp);
548 for (p = plist.head; p ; p = p->next) {
549 if (p->type != PLIST_PKGDEP)
552 printf("Creating package %s\n", p->name);
553 if (!create_from_installed(p->name, p->name, suf)) {
563 create_from_installed(const char *ipkg, const char *pkg, const char *suf)
567 char homedir[MAXPATHLEN], log_dir[FILENAME_MAX];
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);
574 getcwd(homedir, sizeof(homedir));
575 if (chdir(log_dir) == FAIL) {
576 warnx("can't change directory to '%s'!", log_dir);
579 /* Suck in the contents list */
580 plist.head = plist.tail = NULL;
581 fp = fopen(CONTENTS_FNAME, "r");
583 warnx("unable to open %s file", CONTENTS_FNAME);
586 read_plist(&plist, fp);
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;
599 make_dist(homedir, pkg, suf, &plist);
602 if (chdir(homedir) == FAIL) {
603 warnx("can't change directory to '%s'!", homedir);