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