* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
-//applet:IF_INIT(APPLET(init, BB_DIR_SBIN, BB_SUID_DROP))
-//applet:IF_FEATURE_INITRD(APPLET_ODDNAME(linuxrc, init, BB_DIR_ROOT, BB_SUID_DROP, linuxrc))
-
-//kbuild:lib-$(CONFIG_INIT) += init.o
-
//config:config INIT
//config: bool "init"
//config: default y
//config: Note that on Linux, init attempts to detect serial terminal and
//config: sets TERM to "vt102" if one is found.
+//applet:IF_INIT(APPLET(init, BB_DIR_SBIN, BB_SUID_DROP))
+//applet:IF_FEATURE_INITRD(APPLET_ODDNAME(linuxrc, init, BB_DIR_ROOT, BB_SUID_DROP, linuxrc))
+
+//kbuild:lib-$(CONFIG_INIT) += init.o
+
+#define DEBUG_SEGV_HANDLER 0
+
#include "libbb.h"
#include <syslog.h>
#include <paths.h>
#endif
#include "reboot.h" /* reboot() constants */
+#if DEBUG_SEGV_HANDLER
+# undef _GNU_SOURCE
+# define _GNU_SOURCE 1
+# undef __USE_GNU
+# define __USE_GNU 1
+# include <execinfo.h>
+# include <sys/ucontext.h>
+#endif
+
/* Used only for sanitizing purposes in set_sane_term() below. On systems where
* the baud rate is stored in a separate field, we can safely disable them. */
#ifndef CBAUD
* not fully functional init by switching it on! */
#define DEBUG_INIT 0
-#define COMMAND_SIZE 256
#define CONSOLE_NAME_SIZE 32
/* Default sysinit script. */
#ifndef INIT_SCRIPT
-#define INIT_SCRIPT "/etc/init.d/rcS"
+# define INIT_SCRIPT "/etc/init.d/rcS"
#endif
/* Each type of actions can appear many times. They will be
pid_t pid;
uint8_t action_type;
char terminal[CONSOLE_NAME_SIZE];
- char command[COMMAND_SIZE];
+ char command[1];
};
static struct init_action *init_action_list = NULL;
msg[0] = '\r';
va_start(arguments, fmt);
l = 1 + vsnprintf(msg + 1, sizeof(msg) - 2, fmt, arguments);
- if (l > sizeof(msg) - 1)
- l = sizeof(msg) - 1;
+ if (l > sizeof(msg) - 2)
+ l = sizeof(msg) - 2;
va_end(arguments);
#if ENABLE_FEATURE_INIT_SYSLOG
}
/* Wrapper around exec:
- * Takes string (max COMMAND_SIZE chars).
+ * Takes string.
* If chars like '>' detected, execs '[-]/bin/sh -c "exec ......."'.
* Otherwise splits words on whitespace, deals with leading dash,
* and uses plain exec().
*/
static void init_exec(const char *command)
{
- char *cmd[COMMAND_SIZE / 2];
- char buf[COMMAND_SIZE + 6]; /* COMMAND_SIZE+strlen("exec ")+1 */
- int dash = (command[0] == '-' /* maybe? && command[1] == '/' */);
-
+ /* +8 allows to write VLA sizes below more efficiently: */
+ unsigned command_size = strlen(command) + 8;
+ /* strlen(command) + strlen("exec ")+1: */
+ char buf[command_size];
+ /* strlen(command) / 2 + 4: */
+ char *cmd[command_size / 2];
+ int dash;
+
+ dash = (command[0] == '-' /* maybe? && command[1] == '/' */);
command += dash;
/* See if any special /bin/sh requiring characters are present */
/* Log the process name and args */
message(L_LOG, "starting pid %d, tty '%s': '%s'",
- getpid(), a->terminal, a->command);
+ getpid(), a->terminal, a->command);
/* Now run it. The new program will take over this PID,
* so nothing further in init.c should be run. */
*/
nextp = &init_action_list;
while ((a = *nextp) != NULL) {
- /* Don't enter action if it's already in the list,
+ /* Don't enter action if it's already in the list.
* This prevents losing running RESPAWNs.
*/
if (strcmp(a->command, command) == 0
while (*nextp != NULL)
nextp = &(*nextp)->next;
a->next = NULL;
- break;
+ goto append;
}
nextp = &a->next;
}
- if (!a)
- a = xzalloc(sizeof(*a));
+ a = xzalloc(sizeof(*a) + strlen(command));
+
/* Append to the end of the list */
+ append:
*nextp = a;
a->action_type = action_type;
- safe_strncpy(a->command, command, sizeof(a->command));
+ strcpy(a->command, command);
safe_strncpy(a->terminal, cons, sizeof(a->terminal));
- dbg_message(L_LOG | L_CONSOLE, "command='%s' action=%d tty='%s'\n",
+ dbg_message(L_LOG | L_CONSOLE, "command='%s' action=%x tty='%s'\n",
a->command, a->action_type, a->terminal);
}
/* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
* then parse_inittab() simply adds in some default
- * actions(i.e., runs INIT_SCRIPT and then starts a pair
+ * actions (i.e., runs INIT_SCRIPT and then starts a pair
* of "askfirst" shells). If CONFIG_FEATURE_USE_INITTAB
* _is_ defined, but /etc/inittab is missing, this
* results in the same set of default behaviors.
#endif
{
/* No inittab file - set up some default behavior */
- /* Reboot on Ctrl-Alt-Del */
- new_init_action(CTRLALTDEL, "reboot", "");
- /* Umount all filesystems on halt/reboot */
- new_init_action(SHUTDOWN, "umount -a -r", "");
- /* Swapoff on halt/reboot */
- if (ENABLE_SWAPONOFF)
- new_init_action(SHUTDOWN, "swapoff -a", "");
- /* Prepare to restart init when a QUIT is received */
- new_init_action(RESTART, "init", "");
+ /* Sysinit */
+ new_init_action(SYSINIT, INIT_SCRIPT, "");
/* Askfirst shell on tty1-4 */
new_init_action(ASKFIRST, bb_default_login_shell, "");
//TODO: VC_1 instead of ""? "" is console -> ctty problems -> angry users
new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
new_init_action(ASKFIRST, bb_default_login_shell, VC_3);
new_init_action(ASKFIRST, bb_default_login_shell, VC_4);
- /* sysinit */
- new_init_action(SYSINIT, INIT_SCRIPT, "");
+ /* Reboot on Ctrl-Alt-Del */
+ new_init_action(CTRLALTDEL, "reboot", "");
+ /* Umount all filesystems on halt/reboot */
+ new_init_action(SHUTDOWN, "umount -a -r", "");
+ /* Swapoff on halt/reboot */
+ new_init_action(SHUTDOWN, "swapoff -a", "");
+ /* Restart init when a QUIT is received */
+ new_init_action(RESTART, "init", "");
return;
}
* and only one will be remembered and acted upon.
*/
-/* The SIGUSR[12]/SIGTERM handler */
+/* The SIGPWR/SIGUSR[12]/SIGTERM handler */
static void halt_reboot_pwoff(int sig) NORETURN;
static void halt_reboot_pwoff(int sig)
{
/* Remove stale entries and SYSINIT entries.
* We never rerun SYSINIT entries anyway,
- * removing them too saves a few bytes */
+ * removing them too saves a few bytes
+ */
nextp = &init_action_list;
while ((a = *nextp) != NULL) {
- if ((a->action_type & ~SYSINIT) == 0) {
+ /*
+ * Why pid == 0 check?
+ * Process can be removed from inittab and added *later*.
+ * If we delete its entry but process still runs,
+ * duplicate is spawned when the entry is re-added.
+ */
+ if ((a->action_type & ~SYSINIT) == 0 && a->pid == 0) {
*nextp = a->next;
free(a);
} else {
}
}
+#if DEBUG_SEGV_HANDLER
+static
+void handle_sigsegv(int sig, siginfo_t *info, void *ucontext)
+{
+ long ip;
+ ucontext_t *uc;
+
+ uc = ucontext;
+ ip = uc->uc_mcontext.gregs[REG_EIP];
+ fdprintf(2, "signal:%d address:0x%lx ip:0x%lx\n",
+ sig,
+ /* this is void*, but using %p would print "(null)"
+ * even for ptrs which are not exactly 0, but, say, 0x123:
+ */
+ (long)info->si_addr,
+ ip);
+ {
+ /* glibc extension */
+ void *array[50];
+ int size;
+ size = backtrace(array, 50);
+ backtrace_symbols_fd(array, size, 2);
+ }
+ for (;;) sleep(9999);
+}
+#endif
+
int init_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int init_main(int argc UNUSED_PARAM, char **argv)
{
return kill(1, SIGHUP);
}
+#if DEBUG_SEGV_HANDLER
+ {
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = handle_sigsegv;
+ sa.sa_flags = SA_SIGINFO;
+ sigaction(SIGSEGV, &sa, NULL);
+ sigaction(SIGILL, &sa, NULL);
+ sigaction(SIGFPE, &sa, NULL);
+ sigaction(SIGBUS, &sa, NULL);
+ }
+#endif
+
if (!DEBUG_INIT) {
/* Expect to be invoked as init with PID=1 or be invoked as linuxrc */
if (getpid() != 1
message(L_CONSOLE | L_LOG, "init started: %s", bb_banner);
#endif
+#if 0
+/* It's 2013, does anyone really still depend on this? */
+/* If you do, consider adding swapon to sysinot actions then! */
/* struct sysinfo is linux-specific */
-#ifdef __linux__
+# ifdef __linux__
/* Make sure there is enough memory to do something useful. */
- if (ENABLE_SWAPONOFF) {
+ /*if (ENABLE_SWAPONOFF) - WRONG: we may have non-bbox swapon*/ {
struct sysinfo info;
if (sysinfo(&info) == 0
run_actions(SYSINIT); /* wait and removing */
}
}
+# endif
#endif
/* Check if we are supposed to be in single user mode */
/* NOTE that if CONFIG_FEATURE_USE_INITTAB is NOT defined,
* then parse_inittab() simply adds in some default
- * actions(i.e., INIT_SCRIPT and a pair
- * of "askfirst" shells */
+ * actions (i.e., INIT_SCRIPT and a pair
+ * of "askfirst" shells) */
parse_inittab();
}
strncpy(argv[0], "init", strlen(argv[0]));
/* Wipe argv[1]-argv[N] so they don't clutter the ps listing */
while (*++argv)
- memset(*argv, 0, strlen(*argv));
+ nuke_str(*argv);
/* Set up signal handlers */
if (!DEBUG_INIT) {
struct sigaction sa;
bb_signals(0
+ + (1 << SIGPWR) /* halt */
+ (1 << SIGUSR1) /* halt */
+ (1 << SIGTERM) /* reboot */
+ (1 << SIGUSR2) /* poweroff */
//usage:#define init_trivial_usage
//usage: ""
//usage:#define init_full_usage "\n\n"
-//usage: "Init is the parent of all processes"
+//usage: "Init is the first process started during boot. It never exits."
+//usage: IF_FEATURE_USE_INITTAB(
+//usage: "\n""It (re)spawns children according to /etc/inittab."
+//usage: )
+//usage: IF_NOT_FEATURE_USE_INITTAB(
+//usage: "\n""This version of init doesn't use /etc/inittab,"
+//usage: "\n""has fixed set of processed to run."
+//usage: )
//usage:
//usage:#define init_notes_usage
//usage: "This version of init is designed to be run only by the kernel.\n"
//usage: " ::shutdown:/sbin/swapoff -a\n"
//usage: " ::shutdown:/bin/umount -a -r\n"
//usage: " ::restart:/sbin/init\n"
-//usage: "\n"
-//usage: "if it detects that /dev/console is _not_ a serial console, it will also run:\n"
-//usage: "\n"
//usage: " tty2::askfirst:/bin/sh\n"
//usage: " tty3::askfirst:/bin/sh\n"
//usage: " tty4::askfirst:/bin/sh\n"
//usage: " the specified process to run on. The contents of this field are\n"
//usage: " appended to \"/dev/\" and used as-is. There is no need for this field to\n"
//usage: " be unique, although if it isn't you may have strange results. If this\n"
-//usage: " field is left blank, the controlling tty is set to the console. Also\n"
-//usage: " note that if BusyBox detects that a serial console is in use, then only\n"
-//usage: " entries whose controlling tty is either the serial console or /dev/null\n"
-//usage: " will be run. BusyBox init does nothing with utmp. We don't need no\n"
-//usage: " stinkin' utmp.\n"
+//usage: " field is left blank, then the init's stdin/out will be used.\n"
//usage: "\n"
//usage: " <runlevels>:\n"
//usage: "\n"