#include <paths.h>
#include <sys/reboot.h>
#include <sys/resource.h>
+#include <linux/vt.h>
/* Was a CONFIG_xxx option. A lot of people were building
#endif
};
-static void halt_reboot_pwoff(int sig) NORETURN;
-
/* Print a message to the specified device.
* "where" may be bitwise-or'd from L_LOG | L_CONSOLE
* NB: careful, we can be called after vfork!
*/
-#define messageD(...) do { if (DEBUG_INIT) message(__VA_ARGS__); } while (0)
+#define dbg_message(...) do { if (DEBUG_INIT) message(__VA_ARGS__); } while (0)
static void message(int where, const char *fmt, ...)
__attribute__ ((format(printf, 2, 3)));
static void message(int where, const char *fmt, ...)
{
- static int log_fd = -1;
va_list arguments;
unsigned l;
char msg[128];
l = 1 + vsnprintf(msg + 1, sizeof(msg) - 2, fmt, arguments);
if (l > sizeof(msg) - 1)
l = sizeof(msg) - 1;
- msg[l] = '\0';
va_end(arguments);
- if (ENABLE_FEATURE_INIT_SYSLOG) {
- if (where & L_LOG) {
- /* Log the message to syslogd */
- openlog("init", 0, LOG_DAEMON);
- /* don't print "\r" */
- syslog(LOG_INFO, "%s", msg + 1);
- closelog();
- }
- msg[l++] = '\n';
- msg[l] = '\0';
- } else {
+#if ENABLE_FEATURE_INIT_SYSLOG
+ msg[l] = '\0';
+ if (where & L_LOG) {
+ /* Log the message to syslogd */
+ openlog("init", 0, LOG_DAEMON);
+ /* don't print "\r" */
+ syslog(LOG_INFO, "%s", msg + 1);
+ closelog();
+ }
+ msg[l++] = '\n';
+ msg[l] = '\0';
+#else
+ {
+ static int log_fd = -1;
+
msg[l++] = '\n';
msg[l] = '\0';
/* Take full control of the log tty, and never close it.
return; /* don't print dup messages */
}
}
+#endif
if (where & L_CONSOLE) {
/* Send console messages to console so people will see them. */
}
}
-/* From <linux/serial.h> */
-struct serial_struct {
- int type;
- int line;
- unsigned int port;
- int irq;
- int flags;
- int xmit_fifo_size;
- int custom_divisor;
- int baud_base;
- unsigned short close_delay;
- char io_type;
- char reserved_char[1];
- int hub6;
- unsigned short closing_wait; /* time to wait before closing */
- unsigned short closing_wait2; /* no longer used... */
- unsigned char *iomem_base;
- unsigned short iomem_reg_shift;
- unsigned int port_high;
- unsigned long iomap_base; /* cookie passed into ioremap */
- int reserved[1];
- /* Paranoia (imagine 64bit kernel overwriting 32bit userspace stack) */
- uint32_t bbox_reserved[16];
-};
static void console_init(void)
{
- struct serial_struct sr;
+ int vtno;
char *s;
s = getenv("CONSOLE");
dup2(fd, STDOUT_FILENO);
xmove_fd(fd, STDERR_FILENO);
}
- messageD(L_LOG, "console='%s'", s);
+ dbg_message(L_LOG, "console='%s'", s);
} else {
/* Make sure fd 0,1,2 are not closed
* (so that they won't be used by future opens) */
}
s = getenv("TERM");
- if (ioctl(STDIN_FILENO, TIOCGSERIAL, &sr) == 0) {
- /* Force the TERM setting to vt102 for serial console
+ if (ioctl(STDIN_FILENO, VT_OPENQRY, &vtno) != 0) {
+ /* Not a linux terminal, probably serial console.
+ * Force the TERM setting to vt102
* if TERM is set to linux (the default) */
if (!s || strcmp(s, "linux") == 0)
putenv((char*)"TERM=vt102");
/* Open the new terminal device.
* NB: careful, we can be called after vfork! */
-static void open_stdio_to_tty(const char* tty_name, int exit_on_failure)
+static int open_stdio_to_tty(const char* tty_name)
{
/* empty tty_name means "use init's tty", else... */
if (tty_name[0]) {
if (fd) {
message(L_LOG | L_CONSOLE, "can't open %s: %s",
tty_name, strerror(errno));
- if (exit_on_failure)
- _exit(EXIT_FAILURE);
- if (DEBUG_INIT)
- _exit(2);
- /* NB: we don't reach this if we were called after vfork.
- * Thus halt_reboot_pwoff() itself needs not be vfork-safe.
- */
- halt_reboot_pwoff(SIGUSR1); /* halt the system */
+ return 0; /* failure */
}
dup2(STDIN_FILENO, STDOUT_FILENO);
dup2(STDIN_FILENO, STDERR_FILENO);
}
set_sane_term();
+ return 1; /* success */
}
/* Wrapper around exec:
{
pid_t pid;
+ /* Careful: don't be affected by a signal in vforked child */
+ sigprocmask_allsigs(SIG_BLOCK);
if (BB_MMU && (a->action_type & ASKFIRST))
pid = fork();
else
pid = vfork();
if (pid < 0)
message(L_LOG | L_CONSOLE, "can't fork");
- if (pid)
+ if (pid) {
+ sigprocmask_allsigs(SIG_UNBLOCK);
return pid; /* Parent or error */
+ }
/* Child */
/* Reset signal handlers that were set by the parent process */
-//TODO: block signals across fork(), prevent them to affect child before
-//signals are reset?
bb_signals(0
+ (1 << SIGUSR1)
+ (1 << SIGUSR2)
+ (1 << SIGHUP)
+ (1 << SIGTSTP)
, SIG_DFL);
+ sigprocmask_allsigs(SIG_UNBLOCK);
/* Create a new session and make ourself the process group leader */
setsid();
/* Open the new terminal device */
- open_stdio_to_tty(a->terminal, 1 /* - exit if open fails */);
+ if (!open_stdio_to_tty(a->terminal))
+ _exit(EXIT_FAILURE);
/* NB: on NOMMU we can't wait for input in child, so
* "askfirst" will work the same as "respawn". */
* be allowed to start a shell or whatever an init script
* specifies.
*/
- messageD(L_LOG, "waiting for enter to start '%s'"
+ dbg_message(L_LOG, "waiting for enter to start '%s'"
"(pid %d, tty '%s')\n",
a->command, getpid(), a->terminal);
full_write(STDOUT_FILENO, press_enter, sizeof(press_enter) - 1);
_exit(-1);
}
-static void delete_init_action(struct init_action *action)
-{
- struct init_action *a, **nextp;
-
- nextp = &init_action_list;
- while ((a = *nextp) != NULL) {
- if (a == action) {
- *nextp = a->next;
- free(a);
- break;
- }
- nextp = &a->next;
- }
-}
-
-static struct init_action *mark_terminated(int pid)
+static struct init_action *mark_terminated(pid_t pid)
{
struct init_action *a;
- for (a = init_action_list; a; a = a->next) {
- if (a->pid == pid) {
- a->pid = 0;
- return a;
+ if (pid > 0) {
+ for (a = init_action_list; a; a = a->next) {
+ if (a->pid == pid) {
+ a->pid = 0;
+ return a;
+ }
}
}
return NULL;
mark_terminated(wpid);
/* Unsafe. SIGTSTP handler might have wait'ed it already */
/*if (wpid == pid) break;*/
- /* More reliable */
+ /* More reliable: */
if (kill(pid, 0))
break;
}
{
struct init_action *a, **nextp;
-//BUG
-//old:
-//::shutdown:umount -a -r
-//::shutdown:swapoff -a
-//new: swapped:
-//::shutdown:swapoff -a
-//::shutdown:umount -a -r
-//on SIGHUP, new one will be loaded, but order will be wrong.
+ /* Scenario:
+ * old inittab:
+ * ::shutdown:umount -a -r
+ * ::shutdown:swapoff -a
+ * new inittab:
+ * ::shutdown:swapoff -a
+ * ::shutdown:umount -a -r
+ * On reload, we must ensure entries end up in correct order.
+ * To achieve that, if we find a matching entry, we move it
+ * to the end.
+ */
nextp = &init_action_list;
while ((a = *nextp) != NULL) {
/* Don't enter action if it's already in the list,
- * just overwrite existing one's type.
* This prevents losing running RESPAWNs.
*/
if ((strcmp(a->command, command) == 0)
&& (strcmp(a->terminal, cons) == 0)
) {
- a->action_type = action_type;
- return;
+ /* Remove from list */
+ *nextp = a->next;
+ /* Find the end of the list */
+ while (*nextp != NULL)
+ nextp = &(*nextp)->next;
+ a->next = NULL;
+ break;
}
nextp = &a->next;
}
+ if (!a)
+ a = xzalloc(sizeof(*a));
/* Append to the end of the list */
- a = xzalloc(sizeof(*a));
*nextp = a;
a->action_type = action_type;
safe_strncpy(a->command, command, sizeof(a->command));
safe_strncpy(a->terminal, cons, sizeof(a->terminal));
- messageD(L_LOG | L_CONSOLE, "command='%s' action=%d tty='%s'\n",
+ dbg_message(L_LOG | L_CONSOLE, "command='%s' action=%d tty='%s'\n",
a->command, a->action_type, a->terminal);
}
#endif
}
-static void low_level_reboot(unsigned magic) NORETURN;
-static void low_level_reboot(unsigned magic)
+static void pause_and_low_level_reboot(unsigned magic) NORETURN;
+static void pause_and_low_level_reboot(unsigned magic)
{
pid_t pid;
reboot(magic);
_exit(EXIT_SUCCESS);
}
- waitfor(pid);
while (1)
sleep(1);
}
-static void kill_all_processes(void)
+static void run_shutdown_and_kill_processes(void)
{
/* Run everything to be run at "shutdown". This is done _prior_
* to killing everything, in case people wish to use scripts to
message(L_CONSOLE | L_LOG, "The system is going down NOW!");
- /* Allow Ctrl-Alt-Del to reboot system. */
- reboot(RB_ENABLE_CAD); /* misnomer */
-
/* Send signals to every process _except_ pid 1 */
- message(L_CONSOLE | L_LOG, "Sending SIG%s to all processes", "TERM");
kill(-1, SIGTERM);
+ message(L_CONSOLE | L_LOG, "Sent SIG%s to all processes", "TERM");
sync();
sleep(1);
- message(L_CONSOLE, "Sending SIG%s to all processes", "KILL");
kill(-1, SIGKILL);
+ message(L_CONSOLE, "Sent SIG%s to all processes", "KILL");
sync();
- sleep(1);
+ /*sleep(1); - callers take care about making a pause */
}
/* Signal handling by init:
* and unmasks all signals. However, for process with PID==1,
* default action (SIG_DFL) on any signal is to ignore it,
* even for special signals SIGKILL and SIGCONT.
- * (SIGSTOP is still handled specially, at least in 2.6.20)
* Also, any signal can be caught or blocked.
+ * (but SIGSTOP is still handled specially, at least in 2.6.20)
*
* We install two kinds of handlers, "immediate" and "delayed".
*
* it to happen even somewhere inside "sysinit" would be a bit awkward.
*
* There is a tiny probability that SIGHUP and Ctrl-Alt-Del will collide
- * and only one will be remebered and acted upon.
+ * and only one will be remembered and acted upon.
*/
+static void halt_reboot_pwoff(int sig) NORETURN;
static void halt_reboot_pwoff(int sig)
{
const char *m;
unsigned rb;
- kill_all_processes();
+ run_shutdown_and_kill_processes();
m = "halt";
rb = RB_HALT_SYSTEM;
rb = RB_POWER_OFF;
}
message(L_CONSOLE, "Requesting system %s", m);
- low_level_reboot(rb);
+ pause_and_low_level_reboot(rb);
/* not reached */
}
*/
static void stop_handler(int sig UNUSED_PARAM)
{
- int saved_errno;
smallint saved_bb_got_signal;
+ int saved_errno;
- saved_errno = errno;
saved_bb_got_signal = bb_got_signal;
+ saved_errno = errno;
signal(SIGCONT, record_signo);
while (1) {
* code which is resilient against this.
*/
wpid = wait_any_nohang(NULL);
- if (wpid > 0)
- mark_terminated(wpid);
+ mark_terminated(wpid);
sleep(1);
}
signal(SIGCONT, SIG_DFL);
- bb_got_signal = saved_bb_got_signal;
errno = saved_errno;
+ bb_got_signal = saved_bb_got_signal;
}
/* Handler for QUIT - exec "restart" action,
* Thus don't need to worry about preserving errno
* and such.
*/
- kill_all_processes();
- open_stdio_to_tty(a->terminal, 0 /* - halt if open fails */);
- messageD(L_CONSOLE, "Trying to re-exec %s", a->command);
- init_exec(a->command);
- low_level_reboot(RB_HALT_SYSTEM);
+ run_shutdown_and_kill_processes();
+
+ /* Allow Ctrl-Alt-Del to reboot the system.
+ * This is how kernel sets it up for init, we follow suit.
+ */
+ reboot(RB_ENABLE_CAD); /* misnomer */
+
+ if (open_stdio_to_tty(a->terminal)) {
+ dbg_message(L_CONSOLE, "Trying to re-exec %s", a->command);
+ /* Theoretically should be safe.
+ * But in practice, kernel bugs may leave
+ * unkillable processes, and wait() may block forever.
+ * Oh well. Hoping "new" init won't be too surprised
+ * by having children it didn't create.
+ */
+ //while (wait(NULL) > 0)
+ // continue;
+ init_exec(a->command);
+ }
+ /* Open or exec failed */
+ pause_and_low_level_reboot(RB_HALT_SYSTEM);
/* not reached */
}
}
#if ENABLE_FEATURE_USE_INITTAB
static void reload_inittab(void)
{
- struct init_action *a, *tmp;
+ struct init_action *a, **nextp;
message(L_LOG, "reloading /etc/inittab");
/* Disable old entries */
- for (a = init_action_list; a; a = a->next) {
+ for (a = init_action_list; a; a = a->next)
a->action_type = ONCE;
- }
+ /* Append new entries, or modify existing entries
+ * (set a->action_type) if cmd and device name
+ * match new ones. End result: only entries with
+ * a->action_type == ONCE are stale.
+ */
parse_inittab();
- if (ENABLE_FEATURE_KILL_REMOVED) {
- /* Be nice and send SIGTERM first */
- for (a = init_action_list; a; a = a->next)
- if (a->pid != 0)
- kill(a->pid, SIGTERM);
-#if CONFIG_FEATURE_KILL_DELAY
+#if ENABLE_FEATURE_KILL_REMOVED
+ /* Kill stale entries */
+ /* Be nice and send SIGTERM first */
+ for (a = init_action_list; a; a = a->next)
+ if (a->action_type == ONCE && a->pid != 0)
+ kill(a->pid, SIGTERM);
+ if (CONFIG_FEATURE_KILL_DELAY) {
/* NB: parent will wait in NOMMU case */
if ((BB_MMU ? fork() : vfork()) == 0) { /* child */
sleep(CONFIG_FEATURE_KILL_DELAY);
for (a = init_action_list; a; a = a->next)
- if (a->pid != 0)
+ if (a->action_type == ONCE && a->pid != 0)
kill(a->pid, SIGKILL);
_exit(EXIT_SUCCESS);
}
-#endif
}
+#endif
- /* Remove old and unused entries */
- for (a = init_action_list; a; a = tmp) {
- tmp = a->next;
- if (a->action_type & (ONCE | SYSINIT | WAIT))
- delete_init_action(a);
+ /* Remove stale (ONCE) and not useful (SYSINIT,WAIT) entries */
+ nextp = &init_action_list;
+ while ((a = *nextp) != NULL) {
+ if (a->action_type & (ONCE | SYSINIT | WAIT)) {
+ *nextp = a->next;
+ free(a);
+ } else {
+ nextp = &a->next;
+ }
}
+
/* Not needed: */
/* run_actions(RESPAWN | ASKFIRST); */
/* - we return to main loop, which does this automagically */
struct sysinfo info;
if (sysinfo(&info) == 0
- && (info.mem_unit ? : 1) * (long long)info.totalram < 1024*1024
+ && (info.mem_unit ? info.mem_unit : 1) * (long long)info.totalram < 1024*1024
) {
message(L_CONSOLE, "Low memory, forcing swapon");
/* swapon -a requires /proc typically */
bb_signals_recursive_norestart((1 << SIGHUP), record_signo);
/* Now run the looping stuff for the rest of forever.
- * NB: if delayed signal happened, avoid blocking in wait().
*/
while (1) {
- pid_t wpid;
- int got_sigs;
+ int maybe_WNOHANG;
- got_sigs = check_delayed_sigs();
+ maybe_WNOHANG = check_delayed_sigs();
/* (Re)run the respawn/askfirst stuff */
run_actions(RESPAWN | ASKFIRST);
-
- got_sigs |= check_delayed_sigs();
+ maybe_WNOHANG |= check_delayed_sigs();
/* Don't consume all CPU time - sleep a bit */
sleep(1);
-
- got_sigs |= check_delayed_sigs();
-
- /* Wait for any child process to exit.
- * NB: "delayed" signals will also interrupt this wait(),
- * bb_signals_recursive_norestart() set them up for that.
- * This guarantees we won't be stuck here
- * till next orphan dies.
+ maybe_WNOHANG |= check_delayed_sigs();
+
+ /* Wait for any child process(es) to exit.
+ *
+ * If check_delayed_sigs above reported that a signal
+ * was caught, wait will be nonblocking. This ensures
+ * that if SIGHUP has reloaded inittab, respawn and askfirst
+ * actions will not be delayed until next child death.
*/
- if (got_sigs)
- goto dont_block;
- wpid = wait(NULL);
- while (wpid > 0) {
- struct init_action *a = mark_terminated(wpid);
+ if (maybe_WNOHANG)
+ maybe_WNOHANG = WNOHANG;
+ while (1) {
+ pid_t wpid;
+ struct init_action *a;
+
+ /* If signals happen _in_ the wait, they interrupt it,
+ * bb_signals_recursive_norestart set them up that way
+ */
+ wpid = waitpid(-1, NULL, maybe_WNOHANG);
+ if (wpid <= 0)
+ break;
+
+ a = mark_terminated(wpid);
if (a) {
message(L_LOG, "process '%s' (pid %d) exited. "
"Scheduling for restart.",
a->command, wpid);
}
/* See if anyone else is waiting to be reaped */
- dont_block:
- wpid = wait_any_nohang(NULL);
+ maybe_WNOHANG = WNOHANG;
}
} /* while (1) */
}