/* * openrc-init.c * This is the init process (pid 1) for OpenRC. * * This is based on code written by James Hammons , so * I would like to publically thank him for his work. */ /* * Copyright (c) 2017 The OpenRC Authors. * See the Authors file at the top-level directory of this distribution and * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS * * This file is part of OpenRC. It is subject to the license terms in * the LICENSE file found in the top-level directory of this * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE * This file may not be copied, modified, propagated, or distributed * except according to the terms contained in the LICENSE file. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SELINUX # include #endif #include "rc.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"; static void do_openrc(const char *runlevel) { pid_t pid; sigset_t all_signals; sigset_t our_signals; sigfillset(&all_signals); /* block all signals */ sigprocmask(SIG_BLOCK, &all_signals, &our_signals); pid = fork(); switch (pid) { case -1: perror("fork"); exit(1); break; case 0: setsid(); /* unblock all signals */ sigprocmask(SIG_UNBLOCK, &all_signals, NULL); printf("Starting %s runlevel\n", runlevel); execlp("openrc", "openrc", runlevel, NULL); perror("exec"); exit(1); break; default: /* restore our signal mask */ sigprocmask(SIG_SETMASK, &our_signals, NULL); while (waitpid(pid, NULL, 0) != pid) if (errno == ECHILD) break; break; } } static void init(const char *default_runlevel) { const char *runlevel = NULL; do_openrc("sysinit"); do_openrc("boot"); if (default_runlevel) runlevel = default_runlevel; else runlevel = rc_conf_value("rc_default_runlevel"); if (!runlevel) runlevel = rc_default_runlevel; if (!rc_runlevel_exists(runlevel)) { printf("%s is an invalid runlevel\n", runlevel); runlevel = rc_default_runlevel; } do_openrc(runlevel); log_wtmp("reboot", "~~", 0, RUN_LVL, "~~"); } static void handle_reexec(char *my_name) { execlp(my_name, my_name, "reexec", NULL); return; } 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); sync(); reboot(cmd); } static void run_program(const char *prog) { sigset_t full; sigset_t old; pid_t pid; /* We need to block signals until we have forked */ sigfillset(&full); sigprocmask(SIG_SETMASK, &full, &old); pid = fork(); if (pid == -1) { perror("init"); return; } if (pid == 0) { /* Unmask signals */ sigprocmask(SIG_SETMASK, &old, NULL); execl(prog, prog, (char *)NULL); perror("init"); exit(1); } /* Unmask signals and wait for child */ sigprocmask(SIG_SETMASK, &old, NULL); if (rc_waitpid(pid) == -1) perror("init"); } static void open_shell(void) { const char *shell; struct passwd *pw; #ifdef __linux__ const char *sys = rc_sys(); /* VSERVER systems cannot really drop to shells */ if (sys && strcmp(sys, RC_SYS_VSERVER) == 0) { execlp("halt", "halt", "-f", (char *) NULL); perror("init"); return; } #endif shell = rc_conf_value("rc_shell"); /* No shell set, so obey env, then passwd, then default to /bin/sh */ if (!shell) { shell = getenv("SHELL"); if (!shell) { pw = getpwuid(getuid()); if (pw) shell = pw->pw_shell; if (!shell) shell = "/bin/sh"; } } run_program(shell); } static void handle_single(void) { do_openrc("single"); } static void reap_zombies(void) { pid_t pid; for (;;) { pid = waitpid(-1, NULL, WNOHANG); if (pid == 0) break; else if (pid == -1) { if (errno == ECHILD) break; perror("waitpid"); continue; } } } static void signal_handler(int sig) { switch (sig) { case SIGINT: handle_shutdown("reboot", RB_AUTOBOOT); break; case SIGTERM: #ifdef SIGPWR case SIGPWR: #endif handle_shutdown("shutdown", RB_HALT_SYSTEM); break; case SIGCHLD: reap_zombies(); break; default: printf("Unknown signal received, %d\n", sig); break; } } 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 if (getpid() != 1) return 1; #ifdef HAVE_SELINUX if (getenv("SELINUX_INIT") == NULL) { if (is_selinux_enabled() != 1) { if (selinux_init_load_policy(&enforce) == 0) { putenv("SELINUX_INIT=YES"); execv(argv[0], argv); } else { if (enforce > 0) { /* * SELinux in enforcing mode but load_policy failed * At this point, we probably can't open /dev/console, * so log() won't work */ fprintf(stderr,"Unable to load SELinux Policy.\n"); fprintf(stderr,"Machine is in enforcing mode.\n"); fprintf(stderr,"Halting now.\n"); exit(1); } } } } #endif printf("OpenRC init version %s starting\n", VERSION); 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); /* 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); 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); } } return 0; }