More stuff
[oweals/busybox.git] / 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 int
198 get_kernel_revision()
199 {
200   FILE *f;
201   int major=0, minor=0, patch=0;
202
203   f = fopen("/proc/sys/kernel/osrelease","r");
204   fscanf(f,"%d.%d.%d",&major,&minor,&patch);
205   fclose(f);
206   return major*65536 + minor*256 + patch;
207 }
208
209
210 static void
211 shutdown_system(void)
212 {
213         static const char * const umount_args[] = {"/bin/umount", "-a", "-n", 0};
214         static const char * const swapoff_args[] = {"/bin/swapoff", "-a", 0};
215
216         message(console, "The system is going down NOW !!");
217         sync();
218         /* Allow Ctrl-Alt-Del to reboot system. */
219         reboot(RB_ENABLE_CAD);
220
221         /* Send signals to every process _except_ pid 1 */
222         message(console, "Sending SIGHUP to all processes.\r\n");
223         kill(-1, SIGHUP);
224         sleep(2);
225         sync();
226         message(console, "Sending SIGKILL to all processes.\r\n");
227         kill(-1, SIGKILL);
228         sleep(1);
229         waitfor(run("/bin/swapoff", swapoff_args, console, 0));
230         waitfor(run("/bin/umount", umount_args, console, 0));
231         sync();
232         if (get_kernel_revision() <= 2*65536+2*256+11) {
233             /* Removed  bdflush call, kupdate in kernels >2.2.11 */
234             bdflush(1, 0);
235             sync();
236         }
237 }
238
239 static void
240 halt_signal(int sig)
241 {
242     shutdown_system();
243     message(console, "The system is halted. Press CTRL-ALT-DEL or turn off power\r\n");
244     reboot( RB_HALT_SYSTEM);
245     exit(0);
246 }
247
248 static void
249 reboot_signal(int sig)
250 {
251     shutdown_system();
252     message(console, "Please stand by while rebooting the system.\r\n");
253     reboot( RB_AUTOBOOT);
254     exit(0);
255 }
256
257 static void
258 configure_terminals( int serial_cons, int single_user_mode )
259 {
260         char *tty;
261         struct serial_struct sr;
262         struct vt_stat vt;
263
264
265         switch (serial_cons) {
266         case -1:
267             /* 2.2 kernels:
268             * identify the real console backend and try to make use of it */
269             if (ioctl(0,TIOCGSERIAL,&sr) == 0) {
270                 sprintf( console, "/dev/ttyS%d", sr.line );
271                 serial_cons = sr.line+1;
272             }
273             else if (ioctl(0, VT_GETSTATE, &vt) == 0) {
274                 sprintf( console, "/dev/tty%d", vt.v_active );
275                 serial_cons = 0;
276             }
277             else {
278                 /* unknown backend: fallback to /dev/console */
279                 strcpy( console, "/dev/console" );
280                 serial_cons = 0;
281             }
282             break;
283
284         case 1:
285                 strcpy( console, "/dev/cua0" );
286                 break;
287         case 2:
288                 strcpy( console, "/dev/cua1" );
289                 break;
290         default:
291                 tty = ttyname(0);
292                 if (tty) {
293                         strcpy( console, tty );
294                         if (!strncmp( tty, "/dev/cua", 8 ))
295                                 serial_cons=1;
296                 }
297                 else
298                         /* falls back to /dev/tty1 if an error occurs */
299                         strcpy( console, default_console );
300         }
301         if (!first_terminal)
302                 first_terminal = console;
303 #if defined (__sparc__)
304         if (serial_cons > 0 && !strncmp(term_ptr,"TERM=linux",10))
305             term_ptr = "TERM=vt100";
306 #endif
307         if (serial_cons) {
308             /* disable other but the first terminal:
309             * VT is not initialized anymore on 2.2 kernel when booting from
310             * serial console, therefore modprobe is flooding the display with
311             * "can't locate module char-major-4" messages. */
312             log = 0;
313             second_terminal = 0;
314         }
315 }
316
317 extern int
318 init_main(int argc, char * * argv)
319 {
320         const char *                rc_arguments[100];
321         const char *                arguments[100];
322         int                         run_rc = TRUE;
323         int                         j;
324         int                         pid1 = 0;
325         int                         pid2 = 0;
326         struct stat                 statbuf;
327         const char *                tty_commands[3] = { "etc/init.d/rcS", "bin/sh"};
328         int                         serial_console = 0;
329         int retval;
330
331         /*
332          * If I am started as /linuxrc instead of /sbin/init, I don't have the
333          * environment that init expects. I can't fix the signal behavior. Try
334          * to divorce from the controlling terminal with setsid(). This won't work
335          * if I am the process group leader.
336          */
337         setsid();
338
339         signal(SIGUSR1, halt_signal);
340         signal(SIGUSR2, reboot_signal);
341         signal(SIGINT, reboot_signal);
342         signal(SIGTERM, reboot_signal);
343
344         reboot(RB_DISABLE_CAD);
345
346         message(log, "%s: started. ", argv[0]);
347
348         for ( j = 1; j < argc; j++ ) {
349                 if ( strcmp(argv[j], "single") == 0 ) {
350                         run_rc = FALSE;
351                         tty_commands[0] = "bin/sh";
352                         tty_commands[1] = 0;
353                 }
354         }
355         for ( j = 0; __environ[j] != 0; j++ ) {
356                 if ( strncmp(__environ[j], "tty", 3) == 0
357                  && __environ[j][3] >= '1'
358                  && __environ[j][3] <= '2'
359                  && __environ[j][4] == '=' ) {
360                         const char * s = &__environ[j][5];
361
362                         if ( *s == 0 || strcmp(s, "off") == 0 )
363                                 s = 0;
364
365                         tty_commands[__environ[j][3] - '1'] = s;
366                 }
367                 /* Should catch the syntax of Sparc kernel console setting.   */
368                 /* The kernel does not recognize a serial console when getting*/
369                 /* console=/dev/ttySX !! */
370                 else if ( strcmp(__environ[j], "console=ttya") == 0 ) {
371                         serial_console=1;
372                 }
373                 else if ( strcmp(__environ[j], "console=ttyb") == 0 ) {
374                         serial_console=2;
375                 }
376                 /* standard console settings */
377                 else if ( strncmp(__environ[j], "console=", 8) == 0 ) {
378                         first_terminal=&(__environ[j][8]);
379                 }
380                 else if ( strncmp(__environ[j], "TERM=", 5) == 0) {
381                         term_ptr=__environ[j];
382                 }
383         }
384
385         printf("mounting /proc ...\n");
386         if (mount("/proc","/proc","proc",0,0)) {
387           perror("mounting /proc failed\n");
388         }
389         printf("\tdone.\n");
390
391         if (get_kernel_revision() >= 2*65536+1*256+71) {
392             /* if >= 2.1.71 kernel, /dev/console is not a symlink anymore:
393             * use it as primary console */
394             serial_console=-1;
395         }
396
397         /* Make sure /etc/init.d/rc exists */
398         retval= stat(tty_commands[0],&statbuf);
399         if (retval)
400             run_rc = FALSE;
401
402         configure_terminals( serial_console,  run_rc);
403
404         set_free_pages();
405
406         /* not enough memory to do anything useful*/
407         if (mem_total() < 2000) { 
408             retval= stat("/etc/fstab",&statbuf);
409             if (retval) {
410                 printf("You do not have enough RAM, sorry.\n");
411                 while (1) { sleep(1);}
412             } else { 
413               /* Try to turn on swap */
414                 static const char * const swapon_args[] = {"/bin/swapon", "-a", 0};
415                 waitfor(run("/bin/swapon", swapon_args, console, 0));
416                 if (mem_total() < 2000) { 
417                     printf("You do not have enough RAM, sorry.\n");
418                     while (1) { sleep(1);}
419                 }
420             }
421         }
422
423         /*
424          * Don't modify **argv directly, it would show up in the "ps" display.
425          * I don't want "init" to look like "rc".
426          */
427         rc_arguments[0] = tty_commands[0];
428         for ( j = 1; j < argc; j++ ) {
429                 rc_arguments[j] = argv[j];
430         }
431         rc_arguments[j] = 0;
432
433         arguments[0] = "-sh";
434         arguments[1] = 0;
435         
436         /* Ok, now launch the rc script /etc/init.d/rcS and prepare to start up
437          * some VTs on tty1 and tty2 if somebody hits enter 
438          */
439         for ( ; ; ) {
440                 int     wpid;
441                 int     status;
442
443                 if ( pid1 == 0  && tty_commands[0] ) {
444                    if ( run_rc == TRUE ) {
445                         pid1 = run(tty_commands[0], rc_arguments, first_terminal, 0);
446                    } else {
447                         pid2 = run(tty_commands[1], arguments, first_terminal, 1);
448                    }
449                 }
450                 if ( pid2 == 0 && second_terminal && tty_commands[1] ) {
451                         pid2 = run(tty_commands[1], arguments, second_terminal, 1);
452                 }
453                 wpid = wait(&status);
454                 if ( wpid > 0  && wpid != pid1) {
455                         message(log, "pid %d exited, status=%x.\n", wpid, status);
456                 }
457                 if ( wpid == pid2 ) {
458                         pid2 = 0;
459                 }
460         }
461 }
462