init: fix bug 1111: restart actions were not splitting words:

::restart:/sbin/rc restart
resulted in:
exec of '/sbin/rc restart' failed: No such file or directory
This commit is contained in:
Denis Vlasenko 2008-02-19 02:57:07 +00:00
parent 92657d484f
commit a37e7134f7
2 changed files with 96 additions and 108 deletions

View File

@ -47,15 +47,18 @@ config FEATURE_KILL_DELAY
wrong process!) wrong process!)
config FEATURE_INIT_SCTTY config FEATURE_INIT_SCTTY
bool "Support running commands with a controlling-tty" bool "Run commands with leading dash with controlling tty"
default n default n
depends on INIT depends on INIT
help help
If this option is enabled a command starting with hyphen (-) If this option is enabled, init will try to give a controlling
is run in its own session (setsid(2)) and possibly with a tty to any command which has leading hyphen (often it's "-/bin/sh").
controlling tty (TIOCSCTTY). This is not the traditional init More precisely, init will do "ioctl(STDIN_FILENO, TIOCSCTTY, 0)".
behavour, but is often what you want in an embedded system where If device attached to STDIN_FILENO can be a ctty but is not yet
the console is only accessed during development or for maintenance. a ctty for other session, it will become this process' ctty.
This is not the traditional init behavour, but is often what you want
in an embedded system where the console is only accessed during
development or for maintenance.
NB: using cttyhack applet may work better. NB: using cttyhack applet may work better.
config FEATURE_INIT_SYSLOG config FEATURE_INIT_SYSLOG

View File

@ -14,7 +14,7 @@
#include <paths.h> #include <paths.h>
#include <sys/reboot.h> #include <sys/reboot.h>
#define INIT_BUFFS_SIZE 256 #define COMMAND_SIZE 256
#define CONSOLE_NAME_SIZE 32 #define CONSOLE_NAME_SIZE 32
#define MAXENV 16 /* Number of env. vars */ #define MAXENV 16 /* Number of env. vars */
@ -55,9 +55,9 @@
struct init_action { struct init_action {
struct init_action *next; struct init_action *next;
pid_t pid; pid_t pid;
uint8_t action; uint8_t action_type;
char terminal[CONSOLE_NAME_SIZE]; char terminal[CONSOLE_NAME_SIZE];
char command[INIT_BUFFS_SIZE]; char command[COMMAND_SIZE];
}; };
/* Static variables */ /* Static variables */
@ -294,14 +294,57 @@ static void open_stdio_to_tty(const char* tty_name, int exit_on_failure)
set_sane_term(); set_sane_term();
} }
/* wrapper around exec:
* takes string (max COMMAND_SIZE chars)
* runs [-]/bin/sh -c "exec ......." if '>' etc detected.
* otherwise splits words on whitespace and deals with leading dash.
*/
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] == '-');
/* See if any special /bin/sh requiring characters are present */
if (strpbrk(command, "~`!$^&*()=|\\{}[];\"'<>?") != NULL) {
strcpy(buf, "exec ");
strcpy(buf + 5, command + dash); /* excluding "-" */
/* LIBBB_DEFAULT_LOGIN_SHELL has leading dash */
cmd[0] = (char*)(LIBBB_DEFAULT_LOGIN_SHELL + !dash);
cmd[1] = (char*)"-c";
cmd[2] = buf;
cmd[3] = NULL;
} else {
/* Convert command (char*) into cmd (char**, one word per string) */
char *word, *next;
int i = 0;
next = strcpy(buf, command); /* including "-" */
while ((word = strsep(&next, " \t")) != NULL) {
if (*word != '\0') { /* not two spaces/tabs together? */
cmd[i] = word;
i++;
}
}
cmd[i] = NULL;
}
/* If we saw leading "-", it is interactive shell.
* Try harder to give it a controlling tty.
* And skip "-" in actual exec call. */
if (dash) {
/* _Attempt_ to make stdin a controlling tty. */
if (ENABLE_FEATURE_INIT_SCTTY)
ioctl(STDIN_FILENO, TIOCSCTTY, 0 /*only try, don't steal*/);
}
BB_EXECVP(cmd[0] + dash, cmd);
message(L_LOG | L_CONSOLE, "Cannot run '%s': %s",
cmd[0], strerror(errno));
/* returns if execvp fails */
}
/* Used only by run_actions */ /* Used only by run_actions */
static pid_t run(const struct init_action *a) static pid_t run(const struct init_action *a)
{ {
int i;
pid_t pid; pid_t pid;
char *s, *tmpCmd, *cmdpath;
char *cmd[INIT_BUFFS_SIZE];
char buf[INIT_BUFFS_SIZE + 6]; /* INIT_BUFFS_SIZE+strlen("exec ")+1 */
sigset_t nmask, omask; sigset_t nmask, omask;
/* Block sigchild while forking (why?) */ /* Block sigchild while forking (why?) */
@ -341,7 +384,7 @@ static pid_t run(const struct init_action *a)
#ifdef BUT_RUN_ACTIONS_ALREADY_DOES_WAITING #ifdef BUT_RUN_ACTIONS_ALREADY_DOES_WAITING
/* If the init Action requires us to wait, then force the /* If the init Action requires us to wait, then force the
* supplied terminal to be the controlling tty. */ * supplied terminal to be the controlling tty. */
if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) { if (a->action_type & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) {
/* Now fork off another process to just hang around */ /* Now fork off another process to just hang around */
pid = fork(); pid = fork();
if (pid < 0) { if (pid < 0) {
@ -381,58 +424,7 @@ static pid_t run(const struct init_action *a)
/* Child - fall though to actually execute things */ /* Child - fall though to actually execute things */
} }
#endif #endif
if (a->action_type & ASKFIRST) {
/* See if any special /bin/sh requiring characters are present */
if (strpbrk(a->command, "~`!$^&*()=|\\{}[];\"'<>?") != NULL) {
cmd[0] = (char*)DEFAULT_SHELL;
cmd[1] = (char*)"-c";
cmd[2] = strcat(strcpy(buf, "exec "), a->command);
cmd[3] = NULL;
} else {
/* Convert command (char*) into cmd (char**, one word per string) */
strcpy(buf, a->command);
s = buf;
for (tmpCmd = buf, i = 0; (tmpCmd = strsep(&s, " \t")) != NULL;) {
if (*tmpCmd != '\0') {
cmd[i] = tmpCmd;
i++;
}
}
cmd[i] = NULL;
}
cmdpath = cmd[0];
/*
* Interactive shells want to see a dash in argv[0]. This
* typically is handled by login, argv will be setup this
* way if a dash appears at the front of the command path
* (like "-/bin/sh").
*/
if (*cmdpath == '-') {
/* skip over the dash */
++cmdpath;
#ifdef WHY_WE_DO_THIS_SHELL_MUST_HANDLE_THIS_ITSELF
/* find the last component in the command pathname */
s = bb_get_last_path_component_nostrip(cmdpath);
/* make a new argv[0] */
cmd[0] = malloc(strlen(s) + 2);
if (cmd[0] == NULL) {
message(L_LOG | L_CONSOLE, bb_msg_memory_exhausted);
cmd[0] = cmdpath;
} else {
cmd[0][0] = '-';
strcpy(cmd[0] + 1, s);
}
#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) {
static const char press_enter[] ALIGN1 = static const char press_enter[] ALIGN1 =
#ifdef CUSTOMIZED_BANNER #ifdef CUSTOMIZED_BANNER
#include CUSTOMIZED_BANNER #include CUSTOMIZED_BANNER
@ -449,58 +441,54 @@ static pid_t run(const struct init_action *a)
*/ */
messageD(L_LOG, "waiting for enter to start '%s'" messageD(L_LOG, "waiting for enter to start '%s'"
"(pid %d, tty '%s')\n", "(pid %d, tty '%s')\n",
cmdpath, getpid(), a->terminal); a->command, getpid(), a->terminal);
full_write(1, press_enter, sizeof(press_enter) - 1); full_write(1, press_enter, sizeof(press_enter) - 1);
while (safe_read(0, &c, 1) == 1 && c != '\n') while (safe_read(0, &c, 1) == 1 && c != '\n')
continue; continue;
} }
/* Log the process name and args */
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; struct stat sb;
if (stat(CORE_ENABLE_FLAG_FILE, &sb) == 0) { if (stat(CORE_ENABLE_FLAG_FILE, &sb) == 0) {
struct rlimit limit; struct rlimit limit;
limit.rlim_cur = RLIM_INFINITY; limit.rlim_cur = RLIM_INFINITY;
limit.rlim_max = RLIM_INFINITY; limit.rlim_max = RLIM_INFINITY;
setrlimit(RLIMIT_CORE, &limit); setrlimit(RLIMIT_CORE, &limit);
} }
} }
/* Log the process name and args */
message(L_LOG, "starting pid %d, tty '%s': '%s'",
getpid(), a->terminal, a->command);
/* Now run it. The new program will take over this PID, /* Now run it. The new program will take over this PID,
* so nothing further in init.c should be run. */ * so nothing further in init.c should be run. */
BB_EXECVP(cmdpath, cmd); init_exec(a->command);
/* We're still here? Some error happened. */ /* We're still here? Some error happened. */
message(L_LOG | L_CONSOLE, "Cannot run '%s': %s",
cmdpath, strerror(errno));
_exit(-1); _exit(-1);
} }
/* Run all commands of a particular type */ /* Run all commands of a particular type */
static void run_actions(int action) static void run_actions(int action_type)
{ {
struct init_action *a, *tmp; struct init_action *a, *tmp;
for (a = init_action_list; a; a = tmp) { for (a = init_action_list; a; a = tmp) {
tmp = a->next; tmp = a->next;
if (a->action == action) { if (a->action_type == action_type) {
// Pointless: run() will error out if open of device fails. // Pointless: run() will error out if open of device fails.
///* a->terminal of "" means "init's console" */ ///* a->terminal of "" means "init's console" */
//if (a->terminal[0] && access(a->terminal, R_OK | W_OK)) { //if (a->terminal[0] && access(a->terminal, R_OK | W_OK)) {
// //message(L_LOG | L_CONSOLE, "Device %s cannot be opened in RW mode", a->terminal /*, strerror(errno)*/); // //message(L_LOG | L_CONSOLE, "Device %s cannot be opened in RW mode", a->terminal /*, strerror(errno)*/);
// delete_init_action(a); // delete_init_action(a);
//} else //} else
if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) { if (a->action_type & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) {
waitfor(run(a)); waitfor(run(a));
delete_init_action(a); delete_init_action(a);
} else if (a->action & ONCE) { } else if (a->action_type & ONCE) {
run(a); run(a);
delete_init_action(a); delete_init_action(a);
} else if (a->action & (RESPAWN | ASKFIRST)) { } else if (a->action_type & (RESPAWN | ASKFIRST)) {
/* Only run stuff with pid==0. If they have /* Only run stuff with pid==0. If they have
* a pid, that means it is still running */ * a pid, that means it is still running */
if (a->pid == 0) { if (a->pid == 0) {
@ -593,12 +581,11 @@ static void halt_reboot_pwoff(int sig)
* else (no such action defined) do nothing */ * else (no such action defined) do nothing */
static void exec_restart_action(int sig ATTRIBUTE_UNUSED) static void exec_restart_action(int sig ATTRIBUTE_UNUSED)
{ {
struct init_action *a, *tmp; struct init_action *a;
sigset_t unblock_signals; sigset_t unblock_signals;
for (a = init_action_list; a; a = tmp) { for (a = init_action_list; a; a = a->next) {
tmp = a->next; if (a->action_type & RESTART) {
if (a->action & RESTART) {
kill_all_processes(); kill_all_processes();
/* unblock all signals (blocked in kill_all_processes()) */ /* unblock all signals (blocked in kill_all_processes()) */
@ -620,10 +607,7 @@ static void exec_restart_action(int sig ATTRIBUTE_UNUSED)
open_stdio_to_tty(a->terminal, 0 /* - 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); messageD(L_CONSOLE | L_LOG, "Trying to re-exec %s", a->command);
BB_EXECLP(a->command, a->command, NULL); init_exec(a->command);
message(L_CONSOLE | L_LOG, "Cannot run '%s': %s",
a->command, strerror(errno));
sleep(2); sleep(2);
init_reboot(RB_HALT_SYSTEM); init_reboot(RB_HALT_SYSTEM);
loop_forever(); loop_forever();
@ -654,12 +638,13 @@ static void cont_handler(int sig ATTRIBUTE_UNUSED)
got_cont = 1; got_cont = 1;
} }
static void new_init_action(uint8_t action, const char *command, const char *cons) static void new_init_action(uint8_t action_type, const char *command, const char *cons)
{ {
struct init_action *new_action, *a, *last; struct init_action *a, *last;
if (strcmp(cons, bb_dev_null) == 0 && (action & ASKFIRST)) // Why?
return; // if (strcmp(cons, bb_dev_null) == 0 && (action & ASKFIRST))
// return;
/* Append to the end of the list */ /* Append to the end of the list */
for (a = last = init_action_list; a; a = a->next) { for (a = last = init_action_list; a; a = a->next) {
@ -668,23 +653,23 @@ static void new_init_action(uint8_t action, const char *command, const char *con
if ((strcmp(a->command, command) == 0) if ((strcmp(a->command, command) == 0)
&& (strcmp(a->terminal, cons) == 0) && (strcmp(a->terminal, cons) == 0)
) { ) {
a->action = action; a->action_type = action_type;
return; return;
} }
last = a; last = a;
} }
new_action = xzalloc(sizeof(struct init_action)); a = xzalloc(sizeof(*a));
if (last) { if (last) {
last->next = new_action; last->next = a;
} else { } else {
init_action_list = new_action; init_action_list = a;
} }
strcpy(new_action->command, command); a->action_type = action_type;
new_action->action = action; safe_strncpy(a->command, command, sizeof(a->command));
strcpy(new_action->terminal, cons); safe_strncpy(a->terminal, cons, sizeof(a->terminal));
messageD(L_LOG | L_CONSOLE, "command='%s' action=%d tty='%s'\n", messageD(L_LOG | L_CONSOLE, "command='%s' action=%d tty='%s'\n",
new_action->command, new_action->action, new_action->terminal); a->command, a->action_type, a->terminal);
} }
static void delete_init_action(struct init_action *action) static void delete_init_action(struct init_action *action)
@ -714,7 +699,7 @@ static void delete_init_action(struct init_action *action)
static void parse_inittab(void) static void parse_inittab(void)
{ {
FILE *file; FILE *file;
char buf[INIT_BUFFS_SIZE]; char buf[COMMAND_SIZE];
if (ENABLE_FEATURE_USE_INITTAB) if (ENABLE_FEATURE_USE_INITTAB)
file = fopen(INITTAB, "r"); file = fopen(INITTAB, "r");
@ -743,7 +728,7 @@ static void parse_inittab(void)
return; return;
} }
while (fgets(buf, INIT_BUFFS_SIZE, file) != NULL) { while (fgets(buf, COMMAND_SIZE, file) != NULL) {
static const char actions[] = static const char actions[] =
STR_SYSINIT "sysinit\0" STR_SYSINIT "sysinit\0"
STR_RESPAWN "respawn\0" STR_RESPAWN "respawn\0"
@ -810,7 +795,7 @@ static void reload_signal(int sig ATTRIBUTE_UNUSED)
/* disable old entrys */ /* disable old entrys */
for (a = init_action_list; a; a = a->next) { for (a = init_action_list; a; a = a->next) {
a->action = ONCE; a->action_type = ONCE;
} }
parse_inittab(); parse_inittab();
@ -819,7 +804,7 @@ static void reload_signal(int sig ATTRIBUTE_UNUSED)
/* 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) {
pid_t pid = a->pid; pid_t pid = a->pid;
if ((a->action & ONCE) && pid != 0) { if ((a->action_type & ONCE) && pid != 0) {
kill(pid, SIGTERM); kill(pid, SIGTERM);
} }
} }
@ -828,7 +813,7 @@ static void reload_signal(int sig ATTRIBUTE_UNUSED)
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) {
pid_t pid = a->pid; pid_t pid = a->pid;
if ((a->action & ONCE) && pid != 0) { if ((a->action_type & ONCE) && pid != 0) {
kill(pid, SIGKILL); kill(pid, SIGKILL);
} }
} }
@ -840,7 +825,7 @@ static void reload_signal(int sig ATTRIBUTE_UNUSED)
/* remove unused entrys */ /* remove unused entrys */
for (a = init_action_list; a; a = tmp) { for (a = init_action_list; a; a = tmp) {
tmp = a->next; tmp = a->next;
if ((a->action & (ONCE | SYSINIT | WAIT)) && a->pid == 0) { if ((a->action_type & (ONCE | SYSINIT | WAIT)) && a->pid == 0) {
delete_init_action(a); delete_init_action(a);
} }
} }
@ -998,7 +983,7 @@ int init_main(int argc, char **argv)
* restarted by run_actions() */ * restarted by run_actions() */
a->pid = 0; a->pid = 0;
message(L_LOG, "process '%s' (pid %d) exited. " message(L_LOG, "process '%s' (pid %d) exited. "
"Scheduling it for restart.", "Scheduling for restart.",
a->command, wpid); a->command, wpid);
} }
} }