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