blob: c32124dd0d0af372e247eb355d431bfdc8320fe5 [file] [log] [blame]
Eric Andersenc4996011999-10-20 22:08:37 +00001/*
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
Eric Andersencc8ed391999-10-05 16:24:54 +000024#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>
Eric Andersencc8ed391999-10-05 16:24:54 +000039#include <sys/sysmacros.h>
Eric Andersenabc7d591999-10-19 00:27:50 +000040#include <linux/serial.h> /* for serial_struct */
41#include <sys/vt.h> /* for vt_stat */
42#include <sys/ioctl.h>
Eric Andersencc8ed391999-10-05 16:24:54 +000043
Eric Andersenabc7d591999-10-19 00:27:50 +000044static const char init_usage[] = "Used internally by the system.";
45static char console[16] = "";
46static const char* default_console = "/dev/tty2";
47static char* first_terminal = NULL;
48static const char* second_terminal = "/dev/tty2";
49static const char* log = "/dev/tty3";
50static char* term_ptr = NULL;
Eric Andersencc8ed391999-10-05 16:24:54 +000051
52static void
Eric Andersenabc7d591999-10-19 00:27:50 +000053message(const char* terminal, const char * pattern, ...)
Eric Andersencc8ed391999-10-05 16:24:54 +000054{
55 int fd;
56 FILE * con = 0;
57 va_list arguments;
58
59 /*
60 * Open the console device each time a message is printed. If the user
61 * has switched consoles, the open will get the new console. If we kept
62 * the console open, we'd always print to the same one.
63 */
Eric Andersenabc7d591999-10-19 00:27:50 +000064 if ( !terminal
65 || ((fd = open(terminal, O_WRONLY|O_NOCTTY)) < 0)
Eric Andersencc8ed391999-10-05 16:24:54 +000066 || ((con = fdopen(fd, "w")) == NULL) )
67 return;
68
69 va_start(arguments, pattern);
70 vfprintf(con, pattern, arguments);
71 va_end(arguments);
72 fclose(con);
73}
74
75static int
76waitfor(int pid)
77{
78 int status;
79 int wpid;
80
81 message(log, "Waiting for process %d.\n", pid);
82 while ( (wpid = wait(&status)) != pid ) {
83 if ( wpid > 0 ) {
84 message(
85 log
86 ,"pid %d exited, status=%x.\n"
87 ,wpid
88 ,status);
89 }
90 }
91 return wpid;
92}
93
94static int
Eric Andersen3c163821999-10-14 22:16:57 +000095run(const char* program, const char* const* arguments,
96 const char* terminal, int get_enter)
Eric Andersencc8ed391999-10-05 16:24:54 +000097{
98 static const char control_characters[] = {
99 '\003',
100 '\034',
101 '\177',
102 '\025',
103 '\004',
104 '\0',
105 '\1',
106 '\0',
107 '\021',
108 '\023',
109 '\032',
110 '\0',
111 '\022',
112 '\017',
113 '\027',
114 '\026',
115 '\0'
116 };
117
118 static char * environment[] = {
119 "HOME=/",
120 "PATH=/bin:/sbin:/usr/bin:/usr/sbin",
121 "SHELL=/bin/sh",
122 0,
123 "USER=root",
124 0
125 };
126
127 static const char press_enter[] =
Eric Andersencb6e2561999-10-16 15:48:40 +0000128 "\nPlease press Enter to activate this console. ";
Eric Andersencc8ed391999-10-05 16:24:54 +0000129
130 int pid;
131
132 environment[3]=term_ptr;
133
134 pid = fork();
135 if ( pid == 0 ) {
136 struct termios t;
137 const char * const * arg;
138
139 close(0);
140 close(1);
141 close(2);
142 setsid();
143
144 open(terminal, O_RDWR);
145 dup(0);
146 dup(0);
147 tcsetpgrp(0, getpgrp());
148
149 tcgetattr(0, &t);
150 memcpy(t.c_cc, control_characters, sizeof(control_characters));
151 t.c_line = 0;
152 t.c_iflag = ICRNL|IXON|IXOFF;
153 t.c_oflag = OPOST|ONLCR;
154 t.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOCTL|ECHOKE|IEXTEN;
155 tcsetattr(0, TCSANOW, &t);
156
157 if ( get_enter ) {
158 /*
159 * Save memory by not exec-ing anything large (like a shell)
160 * before the user wants it. This is critical if swap is not
161 * enabled and the system has low memory. Generally this will
162 * be run on the second virtual console, and the first will
Eric Andersen3c163821999-10-14 22:16:57 +0000163 * be allowed to start a shell or whatever an init script
164 * specifies.
Eric Andersencc8ed391999-10-05 16:24:54 +0000165 */
166 char c;
167 write(1, press_enter, sizeof(press_enter) - 1);
168 read(0, &c, 1);
169 }
170
171 message(log, "Executing ");
172 arg = arguments;
173 while ( *arg != 0 )
174 message(log, "%s ", *arg++);
175 message(log, "\n");
176
177 execve(program, (char * *)arguments, (char * *)environment);
178 message(log, "%s: could not execute: %s.\r\n", program, strerror(errno));
179 exit(-1);
180 }
181 return pid;
182}
183
184static int
185mem_total()
186{
Eric Andersenabc7d591999-10-19 00:27:50 +0000187 char s[80];
188 char *p;
189 FILE *f;
190 const char pattern[]="MemTotal:";
Eric Andersencc8ed391999-10-05 16:24:54 +0000191
Eric Andersenabc7d591999-10-19 00:27:50 +0000192 f=fopen("/proc/meminfo","r");
193 while (NULL != fgets(s,79,f)) {
194 p=strstr(s, pattern);
195 if (NULL != p) {
196 fclose(f);
197 return(atoi(p+strlen(pattern)));
198 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000199 }
Eric Andersenabc7d591999-10-19 00:27:50 +0000200 return -1;
Eric Andersencc8ed391999-10-05 16:24:54 +0000201}
202
203static void
204set_free_pages()
205{
Eric Andersenabc7d591999-10-19 00:27:50 +0000206 char s[80];
207 FILE *f;
Eric Andersencc8ed391999-10-05 16:24:54 +0000208
Eric Andersenabc7d591999-10-19 00:27:50 +0000209 f=fopen("/proc/sys/vm/freepages","r");
210 fgets(s,79,f);
211 if (atoi(s) < 32) {
212 fclose(f);
213 f=fopen("/proc/sys/vm/freepages","w");
214 fprintf(f,"30\t40\t50\n");
215 printf("\nIncreased /proc/sys/vm/freepages values to 30/40/50\n");
216 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000217 fclose(f);
Eric Andersencc8ed391999-10-05 16:24:54 +0000218}
219
220static void
Eric Andersenabc7d591999-10-19 00:27:50 +0000221shutdown_system(void)
Eric Andersencc8ed391999-10-05 16:24:54 +0000222{
223 static const char * const umount_args[] = {"/bin/umount", "-a", "-n", 0};
Eric Andersenabc7d591999-10-19 00:27:50 +0000224 static const char * const swapoff_args[] = {"/bin/swapoff", "-a", 0};
Eric Andersencc8ed391999-10-05 16:24:54 +0000225
Eric Andersenabc7d591999-10-19 00:27:50 +0000226 message(console, "The system is going down NOW !!");
Eric Andersencc8ed391999-10-05 16:24:54 +0000227 sync();
228 /* Allow Ctrl-Alt-Del to reboot system. */
229 reboot(RB_ENABLE_CAD);
230
231 /* Send signals to every process _except_ pid 1 */
232 message(console, "Sending SIGHUP to all processes.\r\n");
233 kill(-1, SIGHUP);
234 sleep(2);
235 sync();
236 message(console, "Sending SIGKILL to all processes.\r\n");
237 kill(-1, SIGKILL);
238 sleep(1);
Eric Andersenabc7d591999-10-19 00:27:50 +0000239 waitfor(run("/bin/swapoff", swapoff_args, console, 0));
Eric Andersencc8ed391999-10-05 16:24:54 +0000240 waitfor(run("/bin/umount", umount_args, console, 0));
241 sync();
Eric Andersenabc7d591999-10-19 00:27:50 +0000242 if (get_kernel_revision() <= 2*65536+2*256+11) {
243 /* Removed bdflush call, kupdate in kernels >2.2.11 */
244 bdflush(1, 0);
245 sync();
246 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000247}
248
249static void
250halt_signal(int sig)
251{
Eric Andersenabc7d591999-10-19 00:27:50 +0000252 shutdown_system();
253 message(console, "The system is halted. Press CTRL-ALT-DEL or turn off power\r\n");
254 reboot( RB_HALT_SYSTEM);
255 exit(0);
Eric Andersencc8ed391999-10-05 16:24:54 +0000256}
257
258static void
259reboot_signal(int sig)
260{
Eric Andersenabc7d591999-10-19 00:27:50 +0000261 shutdown_system();
262 message(console, "Please stand by while rebooting the system.\r\n");
263 reboot( RB_AUTOBOOT);
264 exit(0);
Eric Andersencc8ed391999-10-05 16:24:54 +0000265}
266
267static void
Eric Andersenabc7d591999-10-19 00:27:50 +0000268configure_terminals( int serial_cons, int single_user_mode )
Eric Andersencc8ed391999-10-05 16:24:54 +0000269{
Eric Andersen3c163821999-10-14 22:16:57 +0000270 char *tty;
Eric Andersenabc7d591999-10-19 00:27:50 +0000271 struct serial_struct sr;
272 struct vt_stat vt;
273
Eric Andersen3c163821999-10-14 22:16:57 +0000274
275 switch (serial_cons) {
Eric Andersenabc7d591999-10-19 00:27:50 +0000276 case -1:
277 /* 2.2 kernels:
278 * identify the real console backend and try to make use of it */
279 if (ioctl(0,TIOCGSERIAL,&sr) == 0) {
280 sprintf( console, "/dev/ttyS%d", sr.line );
281 serial_cons = sr.line+1;
282 }
283 else if (ioctl(0, VT_GETSTATE, &vt) == 0) {
284 sprintf( console, "/dev/tty%d", vt.v_active );
285 serial_cons = 0;
286 }
287 else {
288 /* unknown backend: fallback to /dev/console */
289 strcpy( console, "/dev/console" );
290 serial_cons = 0;
291 }
292 break;
293
Eric Andersen3c163821999-10-14 22:16:57 +0000294 case 1:
Eric Andersenabc7d591999-10-19 00:27:50 +0000295 strcpy( console, "/dev/cua0" );
Eric Andersen3c163821999-10-14 22:16:57 +0000296 break;
297 case 2:
Eric Andersenabc7d591999-10-19 00:27:50 +0000298 strcpy( console, "/dev/cua1" );
Eric Andersen3c163821999-10-14 22:16:57 +0000299 break;
300 default:
301 tty = ttyname(0);
302 if (tty) {
303 strcpy( console, tty );
Eric Andersenabc7d591999-10-19 00:27:50 +0000304 if (!strncmp( tty, "/dev/cua", 8 ))
Eric Andersen3c163821999-10-14 22:16:57 +0000305 serial_cons=1;
306 }
307 else
308 /* falls back to /dev/tty1 if an error occurs */
309 strcpy( console, default_console );
310 }
311 if (!first_terminal)
312 first_terminal = console;
Eric Andersene77ae3a1999-10-19 20:03:34 +0000313#if defined (__sparc__)
Eric Andersenabc7d591999-10-19 00:27:50 +0000314 if (serial_cons > 0 && !strncmp(term_ptr,"TERM=linux",10))
315 term_ptr = "TERM=vt100";
316#endif
317 if (serial_cons) {
318 /* disable other but the first terminal:
319 * VT is not initialized anymore on 2.2 kernel when booting from
320 * serial console, therefore modprobe is flooding the display with
321 * "can't locate module char-major-4" messages. */
322 log = 0;
323 second_terminal = 0;
324 }
Eric Andersen3c163821999-10-14 22:16:57 +0000325}
Eric Andersencc8ed391999-10-05 16:24:54 +0000326
327extern int
Eric Andersen2c103011999-10-13 22:56:11 +0000328init_main(int argc, char * * argv)
Eric Andersencc8ed391999-10-05 16:24:54 +0000329{
Eric Andersenabc7d591999-10-19 00:27:50 +0000330 const char * rc_arguments[100];
Eric Andersen3c163821999-10-14 22:16:57 +0000331 const char * arguments[100];
Eric Andersenabc7d591999-10-19 00:27:50 +0000332 int run_rc = TRUE;
Eric Andersen3c163821999-10-14 22:16:57 +0000333 int j;
334 int pid1 = 0;
335 int pid2 = 0;
Eric Andersen3c163821999-10-14 22:16:57 +0000336 struct stat statbuf;
Eric Andersenabc7d591999-10-19 00:27:50 +0000337 const char * tty_commands[3] = { "etc/init.d/rcS", "bin/sh"};
Eric Andersen3c163821999-10-14 22:16:57 +0000338 int serial_console = 0;
Eric Andersenabc7d591999-10-19 00:27:50 +0000339 int retval;
Eric Andersencc8ed391999-10-05 16:24:54 +0000340
341 /*
342 * If I am started as /linuxrc instead of /sbin/init, I don't have the
343 * environment that init expects. I can't fix the signal behavior. Try
344 * to divorce from the controlling terminal with setsid(). This won't work
345 * if I am the process group leader.
346 */
347 setsid();
348
349 signal(SIGUSR1, halt_signal);
350 signal(SIGUSR2, reboot_signal);
351 signal(SIGINT, reboot_signal);
Eric Andersenabc7d591999-10-19 00:27:50 +0000352 signal(SIGTERM, reboot_signal);
Eric Andersencc8ed391999-10-05 16:24:54 +0000353
354 reboot(RB_DISABLE_CAD);
355
356 message(log, "%s: started. ", argv[0]);
357
358 for ( j = 1; j < argc; j++ ) {
359 if ( strcmp(argv[j], "single") == 0 ) {
Eric Andersenabc7d591999-10-19 00:27:50 +0000360 run_rc = FALSE;
Eric Andersencc8ed391999-10-05 16:24:54 +0000361 tty_commands[0] = "bin/sh";
362 tty_commands[1] = 0;
363 }
364 }
365 for ( j = 0; __environ[j] != 0; j++ ) {
366 if ( strncmp(__environ[j], "tty", 3) == 0
367 && __environ[j][3] >= '1'
368 && __environ[j][3] <= '2'
369 && __environ[j][4] == '=' ) {
370 const char * s = &__environ[j][5];
371
372 if ( *s == 0 || strcmp(s, "off") == 0 )
373 s = 0;
374
375 tty_commands[__environ[j][3] - '1'] = s;
376 }
377 /* Should catch the syntax of Sparc kernel console setting. */
378 /* The kernel does not recognize a serial console when getting*/
379 /* console=/dev/ttySX !! */
380 else if ( strcmp(__environ[j], "console=ttya") == 0 ) {
381 serial_console=1;
382 }
383 else if ( strcmp(__environ[j], "console=ttyb") == 0 ) {
384 serial_console=2;
385 }
386 /* standard console settings */
387 else if ( strncmp(__environ[j], "console=", 8) == 0 ) {
388 first_terminal=&(__environ[j][8]);
389 }
390 else if ( strncmp(__environ[j], "TERM=", 5) == 0) {
391 term_ptr=__environ[j];
392 }
393 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000394
395 printf("mounting /proc ...\n");
396 if (mount("/proc","/proc","proc",0,0)) {
397 perror("mounting /proc failed\n");
398 }
399 printf("\tdone.\n");
400
Eric Andersenabc7d591999-10-19 00:27:50 +0000401 if (get_kernel_revision() >= 2*65536+1*256+71) {
402 /* if >= 2.1.71 kernel, /dev/console is not a symlink anymore:
403 * use it as primary console */
404 serial_console=-1;
405 }
406
407 /* Make sure /etc/init.d/rc exists */
408 retval= stat(tty_commands[0],&statbuf);
409 if (retval)
410 run_rc = FALSE;
411
412 configure_terminals( serial_console, run_rc);
413
Eric Andersencc8ed391999-10-05 16:24:54 +0000414 set_free_pages();
415
Eric Andersen3c163821999-10-14 22:16:57 +0000416 /* not enough memory to do anything useful*/
417 if (mem_total() < 2000) {
Eric Andersenabc7d591999-10-19 00:27:50 +0000418 retval= stat("/etc/fstab",&statbuf);
419 if (retval) {
420 printf("You do not have enough RAM, sorry.\n");
421 while (1) { sleep(1);}
422 } else {
423 /* Try to turn on swap */
424 static const char * const swapon_args[] = {"/bin/swapon", "-a", 0};
425 waitfor(run("/bin/swapon", swapon_args, console, 0));
426 if (mem_total() < 2000) {
427 printf("You do not have enough RAM, sorry.\n");
428 while (1) { sleep(1);}
429 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000430 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000431 }
432
433 /*
434 * Don't modify **argv directly, it would show up in the "ps" display.
435 * I don't want "init" to look like "rc".
436 */
Eric Andersenabc7d591999-10-19 00:27:50 +0000437 rc_arguments[0] = tty_commands[0];
Eric Andersencc8ed391999-10-05 16:24:54 +0000438 for ( j = 1; j < argc; j++ ) {
Eric Andersenabc7d591999-10-19 00:27:50 +0000439 rc_arguments[j] = argv[j];
Eric Andersencc8ed391999-10-05 16:24:54 +0000440 }
Eric Andersenabc7d591999-10-19 00:27:50 +0000441 rc_arguments[j] = 0;
Eric Andersencc8ed391999-10-05 16:24:54 +0000442
443 arguments[0] = "-sh";
444 arguments[1] = 0;
Eric Andersenabc7d591999-10-19 00:27:50 +0000445
446 /* Ok, now launch the rc script /etc/init.d/rcS and prepare to start up
447 * some VTs on tty1 and tty2 if somebody hits enter
448 */
Eric Andersencc8ed391999-10-05 16:24:54 +0000449 for ( ; ; ) {
450 int wpid;
451 int status;
452
Eric Andersenabc7d591999-10-19 00:27:50 +0000453 if ( pid1 == 0 && tty_commands[0] ) {
454 if ( run_rc == TRUE ) {
455 pid1 = run(tty_commands[0], rc_arguments, first_terminal, 0);
456 } else {
457 pid2 = run(tty_commands[1], arguments, first_terminal, 1);
458 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000459 }
Eric Andersenabc7d591999-10-19 00:27:50 +0000460 if ( pid2 == 0 && second_terminal && tty_commands[1] ) {
Eric Andersencc8ed391999-10-05 16:24:54 +0000461 pid2 = run(tty_commands[1], arguments, second_terminal, 1);
Eric Andersen3c163821999-10-14 22:16:57 +0000462 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000463 wpid = wait(&status);
Eric Andersenabc7d591999-10-19 00:27:50 +0000464 if ( wpid > 0 && wpid != pid1) {
Eric Andersencc8ed391999-10-05 16:24:54 +0000465 message(log, "pid %d exited, status=%x.\n", wpid, status);
466 }
Eric Andersenabc7d591999-10-19 00:27:50 +0000467 if ( wpid == pid2 ) {
Eric Andersencc8ed391999-10-05 16:24:54 +0000468 pid2 = 0;
Eric Andersenabc7d591999-10-19 00:27:50 +0000469 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000470 }
471}
472