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