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