Skip to content

Commit

Permalink
openrc-init: rework signal handling
Browse files Browse the repository at this point in the history
Minimize the amount of work done in the signal handler by maintaining a
set of received signals and deferring the actual work to the main loop.

Treat SIGRTMIN+4 as a power-off signal for compatibility with
systemd-machined.

Adjust handle_shutdown to skip sending SIGTERM / SIGKILL if there are no
processes remaining.

Signed-off-by: Mike Gilbert <floppym@gentoo.org>
  • Loading branch information
floppym committed May 27, 2024
1 parent 3d30b6f commit 7808607
Showing 1 changed file with 149 additions and 64 deletions.
213 changes: 149 additions & 64 deletions src/openrc-init/openrc-init.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* except according to the terms contained in the LICENSE file.
*/

#include <assert.h>
#include <errno.h>
#include <pwd.h>
#include <signal.h>
Expand All @@ -38,13 +39,34 @@
#endif

#include "rc.h"
#include "helpers.h"
#include "plugin.h"
#include "wtmp.h"
#include "version.h"

static const char *path_default = "/sbin:/usr/sbin:/bin:/usr/bin";
static const char *rc_default_runlevel = "default";

/* wait for children until a signal occurs or we run out of children */
static pid_t wait_children(int options)
{
pid_t pid;
do {
pid = waitpid(-1, NULL, options);
} while (pid > 0);
return pid < 0 ? -errno : pid;
}

/* wait for children to exit, stop when a specific child exits */
static pid_t wait_child(pid_t child)
{
pid_t pid;
do {
pid = waitpid(-1, NULL, 0);
} while ((pid > 0 && pid != child) || (pid < 0 && errno == EINTR));
return pid < 0 ? -errno : pid;
}

static void do_openrc(const char *runlevel)
{
pid_t pid;
Expand Down Expand Up @@ -72,9 +94,7 @@ static void do_openrc(const char *runlevel)
default:
/* restore our signal mask */
sigprocmask(SIG_SETMASK, &our_signals, NULL);
while (waitpid(pid, NULL, 0) != pid)
if (errno == ECHILD)
break;
wait_child(pid);
break;
}
}
Expand Down Expand Up @@ -104,18 +124,44 @@ static void handle_reexec(char *my_name)
return;
}

static void alarm_handler(int signum RC_UNUSED) {
/* do nothing */
}

static void handle_shutdown(const char *runlevel, int cmd)
{
struct timespec ts;

do_openrc(runlevel);
printf("Sending the final term signal\n");
kill(-1, SIGTERM);
ts.tv_sec = 3;
ts.tv_nsec = 0;
nanosleep(&ts, NULL);
printf("Sending the final kill signal\n");
kill(-1, SIGKILL);

/* wait on any children that have already exited */
if (wait_children(WNOHANG) != -ECHILD) {
pid_t pid;
sigset_t signals;
struct sigaction sa = { .sa_handler = alarm_handler };

sigaction(SIGALRM, &sa, NULL);

sigfillset(&signals);
sigdelset(&signals, SIGALRM);
sigprocmask(SIG_SETMASK, &signals, NULL);

printf("Sending the final term signal\n");
kill(-1, SIGTERM);

/* Wait up to 3 seconds for children to exit */
alarm(3);
pid = wait_children(0);
alarm(0);

if (pid != -ECHILD) {
printf("Sending the final kill signal\n");
kill(-1, SIGKILL);

alarm(3);
wait_children(0);
alarm(0);
}
}

sync();
reboot(cmd);
}
Expand Down Expand Up @@ -143,7 +189,7 @@ static void run_program(const char *prog)
}
/* Unmask signals and wait for child */
sigprocmask(SIG_SETMASK, &old, NULL);
if (rc_waitpid(pid) == -1)
if (wait_child(pid) < 0)
perror("init");
}

Expand Down Expand Up @@ -184,53 +230,102 @@ static void handle_single(void)
do_openrc("single");
}

