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