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