1 /* $NetBSD: main.c,v 1.31 2023/06/09 18:44:16 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 /* main sysinst program. */ 36 37 #include <sys/types.h> 38 #include <sys/stat.h> 39 #include <sys/syslimits.h> 40 #include <sys/uio.h> 41 #include <stdio.h> 42 #include <signal.h> 43 #include <curses.h> 44 #include <unistd.h> 45 #include <fcntl.h> 46 #include <dirent.h> 47 #include <locale.h> 48 49 #include "defs.h" 50 #include "md.h" 51 #include "msg_defs.h" 52 #include "menu_defs.h" 53 #include "txtwalk.h" 54 55 int debug; 56 char machine[SSTRSIZE]; 57 int ignorerror; 58 int ttysig_ignore; 59 pid_t ttysig_forward; 60 uint sizemult; 61 int partman_go; 62 FILE *logfp; 63 FILE *script; 64 daddr_t root_limit; 65 struct pm_head_t pm_head; 66 struct pm_devs *pm; 67 struct pm_devs *pm_new; 68 char xfer_dir[STRSIZE]; 69 int clean_xfer_dir; 70 char ext_dir_bin[STRSIZE]; 71 char ext_dir_src[STRSIZE]; 72 char ext_dir_pkgsrc[STRSIZE]; 73 char set_dir_bin[STRSIZE]; 74 char set_dir_src[STRSIZE]; 75 char pkg_dir[STRSIZE]; 76 char pkgsrc_dir[STRSIZE]; 77 const char *ushell; 78 struct ftpinfo ftp, pkg, pkgsrc; 79 int (*fetch_fn)(const char *); 80 char nfs_host[STRSIZE]; 81 char nfs_dir[STRSIZE]; 82 char cdrom_dev[SSTRSIZE]; 83 char fd_dev[SSTRSIZE]; 84 const char *fd_type; 85 char localfs_dev[SSTRSIZE]; 86 char localfs_fs[SSTRSIZE]; 87 char localfs_dir[STRSIZE]; 88 char targetroot_mnt[SSTRSIZE]; 89 int mnt2_mounted; 90 char dist_postfix[SSTRSIZE]; 91 char dist_tgz_postfix[SSTRSIZE]; 92 WINDOW *mainwin; 93 94 static void select_language(const char*); 95 __dead static void usage(void); 96 __dead static void miscsighandler(int); 97 static void ttysighandler(int); 98 static void cleanup(void); 99 static void process_f_flag(char *); 100 static bool no_openssl_trust_anchors_available(void); 101 102 static int exit_cleanly = 0; /* Did we finish nicely? */ 103 FILE *logfp; /* log file */ 104 FILE *script; /* script file */ 105 106 const char *multname; 107 const char *err_outofmem; 108 109 #ifdef DEBUG 110 extern int log_flip(void); 111 #endif 112 113 /* Definion for colors */ 114 115 struct { 116 unsigned int bg; 117 unsigned int fg; 118 } clr_arg; 119 120 /* String defaults and stuff for processing the -f file argument. */ 121 122 struct f_arg { 123 const char *name; 124 const char *dflt; 125 char *var; 126 int size; 127 }; 128 129 static const struct f_arg fflagopts[] = { 130 {"release", REL, NULL, 0}, 131 {"machine", MACH, machine, sizeof machine}, 132 {"xfer dir", "/usr/INSTALL", xfer_dir, sizeof xfer_dir}, 133 {"ext dir", "", ext_dir_bin, sizeof ext_dir_bin}, 134 {"ext src dir", "", ext_dir_src, sizeof ext_dir_src}, 135 {"ftp host", SYSINST_FTP_HOST, ftp.xfer_host[XFER_FTP], sizeof ftp.xfer_host[XFER_FTP]}, 136 {"http host", SYSINST_HTTP_HOST, ftp.xfer_host[XFER_HTTP], sizeof ftp.xfer_host[XFER_HTTP]}, 137 {"ftp dir", SYSINST_FTP_DIR, ftp.dir, sizeof ftp.dir}, 138 {"ftp prefix", "/" ARCH_SUBDIR "/binary/sets", set_dir_bin, sizeof set_dir_bin}, 139 {"ftp src prefix", "/source/sets", set_dir_src, sizeof set_dir_src}, 140 {"ftp user", "ftp", ftp.user, sizeof ftp.user}, 141 {"ftp pass", "", ftp.pass, sizeof ftp.pass}, 142 {"ftp proxy", "", ftp.proxy, sizeof ftp.proxy}, 143 {"nfs host", "", nfs_host, sizeof nfs_host}, 144 {"nfs dir", "/bsd/release", nfs_dir, sizeof nfs_dir}, 145 {"cd dev", 0, cdrom_dev, sizeof cdrom_dev}, /* default filled in init */ 146 {"fd dev", "/dev/fd0a", fd_dev, sizeof fd_dev}, 147 {"local dev", "", localfs_dev, sizeof localfs_dev}, 148 {"local fs", "ffs", localfs_fs, sizeof localfs_fs}, 149 {"local dir", "release", localfs_dir, sizeof localfs_dir}, 150 {"targetroot mount", "/targetroot", targetroot_mnt, sizeof targetroot_mnt}, 151 {"dist postfix", "." SETS_TAR_SUFF, dist_postfix, sizeof dist_postfix}, 152 {"dist tgz postfix", ".tgz", dist_tgz_postfix, sizeof dist_tgz_postfix}, 153 {"pkg host", SYSINST_PKG_HOST, pkg.xfer_host[XFER_FTP], sizeof pkg.xfer_host[XFER_FTP]}, 154 {"pkg http host", SYSINST_PKG_HTTP_HOST, pkg.xfer_host[XFER_HTTP], sizeof pkg.xfer_host[XFER_HTTP]}, 155 {"pkg dir", SYSINST_PKG_DIR, pkg.dir, sizeof pkg.dir}, 156 {"pkg prefix", "/" PKG_ARCH_SUBDIR "/" PKG_SUBDIR "/All", pkg_dir, sizeof pkg_dir}, 157 {"pkg user", "ftp", pkg.user, sizeof pkg.user}, 158 {"pkg pass", "", pkg.pass, sizeof pkg.pass}, 159 {"pkg proxy", "", pkg.proxy, sizeof pkg.proxy}, 160 {"pkgsrc host", SYSINST_PKGSRC_HOST, pkgsrc.xfer_host[XFER_FTP], sizeof pkgsrc.xfer_host[XFER_FTP]}, 161 {"pkgsrc http host", SYSINST_PKGSRC_HTTP_HOST, pkgsrc.xfer_host[XFER_HTTP], sizeof pkgsrc.xfer_host[XFER_HTTP]}, 162 {"pkgsrc dir", "", pkgsrc.dir, sizeof pkgsrc.dir}, 163 {"pkgsrc prefix", "pub/pkgsrc/stable", pkgsrc_dir, sizeof pkgsrc_dir}, 164 {"pkgsrc user", "ftp", pkgsrc.user, sizeof pkgsrc.user}, 165 {"pkgsrc pass", "", pkgsrc.pass, sizeof pkgsrc.pass}, 166 {"pkgsrc proxy", "", pkgsrc.proxy, sizeof pkgsrc.proxy}, 167 168 {NULL, NULL, NULL, 0} 169 }; 170 171 static void 172 init(void) 173 { 174 const struct f_arg *arg; 175 176 sizemult = 1; 177 clean_xfer_dir = 0; 178 mnt2_mounted = 0; 179 fd_type = "msdos"; 180 181 pm_head = (struct pm_head_t) SLIST_HEAD_INITIALIZER(pm_head); 182 SLIST_INIT(&pm_head); 183 pm_new = malloc(sizeof (struct pm_devs)); 184 memset(pm_new, 0, sizeof *pm_new); 185 186 for (arg = fflagopts; arg->name != NULL; arg++) { 187 if (arg->var == NULL) 188 continue; 189 if (arg->var == cdrom_dev) 190 get_default_cdrom(arg->var, arg->size); 191 else 192 strlcpy(arg->var, arg->dflt, arg->size); 193 } 194 pkg.xfer = pkgsrc.xfer = XFER_HTTP; 195 196 clr_arg.bg=COLOR_BLUE; 197 clr_arg.fg=COLOR_WHITE; 198 } 199 200 static void 201 init_lang(void) 202 { 203 sizemult = 1; 204 err_outofmem = msg_string(MSG_out_of_memory); 205 multname = msg_string(MSG_secname); 206 } 207 208 int 209 main(int argc, char **argv) 210 { 211 int ch; 212 const char *msg_cat_dir = NULL; 213 214 init(); 215 216 #ifdef DEBUG 217 log_flip(); 218 #endif 219 220 /* Check for TERM ... */ 221 if (!getenv("TERM")) { 222 (void)fprintf(stderr, 223 "sysinst: environment variable TERM not set.\n"); 224 exit(4); 225 } 226 227 /* argv processing */ 228 while ((ch = getopt(argc, argv, "Dr:f:C:m:" 229 #ifndef NO_PARTMAN 230 "p" 231 #endif 232 )) != -1) 233 switch(ch) { 234 case 'D': /* set to get past certain errors in testing */ 235 debug = 1; 236 break; 237 case 'r': 238 /* Release name - ignore for compatibility with older versions */ 239 break; 240 case 'f': 241 /* Definition file to read. */ 242 process_f_flag(optarg); 243 break; 244 case 'C': 245 /* Define colors */ 246 sscanf(optarg, "%u:%u", &clr_arg.bg, &clr_arg.fg); 247 break; 248 case 'm': 249 /* set message catalog directory */ 250 msg_cat_dir = optarg; 251 break; 252 #ifndef NO_PARTMAN 253 case 'p': 254 /* Partition tool */ 255 partman_go = 1; 256 break; 257 #endif 258 case '?': 259 default: 260 usage(); 261 } 262 263 md_init(); 264 265 /* Initialize the partitioning subsystem */ 266 partitions_init(); 267 268 /* do we need to tell ftp(1) to avoid checking certificate chains? */ 269 if (no_openssl_trust_anchors_available()) 270 setenv("FTPSSLNOVERIFY", "1", 1); 271 272 /* initialize message window */ 273 if (menu_init()) { 274 __menu_initerror(); 275 exit(4); 276 } 277 278 /* 279 * Put 'messages' in a window that has a one-character border 280 * on the real screen. 281 */ 282 mainwin = newwin(getmaxy(stdscr) - 2, getmaxx(stdscr) - 2, 1, 1); 283 if (mainwin == NULL) { 284 (void)fprintf(stderr, 285 "sysinst: screen too small\n"); 286 exit(1); 287 } 288 if (has_colors()) { 289 start_color(); 290 do_coloring(clr_arg.fg,clr_arg.bg); 291 } else { 292 remove_color_options(); 293 } 294 msg_window(mainwin); 295 296 /* Watch for signals and clean up */ 297 (void)atexit(cleanup); 298 (void)signal(SIGINT, ttysighandler); 299 (void)signal(SIGQUIT, ttysighandler); 300 (void)signal(SIGHUP, miscsighandler); 301 302 /* redraw screen */ 303 touchwin(stdscr); 304 refresh(); 305 306 /* Ensure we have mountpoint for target filesystems */ 307 mkdir(targetroot_mnt, S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); 308 309 select_language(msg_cat_dir); 310 get_kb_encoding(); 311 init_lang(); 312 313 /* Menu processing */ 314 if (partman_go) 315 partman(NULL); 316 else 317 process_menu(MENU_netbsd, NULL); 318 319 #ifndef NO_PARTMAN 320 /* clean up internal storage */ 321 pm_destroy_all(); 322 #endif 323 324 partitions_cleanup(); 325 326 exit_cleanly = 1; 327 return 0; 328 } 329 330 static int 331 set_language(menudesc *m, void *arg) 332 { 333 char **fnames = arg; 334 335 msg_file(fnames[m->cursel]); 336 return 1; 337 } 338 339 /* 340 * Search for sysinstmsg.* files in the given dir, collect 341 * their names and return the number of files found. 342 * fnames[0] is preallocated and duplicates are ignored. 343 */ 344 struct found_msgs { 345 char **lang_msg, **fnames; 346 int max_lang, num_lang; 347 348 }; 349 static void 350 find_language_files(const char *path, struct found_msgs *res) 351 { 352 DIR *dir; 353 struct dirent *dirent; 354 char fname[PATH_MAX]; 355 const char *cp; 356 357 res->num_lang = 0; 358 dir = opendir(path); 359 if (!dir) 360 return; 361 362 while ((dirent = readdir(dir)) != 0) { 363 if (memcmp(dirent->d_name, "sysinstmsgs.", 12)) 364 continue; 365 366 if (res->num_lang == 0) 367 res->num_lang = 1; 368 strcpy(fname, path); 369 strcat(fname, "/"); 370 strcat(fname, dirent->d_name); 371 if (msg_file(fname)) 372 continue; 373 cp = msg_string(MSG_sysinst_message_language); 374 if (!strcmp(cp, res->lang_msg[0])) 375 continue; 376 if (res->num_lang == res->max_lang) { 377 char **new; 378 res->max_lang *= 2; 379 new = realloc(res->lang_msg, 380 res->max_lang * sizeof *res->lang_msg); 381 if (!new) 382 break; 383 res->lang_msg = new; 384 new = realloc(res->fnames, 385 res->max_lang * sizeof *res->fnames); 386 if (!new) 387 break; 388 res->fnames = new; 389 } 390 res->fnames[res->num_lang] = strdup(fname); 391 res->lang_msg[res->num_lang++] = strdup(cp); 392 } 393 394 closedir(dir); 395 } 396 397 static void 398 select_language(const char *msg_cat_path) 399 { 400 struct found_msgs found; 401 menu_ent *opt = 0; 402 const char *cp; 403 int lang_menu = -1; 404 int lang; 405 406 found.max_lang = 16; 407 found.num_lang = 0; 408 found.lang_msg = malloc(found.max_lang * sizeof *found.lang_msg); 409 found.fnames = malloc(found.max_lang * sizeof *found.fnames); 410 if (!found.lang_msg || !found.fnames) 411 goto done; 412 found.lang_msg[0] = strdup(msg_string(MSG_sysinst_message_language)); 413 found.fnames[0] = NULL; 414 415 if (msg_cat_path != NULL) 416 find_language_files(msg_cat_path, &found); 417 if (found.num_lang == 0) 418 find_language_files(".", &found); 419 #ifdef CATALOG_DIR 420 if (found.num_lang == 0) 421 find_language_files(CATALOG_DIR, &found); 422 #endif 423 424 msg_file(0); 425 426 if (found.num_lang <= 1) 427 goto done; 428 429 opt = calloc(found.num_lang, sizeof *opt); 430 if (!opt) 431 goto done; 432 433 for (lang = 0; lang < found.num_lang; lang++) { 434 opt[lang].opt_name = found.lang_msg[lang]; 435 opt[lang].opt_action = set_language; 436 } 437 438 lang_menu = new_menu(NULL, opt, found.num_lang, -1, 12, 0, 0, 439 MC_NOEXITOPT, NULL, NULL, NULL, NULL, NULL); 440 441 if (lang_menu != -1) { 442 msg_display(MSG_hello); 443 process_menu(lang_menu, found.fnames); 444 } 445 446 done: 447 if (lang_menu != -1) 448 free_menu(lang_menu); 449 free(opt); 450 for (int i = 0; i < found.num_lang; i++) { 451 free(found.lang_msg[i]); 452 free(found.fnames[i]); 453 } 454 free(found.lang_msg); 455 free(found.fnames); 456 457 /* set locale according to selected language */ 458 cp = msg_string(MSG_sysinst_message_locale); 459 if (cp) { 460 setlocale(LC_CTYPE, cp); 461 setenv("LC_CTYPE", cp, 1); 462 } 463 } 464 465 #ifndef md_may_remove_boot_medium 466 #define md_may_remove_boot_medium() (boot_media_still_needed()<=0) 467 #endif 468 469 /* toplevel menu handler ... */ 470 void 471 toplevel(void) 472 { 473 /* 474 * Undo any stateful side-effects of previous menu choices. 475 * XXX must be idempotent, since we get run each time the main 476 * menu is displayed. 477 */ 478 char *home = getenv("HOME"); 479 if (home != NULL) 480 if (chdir(home) != 0) 481 (void)chdir("/"); 482 unwind_mounts(); 483 clear_swap(); 484 485 /* Display banner message in (english, francais, deutsch..) */ 486 msg_display(MSG_hello); 487 msg_display_add(MSG_md_hello); 488 if (md_may_remove_boot_medium()) 489 msg_display_add(MSG_md_may_remove_boot_medium); 490 msg_display_add(MSG_thanks); 491 } 492 493 494 /* The usage ... */ 495 496 static void 497 usage(void) 498 { 499 500 (void)fprintf(stderr, "usage: sysinst [-C bg:fg] [-D" 501 #ifndef NO_PARTMAN 502 "p" 503 #endif 504 "] [-f definition_file] " 505 "[-m message_catalog_dir]" 506 "\n" 507 "where:\n" 508 "\t-C bg:fg\n\t\tuse different color scheme\n" 509 "\t-D\n\t\trun in debug mode\n" 510 "\t-f definition_file\n\t\toverride built-in defaults from file\n" 511 "\t-m msg_catalog_dir\n\t\tuse translation files from msg_catalog_dir\n" 512 #ifndef NO_PARTMAN 513 "\t-p\n\t\tonly run the partition editor, no installation\n" 514 #endif 515 ); 516 517 exit(1); 518 } 519 520 /* ARGSUSED */ 521 static void 522 miscsighandler(int signo) 523 { 524 525 /* 526 * we need to cleanup(), but it was already scheduled with atexit(), 527 * so it'll be invoked on exit(). 528 */ 529 exit(1); 530 } 531 532 static void 533 ttysighandler(int signo) 534 { 535 536 /* 537 * if we want to ignore a TTY signal (SIGINT or SIGQUIT), then we 538 * just return. If we want to forward a TTY signal, we forward it 539 * to the specified process group. 540 * 541 * This functionality is used when setting up and displaying child 542 * output so that the child gets the signal and presumably dies, 543 * but sysinst continues. We use this rather than actually ignoring 544 * the signals, because that will be passed on to a child 545 * through fork/exec, whereas special handlers get reset on exec.. 546 */ 547 if (ttysig_ignore) 548 return; 549 if (ttysig_forward) { 550 killpg(ttysig_forward, signo); 551 return; 552 } 553 554 /* 555 * we need to cleanup(), but it was already scheduled with atexit(), 556 * so it'll be invoked on exit(). 557 */ 558 exit(1); 559 } 560 561 static void 562 cleanup(void) 563 { 564 time_t tloc; 565 566 (void)time(&tloc); 567 568 #if 0 569 restore_etc(); 570 #endif 571 /* Ensure we aren't inside the target tree */ 572 chdir(getenv("HOME")); 573 unwind_mounts(); 574 umount_mnt2(); 575 clear_swap(); 576 577 endwin(); 578 579 if (logfp) { 580 fprintf(logfp, "Log ended at: %s\n", safectime(&tloc)); 581 fflush(logfp); 582 fclose(logfp); 583 logfp = NULL; 584 } 585 if (script) { 586 fprintf(script, "# Script ended at: %s\n", safectime(&tloc)); 587 fflush(script); 588 fclose(script); 589 script = NULL; 590 } 591 592 if (!exit_cleanly) 593 fprintf(stderr, "\n\nsysinst terminated.\n"); 594 } 595 596 597 /* process function ... */ 598 599 void 600 process_f_flag(char *f_name) 601 { 602 char buffer[STRSIZE]; 603 int len; 604 const struct f_arg *arg; 605 FILE *fp; 606 char *cp, *cp1, *err; 607 608 /* open the file */ 609 fp = fopen(f_name, "r"); 610 if (fp == NULL) { 611 const char *args[] = { f_name }; 612 err = str_arg_subst(msg_string(MSG_config_open_error), 613 __arraycount(args), args); 614 fprintf(stderr, "%s\n", err); 615 free(err); 616 exit(1); 617 } 618 619 while (fgets(buffer, sizeof buffer, fp) != NULL) { 620 cp = buffer + strspn(buffer, " \t"); 621 if (strchr("#\r\n", *cp) != NULL) 622 continue; 623 for (arg = fflagopts; arg->name != NULL; arg++) { 624 len = strlen(arg->name); 625 if (memcmp(cp, arg->name, len) != 0) 626 continue; 627 if (arg->var == NULL || arg->size == 0) 628 continue; 629 cp1 = cp + len; 630 cp1 += strspn(cp1, " \t"); 631 if (*cp1++ != '=') 632 continue; 633 cp1 += strspn(cp1, " \t"); 634 len = strcspn(cp1, " \n\r\t"); 635 cp1[len] = 0; 636 strlcpy(arg->var, cp1, arg->size); 637 break; 638 } 639 } 640 641 fclose(fp); 642 } 643 644 /* 645 * return true if we do not have any root certificates installed, 646 * so can not verify any trust chain. 647 * We rely on /etc/openssl being the OPENSSLDIR and test the 648 * "all in one" /etc/openssl/cert.pem first, if that is not found 649 * check if there are multiple regular files or symlinks in 650 * /etc/openssl/certs/. 651 */ 652 static bool 653 no_openssl_trust_anchors_available(void) 654 { 655 struct stat sb; 656 DIR *dir; 657 struct dirent *ent; 658 size_t cnt; 659 660 /* check the omnibus single file variant first */ 661 if (stat("/etc/openssl/cert.pem", &sb) == 0 && 662 S_ISREG(sb.st_mode) && sb.st_size > 0) 663 return false; /* exists and is a non-empty file */ 664 665 /* look for files/symlinks in the certs subdirectory */ 666 dir = opendir("/etc/openssl/certs"); 667 if (dir == NULL) 668 return true; 669 for (cnt = 0; cnt < 2; ) { 670 ent = readdir(dir); 671 if (ent == NULL) 672 break; 673 switch (ent->d_type) { 674 case DT_REG: 675 case DT_LNK: 676 cnt++; 677 break; 678 default: 679 break; 680 } 681 } 682 closedir(dir); 683 684 return cnt < 2; 685 } 686