1 /* $NetBSD: util.c,v 1.18 2018/11/19 17:17:43 martin Exp $ */ 2 3 /* 4 * Copyright 1997 Piermont Information Systems Inc. 5 * All rights reserved. 6 * 7 * Written by Philip A. Nelson for Piermont Information Systems Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. The name of Piermont Information Systems Inc. may not be used to endorse 18 * or promote products derived from this software without specific prior 19 * written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS'' 22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 31 * THE POSSIBILITY OF SUCH DAMAGE. 32 * 33 */ 34 35 /* util.c -- routines that don't really fit anywhere else... */ 36 37 #include <assert.h> 38 #include <inttypes.h> 39 #include <stdio.h> 40 #include <stdarg.h> 41 #include <string.h> 42 #include <unistd.h> 43 #include <sys/mount.h> 44 #include <sys/disklabel.h> 45 #include <sys/dkio.h> 46 #include <sys/ioctl.h> 47 #include <sys/types.h> 48 #include <sys/param.h> 49 #include <sys/sysctl.h> 50 #include <sys/stat.h> 51 #include <sys/statvfs.h> 52 #include <isofs/cd9660/iso.h> 53 #include <curses.h> 54 #include <err.h> 55 #include <errno.h> 56 #include <dirent.h> 57 #include <util.h> 58 #include "defs.h" 59 #include "md.h" 60 #include "msg_defs.h" 61 #include "menu_defs.h" 62 63 #ifndef MD_SETS_SELECTED 64 #define MD_SETS_SELECTED SET_KERNEL_1, SET_SYSTEM, SET_X11, SET_MD 65 #endif 66 #ifndef MD_SETS_SELECTED_MINIMAL 67 #define MD_SETS_SELECTED_MINIMAL SET_KERNEL_1, SET_CORE 68 #endif 69 #ifndef MD_SETS_SELECTED_NOX 70 #define MD_SETS_SELECTED_NOX SET_KERNEL_1, SET_SYSTEM, SET_MD 71 #endif 72 #ifndef MD_SETS_VALID 73 #define MD_SETS_VALID SET_KERNEL, SET_SYSTEM, SET_X11, SET_MD, SET_SOURCE, SET_DEBUGGING 74 #endif 75 76 #define MAX_CD_DEVS 256 /* how many cd drives do we expect to attach */ 77 #define ISO_BLKSIZE ISO_DEFAULT_BLOCK_SIZE 78 79 static const char *msg_yes, *msg_no, *msg_all, *msg_some, *msg_none; 80 static const char *msg_cur_distsets_row; 81 static int select_menu_width; 82 83 static uint8_t set_status[SET_GROUP_END]; 84 #define SET_VALID 0x01 85 #define SET_SELECTED 0x02 86 #define SET_SKIPPED 0x04 87 #define SET_INSTALLED 0x08 88 89 struct tarstats { 90 int nselected; 91 int nfound; 92 int nnotfound; 93 int nerror; 94 int nsuccess; 95 int nskipped; 96 } tarstats; 97 98 distinfo dist_list[] = { 99 #ifdef SET_KERNEL_1_NAME 100 {SET_KERNEL_1_NAME, SET_KERNEL_1, false, MSG_set_kernel_1, NULL}, 101 #endif 102 #ifdef SET_KERNEL_2_NAME 103 {SET_KERNEL_2_NAME, SET_KERNEL_2, false, MSG_set_kernel_2, NULL}, 104 #endif 105 #ifdef SET_KERNEL_3_NAME 106 {SET_KERNEL_3_NAME, SET_KERNEL_3, false, MSG_set_kernel_3, NULL}, 107 #endif 108 #ifdef SET_KERNEL_4_NAME 109 {SET_KERNEL_4_NAME, SET_KERNEL_4, false, MSG_set_kernel_4, NULL}, 110 #endif 111 #ifdef SET_KERNEL_5_NAME 112 {SET_KERNEL_5_NAME, SET_KERNEL_5, false, MSG_set_kernel_5, NULL}, 113 #endif 114 #ifdef SET_KERNEL_6_NAME 115 {SET_KERNEL_6_NAME, SET_KERNEL_6, false, MSG_set_kernel_6, NULL}, 116 #endif 117 #ifdef SET_KERNEL_7_NAME 118 {SET_KERNEL_7_NAME, SET_KERNEL_7, false, MSG_set_kernel_7, NULL}, 119 #endif 120 #ifdef SET_KERNEL_8_NAME 121 {SET_KERNEL_8_NAME, SET_KERNEL_8, false, MSG_set_kernel_8, NULL}, 122 #endif 123 #ifdef SET_KERNEL_9_NAME 124 {SET_KERNEL_9_NAME, SET_KERNEL_9, false, MSG_set_kernel_9, NULL}, 125 #endif 126 127 {"modules", SET_MODULES, false, MSG_set_modules, NULL}, 128 {"base", SET_BASE, false, MSG_set_base, NULL}, 129 {"etc", SET_ETC, false, MSG_set_system, NULL}, 130 {"comp", SET_COMPILER, false, MSG_set_compiler, NULL}, 131 {"games", SET_GAMES, false, MSG_set_games, NULL}, 132 {"man", SET_MAN_PAGES, false, MSG_set_man_pages, NULL}, 133 {"misc", SET_MISC, false, MSG_set_misc, NULL}, 134 {"tests", SET_TESTS, false, MSG_set_tests, NULL}, 135 {"text", SET_TEXT_TOOLS, false, MSG_set_text_tools, NULL}, 136 137 {NULL, SET_GROUP, false, MSG_set_X11, NULL}, 138 {"xbase", SET_X11_BASE, false, MSG_set_X11_base, NULL}, 139 {"xcomp", SET_X11_PROG, false, MSG_set_X11_prog, NULL}, 140 {"xetc", SET_X11_ETC, false, MSG_set_X11_etc, NULL}, 141 {"xfont", SET_X11_FONTS, false, MSG_set_X11_fonts, NULL}, 142 {"xserver", SET_X11_SERVERS, false, MSG_set_X11_servers, NULL}, 143 {NULL, SET_GROUP_END, false, NULL, NULL}, 144 145 #ifdef SET_MD_1_NAME 146 {SET_MD_1_NAME, SET_MD_1, false, MSG_set_md_1, NULL}, 147 #endif 148 #ifdef SET_MD_2_NAME 149 {SET_MD_2_NAME, SET_MD_2, false, MSG_set_md_2, NULL}, 150 #endif 151 #ifdef SET_MD_3_NAME 152 {SET_MD_3_NAME, SET_MD_3, false, MSG_set_md_3, NULL}, 153 #endif 154 #ifdef SET_MD_4_NAME 155 {SET_MD_4_NAME, SET_MD_4, false, MSG_set_md_4, NULL}, 156 #endif 157 158 {NULL, SET_GROUP, true, MSG_set_source, NULL}, 159 {"syssrc", SET_SYSSRC, true, MSG_set_syssrc, NULL}, 160 {"src", SET_SRC, true, MSG_set_src, NULL}, 161 {"sharesrc", SET_SHARESRC, true, MSG_set_sharesrc, NULL}, 162 {"gnusrc", SET_GNUSRC, true, MSG_set_gnusrc, NULL}, 163 {"xsrc", SET_XSRC, true, MSG_set_xsrc, NULL}, 164 {"debug", SET_DEBUG, false, MSG_set_debug, NULL}, 165 {"xdebug", SET_X11_DEBUG, false, MSG_set_xdebug, NULL}, 166 {NULL, SET_GROUP_END, false, NULL, NULL}, 167 168 {NULL, SET_LAST, false, NULL, NULL}, 169 }; 170 171 #define MAX_CD_INFOS 16 /* how many media can be found? */ 172 struct cd_info { 173 char device_name[16]; 174 char menu[100]; 175 }; 176 static struct cd_info cds[MAX_CD_INFOS]; 177 178 /* 179 * local prototypes 180 */ 181 182 static int check_for(unsigned int mode, const char *pathname); 183 static int get_iso9660_volname(int dev, int sess, char *volname); 184 static int get_available_cds(void); 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 = nelem(sets_selected_minimal); 201 } else if (flags & SFLAG_NOX) { 202 sets_selected = sets_selected_nox; 203 nelem_selected = nelem(sets_selected_nox); 204 } else { 205 sets_selected = sets_selected_full; 206 nelem_selected = nelem(sets_selected_full); 207 } 208 209 for (i = 0; i < nelem(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 msg_cur_distsets_row = msg_string(MSG_cur_distsets_row); 223 224 /* Find longest and use it to determine width of selection menu */ 225 len = strlen(msg_no); longest = msg_no; 226 i = strlen(msg_yes); if (i > len) {len = i; longest = msg_yes; } 227 i = strlen(msg_all); if (i > len) {len = i; longest = msg_all; } 228 i = strlen(msg_some); if (i > len) {len = i; longest = msg_some; } 229 i = strlen(msg_none); if (i > len) {len = i; longest = msg_none; } 230 select_menu_width = snprintf(NULL, 0, msg_cur_distsets_row, "",longest); 231 232 /* Give the md code a chance to choose the right kernel, etc. */ 233 md_init_set_status(flags); 234 } 235 236 int 237 dir_exists_p(const char *path) 238 { 239 240 return file_mode_match(path, S_IFDIR); 241 } 242 243 int 244 file_exists_p(const char *path) 245 { 246 247 return file_mode_match(path, S_IFREG); 248 } 249 250 int 251 file_mode_match(const char *path, unsigned int mode) 252 { 253 struct stat st; 254 255 return (stat(path, &st) == 0 && (st.st_mode & S_IFMT) == mode); 256 } 257 258 uint 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_display_add(MSG_fdmount, 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) 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, sizeof pd->volume_id); 406 last = sizeof pd->volume_id-1; 407 while (last >= 0 408 && (volname[last] == ' ' || volname[last] == 0)) 409 last--; 410 volname[last+1] = 0; 411 return 0; 412 } 413 } 414 return -1; 415 } 416 417 /* 418 * Local state while iterating CDs and collecting volumes 419 */ 420 struct get_available_cds_state { 421 struct cd_info *info; 422 size_t count; 423 }; 424 425 /* 426 * Callback function: if this is a CD, enumerate all volumes on it 427 */ 428 static bool 429 get_available_cds_helper(void *arg, const char *device) 430 { 431 struct get_available_cds_state *state = arg; 432 char dname[16], volname[80]; 433 struct disklabel label; 434 int part, dev, error, sess, ready; 435 436 if (!is_cdrom_device(device, false)) 437 return true; 438 439 sprintf(dname, "/dev/r%s%c", device, 'a'+RAW_PART); 440 dev = open(dname, O_RDONLY, 0); 441 if (dev == -1) 442 return true; 443 444 ready = 0; 445 error = ioctl(dev, DIOCTUR, &ready); 446 if (error != 0 || ready == 0) { 447 close(dev); 448 return true; 449 } 450 error = ioctl(dev, DIOCGDINFO, &label); 451 close(dev); 452 if (error != 0) 453 return true; 454 455 for (part = 0; part < label.d_npartitions; part++) { 456 457 if (label.d_partitions[part].p_fstype == FS_UNUSED 458 || label.d_partitions[part].p_size == 0) 459 continue; 460 461 if (label.d_partitions[part].p_fstype == FS_ISO9660) { 462 sess = label.d_partitions[part].p_cdsession; 463 sprintf(dname, "/dev/r%s%c", device, 'a'+part); 464 dev = open(dname, O_RDONLY, 0); 465 if (dev == -1) 466 continue; 467 error = get_iso9660_volname(dev, sess, volname); 468 close(dev); 469 if (error) 470 continue; 471 sprintf(state->info->device_name, 472 "%s%c", device, 'a'+part); 473 sprintf(state->info->menu, "%s (%s)", 474 state->info->device_name, volname); 475 } else { 476 /* 477 * All install CDs use partition 478 * a for the sets. 479 */ 480 if (part > 0) 481 continue; 482 sprintf(state->info->device_name, 483 "%s%c", device, 'a'+part); 484 strcpy(state->info->menu, state->info->device_name); 485 } 486 state->info++; 487 if (++state->count >= MAX_CD_INFOS) 488 return false; 489 } 490 491 return true; 492 } 493 494 /* 495 * Get a list of all available CD media (not drives!), return 496 * the number of entries collected. 497 */ 498 static int 499 get_available_cds(void) 500 { 501 struct get_available_cds_state data; 502 503 data.info = cds; 504 data.count = 0; 505 506 enumerate_disks(&data, get_available_cds_helper); 507 508 return data.count; 509 } 510 511 static int 512 cd_has_sets(void) 513 { 514 /* Mount it */ 515 if (run_program(RUN_SILENT, "/sbin/mount -rt cd9660 /dev/%s /mnt2", 516 cdrom_dev) != 0) 517 return 0; 518 519 mnt2_mounted = 1; 520 521 snprintf(ext_dir_bin, sizeof ext_dir_bin, "%s/%s", "/mnt2", set_dir_bin); 522 snprintf(ext_dir_src, sizeof ext_dir_src, "%s/%s", "/mnt2", set_dir_src); 523 return dir_exists_p(ext_dir_bin); 524 } 525 526 /* 527 * Check whether we can remove the boot media. 528 * If it is not a local filesystem, return -1. 529 * If we can not decide for sure (can not tell MD content from plain ffs 530 * on hard disk, for example), return 0. 531 * If it is a CD/DVD, return 1. 532 */ 533 int 534 boot_media_still_needed(void) 535 { 536 struct statvfs sb; 537 538 if (statvfs("/", &sb) == 0) { 539 if (!(sb.f_flag & ST_LOCAL)) 540 return -1; 541 if (strcmp(sb.f_fstypename, MOUNT_CD9660) == 0 542 || strcmp(sb.f_fstypename, MOUNT_UDF) == 0) 543 return 1; 544 } 545 546 return 0; 547 } 548 549 /* 550 * Get from a CDROM distribution. 551 * Also used on "installation using bootable install media" 552 * as the default option in the "distmedium" menu. 553 */ 554 int 555 get_via_cdrom(void) 556 { 557 menu_ent cd_menu[MAX_CD_INFOS]; 558 struct stat sb; 559 int rv, num_cds, menu_cd, i, selected_cd = 0; 560 bool silent = false; 561 int mib[2]; 562 char rootdev[SSTRSIZE] = ""; 563 size_t varlen; 564 565 /* If root is not md(4) and we have set dir, skip this step. */ 566 mib[0] = CTL_KERN; 567 mib[1] = KERN_ROOT_DEVICE; 568 varlen = sizeof(rootdev); 569 (void)sysctl(mib, 2, rootdev, &varlen, NULL, 0); 570 if (stat(set_dir_bin, &sb) == 0 && S_ISDIR(sb.st_mode) && 571 strncmp("md", rootdev, 2) != 0) { 572 strlcpy(ext_dir_bin, set_dir_bin, sizeof ext_dir_bin); 573 strlcpy(ext_dir_src, set_dir_src, sizeof ext_dir_src); 574 return SET_OK; 575 } 576 577 num_cds = get_available_cds(); 578 if (num_cds <= 0) { 579 silent = true; 580 } else if (num_cds == 1) { 581 /* single CD found, check for sets on it */ 582 strcpy(cdrom_dev, cds[0].device_name); 583 if (cd_has_sets()) 584 return SET_OK; 585 } else { 586 for (i = 0; i< num_cds; i++) { 587 cd_menu[i].opt_name = cds[i].menu; 588 cd_menu[i].opt_menu = OPT_NOMENU; 589 cd_menu[i].opt_flags = OPT_EXIT; 590 cd_menu[i].opt_action = set_menu_select; 591 } 592 /* create a menu offering available choices */ 593 menu_cd = new_menu(MSG_Available_cds, 594 cd_menu, num_cds, -1, 4, 0, 0, 595 MC_SCROLL | MC_NOEXITOPT, 596 NULL, NULL, NULL, NULL, NULL); 597 if (menu_cd == -1) 598 return SET_RETRY; 599 msg_display(MSG_ask_cd); 600 process_menu(menu_cd, &selected_cd); 601 free_menu(menu_cd); 602 strcpy(cdrom_dev, cds[selected_cd].device_name); 603 if (cd_has_sets()) 604 return SET_OK; 605 } 606 607 if (silent) 608 msg_display(""); 609 else { 610 umount_mnt2(); 611 msg_display(MSG_cd_path_not_found); 612 process_menu(MENU_ok, NULL); 613 } 614 615 /* ask for paths on the CD */ 616 rv = -1; 617 process_menu(MENU_cdromsource, &rv); 618 if (rv == SET_RETRY) 619 return SET_RETRY; 620 621 if (cd_has_sets()) 622 return SET_OK; 623 624 return SET_RETRY; 625 } 626 627 628 /* 629 * Get from a pathname inside an unmounted local filesystem 630 * (e.g., where sets were preloaded onto a local DOS partition) 631 */ 632 int 633 get_via_localfs(void) 634 { 635 int rv = -1; 636 637 /* Get device, filesystem, and filepath */ 638 process_menu (MENU_localfssource, &rv); 639 if (rv == SET_RETRY) 640 return SET_RETRY; 641 642 /* Mount it */ 643 if (run_program(0, "/sbin/mount -rt %s /dev/%s /mnt2", 644 localfs_fs, localfs_dev)) 645 return SET_RETRY; 646 647 mnt2_mounted = 1; 648 649 snprintf(ext_dir_bin, sizeof ext_dir_bin, "%s/%s/%s", 650 "/mnt2", localfs_dir, set_dir_bin); 651 snprintf(ext_dir_src, sizeof ext_dir_src, "%s/%s/%s", 652 "/mnt2", localfs_dir, set_dir_src); 653 654 return SET_OK; 655 } 656 657 /* 658 * Get from an already-mounted pathname. 659 */ 660 661 int 662 get_via_localdir(void) 663 { 664 int rv = -1; 665 666 /* Get filepath */ 667 process_menu(MENU_localdirsource, &rv); 668 if (rv == SET_RETRY) 669 return SET_RETRY; 670 671 /* 672 * We have to have an absolute path ('cos pax runs in a 673 * different directory), make it so. 674 */ 675 snprintf(ext_dir_bin, sizeof ext_dir_bin, "/%s/%s", localfs_dir, set_dir_bin); 676 snprintf(ext_dir_src, sizeof ext_dir_src, "/%s/%s", localfs_dir, set_dir_src); 677 678 return SET_OK; 679 } 680 681 682 /* 683 * Support for custom distribution fetches / unpacks. 684 */ 685 686 unsigned int 687 set_X11_selected(void) 688 { 689 int i; 690 691 for (i = SET_X11_FIRST; ++i < SET_X11_LAST;) 692 if (set_status[i] & SET_SELECTED) 693 return 1; 694 return 0; 695 } 696 697 unsigned int 698 get_kernel_set(void) 699 { 700 int i; 701 702 for (i = SET_KERNEL_FIRST; ++i < SET_KERNEL_LAST;) 703 if (set_status[i] & SET_SELECTED) 704 return i; 705 return SET_NONE; 706 } 707 708 void 709 set_kernel_set(unsigned int kernel_set) 710 { 711 int i; 712 713 /* only one kernel set is allowed */ 714 for (i = SET_KERNEL_FIRST; ++i < SET_KERNEL_LAST;) 715 set_status[i] &= ~SET_SELECTED; 716 set_status[kernel_set] |= SET_SELECTED; 717 } 718 719 static int 720 set_toggle(menudesc *menu, void *arg) 721 { 722 distinfo **distp = arg; 723 int set = distp[menu->cursel]->set; 724 725 if (set > SET_KERNEL_FIRST && set < SET_KERNEL_LAST && 726 !(set_status[set] & SET_SELECTED)) 727 set_kernel_set(set); 728 else 729 set_status[set] ^= SET_SELECTED; 730 return 0; 731 } 732 733 static int 734 set_all_none(menudesc *menu, void *arg, int set, int clr) 735 { 736 distinfo **distp = arg; 737 distinfo *dist = *distp; 738 int nested; 739 740 for (nested = 0; dist->set != SET_GROUP_END || nested--; dist++) { 741 if (dist->set == SET_GROUP) { 742 nested++; 743 continue; 744 } 745 set_status[dist->set] = (set_status[dist->set] & ~clr) | set; 746 } 747 return 0; 748 } 749 750 static int 751 set_all(menudesc *menu, void *arg) 752 { 753 return set_all_none(menu, arg, SET_SELECTED, 0); 754 } 755 756 static int 757 set_none(menudesc *menu, void *arg) 758 { 759 return set_all_none(menu, arg, 0, SET_SELECTED); 760 } 761 762 static void 763 set_label(menudesc *menu, int opt, void *arg) 764 { 765 distinfo **distp = arg; 766 distinfo *dist = distp[opt]; 767 const char *selected; 768 const char *desc; 769 int nested; 770 771 desc = dist->desc; 772 773 if (dist->set != SET_GROUP) 774 selected = set_status[dist->set] & SET_SELECTED ? msg_yes : msg_no; 775 else { 776 /* sub menu - display None/Some/All */ 777 nested = 0; 778 selected = "unknown"; 779 while ((++dist)->set != SET_GROUP_END || nested--) { 780 if (dist->set == SET_GROUP) { 781 nested++; 782 continue; 783 } 784 if (!(set_status[dist->set] & SET_VALID)) 785 continue; 786 if (set_status[dist->set] & SET_SELECTED) { 787 if (selected == msg_none) { 788 selected = msg_some; 789 break; 790 } 791 selected = msg_all; 792 } else { 793 if (selected == msg_all) { 794 selected = msg_some; 795 break; 796 } 797 selected = msg_none; 798 } 799 } 800 } 801 802 wprintw(menu->mw, msg_cur_distsets_row, msg_string(desc), selected); 803 } 804 805 static int set_sublist(menudesc *menu, void *arg); 806 807 static int 808 initialise_set_menu(distinfo *dist, menu_ent *me, distinfo **de, int all_none) 809 { 810 int set; 811 int sets; 812 int nested; 813 814 for (sets = 0; ; dist++) { 815 set = dist->set; 816 if (set == SET_LAST || set == SET_GROUP_END) 817 break; 818 if (!(set_status[set] & SET_VALID)) 819 continue; 820 *de = dist; 821 me->opt_menu = OPT_NOMENU; 822 me->opt_flags = 0; 823 me->opt_name = NULL; 824 if (set != SET_GROUP) 825 me->opt_action = set_toggle; 826 else { 827 /* Collapse sublist */ 828 nested = 0; 829 while ((++dist)->set != SET_GROUP_END || nested--) { 830 if (dist->set == SET_GROUP) 831 nested++; 832 } 833 me->opt_action = set_sublist; 834 } 835 sets++; 836 de++; 837 me++; 838 } 839 840 if (all_none) { 841 me->opt_menu = OPT_NOMENU; 842 me->opt_flags = 0; 843 me->opt_name = MSG_select_all; 844 me->opt_action = set_all; 845 me++; 846 me->opt_menu = OPT_NOMENU; 847 me->opt_flags = 0; 848 me->opt_name = MSG_select_none; 849 me->opt_action = set_none; 850 sets += 2; 851 } 852 853 return sets; 854 } 855 856 static int 857 set_sublist(menudesc *menu, void *arg) 858 { 859 distinfo *de[SET_LAST]; 860 menu_ent me[SET_LAST]; 861 distinfo **dist = arg; 862 int menu_no; 863 int sets; 864 865 sets = initialise_set_menu(dist[menu->cursel] + 1, me, de, 1); 866 867 menu_no = new_menu(NULL, me, sets, 20, 10, 0, select_menu_width, 868 MC_SUBMENU | MC_SCROLL | MC_DFLTEXIT, 869 NULL, set_label, NULL, NULL, 870 MSG_install_selected_sets); 871 872 process_menu(menu_no, de); 873 free_menu(menu_no); 874 875 return 0; 876 } 877 878 void 879 customise_sets(void) 880 { 881 distinfo *de[SET_LAST]; 882 menu_ent me[SET_LAST]; 883 int sets; 884 int menu_no; 885 886 msg_display(MSG_cur_distsets); 887 msg_table_add(MSG_cur_distsets_header); 888 889 sets = initialise_set_menu(dist_list, me, de, 0); 890 891 menu_no = new_menu(NULL, me, sets, 0, 5, 0, select_menu_width, 892 MC_SCROLL | MC_NOBOX | MC_DFLTEXIT | MC_NOCLEAR, 893 NULL, set_label, NULL, NULL, 894 MSG_install_selected_sets); 895 896 process_menu(menu_no, de); 897 free_menu(menu_no); 898 } 899 900 /* 901 * Extract_file **REQUIRES** an absolute path in ext_dir. Any code 902 * that sets up xfer_dir for use by extract_file needs to put in the 903 * full path name to the directory. 904 */ 905 906 int 907 extract_file(distinfo *dist, int update) 908 { 909 char path[STRSIZE]; 910 char *owd; 911 int rval; 912 913 /* If we might need to tidy up, ensure directory exists */ 914 if (fetch_fn != NULL) 915 make_target_dir(xfer_dir); 916 917 (void)snprintf(path, sizeof path, "%s/%s%s", 918 ext_dir_for_set(dist->name), dist->name, set_postfix(dist->name)); 919 920 owd = getcwd(NULL, 0); 921 922 /* Do we need to fetch the file now? */ 923 if (fetch_fn != NULL) { 924 rval = fetch_fn(dist->name); 925 if (rval != SET_OK) 926 return rval; 927 } 928 929 /* check tarfile exists */ 930 if (!file_exists_p(path)) { 931 932 #ifdef SUPPORT_8_3_SOURCE_FILESYSTEM 933 /* 934 * Update path to use dist->name truncated to the first eight 935 * characters and check again 936 */ 937 (void)snprintf(path, sizeof path, "%s/%.8s%.4s", /* 4 as includes '.' */ 938 ext_dir_for_set(dist->name), dist->name, set_postfix(dist->name)); 939 if (!file_exists_p(path)) { 940 #endif /* SUPPORT_8_3_SOURCE_FILESYSTEM */ 941 942 tarstats.nnotfound++; 943 944 msg_display(MSG_notarfile, path); 945 process_menu(MENU_ok, NULL); 946 return SET_RETRY; 947 } 948 #ifdef SUPPORT_8_3_SOURCE_FILESYSTEM 949 } 950 #endif /* SUPPORT_8_3_SOURCE_FILESYSTEM */ 951 952 tarstats.nfound++; 953 /* cd to the target root. */ 954 if (update && (dist->set == SET_ETC || dist->set == SET_X11_ETC)) { 955 make_target_dir("/.sysinst"); 956 target_chdir_or_die("/.sysinst"); 957 } else if (dist->set == SET_PKGSRC) 958 target_chdir_or_die("/usr"); 959 else 960 target_chdir_or_die("/"); 961 962 /* 963 * /usr/X11R7/lib/X11/xkb/symbols/pc was a directory in 5.0 964 * but is a file in 5.1 and beyond, so on upgrades we need to 965 * delete it before extracting the xbase set. 966 */ 967 if (update && dist->set == SET_X11_BASE) 968 run_program(0, "rm -rf usr/X11R7/lib/X11/xkb/symbols/pc"); 969 970 /* now extract set files into "./". */ 971 rval = run_program(RUN_DISPLAY | RUN_PROGRESS, 972 "progress -zf %s tar --chroot -xhepf -", path); 973 974 chdir(owd); 975 free(owd); 976 977 /* Check rval for errors and give warning. */ 978 if (rval != 0) { 979 tarstats.nerror++; 980 msg_display(MSG_tarerror, path); 981 process_menu(MENU_ok, NULL); 982 return SET_RETRY; 983 } 984 985 if (fetch_fn != NULL && clean_xfer_dir) { 986 run_program(0, "rm %s", path); 987 /* Plausibly we should unlink an empty xfer_dir as well */ 988 } 989 990 set_status[dist->set] |= SET_INSTALLED; 991 tarstats.nsuccess++; 992 return SET_OK; 993 } 994 995 static void 996 skip_set(distinfo *dist, int skip_type) 997 { 998 int nested; 999 int set; 1000 1001 nested = 0; 1002 while ((++dist)->set != SET_GROUP_END || nested--) { 1003 set = dist->set; 1004 if (set == SET_GROUP) { 1005 nested++; 1006 continue; 1007 } 1008 if (set == SET_LAST) 1009 break; 1010 if (set_status[set] == (SET_SELECTED | SET_VALID)) 1011 set_status[set] |= SET_SKIPPED; 1012 tarstats.nskipped++; 1013 } 1014 } 1015 1016 /* 1017 * Get and unpack the distribution. 1018 * Show success_msg if installation completes. 1019 * Otherwise show failure_msg and wait for the user to ack it before continuing. 1020 * success_msg and failure_msg must both be 0-adic messages. 1021 */ 1022 int 1023 get_and_unpack_sets(int update, msg setupdone_msg, msg success_msg, msg failure_msg) 1024 { 1025 distinfo *dist; 1026 int status; 1027 int set; 1028 1029 /* Ensure mountpoint for distribution files exists in current root. */ 1030 (void)mkdir("/mnt2", S_IRWXU| S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); 1031 if (script) 1032 (void)fprintf(script, "mkdir -m 755 /mnt2\n"); 1033 1034 /* reset failure/success counters */ 1035 memset(&tarstats, 0, sizeof(tarstats)); 1036 1037 /* Find out which files to "get" if we get files. */ 1038 1039 /* Accurately count selected sets */ 1040 for (dist = dist_list; (set = dist->set) != SET_LAST; dist++) { 1041 if (dist->name == NULL) 1042 continue; 1043 if ((set_status[set] & (SET_VALID | SET_SELECTED)) 1044 == (SET_VALID | SET_SELECTED)) 1045 tarstats.nselected++; 1046 } 1047 1048 status = SET_RETRY; 1049 for (dist = dist_list; (set = dist->set) != SET_LAST; dist++) { 1050 if (dist->name == NULL) 1051 continue; 1052 if (set_status[set] != (SET_VALID | SET_SELECTED)) 1053 continue; 1054 1055 if (status != SET_OK) { 1056 /* This might force a redraw.... */ 1057 clearok(curscr, 1); 1058 touchwin(stdscr); 1059 wrefresh(stdscr); 1060 /* Sort out the location of the set files */ 1061 do { 1062 umount_mnt2(); 1063 msg_display(MSG_distmedium, tarstats.nselected, 1064 tarstats.nsuccess + tarstats.nskipped, 1065 dist->name); 1066 fetch_fn = NULL; 1067 process_menu(MENU_distmedium, &status); 1068 } while (status == SET_RETRY); 1069 1070 if (status == SET_SKIP) { 1071 set_status[set] |= SET_SKIPPED; 1072 tarstats.nskipped++; 1073 continue; 1074 } 1075 if (status == SET_SKIP_GROUP) { 1076 skip_set(dist, status); 1077 continue; 1078 } 1079 if (status != SET_OK) { 1080 msg_display(failure_msg); 1081 process_menu(MENU_ok, NULL); 1082 return 1; 1083 } 1084 } 1085 1086 /* Try to extract this set */ 1087 status = extract_file(dist, update); 1088 if (status == SET_RETRY) 1089 dist--; 1090 } 1091 1092 if (tarstats.nerror == 0 && tarstats.nsuccess == tarstats.nselected) { 1093 msg_display(MSG_endtarok); 1094 /* Give user a chance to see the success message */ 1095 sleep(1); 1096 } else { 1097 /* We encountered errors. Let the user know. */ 1098 msg_display(MSG_endtar, 1099 tarstats.nselected, tarstats.nnotfound, tarstats.nskipped, 1100 tarstats.nfound, tarstats.nsuccess, tarstats.nerror); 1101 process_menu(MENU_ok, NULL); 1102 msg_clear(); 1103 } 1104 1105 /* 1106 * postinstall needs to be run after extracting all sets, because 1107 * otherwise /var/db/obsolete will only have current information 1108 * from the base, comp, and etc sets. 1109 */ 1110 if (update && (set_status[SET_ETC] & SET_INSTALLED)) { 1111 int oldsendmail; 1112 oldsendmail = run_program(RUN_DISPLAY | RUN_CHROOT | 1113 RUN_ERROR_OK | RUN_PROGRESS, 1114 "/usr/sbin/postinstall -s /.sysinst -d / check mailerconf"); 1115 if (oldsendmail == 1) { 1116 msg_display(MSG_oldsendmail); 1117 if (ask_yesno(NULL)) { 1118 run_program(RUN_DISPLAY | RUN_CHROOT, 1119 "/usr/sbin/postinstall -s /.sysinst -d / fix mailerconf"); 1120 } 1121 } 1122 run_program(RUN_DISPLAY | RUN_CHROOT, 1123 "/usr/sbin/postinstall -s /.sysinst -d / fix"); 1124 1125 /* Don't discard the system's old entropy if any */ 1126 run_program(RUN_CHROOT | RUN_SILENT, 1127 "/etc/rc.d/random_seed start"); 1128 } 1129 1130 /* Configure the system */ 1131 if (set_status[SET_BASE] & SET_INSTALLED) 1132 run_makedev(); 1133 1134 if (!update) { 1135 struct stat sb1, sb2; 1136 1137 if (stat(target_expand("/"), &sb1) == 0 1138 && stat(target_expand("/var"), &sb2) == 0 1139 && sb1.st_dev != sb2.st_dev) { 1140 add_rc_conf("random_file=/etc/entropy-file\n"); 1141 if (target_file_exists_p("/boot.cfg")) { 1142 run_program(RUN_CHROOT|RUN_FATAL, 1143 "sh -c 'sed -e s./var/db/./etc/. " 1144 "< /boot.cfg " 1145 "> /tmp/boot.cfg.tmp'"); 1146 mv_within_target_or_die("/tmp/boot.cfg.tmp", 1147 "/boot.cfg"); 1148 } 1149 } 1150 1151 /* Save keyboard type */ 1152 save_kb_encoding(); 1153 1154 /* Other configuration. */ 1155 mnt_net_config(); 1156 } 1157 1158 /* Mounted dist dir? */ 1159 umount_mnt2(); 1160 1161 /* Save entropy -- on some systems it's ~all we'll ever get */ 1162 run_program(RUN_DISPLAY | RUN_CHROOT | RUN_FATAL | RUN_PROGRESS, 1163 "/etc/rc.d/random_seed stop"); 1164 /* Install/Upgrade complete ... reboot or exit to script */ 1165 msg_display(success_msg); 1166 process_menu(MENU_ok, NULL); 1167 return 0; 1168 } 1169 1170 void 1171 umount_mnt2(void) 1172 { 1173 if (!mnt2_mounted) 1174 return; 1175 run_program(RUN_SILENT, "/sbin/umount /mnt2"); 1176 mnt2_mounted = 0; 1177 } 1178 1179 1180 /* 1181 * Do a quick sanity check that the target can reboot. 1182 * return 1 if everything OK, 0 if there is a problem. 1183 * Uses a table of files we expect to find after a base install/upgrade. 1184 */ 1185 1186 /* test flag and pathname to check for after unpacking. */ 1187 struct check_table { unsigned int mode; const char *path;} checks[] = { 1188 { S_IFREG, "/netbsd" }, 1189 { S_IFDIR, "/etc" }, 1190 { S_IFREG, "/etc/fstab" }, 1191 { S_IFREG, "/sbin/init" }, 1192 { S_IFREG, "/bin/sh" }, 1193 { S_IFREG, "/etc/rc" }, 1194 { S_IFREG, "/etc/rc.subr" }, 1195 { S_IFREG, "/etc/rc.conf" }, 1196 { S_IFDIR, "/dev" }, 1197 { S_IFCHR, "/dev/console" }, 1198 /* XXX check for rootdev in target /dev? */ 1199 { S_IFREG, "/sbin/fsck" }, 1200 { S_IFREG, "/sbin/fsck_ffs" }, 1201 { S_IFREG, "/sbin/mount" }, 1202 { S_IFREG, "/sbin/mount_ffs" }, 1203 { S_IFREG, "/sbin/mount_nfs" }, 1204 #if defined(DEBUG) || defined(DEBUG_CHECK) 1205 { S_IFREG, "/foo/bar" }, /* bad entry to exercise warning */ 1206 #endif 1207 { 0, 0 } 1208 1209 }; 1210 1211 /* 1212 * Check target for a single file. 1213 */ 1214 static int 1215 check_for(unsigned int mode, const char *pathname) 1216 { 1217 int found; 1218 1219 found = (target_test(mode, pathname) == 0); 1220 if (found == 0) 1221 msg_display(MSG_rootmissing, pathname); 1222 return found; 1223 } 1224 1225 /* 1226 * Check that all the files in check_table are present in the 1227 * target root. Warn if not found. 1228 */ 1229 int 1230 sanity_check(void) 1231 { 1232 int target_ok = 1; 1233 struct check_table *p; 1234 1235 for (p = checks; p->path; p++) { 1236 target_ok = target_ok && check_for(p->mode, p->path); 1237 } 1238 if (target_ok) 1239 return 0; 1240 1241 /* Uh, oh. Something's missing. */ 1242 msg_display(MSG_badroot); 1243 process_menu(MENU_ok, NULL); 1244 return 1; 1245 } 1246 1247 /* 1248 * Some globals to pass things back from callbacks 1249 */ 1250 static char zoneinfo_dir[STRSIZE]; 1251 static int zonerootlen; 1252 static char *tz_selected; /* timezonename (relative to share/zoneinfo */ 1253 const char *tz_default; /* UTC, or whatever /etc/localtime points to */ 1254 static char tz_env[STRSIZE]; 1255 static int save_cursel, save_topline; 1256 1257 /* 1258 * Callback from timezone menu 1259 */ 1260 static int 1261 set_tz_select(menudesc *m, void *arg) 1262 { 1263 time_t t; 1264 char *new; 1265 struct tm *tm; 1266 1267 if (m && strcmp(tz_selected, m->opts[m->cursel].opt_name) != 0) { 1268 /* Change the displayed timezone */ 1269 new = strdup(m->opts[m->cursel].opt_name); 1270 if (new == NULL) 1271 return 0; 1272 free(tz_selected); 1273 tz_selected = new; 1274 snprintf(tz_env, sizeof tz_env, "%.*s%s", 1275 zonerootlen, zoneinfo_dir, tz_selected); 1276 setenv("TZ", tz_env, 1); 1277 } 1278 if (m) 1279 /* Warp curser to 'Exit' line on menu */ 1280 m->cursel = -1; 1281 1282 /* Update displayed time */ 1283 t = time(NULL); 1284 tm = localtime(&t); 1285 msg_display(MSG_choose_timezone, 1286 tz_default, tz_selected, safectime(&t), tm ? tm->tm_zone : 1287 "?"); 1288 return 0; 1289 } 1290 1291 static int 1292 set_tz_back(menudesc *m, void *arg) 1293 { 1294 1295 zoneinfo_dir[zonerootlen] = 0; 1296 m->cursel = save_cursel; 1297 m->topline = save_topline; 1298 return 0; 1299 } 1300 1301 static int 1302 set_tz_dir(menudesc *m, void *arg) 1303 { 1304 1305 strlcpy(zoneinfo_dir + zonerootlen, m->opts[m->cursel].opt_name, 1306 sizeof zoneinfo_dir - zonerootlen); 1307 save_cursel = m->cursel; 1308 save_topline = m->topline; 1309 m->cursel = 0; 1310 m->topline = 0; 1311 return 0; 1312 } 1313 1314 /* 1315 * Alarm-handler to update example-display 1316 */ 1317 static void 1318 /*ARGSUSED*/ 1319 timezone_sig(int sig) 1320 { 1321 1322 set_tz_select(NULL, NULL); 1323 alarm(60); 1324 } 1325 1326 static int 1327 tz_sort(const void *a, const void *b) 1328 { 1329 return strcmp(((const menu_ent *)a)->opt_name, ((const menu_ent *)b)->opt_name); 1330 } 1331 1332 static void 1333 tzm_set_names(menudesc *m, void *arg) 1334 { 1335 DIR *dir; 1336 struct dirent *dp; 1337 static int nfiles; 1338 static int maxfiles = 32; 1339 static menu_ent *tz_menu; 1340 static char **tz_names; 1341 void *p; 1342 int maxfname; 1343 char *fp; 1344 struct stat sb; 1345 1346 if (tz_menu == NULL) 1347 tz_menu = malloc(maxfiles * sizeof *tz_menu); 1348 if (tz_names == NULL) 1349 tz_names = malloc(maxfiles * sizeof *tz_names); 1350 if (tz_menu == NULL || tz_names == NULL) 1351 return; /* error - skip timezone setting */ 1352 while (nfiles > 0) 1353 free(tz_names[--nfiles]); 1354 1355 dir = opendir(zoneinfo_dir); 1356 fp = strchr(zoneinfo_dir, 0); 1357 if (fp != zoneinfo_dir + zonerootlen) { 1358 tz_names[0] = 0; 1359 tz_menu[0].opt_name = msg_string(MSG_tz_back); 1360 tz_menu[0].opt_menu = OPT_NOMENU; 1361 tz_menu[0].opt_flags = 0; 1362 tz_menu[0].opt_action = set_tz_back; 1363 nfiles = 1; 1364 } 1365 maxfname = zoneinfo_dir + sizeof zoneinfo_dir - fp - 1; 1366 if (dir != NULL) { 1367 while ((dp = readdir(dir)) != NULL) { 1368 if (dp->d_namlen > maxfname || dp->d_name[0] == '.') 1369 continue; 1370 strlcpy(fp, dp->d_name, maxfname); 1371 if (stat(zoneinfo_dir, &sb) == -1) 1372 continue; 1373 if (nfiles >= maxfiles) { 1374 p = realloc(tz_menu, 2 * maxfiles * sizeof *tz_menu); 1375 if (p == NULL) 1376 break; 1377 tz_menu = p; 1378 p = realloc(tz_names, 2 * maxfiles * sizeof *tz_names); 1379 if (p == NULL) 1380 break; 1381 tz_names = p; 1382 maxfiles *= 2; 1383 } 1384 if (S_ISREG(sb.st_mode)) 1385 tz_menu[nfiles].opt_action = set_tz_select; 1386 else if (S_ISDIR(sb.st_mode)) { 1387 tz_menu[nfiles].opt_action = set_tz_dir; 1388 strlcat(fp, "/", 1389 sizeof(zoneinfo_dir) - (fp - zoneinfo_dir)); 1390 } else 1391 continue; 1392 tz_names[nfiles] = strdup(zoneinfo_dir + zonerootlen); 1393 tz_menu[nfiles].opt_name = tz_names[nfiles]; 1394 tz_menu[nfiles].opt_menu = OPT_NOMENU; 1395 tz_menu[nfiles].opt_flags = 0; 1396 nfiles++; 1397 } 1398 closedir(dir); 1399 } 1400 *fp = 0; 1401 1402 m->opts = tz_menu; 1403 m->numopts = nfiles; 1404 qsort(tz_menu, nfiles, sizeof *tz_menu, tz_sort); 1405 } 1406 1407 void 1408 get_tz_default(void) 1409 { 1410 char localtime_link[STRSIZE]; 1411 static char localtime_target[STRSIZE]; 1412 int rc; 1413 1414 strlcpy(localtime_link, target_expand("/etc/localtime"), 1415 sizeof localtime_link); 1416 1417 /* Add sanity check that /mnt/usr/share/zoneinfo contains 1418 * something useful 1419 */ 1420 1421 rc = readlink(localtime_link, localtime_target, 1422 sizeof(localtime_target) - 1); 1423 if (rc < 0) { 1424 /* error, default to UTC */ 1425 tz_default = "UTC"; 1426 } else { 1427 localtime_target[rc] = '\0'; 1428 tz_default = strchr(strstr(localtime_target, "zoneinfo"), '/') + 1; 1429 } 1430 } 1431 1432 /* 1433 * Choose from the files in usr/share/zoneinfo and set etc/localtime 1434 */ 1435 int 1436 set_timezone(void) 1437 { 1438 char localtime_link[STRSIZE]; 1439 char localtime_target[STRSIZE]; 1440 time_t t; 1441 struct tm *tm; 1442 int menu_no; 1443 1444 strlcpy(zoneinfo_dir, target_expand("/usr/share/zoneinfo/"), 1445 sizeof zoneinfo_dir - 1); 1446 zonerootlen = strlen(zoneinfo_dir); 1447 1448 get_tz_default(); 1449 1450 tz_selected = strdup(tz_default); 1451 snprintf(tz_env, sizeof(tz_env), "%s%s", zoneinfo_dir, tz_selected); 1452 setenv("TZ", tz_env, 1); 1453 t = time(NULL); 1454 tm = localtime(&t); 1455 msg_display(MSG_choose_timezone, 1456 tz_default, tz_selected, safectime(&t), tm ? tm->tm_zone : 1457 "?"); 1458 1459 signal(SIGALRM, timezone_sig); 1460 alarm(60); 1461 1462 menu_no = new_menu(NULL, NULL, 14, 23, 9, 1463 12, 32, MC_ALWAYS_SCROLL | MC_NOSHORTCUT, 1464 tzm_set_names, NULL, NULL, 1465 "\nPlease consult the install documents.", NULL); 1466 if (menu_no < 0) 1467 goto done; /* error - skip timezone setting */ 1468 1469 process_menu(menu_no, NULL); 1470 1471 free_menu(menu_no); 1472 1473 signal(SIGALRM, SIG_IGN); 1474 1475 snprintf(localtime_target, sizeof(localtime_target), 1476 "/usr/share/zoneinfo/%s", tz_selected); 1477 strlcpy(localtime_link, target_expand("/etc/localtime"), 1478 sizeof localtime_link); 1479 unlink(localtime_link); 1480 symlink(localtime_target, localtime_link); 1481 1482 done: 1483 return 1; 1484 } 1485 1486 void 1487 scripting_vfprintf(FILE *f, const char *fmt, va_list ap) 1488 { 1489 1490 if (f) 1491 (void)vfprintf(f, fmt, ap); 1492 if (script) 1493 (void)vfprintf(script, fmt, ap); 1494 } 1495 1496 void 1497 scripting_fprintf(FILE *f, const char *fmt, ...) 1498 { 1499 va_list ap; 1500 1501 va_start(ap, fmt); 1502 scripting_vfprintf(f, fmt, ap); 1503 va_end(ap); 1504 } 1505 1506 void 1507 add_rc_conf(const char *fmt, ...) 1508 { 1509 FILE *f; 1510 va_list ap; 1511 1512 va_start(ap, fmt); 1513 f = target_fopen("/etc/rc.conf", "a"); 1514 if (f != 0) { 1515 scripting_fprintf(NULL, "cat <<EOF >>%s/etc/rc.conf\n", 1516 target_prefix()); 1517 scripting_vfprintf(f, fmt, ap); 1518 fclose(f); 1519 scripting_fprintf(NULL, "EOF\n"); 1520 } 1521 va_end(ap); 1522 } 1523 1524 int 1525 del_rc_conf(const char *value) 1526 { 1527 FILE *fp, *nfp; 1528 char buf[4096]; /* Ridiculously high, but should be enough in any way */ 1529 char *rcconf, *tempname = NULL, *bakname = NULL; 1530 char *cp; 1531 int done = 0; 1532 int fd; 1533 int retval = 0; 1534 1535 /* The paths might seem strange, but using /tmp would require copy instead 1536 * of rename operations. */ 1537 if (asprintf(&rcconf, "%s", target_expand("/etc/rc.conf")) < 0 1538 || asprintf(&tempname, "%s", target_expand("/etc/rc.conf.tmp.XXXXXX")) < 0 1539 || asprintf(&bakname, "%s", target_expand("/etc/rc.conf.bak.XXXXXX")) < 0) { 1540 if (rcconf) 1541 free(rcconf); 1542 if (tempname) 1543 free(tempname); 1544 msg_display(MSG_rcconf_delete_failed, value); 1545 process_menu(MENU_ok, NULL); 1546 return -1; 1547 } 1548 1549 if ((fd = mkstemp(bakname)) < 0) { 1550 msg_display(MSG_rcconf_delete_failed, value); 1551 process_menu(MENU_ok, NULL); 1552 return -1; 1553 } 1554 close(fd); 1555 1556 if (!(fp = fopen(rcconf, "r+")) || (fd = mkstemp(tempname)) < 0) { 1557 if (fp) 1558 fclose(fp); 1559 msg_display(MSG_rcconf_delete_failed, value); 1560 process_menu(MENU_ok, NULL); 1561 return -1; 1562 } 1563 1564 nfp = fdopen(fd, "w"); 1565 if (!nfp) { 1566 fclose(fp); 1567 close(fd); 1568 msg_display(MSG_rcconf_delete_failed, value); 1569 process_menu(MENU_ok, NULL); 1570 return -1; 1571 } 1572 1573 while (fgets(buf, sizeof buf, fp) != NULL) { 1574 1575 cp = buf + strspn(buf, " \t"); /* Skip initial spaces */ 1576 if (strncmp(cp, value, strlen(value)) == 0) { 1577 cp += strlen(value); 1578 if (*cp != '=') 1579 scripting_fprintf(nfp, "%s", buf); 1580 else 1581 done = 1; 1582 } else { 1583 scripting_fprintf(nfp, "%s", buf); 1584 } 1585 } 1586 fclose(fp); 1587 fclose(nfp); 1588 1589 if (done) { 1590 if (rename(rcconf, bakname)) { 1591 msg_display(MSG_rcconf_backup_failed); 1592 if (!ask_noyes(NULL)) { 1593 retval = -1; 1594 goto done; 1595 } 1596 } 1597 1598 if (rename(tempname, rcconf)) { 1599 if (rename(bakname, rcconf)) { 1600 msg_display(MSG_rcconf_restore_failed); 1601 process_menu(MENU_ok, NULL); 1602 } else { 1603 msg_display(MSG_rcconf_delete_failed, value); 1604 process_menu(MENU_ok, NULL); 1605 } 1606 } else { 1607 (void)unlink(bakname); 1608 } 1609 } 1610 1611 done: 1612 (void)unlink(tempname); 1613 free(rcconf); 1614 free(tempname); 1615 free(bakname); 1616 return retval; 1617 } 1618 1619 void 1620 add_sysctl_conf(const char *fmt, ...) 1621 { 1622 FILE *f; 1623 va_list ap; 1624 1625 va_start(ap, fmt); 1626 f = target_fopen("/etc/sysctl.conf", "a"); 1627 if (f != 0) { 1628 scripting_fprintf(NULL, "cat <<EOF >>%s/etc/sysctl.conf\n", 1629 target_prefix()); 1630 scripting_vfprintf(f, fmt, ap); 1631 fclose(f); 1632 scripting_fprintf(NULL, "EOF\n"); 1633 } 1634 va_end(ap); 1635 } 1636 1637 void 1638 enable_rc_conf(void) 1639 { 1640 1641 replace("/etc/rc.conf", "s/^rc_configured=NO/rc_configured=YES/"); 1642 } 1643 1644 int 1645 check_lfs_progs(void) 1646 { 1647 1648 #ifndef NO_LFS 1649 return binary_available("fsck_lfs") && binary_available("mount_lfs") 1650 && binary_available("newfs_lfs"); 1651 #else 1652 return 0; 1653 #endif 1654 } 1655 1656 int 1657 set_is_source(const char *set_name) { 1658 int len = strlen(set_name); 1659 return len >= 3 && memcmp(set_name + len - 3, "src", 3) == 0; 1660 } 1661 1662 const char * 1663 set_dir_for_set(const char *set_name) { 1664 if (strcmp(set_name, "pkgsrc") == 0) 1665 return pkgsrc_dir; 1666 return set_is_source(set_name) ? set_dir_src : set_dir_bin; 1667 } 1668 1669 const char * 1670 ext_dir_for_set(const char *set_name) { 1671 if (strcmp(set_name, "pkgsrc") == 0) 1672 return ext_dir_pkgsrc; 1673 return set_is_source(set_name) ? ext_dir_src : ext_dir_bin; 1674 } 1675 1676 void 1677 do_coloring(unsigned int fg, unsigned int bg) { 1678 if (bg > COLOR_WHITE) 1679 bg = COLOR_BLUE; 1680 if (fg > COLOR_WHITE) 1681 fg = COLOR_WHITE; 1682 if (fg != bg && has_colors()) { 1683 init_pair(1, fg, bg); 1684 wbkgd(stdscr, COLOR_PAIR(1)); 1685 wbkgd(mainwin, COLOR_PAIR(1)); 1686 wattrset(stdscr, COLOR_PAIR(1)); 1687 wattrset(mainwin, COLOR_PAIR(1)); 1688 } 1689 /* redraw screen */ 1690 touchwin(stdscr); 1691 touchwin(mainwin); 1692 wrefresh(stdscr); 1693 wrefresh(mainwin); 1694 return; 1695 } 1696 1697 int 1698 set_menu_select(menudesc *m, void *arg) 1699 { 1700 *(int *)arg = m->cursel; 1701 return 1; 1702 } 1703 1704 /* 1705 * check wether a binary is available somewhere in PATH, 1706 * return 1 if found, 0 if not. 1707 */ 1708 int 1709 binary_available(const char *prog) 1710 { 1711 char *p, tmp[MAXPATHLEN], *path = getenv("PATH"), *opath; 1712 1713 if (path == NULL) 1714 return access(prog, X_OK) == 0; 1715 path = strdup(path); 1716 if (path == NULL) 1717 return 0; 1718 1719 opath = path; 1720 1721 while ((p = strsep(&path, ":")) != NULL) { 1722 if (strlcpy(tmp, p, MAXPATHLEN) >= MAXPATHLEN) 1723 continue; 1724 if (strlcat(tmp, "/", MAXPATHLEN) >= MAXPATHLEN) 1725 continue; 1726 if (strlcat(tmp, prog, MAXPATHLEN) >= MAXPATHLEN) 1727 continue; 1728 if (access(tmp, X_OK) == 0) { 1729 free(opath); 1730 return 1; 1731 } 1732 } 1733 free(opath); 1734 return 0; 1735 } 1736 1737 const char * 1738 safectime(time_t *t) 1739 { 1740 const char *s = ctime(t); 1741 if (s != NULL) 1742 return s; 1743 1744 // Debugging. 1745 fprintf(stderr, "Can't convert to localtime 0x%jx (%s)\n", 1746 (intmax_t)*t, strerror(errno)); 1747 /*123456789012345678901234*/ 1748 return "preposterous clock time\n"; 1749 } 1750 1751 int 1752 ask_yesno(const char* msgtxt) 1753 { 1754 arg_rv p; 1755 1756 p.arg = __UNCONST(msgtxt); 1757 p.rv = -1; 1758 1759 process_menu(MENU_yesno, &p); 1760 return p.rv; 1761 } 1762 1763 int 1764 ask_noyes(const char *msgtxt) 1765 { 1766 arg_rv p; 1767 1768 p.arg = __UNCONST(msgtxt); 1769 p.rv = -1; 1770 1771 process_menu(MENU_noyes, &p); 1772 return p.rv; 1773 } 1774 1775 bool 1776 use_tgz_for_set(const char *set_name) 1777 { 1778 const struct distinfo *dist; 1779 1780 for (dist = dist_list; dist->set != SET_LAST; dist++) { 1781 if (dist->name == NULL) 1782 continue; 1783 if (strcmp(set_name, dist->name) == 0) 1784 return dist->force_tgz; 1785 } 1786 1787 return true; 1788 } 1789 1790 /* Return the postfix used for a given set */ 1791 const char * 1792 set_postfix(const char *set_name) 1793 { 1794 return use_tgz_for_set(set_name) ? dist_tgz_postfix : dist_postfix; 1795 } 1796 1797 /* 1798 * Replace positional arguments (encoded as $0 .. $N) in the string 1799 * passed by the contents of the passed argument array. 1800 * Caller must free() the result string. 1801 */ 1802 char* 1803 str_arg_subst(const char *src, size_t argc, const char **argv) 1804 { 1805 const char *p, *last; 1806 char *out, *t; 1807 size_t len; 1808 1809 len = strlen(src); 1810 for (p = strchr(src, '$'); p; p = strchr(p+1, '$')) { 1811 char *endp = NULL; 1812 size_t n; 1813 int e; 1814 1815 /* $ followed by a correct numeric position? */ 1816 n = strtou(p+1, &endp, 10, 0, INT_MAX, &e); 1817 if ((e == 0 || e == ENOTSUP) && n < argc) { 1818 len += strlen(argv[n]); 1819 len -= endp-p; 1820 p = endp-1; 1821 } 1822 } 1823 1824 out = malloc(len+1); 1825 if (out == NULL) 1826 return NULL; 1827 1828 t = out; 1829 for (last = src, p = strchr(src, '$'); p; p = strchr(p+1, '$')) { 1830 char *endp = NULL; 1831 size_t n; 1832 int e; 1833 1834 /* $ followed by a correct numeric position? */ 1835 n = strtou(p+1, &endp, 10, 0, INT_MAX, &e); 1836 if ((e == 0 || e == ENOTSUP) && n < argc) { 1837 size_t l = p-last; 1838 memcpy(t, last, l); 1839 t += l; 1840 strcpy(t, argv[n]); 1841 t += strlen(argv[n]); 1842 last = endp; 1843 } 1844 } 1845 if (*last) { 1846 strcpy(t, last); 1847 t += strlen(last); 1848 } else { 1849 *t = 0; 1850 } 1851 assert((size_t)(t-out) == len); 1852 1853 return out; 1854 } 1855 1856 /* 1857 * Replace positional arguments (encoded as $0 .. $N) in the 1858 * message by the strings passed as ... 1859 */ 1860 void 1861 msg_display_subst(const char *master, size_t argc, ...) 1862 { 1863 va_list ap; 1864 const char **args, **arg; 1865 char *out; 1866 1867 args = malloc(sizeof(const char *)*argc); 1868 if (args == NULL) 1869 return; 1870 1871 arg = args; 1872 va_start(ap, argc); 1873 for (size_t i = 0; i < argc; i++) 1874 *arg++ = va_arg(ap, const char*); 1875 va_end(ap); 1876 1877 out = str_arg_subst(msg_string(master), argc, args); 1878 if (out != NULL) { 1879 msg_display(out); 1880 free(out); 1881 } 1882 free(args); 1883 } 1884 1885