system.c revision 83820
1146773Ssam/* 2146773Ssam * The new sysinstall program. 3146773Ssam * 4146773Ssam * This is probably the last program in the `sysinstall' line - the next 5146773Ssam * generation being essentially a complete rewrite. 6146773Ssam * 7146773Ssam * $FreeBSD: head/usr.sbin/sade/system.c 83820 2001-09-22 18:10:56Z murray $ 8146773Ssam * 9146773Ssam * Jordan Hubbard 10146773Ssam * 11146773Ssam * My contributions are in the public domain. 12146773Ssam * 13146773Ssam * Parts of this file are also blatently stolen from Poul-Henning Kamp's 14146773Ssam * previous version of sysinstall, and as such fall under his "BEERWARE license" 15146773Ssam * so buy him a beer if you like it! Buy him a beer for me, too! 16146773Ssam * Heck, get him completely drunk and send me pictures! :-) 17146773Ssam */ 18146773Ssam 19147899Ssam#include "sysinstall.h" 20146773Ssam#include <signal.h> 21146773Ssam#include <termios.h> 22146773Ssam#include <sys/reboot.h> 23146773Ssam#include <sys/consio.h> 24146773Ssam#include <sys/fcntl.h> 25146773Ssam#include <sys/ioctl.h> 26146773Ssam#include <sys/stat.h> 27146773Ssam#include <sys/types.h> 28146773Ssam#include <sys/sysctl.h> 29146773Ssam 30146773Ssam 31146773Ssam/* Where we stick our temporary expanded doc file */ 32146773Ssam#define DOC_TMP_DIR "/tmp/.doc" 33146773Ssam#define DOC_TMP_FILE "/tmp/.doc/doc.tmp" 34146773Ssam 35146773Ssamstatic pid_t ehs_pid; 36146773Ssam 37146773Ssam/* 38146773Ssam * Handle interrupt signals - this probably won't work in all cases 39146773Ssam * due to our having bogotified the internal state of dialog or curses, 40146773Ssam * but we'll give it a try. 41146773Ssam */ 42146773Ssamstatic int 43146773Ssamintr_continue(dialogMenuItem *self) 44146773Ssam{ 45146773Ssam return DITEM_LEAVE_MENU; 46146773Ssam} 47146773Ssam 48146773Ssamstatic int 49146773Ssamintr_reboot(dialogMenuItem *self) 50146773Ssam{ 51146773Ssam systemShutdown(-1); 52146773Ssam /* NOTREACHED */ 53146773Ssam return 0; 54146773Ssam} 55146773Ssam 56146773Ssamstatic int 57146773Ssamintr_restart(dialogMenuItem *self) 58146773Ssam{ 59146773Ssam int ret; 60146773Ssam free_variables(); 61146773Ssam ret = execl(StartName, StartName, (char *)NULL); 62146773Ssam msgDebug("execl failed (%s)\n", strerror(errno)); 63146773Ssam /* NOTREACHED */ 64146773Ssam return -1; 65146773Ssam} 66146773Ssam 67146773Ssamstatic dialogMenuItem intrmenu[] = { 68146773Ssam { "Abort", "Abort the installation", NULL, intr_reboot }, 69146773Ssam { "Restart", "Restart the installation program", NULL, intr_restart }, 70146773Ssam { "Continue", "Continue the installation", NULL, intr_continue }, 71146773Ssam}; 72146773Ssam 73146773Ssam 74146773Ssamstatic void 75146773Ssamhandle_intr(int sig) 76146773Ssam{ 77146773Ssam WINDOW *save = savescr(); 78146773Ssam 79146773Ssam use_helpline(NULL); 80146773Ssam use_helpfile(NULL); 81146773Ssam if (OnVTY) { 82146773Ssam ioctl(0, VT_ACTIVATE, 1); /* Switch back */ 83146773Ssam msgInfo(NULL); 84146773Ssam } 85146773Ssam (void)dialog_menu("Installation interrupt", 86146773Ssam "Do you want to abort the installation?", 87146773Ssam -1, -1, 3, -3, intrmenu, NULL, NULL, NULL); 88146773Ssam restorescr(save); 89146773Ssam} 90146773Ssam 91146773Ssam/* 92146773Ssam * Harvest children if we are init. 93146773Ssam */ 94146773Ssamstatic void 95146773Ssamreap_children(int sig) 96146773Ssam{ 97146773Ssam int errbak = errno; 98146773Ssam 99146773Ssam while (waitpid(-1, NULL, WNOHANG) > 0) 100146773Ssam ; 101146773Ssam errno = errbak; 102146773Ssam} 103146773Ssam 104146773Ssam/* Expand a file into a convenient location, nuking it each time */ 105146773Ssamstatic char * 106146773Ssamexpand(char *fname) 107146773Ssam{ 108146773Ssam char *gunzip = RunningAsInit ? "/stand/gunzip" : "/usr/bin/gunzip"; 109146773Ssam 110146773Ssam if (!directory_exists(DOC_TMP_DIR)) { 111146773Ssam Mkdir(DOC_TMP_DIR); 112146773Ssam if (chown(DOC_TMP_DIR, 0, 0) < 0) 113146773Ssam return NULL; 114146773Ssam if (chmod(DOC_TMP_DIR, S_IRWXU) < 0) 115146773Ssam return NULL; 116146773Ssam } 117146773Ssam else 118146773Ssam unlink(DOC_TMP_FILE); 119146773Ssam if (!file_readable(fname) || vsystem("%s < %s > %s", gunzip, fname, DOC_TMP_FILE)) 120146773Ssam return NULL; 121146773Ssam return DOC_TMP_FILE; 122146773Ssam} 123146773Ssam 124146773Ssam/* Initialize system defaults */ 125146773Ssamvoid 126146773SsamsystemInitialize(int argc, char **argv) 127146773Ssam{ 128146773Ssam int i, boothowto; 129146773Ssam sigset_t signalset; 130146773Ssam 131146773Ssam signal(SIGINT, SIG_IGN); 132146773Ssam globalsInit(); 133146773Ssam 134146773Ssam i = sizeof(boothowto); 135146773Ssam if (!sysctlbyname("debug.boothowto", &boothowto, &i, NULL, NULL) && 136146773Ssam (i == sizeof(boothowto)) && (boothowto & RB_VERBOSE)) 137146773Ssam variable_set2(VAR_DEBUG, "YES", 0); 138146773Ssam 139146773Ssam /* Are we running as init? */ 140146773Ssam if (getpid() == 1) { 141146773Ssam int fd, type; 142146773Ssam 143146773Ssam RunningAsInit = 1; 144146773Ssam setsid(); 145146773Ssam close(0); 146146773Ssam fd = open("/dev/ttyv0", O_RDWR); 147146773Ssam if (fd == -1) { 148146773Ssam fd = open("/dev/console", O_RDWR); /* fallback */ 149146773Ssam variable_set2(VAR_FIXIT_TTY, "serial", 0); /* give fixit a hint */ 150146773Ssam } else 151146773Ssam OnVTY = TRUE; 152146773Ssam /* 153146773Ssam * To make _sure_ we're on a VTY and don't have /dev/console switched 154146773Ssam * away to a serial port or something, attempt to set the cursor appearance. 155146773Ssam */ 156146773Ssam type = 0; /* normal */ 157146773Ssam if (OnVTY) { 158146773Ssam int fd2; 159146773Ssam 160146773Ssam if ((fd2 = open("/dev/console", O_RDWR)) != -1) { 161146773Ssam if (ioctl(fd2, CONS_CURSORTYPE, &type) == -1) { 162146773Ssam OnVTY = FALSE; 163146773Ssam variable_set2(VAR_FIXIT_TTY, "serial", 0); /* Tell Fixit 164146773Ssam the console 165146773Ssam type */ 166146773Ssam close(fd); close(fd2); 167146773Ssam open("/dev/console", O_RDWR); 168146773Ssam } 169146773Ssam else 170146773Ssam close(fd2); 171146773Ssam } 172146773Ssam } 173146773Ssam close(1); dup(0); 174146773Ssam close(2); dup(0); 175146773Ssam printf("%s running as init on %s\n", argv[0], OnVTY ? "vty0" : "serial console"); 176146773Ssam ioctl(0, TIOCSCTTY, (char *)NULL); 177146773Ssam setlogin("root"); 178146773Ssam setenv("PATH", "/stand:/bin:/sbin:/usr/sbin:/usr/bin:/mnt/bin:/mnt/sbin:/mnt/usr/sbin:/mnt/usr/bin:/usr/X11R6/bin", 1); 179146773Ssam setbuf(stdin, 0); 180146773Ssam setbuf(stderr, 0); 181146773Ssam#ifdef __alpha__ 182146773Ssam i = 0; 183146773Ssam sysctlbyname("machdep.unaligned_print", NULL, 0, &i, sizeof(i)); 184146773Ssam#endif 185146773Ssam signal(SIGCHLD, reap_children); 186146773Ssam } 187146773Ssam else { 188146773Ssam char hname[256]; 189146773Ssam 190146773Ssam /* Initalize various things for a multi-user environment */ 191146773Ssam if (!gethostname(hname, sizeof hname)) 192146773Ssam variable_set2(VAR_HOSTNAME, hname, 0); 193146773Ssam } 194146773Ssam 195146773Ssam if (set_termcap() == -1) { 196146773Ssam printf("Can't find terminal entry\n"); 197146773Ssam exit(-1); 198146773Ssam } 199146773Ssam 200146773Ssam /* XXX - libdialog has particularly bad return value checking */ 201146773Ssam init_dialog(); 202146773Ssam 203146773Ssam /* If we haven't crashed I guess dialog is running ! */ 204146773Ssam DialogActive = TRUE; 205146773Ssam 206146773Ssam /* Make sure HOME is set for those utilities that need it */ 207146773Ssam if (!getenv("HOME")) 208146773Ssam setenv("HOME", "/", 1); 209146773Ssam signal(SIGINT, handle_intr); 210146773Ssam /* 211146773Ssam * Make sure we can be interrupted even if we were re-executed 212146773Ssam * from an interrupt. 213146773Ssam */ 214146773Ssam sigemptyset(&signalset); 215146773Ssam sigaddset(&signalset, SIGINT); 216146773Ssam sigprocmask(SIG_UNBLOCK, &signalset, NULL); 217146773Ssam 218146773Ssam (void)vsystem("rm -rf %s", DOC_TMP_DIR); 219147899Ssam} 220146773Ssam 221146773Ssam/* Close down and prepare to exit */ 222146773Ssamvoid 223146773SsamsystemShutdown(int status) 224146773Ssam{ 225146773Ssam /* If some media is open, close it down */ 226146773Ssam if (status >=0) 227146773Ssam mediaClose(); 228146773Ssam 229146773Ssam /* write out any changes to rc.conf .. */ 230146773Ssam configRC_conf(); 231146773Ssam 232146773Ssam /* Shut down the dialog library */ 233146773Ssam if (DialogActive) { 234146773Ssam end_dialog(); 235146773Ssam DialogActive = FALSE; 236146773Ssam } 237146773Ssam 238146773Ssam /* Shut down curses */ 239146773Ssam endwin(); 240146773Ssam 241146773Ssam /* If we have a temporary doc dir lying around, nuke it */ 242146773Ssam (void)vsystem("rm -rf %s", DOC_TMP_DIR); 243146773Ssam 244146773Ssam /* REALLY exit! */ 245146773Ssam if (RunningAsInit) { 246146773Ssam /* Put the console back */ 247146773Ssam ioctl(0, VT_ACTIVATE, 2); 248146773Ssam#ifdef __alpha__ 249146773Ssam reboot(RB_HALT); 250146773Ssam#else 251146773Ssam reboot(0); 252146773Ssam#endif 253146773Ssam } 254146773Ssam else 255146773Ssam exit(status); 256146773Ssam} 257146773Ssam 258146773Ssam/* Run some general command */ 259146773Ssamint 260146773SsamsystemExecute(char *command) 261146773Ssam{ 262146773Ssam int status; 263146773Ssam struct termios foo; 264146773Ssam WINDOW *w = savescr(); 265146773Ssam 266146773Ssam dialog_clear(); 267146773Ssam dialog_update(); 268146773Ssam end_dialog(); 269146773Ssam DialogActive = FALSE; 270146773Ssam if (tcgetattr(0, &foo) != -1) { 271146773Ssam foo.c_cc[VERASE] = '\010'; 272146773Ssam tcsetattr(0, TCSANOW, &foo); 273146773Ssam } 274147899Ssam if (!Fake) 275146773Ssam status = system(command); 276146773Ssam else { 277146773Ssam status = 0; 278146773Ssam msgDebug("systemExecute: Faked execution of `%s'\n", command); 279146773Ssam } 280146773Ssam DialogActive = TRUE; 281147899Ssam restorescr(w); 282147899Ssam return status; 283146773Ssam} 284146773Ssam 285146773Ssam/* suspend/resume libdialog/curses screen */ 286146773Ssamstatic WINDOW *oldW; 287146773Ssam 288146773Ssamvoid 289146773SsamsystemSuspendDialog(void) 290146773Ssam{ 291146773Ssam 292146773Ssam oldW = savescr(); 293146773Ssam dialog_clear(); 294146773Ssam dialog_update(); 295146773Ssam end_dialog(); 296146773Ssam DialogActive = FALSE; 297146773Ssam} 298147899Ssam 299146773Ssamvoid 300146773SsamsystemResumeDialog(void) 301146773Ssam{ 302146773Ssam 303146773Ssam DialogActive = TRUE; 304146773Ssam restorescr(oldW); 305146773Ssam} 306146773Ssam 307146773Ssam/* Display a help file in a filebox */ 308146773Ssamint 309146773SsamsystemDisplayHelp(char *file) 310146773Ssam{ 311146773Ssam char *fname = NULL; 312146773Ssam char buf[FILENAME_MAX]; 313146773Ssam int ret = 0; 314146773Ssam WINDOW *w = savescr(); 315146773Ssam 316146773Ssam fname = systemHelpFile(file, buf); 317146773Ssam if (!fname) { 318146773Ssam snprintf(buf, FILENAME_MAX, "The %s file is not provided on this particular floppy image.", file); 319146773Ssam use_helpfile(NULL); 320146773Ssam use_helpline(NULL); 321146773Ssam dialog_mesgbox("Sorry!", buf, -1, -1); 322146773Ssam ret = 1; 323146773Ssam } 324146773Ssam else { 325146773Ssam use_helpfile(NULL); 326146773Ssam use_helpline(NULL); 327146773Ssam dialog_textbox(file, fname, LINES, COLS); 328147899Ssam } 329146773Ssam restorescr(w); 330146773Ssam return ret; 331146773Ssam} 332146773Ssam 333146773Ssamchar * 334146773SsamsystemHelpFile(char *file, char *buf) 335146773Ssam{ 336146773Ssam if (!file) 337146773Ssam return NULL; 338146773Ssam if (file[0] == '/') 339146773Ssam return file; 340146773Ssam snprintf(buf, FILENAME_MAX, "/stand/help/%s.hlp.gz", file); 341146773Ssam if (file_readable(buf)) 342147899Ssam return expand(buf); 343146773Ssam snprintf(buf, FILENAME_MAX, "/stand/help/%s.TXT.gz", file); 344146773Ssam if (file_readable(buf)) 345146773Ssam return expand(buf); 346146773Ssam snprintf(buf, FILENAME_MAX, "/usr/src/usr.sbin/sysinstall/help/%s.hlp", file); 347146773Ssam if (file_readable(buf)) 348146773Ssam return buf; 349146773Ssam snprintf(buf, FILENAME_MAX, "/usr/src/usr.sbin/sysinstall/help/%s.TXT", file); 350146773Ssam if (file_readable(buf)) 351146773Ssam return buf; 352146773Ssam return NULL; 353146773Ssam} 354146773Ssam 355146773Ssamvoid 356146773SsamsystemChangeTerminal(char *color, const u_char c_term[], 357147899Ssam char *mono, const u_char m_term[]) 358146773Ssam{ 359146773Ssam if (OnVTY) { 360146773Ssam int setupterm(char *color, int, int *); 361146773Ssam 362146773Ssam if (ColorDisplay) { 363146773Ssam setenv("TERM", color, 1); 364146773Ssam setenv("TERMCAP", c_term, 1); 365146773Ssam reset_shell_mode(); 366146773Ssam setterm(color); 367146773Ssam cbreak(); noecho(); 368146773Ssam } 369146773Ssam else { 370146773Ssam setenv("TERM", mono, 1); 371147899Ssam setenv("TERMCAP", m_term, 1); 372146773Ssam reset_shell_mode(); 373146773Ssam setterm(mono); 374146773Ssam cbreak(); noecho(); 375146773Ssam } 376146773Ssam } 377146773Ssam clear(); 378146773Ssam refresh(); 379146773Ssam dialog_clear(); 380146773Ssam} 381146773Ssam 382146773Ssamint 383146773Ssamvsystem(char *fmt, ...) 384146773Ssam{ 385146773Ssam va_list args; 386146773Ssam int pstat; 387146773Ssam pid_t pid; 388146773Ssam int omask; 389146773Ssam sig_t intsave, quitsave; 390146773Ssam char *cmd; 391146773Ssam int i; 392146773Ssam 393146773Ssam cmd = (char *)alloca(FILENAME_MAX); 394146773Ssam cmd[0] = '\0'; 395146773Ssam va_start(args, fmt); 396146773Ssam vsnprintf(cmd, FILENAME_MAX, fmt, args); 397146773Ssam va_end(args); 398146773Ssam 399146773Ssam omask = sigblock(sigmask(SIGCHLD)); 400146773Ssam if (Fake) { 401146773Ssam msgDebug("vsystem: Faked execution of `%s'\n", cmd); 402146773Ssam return 0; 403146773Ssam } 404146773Ssam if (isDebug()) 405146773Ssam msgDebug("Executing command `%s'\n", cmd); 406146773Ssam pid = fork(); 407146773Ssam if (pid == -1) { 408146773Ssam (void)sigsetmask(omask); 409146773Ssam i = 127; 410146773Ssam } 411146773Ssam else if (!pid) { /* Junior */ 412146773Ssam (void)sigsetmask(omask); 413146773Ssam if (DebugFD != -1) { 414146773Ssam dup2(DebugFD, 0); 415146773Ssam dup2(DebugFD, 1); 416146773Ssam dup2(DebugFD, 2); 417146773Ssam } 418146773Ssam else { 419146773Ssam close(1); open("/dev/null", O_WRONLY); 420146773Ssam dup2(1, 2); 421146773Ssam } 422146773Ssam if (!RunningAsInit) 423146773Ssam execl("/bin/sh", "/bin/sh", "-c", cmd, (char *)NULL); 424146773Ssam else 425146773Ssam execl("/stand/sh", "/stand/sh", "-c", cmd, (char *)NULL); 426146773Ssam exit(1); 427146773Ssam } 428146773Ssam else { 429146773Ssam intsave = signal(SIGINT, SIG_IGN); 430146773Ssam quitsave = signal(SIGQUIT, SIG_IGN); 431146773Ssam pid = waitpid(pid, &pstat, 0); 432146773Ssam (void)sigsetmask(omask); 433146773Ssam (void)signal(SIGINT, intsave); 434146773Ssam (void)signal(SIGQUIT, quitsave); 435146773Ssam i = (pid == -1) ? -1 : WEXITSTATUS(pstat); 436146773Ssam if (isDebug()) 437146773Ssam msgDebug("Command `%s' returns status of %d\n", cmd, i); 438146773Ssam } 439146773Ssam return i; 440146773Ssam} 441146773Ssam 442146773Ssamvoid 443146773SsamsystemCreateHoloshell(void) 444146773Ssam{ 445146773Ssam int waitstatus; 446146773Ssam 447146773Ssam if ((FixItMode || OnVTY) && RunningAsInit) { 448146773Ssam 449146773Ssam if (ehs_pid != 0) { 450146773Ssam int pstat; 451146773Ssam 452146773Ssam if (kill(ehs_pid, 0) == 0) { 453146773Ssam 454146773Ssam if (msgNoYes("There seems to be an emergency holographic shell\n" 455146773Ssam "already running on VTY 4.\n\n" 456146773Ssam "Kill it and start a new one?")) 457146773Ssam return; 458146773Ssam 459146773Ssam /* try cleaning up as much as possible */ 460146773Ssam (void) kill(ehs_pid, SIGHUP); 461146773Ssam sleep(1); 462146773Ssam (void) kill(ehs_pid, SIGKILL); 463146773Ssam } 464146773Ssam 465146773Ssam /* avoid too many zombies */ 466146773Ssam (void) waitpid(ehs_pid, &pstat, WNOHANG); 467146773Ssam } 468146773Ssam 469146773Ssam if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0) 470146773Ssam systemSuspendDialog(); /* must be before the fork() */ 471146773Ssam if ((ehs_pid = fork()) == 0) { 472146773Ssam int i, fd; 473146773Ssam struct termios foo; 474146773Ssam extern int login_tty(int); 475146773Ssam 476146773Ssam ioctl(0, TIOCNOTTY, NULL); 477146773Ssam for (i = getdtablesize(); i >= 0; --i) 478146773Ssam close(i); 479146773Ssam if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0) 480146773Ssam fd = open("/dev/console", O_RDWR); 481 else 482 fd = open("/dev/ttyv3", O_RDWR); 483 ioctl(0, TIOCSCTTY, &fd); 484 dup2(0, 1); 485 dup2(0, 2); 486 DebugFD = 2; 487 if (login_tty(fd) == -1) 488 msgDebug("Doctor: I can't set the controlling terminal.\n"); 489 signal(SIGTTOU, SIG_IGN); 490 if (tcgetattr(fd, &foo) != -1) { 491 foo.c_cc[VERASE] = '\010'; 492 if (tcsetattr(fd, TCSANOW, &foo) == -1) 493 msgDebug("Doctor: I'm unable to set the erase character.\n"); 494 } 495 else 496 msgDebug("Doctor: I'm unable to get the terminal attributes!\n"); 497 if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0) { 498 printf("Type ``exit'' in this fixit shell to resume sysinstall.\n\n"); 499 fflush(stdout); 500 } 501 execlp("sh", "-sh", 0); 502 msgDebug("Was unable to execute sh for Holographic shell!\n"); 503 exit(1); 504 } 505 else { 506 if (strcmp(variable_get(VAR_FIXIT_TTY), "standard") == 0) { 507 WINDOW *w = savescr(); 508 509 msgNotify("Starting an emergency holographic shell on VTY4"); 510 sleep(2); 511 restorescr(w); 512 } 513 else { 514 (void)waitpid(ehs_pid, &waitstatus, 0); /* we only wait for 515 shell to finish 516 it serial mode 517 since there is no 518 virtual console */ 519 systemResumeDialog(); 520 } 521 } 522 } 523} 524