static void reap_zombies(void)
{
pid_t pid;
static volatile sig_atomic_t received_signals[64] = { 0 };

for (;;) {
pid = waitpid(-1, NULL, WNOHANG);
if (pid == 0)
break;
else if (pid == -1) {
if (errno == ECHILD)
break;
perror("waitpid");
continue;
}
static bool check_signal(int signum)
{
assert(signum >= 0 && signum < 64);
if (received_signals[signum]) {
received_signals[signum] = 0;
return true;
}
return false;
}

static void signal_handler(int sig)
static void signal_handler(int signum)
{
switch (sig) {
case SIGINT:
handle_shutdown("reboot", RB_AUTOBOOT);
break;
case SIGTERM:
assert(signum >= 0 && signum < 64);
received_signals[signum] = 1;
}

static void setup_signal(sigset_t *signals, int signum)
{
struct sigaction sa = { .sa_handler = signal_handler };
sigaction(signum, &sa, NULL);
sigaddset(signals, signum);
}

static void setup_signals()
{
sigset_t signals;

sigfillset(&signals);
sigprocmask(SIG_SETMASK, &signals, NULL);

sigemptyset(&signals);
setup_signal(&signals, SIGCHLD);
setup_signal(&signals, SIGINT);
setup_signal(&signals, SIGTERM);
#ifdef SIGPWR
case SIGPWR:
setup_signal(&signals, SIGPWR);
#endif
handle_shutdown("shutdown", RB_HALT_SYSTEM);
break;
case SIGCHLD:
reap_zombies();
break;
default:
printf("Unknown signal received, %d\n", sig);
break;
}
setup_signal(&signals, SIGRTMIN+3);
setup_signal(&signals, SIGRTMIN+4);
setup_signal(&signals, SIGRTMIN+5);
setup_signal(&signals, SIGRTMIN+6);
setup_signal(&signals, SIGRTMIN+13);
setup_signal(&signals, SIGRTMIN+14);
setup_signal(&signals, SIGRTMIN+15);
setup_signal(&signals, SIGRTMIN+16);
sigprocmask(SIG_UNBLOCK, &signals, NULL);
}

static void process_signals()
{
if (check_signal(SIGRTMIN+16))
reboot(RB_KEXEC);

if (check_signal(SIGRTMIN+15))
reboot(RB_AUTOBOOT);

if (check_signal(SIGRTMIN+14))
reboot(RB_POWER_OFF);

if (check_signal(SIGRTMIN+13))
reboot(RB_HALT_SYSTEM);

if (check_signal(SIGRTMIN+6))
handle_shutdown("reboot", RB_KEXEC);

if (check_signal(SIGRTMIN+5))
handle_shutdown("reboot", RB_AUTOBOOT);

if (check_signal(SIGINT))
handle_shutdown("reboot", RB_AUTOBOOT);

if (check_signal(SIGRTMIN+4))
handle_shutdown("shutdown", RB_POWER_OFF);

if (check_signal(SIGTERM))
handle_shutdown("shutdown", RB_POWER_OFF);
#ifdef SIGPWR
if (check_signal(SIGPWR))
handle_shutdown("shutdown", RB_HALT_SYSTEM);
#endif
if (check_signal(SIGRTMIN+3))
handle_shutdown("shutdown", RB_HALT_SYSTEM);

if (check_signal(SIGCHLD))
wait_children(WNOHANG);
}

int main(int argc, char **argv)
{
char *default_runlevel;
char buf[2048];
int count;
size_t count;
FILE *fifo;
bool reexec = false;
sigset_t signals;
struct sigaction sa;
#ifdef HAVE_SELINUX
int enforce = 0;
#endif
Expand Down Expand Up @@ -271,25 +366,8 @@ int main(int argc, char **argv)
if (default_runlevel && strcmp(default_runlevel, "reexec") == 0)
reexec = true;

/* block all signals we do not handle */
sigfillset(&signals);
sigdelset(&signals, SIGCHLD);
sigdelset(&signals, SIGINT);
sigdelset(&signals, SIGTERM);
#ifdef SIGPWR
sigdelset(&signals, SIGPWR);
#endif
sigprocmask(SIG_SETMASK, &signals, NULL);
setup_signals();

/* install signal handler */
memset(&sa, 0, sizeof(sa));
sa.sa_handler = signal_handler;
sigaction(SIGCHLD, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
#ifdef SIGPWR
sigaction(SIGPWR, &sa, NULL);
#endif
reboot(RB_DISABLE_CAD);

/* set default path */
Expand All @@ -302,16 +380,23 @@ int main(int argc, char **argv)
perror("mkfifo");

for (;;) {
process_signals();

/* This will block until a command is sent down the pipe... */
fifo = fopen(RC_INIT_FIFO, "r");
if (!fifo) {
if (errno != EINTR)
perror("fopen");
continue;
}
count = fread(buf, 1, sizeof(buf) - 1, fifo);

do {
count = fread(buf, 1, sizeof(buf) - 1, fifo);
} while (count == 0 && ferror(fifo) && errno == EINTR);

buf[count] = 0;
fclose(fifo);

printf("PID1: Received \"%s\" from FIFO...\n", buf);
if (strcmp(buf, "halt") == 0)
handle_shutdown("shutdown", RB_HALT_SYSTEM);
Expand Down

0 comments on commit 7808607

Please sign in to comment.