1 /* $NetBSD: util.c,v 1.57 2021/01/31 22:45:47 rillig Exp $ */ 2 3 /* 4 * Copyright 1997 Piermont Information Systems Inc. 5 * All rights reserved. 6 * 7 * Written by Philip A. Nelson for Piermont Information Systems Inc. 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 * 3. The name of Piermont Information Systems Inc. may not be used to endorse 18 * or promote products derived from this software without specific prior 19 * written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS'' 22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 31 * THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 */ 34 35 /* util.c -- routines that don't really fit anywhere else... */ 36 37 #include <assert.h> 38 #include <inttypes.h> 39 #include <stdio.h> 40 #include <stdarg.h> 41 #include <string.h> 42 #include <unistd.h> 43 #include <sys/mount.h> 44 #include <sys/dkio.h> 45 #include <sys/ioctl.h> 46 #include <sys/types.h> 47 #include <sys/param.h> 48 #include <sys/sysctl.h> 49 #include <sys/stat.h> 50 #include <sys/statvfs.h> 51 #include <isofs/cd9660/iso.h> 52 #include <curses.h> 53 #include <err.h> 54 #include <errno.h> 55 #include <dirent.h> 56 #include <util.h> 57 #include "defs.h" 58 #include "md.h" 59 #include "defsizes.h" 60 #include "msg_defs.h" 61 #include "menu_defs.h" 62 #ifdef MD_MAY_SWAP_TO 63 #include <sys/drvctlio.h> 64 #endif 65 #ifdef CHECK_ENTROPY 66 #include <sha2.h> 67 #include <paths.h> 68 #endif 69 70 #define MAX_CD_DEVS 256 /* how many cd drives do we expect to attach */ 71 #define ISO_BLKSIZE ISO_DEFAULT_BLOCK_SIZE 72 73 static const char *msg_yes, *msg_no, *msg_all, *msg_some, *msg_none; 74 static int select_menu_width; 75 76 static uint8_t set_status[SET_GROUP_END]; 77 #define SET_VALID 0x01 78 #define SET_SELECTED 0x02 79 #define SET_SKIPPED 0x04 80 #define SET_INSTALLED 0x08 81 #define SET_NO_EXTRACT 0x10 82 83 struct tarstats { 84 int nselected; 85 int nfound; 86 int nnotfound; 87 int nerror; 88 int nsuccess; 89 int nskipped; 90 } tarstats; 91 92 distinfo dist_list[] = { 93 #ifdef SET_KERNEL_1_NAME 94 {SET_KERNEL_1_NAME, SET_KERNEL_1, false, MSG_set_kernel_1, NULL}, 95 #endif 96 #ifdef SET_KERNEL_2_NAME 97 {SET_KERNEL_2_NAME, SET_KERNEL_2, false, MSG_set_kernel_2, NULL}, 98 #endif 99 #ifdef SET_KERNEL_3_NAME 100 {SET_KERNEL_3_NAME, SET_KERNEL_3, false, MSG_set_kernel_3, NULL}, 101 #endif 102 #ifdef SET_KERNEL_4_NAME 103 {SET_KERNEL_4_NAME, SET_KERNEL_4, false, MSG_set_kernel_4, NULL}, 104 #endif 105 #ifdef SET_KERNEL_5_NAME 106 {SET_KERNEL_5_NAME, SET_KERNEL_5, false, MSG_set_kernel_5, NULL}, 107 #endif 108 #ifdef SET_KERNEL_6_NAME 109 {SET_KERNEL_6_NAME, SET_KERNEL_6, false, MSG_set_kernel_6, NULL}, 110 #endif 111 #ifdef SET_KERNEL_7_NAME 112 {SET_KERNEL_7_NAME, SET_KERNEL_7, false, MSG_set_kernel_7, NULL}, 113 #endif 114 #ifdef SET_KERNEL_8_NAME 115 {SET_KERNEL_8_NAME, SET_KERNEL_8, false, MSG_set_kernel_8, NULL}, 116 #endif 117 #ifdef SET_KERNEL_9_NAME 118 {SET_KERNEL_9_NAME, SET_KERNEL_9, false, MSG_set_kernel_9, NULL}, 119 #endif 120 121 #ifdef HAVE_MODULES 122 {"modules", SET_MODULES, false, MSG_set_modules, NULL}, 123 #endif 124 {"base", SET_BASE, false, MSG_set_base, NULL}, 125 #ifdef HAVE_DTB 126 {"dtb", SET_DTB, false, MSG_set_dtb, NULL}, 127 #endif 128 {"etc", SET_ETC, false, MSG_set_system, NULL}, 129 {"comp", SET_COMPILER, false, MSG_set_compiler, NULL}, 130 {"games", SET_GAMES, false, MSG_set_games, NULL}, 131 {"man", SET_MAN_PAGES, false, MSG_set_man_pages, NULL}, 132 {"misc", SET_MISC, false, MSG_set_misc, NULL}, 133 {"rescue", SET_RESCUE, false, MSG_set_rescue, NULL}, 134 {"tests", SET_TESTS, false, MSG_set_tests, NULL}, 135 {"text", SET_TEXT_TOOLS, false, MSG_set_text_tools, NULL}, 136 137 {NULL, SET_GROUP, false, MSG_set_X11, NULL}, 138 {"xbase", SET_X11_BASE, false, MSG_set_X11_base, NULL}, 139 {"xcomp", SET_X11_PROG, false, MSG_set_X11_prog, NULL}, 140 {"xetc", SET_X11_ETC, false, MSG_set_X11_etc, NULL}, 141 {"xfont", SET_X11_FONTS, false, MSG_set_X11_fonts, NULL}, 142 {"xserver", SET_X11_SERVERS, false, MSG_set_X11_servers, NULL}, 143 {NULL, SET_GROUP_END, false, NULL, NULL}, 144 145 #ifdef SET_MD_1_NAME 146 {SET_MD_1_NAME, SET_MD_1, false, MSG_set_md_1, NULL}, 147 #endif 148 #ifdef SET_MD_2_NAME 149 {SET_MD_2_NAME, SET_MD_2, false, MSG_set_md_2, NULL}, 150 #endif 151 #ifdef SET_MD_3_NAME 152 {SET_MD_3_NAME, SET_MD_3, false, MSG_set_md_3, NULL}, 153 #endif 154 #ifdef SET_MD_4_NAME 155 {SET_MD_4_NAME, SET_MD_4, false, MSG_set_md_4, NULL}, 156 #endif 157 158 {NULL, SET_GROUP, true, MSG_set_source, NULL}, 159 {"syssrc", SET_SYSSRC, true, MSG_set_syssrc, NULL}, 160 {"src", SET_SRC, true, MSG_set_src, NULL}, 161 {"sharesrc", SET_SHARESRC, true, MSG_set_sharesrc, NULL}, 162 {"gnusrc", SET_GNUSRC, true, MSG_set_gnusrc, NULL}, 163 {"xsrc", SET_XSRC, true, MSG_set_xsrc, NULL}, 164 {"debug", SET_DEBUG, false, MSG_set_debug, NULL}, 165 {"xdebug", SET_X11_DEBUG, false, MSG_set_xdebug, NULL}, 166 {NULL, SET_GROUP_END, false, NULL, NULL}, 167 168 {NULL, SET_LAST, false, NULL, NULL}, 169 }; 170 171 #define MAX_CD_INFOS 16 /* how many media can be found? */ 172 struct cd_info { 173 char device_name[16]; 174 char menu[100]; 175 }; 176 static struct cd_info cds[MAX_CD_INFOS]; 177 178 /* flags whether to offer the respective options (depending on helper 179 programs available on install media */ 180 int have_raid, have_vnd, have_cgd, have_lvm, have_gpt, have_dk; 181 182 /* 183 * local prototypes 184 */ 185 186 static int check_for(unsigned int mode, const char *pathname); 187 static int get_iso9660_volname(int dev, int sess, char *volname, 188 size_t volnamelen); 189 static int get_available_cds(void); 190 static int binary_available(const char *prog); 191 192 void 193 init_set_status(int flags) 194 { 195 static const uint8_t sets_valid[] = {MD_SETS_VALID}; 196 static const uint8_t sets_selected_full[] = {MD_SETS_SELECTED}; 197 static const uint8_t sets_selected_minimal[] = {MD_SETS_SELECTED_MINIMAL}; 198 static const uint8_t sets_selected_nox[] = {MD_SETS_SELECTED_NOX}; 199 static const uint8_t *sets_selected; 200 unsigned int nelem_selected; 201 unsigned int i, len; 202 const char *longest; 203 204 if (flags & SFLAG_MINIMAL) { 205 sets_selected = sets_selected_minimal; 206 nelem_selected = __arraycount(sets_selected_minimal); 207 } else if (flags & SFLAG_NOX) { 208 sets_selected = sets_selected_nox; 209 nelem_selected = __arraycount(sets_selected_nox); 210 } else { 211 sets_selected = sets_selected_full; 212 nelem_selected = __arraycount(sets_selected_full); 213 } 214 215 for (i = 0; i < __arraycount(sets_valid); i++) 216 set_status[sets_valid[i]] = SET_VALID; 217 for (i = 0; i < nelem_selected; i++) 218 set_status[sets_selected[i]] |= SET_SELECTED; 219 220 set_status[SET_GROUP] = SET_VALID; 221 222 /* Lookup some strings we need lots of times */ 223 msg_yes = msg_string(MSG_Yes); 224 msg_no = msg_string(MSG_No); 225 msg_all = msg_string(MSG_All); 226 msg_some = msg_string(MSG_Some); 227 msg_none = msg_string(MSG_None); 228 229 /* Find longest and use it to determine width of selection menu */ 230 len = strlen(msg_no); longest = msg_no; 231 i = strlen(msg_yes); if (i > len) {len = i; longest = msg_yes; } 232 i = strlen(msg_all); if (i > len) {len = i; longest = msg_all; } 233 i = strlen(msg_some); if (i > len) {len = i; longest = msg_some; } 234 i = strlen(msg_none); if (i > len) {len = i; longest = msg_none; } 235 select_menu_width = snprintf(NULL, 0, "%-30s %s", "", longest); 236 237 /* Give the md code a chance to choose the right kernel, etc. */ 238 md_init_set_status(flags); 239 } 240 241 int 242 dir_exists_p(const char *path) 243 { 244 245 return file_mode_match(path, S_IFDIR); 246 } 247 248 int 249 file_exists_p(const char *path) 250 { 251 252 return file_mode_match(path, S_IFREG); 253 } 254 255 int 256 file_mode_match(const char *path, unsigned int mode) 257 { 258 struct stat st; 259 260 return (stat(path, &st) == 0 && (st.st_mode & S_IFMT) == mode); 261 } 262 263 /* return ram size in MB */ 264 uint64_t 265 get_ramsize(void) 266 { 267 uint64_t ramsize; 268 size_t len = sizeof ramsize; 269 int mib[2] = {CTL_HW, HW_PHYSMEM64}; 270 271 sysctl(mib, 2, &ramsize, &len, NULL, 0); 272 273 /* Find out how many Megs ... round up. */ 274 return (ramsize + MEG - 1) / MEG; 275 } 276 277 void 278 run_makedev(void) 279 { 280 char *owd; 281 282 msg_display_add("\n\n"); 283 msg_display_add(MSG_makedev); 284 285 owd = getcwd(NULL, 0); 286 287 /* make /dev, in case the user didn't extract it. */ 288 make_target_dir("/dev"); 289 target_chdir_or_die("/dev"); 290 run_program(0, "/bin/sh MAKEDEV all"); 291 292 chdir(owd); 293 free(owd); 294 } 295 296 /* 297 * Performs in-place replacement of a set of patterns in a file that lives 298 * inside the installed system. The patterns must be separated by a semicolon. 299 * For example: 300 * 301 * replace("/etc/some-file.conf", "s/prop1=NO/prop1=YES/;s/foo/bar/"); 302 */ 303 void 304 replace(const char *path, const char *patterns, ...) 305 { 306 char *spatterns; 307 va_list ap; 308 309 va_start(ap, patterns); 310 vasprintf(&spatterns, patterns, ap); 311 va_end(ap); 312 if (spatterns == NULL) 313 err(1, "vasprintf(&spatterns, \"%s\", ...)", patterns); 314 315 run_program(RUN_CHROOT, "sed -an -e '%s;H;$!d;g;w %s' %s", spatterns, 316 path, path); 317 318 free(spatterns); 319 } 320 321 static int 322 floppy_fetch(const char *set_name) 323 { 324 char post[4]; 325 msg errmsg; 326 int menu; 327 int status; 328 const char *write_mode = ">"; 329 330 strcpy(post, "aa"); 331 332 errmsg = ""; 333 menu = MENU_fdok; 334 for (;;) { 335 umount_mnt2(); 336 msg_display(errmsg); 337 msg_fmt_display_add(MSG_fdmount, "%s%s", set_name, post); 338 process_menu(menu, &status); 339 if (status != SET_CONTINUE) 340 return status; 341 menu = MENU_fdremount; 342 errmsg = MSG_fdremount; 343 if (run_program(0, "/sbin/mount -r -t %s %s /mnt2", 344 fd_type, fd_dev)) 345 continue; 346 mnt2_mounted = 1; 347 errmsg = MSG_fdnotfound; 348 349 /* Display this because it might take a while.... */ 350 if (run_program(RUN_DISPLAY, 351 "sh -c '/bin/cat /mnt2/%s.%s %s %s/%s/%s%s'", 352 set_name, post, write_mode, 353 target_prefix(), xfer_dir, set_name, 354 set_postfix(set_name))) 355 /* XXX: a read error will give a corrupt file! */ 356 continue; 357 358 /* We got that file, advance to next fragment */ 359 if (post[1] < 'z') 360 post[1]++; 361 else 362 post[1] = 'a', post[0]++; 363 write_mode = ">>"; 364 errmsg = ""; 365 menu = MENU_fdok; 366 } 367 } 368 369 /* 370 * Load files from floppy. Requires a /mnt2 directory for mounting them. 371 */ 372 int 373 get_via_floppy(void) 374 { 375 int rv = -1; 376 377 process_menu(MENU_floppysource, &rv); 378 if (rv == SET_RETRY) 379 return SET_RETRY; 380 381 fetch_fn = floppy_fetch; 382 383 /* Set ext_dir for absolute path. */ 384 snprintf(ext_dir_bin, sizeof ext_dir_bin, "%s/%s", target_prefix(), xfer_dir); 385 snprintf(ext_dir_src, sizeof ext_dir_src, "%s/%s", target_prefix(), xfer_dir); 386 387 return SET_OK; 388 } 389 390 /* 391 * Get the volume name of a ISO9660 file system 392 */ 393 static int 394 get_iso9660_volname(int dev, int sess, char *volname, size_t volnamelen) 395 { 396 int blkno, error, last; 397 char buf[ISO_BLKSIZE]; 398 struct iso_volume_descriptor *vd = NULL; 399 struct iso_primary_descriptor *pd = NULL; 400 401 for (blkno = sess+16; blkno < sess+16+100; blkno++) { 402 error = pread(dev, buf, ISO_BLKSIZE, blkno*ISO_BLKSIZE); 403 if (error == -1) 404 return -1; 405 vd = (struct iso_volume_descriptor *)&buf; 406 if (memcmp(vd->id, ISO_STANDARD_ID, sizeof(vd->id)) != 0) 407 return -1; 408 if (isonum_711((const unsigned char *)&vd->type) 409 == ISO_VD_PRIMARY) { 410 pd = (struct iso_primary_descriptor*)buf; 411 strncpy(volname, pd->volume_id, volnamelen - 1); 412 volname[volnamelen - 1] = '\0'; 413 last = volnamelen - 1; 414 while (last >= 0 415 && (volname[last] == ' ' || volname[last] == 0)) 416 last--; 417 volname[last+1] = 0; 418 return 0; 419 } 420 } 421 return -1; 422 } 423 424 /* 425 * Local state while iterating CDs and collecting volumes 426 */ 427 struct get_available_cds_state { 428 size_t num_mounted; 429 struct statvfs *mounted; 430 struct cd_info *info; 431 size_t count; 432 }; 433 434 /* 435 * Callback function: if this is a CD, enumerate all volumes on it 436 */ 437 static bool 438 get_available_cds_helper(void *arg, const char *device) 439 { 440 struct get_available_cds_state *state = arg; 441 char dname[16], tname[16], volname[80], *t; 442 struct disklabel label; 443 int part, dev, error, sess, ready, tlen; 444 445 if (!is_cdrom_device(device, false)) 446 return true; 447 448 sprintf(dname, "/dev/r%s%c", device, 'a'+RAW_PART); 449 tlen = sprintf(tname, "/dev/%s", device); 450 451 /* check if this is mounted already */ 452 for (size_t i = 0; i < state->num_mounted; i++) { 453 if (strncmp(state->mounted[i].f_mntfromname, tname, tlen) 454 == 0) { 455 t = state->mounted[i].f_mntfromname + tlen; 456 if (t[0] >= 'a' && t[0] <= 'z' && t[1] == 0) 457 return true; 458 } 459 } 460 461 dev = open(dname, O_RDONLY, 0); 462 if (dev == -1) 463 return true; 464 465 ready = 0; 466 error = ioctl(dev, DIOCTUR, &ready); 467 if (error != 0 || ready == 0) { 468 close(dev); 469 return true; 470 } 471 error = ioctl(dev, DIOCGDINFO, &label); 472 close(dev); 473 if (error != 0) 474 return true; 475 476 for (part = 0; part < label.d_npartitions; part++) { 477 478 if (label.d_partitions[part].p_fstype == FS_UNUSED 479 || label.d_partitions[part].p_size == 0) 480 continue; 481 482 if (label.d_partitions[part].p_fstype == FS_ISO9660) { 483 sess = label.d_partitions[part].p_cdsession; 484 sprintf(dname, "/dev/r%s%c", device, 'a'+part); 485 dev = open(dname, O_RDONLY, 0); 486 if (dev == -1) 487 continue; 488 error = get_iso9660_volname(dev, sess, volname, 489 sizeof volname); 490 close(dev); 491 if (error) 492 continue; 493 sprintf(state->info->device_name, 494 "%s%c", device, 'a'+part); 495 sprintf(state->info->menu, "%s (%s)", 496 state->info->device_name, volname); 497 } else { 498 /* 499 * All install CDs use partition 500 * a for the sets. 501 */ 502 if (part > 0) 503 continue; 504 sprintf(state->info->device_name, 505 "%s%c", device, 'a'+part); 506 strcpy(state->info->menu, state->info->device_name); 507 } 508 state->info++; 509 if (++state->count >= MAX_CD_INFOS) 510 return false; 511 } 512 513 return true; 514 } 515 516 /* 517 * Get a list of all available CD media (not drives!), return 518 * the number of entries collected. 519 */ 520 static int 521 get_available_cds(void) 522 { 523 struct get_available_cds_state data; 524 int n, m; 525 526 memset(&data, 0, sizeof data); 527 data.info = cds; 528 529 n = getvfsstat(NULL, 0, ST_NOWAIT); 530 if (n > 0) { 531 data.mounted = calloc(n, sizeof(*data.mounted)); 532 m = getvfsstat(data.mounted, n*sizeof(*data.mounted), 533 ST_NOWAIT); 534 assert(m >= 0 && m <= n); 535 data.num_mounted = m; 536 } 537 538 enumerate_disks(&data, get_available_cds_helper); 539 540 free(data.mounted); 541 542 return data.count; 543 } 544 545 static int 546 cd_has_sets(void) 547 { 548 549 /* sanity check */ 550 if (cdrom_dev[0] == 0) 551 return 0; 552 553 /* Mount it */ 554 if (run_program(RUN_SILENT, "/sbin/mount -rt cd9660 /dev/%s /mnt2", 555 cdrom_dev) != 0) 556 return 0; 557 558 mnt2_mounted = 1; 559 560 snprintf(ext_dir_bin, sizeof ext_dir_bin, "%s/%s", "/mnt2", set_dir_bin); 561 snprintf(ext_dir_src, sizeof ext_dir_src, "%s/%s", "/mnt2", set_dir_src); 562 return dir_exists_p(ext_dir_bin); 563 } 564 565 /* 566 * Check whether we can remove the boot media. 567 * If it is not a local filesystem, return -1. 568 * If we can not decide for sure (can not tell MD content from plain ffs 569 * on hard disk, for example), return 0. 570 * If it is a CD/DVD, return 1. 571 */ 572 int 573 boot_media_still_needed(void) 574 { 575 struct statvfs sb; 576 577 if (statvfs("/", &sb) == 0) { 578 if (!(sb.f_flag & ST_LOCAL)) 579 return -1; 580 if (strcmp(sb.f_fstypename, MOUNT_CD9660) == 0 581 || strcmp(sb.f_fstypename, MOUNT_UDF) == 0) 582 return 1; 583 } 584 585 return 0; 586 } 587 588 bool 589 root_is_read_only(void) 590 { 591 struct statvfs sb; 592 593 if (statvfs("/", &sb) == 0) 594 return sb.f_flag & ST_RDONLY; 595 596 return false; 597 } 598 599 /* 600 * Get from a CDROM distribution. 601 * Also used on "installation using bootable install media" 602 * as the default option in the "distmedium" menu. 603 */ 604 int 605 get_via_cdrom(void) 606 { 607 menu_ent cd_menu[MAX_CD_INFOS]; 608 struct stat sb; 609 int rv, num_cds, menu_cd, i, selected_cd = 0; 610 int mib[2]; 611 char rootdev[SSTRSIZE] = ""; 612 size_t varlen; 613 614 /* If root is not md(4) and we have set dir, skip this step. */ 615 mib[0] = CTL_KERN; 616 mib[1] = KERN_ROOT_DEVICE; 617 varlen = sizeof(rootdev); 618 (void)sysctl(mib, 2, rootdev, &varlen, NULL, 0); 619 if (stat(set_dir_bin, &sb) == 0 && S_ISDIR(sb.st_mode) && 620 strncmp("md", rootdev, 2) != 0) { 621 strlcpy(ext_dir_bin, set_dir_bin, sizeof ext_dir_bin); 622 strlcpy(ext_dir_src, set_dir_src, sizeof ext_dir_src); 623 return SET_OK; 624 } 625 626 memset(cd_menu, 0, sizeof(cd_menu)); 627 num_cds = get_available_cds(); 628 if (num_cds <= 0) { 629 msg_display(MSG_No_cd_found); 630 cdrom_dev[0] = 0; 631 } else if (num_cds == 1) { 632 /* single CD found, check for sets on it */ 633 strcpy(cdrom_dev, cds[0].device_name); 634 if (cd_has_sets()) 635 return SET_OK; 636 } else { 637 for (i = 0; i< num_cds; i++) { 638 cd_menu[i].opt_name = cds[i].menu; 639 cd_menu[i].opt_flags = OPT_EXIT; 640 cd_menu[i].opt_action = set_menu_select; 641 } 642 /* create a menu offering available choices */ 643 menu_cd = new_menu(MSG_Available_cds, 644 cd_menu, num_cds, -1, 4, 0, 0, 645 MC_SCROLL | MC_NOEXITOPT, 646 NULL, NULL, NULL, NULL, NULL); 647 if (menu_cd == -1) 648 return SET_RETRY; 649 msg_display(MSG_ask_cd); 650 process_menu(menu_cd, &selected_cd); 651 free_menu(menu_cd); 652 strcpy(cdrom_dev, cds[selected_cd].device_name); 653 if (cd_has_sets()) 654 return SET_OK; 655 } 656 657 if (num_cds >= 1 && mnt2_mounted) { 658 umount_mnt2(); 659 hit_enter_to_continue(MSG_cd_path_not_found, NULL); 660 } 661 662 /* ask for paths on the CD */ 663 rv = -1; 664 process_menu(MENU_cdromsource, &rv); 665 if (rv == SET_RETRY || rv == SET_ABANDON) 666 return rv; 667 668 if (cd_has_sets()) 669 return SET_OK; 670 671 return SET_RETRY; 672 } 673 674 675 /* 676 * Get from a pathname inside an unmounted local filesystem 677 * (e.g., where sets were preloaded onto a local DOS partition) 678 */ 679 int 680 get_via_localfs(void) 681 { 682 int rv = -1; 683 684 /* Get device, filesystem, and filepath */ 685 process_menu (MENU_localfssource, &rv); 686 if (rv == SET_RETRY) 687 return SET_RETRY; 688 689 /* Mount it */ 690 if (run_program(0, "/sbin/mount -rt %s /dev/%s /mnt2", 691 localfs_fs, localfs_dev)) 692 return SET_RETRY; 693 694 mnt2_mounted = 1; 695 696 snprintf(ext_dir_bin, sizeof ext_dir_bin, "%s/%s/%s", 697 "/mnt2", localfs_dir, set_dir_bin); 698 snprintf(ext_dir_src, sizeof ext_dir_src, "%s/%s/%s", 699 "/mnt2", localfs_dir, set_dir_src); 700 701 return SET_OK; 702 } 703 704 /* 705 * Get from an already-mounted pathname. 706 */ 707 708 int 709 get_via_localdir(void) 710 { 711 int rv = -1; 712 713 /* Get filepath */ 714 process_menu(MENU_localdirsource, &rv); 715 if (rv == SET_RETRY) 716 return SET_RETRY; 717 718 /* 719 * We have to have an absolute path ('cos pax runs in a 720 * different directory), make it so. 721 */ 722 snprintf(ext_dir_bin, sizeof ext_dir_bin, "/%s/%s", localfs_dir, set_dir_bin); 723 snprintf(ext_dir_src, sizeof ext_dir_src, "/%s/%s", localfs_dir, set_dir_src); 724 725 return SET_OK; 726 } 727 728 729 /* 730 * Support for custom distribution fetches / unpacks. 731 */ 732 733 unsigned int 734 set_X11_selected(void) 735 { 736 int i; 737 738 for (i = SET_X11_FIRST; ++i < SET_X11_LAST;) 739 if (set_status[i] & SET_SELECTED) 740 return 1; 741 return 0; 742 } 743 744 unsigned int 745 get_kernel_set(void) 746 { 747 int i; 748 749 for (i = SET_KERNEL_FIRST; ++i < SET_KERNEL_LAST;) 750 if (set_status[i] & SET_SELECTED) 751 return i; 752 return SET_NONE; 753 } 754 755 void 756 set_kernel_set(unsigned int kernel_set) 757 { 758 int i; 759 760 /* only one kernel set is allowed */ 761 for (i = SET_KERNEL_FIRST; ++i < SET_KERNEL_LAST;) 762 set_status[i] &= ~SET_SELECTED; 763 set_status[kernel_set] |= SET_SELECTED; 764 } 765 766 void 767 set_noextract_set(unsigned int set) 768 { 769 770 set_status[set] |= SET_NO_EXTRACT; 771 } 772 773 static int 774 set_toggle(menudesc *menu, void *arg) 775 { 776 distinfo **distp = arg; 777 int set = distp[menu->cursel]->set; 778 779 if (set > SET_KERNEL_FIRST && set < SET_KERNEL_LAST && 780 !(set_status[set] & SET_SELECTED)) 781 set_kernel_set(set); 782 else 783 set_status[set] ^= SET_SELECTED; 784 return 0; 785 } 786 787 static int 788 set_all_none(menudesc *menu, void *arg, int set, int clr) 789 { 790 distinfo **distp = arg; 791 distinfo *dist = *distp; 792 int nested; 793 794 for (nested = 0; dist->set != SET_GROUP_END || nested--; dist++) { 795 if (dist->set == SET_GROUP) { 796 nested++; 797 continue; 798 } 799 set_status[dist->set] = (set_status[dist->set] & ~clr) | set; 800 } 801 return 0; 802 } 803 804 static int 805 set_all(menudesc *menu, void *arg) 806 { 807 return set_all_none(menu, arg, SET_SELECTED, 0); 808 } 809 810 static int 811 set_none(menudesc *menu, void *arg) 812 { 813 return set_all_none(menu, arg, 0, SET_SELECTED); 814 } 815 816 static void 817 set_label(menudesc *menu, int opt, void *arg) 818 { 819 distinfo **distp = arg; 820 distinfo *dist = distp[opt]; 821 const char *selected; 822 const char *desc; 823 int nested; 824 825 desc = dist->desc; 826 827 if (dist->set != SET_GROUP) 828 selected = set_status[dist->set] & SET_SELECTED ? msg_yes : msg_no; 829 else { 830 /* sub menu - display None/Some/All */ 831 nested = 0; 832 selected = "unknown"; 833 while ((++dist)->set != SET_GROUP_END || nested--) { 834 if (dist->set == SET_GROUP) { 835 nested++; 836 continue; 837 } 838 if (!(set_status[dist->set] & SET_VALID)) 839 continue; 840 if (set_status[dist->set] & SET_SELECTED) { 841 if (selected == msg_none) { 842 selected = msg_some; 843 break; 844 } 845 selected = msg_all; 846 } else { 847 if (selected == msg_all) { 848 selected = msg_some; 849 break; 850 } 851 selected = msg_none; 852 } 853 } 854 } 855 856 wprintw(menu->mw, "%-30s %s", msg_string(desc), selected); 857 } 858 859 static int set_sublist(menudesc *menu, void *arg); 860 861 static int 862 initialise_set_menu(distinfo *dist, menu_ent *me, distinfo **de, int all_none) 863 { 864 int set; 865 int sets; 866 int nested; 867 868 for (sets = 0; ; dist++) { 869 set = dist->set; 870 if (set == SET_LAST || set == SET_GROUP_END) 871 break; 872 if (!(set_status[set] & SET_VALID)) 873 continue; 874 *de = dist; 875 memset(me, 0, sizeof(*me)); 876 if (set != SET_GROUP) 877 me->opt_action = set_toggle; 878 else { 879 /* Collapse sublist */ 880 nested = 0; 881 while ((++dist)->set != SET_GROUP_END || nested--) { 882 if (dist->set == SET_GROUP) 883 nested++; 884 } 885 me->opt_action = set_sublist; 886 } 887 sets++; 888 de++; 889 me++; 890 } 891 892 if (all_none) { 893 me->opt_name = MSG_select_all; 894 me->opt_action = set_all; 895 me++; 896 me->opt_name = MSG_select_none; 897 me->opt_action = set_none; 898 sets += 2; 899 } 900 901 return sets; 902 } 903 904 static int 905 set_sublist(menudesc *menu, void *arg) 906 { 907 distinfo *de[SET_LAST]; 908 menu_ent me[SET_LAST]; 909 distinfo **dist = arg; 910 int menu_no; 911 int sets; 912 913 memset(me, 0, sizeof(me)); 914 sets = initialise_set_menu(dist[menu->cursel] + 1, me, de, 1); 915 916 menu_no = new_menu(NULL, me, sets, 20, 10, 0, select_menu_width, 917 MC_SUBMENU | MC_SCROLL | MC_DFLTEXIT, 918 NULL, set_label, NULL, NULL, 919 MSG_install_selected_sets); 920 921 process_menu(menu_no, de); 922 free_menu(menu_no); 923 924 return 0; 925 } 926 927 void 928 customise_sets(void) 929 { 930 distinfo *de[SET_LAST]; 931 menu_ent me[SET_LAST]; 932 int sets; 933 int menu_no; 934 935 msg_display(MSG_cur_distsets); 936 msg_table_add(MSG_cur_distsets_header); 937 938 memset(me, 0, sizeof(me)); 939 sets = initialise_set_menu(dist_list, me, de, 0); 940 941 menu_no = new_menu(NULL, me, sets, 0, 5, 0, select_menu_width, 942 MC_SCROLL | MC_NOBOX | MC_DFLTEXIT | MC_NOCLEAR, 943 NULL, set_label, NULL, NULL, 944 MSG_install_selected_sets); 945 946 process_menu(menu_no, de); 947 free_menu(menu_no); 948 } 949 950 /* 951 * Extract_file **REQUIRES** an absolute path in ext_dir. Any code 952 * that sets up xfer_dir for use by extract_file needs to put in the 953 * full path name to the directory. 954 */ 955 956 int 957 extract_file(distinfo *dist, int update) 958 { 959 const char *dest_dir = NULL; 960 961 if (update && (dist->set == SET_ETC || dist->set == SET_X11_ETC)) { 962 dest_dir = "/.sysinst"; 963 make_target_dir(dest_dir); 964 } else if (dist->set == SET_PKGSRC) 965 dest_dir = "/usr"; 966 else 967 dest_dir = "/"; 968 969 return extract_file_to(dist, update, dest_dir, NULL, true); 970 } 971 972 int 973 extract_file_to(distinfo *dist, int update, const char *dest_dir, 974 const char *extr_pattern, bool do_stats) 975 { 976 char path[STRSIZE]; 977 char *owd; 978 int rval; 979 980 /* If we might need to tidy up, ensure directory exists */ 981 if (fetch_fn != NULL) 982 make_target_dir(xfer_dir); 983 984 (void)snprintf(path, sizeof path, "%s/%s%s", 985 ext_dir_for_set(dist->name), dist->name, set_postfix(dist->name)); 986 987 owd = getcwd(NULL, 0); 988 989 /* Do we need to fetch the file now? */ 990 if (fetch_fn != NULL) { 991 rval = fetch_fn(dist->name); 992 if (rval != SET_OK) 993 return rval; 994 } 995 996 /* check tarfile exists */ 997 if (!file_exists_p(path)) { 998 999 #ifdef SUPPORT_8_3_SOURCE_FILESYSTEM 1000 /* 1001 * Update path to use dist->name truncated to the first eight 1002 * characters and check again 1003 */ 1004 (void)snprintf(path, sizeof path, 1005 "%s/%.8s%.4s", /* 4 as includes '.' */ 1006 ext_dir_for_set(dist->name), dist->name, 1007 set_postfix(dist->name)); 1008 1009 if (!file_exists_p(path)) { 1010 #endif /* SUPPORT_8_3_SOURCE_FILESYSTEM */ 1011 if (do_stats) 1012 tarstats.nnotfound++; 1013 1014 char *err = str_arg_subst(msg_string(MSG_notarfile), 1015 1, &dist->name); 1016 hit_enter_to_continue(err, NULL); 1017 free(err); 1018 free(owd); 1019 return SET_RETRY; 1020 } 1021 #ifdef SUPPORT_8_3_SOURCE_FILESYSTEM 1022 } 1023 #endif /* SUPPORT_8_3_SOURCE_FILESYSTEM */ 1024 1025 if (do_stats) 1026 tarstats.nfound++; 1027 /* cd to the target root. */ 1028 target_chdir_or_die(dest_dir); 1029 1030 /* 1031 * /usr/X11R7/lib/X11/xkb/symbols/pc was a directory in 5.0 1032 * but is a file in 5.1 and beyond, so on upgrades we need to 1033 * delete it before extracting the xbase set. 1034 */ 1035 if (update && dist->set == SET_X11_BASE) 1036 run_program(0, "rm -rf usr/X11R7/lib/X11/xkb/symbols/pc"); 1037 1038 /* now extract set files into "./". */ 1039 if (extr_pattern != NULL) { 1040 rval = run_program(RUN_DISPLAY | RUN_PROGRESS, 1041 "progress -zf %s tar --chroot " 1042 TAR_EXTRACT_FLAGS " - '%s'", 1043 path, extr_pattern); 1044 } else { 1045 rval = run_program(RUN_DISPLAY | RUN_PROGRESS, 1046 "progress -zf %s tar --chroot " 1047 TAR_EXTRACT_FLAGS " -", path); 1048 } 1049 1050 chdir(owd); 1051 free(owd); 1052 1053 /* Check rval for errors and give warning. */ 1054 if (rval != 0) { 1055 if (do_stats) 1056 tarstats.nerror++; 1057 msg_fmt_display(MSG_tarerror, "%s", path); 1058 hit_enter_to_continue(NULL, NULL); 1059 return SET_RETRY; 1060 } 1061 1062 if (fetch_fn != NULL && clean_xfer_dir) { 1063 run_program(0, "rm %s", path); 1064 /* Plausibly we should unlink an empty xfer_dir as well */ 1065 } 1066 1067 set_status[dist->set] |= SET_INSTALLED; 1068 if (do_stats) 1069 tarstats.nsuccess++; 1070 return SET_OK; 1071 } 1072 1073 static void 1074 skip_set(distinfo *dist, int skip_type) 1075 { 1076 int nested; 1077 int set; 1078 1079 nested = 0; 1080 while ((++dist)->set != SET_GROUP_END || nested--) { 1081 set = dist->set; 1082 if (set == SET_GROUP) { 1083 nested++; 1084 continue; 1085 } 1086 if (set == SET_LAST) 1087 break; 1088 if (set_status[set] == (SET_SELECTED | SET_VALID)) 1089 set_status[set] |= SET_SKIPPED; 1090 tarstats.nskipped++; 1091 } 1092 } 1093 1094 distinfo* 1095 get_set_distinfo(int opt) 1096 { 1097 distinfo *dist; 1098 int set; 1099 1100 for (dist = dist_list; (set = dist->set) != SET_LAST; dist++) { 1101 if (set != opt) 1102 continue; 1103 if (dist->name == NULL) 1104 continue; 1105 if ((set_status[set] & (SET_VALID | SET_SELECTED)) 1106 != (SET_VALID | SET_SELECTED)) 1107 continue; 1108 return dist; 1109 } 1110 1111 return NULL; 1112 } 1113 1114 #ifdef CHECK_ENTROPY 1115 1116 char entropy_file[PATH_MAX]; 1117 1118 /* 1119 * Are we short of entropy? 1120 */ 1121 static size_t 1122 entropy_needed(void) 1123 { 1124 int needed; 1125 size_t len; 1126 1127 len = sizeof(needed); 1128 if (sysctlbyname("kern.entropy.needed", &needed, &len, NULL, 0)) 1129 return 0; 1130 1131 if (needed < 0) 1132 return 0; 1133 1134 return needed; 1135 } 1136 1137 static void 1138 entropy_write_to_kernel(const uint8_t *data, size_t len) 1139 { 1140 int fd; 1141 1142 fd = open(_PATH_RANDOM, O_RDWR, 0); 1143 if (fd >= 0) { 1144 write(fd, data, len); 1145 close(fd); 1146 } 1147 } 1148 1149 static void 1150 entropy_add_manual(void) 1151 { 1152 SHA256_CTX ctx; 1153 char buf[256], line[25]; 1154 size_t line_no, l; 1155 uint8_t digest[SHA256_DIGEST_LENGTH]; 1156 bool ok = false; 1157 1158 msg_display(MSG_entropy_enter_manual1); 1159 msg_printf("\n\n"); 1160 msg_display_add(MSG_entropy_enter_manual2); 1161 msg_printf("\n\n dd if=/dev/random bs=32 count=16 | openssl base64\n\n"); 1162 msg_display_add(MSG_entropy_enter_manual3); 1163 msg_printf("\n\n"); 1164 SHA256_Init(&ctx); 1165 line_no = 1; 1166 do { 1167 sprintf(line, "%zu", line_no); 1168 msg_prompt_win(line, -1, 15, 0, 0, "", buf, sizeof(buf)); 1169 l = strlen(buf); 1170 if (l > 0) 1171 SHA256_Update(&ctx, (const uint8_t*)buf, l); 1172 line_no++; 1173 } while(buf[0] != 0); 1174 ok = ctx.bitcount >= 256; 1175 SHA256_Final(digest, &ctx); 1176 1177 if (ok) 1178 entropy_write_to_kernel(digest, sizeof digest); 1179 else 1180 hit_enter_to_continue(NULL, MSG_entropy_manual_not_enough); 1181 } 1182 1183 /* 1184 * Get a file by some means and return a (potentially only 1185 * temporary valid) path to the local copy. 1186 * If mountpt is nonempty, the caller should unmount that 1187 * directory after processing the file. 1188 * Return success if the file is available, or failure if 1189 * the user cancelled the request or network transfer failed. 1190 */ 1191 static bool 1192 entropy_get_file(bool use_netbsd_seed, char *path) 1193 { 1194 static struct ftpinfo server = { .user = "ftp" }; 1195 char url[STRSIZE], tmpf[PATH_MAX], mountpt[PATH_MAX]; 1196 const char *ftp_opt; 1197 arg_rv arg; 1198 int rv = 0; 1199 const char *file_desc = msg_string(use_netbsd_seed ? 1200 MSG_entropy_seed : MSG_entropy_data); 1201 char *dir; 1202 1203 path[0] = 0; 1204 mountpt[0] = 0; 1205 1206 sprintf(tmpf, "/tmp/entr.%06x", getpid()); 1207 1208 msg_display(use_netbsd_seed ? 1209 MSG_entropy_seed_hdr : MSG_entropy_data_hdr); 1210 msg_printf("\n\n %s\n\n", 1211 use_netbsd_seed ? 1212 "rndctl -S /tmp/entropy-file" : 1213 "dd if=/dev/random bs=32 count=1 of=/tmp/random.tmp"); 1214 strcpy(entropy_file, use_netbsd_seed ? 1215 "entropy-file" : "random.tmp"); 1216 process_menu(MENU_entropy_select_file, &rv); 1217 switch (rv) { 1218 case 1: 1219 case 2: 1220 #ifndef DEBUG 1221 if (!network_up) 1222 config_network(); 1223 #endif 1224 server.xfer = rv == 1 ? XFER_HTTP : XFER_FTP; 1225 arg.arg = &server; 1226 arg.rv = -1; 1227 msg_display_add_subst(MSG_entropy_via_download, 1, file_desc); 1228 msg_printf("\n\n"); 1229 process_menu(MENU_entropy_ftpsource, &arg); 1230 if (arg.rv == SET_RETRY) 1231 return false; 1232 make_url(url, &server, entropy_file); 1233 if (server.xfer == XFER_FTP && 1234 strcmp("ftp", server.user) == 0 && server.pass[0] == 0) { 1235 /* do anon ftp */ 1236 ftp_opt = "-a "; 1237 } else { 1238 ftp_opt = ""; 1239 } 1240 rv = run_program(RUN_DISPLAY | RUN_PROGRESS, 1241 "/usr/bin/ftp %s -o %s %s", 1242 ftp_opt, tmpf, url); 1243 strcpy(path, tmpf); 1244 return rv == 0; 1245 case 3: 1246 #ifndef DEBUG 1247 if (!network_up) 1248 config_network(); 1249 #endif 1250 rv = -1; 1251 msg_display_add_subst(MSG_entropy_via_nfs, 1, file_desc); 1252 msg_printf("\n\n"); 1253 process_menu(MENU_entropy_nfssource, &rv); 1254 if (rv == SET_RETRY) 1255 return false; 1256 if (nfs_host[0] != 0 && nfs_dir[0] != 0 && 1257 entropy_file[0] != 0) { 1258 strcpy(mountpt, "/tmp/ent-mnt.XXXXXX"); 1259 dir = mkdtemp(mountpt); 1260 if (dir == NULL) 1261 return false; 1262 sprintf(path, "%s/%s", mountpt, entropy_file); 1263 if (run_program(RUN_SILENT, 1264 "mount -t nfs -r %s:/%s %s", 1265 nfs_host, nfs_dir, mountpt) == 0) { 1266 run_program(RUN_SILENT, 1267 "cp %s %s", path, tmpf); 1268 run_program(RUN_SILENT, 1269 "umount %s", mountpt); 1270 rmdir(mountpt); 1271 strcpy(path, tmpf); 1272 } 1273 } 1274 break; 1275 case 4: 1276 rv = -1; 1277 /* Get device, filesystem, and filepath */ 1278 process_menu (MENU_entropy_localfs, &rv); 1279 if (rv == SET_RETRY) 1280 return false; 1281 if (localfs_dev[0] != 0 && localfs_fs[0] != 0 && 1282 entropy_file[0] != 0) { 1283 strcpy(mountpt, "/tmp/ent-mnt.XXXXXX"); 1284 dir = mkdtemp(mountpt); 1285 if (dir == NULL) 1286 return false; 1287 sprintf(path, "%s/%s", mountpt, entropy_file); 1288 if (run_program(RUN_SILENT, 1289 "mount -t %s -r /dev/%s %s", 1290 localfs_fs, localfs_dev, mountpt) == 0) { 1291 run_program(RUN_SILENT, 1292 "cp %s %s", path, tmpf); 1293 run_program(RUN_SILENT, 1294 "umount %s", mountpt); 1295 rmdir(mountpt); 1296 strcpy(path, tmpf); 1297 } 1298 } 1299 break; 1300 } 1301 return path[0] != 0; 1302 } 1303 1304 static void 1305 entropy_add_bin_file(void) 1306 { 1307 char fname[PATH_MAX]; 1308 1309 if (!entropy_get_file(false, fname)) 1310 return; 1311 if (access(fname, R_OK) == 0) 1312 run_program(RUN_SILENT, "dd if=%s of=" _PATH_RANDOM, 1313 fname); 1314 } 1315 1316 static void 1317 entropy_add_seed(void) 1318 { 1319 char fname[PATH_MAX]; 1320 1321 if (!entropy_get_file(true, fname)) 1322 return; 1323 if (access(fname, R_OK) == 0) 1324 run_program(RUN_SILENT, "rndctl -L %s", fname); 1325 } 1326 1327 /* 1328 * return true if we have enough entropy 1329 */ 1330 bool 1331 do_check_entropy(void) 1332 { 1333 int rv; 1334 1335 if (entropy_needed() == 0) 1336 return true; 1337 1338 for (;;) { 1339 if (entropy_needed() == 0) 1340 return true; 1341 1342 msg_clear(); 1343 rv = 0; 1344 process_menu(MENU_not_enough_entropy, &rv); 1345 switch (rv) { 1346 case 0: 1347 return false; 1348 case 1: 1349 entropy_add_manual(); 1350 break; 1351 case 2: 1352 entropy_add_seed(); 1353 break; 1354 case 3: 1355 entropy_add_bin_file(); 1356 break; 1357 default: 1358 /* 1359 * retry after small delay to give a new USB device 1360 * a chance to attach and do deliver some 1361 * entropy 1362 */ 1363 msg_display("."); 1364 for (size_t i = 0; i < 10; i++) { 1365 if (entropy_needed() == 0) 1366 return true; 1367 sleep(1); 1368 msg_display_add("."); 1369 } 1370 } 1371 } 1372 } 1373 #endif 1374 1375 1376 1377 /* 1378 * Get and unpack the distribution. 1379 * Show success_msg if installation completes. 1380 * Otherwise show failure_msg and wait for the user to ack it before continuing. 1381 * success_msg and failure_msg must both be 0-adic messages. 1382 */ 1383 int 1384 get_and_unpack_sets(int update, msg setupdone_msg, msg success_msg, msg failure_msg) 1385 { 1386 distinfo *dist; 1387 int status; 1388 int set, olderror, oldfound; 1389 bool entropy_loaded = false; 1390 1391 /* Ensure mountpoint for distribution files exists in current root. */ 1392 (void)mkdir("/mnt2", S_IRWXU| S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); 1393 if (script) 1394 (void)fprintf(script, "mkdir -m 755 /mnt2\n"); 1395 1396 /* reset failure/success counters */ 1397 memset(&tarstats, 0, sizeof(tarstats)); 1398 1399 /* Find out which files to "get" if we get files. */ 1400 1401 /* Accurately count selected sets */ 1402 for (dist = dist_list; (set = dist->set) != SET_LAST; dist++) { 1403 if (dist->name == NULL) 1404 continue; 1405 if (set_status[set] & SET_NO_EXTRACT) 1406 continue; 1407 if ((set_status[set] & (SET_VALID | SET_SELECTED)) 1408 == (SET_VALID | SET_SELECTED)) 1409 tarstats.nselected++; 1410 } 1411 1412 status = SET_RETRY; 1413 for (dist = dist_list; (set = dist->set) != SET_LAST; dist++) { 1414 if (dist->name == NULL) 1415 continue; 1416 if (set_status[set] != (SET_VALID | SET_SELECTED)) 1417 continue; 1418 1419 /* save stats, in case we will retry */ 1420 oldfound = tarstats.nfound; 1421 olderror = tarstats.nerror; 1422 1423 if (status != SET_OK) { 1424 /* This might force a redraw.... */ 1425 clearok(curscr, 1); 1426 touchwin(stdscr); 1427 wrefresh(stdscr); 1428 /* Sort out the location of the set files */ 1429 do { 1430 umount_mnt2(); 1431 msg_fmt_display(MSG_distmedium, "%d%d%s", 1432 tarstats.nselected, 1433 tarstats.nsuccess + tarstats.nskipped, 1434 dist->name); 1435 fetch_fn = NULL; 1436 process_menu(MENU_distmedium, &status); 1437 } while (status == SET_RETRY); 1438 1439 if (status == SET_SKIP) { 1440 set_status[set] |= SET_SKIPPED; 1441 tarstats.nskipped++; 1442 continue; 1443 } 1444 if (status == SET_SKIP_GROUP) { 1445 skip_set(dist, status); 1446 continue; 1447 } 1448 if (status != SET_OK) { 1449 hit_enter_to_continue(failure_msg, NULL); 1450 return 1; 1451 } 1452 } 1453 1454 if (set_status[set] & SET_NO_EXTRACT) 1455 continue; 1456 1457 /* Try to extract this set */ 1458 status = extract_file(dist, update); 1459 if (status == SET_RETRY) { 1460 /* do this set again */ 1461 dist--; 1462 /* and reset statistics to what we had before this 1463 * set */ 1464 tarstats.nfound = oldfound; 1465 tarstats.nerror = olderror; 1466 } 1467 } 1468 1469 #ifdef MD_SET_EXTRACT_FINALIZE 1470 MD_SET_EXTRACT_FINALIZE(update); 1471 #endif 1472 1473 if (tarstats.nerror == 0 && tarstats.nsuccess == tarstats.nselected) { 1474 msg_display(MSG_endtarok); 1475 /* Give user a chance to see the success message */ 1476 sleep(1); 1477 } else { 1478 /* We encountered errors. Let the user know. */ 1479 msg_fmt_display(MSG_endtar, "%d%d%d%d%d%d", 1480 tarstats.nselected, tarstats.nnotfound, tarstats.nskipped, 1481 tarstats.nfound, tarstats.nsuccess, tarstats.nerror); 1482 hit_enter_to_continue(NULL, NULL); 1483 } 1484 1485 /* 1486 * postinstall needs to be run after extracting all sets, because 1487 * otherwise /var/db/obsolete will only have current information 1488 * from the base, comp, and etc sets. 1489 */ 1490 if (update && (set_status[SET_ETC] & SET_INSTALLED)) { 1491 int oldsendmail; 1492 oldsendmail = run_program(RUN_DISPLAY | RUN_CHROOT | 1493 RUN_ERROR_OK | RUN_PROGRESS, 1494 "/usr/sbin/postinstall -s /.sysinst -d / check mailerconf"); 1495 if (oldsendmail == 1) { 1496 msg_display(MSG_oldsendmail); 1497 if (ask_yesno(NULL)) { 1498 run_program(RUN_DISPLAY | RUN_CHROOT, 1499 "/usr/sbin/postinstall -s /.sysinst -d / fix mailerconf"); 1500 } 1501 } 1502 run_program(RUN_DISPLAY | RUN_CHROOT, 1503 "/usr/sbin/postinstall -s /.sysinst -d / fix"); 1504 1505 /* Don't discard the system's old entropy if any */ 1506 run_program(RUN_CHROOT | RUN_SILENT, 1507 "/etc/rc.d/random_seed start"); 1508 entropy_loaded = true; 1509 } 1510 1511 /* Configure the system */ 1512 if (set_status[SET_BASE] & SET_INSTALLED) 1513 run_makedev(); 1514 1515 if (update) { 1516 #ifdef CHECK_ENTROPY 1517 if (!do_check_entropy()) { 1518 hit_enter_to_continue(NULL, MSG_abortupgr); 1519 return 1; 1520 } 1521 #endif 1522 } else { 1523 struct stat sb1, sb2; 1524 1525 if (stat(target_expand("/"), &sb1) == 0 1526 && stat(target_expand("/var"), &sb2) == 0 1527 && sb1.st_dev != sb2.st_dev) { 1528 add_rc_conf("random_file=/etc/entropy-file\n"); 1529 if (target_file_exists_p("/boot.cfg")) { 1530 run_program(RUN_CHROOT|RUN_FATAL, 1531 "sh -c 'sed -e s./var/db/./etc/. " 1532 "< /boot.cfg " 1533 "> /tmp/boot.cfg.tmp'"); 1534 mv_within_target_or_die("/tmp/boot.cfg.tmp", 1535 "/boot.cfg"); 1536 1537 } 1538 } 1539 1540 #ifdef MD_BOOT_CFG_FINALIZE 1541 if (target_file_exists_p("/boot.cfg")) { 1542 MD_BOOT_CFG_FINALIZE("/boot.cfg"); 1543 } 1544 #endif 1545 1546 /* Save keyboard type */ 1547 save_kb_encoding(); 1548 1549 /* Other configuration. */ 1550 mnt_net_config(); 1551 } 1552 1553 /* Mounted dist dir? */ 1554 umount_mnt2(); 1555 1556 #ifdef CHECK_ENTROPY 1557 entropy_loaded |= entropy_needed() == 0; 1558 #endif 1559 1560 /* Save entropy -- on some systems it's ~all we'll ever get */ 1561 if (!update || entropy_loaded) 1562 run_program(RUN_SILENT | RUN_CHROOT | RUN_ERROR_OK, 1563 "/etc/rc.d/random_seed stop"); 1564 /* Install/Upgrade complete ... reboot or exit to script */ 1565 hit_enter_to_continue(success_msg, NULL); 1566 return 0; 1567 } 1568 1569 void 1570 umount_mnt2(void) 1571 { 1572 if (!mnt2_mounted) 1573 return; 1574 run_program(RUN_SILENT, "/sbin/umount /mnt2"); 1575 mnt2_mounted = 0; 1576 } 1577 1578 1579 /* 1580 * Do a quick sanity check that the target can reboot. 1581 * return 1 if everything OK, 0 if there is a problem. 1582 * Uses a table of files we expect to find after a base install/upgrade. 1583 */ 1584 1585 /* test flag and pathname to check for after unpacking. */ 1586 struct check_table { unsigned int mode; const char *path;} checks[] = { 1587 { S_IFREG, "/netbsd" }, 1588 { S_IFDIR, "/etc" }, 1589 { S_IFREG, "/etc/fstab" }, 1590 { S_IFREG, "/sbin/init" }, 1591 { S_IFREG, "/bin/sh" }, 1592 { S_IFREG, "/etc/rc" }, 1593 { S_IFREG, "/etc/rc.subr" }, 1594 { S_IFREG, "/etc/rc.conf" }, 1595 { S_IFDIR, "/dev" }, 1596 { S_IFCHR, "/dev/console" }, 1597 /* XXX check for rootdev in target /dev? */ 1598 { S_IFREG, "/sbin/fsck" }, 1599 { S_IFREG, "/sbin/fsck_ffs" }, 1600 { S_IFREG, "/sbin/mount" }, 1601 { S_IFREG, "/sbin/mount_ffs" }, 1602 { S_IFREG, "/sbin/mount_nfs" }, 1603 #if defined(DEBUG) || defined(DEBUG_CHECK) 1604 { S_IFREG, "/foo/bar" }, /* bad entry to exercise warning */ 1605 #endif 1606 { 0, 0 } 1607 1608 }; 1609 1610 /* 1611 * Check target for a single file. 1612 */ 1613 static int 1614 check_for(unsigned int mode, const char *pathname) 1615 { 1616 int found; 1617 1618 found = (target_test(mode, pathname) == 0); 1619 if (found == 0) 1620 msg_fmt_display(MSG_rootmissing, "%s", pathname); 1621 return found; 1622 } 1623 1624 /* 1625 * Check that all the files in check_table are present in the 1626 * target root. Warn if not found. 1627 */ 1628 int 1629 sanity_check(void) 1630 { 1631 int target_ok = 1; 1632 struct check_table *p; 1633 1634 for (p = checks; p->path; p++) { 1635 target_ok = target_ok && check_for(p->mode, p->path); 1636 } 1637 if (target_ok) 1638 return 0; 1639 1640 /* Uh, oh. Something's missing. */ 1641 hit_enter_to_continue(MSG_badroot, NULL); 1642 return 1; 1643 } 1644 1645 /* 1646 * Some globals to pass things back from callbacks 1647 */ 1648 static char zoneinfo_dir[STRSIZE]; 1649 static int zonerootlen; 1650 static char *tz_selected; /* timezonename (relative to share/zoneinfo */ 1651 const char *tz_default; /* UTC, or whatever /etc/localtime points to */ 1652 static char tz_env[STRSIZE]; 1653 static int save_cursel, save_topline; 1654 static int time_menu = -1; 1655 1656 static void 1657 update_time_display(void) 1658 { 1659 time_t t; 1660 struct tm *tm; 1661 char cur_time[STRSIZE], *p; 1662 1663 t = time(NULL); 1664 tm = localtime(&t); 1665 strlcpy(cur_time, safectime(&t), sizeof cur_time); 1666 p = strchr(cur_time, '\n'); 1667 if (p != NULL) 1668 *p = 0; 1669 1670 msg_clear(); 1671 msg_fmt_table_add(MSG_choose_timezone, "%s%s%s%s", 1672 tz_default, tz_selected, cur_time, tm ? tm->tm_zone : "?"); 1673 } 1674 1675 /* 1676 * Callback from timezone menu 1677 */ 1678 static int 1679 set_tz_select(menudesc *m, void *arg) 1680 { 1681 char *new; 1682 1683 if (m && strcmp(tz_selected, m->opts[m->cursel].opt_name) != 0) { 1684 /* Change the displayed timezone */ 1685 new = strdup(m->opts[m->cursel].opt_name); 1686 if (new == NULL) 1687 return 0; 1688 free(tz_selected); 1689 tz_selected = new; 1690 snprintf(tz_env, sizeof tz_env, "%.*s%s", 1691 zonerootlen, zoneinfo_dir, tz_selected); 1692 setenv("TZ", tz_env, 1); 1693 } 1694 if (m) 1695 /* Warp curser to 'Exit' line on menu */ 1696 m->cursel = -1; 1697 1698 update_time_display(); 1699 if (time_menu >= 1) { 1700 WINDOW *w = get_menudesc(time_menu)->mw; 1701 if (w != NULL) { 1702 touchwin(w); 1703 wrefresh(w); 1704 } 1705 } 1706 return 0; 1707 } 1708 1709 static int 1710 set_tz_back(menudesc *m, void *arg) 1711 { 1712 1713 zoneinfo_dir[zonerootlen] = 0; 1714 m->cursel = save_cursel; 1715 m->topline = save_topline; 1716 return 0; 1717 } 1718 1719 static int 1720 set_tz_dir(menudesc *m, void *arg) 1721 { 1722 1723 strlcpy(zoneinfo_dir + zonerootlen, m->opts[m->cursel].opt_name, 1724 sizeof zoneinfo_dir - zonerootlen); 1725 save_cursel = m->cursel; 1726 save_topline = m->topline; 1727 m->cursel = 0; 1728 m->topline = 0; 1729 return 0; 1730 } 1731 1732 /* 1733 * Alarm-handler to update example-display 1734 */ 1735 static void 1736 /*ARGSUSED*/ 1737 timezone_sig(int sig) 1738 { 1739 1740 set_tz_select(NULL, NULL); 1741 alarm(60); 1742 } 1743 1744 static int 1745 tz_sort(const void *a, const void *b) 1746 { 1747 return strcmp(((const menu_ent *)a)->opt_name, ((const menu_ent *)b)->opt_name); 1748 } 1749 1750 static void 1751 tzm_set_names(menudesc *m, void *arg) 1752 { 1753 DIR *dir; 1754 struct dirent *dp; 1755 static int nfiles; 1756 static int maxfiles = 32; 1757 static menu_ent *tz_menu; 1758 static char **tz_names; 1759 void *p; 1760 int maxfname; 1761 char *fp; 1762 struct stat sb; 1763 1764 if (tz_menu == NULL) 1765 tz_menu = calloc(maxfiles, sizeof *tz_menu); 1766 if (tz_names == NULL) 1767 tz_names = malloc(maxfiles * sizeof *tz_names); 1768 if (tz_menu == NULL || tz_names == NULL) 1769 return; /* error - skip timezone setting */ 1770 while (nfiles > 0) 1771 free(tz_names[--nfiles]); 1772 1773 dir = opendir(zoneinfo_dir); 1774 fp = strchr(zoneinfo_dir, 0); 1775 if (fp != zoneinfo_dir + zonerootlen) { 1776 tz_names[0] = 0; 1777 tz_menu[0].opt_name = msg_string(MSG_tz_back); 1778 tz_menu[0].opt_action = set_tz_back; 1779 nfiles = 1; 1780 } 1781 maxfname = zoneinfo_dir + sizeof zoneinfo_dir - fp - 1; 1782 if (dir != NULL) { 1783 while ((dp = readdir(dir)) != NULL) { 1784 if (dp->d_namlen > maxfname || dp->d_name[0] == '.') 1785 continue; 1786 strlcpy(fp, dp->d_name, maxfname); 1787 if (stat(zoneinfo_dir, &sb) == -1) 1788 continue; 1789 if (nfiles >= maxfiles) { 1790 p = realloc(tz_menu, 1791 2 * maxfiles * sizeof *tz_menu); 1792 if (p == NULL) 1793 break; 1794 tz_menu = p; 1795 memset(tz_menu + maxfiles, 0, 1796 maxfiles * sizeof *tz_menu); 1797 p = realloc(tz_names, 1798 2 * maxfiles * sizeof *tz_names); 1799 if (p == NULL) 1800 break; 1801 tz_names = p; 1802 memset(tz_names + maxfiles, 0, 1803 maxfiles * sizeof *tz_names); 1804 maxfiles *= 2; 1805 } 1806 if (S_ISREG(sb.st_mode)) 1807 tz_menu[nfiles].opt_action = set_tz_select; 1808 else if (S_ISDIR(sb.st_mode)) { 1809 tz_menu[nfiles].opt_action = set_tz_dir; 1810 strlcat(fp, "/", 1811 sizeof(zoneinfo_dir) - (fp - zoneinfo_dir)); 1812 } else 1813 continue; 1814 tz_names[nfiles] = strdup(zoneinfo_dir + zonerootlen); 1815 tz_menu[nfiles].opt_name = tz_names[nfiles]; 1816 nfiles++; 1817 } 1818 closedir(dir); 1819 } 1820 *fp = 0; 1821 1822 m->opts = tz_menu; 1823 m->numopts = nfiles; 1824 qsort(tz_menu, nfiles, sizeof *tz_menu, tz_sort); 1825 } 1826 1827 void 1828 get_tz_default(void) 1829 { 1830 char localtime_link[STRSIZE]; 1831 static char localtime_target[STRSIZE]; 1832 int rc; 1833 1834 strlcpy(localtime_link, target_expand("/etc/localtime"), 1835 sizeof localtime_link); 1836 1837 /* Add sanity check that /mnt/usr/share/zoneinfo contains 1838 * something useful 1839 */ 1840 1841 rc = readlink(localtime_link, localtime_target, 1842 sizeof(localtime_target) - 1); 1843 if (rc < 0) { 1844 /* error, default to UTC */ 1845 tz_default = "UTC"; 1846 } else { 1847 localtime_target[rc] = '\0'; 1848 tz_default = strchr(strstr(localtime_target, "zoneinfo"), '/') + 1; 1849 } 1850 } 1851 1852 /* 1853 * Choose from the files in usr/share/zoneinfo and set etc/localtime 1854 */ 1855 int 1856 set_timezone(void) 1857 { 1858 char localtime_link[STRSIZE]; 1859 char localtime_target[STRSIZE]; 1860 int menu_no; 1861 1862 strlcpy(zoneinfo_dir, target_expand("/usr/share/zoneinfo/"), 1863 sizeof zoneinfo_dir - 1); 1864 zonerootlen = strlen(zoneinfo_dir); 1865 1866 get_tz_default(); 1867 1868 tz_selected = strdup(tz_default); 1869 snprintf(tz_env, sizeof(tz_env), "%s%s", zoneinfo_dir, tz_selected); 1870 setenv("TZ", tz_env, 1); 1871 update_time_display(); 1872 1873 signal(SIGALRM, timezone_sig); 1874 alarm(60); 1875 1876 menu_no = new_menu(NULL, NULL, 14, 23, 9, 1877 12, 32, MC_ALWAYS_SCROLL | MC_NOSHORTCUT, 1878 tzm_set_names, NULL, NULL, 1879 "\nPlease consult the install documents.", 1880 MSG_exit_menu_generic); 1881 if (menu_no < 0) 1882 goto done; /* error - skip timezone setting */ 1883 1884 time_menu = menu_no; 1885 process_menu(menu_no, NULL); 1886 time_menu = -1; 1887 1888 free_menu(menu_no); 1889 1890 signal(SIGALRM, SIG_IGN); 1891 1892 snprintf(localtime_target, sizeof(localtime_target), 1893 "/usr/share/zoneinfo/%s", tz_selected); 1894 strlcpy(localtime_link, target_expand("/etc/localtime"), 1895 sizeof localtime_link); 1896 unlink(localtime_link); 1897 symlink(localtime_target, localtime_link); 1898 1899 done: 1900 return 1; 1901 } 1902 1903 void 1904 scripting_vfprintf(FILE *f, const char *fmt, va_list ap) 1905 { 1906 va_list ap2; 1907 1908 va_copy(ap2, ap); 1909 if (f) 1910 (void)vfprintf(f, fmt, ap); 1911 if (script) 1912 (void)vfprintf(script, fmt, ap2); 1913 } 1914 1915 void 1916 scripting_fprintf(FILE *f, const char *fmt, ...) 1917 { 1918 va_list ap; 1919 1920 va_start(ap, fmt); 1921 scripting_vfprintf(f, fmt, ap); 1922 va_end(ap); 1923 } 1924 1925 void 1926 add_rc_conf(const char *fmt, ...) 1927 { 1928 FILE *f; 1929 va_list ap; 1930 1931 va_start(ap, fmt); 1932 f = target_fopen("/etc/rc.conf", "a"); 1933 if (f != 0) { 1934 scripting_fprintf(NULL, "cat <<EOF >>%s/etc/rc.conf\n", 1935 target_prefix()); 1936 scripting_vfprintf(f, fmt, ap); 1937 fclose(f); 1938 scripting_fprintf(NULL, "EOF\n"); 1939 } 1940 va_end(ap); 1941 } 1942 1943 int 1944 del_rc_conf(const char *value) 1945 { 1946 FILE *fp, *nfp; 1947 char buf[4096]; /* Ridiculously high, but should be enough in any way */ 1948 char *rcconf, *tempname = NULL, *bakname = NULL; 1949 char *cp; 1950 int done = 0; 1951 int fd; 1952 int retval = 0; 1953 1954 /* The paths might seem strange, but using /tmp would require copy instead 1955 * of rename operations. */ 1956 if (asprintf(&rcconf, "%s", target_expand("/etc/rc.conf")) < 0 1957 || asprintf(&tempname, "%s", target_expand("/etc/rc.conf.tmp.XXXXXX")) < 0 1958 || asprintf(&bakname, "%s", target_expand("/etc/rc.conf.bak.XXXXXX")) < 0) { 1959 if (rcconf) 1960 free(rcconf); 1961 if (tempname) 1962 free(tempname); 1963 msg_fmt_display(MSG_rcconf_delete_failed, "%s", value); 1964 hit_enter_to_continue(NULL, NULL); 1965 return -1; 1966 } 1967 1968 if ((fd = mkstemp(bakname)) < 0) { 1969 msg_fmt_display(MSG_rcconf_delete_failed, "%s", value); 1970 hit_enter_to_continue(NULL, NULL); 1971 return -1; 1972 } 1973 close(fd); 1974 1975 if (!(fp = fopen(rcconf, "r+")) || (fd = mkstemp(tempname)) < 0) { 1976 if (fp) 1977 fclose(fp); 1978 msg_fmt_display(MSG_rcconf_delete_failed, "%s", value); 1979 hit_enter_to_continue(NULL, NULL); 1980 return -1; 1981 } 1982 1983 nfp = fdopen(fd, "w"); 1984 if (!nfp) { 1985 fclose(fp); 1986 close(fd); 1987 msg_fmt_display(MSG_rcconf_delete_failed, "%s", value); 1988 hit_enter_to_continue(NULL, NULL); 1989 return -1; 1990 } 1991 1992 while (fgets(buf, sizeof buf, fp) != NULL) { 1993 1994 cp = buf + strspn(buf, " \t"); /* Skip initial spaces */ 1995 if (strncmp(cp, value, strlen(value)) == 0) { 1996 cp += strlen(value); 1997 if (*cp != '=') 1998 scripting_fprintf(nfp, "%s", buf); 1999 else 2000 done = 1; 2001 } else { 2002 scripting_fprintf(nfp, "%s", buf); 2003 } 2004 } 2005 fclose(fp); 2006 fclose(nfp); 2007 2008 if (done) { 2009 if (rename(rcconf, bakname)) { 2010 msg_display(MSG_rcconf_backup_failed); 2011 if (!ask_noyes(NULL)) { 2012 retval = -1; 2013 goto done; 2014 } 2015 } 2016 2017 if (rename(tempname, rcconf)) { 2018 if (rename(bakname, rcconf)) { 2019 hit_enter_to_continue(MSG_rcconf_restore_failed, 2020 NULL); 2021 } else { 2022 hit_enter_to_continue(MSG_rcconf_delete_failed, 2023 NULL); 2024 } 2025 } else { 2026 (void)unlink(bakname); 2027 } 2028 } 2029 2030 done: 2031 (void)unlink(tempname); 2032 free(rcconf); 2033 free(tempname); 2034 free(bakname); 2035 return retval; 2036 } 2037 2038 void 2039 add_sysctl_conf(const char *fmt, ...) 2040 { 2041 FILE *f; 2042 va_list ap; 2043 2044 va_start(ap, fmt); 2045 f = target_fopen("/etc/sysctl.conf", "a"); 2046 if (f != 0) { 2047 scripting_fprintf(NULL, "cat <<EOF >>%s/etc/sysctl.conf\n", 2048 target_prefix()); 2049 scripting_vfprintf(f, fmt, ap); 2050 fclose(f); 2051 scripting_fprintf(NULL, "EOF\n"); 2052 } 2053 va_end(ap); 2054 } 2055 2056 void 2057 enable_rc_conf(void) 2058 { 2059 2060 replace("/etc/rc.conf", "s/^rc_configured=NO/rc_configured=YES/"); 2061 } 2062 2063 int 2064 check_lfs_progs(void) 2065 { 2066 2067 #ifndef NO_LFS 2068 return binary_available("fsck_lfs") && binary_available("mount_lfs") 2069 && binary_available("newfs_lfs"); 2070 #else 2071 return 0; 2072 #endif 2073 } 2074 2075 int 2076 set_is_source(const char *set_name) { 2077 int len = strlen(set_name); 2078 return len >= 3 && memcmp(set_name + len - 3, "src", 3) == 0; 2079 } 2080 2081 const char * 2082 set_dir_for_set(const char *set_name) { 2083 if (strcmp(set_name, "pkgsrc") == 0) 2084 return pkgsrc_dir; 2085 return set_is_source(set_name) ? set_dir_src : set_dir_bin; 2086 } 2087 2088 const char * 2089 ext_dir_for_set(const char *set_name) { 2090 if (strcmp(set_name, "pkgsrc") == 0) 2091 return ext_dir_pkgsrc; 2092 return set_is_source(set_name) ? ext_dir_src : ext_dir_bin; 2093 } 2094 2095 void 2096 do_coloring(unsigned int fg, unsigned int bg) { 2097 if (bg > COLOR_WHITE) 2098 bg = COLOR_BLUE; 2099 if (fg > COLOR_WHITE) 2100 fg = COLOR_WHITE; 2101 if (fg != bg && has_colors()) { 2102 init_pair(1, fg, bg); 2103 wbkgd(stdscr, COLOR_PAIR(1)); 2104 wbkgd(mainwin, COLOR_PAIR(1)); 2105 wattrset(stdscr, COLOR_PAIR(1)); 2106 wattrset(mainwin, COLOR_PAIR(1)); 2107 } 2108 /* redraw screen */ 2109 touchwin(stdscr); 2110 touchwin(mainwin); 2111 wrefresh(stdscr); 2112 wrefresh(mainwin); 2113 return; 2114 } 2115 2116 int 2117 set_menu_select(menudesc *m, void *arg) 2118 { 2119 *(int *)arg = m->cursel; 2120 return 1; 2121 } 2122 2123 /* 2124 * check wether a binary is available somewhere in PATH, 2125 * return 1 if found, 0 if not. 2126 */ 2127 static int 2128 binary_available(const char *prog) 2129 { 2130 char *p, tmp[MAXPATHLEN], *path = getenv("PATH"), *opath; 2131 2132 if (path == NULL) 2133 return access(prog, X_OK) == 0; 2134 path = strdup(path); 2135 if (path == NULL) 2136 return 0; 2137 2138 opath = path; 2139 2140 while ((p = strsep(&path, ":")) != NULL) { 2141 if (strlcpy(tmp, p, MAXPATHLEN) >= MAXPATHLEN) 2142 continue; 2143 if (strlcat(tmp, "/", MAXPATHLEN) >= MAXPATHLEN) 2144 continue; 2145 if (strlcat(tmp, prog, MAXPATHLEN) >= MAXPATHLEN) 2146 continue; 2147 if (access(tmp, X_OK) == 0) { 2148 free(opath); 2149 return 1; 2150 } 2151 } 2152 free(opath); 2153 return 0; 2154 } 2155 2156 const char * 2157 safectime(time_t *t) 2158 { 2159 const char *s = ctime(t); 2160 if (s != NULL) 2161 return s; 2162 2163 // Debugging. 2164 fprintf(stderr, "Can't convert to localtime 0x%jx (%s)\n", 2165 (intmax_t)*t, strerror(errno)); 2166 /*123456789012345678901234*/ 2167 return "preposterous clock time\n"; 2168 } 2169 2170 int 2171 ask_yesno(const char* msgtxt) 2172 { 2173 arg_rv p; 2174 2175 p.arg = __UNCONST(msgtxt); 2176 p.rv = -1; 2177 2178 process_menu(MENU_yesno, &p); 2179 return p.rv; 2180 } 2181 2182 int 2183 ask_noyes(const char *msgtxt) 2184 { 2185 arg_rv p; 2186 2187 p.arg = __UNCONST(msgtxt); 2188 p.rv = -1; 2189 2190 process_menu(MENU_noyes, &p); 2191 return p.rv; 2192 } 2193 2194 int 2195 ask_reedit(const struct disk_partitions *parts) 2196 { 2197 const char *args[2]; 2198 arg_rep_int arg; 2199 2200 args[0] = msg_string(parts->pscheme->name); 2201 args[1] = msg_string(parts->pscheme->short_name); 2202 arg.args.argv = args; 2203 arg.args.argc = 2; 2204 arg.rv = 0; 2205 process_menu(MENU_reedit, &arg); 2206 2207 return arg.rv; 2208 } 2209 2210 bool 2211 use_tgz_for_set(const char *set_name) 2212 { 2213 const struct distinfo *dist; 2214 2215 for (dist = dist_list; dist->set != SET_LAST; dist++) { 2216 if (dist->name == NULL) 2217 continue; 2218 if (strcmp(set_name, dist->name) == 0) 2219 return dist->force_tgz; 2220 } 2221 2222 return true; 2223 } 2224 2225 /* Return the postfix used for a given set */ 2226 const char * 2227 set_postfix(const char *set_name) 2228 { 2229 return use_tgz_for_set(set_name) ? dist_tgz_postfix : dist_postfix; 2230 } 2231 2232 /* 2233 * Replace positional arguments (encoded as $0 .. $N) in the string 2234 * passed by the contents of the passed argument array. 2235 * Caller must free() the result string. 2236 */ 2237 char* 2238 str_arg_subst(const char *src, size_t argc, const char **argv) 2239 { 2240 const char *p, *last; 2241 char *out, *t; 2242 size_t len; 2243 2244 len = strlen(src); 2245 for (p = strchr(src, '$'); p; p = strchr(p+1, '$')) { 2246 char *endp = NULL; 2247 size_t n; 2248 int e; 2249 2250 /* $ followed by a correct numeric position? */ 2251 n = strtou(p+1, &endp, 10, 0, INT_MAX, &e); 2252 if ((e == 0 || e == ENOTSUP) && n < argc) { 2253 len += strlen(argv[n]); 2254 len -= endp-p; 2255 p = endp-1; 2256 } 2257 } 2258 2259 out = malloc(len+1); 2260 if (out == NULL) 2261 return NULL; 2262 2263 t = out; 2264 for (last = src, p = strchr(src, '$'); p; p = strchr(p+1, '$')) { 2265 char *endp = NULL; 2266 size_t n; 2267 int e; 2268 2269 /* $ followed by a correct numeric position? */ 2270 n = strtou(p+1, &endp, 10, 0, INT_MAX, &e); 2271 if ((e == 0 || e == ENOTSUP) && n < argc) { 2272 size_t l = p-last; 2273 memcpy(t, last, l); 2274 t += l; 2275 strcpy(t, argv[n]); 2276 t += strlen(argv[n]); 2277 last = endp; 2278 } 2279 } 2280 if (*last) { 2281 strcpy(t, last); 2282 t += strlen(last); 2283 } else { 2284 *t = 0; 2285 } 2286 assert((size_t)(t-out) == len); 2287 2288 return out; 2289 } 2290 2291 /* 2292 * Does this string have any positional args that need expanding? 2293 */ 2294 bool 2295 needs_expanding(const char *src, size_t argc) 2296 { 2297 const char *p; 2298 2299 for (p = strchr(src, '$'); p; p = strchr(p+1, '$')) { 2300 char *endp = NULL; 2301 size_t n; 2302 int e; 2303 2304 /* $ followed by a correct numeric position? */ 2305 n = strtou(p+1, &endp, 10, 0, INT_MAX, &e); 2306 if ((e == 0 || e == ENOTSUP) && n < argc) 2307 return true; 2308 } 2309 return false; 2310 } 2311 2312 /* 2313 * Replace positional arguments (encoded as $0 .. $N) in the 2314 * message by the strings passed as ... and call outfunc 2315 * with the result. 2316 */ 2317 static void 2318 msg_display_subst_internal(void (*outfunc)(msg), 2319 const char *master, size_t argc, va_list ap) 2320 { 2321 const char **args, **arg; 2322 char *out; 2323 2324 args = calloc(argc, sizeof(*args)); 2325 if (args == NULL) 2326 return; 2327 2328 arg = args; 2329 for (size_t i = 0; i < argc; i++) 2330 *arg++ = va_arg(ap, const char*); 2331 2332 out = str_arg_subst(msg_string(master), argc, args); 2333 if (out != NULL) { 2334 outfunc(out); 2335 free(out); 2336 } 2337 free(args); 2338 } 2339 2340 /* 2341 * Replace positional arguments (encoded as $0 .. $N) in the 2342 * message by the strings passed as ... 2343 */ 2344 void 2345 msg_display_subst(const char *master, size_t argc, ...) 2346 { 2347 va_list ap; 2348 2349 va_start(ap, argc); 2350 msg_display_subst_internal(msg_display, master, argc, ap); 2351 va_end(ap); 2352 } 2353 2354 /* 2355 * Same as above, but add to message instead of starting a new one 2356 */ 2357 void 2358 msg_display_add_subst(const char *master, size_t argc, ...) 2359 { 2360 va_list ap; 2361 2362 va_start(ap, argc); 2363 msg_display_subst_internal(msg_display_add, master, argc, ap); 2364 va_end(ap); 2365 } 2366 2367 /* initialize have_* variables */ 2368 void 2369 check_available_binaries() 2370 { 2371 static int did_test = false; 2372 2373 if (did_test) return; 2374 did_test = 1; 2375 2376 have_raid = binary_available("raidctl"); 2377 have_vnd = binary_available("vndconfig"); 2378 have_cgd = binary_available("cgdconfig"); 2379 have_lvm = binary_available("lvm"); 2380 have_gpt = binary_available("gpt"); 2381 have_dk = binary_available("dkctl"); 2382 } 2383 2384 /* 2385 * Wait for enter and immediately clear the screen after user response 2386 * (in case some longer action follows, so the user has instant feedback) 2387 */ 2388 void 2389 hit_enter_to_continue(const char *prompt, const char *title) 2390 { 2391 if (prompt != NULL) 2392 msg_display(prompt); 2393 process_menu(MENU_ok, __UNCONST(title)); 2394 msg_clear(); 2395 wrefresh(mainwin); 2396 } 2397 2398 /* 2399 * On auto pilot: 2400 * convert an existing set of partitions ot a list of part_usage_info 2401 * so that we "want" exactly what is there already. 2402 */ 2403 static bool 2404 usage_info_list_from_parts(struct part_usage_info **list, size_t *count, 2405 struct disk_partitions *parts) 2406 { 2407 struct disk_part_info info; 2408 part_id pno; 2409 size_t no, num; 2410 2411 num = 0; 2412 for (pno = 0; pno < parts->num_part; pno++) { 2413 if (!parts->pscheme->get_part_info(parts, pno, &info)) 2414 continue; 2415 num++; 2416 } 2417 2418 *list = calloc(num, sizeof(**list)); 2419 if (*list == NULL) 2420 return false; 2421 2422 *count = num; 2423 for (no = pno = 0; pno < parts->num_part && no < num; pno++) { 2424 if (!parts->pscheme->get_part_info(parts, pno, &info)) 2425 continue; 2426 (*list)[no].size = info.size; 2427 if (info.last_mounted != NULL && *info.last_mounted != 0) { 2428 strlcpy((*list)[no].mount, info.last_mounted, 2429 sizeof((*list)[no].mount)); 2430 (*list)[no].instflags |= PUIINST_MOUNT; 2431 } 2432 (*list)[no].parts = parts; 2433 (*list)[no].cur_part_id = pno; 2434 (*list)[no].cur_start = info.start; 2435 (*list)[no].cur_flags = info.flags; 2436 (*list)[no].type = info.nat_type->generic_ptype; 2437 if ((*list)[no].type == PT_swap) { 2438 (*list)[no].fs_type = FS_SWAP; 2439 (*list)[no].fs_version = 0; 2440 } else { 2441 (*list)[no].fs_type = info.fs_type; 2442 (*list)[no].fs_version = info.fs_sub_type; 2443 } 2444 (*list)[no].fs_opt1 = info.fs_opt1; 2445 (*list)[no].fs_opt2 = info.fs_opt2; 2446 (*list)[no].fs_opt3 = info.fs_opt3; 2447 no++; 2448 } 2449 return true; 2450 } 2451 2452 bool 2453 usage_set_from_parts(struct partition_usage_set *wanted, 2454 struct disk_partitions *parts) 2455 { 2456 memset(wanted, 0, sizeof(*wanted)); 2457 wanted->parts = parts; 2458 2459 return usage_info_list_from_parts(&wanted->infos, &wanted->num, parts); 2460 } 2461 2462 struct disk_partitions * 2463 get_inner_parts(struct disk_partitions *parts) 2464 { 2465 daddr_t start, size; 2466 part_id pno; 2467 struct disk_part_info info; 2468 2469 if (parts->pscheme->secondary_scheme == NULL) 2470 return NULL; 2471 2472 start = -1; 2473 size = -1; 2474 if (parts->pscheme->guess_install_target == NULL || 2475 !parts->pscheme->guess_install_target(parts, &start, &size)) { 2476 for (pno = 0; pno < parts->num_part; pno++) { 2477 if (!parts->pscheme->get_part_info(parts, pno, &info)) 2478 continue; 2479 if (!(info.flags & PTI_SEC_CONTAINER)) 2480 continue; 2481 start = info.start; 2482 size = info.size; 2483 } 2484 } 2485 2486 if (size > 0) 2487 return parts->pscheme->secondary_partitions(parts, start, 2488 false); 2489 2490 return NULL; 2491 } 2492 2493 bool 2494 install_desc_from_parts(struct install_partition_desc *install, 2495 struct disk_partitions *parts) 2496 { 2497 struct disk_partitions *inner_parts; 2498 2499 memset(install, 0, sizeof(*install)); 2500 inner_parts = get_inner_parts(parts); 2501 if (inner_parts != NULL) 2502 parts = inner_parts; 2503 2504 return usage_info_list_from_parts(&install->infos, &install->num, 2505 parts); 2506 } 2507 2508 void 2509 free_usage_set(struct partition_usage_set *wanted) 2510 { 2511 /* XXX - free parts? free clone src? */ 2512 free(wanted->write_back); 2513 free(wanted->menu_opts); 2514 free(wanted->infos); 2515 } 2516 2517 void 2518 free_install_desc(struct install_partition_desc *install) 2519 { 2520 #ifndef NO_CLONES 2521 size_t i, j; 2522 2523 for (i = 0; i < install->num; i++) { 2524 struct selected_partitions *src = install->infos[i].clone_src; 2525 if (!(install->infos[i].flags & PUIFLG_CLONE_PARTS) || 2526 src == NULL) 2527 continue; 2528 free_selected_partitions(src); 2529 for (j = i+1; j < install->num; j++) 2530 if (install->infos[j].clone_src == src) 2531 install->infos[j].clone_src = NULL; 2532 } 2533 #endif 2534 free(install->write_back); 2535 free(install->infos); 2536 } 2537 2538 #ifdef MD_MAY_SWAP_TO 2539 bool 2540 may_swap_if_not_sdmmc(const char *disk) 2541 { 2542 int fd, res; 2543 prop_dictionary_t command_dict, args_dict, results_dict, data_dict; 2544 prop_string_t string; 2545 prop_number_t number; 2546 const char *parent = ""; 2547 2548 fd = open(DRVCTLDEV, O_RDONLY, 0); 2549 if (fd == -1) 2550 return true; 2551 2552 command_dict = prop_dictionary_create(); 2553 args_dict = prop_dictionary_create(); 2554 2555 string = prop_string_create_nocopy("get-properties"); 2556 prop_dictionary_set(command_dict, "drvctl-command", string); 2557 prop_object_release(string); 2558 2559 string = prop_string_create_copy(disk); 2560 prop_dictionary_set(args_dict, "device-name", string); 2561 prop_object_release(string); 2562 2563 prop_dictionary_set(command_dict, "drvctl-arguments", 2564 args_dict); 2565 prop_object_release(args_dict); 2566 2567 res = prop_dictionary_sendrecv_ioctl(command_dict, fd, 2568 DRVCTLCOMMAND, &results_dict); 2569 prop_object_release(command_dict); 2570 close(fd); 2571 if (res) 2572 return true; 2573 2574 number = prop_dictionary_get(results_dict, "drvctl-error"); 2575 if (prop_number_signed_value(number) == 0) { 2576 data_dict = prop_dictionary_get(results_dict, 2577 "drvctl-result-data"); 2578 if (data_dict != NULL) { 2579 string = prop_dictionary_get(data_dict, 2580 "device-parent"); 2581 if (string != NULL) 2582 parent = prop_string_value(string); 2583 } 2584 } 2585 2586 prop_object_release(results_dict); 2587 2588 if (parent == NULL) 2589 return true; 2590 2591 return strncmp(parent, "sdmmc", 5) != 0; 2592 } 2593 #endif 2594