1 /* $NetBSD: util.c,v 1.74 2023/11/20 18:03:55 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 static char buf[ISO_BLKSIZE] __aligned(8); 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 if (!update) { 1530 run_program(RUN_CHROOT|RUN_DISPLAY, 1531 "/usr/sbin/certctl rehash"); 1532 } 1533 } 1534 1535 if (!update) { 1536 struct stat sb1, sb2; 1537 1538 if (stat(target_expand("/"), &sb1) == 0 1539 && stat(target_expand("/var"), &sb2) == 0 1540 && sb1.st_dev != sb2.st_dev) { 1541 add_rc_conf("random_file=/etc/entropy-file\n"); 1542 if (target_file_exists_p("/boot.cfg")) { 1543 run_program(RUN_CHROOT|RUN_FATAL, 1544 "sh -c 'sed -e s./var/db/./etc/. " 1545 "< /boot.cfg " 1546 "> /tmp/boot.cfg.tmp'"); 1547 mv_within_target_or_die("/tmp/boot.cfg.tmp", 1548 "/boot.cfg"); 1549 1550 } 1551 } 1552 1553 #ifdef MD_BOOT_CFG_FINALIZE 1554 if (target_file_exists_p("/boot.cfg")) { 1555 MD_BOOT_CFG_FINALIZE("/boot.cfg"); 1556 } 1557 #endif 1558 1559 /* Save keyboard type */ 1560 save_kb_encoding(); 1561 1562 /* Other configuration. */ 1563 mnt_net_config(); 1564 } 1565 1566 /* Mounted dist dir? */ 1567 umount_mnt2(); 1568 1569 #ifdef CHECK_ENTROPY 1570 entropy_loaded |= entropy_needed() == 0; 1571 #endif 1572 1573 /* Save entropy -- on some systems it's ~all we'll ever get */ 1574 if (!update || entropy_loaded) 1575 run_program(RUN_SILENT | RUN_CHROOT | RUN_ERROR_OK, 1576 "/etc/rc.d/random_seed stop"); 1577 /* Install/Upgrade complete ... reboot or exit to script */ 1578 hit_enter_to_continue(success_msg, NULL); 1579 return 0; 1580 } 1581 1582 void 1583 umount_mnt2(void) 1584 { 1585 if (!mnt2_mounted) 1586 return; 1587 run_program(RUN_SILENT, "/sbin/umount /mnt2"); 1588 mnt2_mounted = 0; 1589 } 1590 1591 1592 /* 1593 * Do a quick sanity check that the target can reboot. 1594 * return 1 if everything OK, 0 if there is a problem. 1595 * Uses a table of files we expect to find after a base install/upgrade. 1596 */ 1597 1598 /* test flag and pathname to check for after unpacking. */ 1599 struct check_table { unsigned int mode; const char *path;} checks[] = { 1600 { S_IFREG, "/netbsd" }, 1601 { S_IFDIR, "/etc" }, 1602 { S_IFREG, "/etc/fstab" }, 1603 { S_IFREG, "/sbin/init" }, 1604 { S_IFREG, "/bin/sh" }, 1605 { S_IFREG, "/etc/rc" }, 1606 { S_IFREG, "/etc/rc.subr" }, 1607 { S_IFREG, "/etc/rc.conf" }, 1608 { S_IFDIR, "/dev" }, 1609 { S_IFCHR, "/dev/console" }, 1610 /* XXX check for rootdev in target /dev? */ 1611 { S_IFREG, "/sbin/fsck" }, 1612 { S_IFREG, "/sbin/fsck_ffs" }, 1613 { S_IFREG, "/sbin/mount" }, 1614 { S_IFREG, "/sbin/mount_ffs" }, 1615 { S_IFREG, "/sbin/mount_nfs" }, 1616 #if defined(DEBUG) || defined(DEBUG_CHECK) 1617 { S_IFREG, "/foo/bar" }, /* bad entry to exercise warning */ 1618 #endif 1619 { 0, 0 } 1620 1621 }; 1622 1623 /* 1624 * Check target for a single file. 1625 */ 1626 static int 1627 check_for(unsigned int mode, const char *pathname) 1628 { 1629 int found; 1630 1631 found = (target_test(mode, pathname) == 0); 1632 if (found == 0) 1633 msg_fmt_display(MSG_rootmissing, "%s", pathname); 1634 return found; 1635 } 1636 1637 /* 1638 * Check that all the files in check_table are present in the 1639 * target root. Warn if not found. 1640 */ 1641 int 1642 sanity_check(void) 1643 { 1644 int target_ok = 1; 1645 struct check_table *p; 1646 1647 for (p = checks; p->path; p++) { 1648 target_ok = target_ok && check_for(p->mode, p->path); 1649 } 1650 if (target_ok) 1651 return 0; 1652 1653 /* Uh, oh. Something's missing. */ 1654 hit_enter_to_continue(MSG_badroot, NULL); 1655 return 1; 1656 } 1657 1658 /* 1659 * Some globals to pass things back from callbacks 1660 */ 1661 static char zoneinfo_dir[STRSIZE]; 1662 static int zonerootlen; 1663 static char *tz_selected; /* timezonename (relative to share/zoneinfo */ 1664 const char *tz_default; /* UTC, or whatever /etc/localtime points to */ 1665 static char tz_env[STRSIZE]; 1666 static int save_cursel, save_topline; 1667 static int time_menu = -1; 1668 1669 static void 1670 update_time_display(void) 1671 { 1672 time_t t; 1673 struct tm *tm; 1674 char cur_time[STRSIZE], *p; 1675 1676 t = time(NULL); 1677 tm = localtime(&t); 1678 strlcpy(cur_time, safectime(&t), sizeof cur_time); 1679 p = strchr(cur_time, '\n'); 1680 if (p != NULL) 1681 *p = 0; 1682 1683 msg_clear(); 1684 msg_fmt_table_add(MSG_choose_timezone, "%s%s%s%s", 1685 tz_default, tz_selected, cur_time, tm ? tm->tm_zone : "?"); 1686 } 1687 1688 /* 1689 * Callback from timezone menu 1690 */ 1691 static int 1692 set_tz_select(menudesc *m, void *arg) 1693 { 1694 char *new; 1695 1696 if (m && strcmp(tz_selected, m->opts[m->cursel].opt_name) != 0) { 1697 /* Change the displayed timezone */ 1698 new = strdup(m->opts[m->cursel].opt_name); 1699 if (new == NULL) 1700 return 0; 1701 free(tz_selected); 1702 tz_selected = new; 1703 snprintf(tz_env, sizeof tz_env, "%.*s%s", 1704 zonerootlen, zoneinfo_dir, tz_selected); 1705 setenv("TZ", tz_env, 1); 1706 } 1707 if (m) 1708 /* Warp curser to 'Exit' line on menu */ 1709 m->cursel = -1; 1710 1711 update_time_display(); 1712 if (time_menu >= 1) { 1713 WINDOW *w = get_menudesc(time_menu)->mw; 1714 if (w != NULL) { 1715 touchwin(w); 1716 wrefresh(w); 1717 } 1718 } 1719 return 0; 1720 } 1721 1722 static int 1723 set_tz_back(menudesc *m, void *arg) 1724 { 1725 1726 zoneinfo_dir[zonerootlen] = 0; 1727 m->cursel = save_cursel; 1728 m->topline = save_topline; 1729 return 0; 1730 } 1731 1732 static int 1733 set_tz_dir(menudesc *m, void *arg) 1734 { 1735 1736 strlcpy(zoneinfo_dir + zonerootlen, m->opts[m->cursel].opt_name, 1737 sizeof zoneinfo_dir - zonerootlen); 1738 save_cursel = m->cursel; 1739 save_topline = m->topline; 1740 m->cursel = 0; 1741 m->topline = 0; 1742 return 0; 1743 } 1744 1745 /* 1746 * Alarm-handler to update example-display 1747 */ 1748 static void 1749 /*ARGSUSED*/ 1750 timezone_sig(int sig) 1751 { 1752 1753 set_tz_select(NULL, NULL); 1754 alarm(60); 1755 } 1756 1757 static int 1758 tz_sort(const void *a, const void *b) 1759 { 1760 return strcmp(((const menu_ent *)a)->opt_name, ((const menu_ent *)b)->opt_name); 1761 } 1762 1763 static void 1764 tzm_set_names(menudesc *m, void *arg) 1765 { 1766 DIR *dir; 1767 struct dirent *dp; 1768 static int nfiles; 1769 static int maxfiles = 32; 1770 static menu_ent *tz_menu; 1771 static char **tz_names; 1772 void *p; 1773 int maxfname; 1774 char *fp; 1775 struct stat sb; 1776 1777 if (tz_menu == NULL) 1778 tz_menu = calloc(maxfiles, sizeof *tz_menu); 1779 if (tz_names == NULL) 1780 tz_names = malloc(maxfiles * sizeof *tz_names); 1781 if (tz_menu == NULL || tz_names == NULL) 1782 return; /* error - skip timezone setting */ 1783 while (nfiles > 0) 1784 free(tz_names[--nfiles]); 1785 1786 dir = opendir(zoneinfo_dir); 1787 fp = strchr(zoneinfo_dir, 0); 1788 if (fp != zoneinfo_dir + zonerootlen) { 1789 tz_names[0] = 0; 1790 tz_menu[0].opt_name = msg_string(MSG_tz_back); 1791 tz_menu[0].opt_action = set_tz_back; 1792 nfiles = 1; 1793 } 1794 maxfname = zoneinfo_dir + sizeof zoneinfo_dir - fp - 1; 1795 if (dir != NULL) { 1796 while ((dp = readdir(dir)) != NULL) { 1797 if (dp->d_namlen > maxfname || dp->d_name[0] == '.') 1798 continue; 1799 strlcpy(fp, dp->d_name, maxfname); 1800 if (stat(zoneinfo_dir, &sb) == -1) 1801 continue; 1802 if (nfiles >= maxfiles) { 1803 p = realloc(tz_menu, 1804 2 * maxfiles * sizeof *tz_menu); 1805 if (p == NULL) 1806 break; 1807 tz_menu = p; 1808 memset(tz_menu + maxfiles, 0, 1809 maxfiles * sizeof *tz_menu); 1810 p = realloc(tz_names, 1811 2 * maxfiles * sizeof *tz_names); 1812 if (p == NULL) 1813 break; 1814 tz_names = p; 1815 memset(tz_names + maxfiles, 0, 1816 maxfiles * sizeof *tz_names); 1817 maxfiles *= 2; 1818 } 1819 if (S_ISREG(sb.st_mode)) 1820 tz_menu[nfiles].opt_action = set_tz_select; 1821 else if (S_ISDIR(sb.st_mode)) { 1822 tz_menu[nfiles].opt_action = set_tz_dir; 1823 strlcat(fp, "/", 1824 sizeof(zoneinfo_dir) - (fp - zoneinfo_dir)); 1825 } else 1826 continue; 1827 tz_names[nfiles] = strdup(zoneinfo_dir + zonerootlen); 1828 tz_menu[nfiles].opt_name = tz_names[nfiles]; 1829 nfiles++; 1830 } 1831 closedir(dir); 1832 } 1833 *fp = 0; 1834 1835 m->opts = tz_menu; 1836 m->numopts = nfiles; 1837 qsort(tz_menu, nfiles, sizeof *tz_menu, tz_sort); 1838 } 1839 1840 void 1841 get_tz_default(void) 1842 { 1843 char localtime_link[STRSIZE]; 1844 static char localtime_target[STRSIZE]; 1845 int rc; 1846 1847 strlcpy(localtime_link, target_expand("/etc/localtime"), 1848 sizeof localtime_link); 1849 1850 /* Add sanity check that /mnt/usr/share/zoneinfo contains 1851 * something useful 1852 */ 1853 1854 rc = readlink(localtime_link, localtime_target, 1855 sizeof(localtime_target) - 1); 1856 if (rc < 0) { 1857 /* error, default to UTC */ 1858 tz_default = "UTC"; 1859 } else { 1860 localtime_target[rc] = '\0'; 1861 tz_default = strchr(strstr(localtime_target, "zoneinfo"), '/') + 1; 1862 } 1863 } 1864 1865 /* 1866 * Choose from the files in usr/share/zoneinfo and set etc/localtime 1867 */ 1868 int 1869 set_timezone(void) 1870 { 1871 char localtime_link[STRSIZE]; 1872 char localtime_target[STRSIZE]; 1873 int menu_no; 1874 1875 strlcpy(zoneinfo_dir, target_expand("/usr/share/zoneinfo/"), 1876 sizeof zoneinfo_dir - 1); 1877 zonerootlen = strlen(zoneinfo_dir); 1878 1879 get_tz_default(); 1880 1881 tz_selected = strdup(tz_default); 1882 snprintf(tz_env, sizeof(tz_env), "%s%s", zoneinfo_dir, tz_selected); 1883 setenv("TZ", tz_env, 1); 1884 update_time_display(); 1885 1886 signal(SIGALRM, timezone_sig); 1887 alarm(60); 1888 1889 menu_no = new_menu(NULL, NULL, 14, 23, 9, 1890 12, 32, MC_ALWAYS_SCROLL | MC_NOSHORTCUT, 1891 tzm_set_names, NULL, NULL, 1892 "\nPlease consult the install documents.", 1893 MSG_exit_menu_generic); 1894 if (menu_no >= 0) { 1895 time_menu = menu_no; 1896 process_menu(menu_no, NULL); 1897 time_menu = -1; 1898 1899 free_menu(menu_no); 1900 } 1901 1902 alarm(0); 1903 signal(SIGALRM, SIG_IGN); 1904 1905 if (menu_no >= 0) { 1906 snprintf(localtime_target, sizeof(localtime_target), 1907 "/usr/share/zoneinfo/%s", tz_selected); 1908 strlcpy(localtime_link, target_expand("/etc/localtime"), 1909 sizeof localtime_link); 1910 unlink(localtime_link); 1911 symlink(localtime_target, localtime_link); 1912 } 1913 1914 return 1; 1915 } 1916 1917 void 1918 scripting_vfprintf(FILE *f, const char *fmt, va_list ap) 1919 { 1920 va_list ap2; 1921 1922 va_copy(ap2, ap); 1923 if (f) 1924 (void)vfprintf(f, fmt, ap); 1925 if (script) 1926 (void)vfprintf(script, fmt, ap2); 1927 } 1928 1929 void 1930 scripting_fprintf(FILE *f, const char *fmt, ...) 1931 { 1932 va_list ap; 1933 1934 va_start(ap, fmt); 1935 scripting_vfprintf(f, fmt, ap); 1936 va_end(ap); 1937 } 1938 1939 void 1940 add_rc_conf(const char *fmt, ...) 1941 { 1942 FILE *f; 1943 va_list ap; 1944 1945 va_start(ap, fmt); 1946 f = target_fopen("/etc/rc.conf", "a"); 1947 if (f != 0) { 1948 scripting_fprintf(NULL, "cat <<EOF >>%s/etc/rc.conf\n", 1949 target_prefix()); 1950 scripting_vfprintf(f, fmt, ap); 1951 fclose(f); 1952 scripting_fprintf(NULL, "EOF\n"); 1953 } 1954 va_end(ap); 1955 } 1956 1957 int 1958 del_rc_conf(const char *value) 1959 { 1960 FILE *fp, *nfp; 1961 char buf[4096]; /* Ridiculously high, but should be enough in any way */ 1962 char *rcconf, *tempname = NULL, *bakname = NULL; 1963 char *cp; 1964 int done = 0; 1965 int fd; 1966 int retval = 0; 1967 1968 /* The paths might seem strange, but using /tmp would require copy instead 1969 * of rename operations. */ 1970 if (asprintf(&rcconf, "%s", target_expand("/etc/rc.conf")) < 0 1971 || asprintf(&tempname, "%s", target_expand("/etc/rc.conf.tmp.XXXXXX")) < 0 1972 || asprintf(&bakname, "%s", target_expand("/etc/rc.conf.bak.XXXXXX")) < 0) { 1973 if (rcconf) 1974 free(rcconf); 1975 if (tempname) 1976 free(tempname); 1977 msg_fmt_display(MSG_rcconf_delete_failed, "%s", value); 1978 hit_enter_to_continue(NULL, NULL); 1979 return -1; 1980 } 1981 1982 if ((fd = mkstemp(bakname)) < 0) { 1983 msg_fmt_display(MSG_rcconf_delete_failed, "%s", value); 1984 hit_enter_to_continue(NULL, NULL); 1985 return -1; 1986 } 1987 close(fd); 1988 1989 if (!(fp = fopen(rcconf, "r+")) || (fd = mkstemp(tempname)) < 0) { 1990 if (fp) 1991 fclose(fp); 1992 msg_fmt_display(MSG_rcconf_delete_failed, "%s", value); 1993 hit_enter_to_continue(NULL, NULL); 1994 return -1; 1995 } 1996 1997 nfp = fdopen(fd, "w"); 1998 if (!nfp) { 1999 fclose(fp); 2000 close(fd); 2001 msg_fmt_display(MSG_rcconf_delete_failed, "%s", value); 2002 hit_enter_to_continue(NULL, NULL); 2003 return -1; 2004 } 2005 2006 while (fgets(buf, sizeof buf, fp) != NULL) { 2007 2008 cp = buf + strspn(buf, " \t"); /* Skip initial spaces */ 2009 if (strncmp(cp, value, strlen(value)) == 0) { 2010 cp += strlen(value); 2011 if (*cp != '=') 2012 scripting_fprintf(nfp, "%s", buf); 2013 else 2014 done = 1; 2015 } else { 2016 scripting_fprintf(nfp, "%s", buf); 2017 } 2018 } 2019 fclose(fp); 2020 fclose(nfp); 2021 2022 if (done) { 2023 if (rename(rcconf, bakname)) { 2024 msg_display(MSG_rcconf_backup_failed); 2025 if (!ask_noyes(NULL)) { 2026 retval = -1; 2027 goto done; 2028 } 2029 } 2030 2031 if (rename(tempname, rcconf)) { 2032 if (rename(bakname, rcconf)) { 2033 hit_enter_to_continue(MSG_rcconf_restore_failed, 2034 NULL); 2035 } else { 2036 hit_enter_to_continue(MSG_rcconf_delete_failed, 2037 NULL); 2038 } 2039 } else { 2040 (void)unlink(bakname); 2041 } 2042 } 2043 2044 done: 2045 (void)unlink(tempname); 2046 free(rcconf); 2047 free(tempname); 2048 free(bakname); 2049 return retval; 2050 } 2051 2052 void 2053 add_sysctl_conf(const char *fmt, ...) 2054 { 2055 FILE *f; 2056 va_list ap; 2057 2058 va_start(ap, fmt); 2059 f = target_fopen("/etc/sysctl.conf", "a"); 2060 if (f != 0) { 2061 scripting_fprintf(NULL, "cat <<EOF >>%s/etc/sysctl.conf\n", 2062 target_prefix()); 2063 scripting_vfprintf(f, fmt, ap); 2064 fclose(f); 2065 scripting_fprintf(NULL, "EOF\n"); 2066 } 2067 va_end(ap); 2068 } 2069 2070 void 2071 enable_rc_conf(void) 2072 { 2073 2074 replace("/etc/rc.conf", "s/^rc_configured=NO/rc_configured=YES/"); 2075 } 2076 2077 int 2078 check_lfs_progs(void) 2079 { 2080 2081 #ifndef NO_LFS 2082 return binary_available("fsck_lfs") && binary_available("mount_lfs") 2083 && binary_available("newfs_lfs"); 2084 #else 2085 return 0; 2086 #endif 2087 } 2088 2089 int 2090 set_is_source(const char *set_name) { 2091 int len = strlen(set_name); 2092 return len >= 3 && memcmp(set_name + len - 3, "src", 3) == 0; 2093 } 2094 2095 const char * 2096 set_dir_for_set(const char *set_name) { 2097 if (strcmp(set_name, "pkgsrc") == 0) 2098 return pkgsrc_dir; 2099 return set_is_source(set_name) ? set_dir_src : set_dir_bin; 2100 } 2101 2102 const char * 2103 ext_dir_for_set(const char *set_name) { 2104 if (strcmp(set_name, "pkgsrc") == 0) 2105 return ext_dir_pkgsrc; 2106 return set_is_source(set_name) ? ext_dir_src : ext_dir_bin; 2107 } 2108 2109 void 2110 do_coloring(unsigned int fg, unsigned int bg) { 2111 if (bg > COLOR_WHITE) 2112 bg = COLOR_BLUE; 2113 if (fg > COLOR_WHITE) 2114 fg = COLOR_WHITE; 2115 if (fg != bg && has_colors()) { 2116 init_pair(1, fg, bg); 2117 wbkgd(stdscr, COLOR_PAIR(1)); 2118 wbkgd(mainwin, COLOR_PAIR(1)); 2119 wattrset(stdscr, COLOR_PAIR(1)); 2120 wattrset(mainwin, COLOR_PAIR(1)); 2121 } 2122 /* redraw screen */ 2123 touchwin(stdscr); 2124 touchwin(mainwin); 2125 wrefresh(stdscr); 2126 wrefresh(mainwin); 2127 return; 2128 } 2129 2130 int 2131 set_menu_select(menudesc *m, void *arg) 2132 { 2133 *(int *)arg = m->cursel; 2134 return 1; 2135 } 2136 2137 /* 2138 * check whether a binary is available somewhere in PATH, 2139 * return 1 if found, 0 if not. 2140 */ 2141 static int 2142 binary_available(const char *prog) 2143 { 2144 char *p, tmp[MAXPATHLEN], *path = getenv("PATH"), *opath; 2145 2146 if (path == NULL) 2147 return access(prog, X_OK) == 0; 2148 path = strdup(path); 2149 if (path == NULL) 2150 return 0; 2151 2152 opath = path; 2153 2154 while ((p = strsep(&path, ":")) != NULL) { 2155 if (strlcpy(tmp, p, MAXPATHLEN) >= MAXPATHLEN) 2156 continue; 2157 if (strlcat(tmp, "/", MAXPATHLEN) >= MAXPATHLEN) 2158 continue; 2159 if (strlcat(tmp, prog, MAXPATHLEN) >= MAXPATHLEN) 2160 continue; 2161 if (access(tmp, X_OK) == 0) { 2162 free(opath); 2163 return 1; 2164 } 2165 } 2166 free(opath); 2167 return 0; 2168 } 2169 2170 const char * 2171 safectime(time_t *t) 2172 { 2173 const char *s = ctime(t); 2174 if (s != NULL) 2175 return s; 2176 2177 // Debugging. 2178 fprintf(stderr, "Can't convert to localtime 0x%jx (%s)\n", 2179 (intmax_t)*t, strerror(errno)); 2180 /*123456789012345678901234*/ 2181 return "preposterous clock time\n"; 2182 } 2183 2184 int 2185 ask_yesno(const char* msgtxt) 2186 { 2187 arg_rv p; 2188 2189 p.arg = __UNCONST(msgtxt); 2190 p.rv = -1; 2191 2192 process_menu(MENU_yesno, &p); 2193 return p.rv; 2194 } 2195 2196 int 2197 ask_noyes(const char *msgtxt) 2198 { 2199 arg_rv p; 2200 2201 p.arg = __UNCONST(msgtxt); 2202 p.rv = -1; 2203 2204 process_menu(MENU_noyes, &p); 2205 return p.rv; 2206 } 2207 2208 int 2209 ask_reedit(const struct disk_partitions *parts) 2210 { 2211 const char *args[2]; 2212 arg_rep_int arg; 2213 2214 args[0] = msg_string(parts->pscheme->name); 2215 args[1] = msg_string(parts->pscheme->short_name); 2216 arg.args.argv = args; 2217 arg.args.argc = 2; 2218 arg.rv = 0; 2219 process_menu(MENU_reedit, &arg); 2220 2221 return arg.rv; 2222 } 2223 2224 bool 2225 use_tgz_for_set(const char *set_name) 2226 { 2227 const struct distinfo *dist; 2228 2229 for (dist = dist_list; dist->set != SET_LAST; dist++) { 2230 if (dist->name == NULL) 2231 continue; 2232 if (strcmp(set_name, dist->name) == 0) 2233 return dist->force_tgz; 2234 } 2235 2236 return true; 2237 } 2238 2239 /* Return the postfix used for a given set */ 2240 const char * 2241 set_postfix(const char *set_name) 2242 { 2243 return use_tgz_for_set(set_name) ? dist_tgz_postfix : dist_postfix; 2244 } 2245 2246 /* 2247 * Replace positional arguments (encoded as $0 .. $N) in the string 2248 * passed by the contents of the passed argument array. 2249 * Caller must free() the result string. 2250 */ 2251 char* 2252 str_arg_subst(const char *src, size_t argc, const char **argv) 2253 { 2254 const char *p, *last; 2255 char *out, *t; 2256 size_t len; 2257 2258 len = strlen(src); 2259 for (p = strchr(src, '$'); p; p = strchr(p+1, '$')) { 2260 char *endp = NULL; 2261 size_t n; 2262 int e; 2263 2264 /* $ followed by a correct numeric position? */ 2265 n = strtou(p+1, &endp, 10, 0, INT_MAX, &e); 2266 if ((e == 0 || e == ENOTSUP) && n < argc) { 2267 len += strlen(argv[n]); 2268 len -= endp-p; 2269 p = endp-1; 2270 } 2271 } 2272 2273 out = malloc(len+1); 2274 if (out == NULL) 2275 return NULL; 2276 2277 t = out; 2278 for (last = src, p = strchr(src, '$'); p; p = strchr(p+1, '$')) { 2279 char *endp = NULL; 2280 size_t n; 2281 int e; 2282 2283 /* $ followed by a correct numeric position? */ 2284 n = strtou(p+1, &endp, 10, 0, INT_MAX, &e); 2285 if ((e == 0 || e == ENOTSUP) && n < argc) { 2286 size_t l = p-last; 2287 memcpy(t, last, l); 2288 t += l; 2289 strcpy(t, argv[n]); 2290 t += strlen(argv[n]); 2291 last = endp; 2292 } 2293 } 2294 if (*last) { 2295 strcpy(t, last); 2296 t += strlen(last); 2297 } else { 2298 *t = 0; 2299 } 2300 assert((size_t)(t-out) == len); 2301 2302 return out; 2303 } 2304 2305 /* 2306 * Does this string have any positional args that need expanding? 2307 */ 2308 bool 2309 needs_expanding(const char *src, size_t argc) 2310 { 2311 const char *p; 2312 2313 for (p = strchr(src, '$'); p; p = strchr(p+1, '$')) { 2314 char *endp = NULL; 2315 size_t n; 2316 int e; 2317 2318 /* $ followed by a correct numeric position? */ 2319 n = strtou(p+1, &endp, 10, 0, INT_MAX, &e); 2320 if ((e == 0 || e == ENOTSUP) && n < argc) 2321 return true; 2322 } 2323 return false; 2324 } 2325 2326 /* 2327 * Replace positional arguments (encoded as $0 .. $N) in the 2328 * message by the strings passed as ... and call outfunc 2329 * with the result. 2330 */ 2331 static void 2332 msg_display_subst_internal(void (*outfunc)(msg), 2333 const char *master, size_t argc, va_list ap) 2334 { 2335 const char **args, **arg; 2336 char *out; 2337 2338 args = calloc(argc, sizeof(*args)); 2339 if (args == NULL) 2340 return; 2341 2342 arg = args; 2343 for (size_t i = 0; i < argc; i++) 2344 *arg++ = va_arg(ap, const char*); 2345 2346 out = str_arg_subst(msg_string(master), argc, args); 2347 if (out != NULL) { 2348 outfunc(out); 2349 free(out); 2350 } 2351 free(args); 2352 } 2353 2354 /* 2355 * Replace positional arguments (encoded as $0 .. $N) in the 2356 * message by the strings passed as ... 2357 */ 2358 void 2359 msg_display_subst(const char *master, size_t argc, ...) 2360 { 2361 va_list ap; 2362 2363 va_start(ap, argc); 2364 msg_display_subst_internal(msg_display, master, argc, ap); 2365 va_end(ap); 2366 } 2367 2368 /* 2369 * Same as above, but add to message instead of starting a new one 2370 */ 2371 void 2372 msg_display_add_subst(const char *master, size_t argc, ...) 2373 { 2374 va_list ap; 2375 2376 va_start(ap, argc); 2377 msg_display_subst_internal(msg_display_add, master, argc, ap); 2378 va_end(ap); 2379 } 2380 2381 /* initialize have_* variables */ 2382 void 2383 check_available_binaries(void) 2384 { 2385 static int did_test = false; 2386 2387 if (did_test) return; 2388 did_test = 1; 2389 2390 have_raid = binary_available("raidctl"); 2391 have_vnd = binary_available("vndconfig"); 2392 have_cgd = binary_available("cgdconfig"); 2393 have_lvm = binary_available("lvm"); 2394 have_gpt = binary_available("gpt"); 2395 have_dk = binary_available("dkctl"); 2396 } 2397 2398 /* 2399 * Wait for enter and immediately clear the screen after user response 2400 * (in case some longer action follows, so the user has instant feedback) 2401 */ 2402 void 2403 hit_enter_to_continue(const char *prompt, const char *title) 2404 { 2405 if (prompt != NULL) 2406 msg_display(prompt); 2407 process_menu(MENU_ok, __UNCONST(title)); 2408 msg_clear(); 2409 wrefresh(mainwin); 2410 } 2411 2412 /* 2413 * On auto pilot: 2414 * convert an existing set of partitions ot a list of part_usage_info 2415 * so that we "want" exactly what is there already. 2416 */ 2417 static bool 2418 usage_info_list_from_parts(struct part_usage_info **list, size_t *count, 2419 struct disk_partitions *parts) 2420 { 2421 struct disk_part_info info; 2422 part_id pno; 2423 size_t no, num; 2424 2425 num = 0; 2426 for (pno = 0; pno < parts->num_part; pno++) { 2427 if (!parts->pscheme->get_part_info(parts, pno, &info)) 2428 continue; 2429 num++; 2430 } 2431 2432 *list = calloc(num, sizeof(**list)); 2433 if (*list == NULL) 2434 return false; 2435 2436 *count = num; 2437 for (no = pno = 0; pno < parts->num_part && no < num; pno++) { 2438 if (!parts->pscheme->get_part_info(parts, pno, &info)) 2439 continue; 2440 (*list)[no].size = info.size; 2441 if (info.last_mounted != NULL && *info.last_mounted != 0) { 2442 strlcpy((*list)[no].mount, info.last_mounted, 2443 sizeof((*list)[no].mount)); 2444 (*list)[no].instflags |= PUIINST_MOUNT; 2445 } 2446 (*list)[no].parts = parts; 2447 (*list)[no].cur_part_id = pno; 2448 (*list)[no].cur_start = info.start; 2449 (*list)[no].cur_flags = info.flags; 2450 (*list)[no].type = info.nat_type->generic_ptype; 2451 if ((*list)[no].type == PT_swap) { 2452 (*list)[no].fs_type = FS_SWAP; 2453 (*list)[no].fs_version = 0; 2454 } else { 2455 (*list)[no].fs_type = info.fs_type; 2456 (*list)[no].fs_version = info.fs_sub_type; 2457 } 2458 (*list)[no].fs_opt1 = info.fs_opt1; 2459 (*list)[no].fs_opt2 = info.fs_opt2; 2460 (*list)[no].fs_opt3 = info.fs_opt3; 2461 no++; 2462 } 2463 return true; 2464 } 2465 2466 bool 2467 empty_usage_set_from_parts(struct partition_usage_set *wanted, 2468 struct disk_partitions *parts) 2469 { 2470 usage_set_from_parts(wanted, parts); 2471 return true; 2472 } 2473 2474 bool 2475 usage_set_from_parts(struct partition_usage_set *wanted, 2476 struct disk_partitions *parts) 2477 { 2478 memset(wanted, 0, sizeof(*wanted)); 2479 wanted->parts = parts; 2480 2481 return usage_info_list_from_parts(&wanted->infos, &wanted->num, parts); 2482 } 2483 2484 bool 2485 usage_set_from_install_desc(struct partition_usage_set *pset, 2486 const struct install_partition_desc *install, 2487 struct disk_partitions *parts) 2488 { 2489 size_t cnt, i; 2490 2491 memset(pset, 0, sizeof(*pset)); 2492 pset->parts = parts; 2493 2494 if (!install->infos || !install->num) 2495 return false; 2496 2497 for (cnt = 0, i = 0; i < install->num; i++) { 2498 if (install->infos[i].parts != parts) 2499 continue; 2500 cnt++; 2501 } 2502 if (!cnt) 2503 return false; 2504 pset->num = cnt; 2505 pset->infos = calloc(cnt, sizeof(*pset->infos)); 2506 if (!pset->infos) 2507 return false; 2508 for (cnt = 0, i = 0; i < install->num; i++) { 2509 if (install->infos[i].parts != parts) 2510 continue; 2511 pset->infos[cnt] = install->infos[i]; 2512 cnt++; 2513 } 2514 return true; 2515 } 2516 2517 bool 2518 merge_usage_set_into_install_desc(struct install_partition_desc *install, 2519 const struct partition_usage_set *pset) 2520 { 2521 // XXX 2522 return false; 2523 } 2524 2525 struct disk_partitions * 2526 get_inner_parts(struct disk_partitions *parts) 2527 { 2528 daddr_t start, size; 2529 part_id pno; 2530 struct disk_part_info info; 2531 2532 if (parts->pscheme->secondary_scheme == NULL) 2533 return NULL; 2534 2535 start = -1; 2536 size = -1; 2537 if (parts->pscheme->guess_install_target == NULL || 2538 !parts->pscheme->guess_install_target(parts, &start, &size)) { 2539 for (pno = 0; pno < parts->num_part; pno++) { 2540 if (!parts->pscheme->get_part_info(parts, pno, &info)) 2541 continue; 2542 if (!(info.flags & PTI_SEC_CONTAINER)) 2543 continue; 2544 start = info.start; 2545 size = info.size; 2546 } 2547 } 2548 2549 if (size > 0) 2550 return parts->pscheme->secondary_partitions(parts, start, 2551 false); 2552 2553 return NULL; 2554 } 2555 2556 bool 2557 install_desc_from_parts(struct install_partition_desc *install, 2558 struct disk_partitions *parts) 2559 { 2560 struct disk_partitions *inner_parts; 2561 2562 memset(install, 0, sizeof(*install)); 2563 inner_parts = get_inner_parts(parts); 2564 if (inner_parts != NULL) 2565 parts = inner_parts; 2566 2567 return usage_info_list_from_parts(&install->infos, &install->num, 2568 parts); 2569 } 2570 2571 void 2572 free_usage_set(struct partition_usage_set *wanted) 2573 { 2574 /* XXX - free parts? free clone src? */ 2575 free(wanted->write_back); 2576 free(wanted->menu_opts); 2577 free(wanted->infos); 2578 } 2579 2580 void 2581 free_install_desc(struct install_partition_desc *install) 2582 { 2583 size_t i, j; 2584 2585 #ifndef NO_CLONES 2586 for (i = 0; i < install->num; i++) { 2587 struct selected_partitions *src = install->infos[i].clone_src; 2588 if (!(install->infos[i].flags & PUIFLG_CLONE_PARTS) || 2589 src == NULL) 2590 continue; 2591 free_selected_partitions(src); 2592 for (j = i+1; j < install->num; j++) 2593 if (install->infos[j].clone_src == src) 2594 install->infos[j].clone_src = NULL; 2595 } 2596 #endif 2597 2598 for (i = 0; i < install->num; i++) { 2599 struct disk_partitions * parts = install->infos[i].parts; 2600 2601 if (parts == NULL) 2602 continue; 2603 2604 if (parts->pscheme->free) 2605 parts->pscheme->free(parts); 2606 2607 /* NULL all other references to this parts */ 2608 for (j = i+1; j < install->num; j++) 2609 if (install->infos[j].parts == parts) 2610 install->infos[j].parts = NULL; 2611 } 2612 2613 free(install->write_back); 2614 free(install->infos); 2615 } 2616 2617 #ifdef MD_MAY_SWAP_TO 2618 bool 2619 may_swap_if_not_sdmmc(const char *disk) 2620 { 2621 int fd, res; 2622 prop_dictionary_t command_dict, args_dict, results_dict, data_dict; 2623 prop_string_t string; 2624 prop_number_t number; 2625 const char *parent = ""; 2626 2627 fd = open(DRVCTLDEV, O_RDONLY, 0); 2628 if (fd == -1) 2629 return true; 2630 2631 command_dict = prop_dictionary_create(); 2632 args_dict = prop_dictionary_create(); 2633 2634 string = prop_string_create_nocopy("get-properties"); 2635 prop_dictionary_set(command_dict, "drvctl-command", string); 2636 prop_object_release(string); 2637 2638 string = prop_string_create_copy(disk); 2639 prop_dictionary_set(args_dict, "device-name", string); 2640 prop_object_release(string); 2641 2642 prop_dictionary_set(command_dict, "drvctl-arguments", 2643 args_dict); 2644 prop_object_release(args_dict); 2645 2646 res = prop_dictionary_sendrecv_ioctl(command_dict, fd, 2647 DRVCTLCOMMAND, &results_dict); 2648 prop_object_release(command_dict); 2649 close(fd); 2650 if (res) 2651 return true; 2652 2653 number = prop_dictionary_get(results_dict, "drvctl-error"); 2654 if (prop_number_signed_value(number) == 0) { 2655 data_dict = prop_dictionary_get(results_dict, 2656 "drvctl-result-data"); 2657 if (data_dict != NULL) { 2658 string = prop_dictionary_get(data_dict, 2659 "device-parent"); 2660 if (string != NULL) 2661 parent = prop_string_value(string); 2662 } 2663 } 2664 2665 prop_object_release(results_dict); 2666 2667 if (parent == NULL) 2668 return true; 2669 2670 return strncmp(parent, "sdmmc", 5) != 0; 2671 } 2672 #endif 2673