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