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 37static void sanity_check(void); 38static void make_dist(const char *, const char *, const char *, Package *); 39static int create_from_installed_recursive(const char *, const char *); 40static int create_from_installed(const char *, const char *, const char *); 41 42int 43pkg_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 353static void 354make_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 487static void 488sanity_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 */ 509void 510cleanup(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 522static int 523create_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 562static int 563create_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} 608