diff --git a/README.md b/README.md index c9f5ccd5..804dfdb3 100644 --- a/README.md +++ b/README.md @@ -245,12 +245,45 @@ start any new ones for you, without rebooting the system. Rebooting & Halting ------------------- -Finit handles `SIGUSR1` and `SIGUSR2` for reboot and halt, and listens -to `/dev/initctl` so system reboot and halt commands also work. The -latter is implemented in the optional `initctl.so` plugin and can be -accessed with the traditional `telinit` command line tool, symlinked to -`finit`. Hence, if finit is your system init, then `init q` will work -as the UNIX beards intended. +Finit is often used on embedded and small Linux systems with BusyBox. +For compatibility with its existing toolset (halt, poweroff, reboot), +the following signals have been adopted: + +* `SIGUSR1` + Same effect as `finit q`, reloads all *.conf files in `/etc/finit.d/` +* `SIGUSR1` + Calls shutdown hooks, including HOOK_SHUTDOWN, stops all running + processes, and unmounts all file systems. Then tells kernel to + halt. Most people these days want SIGUSR2 though. + + SysV init and systemd use this to re-open their FIFO/D-Bus. +* `SIGUSR2` + Like SIGUSR1, but tell kernel to power-off the system, if ACPI + or similar exists to actually do this. If the kernel fails + power-off, Finit falls back to halt. + + SysV init N/A, systemd dumps its internal state to log. +* `SIGTERM` + Like `SIGUSR1`, but tell kernel to reboot the system when done. + + SysV init N/A, systemd rexecutes itself. +* `SIGINT` + Sent from kernel when the CTRL-ALT-DEL key combo is pressed. + SysV init and systemd default to reboot with `shutdown -r`. + Finit currently forwards this to `SIGTERM`. +* `SIGPWR` + Sent from a power daemon, like `powstatd(8)`, on changes to the + UPS status. Traditionally SysV init read /etc/powerstatus and + acted on "OK", "FAIL", or "LOW" and then removed the file. + Finit currently forwards this to `SIGUSR2`. + +Finit also listens to the classic SysV init FIFO, which used to be +located in `/dev` but these days default to `/run/initctl`. So existing +tools like, `poweroff`, `halt`, `reboot`, and `shutdown` work as +expected. Support for this old-style handling is implemented in the +optional `initctl.so` plugin and can be accessed with the traditional +`telinit` command line tool, symlinked to `finit`. Hence, if finit is +your system init, then `init q` will work as the UNIX beards intended. ```shell ~ $ telinit diff --git a/TODO.md b/TODO.md index 779dcda0..ef6bc7bc 100644 --- a/TODO.md +++ b/TODO.md @@ -18,6 +18,17 @@ very low dependency on external packages. General ------- +* Add halt/poweroff/shutdown symlinks to finit and convert reboot, but + only install symlinks if these tools don't already exist. +* Add reboot, halt, poweroff commands to `initctl` tool +* SysV init and systemd use SIGUSR1 to restart their FIFO/D-Bus. Add + API restart to SIGHUP callback for the same functionality in Finit. +* Add `finit.conf` support for UPS notification (SIGPWR) to start a task + using, e.g. conditions. More info in sig.c +* Add `finit.conf` support for ctrl-alt-delete (SIGINT) and kbrequest, + i.e. KeyboardSignal, (SIGWINCH) behavior. Using conditions to a task, + e.g, and like SIGPWR handling. +* Add `IFF_RUNNING` support to netlink plugin along `IFF_IUP` * Add compile-time support for running `/bin/sh` instead of `getty` * Add `!` or `[wait ]` to service directive to let finit pause and wait for a pidfile before starting processes -- both at boot and diff --git a/api.c b/api.c index cca6812d..40d29c53 100644 --- a/api.c +++ b/api.c @@ -255,7 +255,7 @@ static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events)) switch (rq.runlevel) { case '0': _d("Halting system (SIGUSR2)"); - do_shutdown(SIGUSR2); + do_shutdown(SHUT_OFF); break; case 's': @@ -272,7 +272,7 @@ static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events)) case '6': _d("Rebooting system (SIGUSR1)"); - do_shutdown(SIGUSR1); + do_shutdown(SHUT_REBOOT); break; default: diff --git a/sig.c b/sig.c index 84c24b3a..c3a902f7 100644 --- a/sig.c +++ b/sig.c @@ -21,21 +21,47 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * - * The standard SysV init only responds to the following signals: - * SIGHUP - * Has the same effect as telinit q. + * Finit is often used on embedded and small Linux systems with BusyBox. + * For compatibility with its existing toolset (halt, poweroff, reboot), + * the following signals have been adopted: * - * SIGUSR1 - * On receipt of this signals, init closes and re-opens its control - * fifo, /dev/initctl. Useful for bootscripts when /dev is remounted. + * SIGHUP + * Same effect as init/telinit q, reloads *.conf in /etc/finit.d/ * - * SIGINT - * Normally the kernel sends this signal to init when CTRL-ALT-DEL is - * pressed. It activates the ctrlaltdel action. + * SIGUSR1 + * Calls shutdown hooks, including HOOK_SHUTDOWN, stops all running + * processes, and unmounts all file systems. Then tells kernel to + * halt. Most people these days want SIGUSR2 though. * - * SIGWINCH - * The kernel sends this signal when the KeyboardSignal key is hit. - * It activates the kbrequest action. + * (SysV init and systemd use this to re-open their FIFO/D-Bus.) + * + * SIGUSR2 + * Like SIGUSR1, but tell kernel to power-off the system, if ACPI + * or similar exists to actually do this. If the kernel fails + * power-off, Finit falls back to halt. + * + * (SysV init N/A, systemd dumps its internal state to log.) + * + * SIGTERM + * Like SIGUSR1, but tell kernel to reboot the system when done. + * + * (SysV init N/A, systemd rexecutes itself.) + * + * SIGINT + * Sent from kernel when the CTRL-ALT-DEL key combo is pressed. + * SysV init and systemd default to reboot with `shutdown -r`. + * Finit currently forwards this to SIGTERM. + * + * SIGWINCH + * Sent from kernel when the KeyboardSignal key is pressed. Some + * users bind keys, see loadkeys(1), to create consoles on demand. + * Finit currently ignores this signal. + * + * SIGPWR + * Sent from a power daemon, like powstatd(8), on changes to the + * UPS status. Traditionally SysV init read /etc/powerstatus and + * acted on "OK", "FAIL", or "LOW" and then removed the file. + * Finit currently forwards this to SIGUSR2. */ #include @@ -54,12 +80,13 @@ #include "service.h" static int stopped = 0; -static uev_t sighup_watcher, sigint_watcher, sigpwr_watcher; +static uev_t sigterm_watcher, sigusr1_watcher, sigusr2_watcher; +static uev_t sighup_watcher, sigint_watcher, sigpwr_watcher; static uev_t sigchld_watcher, sigsegv_watcher; static uev_t sigstop_watcher, sigtstp_watcher, sigcont_watcher; -void do_shutdown(int sig) +void do_shutdown(shutop_t op) { touch(SYNC_SHUTDOWN); @@ -121,15 +148,18 @@ void do_shutdown(int sig) run("/bin/mount -n -o remount,ro dummydev /"); run("/bin/mount -n -o remount,ro /"); - _e("PID %d: %s ...", getpid(), sig == SIGINT || sig == SIGUSR1 ? "Rebooting" : "Halting"); + _e("PID %d: %s ...", getpid(), op == SHUT_REBOOT ? "Rebooting" : "Halting"); do_sleep(5); - _d("%s.", sig == SIGINT || sig == SIGUSR1 ? "Rebooting" : "Halting"); - if (sig == SIGINT || sig == SIGUSR1) + _d("%s.", op == SHUT_REBOOT ? "Rebooting" : "Halting"); + if (op == SHUT_REBOOT) reboot(RB_AUTOBOOT); - reboot(RB_POWER_OFF); - reboot(RB_HALT_SYSTEM); /* Eh, what? Should not get here ... */ + if (op == SHUT_OFF) + reboot(RB_POWER_OFF); + + /* Also fallback if any of the other two fails */ + reboot(RB_HALT_SYSTEM); } /* @@ -142,11 +172,35 @@ static void sighup_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events)) } /* - * Shut down on INT PWR + * SIGINT: Should generate condition, for now reboot */ -static void sigint_cb(uev_t *w, void *UNUSED(arg), int UNUSED(events)) +static void sigint_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events)) { - do_shutdown(w->signo); + do_shutdown(SHUT_REBOOT); +} + +/* + * SIGUSR1: BusyBox style halt + */ +static void sigusr1_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events)) +{ + do_shutdown(SHUT_HALT); +} + +/* + * SIGUSR2: BusyBox style poweroff + */ +static void sigusr2_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events)) +{ + do_shutdown(SHUT_OFF); +} + +/* + * SIGTERM: BusyBox style reboot + */ +static void sigterm_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events)) +{ + do_shutdown(SHUT_REBOOT); } /* @@ -237,6 +291,9 @@ void sig_unblock(void) sigaddset(&nmask, SIGSTOP); sigaddset(&nmask, SIGTSTP); sigaddset(&nmask, SIGCONT); + sigaddset(&nmask, SIGTERM); + sigaddset(&nmask, SIGUSR1); + sigaddset(&nmask, SIGUSR2); sigprocmask(SIG_UNBLOCK, &nmask, NULL); /* Reset signal handlers that were set by the parent process */ @@ -261,12 +318,10 @@ void sig_setup(uev_ctx_t *ctx) uev_signal_init(ctx, &sigint_watcher, sigint_cb, NULL, SIGINT); uev_signal_init(ctx, &sigpwr_watcher, sigint_cb, NULL, SIGPWR); - /* Ignore SIGUSR1/2 for now, only BusyBox init implements them as reboot+halt. */ -// uev_signal_init(&ctx, &sigusr1_watcher, reopen_initctl_cb, NULL, SIGUSR1); -// uev_signal_init(&ctx, &sigusr2_watcher, pwrdwn_cb, NULL, SIGUSR2); - - /* Init must ignore SIGTERM. May otherwise get false SIGTERM in forked children! */ - IGNSIG(sa, SIGTERM, 0); + /* BusyBox init style signals for halt, power-off and reboot. */ + uev_signal_init(ctx, &sigusr1_watcher, sigusr1_cb, NULL, SIGUSR1); + uev_signal_init(ctx, &sigusr2_watcher, sigusr2_cb, NULL, SIGUSR2); + uev_signal_init(ctx, &sigterm_watcher, sigterm_cb, NULL, SIGTERM); /* Some C APIs may need SIGALRM for implementing timers. */ IGNSIG(sa, SIGALRM, 0); @@ -285,8 +340,6 @@ void sig_setup(uev_ctx_t *ctx) uev_signal_init(ctx, &sigtstp_watcher, sigstop_cb, NULL, SIGTSTP); uev_signal_init(ctx, &sigcont_watcher, sigcont_cb, NULL, SIGCONT); - /* Disable CTRL-ALT-DELETE from kernel, we handle shutdown gracefully with SIGINT, above */ - reboot(RB_DISABLE_CAD); setsid(); } diff --git a/sig.h b/sig.h index 22a0a2bd..5d0160da 100644 --- a/sig.h +++ b/sig.h @@ -55,7 +55,13 @@ sigaction(sig, &sa, NULL); \ } while (0) -void do_shutdown (int sig); +typedef enum { + SHUT_OFF, + SHUT_HALT, + SHUT_REBOOT +} shutop_t; + +void do_shutdown (shutop_t op); int sig_stopped (void); void sig_init (void); void sig_unblock (void); diff --git a/sm.c b/sm.c index 9f2b6ff9..34b2834a 100644 --- a/sm.c +++ b/sm.c @@ -155,12 +155,12 @@ restart: svc_clean_dynamic(service_unregister); if (0 == runlevel) { - do_shutdown(SIGUSR2); + do_shutdown(SHUT_OFF); sm->state = SM_RUNNING_STATE; break; } if (6 == runlevel) { - do_shutdown(SIGUSR1); + do_shutdown(SHUT_REBOOT); sm->state = SM_RUNNING_STATE; break; }