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