More stuf. sed works.
[oweals/busybox.git] / init / init.c
1 /*
2  * Mini init implementation for busybox
3  *
4  *
5  * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
6  * Adjusted by so many folks, it's impossible to keep track.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  *
22  */
23
24 #include "internal.h"
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <stdarg.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <signal.h>
31 #include <termios.h>
32 #include <sys/types.h>
33 #include <sys/fcntl.h>
34 #include <sys/wait.h>
35 #include <string.h>
36 #include <sys/mount.h>
37 #include <sys/reboot.h>
38 #include <sys/kdaemon.h>
39 #include <sys/sysmacros.h>
40 #include <linux/serial.h>       /* for serial_struct */
41 #include <sys/vt.h>             /* for vt_stat */
42 #include <sys/ioctl.h>
43
44 /* Turn this on to debug init so it won't reboot when killed */
45 #define DEBUG_INIT
46
47 #define CONSOLE         "/dev/console"  /* Logical system console */
48 #define VT_PRIMARY      "/dev/tty0"     /* Virtual console master */
49 #define VT_SECONDARY    "/dev/tty1"     /* Virtual console master */
50 #define VT_LOG          "/dev/tty2"     /* Virtual console master */
51 #define SHELL           "/bin/sh"       /* Default shell */
52 #define INITSCRIPT      "/etc/init.d/rcS"       /* Initscript. */
53 #define PATH_DEFAULT    "PATH=/usr/local/sbin:/sbin:/bin:/usr/sbin:/usr/bin"
54
55 static char *console = CONSOLE;
56 //static char *first_terminal = "/dev/tty1";
57 static char *second_terminal = "/dev/tty2";
58 static char *log = "/dev/tty3";
59
60
61
62 /* try to open up the specified device */
63 int device_open(char *device, int mode)
64 {
65     int m, f, fd = -1;
66
67     m = mode | O_NONBLOCK;
68
69     /* Retry up to 5 times */
70     for (f = 0; f < 5; f++)
71         if ((fd = open(device, m)) >= 0)
72             break;
73     if (fd < 0)
74         return fd;
75     /* Set original flags. */
76     if (m != mode)
77         fcntl(fd, F_SETFL, mode);
78     return fd;
79 }
80
81 /* print a message to the specified device */
82 void message(char *device, char *fmt, ...)
83 {
84     int fd;
85     va_list arguments;
86
87     if ((fd = device_open(device, O_WRONLY|O_NOCTTY|O_NDELAY)) >= 0) {
88         va_start(arguments, fmt);
89         vdprintf(fd, fmt, arguments);
90         va_end(arguments);
91         close(fd);
92     } else {
93         fprintf(stderr, "Bummer, can't print: ");
94         va_start(arguments, fmt);
95         vfprintf(stderr, fmt, arguments);
96         fflush(stderr);
97         va_end(arguments);
98     }
99 }
100
101 /* Set terminal settings to reasonable defaults */
102 void set_term( int fd)
103 {
104     struct termios tty;
105     static const char control_characters[] = {
106         '\003', '\034', '\177', '\025', '\004', '\0',
107         '\1', '\0', '\021', '\023', '\032', '\0', '\022',
108         '\017', '\027', '\026', '\0'
109         };
110
111     tcgetattr(fd, &tty);
112
113     /* input modes */
114     tty.c_iflag = IGNPAR|ICRNL|IXON|IXOFF|IXANY;
115
116     /* use lineo dicipline 0 */
117     tty.c_line = 0;
118
119     /* output modes */
120     tty.c_oflag = OPOST|ONLCR;
121
122     /* local modes */
123     tty.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOPRT|ECHOKE|IEXTEN;
124
125     /* control chars */
126     memcpy(tty.c_cc, control_characters, sizeof(control_characters));
127
128     tcsetattr(fd, TCSANOW, &tty);
129 }
130
131 static int mem_total()
132 {
133     char s[80];
134     char *p;
135     FILE *f;
136     const char pattern[] = "MemTotal:";
137
138     f = fopen("/proc/meminfo", "r");
139     while (NULL != fgets(s, 79, f)) {
140         p = strstr(s, pattern);
141         if (NULL != p) {
142             fclose(f);
143             return (atoi(p + strlen(pattern)));
144         }
145     }
146     return -1;
147 }
148
149 static void set_free_pages()
150 {
151     char s[80];
152     FILE *f;
153
154     f = fopen("/proc/sys/vm/freepages", "r");
155     fgets(s, 79, f);
156     if (atoi(s) < 32) {
157         fclose(f);
158         f = fopen("/proc/sys/vm/freepages", "w");
159         fprintf(f, "30\t40\t50\n");
160         message(log, "\nIncreased /proc/sys/vm/freepages values to 30/40/50\n");
161     }
162     fclose(f);
163 }
164
165
166 static void console_init()
167 {
168     int fd;
169     struct stat statbuf;
170     int tried_devcons = 0;
171     int tried_vtmaster = 0;
172     char *s;
173
174     if ((s = getenv("CONSOLE")) != NULL)
175         console = s;
176     else {
177         console = CONSOLE;
178         tried_devcons++;
179     }
180
181     if ( stat(CONSOLE, &statbuf) && S_ISLNK(statbuf.st_mode)) {
182         fprintf(stderr, "Yikes! /dev/console does not exist or is a symlink.\n");
183         message(log, "Yikes! /dev/console does not exist or is a symlink.\n");
184     }
185     while ((fd = open(console, O_RDONLY | O_NONBLOCK)) < 0) {
186         if (!tried_devcons) {
187             tried_devcons++;
188             console = CONSOLE;
189             continue;
190         }
191         if (!tried_vtmaster) {
192             tried_vtmaster++;
193             console = VT_PRIMARY;
194             continue;
195         }
196         break;
197     }
198     if (fd < 0)
199         console = "/dev/null";
200     else
201         close(fd);
202     message(log, "console=%s\n", console);
203 }
204
205 static int waitfor(int pid)
206 {
207     int status, wpid;
208
209     message(log, "Waiting for process %d.\n", pid);
210     while ((wpid = wait(&status)) != pid) {
211         if (wpid > 0)
212             message(log, "pid %d exited, status=%x.\n", wpid, status);
213     }
214     return wpid;
215 }
216
217
218 static pid_t run(const char * const* command, 
219         char *terminal, int get_enter)
220 {
221     int i, f;
222     pid_t pid;
223     static const char press_enter[] =
224         "\nPlease press Enter to activate this console. ";
225
226     if ((pid = fork()) == 0) {
227         /* Clean up */
228         close(0);
229         close(1);
230         close(2);
231         setsid();
232
233         f=open(terminal, O_RDWR);
234         dup(f);
235         dup(f);
236         tcsetpgrp(0, getpgrp());
237         set_term(0);
238
239         if (get_enter) {
240             /*
241              * Save memory by not exec-ing anything large (like a shell)
242              * before the user wants it. This is critical if swap is not
243              * enabled and the system has low memory. Generally this will
244              * be run on the second virtual console, and the first will
245              * be allowed to start a shell or whatever an init script 
246              * specifies.
247              */
248             char c;
249             write(1, press_enter, sizeof(press_enter) - 1);
250             read(0, &c, 1);
251         }
252
253         /* Log the process name and args */
254
255         /* Now run it.  The new program will take over this PID, 
256          * so nothing further in init.c should be run. */
257         message(log, "Executing '%s', pid(%d)\r\n", *command, getpid());
258         execvp(*command, (char**)command+1);
259
260         message(log, "Hmm.  Trying as a script.\r\n");
261         /* If shell scripts are not executed, force the issue */
262         if (errno == ENOEXEC) {
263             char * args[16];
264             args[0] = SHELL;
265             args[1] = "-c";
266             args[2] = "exec";
267             for( i=0 ; i<16 && command[i]; i++)
268                 args[3+i] = (char*)command[i];
269             args[i] = NULL;
270             execvp(*args, (char**)args+1);
271         }
272         message(log, "Could not execute '%s'\n", command);
273         exit(-1);
274     }
275     return pid;
276 }
277
278 #ifndef DEBUG_INIT
279 static void shutdown_system(void)
280 {
281     const char* const swap_off_cmd[] = { "/bin/swapoff", "-a", 0};
282     const char* const umount_cmd[] = { "/bin/umount", "-a", "-n", 0};
283
284     message(console, "The system is going down NOW !!\r\n");
285     sync();
286     /* Allow Ctrl-Alt-Del to reboot system. */
287     reboot(RB_ENABLE_CAD);
288
289     /* Send signals to every process _except_ pid 1 */
290     message(console, "Sending SIGHUP to all processes.\r\n");
291     kill(-1, SIGHUP);
292     sleep(2);
293     sync();
294     message(console, "Sending SIGKILL to all processes.\r\n");
295     kill(-1, SIGKILL);
296     sleep(1);
297     waitfor(run( swap_off_cmd, console, 0));
298     waitfor(run( umount_cmd, console, 0));
299     sync();
300     if (get_kernel_revision() <= 2 * 65536 + 2 * 256 + 11) {
301         /* Removed  bdflush call, kupdate in kernels >2.2.11 */
302         bdflush(1, 0);
303         sync();
304     }
305 }
306
307 static void halt_signal(int sig)
308 {
309     shutdown_system();
310     message(console,
311             "The system is halted. Press CTRL-ALT-DEL or turn off power\r\n");
312     reboot(RB_POWER_OFF);
313     exit(0);
314 }
315
316 static void reboot_signal(int sig)
317 {
318     shutdown_system();
319     message(console, "Please stand by while rebooting the system.\r\n");
320     reboot(RB_AUTOBOOT);
321     exit(0);
322 }
323 #endif
324
325 extern int init_main(int argc, char **argv)
326 {
327     int run_rc = TRUE;
328     pid_t pid1 = 0;
329     pid_t pid2 = 0;
330     struct stat statbuf;
331     const char* const swap_on_cmd[] = { "/bin/swapon", " -a ", 0};
332     const char* const init_commands[] = { SHELL, " -c", " exec ", INITSCRIPT, 0};
333     const char* const shell_commands[] = { SHELL, " -", 0};
334     const char* const* tty0_commands = init_commands;
335     const char* const* tty1_commands = shell_commands;
336 #ifndef DEBUG_INIT
337     char *hello_msg_format =
338         "init(%d) started:  BusyBox v%s (%s) multi-call binary\r\n";
339 #else
340     char *hello_msg_format =
341         "init started:  BusyBox v%s (%s) multi-call binary\r\n";
342 #endif
343     const char *no_memory =
344         "Sorry, your computer does not have enough memory.\r\n";
345
346 #ifndef DEBUG_INIT
347     /* Set up sig handlers */
348     signal(SIGUSR1, halt_signal);
349     signal(SIGSEGV, halt_signal);
350     signal(SIGPWR, halt_signal);
351     signal(SIGALRM, halt_signal);
352     signal(SIGHUP, halt_signal);
353     signal(SIGUSR2, reboot_signal);
354     signal(SIGINT, reboot_signal);
355     signal(SIGTERM, reboot_signal);
356
357     /* Turn off rebooting via CTL-ALT-DEL -- we get a 
358      * SIGINT on CAD so we can shut things down gracefully... */
359     reboot(RB_DISABLE_CAD);
360 #endif 
361     /* Figure out where the default console should be */
362     console_init();
363
364     /* Close whatever files are open, and reset the console. */
365     close(0);
366     close(1);
367     close(2);
368     set_term(0);
369     setsid();
370
371     /* Make sure PATH is set to something sane */
372     if (getenv("PATH") == NULL)
373         putenv(PATH_DEFAULT);
374
375     /* Hello world */
376 #ifndef DEBUG_INIT
377     message(log, hello_msg_format, BB_VER, BB_BT);
378     message(console, hello_msg_format, BB_VER, BB_BT);
379 #else
380     message(log, hello_msg_format, getpid(), BB_VER, BB_BT);
381     message(console, hello_msg_format, getpid(), BB_VER, BB_BT);
382 #endif
383
384     
385     /* Mount /proc */
386     if (mount("/proc", "/proc", "proc", 0, 0) == 0) {
387         message(log, "Mounting /proc: done.\n");
388         message(console, "Mounting /proc: done.\n");
389     } else {
390         message(log, "Mounting /proc: failed!\n");
391         message(console, "Mounting /proc: failed!\n");
392     }
393
394     /* Make sure there is enough memory to do something useful */
395     set_free_pages();
396     if (mem_total() < 3500) {
397         int retval;
398         retval = stat("/etc/fstab", &statbuf);
399         if (retval) {
400             message(console, "%s", no_memory);
401             while (1) {
402                 sleep(1);
403             }
404         } else {
405             /* Try to turn on swap */
406             waitfor(run(swap_on_cmd, console, 0));
407             if (mem_total() < 2000) {
408                 message(console, "%s", no_memory);
409                 while (1) {
410                     sleep(1);
411                 }
412             }
413         }
414     }
415
416     /* Check if we are supposed to be in single user mode */
417     if ( argc > 1 && (!strcmp(argv[1], "single") || 
418                 !strcmp(argv[1], "-s") || !strcmp(argv[1], "1"))) {
419         run_rc = FALSE;
420         tty0_commands = shell_commands;
421         tty1_commands = shell_commands;
422     }
423
424     /* Make sure an init script exists before trying to run it */
425     if (run_rc == TRUE && stat(INITSCRIPT, &statbuf)) {
426         tty0_commands = shell_commands;
427         tty1_commands = shell_commands;
428     }
429
430     /* Ok, now launch the rc script and/or prepare to 
431      * start up some VTs if somebody hits enter... 
432      */
433     for (;;) {
434         pid_t wpid;
435         int status;
436
437         if (pid1 == 0 && tty0_commands) {
438             pid1 = run(tty0_commands, console, 1);
439         }
440         if (pid2 == 0 && tty1_commands) {
441             pid2 = run(tty1_commands, second_terminal, 1);
442         }
443         wpid = wait(&status);
444         if (wpid > 0 ) {
445             message(log, "pid %d exited, status=%x.\n", wpid, status);
446         }
447         /* Don't respawn an init script if it exits */
448         if (run_rc == FALSE && wpid == pid1) {
449             pid1 = 0;
450         }
451         if (wpid == pid2) {
452             pid2 = 0;
453         }
454         sleep(1);
455     }
456 }