62865451a975791d0a5e44052796bb5feddb7ba3
[oweals/busybox.git] / init / init.c
1 #include "internal.h"
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <stdarg.h>
5 #include <unistd.h>
6 #include <errno.h>
7 #include <signal.h>
8 #include <termios.h>
9 #include <sys/types.h>
10 #include <sys/fcntl.h>
11 #include <sys/wait.h>
12 #include <string.h>
13 #include <sys/mount.h>
14 #include <sys/reboot.h>
15 #include <sys/kdaemon.h>
16 #include <sys/sysmacros.h>
17 #include <linux/serial.h>      /* for serial_struct */
18 #include <sys/vt.h>            /* for vt_stat */
19 #include <sys/ioctl.h>
20
21 static const char  init_usage[] = "Used internally by the system.";
22 static char        console[16] = "";
23 static const char* default_console = "/dev/tty2";
24 static char*       first_terminal = NULL;
25 static const char* second_terminal = "/dev/tty2";
26 static const char* log = "/dev/tty3";
27 static char* term_ptr = NULL;
28
29 static void
30 message(const char* terminal, const char * pattern, ...)
31 {
32         int     fd;
33         FILE *  con = 0;
34         va_list arguments;
35
36         /*
37          * Open the console device each time a message is printed. If the user
38          * has switched consoles, the open will get the new console. If we kept
39          * the console open, we'd always print to the same one.
40          */
41         if ( !terminal
42          ||  ((fd = open(terminal, O_WRONLY|O_NOCTTY)) < 0)
43          ||  ((con = fdopen(fd, "w")) == NULL) )
44                 return;
45
46         va_start(arguments, pattern);
47         vfprintf(con, pattern, arguments);
48         va_end(arguments);
49         fclose(con);
50 }
51
52 static int
53 waitfor(int pid)
54 {
55         int     status;
56         int     wpid;
57         
58         message(log, "Waiting for process %d.\n", pid);
59         while ( (wpid = wait(&status)) != pid ) {
60                 if ( wpid > 0 ) {
61                         message(
62                          log
63                         ,"pid %d exited, status=%x.\n"
64                         ,wpid
65                         ,status);
66                 }
67         }
68         return wpid;
69 }
70
71 static int
72 run(const char* program, const char* const* arguments, 
73         const char* terminal, int get_enter)
74 {
75         static const char       control_characters[] = {
76                 '\003',
77                 '\034',
78                 '\177',
79                 '\025',
80                 '\004',
81                 '\0',
82                 '\1',
83                 '\0',
84                 '\021',
85                 '\023',
86                 '\032',
87                 '\0',
88                 '\022',
89                 '\017',
90                 '\027',
91                 '\026',
92                 '\0'
93         };
94
95         static char * environment[] = {
96                 "HOME=/",
97                 "PATH=/bin:/sbin:/usr/bin:/usr/sbin",
98                 "SHELL=/bin/sh",
99                 0,
100                 "USER=root",
101                 0
102         };
103
104         static const char       press_enter[] =
105  "\nPlease press Enter to activate this console. ";
106
107         int     pid;
108
109         environment[3]=term_ptr;
110
111         pid = fork();
112         if ( pid == 0 ) {
113                 struct termios  t;
114                 const char * const * arg;
115
116                 close(0);
117                 close(1);
118                 close(2);
119                 setsid();
120
121                 open(terminal, O_RDWR);
122                 dup(0);
123                 dup(0);
124                 tcsetpgrp(0, getpgrp());
125
126                 tcgetattr(0, &t);
127                 memcpy(t.c_cc, control_characters, sizeof(control_characters));
128                 t.c_line = 0;
129                 t.c_iflag = ICRNL|IXON|IXOFF;
130                 t.c_oflag = OPOST|ONLCR;
131                 t.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE|IEXTEN;
132                 tcsetattr(0, TCSANOW, &t);
133
134                 if ( get_enter ) {
135                         /*
136                          * Save memory by not exec-ing anything large (like a shell)
137                          * before the user wants it. This is critical if swap is not
138                          * enabled and the system has low memory. Generally this will
139                          * be run on the second virtual console, and the first will
140                          * be allowed to start a shell or whatever an init script 
141                          * specifies.
142                          */
143                         char    c;
144                         write(1, press_enter, sizeof(press_enter) - 1);
145                         read(0, &c, 1);
146                 }
147                 
148                 message(log, "Executing ");
149                 arg = arguments;
150                 while ( *arg != 0 )
151                         message(log, "%s ", *arg++);
152                 message(log, "\n");
153
154                 execve(program, (char * *)arguments, (char * *)environment);
155                 message(log, "%s: could not execute: %s.\r\n", program, strerror(errno));
156                 exit(-1);
157         }
158         return pid;
159 }
160
161 static int
162 mem_total()
163 {
164     char s[80];
165     char *p;
166     FILE *f;
167     const char pattern[]="MemTotal:";
168
169     f=fopen("/proc/meminfo","r");
170     while (NULL != fgets(s,79,f)) {
171         p=strstr(s, pattern);
172         if (NULL != p) {
173             fclose(f);
174             return(atoi(p+strlen(pattern)));
175         }
176     }
177     return -1;
178 }
179
180 static void
181 set_free_pages()
182 {
183     char s[80];
184     FILE *f;
185
186     f=fopen("/proc/sys/vm/freepages","r");
187     fgets(s,79,f);
188     if (atoi(s) < 32) {
189         fclose(f);
190         f=fopen("/proc/sys/vm/freepages","w");
191         fprintf(f,"30\t40\t50\n");
192         printf("\nIncreased /proc/sys/vm/freepages values to 30/40/50\n");
193     }
194     fclose(f);
195 }
196
197 static void
198 shutdown_system(void)
199 {
200         static const char * const umount_args[] = {"/bin/umount", "-a", "-n", 0};
201         static const char * const swapoff_args[] = {"/bin/swapoff", "-a", 0};
202
203         message(console, "The system is going down NOW !!");
204         sync();
205         /* Allow Ctrl-Alt-Del to reboot system. */
206         reboot(RB_ENABLE_CAD);
207
208         /* Send signals to every process _except_ pid 1 */
209         message(console, "Sending SIGHUP to all processes.\r\n");
210         kill(-1, SIGHUP);
211         sleep(2);
212         sync();
213         message(console, "Sending SIGKILL to all processes.\r\n");
214         kill(-1, SIGKILL);
215         sleep(1);
216         waitfor(run("/bin/swapoff", swapoff_args, console, 0));
217         waitfor(run("/bin/umount", umount_args, console, 0));
218         sync();
219         if (get_kernel_revision() <= 2*65536+2*256+11) {
220             /* Removed  bdflush call, kupdate in kernels >2.2.11 */
221             bdflush(1, 0);
222             sync();
223         }
224 }
225
226 static void
227 halt_signal(int sig)
228 {
229     shutdown_system();
230     message(console, "The system is halted. Press CTRL-ALT-DEL or turn off power\r\n");
231     reboot( RB_HALT_SYSTEM);
232     exit(0);
233 }
234
235 static void
236 reboot_signal(int sig)
237 {
238     shutdown_system();
239     message(console, "Please stand by while rebooting the system.\r\n");
240     reboot( RB_AUTOBOOT);
241     exit(0);
242 }
243
244 static void
245 configure_terminals( int serial_cons, int single_user_mode )
246 {
247         char *tty;
248         struct serial_struct sr;
249         struct vt_stat vt;
250
251
252         switch (serial_cons) {
253         case -1:
254             /* 2.2 kernels:
255             * identify the real console backend and try to make use of it */
256             if (ioctl(0,TIOCGSERIAL,&sr) == 0) {
257                 sprintf( console, "/dev/ttyS%d", sr.line );
258                 serial_cons = sr.line+1;
259             }
260             else if (ioctl(0, VT_GETSTATE, &vt) == 0) {
261                 sprintf( console, "/dev/tty%d", vt.v_active );
262                 serial_cons = 0;
263             }
264             else {
265                 /* unknown backend: fallback to /dev/console */
266                 strcpy( console, "/dev/console" );
267                 serial_cons = 0;
268             }
269             break;
270
271         case 1:
272                 strcpy( console, "/dev/cua0" );
273                 break;
274         case 2:
275                 strcpy( console, "/dev/cua1" );
276                 break;
277         default:
278                 tty = ttyname(0);
279                 if (tty) {
280                         strcpy( console, tty );
281                         if (!strncmp( tty, "/dev/cua", 8 ))
282                                 serial_cons=1;
283                 }
284                 else
285                         /* falls back to /dev/tty1 if an error occurs */
286                         strcpy( console, default_console );
287         }
288         if (!first_terminal)
289                 first_terminal = console;
290 #if defined (__sparc__)
291         if (serial_cons > 0 && !strncmp(term_ptr,"TERM=linux",10))
292             term_ptr = "TERM=vt100";
293 #endif
294         if (serial_cons) {
295             /* disable other but the first terminal:
296             * VT is not initialized anymore on 2.2 kernel when booting from
297             * serial console, therefore modprobe is flooding the display with
298             * "can't locate module char-major-4" messages. */
299             log = 0;
300             second_terminal = 0;
301         }
302 }
303
304 extern int
305 init_main(int argc, char * * argv)
306 {
307         const char *                rc_arguments[100];
308         const char *                arguments[100];
309         int                         run_rc = TRUE;
310         int                         j;
311         int                         pid1 = 0;
312         int                         pid2 = 0;
313         struct stat                 statbuf;
314         const char *                tty_commands[3] = { "etc/init.d/rcS", "bin/sh"};
315         int                         serial_console = 0;
316         int retval;
317
318         /*
319          * If I am started as /linuxrc instead of /sbin/init, I don't have the
320          * environment that init expects. I can't fix the signal behavior. Try
321          * to divorce from the controlling terminal with setsid(). This won't work
322          * if I am the process group leader.
323          */
324         setsid();
325
326         signal(SIGUSR1, halt_signal);
327         signal(SIGUSR2, reboot_signal);
328         signal(SIGINT, reboot_signal);
329         signal(SIGTERM, reboot_signal);
330
331         reboot(RB_DISABLE_CAD);
332
333         message(log, "%s: started. ", argv[0]);
334
335         for ( j = 1; j < argc; j++ ) {
336                 if ( strcmp(argv[j], "single") == 0 ) {
337                         run_rc = FALSE;
338                         tty_commands[0] = "bin/sh";
339                         tty_commands[1] = 0;
340                 }
341         }
342         for ( j = 0; __environ[j] != 0; j++ ) {
343                 if ( strncmp(__environ[j], "tty", 3) == 0
344                  && __environ[j][3] >= '1'
345                  && __environ[j][3] <= '2'
346                  && __environ[j][4] == '=' ) {
347                         const char * s = &__environ[j][5];
348
349                         if ( *s == 0 || strcmp(s, "off") == 0 )
350                                 s = 0;
351
352                         tty_commands[__environ[j][3] - '1'] = s;
353                 }
354                 /* Should catch the syntax of Sparc kernel console setting.   */
355                 /* The kernel does not recognize a serial console when getting*/
356                 /* console=/dev/ttySX !! */
357                 else if ( strcmp(__environ[j], "console=ttya") == 0 ) {
358                         serial_console=1;
359                 }
360                 else if ( strcmp(__environ[j], "console=ttyb") == 0 ) {
361                         serial_console=2;
362                 }
363                 /* standard console settings */
364                 else if ( strncmp(__environ[j], "console=", 8) == 0 ) {
365                         first_terminal=&(__environ[j][8]);
366                 }
367                 else if ( strncmp(__environ[j], "TERM=", 5) == 0) {
368                         term_ptr=__environ[j];
369                 }
370         }
371
372         printf("mounting /proc ...\n");
373         if (mount("/proc","/proc","proc",0,0)) {
374           perror("mounting /proc failed\n");
375         }
376         printf("\tdone.\n");
377
378         if (get_kernel_revision() >= 2*65536+1*256+71) {
379             /* if >= 2.1.71 kernel, /dev/console is not a symlink anymore:
380             * use it as primary console */
381             serial_console=-1;
382         }
383
384         /* Make sure /etc/init.d/rc exists */
385         retval= stat(tty_commands[0],&statbuf);
386         if (retval)
387             run_rc = FALSE;
388
389         configure_terminals( serial_console,  run_rc);
390
391         set_free_pages();
392
393         /* not enough memory to do anything useful*/
394         if (mem_total() < 2000) { 
395             retval= stat("/etc/fstab",&statbuf);
396             if (retval) {
397                 printf("You do not have enough RAM, sorry.\n");
398                 while (1) { sleep(1);}
399             } else { 
400               /* Try to turn on swap */
401                 static const char * const swapon_args[] = {"/bin/swapon", "-a", 0};
402                 waitfor(run("/bin/swapon", swapon_args, console, 0));
403                 if (mem_total() < 2000) { 
404                     printf("You do not have enough RAM, sorry.\n");
405                     while (1) { sleep(1);}
406                 }
407             }
408         }
409
410         /*
411          * Don't modify **argv directly, it would show up in the "ps" display.
412          * I don't want "init" to look like "rc".
413          */
414         rc_arguments[0] = tty_commands[0];
415         for ( j = 1; j < argc; j++ ) {
416                 rc_arguments[j] = argv[j];
417         }
418         rc_arguments[j] = 0;
419
420         arguments[0] = "-sh";
421         arguments[1] = 0;
422         
423         /* Ok, now launch the rc script /etc/init.d/rcS and prepare to start up
424          * some VTs on tty1 and tty2 if somebody hits enter 
425          */
426         for ( ; ; ) {
427                 int     wpid;
428                 int     status;
429
430                 if ( pid1 == 0  && tty_commands[0] ) {
431                    if ( run_rc == TRUE ) {
432                         pid1 = run(tty_commands[0], rc_arguments, first_terminal, 0);
433                    } else {
434                         pid2 = run(tty_commands[1], arguments, first_terminal, 1);
435                    }
436                 }
437                 if ( pid2 == 0 && second_terminal && tty_commands[1] ) {
438                         pid2 = run(tty_commands[1], arguments, second_terminal, 1);
439                 }
440                 wpid = wait(&status);
441                 if ( wpid > 0  && wpid != pid1) {
442                         message(log, "pid %d exited, status=%x.\n", wpid, status);
443                 }
444                 if ( wpid == pid2 ) {
445                         pid2 = 0;
446                 }
447         }
448 }
449