1 /* $NetBSD: util.c,v 1.63 2022/01/03 11:44:02 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 size_t l; 1160 int txt_y, maxy, init_y; 1161 bool ok = false; 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=16 | 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); 1171 maxy = getmaxy(mainwin); 1172 init_y = txt_y; 1173 1174 echo(); 1175 do { 1176 txt_y++; 1177 if (txt_y >= maxy) { 1178 txt_y = init_y; 1179 wmove(mainwin, txt_y, 0); 1180 wclrtobot(mainwin); 1181 } else { 1182 wmove(mainwin, txt_y, 0); 1183 } 1184 msg_fmt_table_add(0, "> "); 1185 mvwgetnstr(mainwin, txt_y, 2, buf, sizeof buf); 1186 l = strlen(buf); 1187 if (l > 0) 1188 SHA256_Update(&ctx, (const uint8_t*)buf, l); 1189 } while(l > 0); 1190 noecho(); 1191 ok = ctx.bitcount >= 256; 1192 SHA256_Final(digest, &ctx); 1193 1194 wmove(mainwin, init_y, 0); 1195 wclrtobot(mainwin); 1196 wrefresh(mainwin); 1197 1198 if (ok) 1199 entropy_write_to_kernel(digest, sizeof digest); 1200 else 1201 hit_enter_to_continue(NULL, MSG_entropy_manual_not_enough); 1202 } 1203 1204 /* 1205 * Get a file by some means and return a (potentially only 1206 * temporary valid) path to the local copy. 1207 * If mountpt is nonempty, the caller should unmount that 1208 * directory after processing the file. 1209 * Return success if the file is available, or failure if 1210 * the user cancelled the request or network transfer failed. 1211 */ 1212 static bool 1213 entropy_get_file(bool use_netbsd_seed, char *path) 1214 { 1215 static struct ftpinfo server = { .user = "ftp" }; 1216 char url[STRSIZE], tmpf[PATH_MAX], mountpt[PATH_MAX]; 1217 const char *ftp_opt; 1218 arg_rv arg; 1219 int rv = 0; 1220 const char *file_desc = msg_string(use_netbsd_seed ? 1221 MSG_entropy_seed : MSG_entropy_data); 1222 char *dir; 1223 1224 path[0] = 0; 1225 mountpt[0] = 0; 1226 1227 sprintf(tmpf, "/tmp/entr.%06x", getpid()); 1228 1229 msg_display(use_netbsd_seed ? 1230 MSG_entropy_seed_hdr : MSG_entropy_data_hdr); 1231 msg_printf("\n\n %s\n\n", 1232 use_netbsd_seed ? 1233 "rndctl -S /tmp/entropy-file" : 1234 "dd if=/dev/random bs=32 count=1 of=/tmp/random.tmp"); 1235 strcpy(entropy_file, use_netbsd_seed ? 1236 "entropy-file" : "random.tmp"); 1237 process_menu(MENU_entropy_select_file, &rv); 1238 switch (rv) { 1239 case 1: 1240 case 2: 1241 #ifndef DEBUG 1242 if (!network_up) 1243 config_network(); 1244 #endif 1245 server.xfer = rv == 1 ? XFER_HTTP : XFER_FTP; 1246 arg.arg = &server; 1247 arg.rv = -1; 1248 msg_display_add_subst(MSG_entropy_via_download, 1, file_desc); 1249 msg_printf("\n\n"); 1250 process_menu(MENU_entropy_ftpsource, &arg); 1251 if (arg.rv == SET_RETRY) 1252 return false; 1253 make_url(url, &server, entropy_file); 1254 if (server.xfer == XFER_FTP && 1255 strcmp("ftp", server.user) == 0 && server.pass[0] == 0) { 1256 /* do anon ftp */ 1257 ftp_opt = "-a "; 1258 } else { 1259 ftp_opt = ""; 1260 } 1261 rv = run_program(RUN_DISPLAY | RUN_PROGRESS, 1262 "/usr/bin/ftp %s -o %s %s", 1263 ftp_opt, tmpf, url); 1264 strcpy(path, tmpf); 1265 return rv == 0; 1266 case 3: 1267 #ifndef DEBUG 1268 if (!network_up) 1269 config_network(); 1270 #endif 1271 rv = -1; 1272 msg_display_add_subst(MSG_entropy_via_nfs, 1, file_desc); 1273 msg_printf("\n\n"); 1274 process_menu(MENU_entropy_nfssource, &rv); 1275 if (rv == SET_RETRY) 1276 return false; 1277 if (nfs_host[0] != 0 && nfs_dir[0] != 0 && 1278 entropy_file[0] != 0) { 1279 strcpy(mountpt, "/tmp/ent-mnt.XXXXXX"); 1280 dir = mkdtemp(mountpt); 1281 if (dir == NULL) 1282 return false; 1283 sprintf(path, "%s/%s", mountpt, entropy_file); 1284 if (run_program(RUN_SILENT, 1285 "mount -t nfs -r %s:/%s %s", 1286 nfs_host, nfs_dir, mountpt) == 0) { 1287 run_program(RUN_SILENT, 1288 "cp %s %s", path, tmpf); 1289 run_program(RUN_SILENT, 1290 "umount %s", mountpt); 1291 rmdir(mountpt); 1292 strcpy(path, tmpf); 1293 } 1294 } 1295 break; 1296 case 4: 1297 rv = -1; 1298 /* Get device, filesystem, and filepath */ 1299 process_menu (MENU_entropy_localfs, &rv); 1300 if (rv == SET_RETRY) 1301 return false; 1302 if (localfs_dev[0] != 0 && localfs_fs[0] != 0 && 1303 entropy_file[0] != 0) { 1304 strcpy(mountpt, "/tmp/ent-mnt.XXXXXX"); 1305 dir = mkdtemp(mountpt); 1306 if (dir == NULL) 1307 return false; 1308 sprintf(path, "%s/%s", mountpt, entropy_file); 1309 if (run_program(RUN_SILENT, 1310 "mount -t %s -r /dev/%s %s", 1311 localfs_fs, localfs_dev, mountpt) == 0) { 1312 run_program(RUN_SILENT, 1313 "cp %s %s", path, tmpf); 1314 run_program(RUN_SILENT, 1315 "umount %s", mountpt); 1316 rmdir(mountpt); 1317 strcpy(path, tmpf); 1318 } 1319 } 1320 break; 1321 } 1322 return path[0] != 0; 1323 } 1324 1325 static void 1326 entropy_add_bin_file(void) 1327 { 1328 char fname[PATH_MAX]; 1329 1330 if (!entropy_get_file(false, fname)) 1331 return; 1332 if (access(fname, R_OK) == 0) 1333 run_program(RUN_SILENT, "dd if=%s of=" _PATH_RANDOM, 1334 fname); 1335 } 1336 1337 static void 1338 entropy_add_seed(void) 1339 { 1340 char fname[PATH_MAX]; 1341 1342 if (!entropy_get_file(true, fname)) 1343 return; 1344 if (access(fname, R_OK) == 0) 1345 run_program(RUN_SILENT, "rndctl -L %s", fname); 1346 } 1347 1348 /* 1349 * return true if we have enough entropy 1350 */ 1351 bool 1352 do_add_entropy(void) 1353 { 1354 int rv; 1355 1356 if (entropy_needed() == 0) 1357 return true; 1358 1359 for (;;) { 1360 if (entropy_needed() == 0) 1361 break; 1362 1363 msg_clear(); 1364 rv = 0; 1365 process_menu(MENU_not_enough_entropy, &rv); 1366 switch (rv) { 1367 case 0: 1368 return false; 1369 case 1: 1370 entropy_add_manual(); 1371 break; 1372 case 2: 1373 entropy_add_seed(); 1374 break; 1375 case 3: 1376 entropy_add_bin_file(); 1377 break; 1378 default: 1379 /* 1380 * retry after small delay to give a new USB device 1381 * a chance to attach and do deliver some 1382 * entropy 1383 */ 1384 msg_display("."); 1385 for (size_t i = 0; i < 10; i++) { 1386 if (entropy_needed() == 0) 1387 return true; 1388 sleep(1); 1389 msg_display_add("."); 1390 } 1391 } 1392 } 1393 1394 /* 1395 * Save entropy (maybe again) to give the seed file a good 1396 * entropy estimate. 1397 */ 1398 run_program(RUN_SILENT | RUN_CHROOT | RUN_ERROR_OK, 1399 "/etc/rc.d/random_seed stop"); 1400 1401 return true; 1402 } 1403 #endif 1404 1405 1406 1407 /* 1408 * Get and unpack the distribution. 1409 * Show success_msg if installation completes. 1410 * Otherwise show failure_msg and wait for the user to ack it before continuing. 1411 * success_msg and failure_msg must both be 0-adic messages. 1412 */ 1413 int 1414 get_and_unpack_sets(int update, msg setupdone_msg, msg success_msg, msg failure_msg) 1415 { 1416 distinfo *dist; 1417 int status; 1418 int set, olderror, oldfound; 1419 bool entropy_loaded = false; 1420 1421 /* Ensure mountpoint for distribution files exists in current root. */ 1422 (void)mkdir("/mnt2", S_IRWXU| S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); 1423 if (script) 1424 (void)fprintf(script, "mkdir -m 755 /mnt2\n"); 1425 1426 /* reset failure/success counters */ 1427 memset(&tarstats, 0, sizeof(tarstats)); 1428 1429 /* Find out which files to "get" if we get files. */ 1430 1431 /* Accurately count selected sets */ 1432 for (dist = dist_list; (set = dist->set) != SET_LAST; dist++) { 1433 if (dist->name == NULL) 1434 continue; 1435 if (set_status[set] & SET_NO_EXTRACT) 1436 continue; 1437 if ((set_status[set] & (SET_VALID | SET_SELECTED)) 1438 == (SET_VALID | SET_SELECTED)) 1439 tarstats.nselected++; 1440 } 1441 1442 status = SET_RETRY; 1443 for (dist = dist_list; (set = dist->set) != SET_LAST; dist++) { 1444 if (dist->name == NULL) 1445 continue; 1446 if (set_status[set] != (SET_VALID | SET_SELECTED)) 1447 continue; 1448 1449 /* save stats, in case we will retry */ 1450 oldfound = tarstats.nfound; 1451 olderror = tarstats.nerror; 1452 1453 if (status != SET_OK) { 1454 /* This might force a redraw.... */ 1455 clearok(curscr, 1); 1456 touchwin(stdscr); 1457 wrefresh(stdscr); 1458 /* Sort out the location of the set files */ 1459 do { 1460 umount_mnt2(); 1461 msg_fmt_display(MSG_distmedium, "%d%d%s", 1462 tarstats.nselected, 1463 tarstats.nsuccess + tarstats.nskipped, 1464 dist->name); 1465 fetch_fn = NULL; 1466 process_menu(MENU_distmedium, &status); 1467 } while (status == SET_RETRY); 1468 1469 if (status == SET_SKIP) { 1470 set_status[set] |= SET_SKIPPED; 1471 tarstats.nskipped++; 1472 continue; 1473 } 1474 if (status == SET_SKIP_GROUP) { 1475 skip_set(dist, status); 1476 continue; 1477 } 1478 if (status != SET_OK) { 1479 hit_enter_to_continue(failure_msg, NULL); 1480 return 1; 1481 } 1482 } 1483 1484 if (set_status[set] & SET_NO_EXTRACT) 1485 continue; 1486 1487 /* Try to extract this set */ 1488 status = extract_file(dist, update); 1489 if (status == SET_RETRY) { 1490 /* do this set again */ 1491 dist--; 1492 /* and reset statistics to what we had before this 1493 * set */ 1494 tarstats.nfound = oldfound; 1495 tarstats.nerror = olderror; 1496 } 1497 } 1498 1499 #ifdef MD_SET_EXTRACT_FINALIZE 1500 MD_SET_EXTRACT_FINALIZE(update); 1501 #endif 1502 1503 if (tarstats.nerror == 0 && tarstats.nsuccess == tarstats.nselected) { 1504 msg_display(MSG_endtarok); 1505 /* Give user a chance to see the success message */ 1506 sleep(1); 1507 } else { 1508 /* We encountered errors. Let the user know. */ 1509 msg_fmt_display(MSG_endtar, "%d%d%d%d%d%d", 1510 tarstats.nselected, tarstats.nnotfound, tarstats.nskipped, 1511 tarstats.nfound, tarstats.nsuccess, tarstats.nerror); 1512 hit_enter_to_continue(NULL, NULL); 1513 } 1514 1515 /* 1516 * postinstall needs to be run after extracting all sets, because 1517 * otherwise /var/db/obsolete will only have current information 1518 * from the base, comp, and etc sets. 1519 */ 1520 if (update && (set_status[SET_ETC] & SET_INSTALLED)) { 1521 int oldsendmail; 1522 oldsendmail = run_program(RUN_DISPLAY | RUN_CHROOT | 1523 RUN_ERROR_OK | RUN_PROGRESS, 1524 "/usr/sbin/postinstall -s /.sysinst -d / check mailerconf"); 1525 if (oldsendmail == 1) { 1526 msg_display(MSG_oldsendmail); 1527 if (ask_yesno(NULL)) { 1528 run_program(RUN_DISPLAY | RUN_CHROOT, 1529 "/usr/sbin/postinstall -s /.sysinst -d / fix mailerconf"); 1530 } 1531 } 1532 run_program(RUN_DISPLAY | RUN_CHROOT, 1533 "/usr/sbin/postinstall -s /.sysinst -d / fix"); 1534 1535 /* Don't discard the system's old entropy if any */ 1536 run_program(RUN_CHROOT | RUN_SILENT, 1537 "/etc/rc.d/random_seed start"); 1538 entropy_loaded = true; 1539 } 1540 1541 /* Configure the system */ 1542 if (set_status[SET_BASE] & SET_INSTALLED) 1543 run_makedev(); 1544 1545 if (!update) { 1546 struct stat sb1, sb2; 1547 1548 if (stat(target_expand("/"), &sb1) == 0 1549 && stat(target_expand("/var"), &sb2) == 0 1550 && sb1.st_dev != sb2.st_dev) { 1551 add_rc_conf("random_file=/etc/entropy-file\n"); 1552 if (target_file_exists_p("/boot.cfg")) { 1553 run_program(RUN_CHROOT|RUN_FATAL, 1554 "sh -c 'sed -e s./var/db/./etc/. " 1555 "< /boot.cfg " 1556 "> /tmp/boot.cfg.tmp'"); 1557 mv_within_target_or_die("/tmp/boot.cfg.tmp", 1558 "/boot.cfg"); 1559 1560 } 1561 } 1562 1563 #ifdef MD_BOOT_CFG_FINALIZE 1564 if (target_file_exists_p("/boot.cfg")) { 1565 MD_BOOT_CFG_FINALIZE("/boot.cfg"); 1566 } 1567 #endif 1568 1569 /* Save keyboard type */ 1570 save_kb_encoding(); 1571 1572 /* Other configuration. */ 1573 mnt_net_config(); 1574 } 1575 1576 /* Mounted dist dir? */ 1577 umount_mnt2(); 1578 1579 #ifdef CHECK_ENTROPY 1580 entropy_loaded |= entropy_needed() == 0; 1581 #endif 1582 1583 /* Save entropy -- on some systems it's ~all we'll ever get */ 1584 if (!update || entropy_loaded) 1585 run_program(RUN_SILENT | RUN_CHROOT | RUN_ERROR_OK, 1586 "/etc/rc.d/random_seed stop"); 1587 /* Install/Upgrade complete ... reboot or exit to script */ 1588 hit_enter_to_continue(success_msg, NULL); 1589 return 0; 1590 } 1591 1592 void 1593 umount_mnt2(void) 1594 { 1595 if (!mnt2_mounted) 1596 return; 1597 run_program(RUN_SILENT, "/sbin/umount /mnt2"); 1598 mnt2_mounted = 0; 1599 } 1600 1601 1602 /* 1603 * Do a quick sanity check that the target can reboot. 1604 * return 1 if everything OK, 0 if there is a problem. 1605 * Uses a table of files we expect to find after a base install/upgrade. 1606 */ 1607 1608 /* test flag and pathname to check for after unpacking. */ 1609 struct check_table { unsigned int mode; const char *path;} checks[] = { 1610 { S_IFREG, "/netbsd" }, 1611 { S_IFDIR, "/etc" }, 1612 { S_IFREG, "/etc/fstab" }, 1613 { S_IFREG, "/sbin/init" }, 1614 { S_IFREG, "/bin/sh" }, 1615 { S_IFREG, "/etc/rc" }, 1616 { S_IFREG, "/etc/rc.subr" }, 1617 { S_IFREG, "/etc/rc.conf" }, 1618 { S_IFDIR, "/dev" }, 1619 { S_IFCHR, "/dev/console" }, 1620 /* XXX check for rootdev in target /dev? */ 1621 { S_IFREG, "/sbin/fsck" }, 1622 { S_IFREG, "/sbin/fsck_ffs" }, 1623 { S_IFREG, "/sbin/mount" }, 1624 { S_IFREG, "/sbin/mount_ffs" }, 1625 { S_IFREG, "/sbin/mount_nfs" }, 1626 #if defined(DEBUG) || defined(DEBUG_CHECK) 1627 { S_IFREG, "/foo/bar" }, /* bad entry to exercise warning */ 1628 #endif 1629 { 0, 0 } 1630 1631 }; 1632 1633 /* 1634 * Check target for a single file. 1635 */ 1636 static int 1637 check_for(unsigned int mode, const char *pathname) 1638 { 1639 int found; 1640 1641 found = (target_test(mode, pathname) == 0); 1642 if (found == 0) 1643 msg_fmt_display(MSG_rootmissing, "%s", pathname); 1644 return found; 1645 } 1646 1647 /* 1648 * Check that all the files in check_table are present in the 1649 * target root. Warn if not found. 1650 */ 1651 int 1652 sanity_check(void) 1653 { 1654 int target_ok = 1; 1655 struct check_table *p; 1656 1657 for (p = checks; p->path; p++) { 1658 target_ok = target_ok && check_for(p->mode, p->path); 1659 } 1660 if (target_ok) 1661 return 0; 1662 1663 /* Uh, oh. Something's missing. */ 1664 hit_enter_to_continue(MSG_badroot, NULL); 1665 return 1; 1666 } 1667 1668 /* 1669 * Some globals to pass things back from callbacks 1670 */ 1671 static char zoneinfo_dir[STRSIZE]; 1672 static int zonerootlen; 1673 static char *tz_selected; /* timezonename (relative to share/zoneinfo */ 1674 const char *tz_default; /* UTC, or whatever /etc/localtime points to */ 1675 static char tz_env[STRSIZE]; 1676 static int save_cursel, save_topline; 1677 static int time_menu = -1; 1678 1679 static void 1680 update_time_display(void) 1681 { 1682 time_t t; 1683 struct tm *tm; 1684 char cur_time[STRSIZE], *p; 1685 1686 t = time(NULL); 1687 tm = localtime(&t); 1688 strlcpy(cur_time, safectime(&t), sizeof cur_time); 1689 p = strchr(cur_time, '\n'); 1690 if (p != NULL) 1691 *p = 0; 1692 1693 msg_clear(); 1694 msg_fmt_table_add(MSG_choose_timezone, "%s%s%s%s", 1695 tz_default, tz_selected, cur_time, tm ? tm->tm_zone : "?"); 1696 } 1697 1698 /* 1699 * Callback from timezone menu 1700 */ 1701 static int 1702 set_tz_select(menudesc *m, void *arg) 1703 { 1704 char *new; 1705 1706 if (m && strcmp(tz_selected, m->opts[m->cursel].opt_name) != 0) { 1707 /* Change the displayed timezone */ 1708 new = strdup(m->opts[m->cursel].opt_name); 1709 if (new == NULL) 1710 return 0; 1711 free(tz_selected); 1712 tz_selected = new; 1713 snprintf(tz_env, sizeof tz_env, "%.*s%s", 1714 zonerootlen, zoneinfo_dir, tz_selected); 1715 setenv("TZ", tz_env, 1); 1716 } 1717 if (m) 1718 /* Warp curser to 'Exit' line on menu */ 1719 m->cursel = -1; 1720 1721 update_time_display(); 1722 if (time_menu >= 1) { 1723 WINDOW *w = get_menudesc(time_menu)->mw; 1724 if (w != NULL) { 1725 touchwin(w); 1726 wrefresh(w); 1727 } 1728 } 1729 return 0; 1730 } 1731 1732 static int 1733 set_tz_back(menudesc *m, void *arg) 1734 { 1735 1736 zoneinfo_dir[zonerootlen] = 0; 1737 m->cursel = save_cursel; 1738 m->topline = save_topline; 1739 return 0; 1740 } 1741 1742 static int 1743 set_tz_dir(menudesc *m, void *arg) 1744 { 1745 1746 strlcpy(zoneinfo_dir + zonerootlen, m->opts[m->cursel].opt_name, 1747 sizeof zoneinfo_dir - zonerootlen); 1748 save_cursel = m->cursel; 1749 save_topline = m->topline; 1750 m->cursel = 0; 1751 m->topline = 0; 1752 return 0; 1753 } 1754 1755 /* 1756 * Alarm-handler to update example-display 1757 */ 1758 static void 1759 /*ARGSUSED*/ 1760 timezone_sig(int sig) 1761 { 1762 1763 set_tz_select(NULL, NULL); 1764 alarm(60); 1765 } 1766 1767 static int 1768 tz_sort(const void *a, const void *b) 1769 { 1770 return strcmp(((const menu_ent *)a)->opt_name, ((const menu_ent *)b)->opt_name); 1771 } 1772 1773 static void 1774 tzm_set_names(menudesc *m, void *arg) 1775 { 1776 DIR *dir; 1777 struct dirent *dp; 1778 static int nfiles; 1779 static int maxfiles = 32; 1780 static menu_ent *tz_menu; 1781 static char **tz_names; 1782 void *p; 1783 int maxfname; 1784 char *fp; 1785 struct stat sb; 1786 1787 if (tz_menu == NULL) 1788 tz_menu = calloc(maxfiles, sizeof *tz_menu); 1789 if (tz_names == NULL) 1790 tz_names = malloc(maxfiles * sizeof *tz_names); 1791 if (tz_menu == NULL || tz_names == NULL) 1792 return; /* error - skip timezone setting */ 1793 while (nfiles > 0) 1794 free(tz_names[--nfiles]); 1795 1796 dir = opendir(zoneinfo_dir); 1797 fp = strchr(zoneinfo_dir, 0); 1798 if (fp != zoneinfo_dir + zonerootlen) { 1799 tz_names[0] = 0; 1800 tz_menu[0].opt_name = msg_string(MSG_tz_back); 1801 tz_menu[0].opt_action = set_tz_back; 1802 nfiles = 1; 1803 } 1804 maxfname = zoneinfo_dir + sizeof zoneinfo_dir - fp - 1; 1805 if (dir != NULL) { 1806 while ((dp = readdir(dir)) != NULL) { 1807 if (dp->d_namlen > maxfname || dp->d_name[0] == '.') 1808 continue; 1809 strlcpy(fp, dp->d_name, maxfname); 1810 if (stat(zoneinfo_dir, &sb) == -1) 1811 continue; 1812 if (nfiles >= maxfiles) { 1813 p = realloc(tz_menu, 1814 2 * maxfiles * sizeof *tz_menu); 1815 if (p == NULL) 1816 break; 1817 tz_menu = p; 1818 memset(tz_menu + maxfiles, 0, 1819 maxfiles * sizeof *tz_menu); 1820 p = realloc(tz_names, 1821 2 * maxfiles * sizeof *tz_names); 1822 if (p == NULL) 1823 break; 1824 tz_names = p; 1825 memset(tz_names + maxfiles, 0, 1826 maxfiles * sizeof *tz_names); 1827 maxfiles *= 2; 1828 } 1829 if (S_ISREG(sb.st_mode)) 1830 tz_menu[nfiles].opt_action = set_tz_select; 1831 else if (S_ISDIR(sb.st_mode)) { 1832 tz_menu[nfiles].opt_action = set_tz_dir; 1833 strlcat(fp, "/", 1834 sizeof(zoneinfo_dir) - (fp - zoneinfo_dir)); 1835 } else 1836 continue; 1837 tz_names[nfiles] = strdup(zoneinfo_dir + zonerootlen); 1838 tz_menu[nfiles].opt_name = tz_names[nfiles]; 1839 nfiles++; 1840 } 1841 closedir(dir); 1842 } 1843 *fp = 0; 1844 1845 m->opts = tz_menu; 1846 m->numopts = nfiles; 1847 qsort(tz_menu, nfiles, sizeof *tz_menu, tz_sort); 1848 } 1849 1850 void 1851 get_tz_default(void) 1852 { 1853 char localtime_link[STRSIZE]; 1854 static char localtime_target[STRSIZE]; 1855 int rc; 1856 1857 strlcpy(localtime_link, target_expand("/etc/localtime"), 1858 sizeof localtime_link); 1859 1860 /* Add sanity check that /mnt/usr/share/zoneinfo contains 1861 * something useful 1862 */ 1863 1864 rc = readlink(localtime_link, localtime_target, 1865 sizeof(localtime_target) - 1); 1866 if (rc < 0) { 1867 /* error, default to UTC */ 1868 tz_default = "UTC"; 1869 } else { 1870 localtime_target[rc] = '\0'; 1871 tz_default = strchr(strstr(localtime_target, "zoneinfo"), '/') + 1; 1872 } 1873 } 1874 1875 /* 1876 * Choose from the files in usr/share/zoneinfo and set etc/localtime 1877 */ 1878 int 1879 set_timezone(void) 1880 { 1881 char localtime_link[STRSIZE]; 1882 char localtime_target[STRSIZE]; 1883 int menu_no; 1884 1885 strlcpy(zoneinfo_dir, target_expand("/usr/share/zoneinfo/"), 1886 sizeof zoneinfo_dir - 1); 1887 zonerootlen = strlen(zoneinfo_dir); 1888 1889 get_tz_default(); 1890 1891 tz_selected = strdup(tz_default); 1892 snprintf(tz_env, sizeof(tz_env), "%s%s", zoneinfo_dir, tz_selected); 1893 setenv("TZ", tz_env, 1); 1894 update_time_display(); 1895 1896 signal(SIGALRM, timezone_sig); 1897 alarm(60); 1898 1899 menu_no = new_menu(NULL, NULL, 14, 23, 9, 1900 12, 32, MC_ALWAYS_SCROLL | MC_NOSHORTCUT, 1901 tzm_set_names, NULL, NULL, 1902 "\nPlease consult the install documents.", 1903 MSG_exit_menu_generic); 1904 if (menu_no >= 0) { 1905 time_menu = menu_no; 1906 process_menu(menu_no, NULL); 1907 time_menu = -1; 1908 1909 free_menu(menu_no); 1910 } 1911 1912 alarm(0); 1913 signal(SIGALRM, SIG_IGN); 1914 1915 if (menu_no >= 0) { 1916 snprintf(localtime_target, sizeof(localtime_target), 1917 "/usr/share/zoneinfo/%s", tz_selected); 1918 strlcpy(localtime_link, target_expand("/etc/localtime"), 1919 sizeof localtime_link); 1920 unlink(localtime_link); 1921 symlink(localtime_target, localtime_link); 1922 } 1923 1924 return 1; 1925 } 1926 1927 void 1928 scripting_vfprintf(FILE *f, const char *fmt, va_list ap) 1929 { 1930 va_list ap2; 1931 1932 va_copy(ap2, ap); 1933 if (f) 1934 (void)vfprintf(f, fmt, ap); 1935 if (script) 1936 (void)vfprintf(script, fmt, ap2); 1937 } 1938 1939 void 1940 scripting_fprintf(FILE *f, const char *fmt, ...) 1941 { 1942 va_list ap; 1943 1944 va_start(ap, fmt); 1945 scripting_vfprintf(f, fmt, ap); 1946 va_end(ap); 1947 } 1948 1949 void 1950 add_rc_conf(const char *fmt, ...) 1951 { 1952 FILE *f; 1953 va_list ap; 1954 1955 va_start(ap, fmt); 1956 f = target_fopen("/etc/rc.conf", "a"); 1957 if (f != 0) { 1958 scripting_fprintf(NULL, "cat <<EOF >>%s/etc/rc.conf\n", 1959 target_prefix()); 1960 scripting_vfprintf(f, fmt, ap); 1961 fclose(f); 1962 scripting_fprintf(NULL, "EOF\n"); 1963 } 1964 va_end(ap); 1965 } 1966 1967 int 1968 del_rc_conf(const char *value) 1969 { 1970 FILE *fp, *nfp; 1971 char buf[4096]; /* Ridiculously high, but should be enough in any way */ 1972 char *rcconf, *tempname = NULL, *bakname = NULL; 1973 char *cp; 1974 int done = 0; 1975 int fd; 1976 int retval = 0; 1977 1978 /* The paths might seem strange, but using /tmp would require copy instead 1979 * of rename operations. */ 1980 if (asprintf(&rcconf, "%s", target_expand("/etc/rc.conf")) < 0 1981 || asprintf(&tempname, "%s", target_expand("/etc/rc.conf.tmp.XXXXXX")) < 0 1982 || asprintf(&bakname, "%s", target_expand("/etc/rc.conf.bak.XXXXXX")) < 0) { 1983 if (rcconf) 1984 free(rcconf); 1985 if (tempname) 1986 free(tempname); 1987 msg_fmt_display(MSG_rcconf_delete_failed, "%s", value); 1988 hit_enter_to_continue(NULL, NULL); 1989 return -1; 1990 } 1991 1992 if ((fd = mkstemp(bakname)) < 0) { 1993 msg_fmt_display(MSG_rcconf_delete_failed, "%s", value); 1994 hit_enter_to_continue(NULL, NULL); 1995 return -1; 1996 } 1997 close(fd); 1998 1999 if (!(fp = fopen(rcconf, "r+")) || (fd = mkstemp(tempname)) < 0) { 2000 if (fp) 2001 fclose(fp); 2002 msg_fmt_display(MSG_rcconf_delete_failed, "%s", value); 2003 hit_enter_to_continue(NULL, NULL); 2004 return -1; 2005 } 2006 2007 nfp = fdopen(fd, "w"); 2008 if (!nfp) { 2009 fclose(fp); 2010 close(fd); 2011 msg_fmt_display(MSG_rcconf_delete_failed, "%s", value); 2012 hit_enter_to_continue(NULL, NULL); 2013 return -1; 2014 } 2015 2016 while (fgets(buf, sizeof buf, fp) != NULL) { 2017 2018 cp = buf + strspn(buf, " \t"); /* Skip initial spaces */ 2019 if (strncmp(cp, value, strlen(value)) == 0) { 2020 cp += strlen(value); 2021 if (*cp != '=') 2022 scripting_fprintf(nfp, "%s", buf); 2023 else 2024 done = 1; 2025 } else { 2026 scripting_fprintf(nfp, "%s", buf); 2027 } 2028 } 2029 fclose(fp); 2030 fclose(nfp); 2031 2032 if (done) { 2033 if (rename(rcconf, bakname)) { 2034 msg_display(MSG_rcconf_backup_failed); 2035 if (!ask_noyes(NULL)) { 2036 retval = -1; 2037 goto done; 2038 } 2039 } 2040 2041 if (rename(tempname, rcconf)) { 2042 if (rename(bakname, rcconf)) { 2043 hit_enter_to_continue(MSG_rcconf_restore_failed, 2044 NULL); 2045 } else { 2046 hit_enter_to_continue(MSG_rcconf_delete_failed, 2047 NULL); 2048 } 2049 } else { 2050 (void)unlink(bakname); 2051 } 2052 } 2053 2054 done: 2055 (void)unlink(tempname); 2056 free(rcconf); 2057 free(tempname); 2058 free(bakname); 2059 return retval; 2060 } 2061 2062 void 2063 add_sysctl_conf(const char *fmt, ...) 2064 { 2065 FILE *f; 2066 va_list ap; 2067 2068 va_start(ap, fmt); 2069 f = target_fopen("/etc/sysctl.conf", "a"); 2070 if (f != 0) { 2071 scripting_fprintf(NULL, "cat <<EOF >>%s/etc/sysctl.conf\n", 2072 target_prefix()); 2073 scripting_vfprintf(f, fmt, ap); 2074 fclose(f); 2075 scripting_fprintf(NULL, "EOF\n"); 2076 } 2077 va_end(ap); 2078 } 2079 2080 void 2081 enable_rc_conf(void) 2082 { 2083 2084 replace("/etc/rc.conf", "s/^rc_configured=NO/rc_configured=YES/"); 2085 } 2086 2087 int 2088 check_lfs_progs(void) 2089 { 2090 2091 #ifndef NO_LFS 2092 return binary_available("fsck_lfs") && binary_available("mount_lfs") 2093 && binary_available("newfs_lfs"); 2094 #else 2095 return 0; 2096 #endif 2097 } 2098 2099 int 2100 set_is_source(const char *set_name) { 2101 int len = strlen(set_name); 2102 return len >= 3 && memcmp(set_name + len - 3, "src", 3) == 0; 2103 } 2104 2105 const char * 2106 set_dir_for_set(const char *set_name) { 2107 if (strcmp(set_name, "pkgsrc") == 0) 2108 return pkgsrc_dir; 2109 return set_is_source(set_name) ? set_dir_src : set_dir_bin; 2110 } 2111 2112 const char * 2113 ext_dir_for_set(const char *set_name) { 2114 if (strcmp(set_name, "pkgsrc") == 0) 2115 return ext_dir_pkgsrc; 2116 return set_is_source(set_name) ? ext_dir_src : ext_dir_bin; 2117 } 2118 2119 void 2120 do_coloring(unsigned int fg, unsigned int bg) { 2121 if (bg > COLOR_WHITE) 2122 bg = COLOR_BLUE; 2123 if (fg > COLOR_WHITE) 2124 fg = COLOR_WHITE; 2125 if (fg != bg && has_colors()) { 2126 init_pair(1, fg, bg); 2127 wbkgd(stdscr, COLOR_PAIR(1)); 2128 wbkgd(mainwin, COLOR_PAIR(1)); 2129 wattrset(stdscr, COLOR_PAIR(1)); 2130 wattrset(mainwin, COLOR_PAIR(1)); 2131 } 2132 /* redraw screen */ 2133 touchwin(stdscr); 2134 touchwin(mainwin); 2135 wrefresh(stdscr); 2136 wrefresh(mainwin); 2137 return; 2138 } 2139 2140 int 2141 set_menu_select(menudesc *m, void *arg) 2142 { 2143 *(int *)arg = m->cursel; 2144 return 1; 2145 } 2146 2147 /* 2148 * check wether a binary is available somewhere in PATH, 2149 * return 1 if found, 0 if not. 2150 */ 2151 static int 2152 binary_available(const char *prog) 2153 { 2154 char *p, tmp[MAXPATHLEN], *path = getenv("PATH"), *opath; 2155 2156 if (path == NULL) 2157 return access(prog, X_OK) == 0; 2158 path = strdup(path); 2159 if (path == NULL) 2160 return 0; 2161 2162 opath = path; 2163 2164 while ((p = strsep(&path, ":")) != NULL) { 2165 if (strlcpy(tmp, p, MAXPATHLEN) >= MAXPATHLEN) 2166 continue; 2167 if (strlcat(tmp, "/", MAXPATHLEN) >= MAXPATHLEN) 2168 continue; 2169 if (strlcat(tmp, prog, MAXPATHLEN) >= MAXPATHLEN) 2170 continue; 2171 if (access(tmp, X_OK) == 0) { 2172 free(opath); 2173 return 1; 2174 } 2175 } 2176 free(opath); 2177 return 0; 2178 } 2179 2180 const char * 2181 safectime(time_t *t) 2182 { 2183 const char *s = ctime(t); 2184 if (s != NULL) 2185 return s; 2186 2187 // Debugging. 2188 fprintf(stderr, "Can't convert to localtime 0x%jx (%s)\n", 2189 (intmax_t)*t, strerror(errno)); 2190 /*123456789012345678901234*/ 2191 return "preposterous clock time\n"; 2192 } 2193 2194 int 2195 ask_yesno(const char* msgtxt) 2196 { 2197 arg_rv p; 2198 2199 p.arg = __UNCONST(msgtxt); 2200 p.rv = -1; 2201 2202 process_menu(MENU_yesno, &p); 2203 return p.rv; 2204 } 2205 2206 int 2207 ask_noyes(const char *msgtxt) 2208 { 2209 arg_rv p; 2210 2211 p.arg = __UNCONST(msgtxt); 2212 p.rv = -1; 2213 2214 process_menu(MENU_noyes, &p); 2215 return p.rv; 2216 } 2217 2218 int 2219 ask_reedit(const struct disk_partitions *parts) 2220 { 2221 const char *args[2]; 2222 arg_rep_int arg; 2223 2224 args[0] = msg_string(parts->pscheme->name); 2225 args[1] = msg_string(parts->pscheme->short_name); 2226 arg.args.argv = args; 2227 arg.args.argc = 2; 2228 arg.rv = 0; 2229 process_menu(MENU_reedit, &arg); 2230 2231 return arg.rv; 2232 } 2233 2234 bool 2235 use_tgz_for_set(const char *set_name) 2236 { 2237 const struct distinfo *dist; 2238 2239 for (dist = dist_list; dist->set != SET_LAST; dist++) { 2240 if (dist->name == NULL) 2241 continue; 2242 if (strcmp(set_name, dist->name) == 0) 2243 return dist->force_tgz; 2244 } 2245 2246 return true; 2247 } 2248 2249 /* Return the postfix used for a given set */ 2250 const char * 2251 set_postfix(const char *set_name) 2252 { 2253 return use_tgz_for_set(set_name) ? dist_tgz_postfix : dist_postfix; 2254 } 2255 2256 /* 2257 * Replace positional arguments (encoded as $0 .. $N) in the string 2258 * passed by the contents of the passed argument array. 2259 * Caller must free() the result string. 2260 */ 2261 char* 2262 str_arg_subst(const char *src, size_t argc, const char **argv) 2263 { 2264 const char *p, *last; 2265 char *out, *t; 2266 size_t len; 2267 2268 len = strlen(src); 2269 for (p = strchr(src, '$'); p; p = strchr(p+1, '$')) { 2270 char *endp = NULL; 2271 size_t n; 2272 int e; 2273 2274 /* $ followed by a correct numeric position? */ 2275 n = strtou(p+1, &endp, 10, 0, INT_MAX, &e); 2276 if ((e == 0 || e == ENOTSUP) && n < argc) { 2277 len += strlen(argv[n]); 2278 len -= endp-p; 2279 p = endp-1; 2280 } 2281 } 2282 2283 out = malloc(len+1); 2284 if (out == NULL) 2285 return NULL; 2286 2287 t = out; 2288 for (last = src, p = strchr(src, '$'); p; p = strchr(p+1, '$')) { 2289 char *endp = NULL; 2290 size_t n; 2291 int e; 2292 2293 /* $ followed by a correct numeric position? */ 2294 n = strtou(p+1, &endp, 10, 0, INT_MAX, &e); 2295 if ((e == 0 || e == ENOTSUP) && n < argc) { 2296 size_t l = p-last; 2297 memcpy(t, last, l); 2298 t += l; 2299 strcpy(t, argv[n]); 2300 t += strlen(argv[n]); 2301 last = endp; 2302 } 2303 } 2304 if (*last) { 2305 strcpy(t, last); 2306 t += strlen(last); 2307 } else { 2308 *t = 0; 2309 } 2310 assert((size_t)(t-out) == len); 2311 2312 return out; 2313 } 2314 2315 /* 2316 * Does this string have any positional args that need expanding? 2317 */ 2318 bool 2319 needs_expanding(const char *src, size_t argc) 2320 { 2321 const char *p; 2322 2323 for (p = strchr(src, '$'); p; p = strchr(p+1, '$')) { 2324 char *endp = NULL; 2325 size_t n; 2326 int e; 2327 2328 /* $ followed by a correct numeric position? */ 2329 n = strtou(p+1, &endp, 10, 0, INT_MAX, &e); 2330 if ((e == 0 || e == ENOTSUP) && n < argc) 2331 return true; 2332 } 2333 return false; 2334 } 2335 2336 /* 2337 * Replace positional arguments (encoded as $0 .. $N) in the 2338 * message by the strings passed as ... and call outfunc 2339 * with the result. 2340 */ 2341 static void 2342 msg_display_subst_internal(void (*outfunc)(msg), 2343 const char *master, size_t argc, va_list ap) 2344 { 2345 const char **args, **arg; 2346 char *out; 2347 2348 args = calloc(argc, sizeof(*args)); 2349 if (args == NULL) 2350 return; 2351 2352 arg = args; 2353 for (size_t i = 0; i < argc; i++) 2354 *arg++ = va_arg(ap, const char*); 2355 2356 out = str_arg_subst(msg_string(master), argc, args); 2357 if (out != NULL) { 2358 outfunc(out); 2359 free(out); 2360 } 2361 free(args); 2362 } 2363 2364 /* 2365 * Replace positional arguments (encoded as $0 .. $N) in the 2366 * message by the strings passed as ... 2367 */ 2368 void 2369 msg_display_subst(const char *master, size_t argc, ...) 2370 { 2371 va_list ap; 2372 2373 va_start(ap, argc); 2374 msg_display_subst_internal(msg_display, master, argc, ap); 2375 va_end(ap); 2376 } 2377 2378 /* 2379 * Same as above, but add to message instead of starting a new one 2380 */ 2381 void 2382 msg_display_add_subst(const char *master, size_t argc, ...) 2383 { 2384 va_list ap; 2385 2386 va_start(ap, argc); 2387 msg_display_subst_internal(msg_display_add, master, argc, ap); 2388 va_end(ap); 2389 } 2390 2391 /* initialize have_* variables */ 2392 void 2393 check_available_binaries() 2394 { 2395 static int did_test = false; 2396 2397 if (did_test) return; 2398 did_test = 1; 2399 2400 have_raid = binary_available("raidctl"); 2401 have_vnd = binary_available("vndconfig"); 2402 have_cgd = binary_available("cgdconfig"); 2403 have_lvm = binary_available("lvm"); 2404 have_gpt = binary_available("gpt"); 2405 have_dk = binary_available("dkctl"); 2406 } 2407 2408 /* 2409 * Wait for enter and immediately clear the screen after user response 2410 * (in case some longer action follows, so the user has instant feedback) 2411 */ 2412 void 2413 hit_enter_to_continue(const char *prompt, const char *title) 2414 { 2415 if (prompt != NULL) 2416 msg_display(prompt); 2417 process_menu(MENU_ok, __UNCONST(title)); 2418 msg_clear(); 2419 wrefresh(mainwin); 2420 } 2421 2422 /* 2423 * On auto pilot: 2424 * convert an existing set of partitions ot a list of part_usage_info 2425 * so that we "want" exactly what is there already. 2426 */ 2427 static bool 2428 usage_info_list_from_parts(struct part_usage_info **list, size_t *count, 2429 struct disk_partitions *parts) 2430 { 2431 struct disk_part_info info; 2432 part_id pno; 2433 size_t no, num; 2434 2435 num = 0; 2436 for (pno = 0; pno < parts->num_part; pno++) { 2437 if (!parts->pscheme->get_part_info(parts, pno, &info)) 2438 continue; 2439 num++; 2440 } 2441 2442 *list = calloc(num, sizeof(**list)); 2443 if (*list == NULL) 2444 return false; 2445 2446 *count = num; 2447 for (no = pno = 0; pno < parts->num_part && no < num; pno++) { 2448 if (!parts->pscheme->get_part_info(parts, pno, &info)) 2449 continue; 2450 (*list)[no].size = info.size; 2451 if (info.last_mounted != NULL && *info.last_mounted != 0) { 2452 strlcpy((*list)[no].mount, info.last_mounted, 2453 sizeof((*list)[no].mount)); 2454 (*list)[no].instflags |= PUIINST_MOUNT; 2455 } 2456 (*list)[no].parts = parts; 2457 (*list)[no].cur_part_id = pno; 2458 (*list)[no].cur_start = info.start; 2459 (*list)[no].cur_flags = info.flags; 2460 (*list)[no].type = info.nat_type->generic_ptype; 2461 if ((*list)[no].type == PT_swap) { 2462 (*list)[no].fs_type = FS_SWAP; 2463 (*list)[no].fs_version = 0; 2464 } else { 2465 (*list)[no].fs_type = info.fs_type; 2466 (*list)[no].fs_version = info.fs_sub_type; 2467 } 2468 (*list)[no].fs_opt1 = info.fs_opt1; 2469 (*list)[no].fs_opt2 = info.fs_opt2; 2470 (*list)[no].fs_opt3 = info.fs_opt3; 2471 no++; 2472 } 2473 return true; 2474 } 2475 2476 bool 2477 usage_set_from_parts(struct partition_usage_set *wanted, 2478 struct disk_partitions *parts) 2479 { 2480 memset(wanted, 0, sizeof(*wanted)); 2481 wanted->parts = parts; 2482 2483 return usage_info_list_from_parts(&wanted->infos, &wanted->num, parts); 2484 } 2485 2486 struct disk_partitions * 2487 get_inner_parts(struct disk_partitions *parts) 2488 { 2489 daddr_t start, size; 2490 part_id pno; 2491 struct disk_part_info info; 2492 2493 if (parts->pscheme->secondary_scheme == NULL) 2494 return NULL; 2495 2496 start = -1; 2497 size = -1; 2498 if (parts->pscheme->guess_install_target == NULL || 2499 !parts->pscheme->guess_install_target(parts, &start, &size)) { 2500 for (pno = 0; pno < parts->num_part; pno++) { 2501 if (!parts->pscheme->get_part_info(parts, pno, &info)) 2502 continue; 2503 if (!(info.flags & PTI_SEC_CONTAINER)) 2504 continue; 2505 start = info.start; 2506 size = info.size; 2507 } 2508 } 2509 2510 if (size > 0) 2511 return parts->pscheme->secondary_partitions(parts, start, 2512 false); 2513 2514 return NULL; 2515 } 2516 2517 bool 2518 install_desc_from_parts(struct install_partition_desc *install, 2519 struct disk_partitions *parts) 2520 { 2521 struct disk_partitions *inner_parts; 2522 2523 memset(install, 0, sizeof(*install)); 2524 inner_parts = get_inner_parts(parts); 2525 if (inner_parts != NULL) 2526 parts = inner_parts; 2527 2528 return usage_info_list_from_parts(&install->infos, &install->num, 2529 parts); 2530 } 2531 2532 void 2533 free_usage_set(struct partition_usage_set *wanted) 2534 { 2535 /* XXX - free parts? free clone src? */ 2536 free(wanted->write_back); 2537 free(wanted->menu_opts); 2538 free(wanted->infos); 2539 } 2540 2541 void 2542 free_install_desc(struct install_partition_desc *install) 2543 { 2544 #ifndef NO_CLONES 2545 size_t i, j; 2546 2547 for (i = 0; i < install->num; i++) { 2548 struct selected_partitions *src = install->infos[i].clone_src; 2549 if (!(install->infos[i].flags & PUIFLG_CLONE_PARTS) || 2550 src == NULL) 2551 continue; 2552 free_selected_partitions(src); 2553 for (j = i+1; j < install->num; j++) 2554 if (install->infos[j].clone_src == src) 2555 install->infos[j].clone_src = NULL; 2556 } 2557 #endif 2558 free(install->write_back); 2559 free(install->infos); 2560 } 2561 2562 #ifdef MD_MAY_SWAP_TO 2563 bool 2564 may_swap_if_not_sdmmc(const char *disk) 2565 { 2566 int fd, res; 2567 prop_dictionary_t command_dict, args_dict, results_dict, data_dict; 2568 prop_string_t string; 2569 prop_number_t number; 2570 const char *parent = ""; 2571 2572 fd = open(DRVCTLDEV, O_RDONLY, 0); 2573 if (fd == -1) 2574 return true; 2575 2576 command_dict = prop_dictionary_create(); 2577 args_dict = prop_dictionary_create(); 2578 2579 string = prop_string_create_nocopy("get-properties"); 2580 prop_dictionary_set(command_dict, "drvctl-command", string); 2581 prop_object_release(string); 2582 2583 string = prop_string_create_copy(disk); 2584 prop_dictionary_set(args_dict, "device-name", string); 2585 prop_object_release(string); 2586 2587 prop_dictionary_set(command_dict, "drvctl-arguments", 2588 args_dict); 2589 prop_object_release(args_dict); 2590 2591 res = prop_dictionary_sendrecv_ioctl(command_dict, fd, 2592 DRVCTLCOMMAND, &results_dict); 2593 prop_object_release(command_dict); 2594 close(fd); 2595 if (res) 2596 return true; 2597 2598 number = prop_dictionary_get(results_dict, "drvctl-error"); 2599 if (prop_number_signed_value(number) == 0) { 2600 data_dict = prop_dictionary_get(results_dict, 2601 "drvctl-result-data"); 2602 if (data_dict != NULL) { 2603 string = prop_dictionary_get(data_dict, 2604 "device-parent"); 2605 if (string != NULL) 2606 parent = prop_string_value(string); 2607 } 2608 } 2609 2610 prop_object_release(results_dict); 2611 2612 if (parent == NULL) 2613 return true; 2614 2615 return strncmp(parent, "sdmmc", 5) != 0; 2616 } 2617 #endif 2618