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