diff --git a/src/rc/Makefile b/src/rc/Makefile index ea4a8c81..982e7af1 100644 --- a/src/rc/Makefile +++ b/src/rc/Makefile @@ -14,7 +14,8 @@ SRCS+= rc-selinux.c endif ifeq (${OS},Linux) -SRCS+= kill_all.c openrc-init.c openrc-shutdown.c broadcast.c rc-wtmp.c +SRCS+= kill_all.c openrc-init.c openrc-shutdown.c rc-sysvinit.c broadcast.c \ + rc-wtmp.c endif CLEANFILES= version.h rc-selinux.o @@ -134,7 +135,7 @@ 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 rc-misc.o _usage.o broadcast.o rc-wtmp.o +openrc-shutdown: openrc-shutdown.o rc-misc.o _usage.o broadcast.o rc-wtmp.o rc-sysvinit.o ${CC} ${LOCAL_CFLAGS} ${LOCAL_LDFLAGS} ${CFLAGS} ${LDFLAGS} -o $@ $^ ${LDADD} openrc-run runscript: openrc-run.o _usage.o rc-misc.o rc-plugin.o diff --git a/src/rc/openrc-shutdown.c b/src/rc/openrc-shutdown.c index 82dde517..4dcd7e16 100644 --- a/src/rc/openrc-shutdown.c +++ b/src/rc/openrc-shutdown.c @@ -35,8 +35,9 @@ #include "rc.h" #include "helpers.h" #include "rc-misc.h" -#include "_usage.h" +#include "rc-sysvinit.h" #include "rc-wtmp.h" +#include "_usage.h" const char *applet = NULL; const char *extraopts = NULL; @@ -328,15 +329,17 @@ int main(int argc, char **argv) syslog(LOG_NOTICE, "The system will %s now", state); unlink(nologin_file); unlink(shutdown_pid); - if (do_halt) + if (do_halt) { + sysvinit_runlevel('0'); send_cmd("halt"); - else if (do_kexec) + } else if (do_kexec) send_cmd("kexec"); else if (do_poweroff) send_cmd("poweroff"); - else if (do_reboot) - send_cmd("reboot"); - else if (do_single) + else if (do_reboot) { + sysvinit_runlevel('6'); + send_cmd("reboot"); + } else if (do_single) send_cmd("single"); return 0; } diff --git a/src/rc/rc-sysvinit.c b/src/rc/rc-sysvinit.c new file mode 100644 index 00000000..d9d17146 --- /dev/null +++ b/src/rc/rc-sysvinit.c @@ -0,0 +1,66 @@ +/* + * rc-sysvinit.c + * Helper to send a runlevel change to sysvinit + */ + +/* + * Copyright (c) 2019 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 "einfo.h" +#include "rc-sysvinit.h" + +void sysvinit_runlevel(char rl) +{ + struct init_request request; + int fd; + char *p; + size_t bytes; + ssize_t r; + + if (!rl) + return; + + fd = open("/run/initctl", O_WRONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY); + if (fd < 0) { + if (errno != ENOENT) + eerror("Failed to open initctl fifo: %s", strerror(errno)); + return; + } + request = (struct init_request) { + .magic = INIT_MAGIC, + .sleeptime = 0, + .cmd = INIT_CMD_RUNLVL, + .runlevel = rl, + }; + p = (char *) &request; + bytes = sizeof(request); + do { + r = write(fd, p, bytes); + if (r < 0) { + if ((errno == EAGAIN) || (errno == EINTR)) + continue; + eerror("Failed to write to /run/initctl: %s", strerror(errno)); + return; + } + p += r; + bytes -= r; + } while (bytes > 0); + exit(0); +} diff --git a/src/rc/rc-sysvinit.h b/src/rc/rc-sysvinit.h new file mode 100644 index 00000000..55bc434d --- /dev/null +++ b/src/rc/rc-sysvinit.h @@ -0,0 +1,71 @@ +/* + * rc-sysvinit.h - Interface to communicate with sysvinit via /run/initctl. + */ + +/* + * Copyright (c) 2019 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. + */ + +#ifndef _RC_SYSVINIT_H +#define _RC_SYSVINIT_H + +/* + * The #defines and structures below are taken from initreq.h in + * sysvinit and must be used by any program wishing to communicate with + * it. + */ + +#define INIT_MAGIC 0x03091969 +#define INIT_CMD_START 0 +#define INIT_CMD_RUNLVL 1 +#define INIT_CMD_POWERFAIL 2 +#define INIT_CMD_POWERFAILNOW 3 +#define INIT_CMD_POWEROK 4 +#define INIT_CMD_BSD 5 +#define INIT_CMD_SETENV 6 +#define INIT_CMD_UNSETENV 7 + +/* + * This is what BSD 4.4 uses when talking to init. + * Linux doesn't use this right now. + */ +struct init_request_bsd { + char gen_id[8]; /* Beats me.. telnetd uses "fe" */ + char tty_id[16]; /* Tty name minus /dev/tty */ + char host[64]; /* Hostname */ + char term_type[16]; /* Terminal type */ + int signal; /* Signal to send */ + int pid; /* Process to send to */ + char exec_name[128]; /* Program to execute */ + char reserved[128]; /* For future expansion. */ +}; + +/* + * Because of legacy interfaces, "runlevel" and "sleeptime" + * aren't in a seperate struct in the union. + * + * The weird sizes are because init expects the whole + * struct to be 384 bytes. + */ +struct init_request { + int magic; /* Magic number */ + int cmd; /* What kind of request */ + int runlevel; /* Runlevel to change to */ + int sleeptime; /* Time between TERM and KILL */ + union { + struct init_request_bsd bsd; + char data[368]; + } i; +}; + +void sysvinit_runlevel(char rl); + +#endif