X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=init%2Finit.c;h=f7eb8f34ba3510d1b463e8acc1b1d18a97795e07;hb=ce34cccf28d3b46defbc048f8748d0c870ac3178;hp=baa5a50009470f6476ae0908f011bd186b4963c9;hpb=d7e2e127a93afe2a88922ef94fa00fe8db39d834;p=oweals%2Fbusybox.git diff --git a/init/init.c b/init/init.c index baa5a5000..f7eb8f34b 100644 --- a/init/init.c +++ b/init/init.c @@ -12,16 +12,12 @@ #include "libbb.h" #include #include - -#if ENABLE_FEATURE_INIT_SYSLOG -# include -#endif +#include #define INIT_BUFFS_SIZE 256 #define CONSOLE_NAME_SIZE 32 #define MAXENV 16 /* Number of env. vars */ -#if ENABLE_FEATURE_INIT_COREDUMPS /* * When a file named CORE_ENABLE_FLAG_FILE exists, setrlimit is called * before processes are spawned to set core file size as unlimited. @@ -30,7 +26,6 @@ */ #define CORE_ENABLE_FLAG_FILE "/.init_enable_core" #include -#endif #define INITTAB "/etc/inittab" /* inittab file location */ #ifndef INIT_SCRIPT @@ -68,12 +63,8 @@ struct init_action { /* Static variables */ static struct init_action *init_action_list = NULL; -#if !ENABLE_FEATURE_INIT_SYSLOG static const char *log_console = VC_5; -#endif -#if !ENABLE_DEBUG_INIT static sig_atomic_t got_cont = 0; -#endif enum { L_LOG = 0x1, @@ -104,34 +95,35 @@ static const char *const environment[] = { /* Function prototypes */ static void delete_init_action(struct init_action *a); -static int waitfor(pid_t pid); -#if !ENABLE_DEBUG_INIT -static void shutdown_signal(int sig); -#endif +static void halt_reboot_pwoff(int sig) ATTRIBUTE_NORETURN; + +static void waitfor(pid_t pid) +{ + /* waitfor(run(x)): protect against failed fork inside run() */ + if (pid <= 0) + return; + + /* Wait for any child (prevent zombies from exiting orphaned processes) + * but exit the loop only when specified one has exited. */ + while (wait(NULL) != pid) + continue; +} -#if !ENABLE_DEBUG_INIT +static void loop_forever(void) ATTRIBUTE_NORETURN; static void loop_forever(void) { while (1) sleep(1); } -#endif /* Print a message to the specified device. * Device may be bitwise-or'd from L_LOG | L_CONSOLE */ -#if ENABLE_DEBUG_INIT -#define messageD message -#else -#define messageD(...) do {} while (0) -#endif +#define messageD(...) do { if (ENABLE_DEBUG_INIT) message(__VA_ARGS__); } while (0) static void message(int device, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); static void message(int device, const char *fmt, ...) { -#if !ENABLE_FEATURE_INIT_SYSLOG static int log_fd = -1; -#endif - va_list arguments; int l; char msg[128]; @@ -143,40 +135,40 @@ static void message(int device, const char *fmt, ...) msg[sizeof(msg) - 2] = '\0'; l = strlen(msg); -#if ENABLE_FEATURE_INIT_SYSLOG - /* Log the message to syslogd */ - if (device & L_LOG) { - /* don't out "\r" */ - openlog(applet_name, 0, LOG_DAEMON); - syslog(LOG_INFO, "init: %s", msg + 1); - closelog(); - } - msg[l++] = '\n'; - msg[l] = '\0'; -#else - msg[l++] = '\n'; - msg[l] = '\0'; - /* Take full control of the log tty, and never close it. - * It's mine, all mine! Muhahahaha! */ - if (log_fd < 0) { - if (!log_console) { - log_fd = 2; - } else { - log_fd = device_open(log_console, O_WRONLY | O_NONBLOCK | O_NOCTTY); - if (log_fd < 0) { - bb_error_msg("can't log to %s", log_console); - device = L_CONSOLE; + if (ENABLE_FEATURE_INIT_SYSLOG) { + /* Log the message to syslogd */ + if (device & L_LOG) { + /* don't out "\r" */ + openlog(applet_name, 0, LOG_DAEMON); + syslog(LOG_INFO, "init: %s", msg + 1); + closelog(); + } + msg[l++] = '\n'; + msg[l] = '\0'; + } else { + msg[l++] = '\n'; + msg[l] = '\0'; + /* Take full control of the log tty, and never close it. + * It's mine, all mine! Muhahahaha! */ + if (log_fd < 0) { + if (!log_console) { + log_fd = 2; } else { - close_on_exec_on(log_fd); + log_fd = device_open(log_console, O_WRONLY | O_NONBLOCK | O_NOCTTY); + if (log_fd < 0) { + bb_error_msg("can't log to %s", log_console); + device = L_CONSOLE; + } else { + close_on_exec_on(log_fd); + } } } + if (device & L_LOG) { + full_write(log_fd, msg, l); + if (log_fd == 2) + return; /* don't print dup messages */ + } } - if (device & L_LOG) { - full_write(log_fd, msg, l); - if (log_fd == 2) - return; /* don't print dup messages */ - } -#endif if (device & L_CONSOLE) { /* Send console messages to console so people will see them. */ @@ -235,9 +227,8 @@ static void console_init(void) * if TERM is set to linux (the default) */ if (!s || strcmp(s, "linux") == 0) putenv((char*)"TERM=vt102"); -#if !ENABLE_FEATURE_INIT_SYSLOG - log_console = NULL; -#endif + if (!ENABLE_FEATURE_INIT_SYSLOG) + log_console = NULL; } else if (!s) putenv((char*)"TERM=linux"); } @@ -280,27 +271,25 @@ static void set_sane_term(void) } /* Open the new terminal device */ -static void open_stdio_to_tty(const char* tty_name, int fail) +static void open_stdio_to_tty(const char* tty_name, int exit_on_failure) { /* empty tty_name means "use init's tty", else... */ if (tty_name[0]) { - int fd = device_open(tty_name, O_RDWR); - if (fd < 0) { + int fd; + close(0); + /* fd can be only < 0 or 0: */ + fd = device_open(tty_name, O_RDWR); + if (fd) { message(L_LOG | L_CONSOLE, "Can't open %s: %s", tty_name, strerror(errno)); - if (fail) + if (exit_on_failure) _exit(1); -#if !ENABLE_DEBUG_INIT - shutdown_signal(SIGUSR1); -#else - _exit(2); -#endif - } else { - dup2(fd, 0); - dup2(fd, 1); - dup2(fd, 2); - if (fd > 2) close(fd); + if (ENABLE_DEBUG_INIT) + _exit(2); + halt_reboot_pwoff(SIGUSR1); /* halt the system */ } + dup2(0, 1); + dup2(0, 2); } set_sane_term(); } @@ -351,7 +340,6 @@ static pid_t run(const struct init_action *a) /* If the init Action requires us to wait, then force the * supplied terminal to be the controlling tty. */ if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) { - /* Now fork off another process to just hang around */ pid = fork(); if (pid < 0) { @@ -435,12 +423,9 @@ static pid_t run(const struct init_action *a) } #endif -#if ENABLE_FEATURE_INIT_SCTTY - /* Establish this process as session leader and - * _attempt_ to make stdin a controlling tty. - */ - ioctl(0, TIOCSCTTY, 0 /*only try, don't steal*/); -#endif + /* _Attempt_ to make stdin a controlling tty. */ + if (ENABLE_FEATURE_INIT_SCTTY) + ioctl(0, TIOCSCTTY, 0 /*only try, don't steal*/); } if (a->action & ASKFIRST) { @@ -470,8 +455,7 @@ static pid_t run(const struct init_action *a) message(L_LOG, "starting pid %d, tty '%s': '%s'", getpid(), a->terminal, cmdpath); -#if ENABLE_FEATURE_INIT_COREDUMPS - { + if (ENABLE_FEATURE_INIT_COREDUMPS) { struct stat sb; if (stat(CORE_ENABLE_FLAG_FILE, &sb) == 0) { struct rlimit limit; @@ -481,7 +465,7 @@ static pid_t run(const struct init_action *a) setrlimit(RLIMIT_CORE, &limit); } } -#endif + /* Now run it. The new program will take over this PID, * so nothing further in init.c should be run. */ BB_EXECVP(cmdpath, cmd); @@ -492,19 +476,6 @@ static pid_t run(const struct init_action *a) _exit(-1); } -static int waitfor(pid_t runpid) -{ - int status, wpid; - - while (1) { - wpid = waitpid(runpid, &status, 0); - if (wpid == -1 && errno == EINTR) - continue; - break; - } - return wpid; -} - /* Run all commands of a particular type */ static void run_actions(int action) { @@ -536,7 +507,6 @@ static void run_actions(int action) } } -#if !ENABLE_DEBUG_INIT static void init_reboot(unsigned long magic) { pid_t pid; @@ -548,10 +518,10 @@ static void init_reboot(unsigned long magic) reboot(magic); _exit(0); } - waitpid(pid, NULL, 0); + waitfor(pid); } -static void shutdown_system(void) +static void kill_all_processes(void) { sigset_t block_signals; @@ -561,7 +531,8 @@ static void shutdown_system(void) run_actions(SHUTDOWN); /* first disable all our signals */ - sigemptyset(&block_signals); + sigfillset(&block_signals); + /*sigemptyset(&block_signals); sigaddset(&block_signals, SIGHUP); sigaddset(&block_signals, SIGQUIT); sigaddset(&block_signals, SIGCHLD); @@ -571,7 +542,7 @@ static void shutdown_system(void) sigaddset(&block_signals, SIGTERM); sigaddset(&block_signals, SIGCONT); sigaddset(&block_signals, SIGSTOP); - sigaddset(&block_signals, SIGTSTP); + sigaddset(&block_signals, SIGTSTP);*/ sigprocmask(SIG_BLOCK, &block_signals, NULL); message(L_CONSOLE | L_LOG, "The system is going down NOW!"); @@ -591,12 +562,12 @@ static void shutdown_system(void) sleep(1); } -static void shutdown_signal(int sig) +static void halt_reboot_pwoff(int sig) { const char *m; int rb; - shutdown_system(); + kill_all_processes(); m = "halt"; rb = RB_HALT_SYSTEM; @@ -614,7 +585,9 @@ static void shutdown_signal(int sig) loop_forever(); } -static void exec_signal(int sig ATTRIBUTE_UNUSED) +/* Handler for HUP and QUIT - exec "restart" action, + * else (no such action defined) do nothing */ +static void exec_restart_action(int sig ATTRIBUTE_UNUSED) { struct init_action *a, *tmp; sigset_t unblock_signals; @@ -622,10 +595,11 @@ static void exec_signal(int sig ATTRIBUTE_UNUSED) for (a = init_action_list; a; a = tmp) { tmp = a->next; if (a->action & RESTART) { - shutdown_system(); + kill_all_processes(); - /* unblock all signals, blocked in shutdown_system() */ - sigemptyset(&unblock_signals); + /* unblock all signals (blocked in kill_all_processes()) */ + sigfillset(&unblock_signals); + /*sigemptyset(&unblock_signals); sigaddset(&unblock_signals, SIGHUP); sigaddset(&unblock_signals, SIGQUIT); sigaddset(&unblock_signals, SIGCHLD); @@ -635,11 +609,11 @@ static void exec_signal(int sig ATTRIBUTE_UNUSED) sigaddset(&unblock_signals, SIGTERM); sigaddset(&unblock_signals, SIGCONT); sigaddset(&unblock_signals, SIGSTOP); - sigaddset(&unblock_signals, SIGTSTP); + sigaddset(&unblock_signals, SIGTSTP);*/ sigprocmask(SIG_UNBLOCK, &unblock_signals, NULL); /* Open the new terminal device */ - open_stdio_to_tty(a->terminal, 0 /* - shutdown_signal(SIGUSR1) [halt] if open fails */); + open_stdio_to_tty(a->terminal, 0 /* - halt if open fails */); messageD(L_CONSOLE | L_LOG, "Trying to re-exec %s", a->command); BB_EXECLP(a->command, a->command, NULL); @@ -676,8 +650,6 @@ static void cont_handler(int sig ATTRIBUTE_UNUSED) got_cont = 1; } -#endif /* !ENABLE_DEBUG_INIT */ - static void new_init_action(uint8_t action, const char *command, const char *cons) { struct init_action *new_action, *a, *last; @@ -737,27 +709,16 @@ static void delete_init_action(struct init_action *action) */ static void parse_inittab(void) { -#if ENABLE_FEATURE_USE_INITTAB - static const char actions[] = - STR_SYSINIT "sysinit\0" - STR_RESPAWN "respawn\0" - STR_ASKFIRST "askfirst\0" - STR_WAIT "wait\0" - STR_ONCE "once\0" - STR_CTRLALTDEL "ctrlaltdel\0" - STR_SHUTDOWN "shutdown\0" - STR_RESTART "restart\0" - ; - FILE *file; - char buf[INIT_BUFFS_SIZE], lineAsRead[INIT_BUFFS_SIZE]; - char tmpConsole[CONSOLE_NAME_SIZE]; - char *id, *runlev, *action, *command, *eol; + char buf[INIT_BUFFS_SIZE]; - file = fopen(INITTAB, "r"); + if (ENABLE_FEATURE_USE_INITTAB) + file = fopen(INITTAB, "r"); + else + file = NULL; + + /* No inittab file -- set up some default behavior */ if (file == NULL) { - /* No inittab file -- set up some default behavior */ -#endif /* Reboot on Ctrl-Alt-Del */ new_init_action(CTRLALTDEL, "reboot", ""); /* Umount all filesystems on halt/reboot */ @@ -776,61 +737,46 @@ static void parse_inittab(void) new_init_action(SYSINIT, INIT_SCRIPT, ""); return; -#if ENABLE_FEATURE_USE_INITTAB } while (fgets(buf, INIT_BUFFS_SIZE, file) != NULL) { + static const char actions[] = + STR_SYSINIT "sysinit\0" + STR_RESPAWN "respawn\0" + STR_ASKFIRST "askfirst\0" + STR_WAIT "wait\0" + STR_ONCE "once\0" + STR_CTRLALTDEL "ctrlaltdel\0" + STR_SHUTDOWN "shutdown\0" + STR_RESTART "restart\0" + ; + char tmpConsole[CONSOLE_NAME_SIZE]; + char *id, *runlev, *action, *command; const char *a; /* Skip leading spaces */ - for (id = buf; *id == ' ' || *id == '\t'; id++); - + id = skip_whitespace(buf); /* Skip the line if it's a comment */ if (*id == '#' || *id == '\n') continue; + /* Trim the trailing '\n' */ + *strchrnul(id, '\n') = '\0'; - /* Trim the trailing \n */ - //XXX: chomp() ? - eol = strrchr(id, '\n'); - if (eol != NULL) - *eol = '\0'; - - /* Keep a copy around for posterity's sake (and error msgs) */ - strcpy(lineAsRead, buf); - - /* Separate the ID field from the runlevels */ + /* Line is: "id:runlevel_ignored:action:command" */ runlev = strchr(id, ':'); - if (runlev == NULL || *(runlev + 1) == '\0') { - message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead); - continue; - } else { - *runlev = '\0'; - ++runlev; - } - - /* Separate the runlevels from the action */ - action = strchr(runlev, ':'); - if (action == NULL || *(action + 1) == '\0') { - message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead); - continue; - } else { - *action = '\0'; - ++action; - } - - /* Separate the action from the command */ - command = strchr(action, ':'); - if (command == NULL || *(command + 1) == '\0') { - message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead); - continue; - } else { - *command = '\0'; - ++command; - } - - /* Ok, now process it */ + if (runlev == NULL /*|| runlev[1] == '\0' - not needed */) + goto bad_entry; + action = strchr(runlev + 1, ':'); + if (action == NULL /*|| action[1] == '\0' - not needed */) + goto bad_entry; + command = strchr(action + 1, ':'); + if (command == NULL || command[1] == '\0') + goto bad_entry; + + *command = '\0'; /* action => ":action\0" now */ for (a = actions; a[0]; a += strlen(a) + 1) { - if (strcmp(a + 1, action) == 0) { + if (strcmp(a + 1, action + 1) == 0) { + *runlev = '\0'; if (*id != '\0') { if (strncmp(id, "/dev/", 5) == 0) id += 5; @@ -839,20 +785,19 @@ static void parse_inittab(void) sizeof(tmpConsole) - 5); id = tmpConsole; } - new_init_action((uint8_t)a[0], command, id); - break; + new_init_action((uint8_t)a[0], command + 1, id); + goto next_line; } } - if (!a[0]) { - /* Choke on an unknown action */ - message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead); - } + *command = ':'; + /* Choke on an unknown action */ + bad_entry: + message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", id); + next_line: ; } fclose(file); -#endif /* FEATURE_USE_INITTAB */ } -#if ENABLE_FEATURE_USE_INITTAB static void reload_signal(int sig ATTRIBUTE_UNUSED) { struct init_action *a, *tmp; @@ -860,12 +805,34 @@ static void reload_signal(int sig ATTRIBUTE_UNUSED) message(L_LOG, "reloading /etc/inittab"); /* disable old entrys */ - for (a = init_action_list; a; a = a->next ) { + for (a = init_action_list; a; a = a->next) { a->action = ONCE; } parse_inittab(); + if (ENABLE_FEATURE_KILL_REMOVED) { + /* Be nice and send SIGTERM first */ + for (a = init_action_list; a; a = a->next) { + pid_t pid = a->pid; + if ((a->action & ONCE) && pid != 0) { + kill(pid, SIGTERM); + } + } +#if CONFIG_FEATURE_KILL_DELAY + if (fork() == 0) { /* child */ + sleep(CONFIG_FEATURE_KILL_DELAY); + for (a = init_action_list; a; a = a->next) { + pid_t pid = a->pid; + if ((a->action & ONCE) && pid != 0) { + kill(pid, SIGKILL); + } + } + _exit(0); + } +#endif + } + /* remove unused entrys */ for (a = init_action_list; a; a = tmp) { tmp = a->next; @@ -875,7 +842,6 @@ static void reload_signal(int sig ATTRIBUTE_UNUSED) } run_actions(RESPAWN); } -#endif /* FEATURE_USE_INITTAB */ int init_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int init_main(int argc, char **argv) @@ -888,30 +854,30 @@ int init_main(int argc, char **argv) if (argc > 1 && !strcmp(argv[1], "-q")) { return kill(1, SIGHUP); } -#if !ENABLE_DEBUG_INIT - /* Expect to be invoked as init with PID=1 or be invoked as linuxrc */ - if (getpid() != 1 - && (!ENABLE_FEATURE_INITRD || !strstr(applet_name, "linuxrc")) - ) { - bb_show_usage(); - } - /* Set up sig handlers -- be sure to - * clear all of these in run() */ - signal(SIGHUP, exec_signal); - signal(SIGQUIT, exec_signal); - signal(SIGUSR1, shutdown_signal); - signal(SIGUSR2, shutdown_signal); - signal(SIGINT, ctrlaltdel_signal); - signal(SIGTERM, shutdown_signal); - signal(SIGCONT, cont_handler); - signal(SIGSTOP, stop_handler); - signal(SIGTSTP, stop_handler); - - /* Turn off rebooting via CTL-ALT-DEL -- we get a - * SIGINT on CAD so we can shut things down gracefully... */ - init_reboot(RB_DISABLE_CAD); -#endif + if (!ENABLE_DEBUG_INIT) { + /* Expect to be invoked as init with PID=1 or be invoked as linuxrc */ + if (getpid() != 1 + && (!ENABLE_FEATURE_INITRD || !strstr(applet_name, "linuxrc")) + ) { + bb_show_usage(); + } + /* Set up sig handlers -- be sure to + * clear all of these in run() */ + signal(SIGHUP, exec_restart_action); + signal(SIGQUIT, exec_restart_action); + signal(SIGUSR1, halt_reboot_pwoff); /* halt */ + signal(SIGUSR2, halt_reboot_pwoff); /* poweroff */ + signal(SIGTERM, halt_reboot_pwoff); /* reboot */ + signal(SIGINT, ctrlaltdel_signal); + signal(SIGCONT, cont_handler); + signal(SIGSTOP, stop_handler); + signal(SIGTSTP, stop_handler); + + /* Turn off rebooting via CTL-ALT-DEL -- we get a + * SIGINT on CAD so we can shut things down gracefully... */ + init_reboot(RB_DISABLE_CAD); + } /* Figure out where the default console should be */ console_init(); @@ -995,12 +961,11 @@ int init_main(int argc, char **argv) /* Next run anything to be run only once */ run_actions(ONCE); -#if ENABLE_FEATURE_USE_INITTAB /* Redefine SIGHUP to reread /etc/inittab */ - signal(SIGHUP, reload_signal); -#else - signal(SIGHUP, SIG_IGN); -#endif /* FEATURE_USE_INITTAB */ + if (ENABLE_FEATURE_USE_INITTAB) + signal(SIGHUP, reload_signal); + else + signal(SIGHUP, SIG_IGN); /* Now run the looping stuff for the rest of forever */ while (1) { @@ -1013,7 +978,7 @@ int init_main(int argc, char **argv) /* Don't consume all CPU time -- sleep a bit */ sleep(1); - /* Wait for a child process to exit */ + /* Wait for any child process to exit */ wpid = wait(NULL); while (wpid > 0) { /* Find out who died and clean up their corpse */ @@ -1028,7 +993,7 @@ int init_main(int argc, char **argv) } } /* see if anyone else is waiting to be reaped */ - wpid = waitpid(-1, NULL, WNOHANG); + wpid = wait_any_nohang(NULL); } } }