/* * Mini init implementation for busybox * * * Copyright (C) 1995, 1996 by Bruce Perens . * Adjusted by so many folks, it's impossible to keep track. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "internal.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for serial_struct */ #include /* for vt_stat */ #include #define DEBUG_INIT #define CONSOLE "/dev/console" /* Logical system console */ #define VT_PRIMARY "/dev/tty0" /* Virtual console master */ #define VT_SECONDARY "/dev/tty1" /* Virtual console master */ #define VT_LOG "/dev/tty2" /* Virtual console master */ #define SHELL "/bin/sh" /* Default shell */ #define INITSCRIPT "/etc/init.d/rcS" /* Initscript. */ #define PATH_DEFAULT "PATH=/usr/local/sbin:/sbin:/bin:/usr/sbin:/usr/bin" static char *console = CONSOLE; static char *second_terminal = "/dev/tty2"; static char *log = "/dev/tty3"; /* try to open up the specified device */ int device_open(char *device, int mode) { int m, f, fd = -1; mode = m | O_NONBLOCK; /* Retry up to 5 times */ for (f = 0; f < 5; f++) if ((fd = open(device, m)) >= 0) break; if (fd < 0) return fd; /* Set original flags. */ if (m != mode) fcntl(fd, F_SETFL, mode); return fd; } /* print a message to the specified device */ void message(char *device, char *fmt, ...) { int fd; va_list arguments; if ((fd = device_open(device, O_WRONLY | O_NOCTTY | O_NDELAY)) >= 0) { va_start(arguments, fmt); vdprintf(fd, fmt, arguments); va_end(arguments); close(fd); } else vprintf(fmt, arguments); } /* Set terminal settings to reasonable defaults */ void set_term() { int fd; struct termios tty; if ((fd = device_open(console, O_RDWR | O_NOCTTY)) < 0) { message(log, "can't open %s\n", console); return; } ioctl(fd, TCGETS, &tty); tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD; tty.c_cflag |= HUPCL | CLOCAL; tty.c_cc[VINTR] = 3; tty.c_cc[VQUIT] = 28; tty.c_cc[VERASE] = 127; tty.c_cc[VKILL] = 24; tty.c_cc[VEOF] = 4; tty.c_cc[VTIME] = 0; tty.c_cc[VMIN] = 1; tty.c_cc[VSTART] = 17; tty.c_cc[VSTOP] = 19; tty.c_cc[VSUSP] = 26; /* Set pre and post processing */ tty.c_iflag = IGNPAR | ICRNL | IXON | IXANY; tty.c_oflag = OPOST | ONLCR; tty.c_lflag = ISIG | ICANON | ECHO | ECHOCTL | ECHOPRT | ECHOKE; /* Now set the terminal line. */ ioctl(fd, TCSETS, &tty); close(fd); } static int mem_total() { char s[80]; char *p; FILE *f; const char pattern[] = "MemTotal:"; f = fopen("/proc/meminfo", "r"); while (NULL != fgets(s, 79, f)) { p = strstr(s, pattern); if (NULL != p) { fclose(f); return (atoi(p + strlen(pattern))); } } return -1; } static void set_free_pages() { char s[80]; FILE *f; f = fopen("/proc/sys/vm/freepages", "r"); fgets(s, 79, f); if (atoi(s) < 32) { fclose(f); f = fopen("/proc/sys/vm/freepages", "w"); fprintf(f, "30\t40\t50\n"); printf("\nIncreased /proc/sys/vm/freepages values to 30/40/50\n"); } fclose(f); } static void console_init() { int fd; int tried_devcons = 0; int tried_vtmaster = 0; char *s; if ((s = getenv("CONSOLE")) != NULL) console = s; else { console = CONSOLE; tried_devcons++; } while ((fd = open(console, O_RDONLY | O_NONBLOCK)) < 0) { if (!tried_devcons) { tried_devcons++; console = CONSOLE; continue; } if (!tried_vtmaster) { tried_vtmaster++; console = VT_PRIMARY; continue; } break; } if (fd < 0) console = "/dev/null"; else close(fd); } static int waitfor(int pid) { int status, wpid; message(log, "Waiting for process %d.\n", pid); while ((wpid = wait(&status)) != pid) { if (wpid > 0) message(log, "pid %d exited, status=%x.\n", wpid, status); } return wpid; } static int run(const char *command, char *terminal, int get_enter) { int f, pid; char *args[16]; char buf[256]; char *ptr; static const char press_enter[] = "\nPlease press Enter to activate this console. "; /* Make a proper command from the command string */ strcpy(buf, command); ptr = buf; for (f = 1; f < 15; f++) { /* Skip white space */ while (*ptr == ' ' || *ptr == '\t') ptr++; args[f] = ptr; /* May be trailing space.. */ if (*ptr == 0) break; /* Skip this `word' */ while (*ptr && *ptr != ' ' && *ptr != '\t' && *ptr != '#') ptr++; /* If end-of-line, break */ if (*ptr == '#' || *ptr == 0) { f++; *ptr = 0; break; } /* End word with \0 and continue */ args[f] = NULL; } args[0] = args[1]; if ((pid = fork()) == 0) { /* Clean up */ close(0); close(1); close(2); setsid(); if ((f = device_open(terminal, O_RDWR | O_NOCTTY)) < 0) { message(log, "open(%s) failed: %s\n", terminal, strerror(errno)); return -1; } dup(f); dup(f); tcsetpgrp(0, getpgrp()); set_term(); if (get_enter) { /* * Save memory by not exec-ing anything large (like a shell) * before the user wants it. This is critical if swap is not * enabled and the system has low memory. Generally this will * be run on the second virtual console, and the first will * be allowed to start a shell or whatever an init script * specifies. */ char c; write(1, press_enter, sizeof(press_enter) - 1); read(0, &c, 1); message(console, "Got an enter\r\n"); } /* Log the process name and args */ message(console, "Executing "); message(console, "'%s'\r\n", command); /* Now run it. This should take over the PID, so nothing * further in init.c should be run by this PID. */ execvp(args[1], args + 1); message(console, "Hmm. Trying as a script.\r\n"); /* If shell scripts are not executed, force the issue */ if (errno == ENOEXEC) { char buf[256]; args[1] = SHELL; args[2] = "-c"; strcpy(buf, "exec "); strcat(buf, command); args[3] = buf; args[4] = NULL; execvp(args[1], args + 1); } message(console, "Could not execute '%s'\n", command); exit(-1); } return pid; } #ifndef DEBUG_INIT static void shutdown_system(void) { message(console, "The system is going down NOW !!\r\n"); sync(); /* Allow Ctrl-Alt-Del to reboot system. */ reboot(RB_ENABLE_CAD); /* Send signals to every process _except_ pid 1 */ message(console, "Sending SIGHUP to all processes.\r\n"); kill(-1, SIGHUP); sleep(2); sync(); message(console, "Sending SIGKILL to all processes.\r\n"); kill(-1, SIGKILL); sleep(1); waitfor(run("/bin/swapoff -a", console, 0)); waitfor(run("/bin/umount -a -n", console, 0)); sync(); if (get_kernel_revision() <= 2 * 65536 + 2 * 256 + 11) { /* Removed bdflush call, kupdate in kernels >2.2.11 */ bdflush(1, 0); sync(); } } static void halt_signal(int sig) { shutdown_system(); message(console, "The system is halted. Press CTRL-ALT-DEL or turn off power\r\n"); reboot(RB_POWER_OFF); exit(0); } static void reboot_signal(int sig) { shutdown_system(); message(console, "Please stand by while rebooting the system.\r\n"); reboot(RB_AUTOBOOT); exit(0); } #endif extern int init_main(int argc, char **argv) { int run_rc = TRUE; int pid1 = 0; int pid2 = 0; struct stat statbuf; const char *init_commands = SHELL "-c exec " INITSCRIPT; const char *shell_commands = SHELL; const char *tty0_commands = init_commands; const char *tty1_commands = shell_commands; char *hello_msg_format = "init started: BusyBox v%s (%s) multi-call binary\r\n"; const char *no_memory = "Sorry, your computer does not have enough memory.\r\n"; #ifndef DEBUG_INIT /* Set up sig handlers */ signal(SIGUSR1, halt_signal); signal(SIGSEGV, halt_signal); signal(SIGPWR, halt_signal); signal(SIGALRM, halt_signal); signal(SIGHUP, halt_signal); signal(SIGUSR2, reboot_signal); signal(SIGINT, reboot_signal); signal(SIGTERM, reboot_signal); #endif /* Figure out where the default console should be */ console_init(); /* Turn off rebooting via CTL-ALT-DEL -- we get a * SIGINT on CAD so we can shut things down gracefully... */ #ifndef DEBUG_INIT reboot(RB_DISABLE_CAD); #endif /* Close whatever files are open, and reset the console. */ close(0); close(1); close(2); set_term(); setsid(); /* Make sure PATH is set to something sane */ if (getenv("PATH") == NULL) putenv(PATH_DEFAULT); /* Hello world */ message(log, hello_msg_format, BB_VER, BB_BT); message(console, hello_msg_format, BB_VER, BB_BT); /* Mount /proc */ if (mount("/proc", "/proc", "proc", 0, 0)) { message(log, "Mounting /proc: failed!\n"); message(console, "Mounting /proc: failed!\r\n"); } else { message(console, "Mounting /proc: done.\r\n"); } /* Make sure there is enough memory to do something useful */ set_free_pages(); if (mem_total() < 2000) { int retval; retval = stat("/etc/fstab", &statbuf); if (retval) { message(console, "%s", no_memory); while (1) { sleep(1); } } else { /* Try to turn on swap */ waitfor(run("/bin/swapon -a", console, 0)); if (mem_total() < 2000) { message(console, "%s", no_memory); while (1) { sleep(1); } } } } /* Check if we are supposed to be in single user mode */ if ( argc > 1 && (!strcmp(argv[1], "single") || !strcmp(argv[1], "-s") || !strcmp(argv[1], "1"))) { run_rc = FALSE; tty0_commands = shell_commands; tty1_commands = 0; } /* Make sure an init script exists before trying to run it */ if (run_rc == TRUE && stat(INITSCRIPT, &statbuf)) { tty0_commands = shell_commands; tty1_commands = shell_commands; } /* Ok, now launch the rc script and/or prepare to * start up some VTs if somebody hits enter... */ for (;;) { int wpid; int status; if (pid1 == 0 && tty0_commands) { pid1 = run(tty0_commands, console, 1); } if (pid2 == 0 && tty1_commands) { pid2 = run(tty1_commands, second_terminal, 1); } wpid = wait(&status); if (wpid > 0 && wpid != pid1) { message(log, "pid %d exited, status=%x.\n", wpid, status); } /* Don't respawn an init script if it exits */ if (run_rc == FALSE && wpid == pid1) { pid1 = 0; } if (wpid == pid2) { pid2 = 0; } sleep(1); } }