0676bf76d62edbf829322777137079b48bb2e560
[oweals/busybox.git] / 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 #define DEBUG_INIT
45
46 #define CONSOLE         "/dev/console"          /* Logical system console */
47 #define VT_PRIMARY      "/dev/tty0"             /* Virtual console master */
48 #define VT_SECONDARY    "/dev/tty1"             /* Virtual console master */
49 #define VT_LOG          "/dev/tty2"             /* Virtual console master */
50 #define SHELL           "/bin/sh"               /* Default shell */
51 #define INITSCRIPT      "/etc/init.d/rcS"       /* Initscript. */
52 #define PATH_DEFAULT    "PATH=/usr/local/sbin:/sbin:/bin:/usr/sbin:/usr/bin"
53
54 static int maxproclen=0;
55 static char* argv0;
56
57 static char* console = CONSOLE;
58 static char* second_terminal = "/dev/tty2";
59 static char* log = "/dev/tty3";
60
61
62
63 /* try to open up the specified device */
64 int device_open(char* device, int mode)
65 {
66     int m, f, fd = -1;
67     
68     mode = m | O_NONBLOCK;
69     
70     /* Retry up to 5 times */
71     for(f = 0; f < 5; f++)
72         if ((fd = open(device, m)) >= 0) break;
73     if (fd < 0) return fd;
74     /* Set original flags. */
75     if (m != mode)
76         fcntl(fd, F_SETFL, mode);
77     return fd;
78 }
79
80 /* print a message to the specified device */
81 void message(char* device, char *fmt, ...)
82 {
83     int fd;
84     va_list arguments;
85     if ((fd = device_open(device, O_WRONLY|O_NOCTTY|O_NDELAY)) >= 0) {
86         va_start(arguments, fmt);
87         vdprintf(fd, fmt, arguments);
88         va_end(arguments);
89     }
90     close( fd);
91 }
92
93 /* Set terminal settings to reasonable defaults */
94 void set_term()
95 {
96     int fd;
97     struct termios tty;
98
99     if ((fd = device_open(console, O_RDWR|O_NOCTTY)) < 0) {
100         message(log, "can't open %s", console);
101         return;
102     }
103     ioctl(fd, TCGETS, &tty);
104     tty.c_cflag &= CBAUD|CBAUDEX|CSIZE|CSTOPB|PARENB|PARODD;
105     tty.c_cflag |= HUPCL|CLOCAL;
106
107     tty.c_cc[VINTR]  = 3;
108     tty.c_cc[VQUIT]  = 28;
109     tty.c_cc[VERASE] = 127;
110     tty.c_cc[VKILL]  = 24;
111     tty.c_cc[VEOF]   = 4;
112     tty.c_cc[VTIME]  = 0;
113     tty.c_cc[VMIN]   = 1;
114     tty.c_cc[VSTART] = 17;
115     tty.c_cc[VSTOP]  = 19;
116     tty.c_cc[VSUSP]  = 26;
117
118     /* Set pre and post processing */
119     tty.c_iflag = IGNPAR|ICRNL|IXON|IXANY;
120     tty.c_oflag = OPOST|ONLCR;
121     tty.c_lflag = ISIG|ICANON|ECHO|ECHOCTL|ECHOPRT|ECHOKE;
122
123     /* Now set the terminal line. */
124     ioctl(fd, TCSETS, &tty);
125     close( fd);
126 }
127
128 static int
129 mem_total()
130 {
131     char s[80];
132     char *p;
133     FILE *f;
134     const char pattern[]="MemTotal:";
135
136     f=fopen("/proc/meminfo","r");
137     while (NULL != fgets(s,79,f)) {
138         p=strstr(s, pattern);
139         if (NULL != p) {
140             fclose(f);
141             return(atoi(p+strlen(pattern)));
142         }
143     }
144     return -1;
145 }
146
147 static void
148 set_free_pages()
149 {
150     char s[80];
151     FILE *f;
152
153     f=fopen("/proc/sys/vm/freepages","r");
154     fgets(s,79,f);
155     if (atoi(s) < 32) {
156         fclose(f);
157         f=fopen("/proc/sys/vm/freepages","w");
158         fprintf(f,"30\t40\t50\n");
159         printf("\nIncreased /proc/sys/vm/freepages values to 30/40/50\n");
160     }
161     fclose(f);
162 }
163
164
165 static void
166 console_init()
167 {
168     int fd;
169     int tried_devcons = 0;
170     int tried_vtmaster = 0;
171     char *s;
172
173     if ((s = getenv("CONSOLE")) != NULL)
174         console = s;
175     else {
176         console = CONSOLE;
177         tried_devcons++;
178     }
179     while ((fd = open(console, O_RDONLY|O_NONBLOCK)) < 0) {
180         if (!tried_devcons) {
181             tried_devcons++;
182             console = CONSOLE;
183             continue;
184         }
185         if (!tried_vtmaster) {
186             tried_vtmaster++;
187             console = VT_PRIMARY;
188             continue;
189         }
190         break;
191     }
192     if (fd < 0)
193         console = "/dev/null";
194     else
195         close(fd);
196 }
197
198 static int
199 waitfor(int pid)
200 {
201     int status, wpid;
202
203     message(log, "Waiting for process %d.\n", pid);
204     while ( (wpid = wait(&status)) != pid ) {
205         if ( wpid > 0 )
206             message(log,"pid %d exited, status=%x.\n", wpid, status);
207     }
208     return wpid;
209 }
210
211 static int
212 run(const char* command, char* terminal, int get_enter)
213 {
214     int f, pid;
215     char *args[16];
216     char buf[256];
217     char* ptr;
218     static const char press_enter[] = 
219         "\nPlease press Enter to activate this console. ";
220
221         
222     /* Make a proper command from the command string */ 
223     strcpy(buf, command);
224     ptr = buf;
225     for(f = 1; f < 15; f++) {
226         /* Skip white space */
227         while(*ptr == ' ' || *ptr == '\t') ptr++;
228         args[f] = ptr;
229         /* May be trailing space.. */
230         if (*ptr == 0) break;
231         /* Skip this `word' */
232         while(*ptr && *ptr != ' ' && *ptr != '\t' && *ptr != '#')
233             ptr++;
234         /* If end-of-line, break */
235         if (*ptr == '#' || *ptr == 0) {
236             f++;
237             *ptr = 0;
238             break;
239         }
240         /* End word with \0 and continue */
241         args[f] = NULL;
242     }
243     args[0] = args[1];
244         
245
246     if ((pid = fork()) == 0) {
247         /* Clean up */
248         close(0);
249         close(1);
250         close(2);
251         setsid();
252
253         if ((f = device_open(terminal, O_RDWR|O_NOCTTY)) < 0) {
254             message( log, "open(%s) failed: %s", terminal, strerror(errno));
255             return -1;
256         }
257         dup(f);
258         dup(f);
259         tcsetpgrp(0, getpgrp());
260         set_term();
261
262         if ( get_enter ) {
263             /*
264              * Save memory by not exec-ing anything large (like a shell)
265              * before the user wants it. This is critical if swap is not
266              * enabled and the system has low memory. Generally this will
267              * be run on the second virtual console, and the first will
268              * be allowed to start a shell or whatever an init script 
269              * specifies.
270              */
271             char        c;
272             write(1, press_enter, sizeof(press_enter) - 1);
273             read(0, &c, 1);
274         }
275
276         /* Log the process name and args */
277         message(log, "Executing '%s'\n", command);
278
279         /* Now run it.  This should take over the PID, so nothing 
280          * further in init.c should be run by this PID. */
281         execvp(args[1], args + 1);
282
283         /* If shell scripts are not executed, force the issue */
284         if (errno == ENOEXEC) {
285             char buf[256];
286             args[1] = SHELL;
287             args[2] = "-c";
288             strcpy(buf, "exec ");
289             strcat(buf, command);
290             args[3] = buf;
291             args[4] = NULL;
292             execvp(args[1], args + 1);
293         }
294         message(log, "Could not execute '%s'\n", command, strerror(errno));
295         exit(-1);
296     }
297     return pid;
298 }
299
300 #ifndef DEBUG_INIT
301 static void
302 shutdown_system(void)
303 {
304
305     message(console, "The system is going down NOW !!");
306     sync();
307     /* Allow Ctrl-Alt-Del to reboot system. */
308     reboot(RB_ENABLE_CAD);
309
310     /* Send signals to every process _except_ pid 1 */
311     message(console, "Sending SIGHUP to all processes.\r\n");
312     kill(-1, SIGHUP);
313     sleep(2);
314     sync();
315     message(console, "Sending SIGKILL to all processes.\r\n");
316     kill(-1, SIGKILL);
317     sleep(1);
318     waitfor(run("/bin/swapoff -a", console, 0));
319     waitfor(run("/bin/umount -a -n", console, 0));
320     sync();
321     if (get_kernel_revision() <= 2*65536+2*256+11) {
322         /* Removed  bdflush call, kupdate in kernels >2.2.11 */
323         bdflush(1, 0);
324         sync();
325     }
326 }
327
328 static void
329 halt_signal(int sig)
330 {
331     shutdown_system();
332     message(console, "The system is halted. Press CTRL-ALT-DEL or turn off power\r\n");
333     reboot( RB_POWER_OFF);
334     exit(0);
335 }
336
337 static void
338 reboot_signal(int sig)
339 {
340     shutdown_system();
341     message(console, "Please stand by while rebooting the system.\r\n");
342     reboot( RB_AUTOBOOT);
343     exit(0);
344 }
345 #endif
346
347 int setproctitle(char *fmt, ...)
348 {
349     va_list ap;
350     int len;
351     char buf[256];
352
353     buf[0] = 0;
354     va_start(ap, fmt);
355     len = vsprintf(buf, fmt, ap);
356     va_end(ap);
357     memset(argv0, 0, maxproclen + 1);
358     strncpy(argv0, buf, maxproclen);
359     return len;
360 }
361
362 extern int
363 init_main(int argc, char * * argv)
364 {
365         int run_rc = TRUE;
366         int pid1 = 0;
367         int pid2 = 0;
368         struct stat statbuf;
369         const char* init_commands = SHELL "-c exec " INITSCRIPT;
370         const char* shell_commands = SHELL;
371         const char* tty0_commands = init_commands;
372         const char* tty1_commands = shell_commands;
373         const char* no_memory = 
374             "Sorry, your computer does not have enough memory.\n";
375
376         /* For later use */
377         argv0 = argv[0];
378         maxproclen = strlen(argv[0]);
379         setproctitle("init [boot]");
380
381
382 #ifndef DEBUG_INIT
383         /* Set up sig handlers */
384         signal(SIGUSR1, halt_signal);
385         signal(SIGSEGV, halt_signal);
386         signal(SIGPWR,  halt_signal);
387         signal(SIGALRM, halt_signal);
388         signal(SIGHUP,  halt_signal);
389         signal(SIGUSR2, reboot_signal);
390         signal(SIGINT,  reboot_signal);
391         signal(SIGTERM, reboot_signal);
392 #endif
393         /* Figure out where the default console should be */
394         console_init();
395
396         /* Turn off rebooting via CTL-ALT-DEL -- we get a 
397          * SIGINT on CAD so we can shut things down gracefully... */
398 #ifndef DEBUG_INIT
399         reboot(RB_DISABLE_CAD);
400 #endif
401
402         /* Close whatever files are open, and reset the console. */
403         close(0);
404         close(1);
405         close(2);
406         set_term();
407         setsid();
408
409         /* Make sure PATH is set to something sane */
410         if (getenv("PATH") == NULL) 
411             putenv(PATH_DEFAULT);
412
413         /* Hello world */
414         message(console, "%s started:  BusyBox v%s (%s) multi-call binary", 
415                 argv[0], BB_VER, BB_BT);
416         message(log, "%s started:  BusyBox v%s (%s) multi-call binary", 
417                 argv[0], BB_VER, BB_BT);
418
419
420         /* Mount /proc */
421         message(console, "Mounting /proc: \n");
422         if (mount("/proc","/proc","proc",0,0)) {
423             message(log, "%s: could not mount /proc!\n", argv[0]);
424             message(console, "failed!\n");
425         }
426         message(console, "done.\n");
427
428
429         /* Make sure there is enough memory to do something useful*/
430         set_free_pages();
431         if (mem_total() < 2000) { 
432             int retval;
433             retval= stat("/etc/fstab",&statbuf);
434             if (retval) {
435                 message(console, "%s", no_memory);
436                 while (1) { sleep(1);}
437             } else { 
438               /* Try to turn on swap */
439                 waitfor(run("/bin/swapon -a", console, 0));
440                 if (mem_total() < 2000) { 
441                     message(console, "%s", no_memory);
442                     while (1) { sleep(1);}
443                 }
444             }
445         }
446
447         /* Check if we are supposed to be in single user mode */
448         if (!strcmp(argv[1], "single") || !strcmp(argv[1], "-s") || !strcmp(argv[1], "1")) {
449                 run_rc = FALSE;
450                 tty0_commands = shell_commands;
451                 tty1_commands = 0;
452                 setproctitle("init [S]");
453         } else { 
454             setproctitle("init [1]");
455         }
456
457         /* Make sure an init script exists before trying to run it */
458         if ( run_rc == TRUE && stat( INITSCRIPT, &statbuf)) {
459             tty0_commands = shell_commands;
460             tty1_commands = shell_commands;
461         } 
462
463         /* Ok, now launch the rc script and/or prepare to 
464          * start up some VTs if somebody hits enter... 
465          */
466         for ( ; ; ) {
467                 int     wpid;
468                 int     status;
469
470                 if ( pid1 == 0  && *tty0_commands ) {
471                     pid1 = run(tty0_commands, console, 1);
472                 }
473                 if ( pid2 == 0 && *tty1_commands ) {
474                     pid2 = run(tty1_commands, second_terminal, 1);
475                 }
476                 wpid = wait(&status);
477                 if ( wpid > 0  && wpid != pid1) {
478                         message(log, "pid %d exited, status=%x.\n", wpid, status);
479                 }
480                 if ( wpid == pid2 ) {
481                         pid2 = 0;
482                 }
483                 sleep(1);
484         }
485 }
486