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