Adopt BusyBox init signal handling for halt/reboot/poweroff

This patch adopts the BusyBox init signals for handling halt, reboot,
and poweroff.  The SIGINT and SIGPWR signals still behave the same as
before, even though they likely will become configurable in a future
release.

Note: the disabling of Ctrl-Alt-Delete (SIGINT) is also removed!

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
Joachim Nilsson
2016-07-29 16:47:38 +02:00
parent 0df0f019e3
commit eeb59eb22b
6 changed files with 144 additions and 41 deletions
+39 -6
View File
@@ -245,12 +245,45 @@ start any new ones for you, without rebooting the system.
Rebooting & Halting Rebooting & Halting
------------------- -------------------
Finit handles `SIGUSR1` and `SIGUSR2` for reboot and halt, and listens Finit is often used on embedded and small Linux systems with BusyBox.
to `/dev/initctl` so system reboot and halt commands also work. The For compatibility with its existing toolset (halt, poweroff, reboot),
latter is implemented in the optional `initctl.so` plugin and can be the following signals have been adopted:
accessed with the traditional `telinit` command line tool, symlinked to
`finit`. Hence, if finit is your system init, then `init q` will work * `SIGUSR1`
as the UNIX beards intended. 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 ```shell
~ $ telinit ~ $ telinit
+11
View File
@@ -18,6 +18,17 @@ very low dependency on external packages.
General 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. <sys/power/{ok,fail,low}> 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, <sys/key/ctrlaltdel> and <sys/key/signal> 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 compile-time support for running `/bin/sh` instead of `getty`
* Add `!` or `[wait <pidfile>]` to service directive to let finit pause * Add `!` or `[wait <pidfile>]` to service directive to let finit pause
and wait for a pidfile before starting processes -- both at boot and and wait for a pidfile before starting processes -- both at boot and
+2 -2
View File
@@ -255,7 +255,7 @@ static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
switch (rq.runlevel) { switch (rq.runlevel) {
case '0': case '0':
_d("Halting system (SIGUSR2)"); _d("Halting system (SIGUSR2)");
do_shutdown(SIGUSR2); do_shutdown(SHUT_OFF);
break; break;
case 's': case 's':
@@ -272,7 +272,7 @@ static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
case '6': case '6':
_d("Rebooting system (SIGUSR1)"); _d("Rebooting system (SIGUSR1)");
do_shutdown(SIGUSR1); do_shutdown(SHUT_REBOOT);
break; break;
default: default:
+83 -30
View File
@@ -21,21 +21,47 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
* *
* The standard SysV init only responds to the following signals: * Finit is often used on embedded and small Linux systems with BusyBox.
* SIGHUP * For compatibility with its existing toolset (halt, poweroff, reboot),
* Has the same effect as telinit q. * the following signals have been adopted:
* *
* SIGUSR1 * SIGHUP
* On receipt of this signals, init closes and re-opens its control * Same effect as init/telinit q, reloads *.conf in /etc/finit.d/
* fifo, /dev/initctl. Useful for bootscripts when /dev is remounted.
* *
* SIGINT * SIGUSR1
* Normally the kernel sends this signal to init when CTRL-ALT-DEL is * Calls shutdown hooks, including HOOK_SHUTDOWN, stops all running
* pressed. It activates the ctrlaltdel action. * processes, and unmounts all file systems. Then tells kernel to
* halt. Most people these days want SIGUSR2 though.
* *
* SIGWINCH * (SysV init and systemd use this to re-open their FIFO/D-Bus.)
* The kernel sends this signal when the KeyboardSignal key is hit. *
* It activates the kbrequest action. * 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 <sched.h> #include <sched.h>
@@ -54,12 +80,13 @@
#include "service.h" #include "service.h"
static int stopped = 0; 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 sigchld_watcher, sigsegv_watcher;
static uev_t sigstop_watcher, sigtstp_watcher, sigcont_watcher; static uev_t sigstop_watcher, sigtstp_watcher, sigcont_watcher;
void do_shutdown(int sig) void do_shutdown(shutop_t op)
{ {
touch(SYNC_SHUTDOWN); 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 dummydev /");
run("/bin/mount -n -o remount,ro /"); 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); do_sleep(5);
_d("%s.", sig == SIGINT || sig == SIGUSR1 ? "Rebooting" : "Halting"); _d("%s.", op == SHUT_REBOOT ? "Rebooting" : "Halting");
if (sig == SIGINT || sig == SIGUSR1) if (op == SHUT_REBOOT)
reboot(RB_AUTOBOOT); reboot(RB_AUTOBOOT);
reboot(RB_POWER_OFF); if (op == SHUT_OFF)
reboot(RB_HALT_SYSTEM); /* Eh, what? Should not get here ... */ 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 <sys/key/ctrlaltdel> 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, SIGSTOP);
sigaddset(&nmask, SIGTSTP); sigaddset(&nmask, SIGTSTP);
sigaddset(&nmask, SIGCONT); sigaddset(&nmask, SIGCONT);
sigaddset(&nmask, SIGTERM);
sigaddset(&nmask, SIGUSR1);
sigaddset(&nmask, SIGUSR2);
sigprocmask(SIG_UNBLOCK, &nmask, NULL); sigprocmask(SIG_UNBLOCK, &nmask, NULL);
/* Reset signal handlers that were set by the parent process */ /* 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, &sigint_watcher, sigint_cb, NULL, SIGINT);
uev_signal_init(ctx, &sigpwr_watcher, sigint_cb, NULL, SIGPWR); uev_signal_init(ctx, &sigpwr_watcher, sigint_cb, NULL, SIGPWR);
/* Ignore SIGUSR1/2 for now, only BusyBox init implements them as reboot+halt. */ /* BusyBox init style signals for halt, power-off and reboot. */
// uev_signal_init(&ctx, &sigusr1_watcher, reopen_initctl_cb, NULL, SIGUSR1); uev_signal_init(ctx, &sigusr1_watcher, sigusr1_cb, NULL, SIGUSR1);
// uev_signal_init(&ctx, &sigusr2_watcher, pwrdwn_cb, NULL, SIGUSR2); uev_signal_init(ctx, &sigusr2_watcher, sigusr2_cb, NULL, SIGUSR2);
uev_signal_init(ctx, &sigterm_watcher, sigterm_cb, NULL, SIGTERM);
/* Init must ignore SIGTERM. May otherwise get false SIGTERM in forked children! */
IGNSIG(sa, SIGTERM, 0);
/* Some C APIs may need SIGALRM for implementing timers. */ /* Some C APIs may need SIGALRM for implementing timers. */
IGNSIG(sa, SIGALRM, 0); 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, &sigtstp_watcher, sigstop_cb, NULL, SIGTSTP);
uev_signal_init(ctx, &sigcont_watcher, sigcont_cb, NULL, SIGCONT); 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(); setsid();
} }
+7 -1
View File
@@ -55,7 +55,13 @@
sigaction(sig, &sa, NULL); \ sigaction(sig, &sa, NULL); \
} while (0) } 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); int sig_stopped (void);
void sig_init (void); void sig_init (void);
void sig_unblock (void); void sig_unblock (void);
+2 -2
View File
@@ -155,12 +155,12 @@ restart:
svc_clean_dynamic(service_unregister); svc_clean_dynamic(service_unregister);
if (0 == runlevel) { if (0 == runlevel) {
do_shutdown(SIGUSR2); do_shutdown(SHUT_OFF);
sm->state = SM_RUNNING_STATE; sm->state = SM_RUNNING_STATE;
break; break;
} }
if (6 == runlevel) { if (6 == runlevel) {
do_shutdown(SIGUSR1); do_shutdown(SHUT_REBOOT);
sm->state = SM_RUNNING_STATE; sm->state = SM_RUNNING_STATE;
break; break;
} }