perform.c revision 84745
1#ifndef lint 2static const char rcsid[] = 3 "$FreeBSD: head/usr.sbin/pkg_install/add/perform.c 84745 2001-10-10 06:58:42Z sobomax $"; 4#endif 5 6/* 7 * FreeBSD install - a package for the installation and maintainance 8 * of non-core utilities. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * Jordan K. Hubbard 20 * 18 July 1993 21 * 22 * This is the main body of the add module. 23 * 24 */ 25 26#include <err.h> 27#include <paths.h> 28#include "lib.h" 29#include "add.h" 30 31#include <libgen.h> 32#include <signal.h> 33#include <sys/wait.h> 34 35static int pkg_do(char *); 36static int sanity_check(char *); 37static char LogDir[FILENAME_MAX]; 38static int zapLogDir; /* Should we delete LogDir? */ 39 40int 41pkg_perform(char **pkgs) 42{ 43 int i, err_cnt = 0; 44 45 signal(SIGINT, cleanup); 46 signal(SIGHUP, cleanup); 47 48 if (AddMode == SLAVE) 49 err_cnt = pkg_do(NULL); 50 else { 51 for (i = 0; pkgs[i]; i++) 52 err_cnt += pkg_do(pkgs[i]); 53 } 54 return err_cnt; 55} 56 57static Package Plist; 58static char *Home; 59 60/* 61 * This is seriously ugly code following. Written very fast! 62 * [And subsequently made even worse.. Sigh! This code was just born 63 * to be hacked, I guess.. :) -jkh] 64 */ 65static int 66pkg_do(char *pkg) 67{ 68 char pkg_fullname[FILENAME_MAX]; 69 char playpen[FILENAME_MAX]; 70 char extract_contents[FILENAME_MAX]; 71 char *where_to, *extract; 72 FILE *cfile; 73 int code; 74 PackingList p; 75 struct stat sb; 76 int inPlace; 77 /* support for separate pre/post install scripts */ 78 int new_m = 0; 79 char pre_script[FILENAME_MAX] = INSTALL_FNAME; 80 char post_script[FILENAME_MAX]; 81 char pre_arg[FILENAME_MAX], post_arg[FILENAME_MAX]; 82 83 code = 0; 84 zapLogDir = 0; 85 LogDir[0] = '\0'; 86 strcpy(playpen, FirstPen); 87 inPlace = 0; 88 89 /* Are we coming in for a second pass, everything already extracted? */ 90 if (!pkg) { 91 fgets(playpen, FILENAME_MAX, stdin); 92 playpen[strlen(playpen) - 1] = '\0'; /* pesky newline! */ 93 if (chdir(playpen) == FAIL) { 94 warnx("pkg_add in SLAVE mode can't chdir to %s", playpen); 95 return 1; 96 } 97 read_plist(&Plist, stdin); 98 where_to = playpen; 99 } 100 /* Nope - do it now */ 101 else { 102 /* Is it an ftp://foo.bar.baz/file.tgz specification? */ 103 if (isURL(pkg)) { 104 if (!(Home = fileGetURL(NULL, pkg))) { 105 warnx("unable to fetch '%s' by URL", pkg); 106 return 1; 107 } 108 where_to = Home; 109 strcpy(pkg_fullname, pkg); 110 cfile = fopen(CONTENTS_FNAME, "r"); 111 if (!cfile) { 112 warnx( 113 "unable to open table of contents file '%s' - not a package?", 114 CONTENTS_FNAME); 115 goto bomb; 116 } 117 read_plist(&Plist, cfile); 118 fclose(cfile); 119 } 120 else { 121 strcpy(pkg_fullname, pkg); /* 122 * Copy for sanity's sake, 123 * could remove pkg_fullname 124 */ 125 if (strcmp(pkg, "-")) { 126 if (stat(pkg_fullname, &sb) == FAIL) { 127 warnx("can't stat package file '%s'", pkg_fullname); 128 goto bomb; 129 } 130 sprintf(extract_contents, "--fast-read %s", CONTENTS_FNAME); 131 extract = extract_contents; 132 } 133 else { 134 extract = NULL; 135 sb.st_size = 100000; /* Make up a plausible average size */ 136 } 137 Home = make_playpen(playpen, sb.st_size * 4); 138 if (!Home) 139 errx(1, "unable to make playpen for %qd bytes", (long long)sb.st_size * 4); 140 where_to = Home; 141 /* Since we can call ourselves recursively, keep notes on where we came from */ 142 if (!getenv("_TOP")) 143 setenv("_TOP", Home, 1); 144 if (unpack(pkg_fullname, extract)) { 145 warnx( 146 "unable to extract table of contents file from '%s' - not a package?", 147 pkg_fullname); 148 goto bomb; 149 } 150 cfile = fopen(CONTENTS_FNAME, "r"); 151 if (!cfile) { 152 warnx( 153 "unable to open table of contents file '%s' - not a package?", 154 CONTENTS_FNAME); 155 goto bomb; 156 } 157 read_plist(&Plist, cfile); 158 fclose(cfile); 159 160 /* Extract directly rather than moving? Oh goodie! */ 161 if (find_plist_option(&Plist, "extract-in-place")) { 162 if (Verbose) 163 printf("Doing in-place extraction for %s\n", pkg_fullname); 164 p = find_plist(&Plist, PLIST_CWD); 165 if (p) { 166 if (!isdir(p->name) && !Fake) { 167 if (Verbose) 168 printf("Desired prefix of %s does not exist, creating..\n", p->name); 169 vsystem("mkdir -p %s", p->name); 170 if (chdir(p->name) == -1) { 171 warn("unable to change directory to '%s'", p->name); 172 goto bomb; 173 } 174 } 175 where_to = p->name; 176 inPlace = 1; 177 } 178 else { 179 warnx( 180 "no prefix specified in '%s' - this is a bad package!", 181 pkg_fullname); 182 goto bomb; 183 } 184 } 185 186 /* 187 * Apply a crude heuristic to see how much space the package will 188 * take up once it's unpacked. I've noticed that most packages 189 * compress an average of 75%, so multiply by 4 for good measure. 190 */ 191 192 if (!inPlace && min_free(playpen) < sb.st_size * 4) { 193 warnx("projected size of %qd exceeds available free space.\n" 194"Please set your PKG_TMPDIR variable to point to a location with more\n" 195 "free space and try again", (long long)sb.st_size * 4); 196 warnx("not extracting %s\ninto %s, sorry!", 197 pkg_fullname, where_to); 198 goto bomb; 199 } 200 201 /* If this is a direct extract and we didn't want it, stop now */ 202 if (inPlace && Fake) 203 goto success; 204 205 /* Finally unpack the whole mess */ 206 if (unpack(pkg_fullname, NULL)) { 207 warnx("unable to extract '%s'!", pkg_fullname); 208 goto bomb; 209 } 210 } 211 212 /* Check for sanity and dependencies */ 213 if (sanity_check(pkg)) 214 goto bomb; 215 216 /* If we're running in MASTER mode, just output the plist and return */ 217 if (AddMode == MASTER) { 218 printf("%s\n", where_playpen()); 219 write_plist(&Plist, stdout); 220 return 0; 221 } 222 } 223 224 /* 225 * If we have a prefix, delete the first one we see and add this 226 * one in place of it. 227 */ 228 if (Prefix) { 229 delete_plist(&Plist, FALSE, PLIST_CWD, NULL); 230 add_plist_top(&Plist, PLIST_CWD, Prefix); 231 } 232 233 setenv(PKG_PREFIX_VNAME, (p = find_plist(&Plist, PLIST_CWD)) ? p->name : ".", 1); 234 /* Protect against old packages with bogus @name fields */ 235 (const char *)PkgName = (p = find_plist(&Plist, PLIST_NAME)) ? p->name : "anonymous"; 236 237 /* See if we're already registered */ 238 sprintf(LogDir, "%s/%s", LOG_DIR, PkgName); 239 if (isdir(LogDir) && !Force) { 240 warnx("package '%s' already recorded as installed", PkgName); 241 code = 1; 242 goto success; /* close enough for government work */ 243 } 244 245 /* Now check the packing list for dependencies */ 246 for (p = Plist.head; p ; p = p->next) { 247 if (p->type != PLIST_PKGDEP) 248 continue; 249 if (Verbose) 250 printf("Package '%s' depends on '%s'.\n", PkgName, p->name); 251 if (vsystem("pkg_info -e %s", p->name)) { 252 char path[FILENAME_MAX], *cp = NULL; 253 254 if (!Fake) { 255 if (!isURL(pkg) && !getenv("PKG_ADD_BASE")) { 256 snprintf(path, FILENAME_MAX, "%s/%s.tgz", getenv("_TOP"), p->name); 257 if (fexists(path)) 258 cp = path; 259 else 260 cp = fileFindByPath(pkg, p->name); 261 if (cp) { 262 if (Verbose) 263 printf("Loading it from %s.\n", cp); 264 if (vsystem("pkg_add %s'%s'", Verbose ? "-v " : "", cp)) { 265 warnx("autoload of dependency '%s' failed%s", 266 cp, Force ? " (proceeding anyway)" : "!"); 267 if (!Force) 268 ++code; 269 } 270 } 271 else { 272 warnx("could not find package %s %s", 273 p->name, Force ? " (proceeding anyway)" : "!"); 274 if (!Force) 275 ++code; 276 } 277 } 278 else if ((cp = fileGetURL(pkg, p->name)) != NULL) { 279 if (Verbose) 280 printf("Finished loading %s over FTP.\n", p->name); 281 if (!fexists("+CONTENTS")) { 282 warnx("autoloaded package %s has no +CONTENTS file?", 283 p->name); 284 if (!Force) 285 ++code; 286 } 287 else if (vsystem("(pwd; cat +CONTENTS) | pkg_add %s-S", Verbose ? "-v " : "")) { 288 warnx("pkg_add of dependency '%s' failed%s", 289 p->name, Force ? " (proceeding anyway)" : "!"); 290 if (!Force) 291 ++code; 292 } 293 else if (Verbose) 294 printf("\t'%s' loaded successfully.\n", p->name); 295 /* Nuke the temporary playpen */ 296 leave_playpen(); 297 } 298 } 299 else { 300 if (Verbose) 301 printf("and was not found%s.\n", Force ? " (proceeding anyway)" : ""); 302 else 303 printf("Package dependency %s for %s not found%s\n", p->name, pkg, 304 Force ? " (proceeding anyway)" : "!"); 305 if (!Force) 306 ++code; 307 } 308 } 309 else if (Verbose) 310 printf(" - already installed.\n"); 311 } 312 313 if (code != 0) 314 goto bomb; 315 316 /* Look for the requirements file */ 317 if (fexists(REQUIRE_FNAME)) { 318 vsystem("chmod +x %s", REQUIRE_FNAME); /* be sure */ 319 if (Verbose) 320 printf("Running requirements file first for %s..\n", PkgName); 321 if (!Fake && vsystem("./%s %s INSTALL", REQUIRE_FNAME, PkgName)) { 322 warnx("package %s fails requirements %s", pkg_fullname, 323 Force ? "installing anyway" : "- not installed"); 324 if (!Force) { 325 code = 1; 326 goto success; /* close enough for government work */ 327 } 328 } 329 } 330 331 /* 332 * Test whether to use the old method of passing tokens to installation 333 * scripts, and set appropriate variables.. 334 */ 335 336 if (fexists(POST_INSTALL_FNAME)) { 337 new_m = 1; 338 sprintf(post_script, "%s", POST_INSTALL_FNAME); 339 pre_arg[0] = '\0'; 340 post_arg[0] = '\0'; 341 } else { 342 if (fexists(INSTALL_FNAME)) { 343 sprintf(post_script, "%s", INSTALL_FNAME); 344 sprintf(pre_arg, "PRE-INSTALL"); 345 sprintf(post_arg, "POST-INSTALL"); 346 } 347 } 348 349 /* If we're really installing, and have an installation file, run it */ 350 if (!NoInstall && fexists(pre_script)) { 351 vsystem("chmod +x %s", pre_script); /* make sure */ 352 if (Verbose) 353 printf("Running pre-install for %s..\n", PkgName); 354 if (!Fake && vsystem("./%s %s %s", pre_script, PkgName, pre_arg)) { 355 warnx("install script returned error status"); 356 unlink(pre_script); 357 code = 1; 358 goto success; /* nothing to uninstall yet */ 359 } 360 } 361 362 /* Now finally extract the entire show if we're not going direct */ 363 if (!inPlace && !Fake) 364 extract_plist(".", &Plist); 365 366 if (!Fake && fexists(MTREE_FNAME)) { 367 if (Verbose) 368 printf("Running mtree for %s..\n", PkgName); 369 p = find_plist(&Plist, PLIST_CWD); 370 if (Verbose) 371 printf("mtree -U -f %s -d -e -p %s >%s\n", MTREE_FNAME, p ? p->name : "/", _PATH_DEVNULL); 372 if (!Fake) { 373 if (vsystem("/usr/sbin/mtree -U -f %s -d -e -p %s >%s", MTREE_FNAME, p ? p->name : "/", _PATH_DEVNULL)) 374 warnx("mtree returned a non-zero status - continuing"); 375 } 376 } 377 378 /* Run the installation script one last time? */ 379 if (!NoInstall && fexists(post_script)) { 380 vsystem("chmod +x %s", post_script); /* make sure */ 381 if (Verbose) 382 printf("Running post-install for %s..\n", PkgName); 383 if (!Fake && vsystem("./%s %s %s", post_script, PkgName, post_arg)) { 384 warnx("install script returned error status"); 385 unlink(post_script); 386 code = 1; 387 goto fail; 388 } 389 } 390 391 /* Time to record the deed? */ 392 if (!NoRecord && !Fake) { 393 char contents[FILENAME_MAX]; 394 FILE *contfile; 395 396 if (getuid() != 0) 397 warnx("not running as root - trying to record install anyway"); 398 if (!PkgName) { 399 warnx("no package name! can't record package, sorry"); 400 code = 1; 401 goto success; /* well, partial anyway */ 402 } 403 sprintf(LogDir, "%s/%s", LOG_DIR, PkgName); 404 zapLogDir = 1; 405 if (Verbose) 406 printf("Attempting to record package into %s..\n", LogDir); 407 if (make_hierarchy(LogDir)) { 408 warnx("can't record package into '%s', you're on your own!", 409 LogDir); 410 bzero(LogDir, FILENAME_MAX); 411 code = 1; 412 goto success; /* close enough for government work */ 413 } 414 /* Make sure pkg_info can read the entry */ 415 vsystem("chmod a+rx %s", LogDir); 416 move_file(".", DESC_FNAME, LogDir); 417 move_file(".", COMMENT_FNAME, LogDir); 418 if (fexists(INSTALL_FNAME)) 419 move_file(".", INSTALL_FNAME, LogDir); 420 if (fexists(POST_INSTALL_FNAME)) 421 move_file(".", POST_INSTALL_FNAME, LogDir); 422 if (fexists(DEINSTALL_FNAME)) 423 move_file(".", DEINSTALL_FNAME, LogDir); 424 if (fexists(POST_DEINSTALL_FNAME)) 425 move_file(".", POST_DEINSTALL_FNAME, LogDir); 426 if (fexists(REQUIRE_FNAME)) 427 move_file(".", REQUIRE_FNAME, LogDir); 428 if (fexists(DISPLAY_FNAME)) 429 move_file(".", DISPLAY_FNAME, LogDir); 430 if (fexists(MTREE_FNAME)) 431 move_file(".", MTREE_FNAME, LogDir); 432 sprintf(contents, "%s/%s", LogDir, CONTENTS_FNAME); 433 contfile = fopen(contents, "w"); 434 if (!contfile) { 435 warnx("can't open new contents file '%s'! can't register pkg", 436 contents); 437 goto success; /* can't log, but still keep pkg */ 438 } 439 write_plist(&Plist, contfile); 440 fclose(contfile); 441 for (p = Plist.head; p ; p = p->next) { 442 if (p->type != PLIST_PKGDEP) 443 continue; 444 if (Verbose) 445 printf("Attempting to record dependency on package '%s'\n", p->name); 446 sprintf(contents, "%s/%s/%s", LOG_DIR, basename(p->name), 447 REQUIRED_BY_FNAME); 448 contfile = fopen(contents, "a"); 449 if (!contfile) 450 warnx("can't open dependency file '%s'!\n" 451 "dependency registration is incomplete", contents); 452 else { 453 fprintf(contfile, "%s\n", PkgName); 454 if (fclose(contfile) == EOF) 455 warnx("cannot properly close file %s", contents); 456 } 457 } 458 if (Verbose) 459 printf("Package %s registered in %s\n", PkgName, LogDir); 460 } 461 462 if ((p = find_plist(&Plist, PLIST_DISPLAY)) != NULL) { 463 FILE *fp; 464 char buf[BUFSIZ]; 465 466 snprintf(buf, sizeof buf, "%s/%s", LogDir, p->name); 467 fp = fopen(buf, "r"); 468 if (fp) { 469 putc('\n', stdout); 470 while (fgets(buf, sizeof(buf), fp)) 471 fputs(buf, stdout); 472 putc('\n', stdout); 473 (void) fclose(fp); 474 } else 475 warnx("cannot open %s as display file", buf); 476 } 477 478 goto success; 479 480 bomb: 481 code = 1; 482 goto success; 483 484 fail: 485 /* Nuke the whole (installed) show, XXX but don't clean directories */ 486 if (!Fake) 487 delete_package(FALSE, FALSE, &Plist); 488 489 success: 490 /* delete the packing list contents */ 491 free_plist(&Plist); 492 leave_playpen(); 493 return code; 494} 495 496static int 497sanity_check(char *pkg) 498{ 499 int code = 0; 500 501 if (!fexists(CONTENTS_FNAME)) { 502 warnx("package %s has no CONTENTS file!", pkg); 503 code = 1; 504 } 505 else if (!fexists(COMMENT_FNAME)) { 506 warnx("package %s has no COMMENT file!", pkg); 507 code = 1; 508 } 509 else if (!fexists(DESC_FNAME)) { 510 warnx("package %s has no DESC file!", pkg); 511 code = 1; 512 } 513 return code; 514} 515 516void 517cleanup(int sig) 518{ 519 static int in_cleanup = 0; 520 521 if (!in_cleanup) { 522 in_cleanup = 1; 523 if (sig) 524 printf("Signal %d received, cleaning up..\n", sig); 525 if (!Fake && zapLogDir && LogDir[0]) 526 vsystem("%s -rf %s", REMOVE_CMD, LogDir); 527 leave_playpen(); 528 } 529 if (sig) 530 exit(1); 531} 532