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