diff options
-rw-r--r-- | lib/lib.h | 1 | ||||
-rw-r--r-- | lib/xwrap.c | 11 | ||||
-rw-r--r-- | toys/other/oneit.c | 99 |
3 files changed, 79 insertions, 32 deletions
@@ -129,6 +129,7 @@ long xparsetime(char *arg, long units, long *fraction); void xpidfile(char *name); void xregcomp(regex_t *preg, char *rexec, int cflags); char *xtzset(char *new); +void xsignal(int signal, void *handler); // lib.c void verror_msg(char *msg, int err, va_list va); diff --git a/lib/xwrap.c b/lib/xwrap.c index 14703a72..44cd1684 100644 --- a/lib/xwrap.c +++ b/lib/xwrap.c @@ -638,3 +638,14 @@ char *xtzset(char *new) return tz; } + +// Set a signal handler +void xsignal(int signal, void *handler) +{ + struct sigaction *sa = (void *)libbuf; + + memset(sa, 0, sizeof(struct sigaction)); + sa->sa_handler = handler; + + if (sigaction(signal, sa, 0)) perror_exit("xsignal %d", signal); +} diff --git a/toys/other/oneit.c b/toys/other/oneit.c index 72395cce..8e4b7133 100644 --- a/toys/other/oneit.c +++ b/toys/other/oneit.c @@ -2,7 +2,7 @@ * * Copyright 2005, 2007 by Rob Landley <rob@landley.net>. -USE_ONEIT(NEWTOY(oneit, "^<1c:p", TOYFLAG_SBIN)) +USE_ONEIT(NEWTOY(oneit, "^<1nc:p3[!pn]", TOYFLAG_SBIN)) config ONEIT bool "oneit" @@ -10,16 +10,18 @@ config ONEIT help usage: oneit [-p] [-c /dev/tty0] command [...] - A simple init program that runs a single supplied command line with a + Simple init program that runs a single supplied command line with a controlling tty (so CTRL-C can kill it). + -c Which console device to use (/dev/console doesn't do CTRL-C, etc). -p Power off instead of rebooting when command exits. - -c Which console device to use. + -r Restart child when it exits. + -3 Write 32 bit PID of each exiting reparented process to fd 3 of child. + (Blocking writes, child must read to avoid eventual deadlock.) - The oneit command runs the supplied command line as a child process - (because PID 1 has signals blocked), attached to /dev/tty0, in its - own session. Then oneit reaps zombies until the child exits, at - which point it reboots (or with -p, powers off) the system. + Spawns a single child process (because PID 1 has signals blocked) + in its own session, reaps zombies until the child exits, then + reboots the system (or powers off with -p, or restarts the child with -r). */ #define FOR_oneit @@ -40,37 +42,70 @@ GLOBALS( // PID 1 then reaps zombies until the child process it spawned exits, at which // point it calls sync() and reboot(). I could stick a kill -1 in there. +// Perform actions in response to signals. (Only root can send us signals.) +static void oneit_signaled(int signal) +{ + int action = RB_AUTOBOOT; + + toys.signal = signal; + if (signal == SIGUSR1) action = RB_HALT_SYSTEM; + if (signal == SIGUSR2) action = RB_POWER_OFF; + + // PID 1 can't call reboot() because it kills the task that calls it, + // which causes the kernel to panic before the actual reboot happens. + sync(); + if (!vfork()) reboot(action); +} void oneit_main(void) { - int i; - pid_t pid; - - // Create a new child process. - pid = vfork(); - if (pid) { - - // pid 1 just reaps zombies until it gets its child, then halts the system. - while (pid != wait(&i)); - sync(); - - // PID 1 can't call reboot() because it kills the task that calls it, - // which causes the kernel to panic before the actual reboot happens. - if (!vfork()) reboot((toys.optflags & FLAG_p) ? RB_POWER_OFF : RB_AUTOBOOT); - sleep(5); - _exit(1); + int i, pid, pipes[] = {SIGUSR1, SIGUSR2, SIGTERM, SIGINT}; + + if (FLAG_3) { + // Ensure next available filehandle is #3 + while (open("/", 0) < 3); + close(3); + close(4); + if (pipe(pipes)) perror_exit("pipe"); + fcntl(4, F_SETFD, FD_CLOEXEC); } - // Redirect stdio to /dev/tty0, with new session ID, so ctrl-c works. - setsid(); - for (i=0; i<3; i++) { - close(i); - // Remember, O_CLOEXEC is backwards for xopen() - xopen(TT.console ? TT.console : "/dev/tty0", O_RDWR|O_CLOEXEC); + // Setup signal handlers for signals of interest + for (i = 0; i<ARRAY_LEN(pipes); i++) xsignal(pipes[i], oneit_signaled); + + while (!toys.signal) { + + // Create a new child process. + pid = vfork(); + if (pid) { + + // pid 1 reaps zombies until it gets its child, then halts system. + // We ignore the return value of write (what would we do with it?) + // but save it in a variable we never read to make fortify shut up. + // (Real problem is if pid2 never reads, write() fills pipe and blocks.) + while (pid != wait(&i)) if (FLAG_3) i = write(4, &pid, 4); + if (toys.optflags & FLAG_n) continue; + + oneit_signaled((toys.optflags & FLAG_p) ? SIGUSR2 : SIGTERM); + } else { + // Redirect stdio to /dev/tty0, with new session ID, so ctrl-c works. + setsid(); + for (i=0; i<3; i++) { + close(i); + // Remember, O_CLOEXEC is backwards for xopen() + xopen(TT.console ? TT.console : "/dev/tty0", O_RDWR|O_CLOEXEC); + } + + // Can't xexec() here, we vforked so we don't want to error_exit(). + toy_exec(toys.optargs); + execvp(*toys.optargs, toys.optargs); + perror_msg("%s not in PATH=%s", *toys.optargs, getenv("PATH")); + + break; + } } - // Can't xexec() here, because we vforked so we don't want to error_exit(). - toy_exec(toys.optargs); - execvp(*toys.optargs, toys.optargs); + // Give reboot() time to kick in, or avoid rapid spinning if exec failed + sleep(5); _exit(127); } |