diff --git a/NEWS.md b/NEWS.md index 9d5117f8..90a452b6 100644 --- a/NEWS.md +++ b/NEWS.md @@ -3,6 +3,13 @@ This file will contain a list of notable changes for each release. Note the information in this file is in reverse order. +## OpenRC-0.25 + +This version contains an OpenRC-specific implementation of init for +Linux which can be used in place of sysvinit or any other init process. +For information on its usage, see the man pages for openrc-init (8) and +openrc-shutdown (8). + ## OpenRC-0.24.1 This version starts cleaning up the dependencies so that rc_parallel diff --git a/etc/rc.conf b/etc/rc.conf index 028ceab4..689e6be2 100644 --- a/etc/rc.conf +++ b/etc/rc.conf @@ -178,6 +178,11 @@ # "xenU" - XenU Domain (Linux and NetBSD) #rc_sys="" +# if you use openrc-init, which is currently only available on Linux, +# this is the default runlevel to activate after "sysinit" and "boot" +# when booting. +#rc_default_runlevel="default" + # on Linux and Hurd, this is the number of ttys allocated for logins # It is used in the consolefont, keymaps, numlock and termencoding # service scripts. diff --git a/man/Makefile b/man/Makefile index 48c58429..a72b7e72 100644 --- a/man/Makefile +++ b/man/Makefile @@ -9,7 +9,7 @@ MAN8= rc-service.8 rc-status.8 rc-update.8 openrc.8 openrc-run.8 \ service.8 start-stop-daemon.8 supervise-daemon.8 ifeq (${OS},Linux) -MAN8 += rc-sstat.8 +MAN8 += rc-sstat.8 openrc-init.8 openrc-shutdown.8 endif # Handy macro to create symlinks diff --git a/man/openrc-init.8 b/man/openrc-init.8 new file mode 100644 index 00000000..93068f10 --- /dev/null +++ b/man/openrc-init.8 @@ -0,0 +1,46 @@ +.\" 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/master/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/master/LICENSE +.\" This file may not be copied, modified, propagated, or distributed +.\" except according to the terms contained in the LICENSE file. +.\" +.Dd April 6, 2017 +.Dt openrc-init 8 SMM +.Os OpenRC +.Sh NAME +.Nm openrc-init +.Nd the parent of all processes +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +.Nm +is an init process which can be an alternative to sysvinit or any other +init process. +.Pp +To use +.Nm +configure your boot loader to invoke it or symlink it to /sbin/init. +Also, you will need to use +.Xr openrc-shutdown 8 , +to halt, reboot or poweroff the system. +.Pp +The default runlevel is read from the init command line, the +rc_default_runlevel setting in rc.conf, the kernel command line, or it is +assumed to be "default" if it is not set in any of these places. +.Pp +.Nm +doesn't manage getty's directly, so you will need to manage them another +way. For example, you can use the agetty service script as described in +agetty-guide.md in this distribution. +.Sh BUGS +OpenRC 0.25 contains the first release of this init process. +I do not know of any specific issues. However, if you use it, please be +aware that there may be bugs and report any issues you find. +.Sh SEE ALSO +.Xr openrc-shutdown 8 , +.Sh AUTHORS +.An William Hubbs diff --git a/man/openrc-shutdown.8 b/man/openrc-shutdown.8 new file mode 100644 index 00000000..98ec64a6 --- /dev/null +++ b/man/openrc-shutdown.8 @@ -0,0 +1,42 @@ +.\" 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/master/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/master/LICENSE +.\" This file may not be copied, modified, propagated, or distributed +.\" except according to the terms contained in the LICENSE file. +.\" +.Dd April 6, 2017 +.Dt openrc-shutdown 8 SMM +.Os OpenRC +.Sh NAME +.Nm openrc-shutdown +.Nd bring the system down +.Sh SYNOPSIS +.Nm +.Op Fl H , -halt +.Op Fl k , -kexec +.Op Fl p , -poweroff +.Op Fl r , -reboot +.Sh DESCRIPTION +.Nm +is the utility that communicates with openrc-init(8) to bring down the +system. The following options affect how the system is brought down: +.Bl -tag -width "poweroff" +.It Fl H , -halt +Stop all services, kill all remaining processes and halt the system. +.It Fl k , -kexec +Stop all services, kill all processes and boot directly into a new +kernel loaded via kexec(8). +.It Fl p , -poweroff +Stop all services, kill all processes and power off the system. +.It Fl r , -reboot +Stop all services, kill all processes and reboot the system. +.El +.Sh SEE ALSO +.Xr openrc-init 8 , +.Xr kexec 8 , +.Sh AUTHORS +.An William Hubbs diff --git a/src/librc/rc.h.in b/src/librc/rc.h.in index 92ecbb4e..1f984d52 100644 --- a/src/librc/rc.h.in +++ b/src/librc/rc.h.in @@ -39,6 +39,7 @@ extern "C" { #define RC_CONFDIR RC_SYSCONFDIR "/conf.d" #define RC_PLUGINDIR RC_LIBDIR "/plugins" +#define RC_INIT_FIFO RC_SVCDIR"/init.ctl" #define RC_PROFILE_ENV RC_SYSCONFDIR "/profile.env" #define RC_SYS_WHITELIST RC_LIBEXECDIR "/conf.d/env_whitelist" #define RC_USR_WHITELIST RC_SYSCONFDIR "/conf.d/env_whitelist" diff --git a/src/rc/.gitignore b/src/rc/.gitignore index c9779194..c3e7b3f6 100644 --- a/src/rc/.gitignore +++ b/src/rc/.gitignore @@ -59,4 +59,6 @@ mark_service_failed rc-abort rc openrc +openrc-init openrc-run +openrc-shutdown diff --git a/src/rc/Makefile b/src/rc/Makefile index 74d74a08..2bc03f76 100644 --- a/src/rc/Makefile +++ b/src/rc/Makefile @@ -1,3 +1,7 @@ +include ../../Makefile.inc +MK= ../../mk +include ${MK}/os.mk + SRCS= checkpath.c do_e.c do_mark_service.c do_service.c \ do_value.c fstabinfo.c is_newer_than.c is_older_than.c \ mountinfo.c openrc-run.c rc-abort.c rc.c \ @@ -9,6 +13,10 @@ ifeq (${MKSELINUX},yes) SRCS+= rc-selinux.c endif +ifeq (${OS},Linux) +SRCS+= openrc-init.c openrc-shutdown.c +endif + CLEANFILES= version.h rc-selinux.o BINDIR= ${PREFIX}/bin @@ -34,6 +42,11 @@ RC_SBINPROGS= mark_service_starting mark_service_started \ mark_service_inactive mark_service_wasinactive \ mark_service_hotplugged mark_service_failed \ rc-abort swclock + +ifeq (${OS},Linux) +SBINPROGS+= openrc-init openrc-shutdown +endif + ALL_PROGS= ${BINPROGS} ${SBINPROGS} ${RC_BINPROGS} ${RC_SBINPROGS} CLEANFILES+= ${ALL_PROGS} @@ -41,8 +54,6 @@ LOCAL_CPPFLAGS=-I../includes -I../librc -I../libeinfo LOCAL_LDFLAGS=-L../librc -L../libeinfo LDADD+= -lutil -lrc -leinfo -include ../../Makefile.inc -MK= ../../mk include ${MK}/prog.mk include ${MK}/gitver.mk include ${MK}/cc.mk @@ -96,6 +107,9 @@ veinfo vewarn vebegin veend vewend veindent veoutdent: do_e.o rc-misc.o fstabinfo: fstabinfo.o _usage.o rc-misc.o ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} +openrc-init: openrc-init.o + ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} + is_newer_than: is_newer_than.o rc-misc.o ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} @@ -114,6 +128,9 @@ mountinfo: mountinfo.o _usage.o rc-misc.o openrc rc: rc.o rc-logger.o rc-misc.o rc-plugin.o _usage.o ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} +openrc-shutdown: openrc-shutdown.o _usage.o + ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} + openrc-run runscript: openrc-run.o _usage.o rc-misc.o rc-plugin.o ifeq (${MKSELINUX},yes) openrc-run runscript: rc-selinux.o diff --git a/src/rc/openrc-init.c b/src/rc/openrc-init.c new file mode 100644 index 00000000..e7d62432 --- /dev/null +++ b/src/rc/openrc-init.c @@ -0,0 +1,168 @@ +/* + * openrc-init.c + * This is the init process (pid 1) for OpenRC. + */ + +/* + * 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/master/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/master/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 "helpers.h" +#include "rc.h" +#include "version.h" + +static const char *rc_default_runlevel = "default"; + +static pid_t do_openrc(const char *runlevel) +{ + pid_t pid; + + pid = fork(); + switch(pid) { + case -1: + perror("fork"); + break; + case 0: + setsid(); + printf("Starting %s runlevel\n", runlevel); + execl("/sbin/openrc", "/sbin/openrc", runlevel, NULL); + perror("exec"); + break; + default: + break; + } + return pid; +} + +static void init(const char *default_runlevel) +{ + const char *runlevel = NULL; + pid_t pid; + + pid = do_openrc("sysinit"); + waitpid(pid, NULL, 0); + pid = do_openrc("boot"); + waitpid(pid, NULL, 0); + 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; + } + pid = do_openrc(runlevel); + waitpid(pid, NULL, 0); +} + +static void handle_shutdown(const char *runlevel, int cmd) +{ + pid_t pid; + + pid = do_openrc(runlevel); + while (waitpid(pid, NULL, 0) != pid); + sync(); + reboot(cmd); +} + +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 SIGCHLD: + reap_zombies(); + break; + default: + printf("Unknown signal received, %d\n", sig); + break; + } +} + +int main(int argc, char **argv) +{ + char *default_runlevel = NULL; + char buf[2048]; + int count; + FILE *fifo; + struct sigaction sa; + + if (getpid() != 1) + return 1; + + if (argc > 1) + default_runlevel = argv[1]; + + printf("OpenRC init version %s starting\n", VERSION); + init(default_runlevel); + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = signal_handler; + sigaction(SIGCHLD, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + reboot(RB_DISABLE_CAD); + + if (mkfifo(RC_INIT_FIFO, 0600) == -1) + 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, 2048, 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); + } + return 0; +} diff --git a/src/rc/openrc-shutdown.c b/src/rc/openrc-shutdown.c new file mode 100644 index 00000000..b68ffd36 --- /dev/null +++ b/src/rc/openrc-shutdown.c @@ -0,0 +1,119 @@ +/* + * openrc-shutdown.c + * If you are using OpenRC's provided init, this will shut down or + * reboot your system. + */ + +/* + * Copyright 2017 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/master/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/master/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 "einfo.h" +#include "rc.h" +#include "helpers.h" +#include "_usage.h" + +const char *applet = NULL; +const char *extraopts = NULL; +const char *getoptstring = "kpr" getoptstring_COMMON; +const struct option longopts[] = { + { "halt", no_argument, NULL, 'H'}, + { "kexec", no_argument, NULL, 'k'}, + { "poweroff", no_argument, NULL, 'p'}, + { "reboot", no_argument, NULL, 'r'}, + longopts_COMMON +}; +const char * const longopts_help[] = { + "halt the system", + "reboot the system using kexec", + "power off the system", + "reboot the system", + longopts_help_COMMON +}; +const char *usagestring = NULL; +const char *exclusive = "Select one of --halt, --kexec, --poweroff or --reboot"; + +static void send_cmd(const char *cmd) +{ + FILE *fifo; + size_t ignored; + + fifo = fopen(RC_INIT_FIFO, "w"); + + if (!fifo) { + perror("fopen"); + return; + } + + ignored = fwrite(cmd, 1, strlen(cmd), fifo); + if (ignored != strlen(cmd)) + printf("Error writing to init fifo\n"); + fclose(fifo); +} + +int main(int argc, char **argv) +{ + int opt; + int cmd_count = 0; + bool do_halt = false; + bool do_kexec = false; + bool do_poweroff = false; + bool do_reboot = false; + + applet = basename_c(argv[0]); +if (geteuid() != 0) + eerrorx("%s: you must be root\n", applet); + while ((opt = getopt_long(argc, argv, getoptstring, + longopts, (int *) 0)) != -1) + { + switch (opt) { + case 'H': + do_halt = true; + cmd_count++; + break; + case 'k': + do_kexec = true; + cmd_count++; + break; + case 'p': + do_poweroff = true; + cmd_count++; + break; + case 'r': + do_reboot = true; + cmd_count++; + break; + case_RC_COMMON_GETOPT + } + } + if (cmd_count != 1) { + eerror("%s: %s\n", applet, exclusive); + usage(EXIT_FAILURE); + } + if (do_halt) + send_cmd("halt"); + else if (do_kexec) + send_cmd("kexec"); + else if (do_poweroff) + send_cmd("poweroff"); + else if (do_reboot) + send_cmd("reboot"); + return 0; +}