2 * Mini init implementation for busybox
5 * Copyright (C) 1995, 1996 by Bruce Perens <bruce@pixar.com>.
6 * Adjusted by so many folks, it's impossible to keep track.
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.
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.
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
32 #include <sys/types.h>
33 #include <sys/fcntl.h>
36 #include <sys/mount.h>
37 #include <sys/reboot.h>
38 #include <sys/kdaemon.h>
39 #include <sys/sysmacros.h>
40 #include <linux/serial.h> /* for serial_struct */
41 #include <sys/vt.h> /* for vt_stat */
42 #include <sys/ioctl.h>
44 static const char init_usage[] = "Used internally by the system.";
45 static char console[16] = "";
46 static const char* default_console = "/dev/tty2";
47 static char* first_terminal = NULL;
48 static const char* second_terminal = "/dev/tty2";
49 static const char* log = "/dev/tty3";
50 static char* term_ptr = NULL;
53 message(const char* terminal, const char * pattern, ...)
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.
65 || ((fd = open(terminal, O_WRONLY|O_NOCTTY)) < 0)
66 || ((con = fdopen(fd, "w")) == NULL) )
69 va_start(arguments, pattern);
70 vfprintf(con, pattern, arguments);
81 message(log, "Waiting for process %d.\n", pid);
82 while ( (wpid = wait(&status)) != pid ) {
86 ,"pid %d exited, status=%x.\n"
95 run(const char* program, const char* const* arguments,
96 const char* terminal, int get_enter)
98 static const char control_characters[] = {
118 static char * environment[] = {
120 "PATH=/bin:/sbin:/usr/bin:/usr/sbin",
127 static const char press_enter[] =
128 "\nPlease press Enter to activate this console. ";
132 environment[3]=term_ptr;
137 const char * const * arg;
144 open(terminal, O_RDWR);
147 tcsetpgrp(0, getpgrp());
150 memcpy(t.c_cc, control_characters, sizeof(control_characters));
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);
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
163 * be allowed to start a shell or whatever an init script
167 write(1, press_enter, sizeof(press_enter) - 1);
171 message(log, "Executing ");
174 message(log, "%s ", *arg++);
177 execve(program, (char * *)arguments, (char * *)environment);
178 message(log, "%s: could not execute: %s.\r\n", program, strerror(errno));
190 const char pattern[]="MemTotal:";
192 f=fopen("/proc/meminfo","r");
193 while (NULL != fgets(s,79,f)) {
194 p=strstr(s, pattern);
197 return(atoi(p+strlen(pattern)));
209 f=fopen("/proc/sys/vm/freepages","r");
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");
221 shutdown_system(void)
223 static const char * const umount_args[] = {"/bin/umount", "-a", "-n", 0};
224 static const char * const swapoff_args[] = {"/bin/swapoff", "-a", 0};
226 message(console, "The system is going down NOW !!");
228 /* Allow Ctrl-Alt-Del to reboot system. */
229 reboot(RB_ENABLE_CAD);
231 /* Send signals to every process _except_ pid 1 */
232 message(console, "Sending SIGHUP to all processes.\r\n");
236 message(console, "Sending SIGKILL to all processes.\r\n");
239 waitfor(run("/bin/swapoff", swapoff_args, console, 0));
240 waitfor(run("/bin/umount", umount_args, console, 0));
242 if (get_kernel_revision() <= 2*65536+2*256+11) {
243 /* Removed bdflush call, kupdate in kernels >2.2.11 */
253 message(console, "The system is halted. Press CTRL-ALT-DEL or turn off power\r\n");
254 reboot( RB_HALT_SYSTEM);
259 reboot_signal(int sig)
262 message(console, "Please stand by while rebooting the system.\r\n");
263 reboot( RB_AUTOBOOT);
268 configure_terminals( int serial_cons, int single_user_mode )
271 struct serial_struct sr;
275 switch (serial_cons) {
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;
283 else if (ioctl(0, VT_GETSTATE, &vt) == 0) {
284 sprintf( console, "/dev/tty%d", vt.v_active );
288 /* unknown backend: fallback to /dev/console */
289 strcpy( console, "/dev/console" );
295 strcpy( console, "/dev/cua0" );
298 strcpy( console, "/dev/cua1" );
303 strcpy( console, tty );
304 if (!strncmp( tty, "/dev/cua", 8 ))
308 /* falls back to /dev/tty1 if an error occurs */
309 strcpy( console, default_console );
312 first_terminal = console;
313 #if defined (__sparc__)
314 if (serial_cons > 0 && !strncmp(term_ptr,"TERM=linux",10))
315 term_ptr = "TERM=vt100";
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. */
328 init_main(int argc, char * * argv)
330 const char * rc_arguments[100];
331 const char * arguments[100];
337 const char * tty_commands[3] = { "etc/init.d/rcS", "bin/sh"};
338 int serial_console = 0;
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.
349 signal(SIGUSR1, halt_signal);
350 signal(SIGUSR2, reboot_signal);
351 signal(SIGINT, reboot_signal);
352 signal(SIGTERM, reboot_signal);
354 reboot(RB_DISABLE_CAD);
356 message(log, "%s: started. ", argv[0]);
358 for ( j = 1; j < argc; j++ ) {
359 if ( strcmp(argv[j], "single") == 0 ) {
361 tty_commands[0] = "bin/sh";
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];
372 if ( *s == 0 || strcmp(s, "off") == 0 )
375 tty_commands[__environ[j][3] - '1'] = s;
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 ) {
383 else if ( strcmp(__environ[j], "console=ttyb") == 0 ) {
386 /* standard console settings */
387 else if ( strncmp(__environ[j], "console=", 8) == 0 ) {
388 first_terminal=&(__environ[j][8]);
390 else if ( strncmp(__environ[j], "TERM=", 5) == 0) {
391 term_ptr=__environ[j];
395 printf("mounting /proc ...\n");
396 if (mount("/proc","/proc","proc",0,0)) {
397 perror("mounting /proc failed\n");
401 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 */
407 /* Make sure /etc/init.d/rc exists */
408 retval= stat(tty_commands[0],&statbuf);
412 configure_terminals( serial_console, run_rc);
416 /* not enough memory to do anything useful*/
417 if (mem_total() < 2000) {
418 retval= stat("/etc/fstab",&statbuf);
420 printf("You do not have enough RAM, sorry.\n");
421 while (1) { sleep(1);}
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);}
434 * Don't modify **argv directly, it would show up in the "ps" display.
435 * I don't want "init" to look like "rc".
437 rc_arguments[0] = tty_commands[0];
438 for ( j = 1; j < argc; j++ ) {
439 rc_arguments[j] = argv[j];
443 arguments[0] = "-sh";
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
453 if ( pid1 == 0 && tty_commands[0] ) {
454 if ( run_rc == TRUE ) {
455 pid1 = run(tty_commands[0], rc_arguments, first_terminal, 0);
457 pid2 = run(tty_commands[1], arguments, first_terminal, 1);
460 if ( pid2 == 0 && second_terminal && tty_commands[1] ) {
461 pid2 = run(tty_commands[1], arguments, second_terminal, 1);
463 wpid = wait(&status);
464 if ( wpid > 0 && wpid != pid1) {
465 message(log, "pid %d exited, status=%x.\n", wpid, status);
467 if ( wpid == pid2 ) {