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