init: fix a bug where on reload order of entries might be wrong

function                                             old     new   delta
run_shutdown_and_kill_processes                        -      97     +97
pause_and_low_level_reboot                             -      48     +48
run_actions                                           81     107     +26
restart_handler                                       56      81     +25
new_init_action                                      137     150     +13
run                                                  576     579      +3
open_stdio_to_tty                                    110      98     -12
check_delayed_sigs                                   195     170     -25
waitfor                                              354     318     -36
low_level_reboot                                      53       -     -53
kill_all_processes                                   115       -    -115
------------------------------------------------------------------------------
(add/remove: 2/2 grow/shrink: 4/3 up/down: 212/-241)          Total: -29 bytes
This commit is contained in:
Denis Vlasenko 2009-01-31 18:55:54 +00:00
parent e35af56790
commit 4ae8a05b13

View File

@ -95,18 +95,15 @@ enum {
#endif #endif
}; };
static void halt_reboot_pwoff(int sig) NORETURN;
/* Print a message to the specified device. /* Print a message to the specified device.
* "where" may be bitwise-or'd from L_LOG | L_CONSOLE * "where" may be bitwise-or'd from L_LOG | L_CONSOLE
* NB: careful, we can be called after vfork! * 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, ...) static void message(int where, const char *fmt, ...)
__attribute__ ((format(printf, 2, 3))); __attribute__ ((format(printf, 2, 3)));
static void message(int where, const char *fmt, ...) static void message(int where, const char *fmt, ...)
{ {
static int log_fd = -1;
va_list arguments; va_list arguments;
unsigned l; unsigned l;
char msg[128]; char msg[128];
@ -116,20 +113,23 @@ static void message(int where, const char *fmt, ...)
l = 1 + vsnprintf(msg + 1, sizeof(msg) - 2, fmt, arguments); l = 1 + vsnprintf(msg + 1, sizeof(msg) - 2, fmt, arguments);
if (l > sizeof(msg) - 1) if (l > sizeof(msg) - 1)
l = sizeof(msg) - 1; l = sizeof(msg) - 1;
msg[l] = '\0';
va_end(arguments); va_end(arguments);
if (ENABLE_FEATURE_INIT_SYSLOG) { #if ENABLE_FEATURE_INIT_SYSLOG
if (where & L_LOG) { msg[l] = '\0';
/* Log the message to syslogd */ if (where & L_LOG) {
openlog("init", 0, LOG_DAEMON); /* Log the message to syslogd */
/* don't print "\r" */ openlog("init", 0, LOG_DAEMON);
syslog(LOG_INFO, "%s", msg + 1); /* don't print "\r" */
closelog(); syslog(LOG_INFO, "%s", msg + 1);
} closelog();
msg[l++] = '\n'; }
msg[l] = '\0'; msg[l++] = '\n';
} else { msg[l] = '\0';
#else
{
static int log_fd = -1;
msg[l++] = '\n'; msg[l++] = '\n';
msg[l] = '\0'; msg[l] = '\0';
/* Take full control of the log tty, and never close it. /* Take full control of the log tty, and never close it.
@ -153,6 +153,7 @@ static void message(int where, const char *fmt, ...)
return; /* don't print dup messages */ return; /* don't print dup messages */
} }
} }
#endif
if (where & L_CONSOLE) { if (where & L_CONSOLE) {
/* Send console messages to console so people will see them. */ /* Send console messages to console so people will see them. */
@ -199,7 +200,7 @@ static void console_init(void)
dup2(fd, STDOUT_FILENO); dup2(fd, STDOUT_FILENO);
xmove_fd(fd, STDERR_FILENO); xmove_fd(fd, STDERR_FILENO);
} }
messageD(L_LOG, "console='%s'", s); dbg_message(L_LOG, "console='%s'", s);
} else { } else {
/* Make sure fd 0,1,2 are not closed /* Make sure fd 0,1,2 are not closed
* (so that they won't be used by future opens) */ * (so that they won't be used by future opens) */
@ -261,7 +262,7 @@ static void set_sane_term(void)
/* Open the new terminal device. /* Open the new terminal device.
* NB: careful, we can be called after vfork! */ * 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... */ /* empty tty_name means "use init's tty", else... */
if (tty_name[0]) { if (tty_name[0]) {
@ -273,19 +274,13 @@ static void open_stdio_to_tty(const char* tty_name, int exit_on_failure)
if (fd) { if (fd) {
message(L_LOG | L_CONSOLE, "can't open %s: %s", message(L_LOG | L_CONSOLE, "can't open %s: %s",
tty_name, strerror(errno)); tty_name, strerror(errno));
if (exit_on_failure) return 0; /* 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 */
} }
dup2(STDIN_FILENO, STDOUT_FILENO); dup2(STDIN_FILENO, STDOUT_FILENO);
dup2(STDIN_FILENO, STDERR_FILENO); dup2(STDIN_FILENO, STDERR_FILENO);
} }
set_sane_term(); set_sane_term();
return 1; /* success */
} }
/* Wrapper around exec: /* Wrapper around exec:
@ -369,7 +364,8 @@ static pid_t run(const struct init_action *a)
setsid(); setsid();
/* Open the new terminal device */ /* 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 /* NB: on NOMMU we can't wait for input in child, so
* "askfirst" will work the same as "respawn". */ * "askfirst" will work the same as "respawn". */
@ -388,7 +384,7 @@ static pid_t run(const struct init_action *a)
* be allowed to start a shell or whatever an init script * be allowed to start a shell or whatever an init script
* specifies. * 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", "(pid %d, tty '%s')\n",
a->command, getpid(), a->terminal); a->command, getpid(), a->terminal);
full_write(STDOUT_FILENO, press_enter, sizeof(press_enter) - 1); full_write(STDOUT_FILENO, press_enter, sizeof(press_enter) - 1);
@ -422,21 +418,6 @@ static pid_t run(const struct init_action *a)
_exit(-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(int pid)
{ {
struct init_action *a; struct init_action *a;
@ -497,36 +478,44 @@ static void new_init_action(uint8_t action_type, const char *command, const char
{ {
struct init_action *a, **nextp; struct init_action *a, **nextp;
//BUG /* Scenario:
//old: * old inittab:
//::shutdown:umount -a -r * ::shutdown:umount -a -r
//::shutdown:swapoff -a * ::shutdown:swapoff -a
//new: swapped: * new inittab:
//::shutdown:swapoff -a * ::shutdown:swapoff -a
//::shutdown:umount -a -r * ::shutdown:umount -a -r
//on SIGHUP, new one will be loaded, but order will be wrong. * 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; nextp = &init_action_list;
while ((a = *nextp) != NULL) { 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,
* just overwrite existing one's type.
* This prevents losing running RESPAWNs. * This prevents losing running RESPAWNs.
*/ */
if ((strcmp(a->command, command) == 0) if ((strcmp(a->command, command) == 0)
&& (strcmp(a->terminal, cons) == 0) && (strcmp(a->terminal, cons) == 0)
) { ) {
a->action_type = action_type; /* Remove from list */
return; *nextp = a->next;
/* Find the end of the list */
while (*nextp != NULL)
nextp = &(*nextp)->next;
a->next = NULL;
break;
} }
nextp = &a->next; nextp = &a->next;
} }
if (!a)
a = xzalloc(sizeof(*a));
/* Append to the end of the list */ /* Append to the end of the list */
a = xzalloc(sizeof(*a));
*nextp = a; *nextp = a;
a->action_type = action_type; a->action_type = action_type;
safe_strncpy(a->command, command, sizeof(a->command)); safe_strncpy(a->command, command, sizeof(a->command));
safe_strncpy(a->terminal, cons, sizeof(a->terminal)); 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); a->command, a->action_type, a->terminal);
} }
@ -603,8 +592,8 @@ static void parse_inittab(void)
#endif #endif
} }
static void low_level_reboot(unsigned magic) NORETURN; static void pause_and_low_level_reboot(unsigned magic) NORETURN;
static void low_level_reboot(unsigned magic) static void pause_and_low_level_reboot(unsigned magic)
{ {
pid_t pid; pid_t pid;
@ -619,12 +608,11 @@ static void low_level_reboot(unsigned magic)
reboot(magic); reboot(magic);
_exit(EXIT_SUCCESS); _exit(EXIT_SUCCESS);
} }
waitfor(pid);
while (1) while (1)
sleep(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_ /* Run everything to be run at "shutdown". This is done _prior_
* to killing everything, in case people wish to use scripts to * to killing everything, in case people wish to use scripts to
@ -633,19 +621,16 @@ static void kill_all_processes(void)
message(L_CONSOLE | L_LOG, "The system is going down NOW!"); 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 */ /* Send signals to every process _except_ pid 1 */
message(L_CONSOLE | L_LOG, "Sending SIG%s to all processes", "TERM");
kill(-1, SIGTERM); kill(-1, SIGTERM);
message(L_CONSOLE | L_LOG, "Sent SIG%s to all processes", "TERM");
sync(); sync();
sleep(1); sleep(1);
message(L_CONSOLE, "Sending SIG%s to all processes", "KILL");
kill(-1, SIGKILL); kill(-1, SIGKILL);
message(L_CONSOLE, "Sent SIG%s to all processes", "KILL");
sync(); sync();
sleep(1); /*sleep(1); - callers take care about making a pause */
} }
/* Signal handling by init: /* Signal handling by init:
@ -684,12 +669,13 @@ static void kill_all_processes(void)
* and only one will be remebered and acted upon. * and only one will be remebered and acted upon.
*/ */
static void halt_reboot_pwoff(int sig) NORETURN;
static void halt_reboot_pwoff(int sig) static void halt_reboot_pwoff(int sig)
{ {
const char *m; const char *m;
unsigned rb; unsigned rb;
kill_all_processes(); run_shutdown_and_kill_processes();
m = "halt"; m = "halt";
rb = RB_HALT_SYSTEM; rb = RB_HALT_SYSTEM;
@ -701,7 +687,7 @@ static void halt_reboot_pwoff(int sig)
rb = RB_POWER_OFF; rb = RB_POWER_OFF;
} }
message(L_CONSOLE, "Requesting system %s", m); message(L_CONSOLE, "Requesting system %s", m);
low_level_reboot(rb); pause_and_low_level_reboot(rb);
/* not reached */ /* not reached */
} }
@ -752,11 +738,21 @@ static void restart_handler(int sig UNUSED_PARAM)
* Thus don't need to worry about preserving errno * Thus don't need to worry about preserving errno
* and such. * and such.
*/ */
kill_all_processes(); run_shutdown_and_kill_processes();
open_stdio_to_tty(a->terminal, 0 /* - halt if open fails */);
messageD(L_CONSOLE, "Trying to re-exec %s", a->command); /* Allow Ctrl-Alt-Del to reboot the system.
init_exec(a->command); * This is how kernel sets it up for init, we follow suit.
low_level_reboot(RB_HALT_SYSTEM); */
reboot(RB_ENABLE_CAD); /* misnomer */
if (open_stdio_to_tty(a->terminal)) {
dbg_message(L_CONSOLE, "Trying to re-exec %s", a->command);
while (wait(NULL) > 0)
continue;
init_exec(a->command);
}
/* Open or exec failed */
pause_and_low_level_reboot(RB_HALT_SYSTEM);
/* not reached */ /* not reached */
} }
} }
@ -764,40 +760,50 @@ static void restart_handler(int sig UNUSED_PARAM)
#if ENABLE_FEATURE_USE_INITTAB #if ENABLE_FEATURE_USE_INITTAB
static void reload_inittab(void) static void reload_inittab(void)
{ {
struct init_action *a, *tmp; struct init_action *a, **nextp;
message(L_LOG, "reloading /etc/inittab"); message(L_LOG, "reloading /etc/inittab");
/* Disable old entries */ /* 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; 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(); parse_inittab();
if (ENABLE_FEATURE_KILL_REMOVED) { if (ENABLE_FEATURE_KILL_REMOVED) {
/* Kill stale entries */
/* Be nice and send SIGTERM first */ /* Be nice and send SIGTERM first */
for (a = init_action_list; a; a = a->next) for (a = init_action_list; a; a = a->next)
if (a->pid != 0) if (a->action_type == ONCE && a->pid != 0)
kill(a->pid, SIGTERM); kill(a->pid, SIGTERM);
if (CONFIG_FEATURE_KILL_DELAY) { if (CONFIG_FEATURE_KILL_DELAY) {
/* NB: parent will wait in NOMMU case */ /* NB: parent will wait in NOMMU case */
if ((BB_MMU ? fork() : vfork()) == 0) { /* child */ if ((BB_MMU ? fork() : vfork()) == 0) { /* child */
sleep(CONFIG_FEATURE_KILL_DELAY); sleep(CONFIG_FEATURE_KILL_DELAY);
for (a = init_action_list; a; a = a->next) 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); kill(a->pid, SIGKILL);
_exit(EXIT_SUCCESS); _exit(EXIT_SUCCESS);
} }
} }
} }
/* Remove old and unused entries */ /* Remove stale (ONCE) and not useful (SYSINIT,WAIT) entries */
for (a = init_action_list; a; a = tmp) { nextp = &init_action_list;
tmp = a->next; while ((a = *nextp) != NULL) {
if (a->action_type & (ONCE | SYSINIT | WAIT)) if (a->action_type & (ONCE | SYSINIT | WAIT)) {
delete_init_action(a); *nextp = a->next;
free(a);
} else {
nextp = &a->next;
}
} }
/* Not needed: */ /* Not needed: */
/* run_actions(RESPAWN | ASKFIRST); */ /* run_actions(RESPAWN | ASKFIRST); */
/* - we return to main loop, which does this automagically */ /* - we return to main loop, which does this automagically */