Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

openrc-init refactoring #710

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
298 changes: 201 additions & 97 deletions src/openrc-init/openrc-init.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
* except according to the terms contained in the LICENSE file.
*/

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <signal.h>
#include <stdbool.h>
Expand All @@ -38,12 +40,35 @@
#endif

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

static const char *default_runlevel = NULL;
static const char *my_name = "openrc-init";
static const char *path_default = "/sbin:/usr/sbin:/bin:/usr/bin";
static const char *rc_default_runlevel = "default";
static const char * const 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)
{
Expand Down Expand Up @@ -72,14 +97,12 @@ 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;
}
}

static void init(const char *default_runlevel)
static void init()
{
const char *runlevel = NULL;
do_openrc("sysinit");
Expand All @@ -98,24 +121,50 @@ static void init(const char *default_runlevel)
log_wtmp("reboot", "~~", 0, RUN_LVL, "~~");
}

static void handle_reexec(char *my_name)
static void handle_reexec()
{
execlp(my_name, my_name, "reexec", NULL);
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 +192,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 +233,148 @@ 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 >= 1 && signum <= 64);
if (received_signals[signum - 1]) {
received_signals[signum - 1] = 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 >= 1 && signum <= 64);
received_signals[signum - 1] = 1;
}

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

static void setup_signals()
{
sigset_t signals;
sigfillset(&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_SETMASK, &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);
}

static void read_fifo()
{
static int fifo = -1;
ssize_t count;
char buf[10];

if (fifo < 0)
/* This will block until a process opens the fifo for writing */
fifo = open(RC_INIT_FIFO, O_RDONLY|O_CLOEXEC);

if (fifo < 0) {
if (errno != EINTR)
fprintf(stderr, "%s: open(%s): %s\n", my_name,
RC_INIT_FIFO, strerror(errno));
return;
}

count = read(fifo, buf, sizeof(buf) - 1);

if (count < 0) {
if (errno != EINTR)
fprintf(stderr, "%s: read(%s): %s\n", my_name,
RC_INIT_FIFO, strerror(errno));
/* Keep the fifo open to avoid sending SIGPIPE to the writer */
return;
}

buf[count] = 0;

close(fifo);
fifo = -1;

if (count == 0)
/* Another process opened the fifo without writing anything */
return;

printf("PID1: Received \"%s\" from FIFO...\n", buf);
if (strcmp(buf, "halt") == 0)
handle_shutdown("shutdown", RB_HALT_SYSTEM);
else if (strcmp(buf, "kexec") == 0)
handle_shutdown("reboot", RB_KEXEC);
else if (strcmp(buf, "poweroff") == 0)
handle_shutdown("shutdown", RB_POWER_OFF);
else if (strcmp(buf, "reboot") == 0)
handle_shutdown("reboot", RB_AUTOBOOT);
else if (strcmp(buf, "reexec") == 0)
handle_reexec();
else if (strcmp(buf, "single") == 0) {
handle_single();
open_shell();
init();
}
}

int main(int argc, char **argv)
{
char *default_runlevel;
char buf[2048];
int count;
FILE *fifo;
bool reexec = false;
sigset_t signals;
struct sigaction sa;
#ifdef HAVE_SELINUX
int enforce = 0;
#endif
Expand Down Expand Up @@ -263,71 +407,31 @@ int main(int argc, char **argv)

printf("OpenRC init version %s starting\n", VERSION);

if (argc > 0)
my_name = argv[0];

if (argc > 1)
default_runlevel = argv[1];
else
default_runlevel = NULL;

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 */
setenv("PATH", path_default, 1);

if (!reexec)
init(default_runlevel);
init();

if (mkfifo(RC_INIT_FIFO, 0600) == -1 && errno != EEXIST)
perror("mkfifo");

for (;;) {
/* 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);
buf[count] = 0;
fclose(fifo);
printf("PID1: Received \"%s\" from FIFO...\n", buf);
if (strcmp(buf, "halt") == 0)
handle_shutdown("shutdown", RB_HALT_SYSTEM);
else if (strcmp(buf, "kexec") == 0)
handle_shutdown("reboot", RB_KEXEC);
else if (strcmp(buf, "poweroff") == 0)
handle_shutdown("shutdown", RB_POWER_OFF);
else if (strcmp(buf, "reboot") == 0)
handle_shutdown("reboot", RB_AUTOBOOT);
else if (strcmp(buf, "reexec") == 0)
handle_reexec(argv[0]);
else if (strcmp(buf, "single") == 0) {
handle_single();
open_shell();
init(default_runlevel);
}
process_signals();
read_fifo();
}
return 0;
}
Loading