system.c revision 83440
1/*
2 * The new sysinstall program.
3 *
4 * This is probably the last program in the `sysinstall' line - the next
5 * generation being essentially a complete rewrite.
6 *
7 * $FreeBSD: head/usr.sbin/sade/system.c 83440 2001-09-14 08:39:22Z murray $
8 *
9 * Jordan Hubbard
10 *
11 * My contributions are in the public domain.
12 *
13 * Parts of this file are also blatently stolen from Poul-Henning Kamp's
14 * previous version of sysinstall, and as such fall under his "BEERWARE license"
15 * so buy him a beer if you like it!  Buy him a beer for me, too!
16 * Heck, get him completely drunk and send me pictures! :-)
17 */
18
19#include "sysinstall.h"
20#include <signal.h>
21#include <termios.h>
22#include <sys/reboot.h>
23#include <sys/consio.h>
24#include <sys/fcntl.h>
25#include <sys/ioctl.h>
26#include <sys/stat.h>
27#include <sys/types.h>
28#include <sys/sysctl.h>
29
30
31/* Where we stick our temporary expanded doc file */
32#define	DOC_TMP_DIR	"/tmp/.doc"
33#define	DOC_TMP_FILE	"/tmp/.doc/doc.tmp"
34
35static pid_t ehs_pid;
36
37/*
38 * Handle interrupt signals - this probably won't work in all cases
39 * due to our having bogotified the internal state of dialog or curses,
40 * but we'll give it a try.
41 */
42static int
43intr_continue(dialogMenuItem *self)
44{
45    return DITEM_LEAVE_MENU;
46}
47
48static int
49intr_reboot(dialogMenuItem *self)
50{
51    systemShutdown(-1);
52    /* NOTREACHED */
53    return 0;
54}
55
56static int
57intr_restart(dialogMenuItem *self)
58{
59    execl(StartName, StartName, (char *)NULL);
60    /* NOTREACHED */
61    return -1;
62}
63
64static dialogMenuItem intrmenu[] = {
65    { "Abort",   "Abort the installation", NULL, intr_reboot },
66    { "Restart", "Restart the installation program", NULL, intr_restart },
67    { "Continue", "Continue the installation", NULL, intr_continue },
68};
69
70
71static void
72handle_intr(int sig)
73{
74    WINDOW *save = savescr();
75
76    use_helpline(NULL);
77    use_helpfile(NULL);
78    if (OnVTY) {
79        ioctl(0, VT_ACTIVATE, 1);       /* Switch back */
80        msgInfo(NULL);
81    }
82    (void)dialog_menu("Installation interrupt",
83		     "Do you want to abort the installation?",
84		     -1, -1, 3, -3, intrmenu, NULL, NULL, NULL);
85    restorescr(save);
86}
87
88/*
89 * Harvest children if we are init.
90 */
91static void
92reap_children(int sig)
93{
94    int errbak = errno;
95
96    while (waitpid(-1, NULL, WNOHANG) > 0)
97	;
98    errno = errbak;
99}
100
101/* Expand a file into a convenient location, nuking it each time */
102static char *
103expand(char *fname)
104{
105    char *gunzip = RunningAsInit ? "/stand/gunzip" : "/usr/bin/gunzip";
106
107    if (!directory_exists(DOC_TMP_DIR)) {
108	Mkdir(DOC_TMP_DIR);
109	if (chown(DOC_TMP_DIR, 0, 0) < 0)
110	    return NULL;
111	if (chmod(DOC_TMP_DIR, S_IRWXU) < 0)
112	    return NULL;
113    }
114    else
115	unlink(DOC_TMP_FILE);
116    if (!file_readable(fname) || vsystem("%s < %s > %s", gunzip, fname, DOC_TMP_FILE))
117	return NULL;
118    return DOC_TMP_FILE;
119}
120
121/* Initialize system defaults */
122void
123systemInitialize(int argc, char **argv)
124{
125    int i, boothowto;
126    sigset_t signalset;
127
128    signal(SIGINT, SIG_IGN);
129    globalsInit();
130
131    i = sizeof(boothowto);
132    if (!sysctlbyname("debug.boothowto", &boothowto, &i, NULL, NULL) &&
133        (i == sizeof(boothowto)) && (boothowto & RB_VERBOSE))
134	variable_set2(VAR_DEBUG, "YES", 0);
135
136    /* Are we running as init? */
137    if (getpid() == 1) {
138	int fd, type;
139
140	RunningAsInit = 1;
141	setsid();
142	close(0);
143	fd = open("/dev/ttyv0", O_RDWR);
144	if (fd == -1) {
145	    fd = open("/dev/console", O_RDWR);	/* fallback */
146	    variable_set2(VAR_FIXIT_TTY, "serial", 0); /* give fixit a hint */
147	} else
148	    OnVTY = TRUE;
149	/*
150	 * To make _sure_ we're on a VTY and don't have /dev/console switched
151	 * away to a serial port or something, attempt to set the cursor appearance.
152	 */
153	type = 0;	/* normal */
154	if (OnVTY) {
155	    int fd2;
156
157	    if ((fd2 = open("/dev/console", O_RDWR)) != -1) {
158		if (ioctl(fd2, CONS_CURSORTYPE, &type) == -1) {
159		    OnVTY = FALSE;
160		    variable_set2(VAR_FIXIT_TTY, "serial", 0); /* Tell Fixit
161								  the console
162								  type */
163		    close(fd); close(fd2);
164		    open("/dev/console", O_RDWR);
165		}
166		else
167		    close(fd2);
168	    }
169	}
170	close(1); dup(0);
171	close(2); dup(0);
172	printf("%s running as init on %s\n", argv[0], OnVTY ? "vty0" : "serial console");
173	ioctl(0, TIOCSCTTY, (char *)NULL);
174	setlogin("root");
175	setenv("PATH", "/stand:/bin:/sbin:/usr/sbin:/usr/bin:/mnt/bin:/mnt/sbin:/mnt/usr/sbin:/mnt/usr/bin:/usr/X11R6/bin", 1);
176	setbuf(stdin, 0);
177	setbuf(stderr, 0);
178#ifdef __alpha__
179	i = 0;
180	sysctlbyname("machdep.unaligned_print", NULL, 0, &i, sizeof(i));
181#endif
182	signal(SIGCHLD, reap_children);
183    }
184    else {
185	char hname[256];
186
187	/* Initalize various things for a multi-user environment */
188	if (!gethostname(hname, sizeof hname))
189	    variable_set2(VAR_HOSTNAME, hname, 0);
190    }
191
192    if (set_termcap() == -1) {
193	printf("Can't find terminal entry\n");
194	exit(-1);
195    }
196
197    /* XXX - libdialog has particularly bad return value checking */
198    init_dialog();
199
200    /* If we haven't crashed I guess dialog is running ! */
201    DialogActive = TRUE;
202
203    /* Make sure HOME is set for those utilities that need it */
204    if (!getenv("HOME"))
205	setenv("HOME", "/", 1);
206    signal(SIGINT, handle_intr);
207    /*
208     * Make sure we can be interrupted even if we were re-executed
209     * from an interrupt.
210     */
211    sigemptyset(&signalset);
212    sigaddset(&signalset, SIGINT);
213    sigprocmask(SIG_UNBLOCK, &signalset, NULL);
214
215    (void)vsystem("rm -rf %s", DOC_TMP_DIR);
216}
217
218/* Close down and prepare to exit */
219void
220systemShutdown(int status)
221{
222    /* If some media is open, close it down */
223    if (status >=0)
224	mediaClose();
225
226    /* write out any changes to rc.conf .. */
227    configRC_conf();
228
229    /* Shut down the dialog library */
230    if (DialogActive) {
231	end_dialog();
232	DialogActive = FALSE;
233    }
234
235    /* Shut down curses */
236    endwin();
237
238    /* If we have a temporary doc dir lying around, nuke it */
239    (void)vsystem("rm -rf %s", DOC_TMP_DIR);
240
241    /* REALLY exit! */
242    if (RunningAsInit) {
243	/* Put the console back */
244	ioctl(0, VT_ACTIVATE, 2);
245#ifdef __alpha__
246	reboot(RB_HALT);
247#else
248	reboot(0);
249#endif
250    }
251    else
252	exit(status);
253}
254
255/* Run some general command */
256int
257systemExecute(char *command)
258{
259    int status;
260    struct termios foo;
261    WINDOW *w = savescr();
262
263    dialog_clear();
264    dialog_update();
265    end_dialog();
266    DialogActive = FALSE;
267    if (tcgetattr(0, &foo) != -1) {
268	foo.c_cc[VERASE] = '\010';
269	tcsetattr(0, TCSANOW, &foo);
270    }
271    if (!Fake)
272	status = system(command);
273    else {
274	status = 0;
275	msgDebug("systemExecute:  Faked execution of `%s'\n", command);
276    }
277    DialogActive = TRUE;
278    restorescr(w);
279    return status;
280}
281
282/* suspend/resume libdialog/curses screen */
283static    WINDOW *oldW;
284
285void
286systemSuspendDialog(void)
287{
288
289    oldW  = savescr();
290    dialog_clear();
291    dialog_update();
292    end_dialog();
293    DialogActive = FALSE;
294}
295
296void
297systemResumeDialog(void)
298{
299
300    DialogActive = TRUE;
301    restorescr(oldW);
302}
303
304/* Display a help file in a filebox */
305int
306systemDisplayHelp(char *file)
307{
308    char *fname = NULL;
309    char buf[FILENAME_MAX];
310    int ret = 0;
311    WINDOW *w = savescr();
312
313    fname = systemHelpFile(file, buf);
314    if (!fname) {
315	snprintf(buf, FILENAME_MAX, "The %s file is not provided on this particular floppy image.", file);
316	use_helpfile(NULL);
317	use_helpline(NULL);
318	dialog_mesgbox("Sorry!", buf, -1, -1);
319	ret = 1;
320    }
321    else {
322	use_helpfile(NULL);
323	use_helpline(NULL);
324	dialog_textbox(file, fname, LINES, COLS);
325    }
326    restorescr(w);
327    return ret;
328}
329
330char *
331systemHelpFile(char *file, char *buf)
332{
333    if (!file)
334	return NULL;
335    if (file[0] == '/')
336	return file;
337    snprintf(buf, FILENAME_MAX, "/stand/help/%s.hlp.gz", file);
338    if (file_readable(buf))
339	return expand(buf);
340    snprintf(buf, FILENAME_MAX, "/stand/help/%s.TXT.gz", file);
341    if (file_readable(buf))
342	return expand(buf);
343    snprintf(buf, FILENAME_MAX, "/usr/src/usr.sbin/sysinstall/help/%s.hlp", file);
344    if (file_readable(buf))
345	return buf;
346    snprintf(buf, FILENAME_MAX, "/usr/src/usr.sbin/sysinstall/help/%s.TXT", file);
347    if (file_readable(buf))
348	return buf;
349    return NULL;
350}
351
352void
353systemChangeTerminal(char *color, const u_char c_term[],
354		     char *mono, const u_char m_term[])
355{
356    if (OnVTY) {
357	int setupterm(char *color, int, int *);
358
359	if (ColorDisplay) {
360	    setenv("TERM", color, 1);
361	    setenv("TERMCAP", c_term, 1);
362	    reset_shell_mode();
363	    setterm(color);
364	    cbreak(); noecho();
365	}
366	else {
367	    setenv("TERM", mono, 1);
368	    setenv("TERMCAP", m_term, 1);
369	    reset_shell_mode();
370	    setterm(mono);
371	    cbreak(); noecho();
372	}
373    }
374    clear();
375    refresh();
376    dialog_clear();
377}
378
379int
380vsystem(char *fmt, ...)
381{
382    va_list args;
383    int pstat;
384    pid_t pid;
385    int omask;
386    sig_t intsave, quitsave;
387    char *cmd;
388    int i;
389
390    cmd = (char *)alloca(FILENAME_MAX);
391    cmd[0] = '\0';
392    va_start(args, fmt);
393    vsnprintf(cmd, FILENAME_MAX, fmt, args);
394    va_end(args);
395
396    omask = sigblock(sigmask(SIGCHLD));
397    if (Fake) {
398	msgDebug("vsystem:  Faked execution of `%s'\n", cmd);
399	return 0;
400    }
401    if (isDebug())
402	msgDebug("Executing command `%s'\n", cmd);
403    pid = fork();
404    if (pid == -1) {
405	(void)sigsetmask(omask);
406	i = 127;
407    }
408    else if (!pid) {	/* Junior */
409	(void)sigsetmask(omask);
410	if (DebugFD != -1) {
411	    dup2(DebugFD, 0);
412	    dup2(DebugFD, 1);
413	    dup2(DebugFD, 2);
414	}
415	else {
416	    close(1); open("/dev/null", O_WRONLY);
417	    dup2(1, 2);
418	}
419	if (!RunningAsInit)
420	    execl("/bin/sh", "/bin/sh", "-c", cmd, (char *)NULL);
421	else
422	    execl("/stand/sh", "/stand/sh", "-c", cmd, (char *)NULL);
423	exit(1);
424    }
425    else {
426	intsave = signal(SIGINT, SIG_IGN);
427	quitsave = signal(SIGQUIT, SIG_IGN);
428	pid = waitpid(pid, &pstat, 0);
429	(void)sigsetmask(omask);
430	(void)signal(SIGINT, intsave);
431	(void)signal(SIGQUIT, quitsave);
432	i = (pid == -1) ? -1 : WEXITSTATUS(pstat);
433	if (isDebug())
434	    msgDebug("Command `%s' returns status of %d\n", cmd, i);
435    }
436    return i;
437}
438
439void
440systemCreateHoloshell(void)
441{
442    int waitstatus;
443
444    if ((FixItMode || OnVTY) && RunningAsInit) {
445
446	if (ehs_pid != 0) {
447	    int pstat;
448
449	    if (kill(ehs_pid, 0) == 0) {
450
451		if (msgNoYes("There seems to be an emergency holographic shell\n"
452			     "already running on VTY 4.\n\n"
453			     "Kill it and start a new one?"))
454		    return;
455
456		/* try cleaning up as much as possible */
457		(void) kill(ehs_pid, SIGHUP);
458		sleep(1);
459		(void) kill(ehs_pid, SIGKILL);
460	    }
461
462	    /* avoid too many zombies */
463	    (void) waitpid(ehs_pid, &pstat, WNOHANG);
464	}
465
466	if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
467	    systemSuspendDialog();	/* must be before the fork() */
468	if ((ehs_pid = fork()) == 0) {
469	    int i, fd;
470	    struct termios foo;
471	    extern int login_tty(int);
472
473	    ioctl(0, TIOCNOTTY, NULL);
474	    for (i = getdtablesize(); i >= 0; --i)
475		close(i);
476	    if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
477	        fd = open("/dev/console", O_RDWR);
478	    else
479	        fd = open("/dev/ttyv3", O_RDWR);
480	    ioctl(0, TIOCSCTTY, &fd);
481	    dup2(0, 1);
482	    dup2(0, 2);
483	    DebugFD = 2;
484	    if (login_tty(fd) == -1)
485		msgDebug("Doctor: I can't set the controlling terminal.\n");
486	    signal(SIGTTOU, SIG_IGN);
487	    if (tcgetattr(fd, &foo) != -1) {
488		foo.c_cc[VERASE] = '\010';
489		if (tcsetattr(fd, TCSANOW, &foo) == -1)
490		    msgDebug("Doctor: I'm unable to set the erase character.\n");
491	    }
492	    else
493		msgDebug("Doctor: I'm unable to get the terminal attributes!\n");
494	    if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0) {
495	        printf("Type ``exit'' in this fixit shell to resume sysinstall.\n\n");
496		fflush(stdout);
497	    }
498	    execlp("sh", "-sh", 0);
499	    msgDebug("Was unable to execute sh for Holographic shell!\n");
500	    exit(1);
501	}
502	else {
503	    if (strcmp(variable_get(VAR_FIXIT_TTY), "standard") == 0) {
504	        WINDOW *w = savescr();
505
506	        msgNotify("Starting an emergency holographic shell on VTY4");
507	        sleep(2);
508	        restorescr(w);
509	    }
510	    else {
511	        (void)waitpid(ehs_pid, &waitstatus, 0); /* we only wait for
512							   shell to finish
513							   it serial mode
514							   since there is no
515							   virtual console */
516	        systemResumeDialog();
517	    }
518	}
519    }
520}
521