1/* $NetBSD: main.c,v 1.33 2024/02/21 20:31:57 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 55int debug; 56char machine[SSTRSIZE]; 57int ignorerror; 58int ttysig_ignore; 59pid_t ttysig_forward; 60uint sizemult; 61int partman_go; 62FILE *logfp; 63FILE *script; 64daddr_t root_limit; 65struct pm_head_t pm_head; 66struct pm_devs *pm; 67struct pm_devs *pm_new; 68char xfer_dir[STRSIZE]; 69int clean_xfer_dir; 70char ext_dir_bin[STRSIZE]; 71char ext_dir_src[STRSIZE]; 72char ext_dir_pkgsrc[STRSIZE]; 73char set_dir_bin[STRSIZE]; 74char set_dir_src[STRSIZE]; 75char pkg_dir[STRSIZE]; 76char pkgsrc_dir[STRSIZE]; 77const char *ushell; 78struct ftpinfo ftp, pkg, pkgsrc; 79int (*fetch_fn)(const char *); 80char nfs_host[STRSIZE]; 81char nfs_dir[STRSIZE]; 82char cdrom_dev[SSTRSIZE]; 83char fd_dev[SSTRSIZE]; 84const char *fd_type; 85char localfs_dev[SSTRSIZE]; 86char localfs_fs[SSTRSIZE]; 87char localfs_dir[STRSIZE]; 88char targetroot_mnt[SSTRSIZE]; 89int mnt2_mounted; 90char dist_postfix[SSTRSIZE]; 91char dist_tgz_postfix[SSTRSIZE]; 92WINDOW *mainwin; 93 94static void select_language(const char*); 95__dead static void usage(void); 96__dead static void miscsighandler(int); 97static void ttysighandler(int); 98static void cleanup(void); 99static void process_f_flag(char *); 100static bool no_openssl_trust_anchors_available(void); 101 102static int exit_cleanly = 0; /* Did we finish nicely? */ 103FILE *logfp; /* log file */ 104FILE *script; /* script file */ 105 106const char *multname; 107const char *err_outofmem; 108 109#ifdef DEBUG 110extern int log_flip(void); 111#endif 112 113/* Definion for colors */ 114 115struct { 116 unsigned int bg; 117 unsigned int fg; 118} clr_arg; 119 120/* String defaults and stuff for processing the -f file argument. */ 121 122struct f_arg { 123 const char *name; 124 const char *dflt; 125 char *var; 126 int size; 127}; 128 129static 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_HOST(XFER_FTP)], sizeof ftp.xfer_host[XFER_HOST(XFER_FTP)]}, 136 {"http host", SYSINST_HTTP_HOST, ftp.xfer_host[XFER_HOST(XFER_HTTP)], sizeof ftp.xfer_host[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_HOST(XFER_FTP)], sizeof pkg.xfer_host[XFER_HOST(XFER_FTP)]}, 154 {"pkg http host", SYSINST_PKG_HTTP_HOST, pkg.xfer_host[XFER_HOST(XFER_HTTP)], sizeof pkg.xfer_host[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_HOST(XFER_FTP)], sizeof pkgsrc.xfer_host[XFER_HOST(XFER_FTP)]}, 161 {"pkgsrc http host", SYSINST_PKGSRC_HTTP_HOST, pkgsrc.xfer_host[XFER_HOST(XFER_HTTP)], sizeof pkgsrc.xfer_host[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 171static void 172init(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 ftp.xfer = pkg.xfer = pkgsrc.xfer = XFER_HTTPS; 195 196 clr_arg.bg=COLOR_BLUE; 197 clr_arg.fg=COLOR_WHITE; 198} 199 200static void 201init_lang(void) 202{ 203 sizemult = 1; 204 err_outofmem = msg_string(MSG_out_of_memory); 205 multname = msg_string(MSG_secname); 206} 207 208int 209main(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 } 292 msg_window(mainwin); 293 294 /* Watch for signals and clean up */ 295 (void)atexit(cleanup); 296 (void)signal(SIGINT, ttysighandler); 297 (void)signal(SIGQUIT, ttysighandler); 298 (void)signal(SIGHUP, miscsighandler); 299 300 /* redraw screen */ 301 touchwin(stdscr); 302 refresh(); 303 304 /* Ensure we have mountpoint for target filesystems */ 305 mkdir(targetroot_mnt, S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH); 306 307 select_language(msg_cat_dir); 308 get_kb_encoding(); 309 init_lang(); 310 311 /* remove some invalid menu entries */ 312 if (!has_colors()) 313 remove_color_options(); 314 315 /* Menu processing */ 316 if (partman_go) 317 partman(NULL); 318 else 319 process_menu(MENU_netbsd, NULL); 320 321#ifndef NO_PARTMAN 322 /* clean up internal storage */ 323 pm_destroy_all(); 324#endif 325 326 partitions_cleanup(); 327 328 exit_cleanly = 1; 329 return 0; 330} 331 332static int 333set_language(menudesc *m, void *arg) 334{ 335 char **fnames = arg; 336 337 msg_file(fnames[m->cursel]); 338 return 1; 339} 340 341/* 342 * Search for sysinstmsg.* files in the given dir, collect 343 * their names and return the number of files found. 344 * fnames[0] is preallocated and duplicates are ignored. 345 */ 346struct found_msgs { 347 char **lang_msg, **fnames; 348 int max_lang, num_lang; 349 350}; 351static void 352find_language_files(const char *path, struct found_msgs *res) 353{ 354 DIR *dir; 355 struct dirent *dirent; 356 char fname[PATH_MAX]; 357 const char *cp; 358 359 res->num_lang = 0; 360 dir = opendir(path); 361 if (!dir) 362 return; 363 364 while ((dirent = readdir(dir)) != 0) { 365 if (memcmp(dirent->d_name, "sysinstmsgs.", 12)) 366 continue; 367 368 if (res->num_lang == 0) 369 res->num_lang = 1; 370 strcpy(fname, path); 371 strcat(fname, "/"); 372 strcat(fname, dirent->d_name); 373 if (msg_file(fname)) 374 continue; 375 cp = msg_string(MSG_sysinst_message_language); 376 if (!strcmp(cp, res->lang_msg[0])) 377 continue; 378 if (res->num_lang == res->max_lang) { 379 char **new; 380 res->max_lang *= 2; 381 new = realloc(res->lang_msg, 382 res->max_lang * sizeof *res->lang_msg); 383 if (!new) 384 break; 385 res->lang_msg = new; 386 new = realloc(res->fnames, 387 res->max_lang * sizeof *res->fnames); 388 if (!new) 389 break; 390 res->fnames = new; 391 } 392 res->fnames[res->num_lang] = strdup(fname); 393 res->lang_msg[res->num_lang++] = strdup(cp); 394 } 395 396 closedir(dir); 397} 398 399static void 400select_language(const char *msg_cat_path) 401{ 402 struct found_msgs found; 403 menu_ent *opt = 0; 404 const char *cp; 405 int lang_menu = -1; 406 int lang; 407 408 found.max_lang = 16; 409 found.num_lang = 0; 410 found.lang_msg = malloc(found.max_lang * sizeof *found.lang_msg); 411 found.fnames = malloc(found.max_lang * sizeof *found.fnames); 412 if (!found.lang_msg || !found.fnames) 413 goto done; 414 found.lang_msg[0] = strdup(msg_string(MSG_sysinst_message_language)); 415 found.fnames[0] = NULL; 416 417 if (msg_cat_path != NULL) 418 find_language_files(msg_cat_path, &found); 419 if (found.num_lang == 0) 420 find_language_files(".", &found); 421#ifdef CATALOG_DIR 422 if (found.num_lang == 0) 423 find_language_files(CATALOG_DIR, &found); 424#endif 425 426 msg_file(0); 427 428 if (found.num_lang <= 1) 429 goto done; 430 431 opt = calloc(found.num_lang, sizeof *opt); 432 if (!opt) 433 goto done; 434 435 for (lang = 0; lang < found.num_lang; lang++) { 436 opt[lang].opt_name = found.lang_msg[lang]; 437 opt[lang].opt_action = set_language; 438 } 439 440 lang_menu = new_menu(NULL, opt, found.num_lang, -1, 12, 0, 0, 441 MC_NOEXITOPT, NULL, NULL, NULL, NULL, NULL); 442 443 if (lang_menu != -1) { 444 msg_display(MSG_hello); 445 process_menu(lang_menu, found.fnames); 446 } 447 448 done: 449 if (lang_menu != -1) 450 free_menu(lang_menu); 451 free(opt); 452 for (int i = 0; i < found.num_lang; i++) { 453 free(found.lang_msg[i]); 454 free(found.fnames[i]); 455 } 456 free(found.lang_msg); 457 free(found.fnames); 458 459 /* set locale according to selected language */ 460 cp = msg_string(MSG_sysinst_message_locale); 461 if (cp) { 462 setlocale(LC_CTYPE, cp); 463 setenv("LC_CTYPE", cp, 1); 464 } 465} 466 467#ifndef md_may_remove_boot_medium 468#define md_may_remove_boot_medium() (boot_media_still_needed()<=0) 469#endif 470 471/* toplevel menu handler ... */ 472void 473toplevel(void) 474{ 475 /* 476 * Undo any stateful side-effects of previous menu choices. 477 * XXX must be idempotent, since we get run each time the main 478 * menu is displayed. 479 */ 480 char *home = getenv("HOME"); 481 if (home != NULL) 482 if (chdir(home) != 0) 483 (void)chdir("/"); 484 unwind_mounts(); 485 clear_swap(); 486 487 /* Display banner message in (english, francais, deutsch..) */ 488 msg_display(MSG_hello); 489 msg_display_add(MSG_md_hello); 490 if (md_may_remove_boot_medium()) 491 msg_display_add(MSG_md_may_remove_boot_medium); 492 msg_display_add(MSG_thanks); 493} 494 495 496/* The usage ... */ 497 498static void 499usage(void) 500{ 501 502 (void)fprintf(stderr, "usage: sysinst [-C bg:fg] [-D" 503#ifndef NO_PARTMAN 504 "p" 505#endif 506 "] [-f definition_file] " 507 "[-m message_catalog_dir]" 508 "\n" 509 "where:\n" 510 "\t-C bg:fg\n\t\tuse different color scheme\n" 511 "\t-D\n\t\trun in debug mode\n" 512 "\t-f definition_file\n\t\toverride built-in defaults from file\n" 513 "\t-m msg_catalog_dir\n\t\tuse translation files from msg_catalog_dir\n" 514#ifndef NO_PARTMAN 515 "\t-p\n\t\tonly run the partition editor, no installation\n" 516#endif 517 ); 518 519 exit(1); 520} 521 522/* ARGSUSED */ 523static void 524miscsighandler(int signo) 525{ 526 527 /* 528 * we need to cleanup(), but it was already scheduled with atexit(), 529 * so it'll be invoked on exit(). 530 */ 531 exit(1); 532} 533 534static void 535ttysighandler(int signo) 536{ 537 538 /* 539 * if we want to ignore a TTY signal (SIGINT or SIGQUIT), then we 540 * just return. If we want to forward a TTY signal, we forward it 541 * to the specified process group. 542 * 543 * This functionality is used when setting up and displaying child 544 * output so that the child gets the signal and presumably dies, 545 * but sysinst continues. We use this rather than actually ignoring 546 * the signals, because that will be passed on to a child 547 * through fork/exec, whereas special handlers get reset on exec.. 548 */ 549 if (ttysig_ignore) 550 return; 551 if (ttysig_forward) { 552 killpg(ttysig_forward, signo); 553 return; 554 } 555 556 /* 557 * we need to cleanup(), but it was already scheduled with atexit(), 558 * so it'll be invoked on exit(). 559 */ 560 exit(1); 561} 562 563static void 564cleanup(void) 565{ 566 time_t tloc; 567 568 (void)time(&tloc); 569 570#if 0 571 restore_etc(); 572#endif 573 /* Ensure we aren't inside the target tree */ 574 chdir(getenv("HOME")); 575 unwind_mounts(); 576 umount_mnt2(); 577 clear_swap(); 578 579 endwin(); 580 581 if (logfp) { 582 fprintf(logfp, "Log ended at: %s\n", safectime(&tloc)); 583 fflush(logfp); 584 fclose(logfp); 585 logfp = NULL; 586 } 587 if (script) { 588 fprintf(script, "# Script ended at: %s\n", safectime(&tloc)); 589 fflush(script); 590 fclose(script); 591 script = NULL; 592 } 593 594 if (!exit_cleanly) 595 fprintf(stderr, "\n\nsysinst terminated.\n"); 596} 597 598 599/* process function ... */ 600 601void 602process_f_flag(char *f_name) 603{ 604 char buffer[STRSIZE]; 605 int len; 606 const struct f_arg *arg; 607 FILE *fp; 608 char *cp, *cp1, *err; 609 610 /* open the file */ 611 fp = fopen(f_name, "r"); 612 if (fp == NULL) { 613 const char *args[] = { f_name }; 614 err = str_arg_subst(msg_string(MSG_config_open_error), 615 __arraycount(args), args); 616 fprintf(stderr, "%s\n", err); 617 free(err); 618 exit(1); 619 } 620 621 while (fgets(buffer, sizeof buffer, fp) != NULL) { 622 cp = buffer + strspn(buffer, " \t"); 623 if (strchr("#\r\n", *cp) != NULL) 624 continue; 625 for (arg = fflagopts; arg->name != NULL; arg++) { 626 len = strlen(arg->name); 627 if (memcmp(cp, arg->name, len) != 0) 628 continue; 629 if (arg->var == NULL || arg->size == 0) 630 continue; 631 cp1 = cp + len; 632 cp1 += strspn(cp1, " \t"); 633 if (*cp1++ != '=') 634 continue; 635 cp1 += strspn(cp1, " \t"); 636 len = strcspn(cp1, " \n\r\t"); 637 cp1[len] = 0; 638 strlcpy(arg->var, cp1, arg->size); 639 break; 640 } 641 } 642 643 fclose(fp); 644} 645 646/* 647 * return true if we do not have any root certificates installed, 648 * so can not verify any trust chain. 649 * We rely on /etc/openssl being the OPENSSLDIR and test the 650 * "all in one" /etc/openssl/cert.pem first, if that is not found 651 * check if there are multiple regular files or symlinks in 652 * /etc/openssl/certs/. 653 */ 654static bool 655no_openssl_trust_anchors_available(void) 656{ 657 struct stat sb; 658 DIR *dir; 659 struct dirent *ent; 660 size_t cnt; 661 662 /* check the omnibus single file variant first */ 663 if (stat("/etc/openssl/cert.pem", &sb) == 0 && 664 S_ISREG(sb.st_mode) && sb.st_size > 0) 665 return false; /* exists and is a non-empty file */ 666 667 /* look for files/symlinks in the certs subdirectory */ 668 dir = opendir("/etc/openssl/certs"); 669 if (dir == NULL) 670 return true; 671 for (cnt = 0; cnt < 2; ) { 672 ent = readdir(dir); 673 if (ent == NULL) 674 break; 675 switch (ent->d_type) { 676 case DT_REG: 677 case DT_LNK: 678 cnt++; 679 break; 680 default: 681 break; 682 } 683 } 684 closedir(dir); 685 686 return cnt < 2; 687} 688