1/* 2 * The new sysinstall program. 3 * 4 * This is probably the last program in the `sysinstall' line - the next 5 * generation being essentially a complete rewrite. 6 * 7 * $FreeBSD$ 8 * 9 * Copyright (c) 1995 10 * Jordan Hubbard. All rights reserved. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer, 17 * verbatim and that no modifications are made prior to this 18 * point in the file. 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 23 * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 */ 36 37#include "sysinstall.h" 38#include <sys/param.h> 39#include <sys/mount.h> 40#include <sys/time.h> 41#include <sys/uio.h> 42#include <ctype.h> 43#include <signal.h> 44#include <libutil.h> 45 46unsigned int Dists; 47unsigned int DocDists; 48unsigned int SrcDists; 49unsigned int KernelDists; 50 51enum _disttype { DT_TARBALL, DT_SUBDIST, DT_PACKAGE }; 52 53typedef struct _dist { 54 char *my_name; 55 unsigned int *my_mask; 56 unsigned int my_bit; 57 enum _disttype my_type; 58 union { 59 char *my_string; /* DT_TARBALL & DT_PACKAGE */ 60 struct _dist *my_dist; /* DT_SUBDIST */ 61 } my_data; 62} Distribution; 63 64static Distribution DocDistTable[]; 65static Distribution KernelDistTable[]; 66static Distribution SrcDistTable[]; 67 68#define DTE_TARBALL(name, mask, flag, directory) \ 69 { name, mask, DIST_ ## flag, DT_TARBALL, { directory } } 70#define DTE_PACKAGE(name, mask, flag, package) \ 71 { name, mask, DIST_ ## flag, DT_PACKAGE, { package } } 72#define DTE_SUBDIST(name, mask, flag, subdist) \ 73 { name, mask, DIST_ ## flag, DT_SUBDIST, { .my_dist = subdist } } 74#define DTE_END { NULL, NULL, 0, 0, { NULL } } 75 76#define BASE_DIST (&DistTable[0]) 77 78/* The top-level distribution categories */ 79static Distribution DistTable[] = { 80 DTE_TARBALL("base", &Dists, BASE, "/"), 81 DTE_SUBDIST("kernels", &Dists, KERNEL, KernelDistTable), 82 DTE_TARBALL("doc", &Dists, DOCUSERLAND, "/"), 83 DTE_SUBDIST("docproj", &Dists, DOC, DocDistTable), 84 DTE_TARBALL("games", &Dists, GAMES, "/"), 85 DTE_TARBALL("manpages", &Dists, MANPAGES, "/"), 86 DTE_TARBALL("catpages", &Dists, CATPAGES, "/"), 87 DTE_TARBALL("proflibs", &Dists, PROFLIBS, "/"), 88 DTE_TARBALL("dict", &Dists, DICT, "/"), 89 DTE_TARBALL("info", &Dists, INFO, "/"), 90#if defined(__amd64__) || defined(__powerpc64__) 91 DTE_TARBALL("lib32", &Dists, LIB32, "/"), 92#endif 93 DTE_SUBDIST("src", &Dists, SRC, SrcDistTable), 94 DTE_TARBALL("ports", &Dists, PORTS, "/usr"), 95 DTE_TARBALL("local", &Dists, LOCAL, "/"), 96 DTE_END, 97}; 98 99/* The kernel distributions */ 100static Distribution KernelDistTable[] = { 101 DTE_TARBALL(GENERIC_KERNEL_NAME, &KernelDists, KERNEL_GENERIC, "/boot"), 102 DTE_END, 103}; 104 105/* The /usr/src distribution */ 106static Distribution SrcDistTable[] = { 107 DTE_TARBALL("sbase", &SrcDists, SRC_BASE, "/usr/src"), 108 DTE_TARBALL("scddl", &SrcDists, SRC_CDDL, "/usr/src"), 109 DTE_TARBALL("scontrib", &SrcDists, SRC_CONTRIB, "/usr/src"), 110 DTE_TARBALL("scrypto", &SrcDists, SRC_SCRYPTO, "/usr/src"), 111 DTE_TARBALL("sgnu", &SrcDists, SRC_GNU, "/usr/src"), 112 DTE_TARBALL("setc", &SrcDists, SRC_ETC, "/usr/src"), 113 DTE_TARBALL("sgames", &SrcDists, SRC_GAMES, "/usr/src"), 114 DTE_TARBALL("sinclude", &SrcDists, SRC_INCLUDE, "/usr/src"), 115 DTE_TARBALL("skrb5", &SrcDists, SRC_SKERBEROS5, "/usr/src"), 116 DTE_TARBALL("slib", &SrcDists, SRC_LIB, "/usr/src"), 117 DTE_TARBALL("slibexec", &SrcDists, SRC_LIBEXEC, "/usr/src"), 118 DTE_TARBALL("srelease", &SrcDists, SRC_RELEASE, "/usr/src"), 119 DTE_TARBALL("sbin", &SrcDists, SRC_BIN, "/usr/src"), 120 DTE_TARBALL("ssecure", &SrcDists, SRC_SSECURE, "/usr/src"), 121 DTE_TARBALL("ssbin", &SrcDists, SRC_SBIN, "/usr/src"), 122 DTE_TARBALL("sshare", &SrcDists, SRC_SHARE, "/usr/src"), 123 DTE_TARBALL("ssys", &SrcDists, SRC_SYS, "/usr/src"), 124 DTE_TARBALL("subin", &SrcDists, SRC_UBIN, "/usr/src"), 125 DTE_TARBALL("susbin", &SrcDists, SRC_USBIN, "/usr/src"), 126 DTE_TARBALL("stools", &SrcDists, SRC_TOOLS, "/usr/src"), 127 DTE_TARBALL("srescue", &SrcDists, SRC_RESCUE, "/usr/src"), 128 DTE_END, 129}; 130 131/* The Documentation distribution */ 132static Distribution DocDistTable[] = { 133 DTE_PACKAGE("Bengali Documentation", &DocDists, DOC_BN, "bn-freebsd-doc"), 134 DTE_PACKAGE("Danish Documentation", &DocDists, DOC_DA, "da-freebsd-doc"), 135 DTE_PACKAGE("German Documentation", &DocDists, DOC_DE, "de-freebsd-doc"), 136 DTE_PACKAGE("Greek Documentation", &DocDists, DOC_EL, "el-freebsd-doc"), 137 DTE_PACKAGE("English Documentation", &DocDists, DOC_EN, "en-freebsd-doc"), 138 DTE_PACKAGE("Spanish Documentation", &DocDists, DOC_ES, "es-freebsd-doc"), 139 DTE_PACKAGE("French Documentation", &DocDists, DOC_FR, "fr-freebsd-doc"), 140 DTE_PACKAGE("Hungarian Documentation", &DocDists, DOC_HU, "hu-freebsd-doc"), 141 DTE_PACKAGE("Italian Documentation", &DocDists, DOC_IT, "it-freebsd-doc"), 142 DTE_PACKAGE("Japanese Documentation", &DocDists, DOC_JA, "ja-freebsd-doc"), 143 DTE_PACKAGE("Mongolian Documentation", &DocDists, DOC_MN, "mn-freebsd-doc"), 144 DTE_PACKAGE("Dutch Documentation", &DocDists, DOC_NL, "nl-freebsd-doc"), 145 DTE_PACKAGE("Polish Documentation", &DocDists, DOC_PL, "pl-freebsd-doc"), 146 DTE_PACKAGE("Portuguese Documentation", &DocDists, DOC_PT, "pt-freebsd-doc"), 147 DTE_PACKAGE("Russian Documentation", &DocDists, DOC_RU, "ru-freebsd-doc"), 148 DTE_PACKAGE("Serbian Documentation", &DocDists, DOC_SR, "sr-freebsd-doc"), 149 DTE_PACKAGE("Turkish Documentation", &DocDists, DOC_TR, "tr-freebsd-doc"), 150 DTE_PACKAGE("Simplified Chinese Documentation", &DocDists, DOC_ZH_CN, "zh_cn-freebsd-doc"), 151 DTE_PACKAGE("Traditional Chinese Documentation", &DocDists, DOC_ZH_TW, "zh_tw-freebsd-doc"), 152 DTE_END, 153}; 154 155static int distMaybeSetPorts(dialogMenuItem *self); 156 157static void 158distVerifyFlags(void) 159{ 160 if (SrcDists) 161 Dists |= DIST_SRC; 162 if (KernelDists) 163 Dists |= DIST_KERNEL; 164 if (DocDists) 165 Dists |= DIST_DOC; 166 if (isDebug()) 167 msgDebug("Dist Masks: Dists: %0x, Srcs: %0x Kernels: %0x Docs: %0x\n", Dists, 168 SrcDists, KernelDists, DocDists); 169} 170 171int 172distReset(dialogMenuItem *self) 173{ 174 Dists = 0; 175 DocDists = 0; 176 SrcDists = 0; 177 KernelDists = 0; 178 return DITEM_SUCCESS | DITEM_REDRAW; 179} 180 181int 182distConfig(dialogMenuItem *self) 183{ 184 char *cp; 185 186 distReset(NULL); 187 188 if ((cp = variable_get(VAR_DIST_MAIN)) != NULL) 189 Dists = atoi(cp); 190 191 if ((cp = variable_get(VAR_DIST_DOC)) != NULL) 192 DocDists = atoi(cp); 193 194 if ((cp = variable_get(VAR_DIST_SRC)) != NULL) 195 SrcDists = atoi(cp); 196 197 if ((cp = variable_get(VAR_DIST_KERNEL)) != NULL) 198 KernelDists = atoi(cp); 199 200 distVerifyFlags(); 201 return DITEM_SUCCESS | DITEM_REDRAW; 202} 203 204int 205selectKernel(void) 206{ 207 return DIST_KERNEL_GENERIC; 208} 209 210int 211distSetDeveloper(dialogMenuItem *self) 212{ 213 int i; 214 215 distReset(NULL); 216 Dists = _DIST_DEVELOPER; 217 SrcDists = DIST_SRC_ALL; 218 KernelDists = selectKernel(); 219 i = distSetDoc(self); 220 i |= distMaybeSetPorts(self); 221 distVerifyFlags(); 222 return i; 223} 224 225int 226distSetKernDeveloper(dialogMenuItem *self) 227{ 228 int i; 229 230 distReset(NULL); 231 Dists = _DIST_DEVELOPER; 232 SrcDists = DIST_SRC_SYS | DIST_SRC_BASE; 233 KernelDists = selectKernel(); 234 i = distSetDoc(self); 235 i |= distMaybeSetPorts(self); 236 distVerifyFlags(); 237 return i; 238} 239 240int 241distSetUser(dialogMenuItem *self) 242{ 243 int i; 244 245 distReset(NULL); 246 Dists = _DIST_USER; 247 KernelDists = selectKernel(); 248 i = distSetDoc(self); 249 i |= distMaybeSetPorts(self); 250 distVerifyFlags(); 251 return i; 252} 253 254int 255distSetMinimum(dialogMenuItem *self) 256{ 257 distReset(NULL); 258 Dists = DIST_BASE | DIST_KERNEL; 259 KernelDists = selectKernel(); 260 distVerifyFlags(); 261 return DITEM_SUCCESS | DITEM_REDRAW; 262} 263 264int 265distSetEverything(dialogMenuItem *self) 266{ 267 int i; 268 269 Dists = DIST_ALL; 270 SrcDists = DIST_SRC_ALL; 271 KernelDists = DIST_KERNEL_ALL; 272 DocDists = DIST_DOC_ALL; 273 i = distMaybeSetPorts(self); 274 distVerifyFlags(); 275 return i | DITEM_REDRAW; 276} 277 278static int 279distMaybeSetPorts(dialogMenuItem *self) 280{ 281 dialog_clear_norefresh(); 282 if (!msgYesNo("Would you like to install the FreeBSD ports collection?\n\n" 283 "This will give you ready access to over 24,000 ported software packages,\n" 284 "at a cost of around 500MB of disk space when \"clean\" and possibly\n" 285 "much more than that when a lot of the distribution tarballs are loaded\n" 286 "(unless you have the extra discs available from a FreeBSD CD/DVD distribution\n" 287 "and can mount them on /cdrom, in which case this is far less of a problem).\n\n" 288 "The ports collection is a very valuable resource and well worth having\n" 289 "on your /usr partition, so it is advisable to say Yes to this option.\n\n" 290 "For more information on the ports collection & the latest ports, visit:\n" 291 " http://www.freebsd.org/ports\n")) 292 Dists |= DIST_PORTS; 293 else 294 Dists &= ~DIST_PORTS; 295 return DITEM_SUCCESS | DITEM_RESTORE; 296} 297 298static Boolean 299distSetByName(Distribution *dist, char *name) 300{ 301 int i, status = FALSE; 302 303 /* Loop through current set */ 304 for (i = 0; dist[i].my_name; i++) { 305 switch (dist[i].my_type) { 306 case DT_TARBALL: 307 case DT_PACKAGE: 308 if (!strcmp(dist[i].my_name, name)) { 309 *(dist[i].my_mask) |= dist[i].my_bit; 310 status = TRUE; 311 } 312 break; 313 case DT_SUBDIST: 314 if (distSetByName(dist[i].my_data.my_dist, name)) { 315 status = TRUE; 316 } 317 break; 318 } 319 } 320 distVerifyFlags(); 321 return status; 322} 323 324static Boolean 325distUnsetByName(Distribution *dist, char *name) 326{ 327 int i, status = FALSE; 328 329 /* Loop through current set */ 330 for (i = 0; dist[i].my_name; i++) { 331 switch (dist[i].my_type) { 332 case DT_TARBALL: 333 case DT_PACKAGE: 334 if (!strcmp(dist[i].my_name, name)) { 335 *(dist[i].my_mask) &= ~(dist[i].my_bit); 336 status = TRUE; 337 } 338 break; 339 case DT_SUBDIST: 340 if (distUnsetByName(dist[i].my_data.my_dist, name)) { 341 status = TRUE; 342 } 343 break; 344 } 345 } 346 return status; 347} 348 349/* Just for the dispatch stuff */ 350int 351distSetCustom(dialogMenuItem *self) 352{ 353 char *cp, *cp2, *tmp; 354 355 if (!(tmp = variable_get(VAR_DISTS))) { 356 msgDebug("distSetCustom() called without %s variable set.\n", VAR_DISTS); 357 return DITEM_FAILURE; 358 } 359 360 cp = alloca(strlen(tmp) + 1); 361 if (!cp) 362 msgFatal("Couldn't alloca() %d bytes!\n", (int)(strlen(tmp) + 1)); 363 strcpy(cp, tmp); 364 while (cp) { 365 if ((cp2 = index(cp, ' ')) != NULL) 366 *(cp2++) = '\0'; 367 if (!distSetByName(DistTable, cp)) 368 msgDebug("distSetCustom: Warning, no such release \"%s\"\n", cp); 369 cp = cp2; 370 } 371 distVerifyFlags(); 372 return DITEM_SUCCESS; 373} 374 375/* Just for the dispatch stuff */ 376int 377distUnsetCustom(dialogMenuItem *self) 378{ 379 char *cp, *cp2, *tmp; 380 381 if (!(tmp = variable_get(VAR_DISTS))) { 382 msgDebug("distUnsetCustom() called without %s variable set.\n", VAR_DISTS); 383 return DITEM_FAILURE; 384 } 385 386 cp = alloca(strlen(tmp) + 1); 387 if (!cp) 388 msgFatal("Couldn't alloca() %d bytes!\n", (int)(strlen(tmp) + 1)); 389 strcpy(cp, tmp); 390 while (cp) { 391 if ((cp2 = index(cp, ' ')) != NULL) 392 *(cp2++) = '\0'; 393 if (!distUnsetByName(DistTable, cp)) 394 msgDebug("distUnsetCustom: Warning, no such release \"%s\"\n", cp); 395 cp = cp2; 396 } 397 return DITEM_SUCCESS; 398} 399 400int 401distSetSrc(dialogMenuItem *self) 402{ 403 int i; 404 405 dialog_clear_norefresh(); 406 if (!dmenuOpenSimple(&MenuSrcDistributions, FALSE)) 407 i = DITEM_FAILURE; 408 else 409 i = DITEM_SUCCESS; 410 distVerifyFlags(); 411 return i | DITEM_RESTORE; 412} 413 414int 415distSetKernel(dialogMenuItem *self) 416{ 417 int i; 418 419 dialog_clear_norefresh(); 420 if (!dmenuOpenSimple(&MenuKernelDistributions, FALSE)) 421 i = DITEM_FAILURE; 422 else 423 i = DITEM_SUCCESS; 424 distVerifyFlags(); 425 return i | DITEM_RESTORE; 426} 427 428static Boolean got_intr = FALSE; 429 430/* timeout handler */ 431static void 432handle_intr(int sig) 433{ 434 msgDebug("User generated interrupt.\n"); 435 got_intr = TRUE; 436} 437 438static int 439check_for_interrupt(void) 440{ 441 if (got_intr) { 442 got_intr = FALSE; 443 return TRUE; 444 } 445 return FALSE; 446} 447 448/* 449 * translate distribution filename to lower case 450 * as doTARBALL does in release/Makefile 451 */ 452static void 453translateDist(char trdist[PATH_MAX], const char *dist) 454{ 455 int j; 456 457 /* 458 * translate distribution filename to lower case 459 * as doTARBALL does in release/Makefile 460 */ 461 for (j = 0; j < PATH_MAX-1 && dist[j] != '\0'; j++) 462 trdist[j] = tolower(dist[j]); 463 trdist[j] = '\0'; 464} 465 466/* 467 * Try to get distribution as multiple pieces, locating and parsing an 468 * info file which tells us how many we need for this distribution. 469 */ 470static Boolean 471distExtractTarball(char *path, char *dist, char *my_dir, int is_base) 472{ 473 char *buf = NULL, trdist[PATH_MAX], fname[PATH_MAX]; 474 struct timeval start, stop; 475 int j, status, total, intr; 476 int cpid, zpid, fd2, chunk, numchunks; 477 properties dist_attr = NULL; 478 const char *tmp; 479 FILE *fp; 480 481 translateDist(trdist, dist); 482 if (isDebug()) 483 msgDebug("%s: path \"%s\" dist \"%s\" trdist \"%s\" " 484 "my_dir \"%s\" %sis_base\n", 485 __func__, path, dist, trdist, my_dir, is_base ? "" : "!"); 486 487 status = TRUE; 488 numchunks = 0; 489 snprintf(fname, sizeof (fname), "%s/%s.inf", path, trdist); 490 491getinfo: 492 fp = DEVICE_GET(mediaDevice, fname, TRUE); 493 intr = check_for_interrupt(); 494 if (fp == (FILE *)IO_ERROR || intr || !mediaDevice) { 495 if (isDebug()) 496 msgDebug("%s: fname %s fp: %p, intr: %d mediaDevice: %p\n", 497 __func__, fname, fp, intr, mediaDevice); 498 /* Hard error, can't continue */ 499 if (!msgYesNo("Unable to open %s: %s.\nReinitialize media?", 500 fname, !intr ? "I/O error." : "User interrupt.")) { 501 DEVICE_SHUTDOWN(mediaDevice); 502 if (!DEVICE_INIT(mediaDevice)) 503 return (FALSE); 504 goto getinfo; 505 } else 506 return (FALSE); 507 } else if (fp == NULL) { 508 /* No attributes file, so try as a single file. */ 509 snprintf(fname, sizeof(fname), "%s/%s.%s", path, trdist, 510 USE_GZIP ? "tgz" : "tbz"); 511 if (isDebug()) 512 msgDebug("%s: fp is NULL (1) fname: %s\n", __func__, fname); 513 /* 514 * Passing TRUE as 3rd parm to get routine makes this a "probing" 515 * get, for which errors are not considered too significant. 516 */ 517 getsingle: 518 fp = DEVICE_GET(mediaDevice, fname, TRUE); 519 intr = check_for_interrupt(); 520 if (fp == (FILE *)IO_ERROR || intr || !mediaDevice) { 521 if (isDebug()) 522 msgDebug("%s: fname %s fp: %p, intr: %d mediaDevice: %p\n", 523 __func__, fname, fp, intr, mediaDevice); 524 /* Hard error, can't continue */ 525 msgConfirm("Unable to open %s: %s", fname, 526 !intr ? "I/O error" : "User interrupt"); 527 DEVICE_SHUTDOWN(mediaDevice); 528 if (!DEVICE_INIT(mediaDevice)) 529 return (FALSE); 530 goto getsingle; 531 } else if (fp != NULL) { 532 char *dir = root_bias(my_dir); 533 534 dialog_clear_norefresh(); 535 msgNotify("Extracting %s into %s directory...", dist, dir); 536 status = mediaExtractDist(dir, dist, fp); 537 fclose(fp); 538 return (status); 539 } else { 540 if (isDebug()) 541 msgDebug("%s: fp is NULL (2) fname %s\n", __func__, fname); 542 return (FALSE); 543 } 544 } 545 546 if (isDebug()) 547 msgDebug("Parsing attributes file for distribution %s\n", dist); 548 549 dist_attr = properties_read(fileno(fp)); 550 intr = check_for_interrupt(); 551 if (intr || !dist_attr) { 552 if (isDebug()) 553 msgDebug("%s: intr %d dist_attr %p\n", __func__, intr, dist_attr); 554 msgConfirm("Cannot parse information file for the %s distribution: %s\n" 555 "Please verify that your media is valid and try again.", 556 dist, !intr ? "I/O error" : "User interrupt"); 557 } else { 558 tmp = property_find(dist_attr, "Pieces"); 559 if (tmp) 560 numchunks = strtol(tmp, 0, 0); 561 } 562 fclose(fp); 563 if (!numchunks) { 564 if (isDebug()) 565 msgDebug("%s: numchunks is zero\n", __func__); 566 return (TRUE); 567 } 568 569 if (isDebug()) 570 msgDebug("Attempting to extract distribution from %u chunks.\n", 571 numchunks); 572 573 total = 0; 574 (void)gettimeofday(&start, (struct timezone *)NULL); 575 576 /* We have one or more chunks, initialize unpackers... */ 577 mediaExtractDistBegin(root_bias(my_dir), &fd2, &zpid, &cpid); 578 579 /* And go for all the chunks */ 580 dialog_clear_norefresh(); 581 for (chunk = 0; chunk < numchunks; chunk++) { 582 int n, retval, last_msg, chunksize, realsize; 583 char prompt[80]; 584 585 last_msg = 0; 586 587 getchunk: 588 snprintf(fname, sizeof(fname), "cksum.%c%c", (chunk / 26) + 'a', 589 (chunk % 26) + 'a'); 590 tmp = property_find(dist_attr, fname); 591 chunksize = 0; 592 if (tmp) { 593 tmp = index(tmp, ' '); 594 chunksize = strtol(tmp, 0, 0); 595 } 596 snprintf(fname, sizeof(fname), "%s/%s.%c%c", path, trdist, (chunk / 26) + 'a', 597 (chunk % 26) + 'a'); 598 if (isDebug()) 599 msgDebug("trying for piece %d of %d: %s\n", chunk + 1, numchunks, 600 fname); 601 fp = DEVICE_GET(mediaDevice, fname, FALSE); 602 intr = check_for_interrupt(); 603 /* XXX: this can't work if we get an I/O error */ 604 if (fp <= (FILE *)NULL || intr) { 605 if (fp == NULL) 606 msgConfirm("Failed to find %s on this media. Reinitializing media.", fname); 607 else 608 msgConfirm("Failed to retrieve piece file %s.\n" 609 "%s: Reinitializing media.", 610 fname, !intr ? "I/O error" : "User interrupt"); 611 DEVICE_SHUTDOWN(mediaDevice); 612 if (!DEVICE_INIT(mediaDevice)) 613 goto punt; 614 else 615 goto getchunk; 616 } 617 618 snprintf(prompt, sizeof(prompt), "Extracting %s into %s directory...", 619 dist, root_bias(my_dir)); 620 dialog_gauge("Progress", prompt, 8, 15, 6, 50, 621 (chunk + 1) * 100 / numchunks); 622 623 buf = safe_realloc(buf, chunksize); 624 realsize = 0; 625 while (1) { 626 int seconds; 627 628 n = fread(buf + realsize, 1, BUFSIZ, fp); 629 if (check_for_interrupt()) { 630 msgConfirm("Media read error: User interrupt."); 631 fclose(fp); 632 goto punt; 633 } else if (n <= 0) 634 break; 635 total += n; 636 realsize += n; 637 638 /* Print statistics about how we're doing */ 639 (void) gettimeofday(&stop, (struct timezone *)0); 640 stop.tv_sec = stop.tv_sec - start.tv_sec; 641 stop.tv_usec = stop.tv_usec - start.tv_usec; 642 if (stop.tv_usec < 0) 643 stop.tv_sec--, stop.tv_usec += 1000000; 644 seconds = stop.tv_sec + (stop.tv_usec / 1000000.0); 645 if (!seconds) 646 seconds = 1; 647 648 if (seconds != last_msg) { 649 last_msg = seconds; 650 msgInfo("%10d bytes read from %s dist, chunk %2d of %2d @ %.1f KBytes/sec.", 651 total, dist, chunk + 1, numchunks, 652 (total / seconds) / 1000.0); 653 } 654 } 655 fclose(fp); 656 657 if (!chunksize || (realsize == chunksize)) { 658 /* No substitution necessary */ 659 retval = write(fd2, buf, realsize); 660 if (retval != realsize) { 661 fclose(fp); 662 dialog_clear_norefresh(); 663 msgConfirm("Write failure on transfer! (wrote %d bytes of %d bytes)", retval, realsize); 664 goto punt; 665 } 666 } else { 667 for (j = 0; j < realsize; j++) { 668 /* On finding CRLF, skip the CR; don't exceed end of buffer. */ 669 if ((buf[j] != 0x0d) || (j == total - 1) || (buf[j + 1] != 0x0a)) { 670 retval = write(fd2, buf + j, 1); 671 if (retval != 1) { 672 fclose(fp); 673 dialog_clear_norefresh(); 674 msgConfirm("Write failure on transfer! (wrote %d bytes of %d bytes)", j, chunksize); 675 goto punt; 676 } 677 } 678 } 679 } 680 } 681 goto done; 682 683punt: 684 status = FALSE; 685done: 686 properties_free(dist_attr); 687 close(fd2); 688 if (status != FALSE) 689 status = mediaExtractDistEnd(zpid, cpid); 690 else 691 (void)mediaExtractDistEnd(zpid, cpid); 692 693 safe_free(buf); 694 return (status); 695} 696 697static Boolean 698distExtract(char *parent, Distribution *me) 699{ 700 int i, status; 701 char *path, *dist; 702 WINDOW *w = savescr(); 703 struct sigaction old, new; 704 int canceled = 0; 705 706 status = TRUE; 707 if (isDebug()) 708 msgDebug("distExtract: parent: %s, me: %s\n", parent ? parent : "(none)", me->my_name); 709 710 /* Make ^C fake a sudden timeout */ 711 new.sa_handler = handle_intr; 712 new.sa_flags = 0; 713 (void)sigemptyset(&new.sa_mask); 714 dialog_clear_norefresh(); 715 dialog_msgbox("Please Wait", "Extracting all requested distributions...", -1, -1, 0); 716 sigaction(SIGINT, &new, &old); 717 718 /* Loop through to see if we're in our parent's plans */ 719 for (i = 0; me[i].my_name && canceled == 0; i++) { 720 dist = me[i].my_name; 721 path = parent ? parent : dist; 722 723 /* If our bit isn't set, go to the next */ 724 if (!(me[i].my_bit & *(me[i].my_mask))) 725 continue; 726 727 switch (me[i].my_type) { 728 case DT_SUBDIST: 729 /* Recurse if we actually have a sub-distribution */ 730 status = distExtract(dist, me[i].my_data.my_dist); 731 if (!status) { 732 dialog_clear_norefresh(); 733 msgConfirm("Unable to transfer all components of the %s distribution.\n" 734 "You may wish to switch media types and try again.\n", 735 me[i].my_name); 736 } 737 break; 738 case DT_PACKAGE: 739 dialog_clear_norefresh(); 740 msgNotify("Installing %s distribution...", dist); 741 status = (package_add(me[i].my_data.my_string) == DITEM_SUCCESS); 742 if (!status) 743 dialog_clear_norefresh(); 744 break; 745 case DT_TARBALL: 746 status = distExtractTarball(path, dist, me[i].my_data.my_string, 747 &me[i] == BASE_DIST); 748 if (!status) { 749 dialog_clear_norefresh(); 750 if (me[i].my_bit != DIST_LOCAL) { 751 status = msgYesNo("Unable to transfer the %s distribution from\n%s.\n\n" 752 "Do you want to try to retrieve it again?", 753 me[i].my_name, mediaDevice->name); 754 if (status == 0) 755 --i; 756 else 757 canceled = 1; 758 759 status = FALSE; 760 } else { 761 // ignore any failures with DIST_LOCAL 762 status = TRUE; 763 } 764 } 765 break; 766 } 767 768 /* 769 * If extract was successful, remove ourselves from further 770 * consideration. 771 */ 772 if (status) 773 *(me[i].my_mask) &= ~(me[i].my_bit); 774 } 775 776 sigaction(SIGINT, &old, NULL); /* Restore signal handler */ 777 restorescr(w); 778 return status; 779} 780 781int 782distSetDoc(dialogMenuItem *self) 783{ 784 int i; 785 786 /* Assume no docs for non-interactive installs. */ 787 if (variable_get(VAR_NONINTERACTIVE)) 788 return DITEM_SUCCESS | DITEM_RESTORE; 789 790 dialog_clear_norefresh(); 791 if (!dmenuOpenSimple(&MenuDocInstall, FALSE)) 792 i = DITEM_FAILURE; 793 else 794 i = DITEM_SUCCESS; 795 796 distVerifyFlags(); 797 798 return i | DITEM_RESTORE; 799} 800 801int 802distSetDocMenu(dialogMenuItem *self) 803{ 804 int i, status; 805 WINDOW *w; 806 807 if (RunningAsInit && !strstr(variable_get(SYSTEM_STATE), "install")) { 808 msgConfirm("This option may only be used after the system is installed, sorry!"); 809 return DITEM_FAILURE; 810 } 811 812 dialog_clear_norefresh(); 813 if (!dmenuOpenSimple(&MenuDocInstall, FALSE)) 814 i = DITEM_FAILURE; 815 else 816 i = DITEM_SUCCESS; 817 818 distVerifyFlags(); 819 820 dialog_clear_norefresh(); 821 w = savescr(); 822 msgNotify("Attempting to install all selected documentations..."); 823 824 for (i = 0; DocDistTable[i].my_name; i++) { 825 if (!(DocDistTable[i].my_bit & *(DocDistTable[i].my_mask))) 826 continue; 827 dialog_clear_norefresh(); 828 msgNotify("Installing %s distribution...", DocDistTable[i].my_name); 829 status = (package_add(DocDistTable[i].my_data.my_string) == DITEM_SUCCESS); 830 if (!status) 831 break; 832 } 833 834 dialog_clear_norefresh(); 835 836 restorescr(w); 837 return (status ? DITEM_SUCCESS : DITEM_FAILURE); 838} 839 840static void 841printSelected(char *buf, int selected, Distribution *me, int *col) 842{ 843 int i; 844 845 /* Loop through to see if we're in our parent's plans */ 846 for (i = 0; me[i].my_name; i++) { 847 848 /* If our bit isn't set, go to the next */ 849 if (!(me[i].my_bit & selected)) 850 continue; 851 852 *col += strlen(me[i].my_name); 853 if (*col > 50) { 854 *col = 0; 855 strcat(buf, "\n"); 856 } 857 sprintf(&buf[strlen(buf)], " %s", me[i].my_name); 858 859 /* Recurse if have a sub-distribution */ 860 if (me[i].my_type == DT_SUBDIST) 861 printSelected(buf, *(me[i].my_mask), me[i].my_data.my_dist, col); 862 } 863} 864 865int 866distExtractAll(dialogMenuItem *self) 867{ 868 int old_dists, old_kernel, status = DITEM_SUCCESS; 869 char buf[512]; 870 int extract_status = TRUE; 871 WINDOW *w; 872 873 /* paranoia */ 874 if (!Dists) { 875 if (!dmenuOpenSimple(&MenuSubDistributions, FALSE) || !Dists) 876 return DITEM_FAILURE; 877 } 878 879 if (!mediaVerify() || !DEVICE_INIT(mediaDevice)) 880 return DITEM_FAILURE; 881 882 old_dists = Dists; 883 old_kernel = KernelDists; 884 distVerifyFlags(); 885 886 dialog_clear_norefresh(); 887 w = savescr(); 888 msgNotify("Attempting to install all selected distributions.."); 889 890 extract_status = distExtract(NULL, DistTable); 891 892 dialog_clear_norefresh(); 893 /* Only do base fixup if base dist was successfully extracted */ 894 if ((old_dists & DIST_BASE) && !(Dists & DIST_BASE)) 895 status |= installFixupBase(self); 896 /* Only do kernel fixup if kernel dist was successfully extracted */ 897 if ((old_dists & DIST_KERNEL) && !(Dists & DIST_KERNEL)) 898 status |= installFixupKernel(self, old_kernel); 899 900 /* Clear any local dist flags now */ 901 Dists &= ~DIST_LOCAL; 902 903 if (Dists) { 904 int col = 0; 905 906 buf[0] = '\0'; 907 dialog_clear_norefresh(); 908 printSelected(buf, Dists, DistTable, &col); 909 dialog_clear_norefresh(); 910 if (col) { 911 msgConfirm("Couldn't extract the following distributions. This may\n" 912 "be because they were not available on the installation\n" 913 "media you've chosen:\n\n\t%s", buf); 914 } 915 } 916 restorescr(w); 917 918 if (extract_status == FALSE) 919 status = FALSE; 920 921 return status; 922} 923