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