Initial revision
[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(
70  const char *                   program
71 ,const char * const *   arguments
72 ,const char *                   terminal
73 ,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  "Please 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 the installation system.
141                          */
142                         char    c;
143                         write(1, press_enter, sizeof(press_enter) - 1);
144                         read(0, &c, 1);
145                 }
146                 
147                 message(log, "Executing ");
148                 arg = arguments;
149                 while ( *arg != 0 )
150                         message(log, "%s ", *arg++);
151                 message(log, "\n");
152
153                 execve(program, (char * *)arguments, (char * *)environment);
154                 message(log, "%s: could not execute: %s.\r\n", program, strerror(errno));
155                 exit(-1);
156         }
157         return pid;
158 }
159
160 static int
161 mem_total()
162 {
163   char s[80];
164   char *p;
165   FILE *f;
166   const char pattern[]="MemTotal:";
167
168   f=fopen("/proc/meminfo","r");
169   while (NULL != fgets(s,79,f)) {
170     p=strstr(s, pattern);
171     if (NULL != p) {
172       fclose(f);
173       return(atoi(p+strlen(pattern)));
174     }
175   }
176   return -1;
177 }
178
179 static void
180 set_free_pages()
181 {
182   char s[80];
183   FILE *f;
184
185   f=fopen("/proc/sys/vm/freepages","r");
186   fgets(s,79,f);
187   if (atoi(s) < 32) {
188     fclose(f);
189     f=fopen("/proc/sys/vm/freepages","w");
190     fprintf(f,"30\t40\t50\n");
191     printf("\nIncreased /proc/sys/vm/freepages values to 30/40/50\n");
192   }
193   fclose(f);
194 }
195
196 static void
197 shutdown_system(int do_reboot)
198 {
199         static const char * const umount_args[] = {"/bin/umount", "-a", "-n", 0};
200
201         sync();
202         /* Allow Ctrl-Alt-Del to reboot system. */
203         reboot(RB_ENABLE_CAD);
204
205         /* Send signals to every process _except_ pid 1 */
206         message(console, "Sending SIGHUP to all processes.\r\n");
207         kill(-1, SIGHUP);
208         sleep(2);
209         sync();
210         message(console, "Sending SIGKILL to all processes.\r\n");
211         kill(-1, SIGKILL);
212         sleep(1);
213         waitfor(run("/bin/umount", umount_args, console, 0));
214         sync();
215         bdflush(1, 0);
216         sync();
217         reboot(do_reboot ?RB_AUTOBOOT : RB_HALT_SYSTEM);
218         exit(0);
219 }
220
221 static void
222 halt_signal(int sig)
223 {
224         shutdown_system(0);
225 }
226
227 static void
228 reboot_signal(int sig)
229 {
230         shutdown_system(1);
231 }
232
233 static void
234 exit_signal(int sig)
235 {
236   
237   /* initrd doesn't work anyway */
238
239   shutdown_system(1);
240
241         /* This is used on the initial ramdisk */
242
243   /*    message(log, "Init exiting.");
244         exit(0);
245         */
246 }
247
248 void
249 configure_terminals( int serial_cons );
250
251 extern int
252 init_main(struct FileInfo * i, int argc, char * * argv)
253 {
254         static const char * const       rc = "etc/rc";
255         const char *                            arguments[100];
256         int                                                     run_rc = 1;
257         int                                                     j;
258         int                                                     pid1 = 0;
259         int                                                     pid2 = 0;
260         int                                                     create_swap= -1;
261         struct stat                                     statbuf;
262 #ifndef INCLUDE_DINSTALL
263         const char *                            tty_commands[2] = { "bin/sh", "bin/sh"};
264 #else
265         const char *                            tty_commands[2] = { "sbin/dinstall", "bin/sh"};
266 #endif
267         char                                            swap[20];
268         int                                                     serial_console = 0;
269
270         /*
271          * If I am started as /linuxrc instead of /sbin/init, I don't have the
272          * environment that init expects. I can't fix the signal behavior. Try
273          * to divorce from the controlling terminal with setsid(). This won't work
274          * if I am the process group leader.
275          */
276         setsid();
277
278         signal(SIGUSR1, halt_signal);
279         signal(SIGUSR2, reboot_signal);
280         signal(SIGINT, reboot_signal);
281         signal(SIGTERM, exit_signal);
282
283         reboot(RB_DISABLE_CAD);
284
285         message(log, "%s: started. ", argv[0]);
286
287         for ( j = 1; j < argc; j++ ) {
288                 if ( strcmp(argv[j], "single") == 0 ) {
289                         run_rc = 0;
290                         tty_commands[0] = "bin/sh";
291                         tty_commands[1] = 0;
292                 }
293         }
294         for ( j = 0; __environ[j] != 0; j++ ) {
295                 if ( strncmp(__environ[j], "tty", 3) == 0
296                  && __environ[j][3] >= '1'
297                  && __environ[j][3] <= '2'
298                  && __environ[j][4] == '=' ) {
299                         const char * s = &__environ[j][5];
300
301                         if ( *s == 0 || strcmp(s, "off") == 0 )
302                                 s = 0;
303
304                         tty_commands[__environ[j][3] - '1'] = s;
305                 }
306                 /* Should catch the syntax of Sparc kernel console setting.   */
307                 /* The kernel does not recognize a serial console when getting*/
308                 /* console=/dev/ttySX !! */
309                 else if ( strcmp(__environ[j], "console=ttya") == 0 ) {
310                         serial_console=1;
311                 }
312                 else if ( strcmp(__environ[j], "console=ttyb") == 0 ) {
313                         serial_console=2;
314                 }
315                 /* standard console settings */
316                 else if ( strncmp(__environ[j], "console=", 8) == 0 ) {
317                         first_terminal=&(__environ[j][8]);
318                 }
319                 else if ( strncmp(__environ[j], "TERM=", 5) == 0) {
320                         term_ptr=__environ[j];
321                 }
322         }
323         configure_terminals( serial_console );
324
325         printf("mounting /proc ...\n");
326         if (mount("/proc","/proc","proc",0,0)) {
327           perror("mounting /proc failed\n");
328         }
329         printf("\tdone.\n");
330
331         set_free_pages();
332
333         if (mem_total() < 3500) { /* not enough memory for standard install */
334           int retval;
335           retval= stat("/etc/swappartition",&statbuf);
336           if (retval) {
337             printf("
338 You do not have enough RAM, hence you must boot using the Boot Disk
339 for Low Memory systems.
340
341 Read the instructions in the install.html file.
342 ");
343             while (1) {;}
344           } else { /* everything OK */
345             FILE *f;
346
347             f=fopen("/etc/swappartition","r");
348             fgets(swap,19,f);
349             fclose(f);
350             *(strstr(swap,"\n"))='\0';
351
352             if (swapon(swap,0)) {
353               perror("swapon failed\n");
354             } else {
355               f=fopen("/etc/swaps","w");
356               fprintf(f,"%s none swap rw 0 0",swap);
357                 fclose(f);
358               create_swap = 0;
359             }
360           }
361         }
362
363         /*
364          * Don't modify **argv directly, it would show up in the "ps" display.
365          * I don't want "init" to look like "rc".
366          */
367         arguments[0] = rc;
368         for ( j = 1; j < argc; j++ ) {
369                 arguments[j] = argv[j];
370         }
371         arguments[j] = 0;
372
373         if ( run_rc )
374                 waitfor(run(rc, arguments, console, 0));
375
376         if ( 0 == create_swap) {
377           if (unlink("/etc/swappartition")) {
378             perror("unlinking /etc/swappartition");
379           }
380         }
381
382         arguments[0] = "-sh";
383         arguments[1] = 0;
384         for ( ; ; ) {
385                 int     wpid;
386                 int     status;
387
388                 if ( pid1 == 0 && tty_commands[0] ) {
389                         /* Ask before starting a shell */
390                         /*
391                          arguments[0] = tty_commands[0];
392                          */
393                         pid1 = run(tty_commands[0], arguments, first_terminal, 0);
394                 }
395                 if ( pid2 == 0 && tty_commands[1] )
396                         pid2 = run(tty_commands[1], arguments, second_terminal, 1);
397                 wpid = wait(&status);
398                 if ( wpid > 0 ) {
399                         /* DEBUGGING */
400                         message(log, "pid %d exited, status=%x.\n", wpid, status);
401                 }
402                 if ( wpid == pid1 ) {
403                   pid1 = 0;
404                 }
405                 if ( wpid == pid2 )
406                         pid2 = 0;
407         }
408 }
409
410 void
411 configure_terminals( int serial_cons )
412 {
413         //struct stat statbuf;
414         char *tty;
415
416         switch (serial_cons) {
417         case 1:
418                 strcpy( console, "/dev/ttyS0" );
419                 break;
420         case 2:
421                 strcpy( console, "/dev/ttyS1" );
422                 break;
423         default:
424                 tty = ttyname(0);
425                 if (tty) {
426                         strcpy( console, tty );
427                         if (!strncmp( tty, "/dev/ttyS", 9 ))
428                                 serial_cons=1;
429                 }
430                 else
431                         /* falls back to /dev/tty1 if an error occurs */
432                         strcpy( console, default_console );
433         }
434         if (!first_terminal)
435                 first_terminal = console;
436         if (serial_cons && !strncmp(term_ptr,"TERM=linux",10))
437                 term_ptr = "TERM=vt100";
438 }