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

@ -18,7 +18,8 @@ HOSTCFLAGS_usage.o = -I$(srctree)/include
applets/applets.o: include/usage_compressed.h include/applet_tables.h
applets/usage: .config $(srctree)/applets/usage_compressed
applets/usage: .config $(srctree)/applets/usage_compressed
applets/applet_tables: .config
quiet_cmd_gen_usage_compressed = GEN include/usage_compressed.h
cmd_gen_usage_compressed = $(srctree)/applets/usage_compressed include/usage_compressed.h applets

View File

@ -365,7 +365,7 @@ this is a great mystery.
becomes its controlling tty.
</p><p>The BSD approach is that one has to explicitly call
</p><blockquote>
<pre>ioctl(fd, TIOCSCTTY, ...);
<pre>ioctl(fd, TIOCSCTTY, 0/1);
</pre>
</blockquote>
@ -378,6 +378,8 @@ and (ii) it does not yet have a controlling tty, and
(iii) maybe the tty should not already control some other session;
if it does it is an error if we aren't root, or we steal the tty
if we are all-powerful.
[vda: correction: third parameter controls this: if 1, we steal tty from
any such session, if 0, we don't steal]
</p><p>Opening some terminal will give us a controlling tty,
provided that (i) the current process is a session leader, and
(ii) it does not yet have a controlling tty, and

View File

@ -37,6 +37,7 @@ config FEATURE_INIT_SCTTY
controlling tty (TIOCSCTTY). 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.
config FEATURE_INIT_SYSLOG
bool "Enable init to write to syslog"

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 */

View File

@ -1,16 +1,18 @@
/* This code is adapted from busybox project
*
/* vi: set sw=4 ts=4: */
/*
* Licensed under GPLv2
*
* Copyright (c) 2007 Denys Vlasenko <vda.linux@googlemail.com>
*/
#include "libbb.h"
/* From <linux/vt.h> */
struct vt_stat {
unsigned short v_active; /* active vt */
unsigned short v_signal; /* signal to send */
unsigned short v_state; /* vt bitmask */
unsigned short v_active; /* active vt */
unsigned short v_signal; /* signal to send */
unsigned short v_state; /* vt bitmask */
};
enum { VT_GETSTATE = 0x5603 }; /* get global vt state info */
enum { VT_GETSTATE = 0x5603 }; /* get global vt state info */
/* From <linux/serial.h> */
struct serial_struct {
@ -26,8 +28,8 @@ struct serial_struct {
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 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;
@ -66,8 +68,10 @@ int cttyhack_main(int argc, char **argv)
dup2(fd, 1);
dup2(fd, 2);
while (fd > 2) close(fd--);
/* Some other session may have it as ctty. Steal it from them */
ioctl(0, TIOCSCTTY, 1)
}
execvp(argv[0], argv);
BB_EXECVP(argv[0], argv);
bb_perror_msg_and_die("cannot exec '%s'", argv[0]);
}