init: remove superfluous forks and messing up with argv[0]

cttyhack: add stealing of ctty
This commit is contained in:
Denis Vlasenko
2007-12-10 07:06:04 +00:00
parent 191836845e
commit 2afabe8b83
5 changed files with 141 additions and 135 deletions

View File

@ -47,31 +47,22 @@
#define SHUTDOWN 0x040
#define RESTART 0x080
/* A mapping between "inittab" action name strings and action type codes. */
struct init_action_type {
const char *name;
int action;
};
static const struct init_action_type actions[] = {
{"sysinit", SYSINIT},
{"respawn", RESPAWN},
{"askfirst", ASKFIRST},
{"wait", WAIT},
{"once", ONCE},
{"ctrlaltdel", CTRLALTDEL},
{"shutdown", SHUTDOWN},
{"restart", RESTART},
{0, 0}
};
#define STR_SYSINIT "\x01"
#define STR_RESPAWN "\x02"
#define STR_ASKFIRST "\x04"
#define STR_WAIT "\x08"
#define STR_ONCE "\x10"
#define STR_CTRLALTDEL "\x20"
#define STR_SHUTDOWN "\x40"
#define STR_RESTART "\x80"
/* Set up a linked list of init_actions, to be read from inittab */
struct init_action {
struct init_action *next;
int action;
pid_t pid;
char command[INIT_BUFFS_SIZE];
uint8_t action;
char terminal[CONSOLE_NAME_SIZE];
char command[INIT_BUFFS_SIZE];
};
/* Static variables */
@ -113,7 +104,7 @@ static const char *const environment[] = {
/* Function prototypes */
static void delete_init_action(struct init_action *a);
static int waitfor(const struct init_action *a, pid_t pid);
static int waitfor(pid_t pid);
#if !ENABLE_DEBUG_INIT
static void shutdown_signal(int sig);
#endif
@ -193,43 +184,6 @@ static void message(int device, const char *fmt, ...)
}
}
/* Set terminal settings to reasonable defaults */
static void set_sane_term(void)
{
struct termios tty;
tcgetattr(STDIN_FILENO, &tty);
/* set control chars */
tty.c_cc[VINTR] = 3; /* C-c */
tty.c_cc[VQUIT] = 28; /* C-\ */
tty.c_cc[VERASE] = 127; /* C-? */
tty.c_cc[VKILL] = 21; /* C-u */
tty.c_cc[VEOF] = 4; /* C-d */
tty.c_cc[VSTART] = 17; /* C-q */
tty.c_cc[VSTOP] = 19; /* C-s */
tty.c_cc[VSUSP] = 26; /* C-z */
/* use line dicipline 0 */
tty.c_line = 0;
/* Make it be sane */
tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD;
tty.c_cflag |= CREAD | HUPCL | CLOCAL;
/* input modes */
tty.c_iflag = ICRNL | IXON | IXOFF;
/* output modes */
tty.c_oflag = OPOST | ONLCR;
/* local modes */
tty.c_lflag =
ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
tcsetattr(STDIN_FILENO, TCSANOW, &tty);
}
/* From <linux/serial.h> */
struct serial_struct {
int type;
@ -277,7 +231,7 @@ static void console_init(void)
s = getenv("TERM");
if (ioctl(0, TIOCGSERIAL, &sr) == 0) {
/* Force the TERM setting to vt102 for serial console --
/* Force the TERM setting to vt102 for serial console
* if TERM is set to linux (the default) */
if (!s || strcmp(s, "linux") == 0)
putenv((char*)"TERM=vt102");
@ -288,14 +242,41 @@ static void console_init(void)
putenv((char*)"TERM=linux");
}
static void fixup_argv(char **argv)
/* Set terminal settings to reasonable defaults */
static void set_sane_term(void)
{
/* Fix up argv[0] to be certain we claim to be init */
strncpy(argv[0], "init", strlen(argv[0]));
struct termios tty;
/* Wipe argv[1]-argv[N] so they don't clutter the ps listing */
while (*++argv)
memset(*argv, 0, strlen(*argv));
tcgetattr(STDIN_FILENO, &tty);
/* set control chars */
tty.c_cc[VINTR] = 3; /* C-c */
tty.c_cc[VQUIT] = 28; /* C-\ */
tty.c_cc[VERASE] = 127; /* C-? */
tty.c_cc[VKILL] = 21; /* C-u */
tty.c_cc[VEOF] = 4; /* C-d */
tty.c_cc[VSTART] = 17; /* C-q */
tty.c_cc[VSTOP] = 19; /* C-s */
tty.c_cc[VSUSP] = 26; /* C-z */
/* use line dicipline 0 */
tty.c_line = 0;
/* Make it be sane */
tty.c_cflag &= CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD;
tty.c_cflag |= CREAD | HUPCL | CLOCAL;
/* input modes */
tty.c_iflag = ICRNL | IXON | IXOFF;
/* output modes */
tty.c_oflag = OPOST | ONLCR;
/* local modes */
tty.c_lflag =
ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
tcsetattr(STDIN_FILENO, TCSANOW, &tty);
}
/* Open the new terminal device */
@ -324,6 +305,7 @@ static void open_stdio_to_tty(const char* tty_name, int fail)
set_sane_term();
}
/* Used only by run_actions */
static pid_t run(const struct init_action *a)
{
int i;
@ -333,16 +315,20 @@ static pid_t run(const struct init_action *a)
char buf[INIT_BUFFS_SIZE + 6]; /* INIT_BUFFS_SIZE+strlen("exec ")+1 */
sigset_t nmask, omask;
/* Block sigchild while forking. */
/* Block sigchild while forking (why?) */
sigemptyset(&nmask);
sigaddset(&nmask, SIGCHLD);
sigprocmask(SIG_BLOCK, &nmask, &omask);
pid = fork();
sigprocmask(SIG_SETMASK, &omask, NULL);
if (pid < 0)
message(L_LOG | L_CONSOLE, "Can't fork");
if (pid)
return pid;
/* Child */
/* Reset signal handlers that were set by the parent process */
signal(SIGUSR1, SIG_DFL);
signal(SIGUSR2, SIG_DFL);
@ -359,8 +345,9 @@ static pid_t run(const struct init_action *a)
setsid();
/* Open the new terminal device */
open_stdio_to_tty(a->terminal, 1);
open_stdio_to_tty(a->terminal, 1 /* - exit if open fails*/);
#ifdef BUT_RUN_ACTIONS_ALREADY_DOES_WAITING
/* 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)) {
@ -373,13 +360,13 @@ static pid_t run(const struct init_action *a)
}
if (pid > 0) {
/* We are the parent -- wait till the child is done */
/* Parent - wait till the child is done */
signal(SIGINT, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
signal(SIGCHLD, SIG_DFL);
waitfor(NULL, pid);
waitfor(pid);
/* See if stealing the controlling tty back is necessary */
if (tcgetpgrp(0) != getpid())
_exit(0);
@ -395,12 +382,13 @@ static pid_t run(const struct init_action *a)
ioctl(0, TIOCSCTTY, 1);
_exit(0);
}
waitfor(NULL, pid);
waitfor(pid);
_exit(0);
}
/* Now fall though to actually execute things */
/* Child - fall though to actually execute things */
}
#endif
/* See if any special /bin/sh requiring characters are present */
if (strpbrk(a->command, "~`!$^&*()=|\\{}[];\"'<>?") != NULL) {
@ -433,9 +421,9 @@ static pid_t run(const struct init_action *a)
/* 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) {
@ -445,16 +433,16 @@ static pid_t run(const struct init_action *a)
cmd[0][0] = '-';
strcpy(cmd[0] + 1, s);
}
#endif
#if ENABLE_FEATURE_INIT_SCTTY
/* Establish this process as session leader and
* (attempt) to make the tty (if any) a controlling tty.
* _attempt_ to make stdin a controlling tty.
*/
setsid();
ioctl(0, TIOCSCTTY, 0 /*don't steal it*/);
ioctl(0, TIOCSCTTY, 0 /*only try, don't steal*/);
#endif
}
#if !defined(__UCLIBC__) || defined(__ARCH_HAS_MMU__)
if (a->action & ASKFIRST) {
static const char press_enter[] ALIGN1 =
#ifdef CUSTOMIZED_BANNER
@ -474,10 +462,10 @@ static pid_t run(const struct init_action *a)
"(pid %d, tty '%s')\n",
cmdpath, getpid(), a->terminal);
full_write(1, press_enter, sizeof(press_enter) - 1);
while (read(0, &c, 1) == 1 && c != '\n')
;
while (safe_read(0, &c, 1) == 1 && c != '\n')
continue;
}
#endif
/* Log the process name and args */
message(L_LOG, "starting pid %d, tty '%s': '%s'",
getpid(), a->terminal, cmdpath);
@ -504,22 +492,15 @@ static pid_t run(const struct init_action *a)
_exit(-1);
}
static int waitfor(const struct init_action *a, pid_t pid)
static int waitfor(pid_t runpid)
{
int runpid;
int status, wpid;
runpid = (NULL == a)? pid : run(a);
while (1) {
wpid = waitpid(runpid, &status, 0);
if (wpid == runpid)
break;
if (wpid == -1 && errno == ECHILD) {
/* we missed its termination */
break;
}
/* FIXME other errors should maybe trigger an error, but allow
* the program to continue */
if (wpid == -1 && errno == EINTR)
continue;
break;
}
return wpid;
}
@ -534,9 +515,10 @@ static void run_actions(int action)
if (a->action == action) {
/* a->terminal of "" means "init's console" */
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)*/);
delete_init_action(a);
} else if (a->action & (SYSINIT | WAIT | CTRLALTDEL | SHUTDOWN | RESTART)) {
waitfor(a, 0);
waitfor(run(a));
delete_init_action(a);
} else if (a->action & ONCE) {
run(a);
@ -607,6 +589,29 @@ static void shutdown_system(void)
sleep(1);
}
static void shutdown_signal(int sig)
{
const char *m;
int rb;
shutdown_system();
m = "halt";
rb = RB_HALT_SYSTEM;
if (sig == SIGTERM) {
m = "reboot";
rb = RB_AUTOBOOT;
} else if (sig == SIGUSR2) {
m = "poweroff";
rb = RB_POWER_OFF;
}
message(L_CONSOLE | L_LOG, "Requesting system %s", m);
/* allow time for last message to reach serial console */
sleep(2);
init_reboot(rb);
loop_forever();
}
static void exec_signal(int sig ATTRIBUTE_UNUSED)
{
struct init_action *a, *tmp;
@ -632,7 +637,7 @@ static void exec_signal(int sig ATTRIBUTE_UNUSED)
sigprocmask(SIG_UNBLOCK, &unblock_signals, NULL);
/* Open the new terminal device */
open_stdio_to_tty(a->terminal, 0);
open_stdio_to_tty(a->terminal, 0 /* - shutdown_signal(SIGUSR1) [halt] if open fails */);
messageD(L_CONSOLE | L_LOG, "Trying to re-exec %s", a->command);
BB_EXECLP(a->command, a->command, NULL);
@ -646,29 +651,6 @@ static void exec_signal(int sig ATTRIBUTE_UNUSED)
}
}
static void shutdown_signal(int sig)
{
const char *m;
int rb;
shutdown_system();
m = "halt";
rb = RB_HALT_SYSTEM;
if (sig == SIGTERM) {
m = "reboot";
rb = RB_AUTOBOOT;
} else if (sig == SIGUSR2) {
m = "poweroff";
rb = RB_POWER_OFF;
}
message(L_CONSOLE | L_LOG, "Requesting system %s", m);
/* allow time for last message to reach serial console */
sleep(2);
init_reboot(rb);
loop_forever();
}
static void ctrlaltdel_signal(int sig ATTRIBUTE_UNUSED)
{
run_actions(CTRLALTDEL);
@ -682,7 +664,7 @@ static void stop_handler(int sig ATTRIBUTE_UNUSED)
got_cont = 0;
while (!got_cont)
pause();
got_cont = 0;
errno = saved_errno;
}
@ -694,7 +676,7 @@ static void cont_handler(int sig ATTRIBUTE_UNUSED)
#endif /* !ENABLE_DEBUG_INIT */
static void new_init_action(int action, const char *command, const char *cons)
static void new_init_action(uint8_t action, const char *command, const char *cons)
{
struct init_action *new_action, *a, *last;
@ -754,11 +736,21 @@ 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;
const struct init_action_type *a = actions;
file = fopen(INITTAB, "r");
if (file == NULL) {
@ -769,7 +761,8 @@ static void parse_inittab(void)
/* 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", "");
if (ENABLE_SWAPONOFF)
new_init_action(SHUTDOWN, "swapoff -a", "");
/* Prepare to restart init when a HUP is received */
new_init_action(RESTART, "init", "");
/* Askfirst shell on tty1-4 */
@ -785,6 +778,8 @@ static void parse_inittab(void)
}
while (fgets(buf, INIT_BUFFS_SIZE, file) != NULL) {
const char *a;
/* Skip leading spaces */
for (id = buf; *id == ' ' || *id == '\t'; id++);
@ -832,8 +827,8 @@ static void parse_inittab(void)
}
/* Ok, now process it */
for (a = actions; a->name != 0; a++) {
if (strcmp(a->name, action) == 0) {
for (a = actions; a[0]; a += strlen(a) + 1) {
if (strcmp(a + 1, action) == 0) {
if (*id != '\0') {
if (strncmp(id, "/dev/", 5) == 0)
id += 5;
@ -842,11 +837,11 @@ static void parse_inittab(void)
sizeof(tmpConsole) - 5);
id = tmpConsole;
}
new_init_action(a->action, command, id);
new_init_action((uint8_t)a[0], command, id);
break;
}
}
if (a->name == 0) {
if (!a[0]) {
/* Choke on an unknown action */
message(L_LOG | L_CONSOLE, "Bad inittab entry: %s", lineAsRead);
}
@ -981,8 +976,11 @@ int init_main(int argc, char **argv)
}
#endif /* CONFIG_SELINUX */
/* Make the command line just say "init" -- thats all, nothing else */
fixup_argv(argv);
/* Make the command line just say "init" - thats all, nothing else */
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));
/* Now run everything that needs to be run */