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