diff options
author | Laurent Bercot <ska-skaware@skarnet.org> | 2019-04-16 20:37:01 +0000 |
---|---|---|
committer | Laurent Bercot <ska-skaware@skarnet.org> | 2019-04-16 20:37:01 +0000 |
commit | 73cff35173dd75b0254ae7cf9098c32822859de0 (patch) | |
tree | fd1bb09611b2b7064967ca8d8b2aef98514e616b /src/shutdown | |
parent | 5199738f4e32773f4f752a94998593e18e3af36f (diff) | |
download | s6-linux-init-73cff35173dd75b0254ae7cf9098c32822859de0.tar.xz |
It builds! Tomorrow: skeletons for stage2, stage3 and runlevel scripts.
Diffstat (limited to 'src/shutdown')
-rw-r--r-- | src/shutdown/deps-exe/s6-linux-init-halt | 5 | ||||
-rw-r--r-- | src/shutdown/deps-exe/s6-linux-init-poweroff | 5 | ||||
-rw-r--r-- | src/shutdown/deps-exe/s6-linux-init-reboot | 5 | ||||
-rw-r--r-- | src/shutdown/deps-exe/s6-linux-init-shutdown | 5 | ||||
-rw-r--r-- | src/shutdown/deps-exe/s6-linux-init-shutdownd | 4 | ||||
-rw-r--r-- | src/shutdown/deps-lib/hpr | 2 | ||||
-rw-r--r-- | src/shutdown/hpr.c | 104 | ||||
-rw-r--r-- | src/shutdown/hpr.h | 20 | ||||
-rw-r--r-- | src/shutdown/hpr_shutdown.c | 16 | ||||
-rw-r--r-- | src/shutdown/hpr_wall.c | 47 | ||||
-rw-r--r-- | src/shutdown/s6-linux-init-halt.c | 7 | ||||
-rw-r--r-- | src/shutdown/s6-linux-init-poweroff.c | 7 | ||||
-rw-r--r-- | src/shutdown/s6-linux-init-reboot.c | 7 | ||||
-rw-r--r-- | src/shutdown/s6-linux-init-shutdown.c | 268 | ||||
-rw-r--r-- | src/shutdown/s6-linux-init-shutdownd.c | 228 |
15 files changed, 730 insertions, 0 deletions
diff --git a/src/shutdown/deps-exe/s6-linux-init-halt b/src/shutdown/deps-exe/s6-linux-init-halt new file mode 100644 index 0000000..0c4376c --- /dev/null +++ b/src/shutdown/deps-exe/s6-linux-init-halt @@ -0,0 +1,5 @@ +libhpr.a.xyzzy +${LIBUTMPS} +-lskarnet +${TAINNOW_LIB} +${SOCKET_LIB} diff --git a/src/shutdown/deps-exe/s6-linux-init-poweroff b/src/shutdown/deps-exe/s6-linux-init-poweroff new file mode 100644 index 0000000..0c4376c --- /dev/null +++ b/src/shutdown/deps-exe/s6-linux-init-poweroff @@ -0,0 +1,5 @@ +libhpr.a.xyzzy +${LIBUTMPS} +-lskarnet +${TAINNOW_LIB} +${SOCKET_LIB} diff --git a/src/shutdown/deps-exe/s6-linux-init-reboot b/src/shutdown/deps-exe/s6-linux-init-reboot new file mode 100644 index 0000000..0c4376c --- /dev/null +++ b/src/shutdown/deps-exe/s6-linux-init-reboot @@ -0,0 +1,5 @@ +libhpr.a.xyzzy +${LIBUTMPS} +-lskarnet +${TAINNOW_LIB} +${SOCKET_LIB} diff --git a/src/shutdown/deps-exe/s6-linux-init-shutdown b/src/shutdown/deps-exe/s6-linux-init-shutdown new file mode 100644 index 0000000..0c4376c --- /dev/null +++ b/src/shutdown/deps-exe/s6-linux-init-shutdown @@ -0,0 +1,5 @@ +libhpr.a.xyzzy +${LIBUTMPS} +-lskarnet +${TAINNOW_LIB} +${SOCKET_LIB} diff --git a/src/shutdown/deps-exe/s6-linux-init-shutdownd b/src/shutdown/deps-exe/s6-linux-init-shutdownd new file mode 100644 index 0000000..5a7d2fd --- /dev/null +++ b/src/shutdown/deps-exe/s6-linux-init-shutdownd @@ -0,0 +1,4 @@ +${LIBUTMPS} +-lskarnet +${TAINNOW_LIB} +${SOCKET_LIB} diff --git a/src/shutdown/deps-lib/hpr b/src/shutdown/deps-lib/hpr new file mode 100644 index 0000000..9e1493d --- /dev/null +++ b/src/shutdown/deps-lib/hpr @@ -0,0 +1,2 @@ +hpr_shutdown.o +hpr_wall.o diff --git a/src/shutdown/hpr.c b/src/shutdown/hpr.c new file mode 100644 index 0000000..a769c8c --- /dev/null +++ b/src/shutdown/hpr.c @@ -0,0 +1,104 @@ +/* ISC license. */ + +#include <skalibs/nonposix.h> + +#include <unistd.h> +#include <signal.h> +#include <errno.h> +#include <utmpx.h> +#include <sys/reboot.h> + +#include <skalibs/strerr2.h> +#include <skalibs/sgetopt.h> +#include <skalibs/sig.h> +#include <skalibs/tai.h> +#include <skalibs/djbunix.h> + +#include "defaults.h" +#include "hpr.h" + +#ifndef UT_NAMESIZE +#define UT_NAMESIZE 32 +#endif + +#ifndef UT_HOSTSIZE +#define UT_HOSTSIZE 256 +#endif + +#ifndef _PATH_WTMP +#define _PATH_WTMP "/dev/null/wtmp" +#endif + +#define USAGE PROGNAME " [ -h | -p | -r ] [ -d | -w ] [ -W ] [ -f ]" + +int main (int argc, char const *const *argv) +{ + int what = WHATDEFAULT ; + int force = 0 ; + int dowtmp = 1 ; + int dowall = 1 ; + PROG = PROGNAME ; + + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + int opt = subgetopt_r(argc, argv, "hprfdwW", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'h' : what = 1 ; break ; + case 'p' : what = 2 ; break ; + case 'r' : what = 3 ; break ; + case 'f' : force = 1 ; break ; + case 'd' : dowtmp = 0 ; break ; + case 'w' : dowtmp = 2 ; break ; + case 'W' : dowall = 0 ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + + if (geteuid()) + { + errno = EPERM ; + strerr_dief1sys(100, "nice try, peon") ; + } + + if (force) + { + reboot(what == 3 ? RB_AUTOBOOT : what == 2 ? RB_POWER_OFF : RB_HALT_SYSTEM) ; + strerr_diefu1sys(111, "reboot()") ; + } + + if (!tain_now_g()) strerr_warnw1sys("get current time") ; + if (dowtmp) + { + struct utmpx utx = + { + .ut_type = RUN_LVL, + .ut_pid = getpid(), + .ut_line = "~", + .ut_id = "", + .ut_session = getsid(0) + } ; + strncpy(utx.ut_user, what == 3 ? "reboot" : "shutdown", UT_NAMESIZE) ; + if (gethostname(utx.ut_host, UT_HOSTSIZE) < 0) + { + utx.ut_host[0] = 0 ; + strerr_warnwu1sys("gethostname") ; + } + else utx.ut_host[UT_HOSTSIZE - 1] = 0 ; + if (!timeval_from_tain(&utx.ut_tv, &STAMP)) + strerr_warnwu1sys("timeval_from_tain") ; + updwtmpx(_PATH_WTMP, &utx) ; + } + if (dowall) hpr_wall(HPR_WALL_BANNER) ; + if (dowtmp < 2) + { + if (!hpr_shutdown(what, &STAMP, 0)) + strerr_diefu1sys(111, "notify s6-linux-init-shutdownd") ; + } + return 0 ; +} diff --git a/src/shutdown/hpr.h b/src/shutdown/hpr.h new file mode 100644 index 0000000..993f5ab --- /dev/null +++ b/src/shutdown/hpr.h @@ -0,0 +1,20 @@ +/* ISC license. */ + +#ifndef HPR_H +#define HPR_H + +#include <stddef.h> + +#include <skalibs/tai.h> +#include <skalibs/djbunix.h> + +#include "initctl.h" + +#define HPR_WALL_BANNER "\n\n*** WARNING ***\nThe system is going down NOW!\n" + +#define hpr_send(s, n) openwritenclose_unsafe(INITCTL, (s), n) +#define hpr_cancel() hpr_send("c", 1) +extern int hpr_shutdown (unsigned int, tain_t const *, unsigned int) ; +extern void hpr_wall (char const *) ; + +#endif diff --git a/src/shutdown/hpr_shutdown.c b/src/shutdown/hpr_shutdown.c new file mode 100644 index 0000000..9c9ff51 --- /dev/null +++ b/src/shutdown/hpr_shutdown.c @@ -0,0 +1,16 @@ +/* ISC license. */ + +#include <stdint.h> + +#include <skalibs/uint32.h> +#include <skalibs/tai.h> + +#include "hpr.h" + +int hpr_shutdown (unsigned int what, tain_t const *when, unsigned int grace) +{ + char pack[5 + TAIN_PACK] = { " hpr"[what] } ; + tain_pack(pack+1, when) ; + uint32_pack_big(pack + 1 + TAIN_PACK, (uint32_t)grace) ; + return hpr_send(pack, 5 + TAIN_PACK) ; +} diff --git a/src/shutdown/hpr_wall.c b/src/shutdown/hpr_wall.c new file mode 100644 index 0000000..cc6da5e --- /dev/null +++ b/src/shutdown/hpr_wall.c @@ -0,0 +1,47 @@ +/* ISC license. */ + +#include <string.h> +#include <errno.h> +#include <utmpx.h> + +#include <skalibs/posixishard.h> +#include <skalibs/allreadwrite.h> +#include <skalibs/strerr2.h> +#include <skalibs/djbunix.h> + +#include "hpr.h" + +#ifndef UT_LINESIZE +#define UT_LINESIZE 32 +#endif + +void hpr_wall (char const *s) +{ + size_t n = strlen(s) ; + char tty[10 + UT_LINESIZE] = "/dev/" ; + char msg[n+1] ; + memcpy(msg, s, n) ; + msg[n++] = '\n' ; + setutxent() ; + for (;;) + { + size_t linelen, idlen ; + struct utmpx *utx ; + int fd ; + errno = 0 ; + utx = getutxent() ; + if (!utx) break ; + if (utx->ut_type != USER_PROCESS) continue ; + linelen = strnlen(utx->ut_line, UT_LINESIZE) ; + idlen = strnlen(utx->ut_id, 4) ; + memcpy(tty + 5, utx->ut_line, linelen) ; + memcpy(tty + 5 + linelen, utx->ut_id, idlen) ; + tty[5 + linelen + idlen] = 0 ; + fd = open_append(tty) ; + if (fd == -1) continue ; + allwrite(fd, msg, n) ; + fd_close(fd) ; + } + if (errno) strerr_warnwu1sys("getutxent") ; + endutxent() ; +} diff --git a/src/shutdown/s6-linux-init-halt.c b/src/shutdown/s6-linux-init-halt.c new file mode 100644 index 0000000..4ccca1d --- /dev/null +++ b/src/shutdown/s6-linux-init-halt.c @@ -0,0 +1,7 @@ +/* ISC license. */ + +#undef PROGNAME +#define PROGNAME "s6-linux-init-halt" +#undef WHATDEFAULT +#define WHATDEFAULT 1 +#include "hpr.c" diff --git a/src/shutdown/s6-linux-init-poweroff.c b/src/shutdown/s6-linux-init-poweroff.c new file mode 100644 index 0000000..d25dbb6 --- /dev/null +++ b/src/shutdown/s6-linux-init-poweroff.c @@ -0,0 +1,7 @@ +/* ISC license. */ + +#undef PROGNAME +#define PROGNAME "s6-linux-init-poweroff" +#undef WHATDEFAULT +#define WHATDEFAULT 2 +#include "hpr.c" diff --git a/src/shutdown/s6-linux-init-reboot.c b/src/shutdown/s6-linux-init-reboot.c new file mode 100644 index 0000000..3612684 --- /dev/null +++ b/src/shutdown/s6-linux-init-reboot.c @@ -0,0 +1,7 @@ +/* ISC license. */ + +#undef PROGNAME +#define PROGNAME "s6-linux-init-reboot" +#undef WHATDEFAULT +#define WHATDEFAULT 3 +#include "hpr.c" diff --git a/src/shutdown/s6-linux-init-shutdown.c b/src/shutdown/s6-linux-init-shutdown.c new file mode 100644 index 0000000..f534944 --- /dev/null +++ b/src/shutdown/s6-linux-init-shutdown.c @@ -0,0 +1,268 @@ +/* ISC license. */ + +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <sys/stat.h> +#include <errno.h> +#include <time.h> +#include <utmpx.h> + +#include <skalibs/uint32.h> +#include <skalibs/types.h> +#include <skalibs/allreadwrite.h> +#include <skalibs/strerr2.h> +#include <skalibs/sgetopt.h> +#include <skalibs/sig.h> +#include <skalibs/tai.h> +#include <skalibs/djbunix.h> +#include <skalibs/djbtime.h> + +#include "defaults.h" +#include "initctl.h" +#include "hpr.h" + +#ifndef UT_NAMESIZE +#define UT_NAMESIZE 32 +#endif + +#define USAGE "s6-linux-init-shutdown [ -h | -p | -r | -k ] [ -f | -F ] [ -a ] [ -t sec ] time [ message ] or s6-linux-init-shutdown -c [ message ]" +#define dieusage() strerr_dieusage(100, USAGE) + +#define AC_FILE "/etc/shutdown.allow" +#define AC_BUFSIZE 4096 +#define AC_MAX 64 +#define AC_SHORT_MESSAGE "no authorized users logged in\n" +#define AC_MESSAGE "s6-linux-init-shutdown: " AC_SHORT_MESSAGE + + + /* shutdown 01:23: date/time format parsing */ + +static inline void add_one_day (struct tm *tm) +{ + tm->tm_isdst = -1 ; + if (tm->tm_mday++ < 31) return ; + tm->tm_mday = 1 ; + if (tm->tm_mon++ < 11) return ; + tm->tm_mon = 0 ; + tm->tm_year++ ; +} + +static inline void parse_hourmin (tain_t *when, char const *s) +{ + tai_t taithen ; + struct tm tmnow, tmthen ; + unsigned int hour, minute ; + size_t len = uint_scan(s, &hour) ; + if (!len || len > 2 || s[len] != ':' || hour > 23) + strerr_dief1x(100, "invalid time format") ; + s += len+1 ; + len = uint0_scan(s, &minute) ; + if (!len || len != 2 || minute > 59) + strerr_dief1x(100, "invalid time format") ; + if (!localtm_from_tai(&tmnow, tain_secp(&STAMP), 1)) + strerr_diefu1sys(111, "break down current time into struct tm") ; + tmthen = tmnow ; + tmthen.tm_hour = hour ; + tmthen.tm_min = minute ; + if (!tai_from_localtm(&taithen, &tmthen)) + strerr_diefu1sys(111, "assemble broken-down time into tain_t") ; + if (tai_less(&taithen, tain_secp(&STAMP))) + { + add_one_day(&tmthen) ; + if (!tai_from_localtm(&taithen, &tmthen)) + strerr_diefu1sys(111, "assemble broken-down time into tain_t") ; + } + tai_sub(tain_secp(when), &taithen, tain_secp(&STAMP)) ; + when->nano = 0 ; +} + +static inline void parse_time (tain_t *when, char const *s) +{ + if (!strcmp(s, "now")) tain_copynow(when) ; + else if (s[0] == '+') + { + unsigned int mins ; + if (!uint0_scan(s+1, &mins)) dieusage() ; + tain_addsec_g(when, mins * 60) ; + } + else if (strchr(s, ':')) parse_hourmin(when, s) ; + else + { + unsigned int mins ; + if (!uint0_scan(s, &mins)) dieusage() ; + tain_addsec_g(when, mins * 60) ; + } +} + + + /* shutdown -a: access control */ + +static inline unsigned char cclass (unsigned char c) +{ + switch (c) + { + case 0 : return 0 ; + case '\n' : return 1 ; + case '#' : return 2 ; + default : return 3 ; + } +} + +static inline unsigned int parse_authorized_users (char *buf, char const **users, unsigned int max) +{ + static unsigned char const table[3][4] = + { + { 0x03, 0x00, 0x01, 0x12 }, + { 0x03, 0x00, 0x01, 0x01 }, + { 0x23, 0x20, 0x02, 0x02 } + } ; + size_t pos = 0 ; + size_t mark = 0 ; + unsigned int n = 0 ; + unsigned int state = 0 ; + for (; state < 3 ; pos++) + { + unsigned char what = table[state][cclass(buf[pos])] ; + state = what & 3 ; + if (what & 0x10) mark = pos ; + if (what & 0x20) + { + if (n >= max) + { + char fmt[UINT32_MAX] ; + fmt[uint32_fmt(fmt, AC_MAX)] = 0 ; + strerr_warnw4x(AC_FILE, " lists more than ", fmt, " authorized users - ignoring the extra ones") ; + break ; + } + buf[pos] = 0 ; + users[n++] = buf + mark ; + } + } + return n ; +} + +static inline int match_users_with_utmp (char const *const *users, unsigned int n) +{ + setutxent() ; + for (;;) + { + struct utmpx *utx ; + errno = 0 ; + utx = getutxent() ; + if (!utx) break ; + if (utx->ut_type != USER_PROCESS) continue ; + for (unsigned int i = 0 ; i < n ; i++) + if (!strncmp(utx->ut_user, users[i], UT_NAMESIZE)) goto yes ; + } + if (errno) strerr_warnwu1sys("getutxent") ; + endutxent() ; + return 0 ; + + yes: + endutxent() ; + return 1 ; +} + +static inline void access_control (void) +{ + char buf[AC_BUFSIZE] ; + char const *users[AC_MAX] ; + unsigned int n ; + struct stat st ; + int fd = open_readb(AC_FILE) ; + if (fd == -1) + { + if (errno == ENOENT) return ; + strerr_diefu2sys(111, "open ", AC_FILE) ; + } + if (fstat(fd, &st) == -1) + strerr_diefu2sys(111, "stat ", AC_FILE) ; + if (st.st_size >= AC_BUFSIZE) + { + char fmt[UINT32_FMT] ; + fmt[uint32_fmt(fmt, AC_BUFSIZE - 1)] = 0 ; + strerr_dief4x(1, AC_FILE, " is too big: it needs to be ", fmt, " bytes or less") ; + } + if (allread(fd, buf, st.st_size) < st.st_size) + strerr_diefu2sys(111, "read ", AC_FILE) ; + fd_close(fd) ; + buf[st.st_size] = 0 ; + n = parse_authorized_users(buf, users, AC_MAX) ; + if (!n || !match_users_with_utmp(users, n)) + { + fd = open_append("/dev/console") ; + if (fd == -1) + strerr_diefu1sys(111, "open /dev/console") ; + if (allwrite(fd, AC_MESSAGE, sizeof(AC_MESSAGE) - 1) < sizeof(AC_MESSAGE) - 1) + strerr_diefu1sys(111, "write to /dev/console") ; + strerr_dief1x(1, AC_SHORT_MESSAGE) ; + } +} + + + /* main */ + +int main (int argc, char const *const *argv) +{ + unsigned int gracetime = 0 ; + int what = 0 ; + int doactl = 0 ; + int docancel = 0 ; + tain_t when ; + PROG = "s6-linux-init-shutdown" ; + + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + int opt = subgetopt_r(argc, argv, "hprkafFct:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'h' : what = 1 ; break ; + case 'p' : what = 2 ; break ; + case 'r' : what = 3 ; break ; + case 'k' : what = 4 ; break ; + case 'a' : doactl = 1 ; break ; + case 'f' : /* talk to the hand */ break ; + case 'F' : /* no, the other hand */ break ; + case 'c' : docancel = 1 ; break ; + case 't' : if (!uint0_scan(l.arg, &gracetime)) dieusage() ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + + if (geteuid()) + { + errno = EPERM ; + strerr_diefu1sys(111, "shutdown") ; + } + if (doactl) access_control() ; + if (!tain_now_g()) strerr_warnw1sys("get current time") ; + if (docancel) + { + if (argv[0]) hpr_wall(argv[0]) ; + if (!hpr_cancel()) goto err ; + return 0 ; + } + if (!argc) dieusage() ; + parse_time(&when, argv[0]) ; + if (argv[1]) hpr_wall(argv[1]) ; + if (what < 4) + { + if (gracetime > 300) + { + gracetime = 300 ; + strerr_warnw1x("delay between SIGTERM and SIGKILL is capped to 300 seconds") ; + } + if (!hpr_shutdown(what, &when, gracetime * 1000)) goto err ; + } + return 0 ; + + err: + strerr_diefu2sys(111, "write to ", INITCTL) ; +} diff --git a/src/shutdown/s6-linux-init-shutdownd.c b/src/shutdown/s6-linux-init-shutdownd.c new file mode 100644 index 0000000..c6d6316 --- /dev/null +++ b/src/shutdown/s6-linux-init-shutdownd.c @@ -0,0 +1,228 @@ +/* ISC license. */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <errno.h> +#include <signal.h> +#include <unistd.h> +#include <stdio.h> +#include <sys/wait.h> + +#include <skalibs/posixplz.h> +#include <skalibs/uint32.h> +#include <skalibs/types.h> +#include <skalibs/allreadwrite.h> +#include <skalibs/bytestr.h> +#include <skalibs/buffer.h> +#include <skalibs/strerr2.h> +#include <skalibs/sgetopt.h> +#include <skalibs/sig.h> +#include <skalibs/tai.h> +#include <skalibs/djbunix.h> +#include <skalibs/iopause.h> +#include <skalibs/skamisc.h> + +#include <execline/config.h> + +#include "defaults.h" +#include "initctl.h" +#include "hpr.h" + +#define STAGE4_FILE "stage 4" + +#define USAGE "s6-linux-init-shutdownd [ -c basedir ] [ -g gracetime ]" +#define dieusage() strerr_dieusage(100, USAGE) + +static char const *basedir = BASEDIR ; + +static inline void prepare_shutdown (char c, unsigned int *what, tain_t *deadline, unsigned int *grace_time) +{ + uint32_t u ; + char pack[TAIN_PACK] ; + ssize_t r = sanitize_read(buffer_get(buffer_0small, pack, TAIN_PACK)) ; + if (r == -1) strerr_diefu1sys(111, "read from pipe") ; + if (r < TAIN_PACK + 4) strerr_dief1x(101, "bad shutdown protocol") ; + tain_unpack(pack, deadline) ; + uint32_unpack_big(pack + TAIN_PACK, &u) ; + if (u && u <= 300000) *grace_time = u ; + *what = 1 + byte_chr("hpr", c, 3) ; +} + +static inline void handle_stdin (unsigned int *what, tain_t *deadline, unsigned int *grace_time) +{ + for (;;) + { + char c ; + ssize_t r = sanitize_read(buffer_get(buffer_0small, &c, 1)) ; + if (r == -1) strerr_diefu1sys(111, "read from pipe") ; + else if (!r) break ; + switch (c) + { + case 'h' : + case 'p' : + case 'r' : + prepare_shutdown(c, what, deadline, grace_time) ; + break ; + case 'c' : + *what = 0 ; + tain_add_g(deadline, &tain_infinite_relative) ; + break ; + default : + { + char s[2] = { c, 0 } ; + strerr_warnw2x("unknown command: ", s) ; + } + break ; + } + } +} + +static inline void prepare_stage4 (char const *basedir, unsigned int what) +{ + static char const *table[3] = { "halt", "poweroff", "reboot" } ; + buffer b ; + int fd ; + char buf[512] ; + unlink_void(STAGE4_FILE ".new") ; + fd = open_excl(STAGE4_FILE ".new") ; + if (fd == -1) strerr_diefu3sys(111, "open ", STAGE4_FILE ".new", " for writing") ; + buffer_init(&b, &buffer_write, fd, buf, 512) ; + + if (buffer_puts(&b, + "#!" EXECLINE_SHEBANGPREFIX "/execlineb -P\n\n" + EXECLINE_EXTBINPREFIX "foreground { " + S6_LINUX_INIT_LIBEXECPREFIX "s6-linux-init-umountall }\n" + S6_LINUX_INIT_LIBEXECPREFIX "s6-linux-init-") < 0 + || buffer_puts(&b, table[what-1]) < 0 + || buffer_putsflush(&b, " -f\n") < 0) + strerr_diefu2sys(111, "write to ", STAGE4_FILE ".new") ; + if (fchmod(fd, S_IRWXU) == -1) + strerr_diefu2sys(111, "fchmod ", STAGE4_FILE ".new") ; + fd_close(fd) ; + if (rename(STAGE4_FILE ".new", STAGE4_FILE) == -1) + strerr_diefu4sys(111, "rename ", STAGE4_FILE ".new", " to ", STAGE4_FILE) ; +} + +int main (int argc, char const *const *argv, char const *const *envp) +{ + pid_t pid ; + tain_t deadline ; + unsigned int what = 0 ; + unsigned int grace_time = 3000 ; + PROG = "s6-linux-init-shutdownd" ; + + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + int opt = subgetopt_r(argc, argv, "c:g:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'c' : basedir = l.arg ; break ; + case 'g' : if (!uint0_scan(l.arg, &grace_time)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (basedir[0] != '/') + strerr_dief2x(100, "basedir", " must be an absolute path") ; + if (grace_time > 300000) grace_time = 300000 ; + + /* if we're in stage 4, exec it immediately */ + { + char const *stage4_argv[2] = { STAGE4_FILE, 0 } ; + pathexec_run(stage4_argv[0], stage4_argv, envp) ; + if (errno != ENOENT) + strerr_warnwu2sys("exec ", stage4_argv[0]) ; + } + + fd_close(1) ; + fd_close(0) ; + + if (open_read(SHUTDOWND_FIFO)) + strerr_diefu3sys(111, "open ", SHUTDOWND_FIFO, " for reading") ; + if (open_write(SHUTDOWND_FIFO) != 1) + strerr_diefu3sys(111, "open ", SHUTDOWND_FIFO, " for writing") ; + if (sig_ignore(SIGPIPE) == -1) + strerr_diefu1sys(111, "sig_ignore SIGPIPE") ; + tain_now_g() ; + tain_add_g(&deadline, &tain_infinite_relative) ; + + for (;;) + { + iopause_fd x = { .fd = 0, .events = IOPAUSE_READ } ; + int r = iopause_g(&x, 1, &deadline) ; + if (r == -1) strerr_diefu1sys(111, "iopause") ; + if (!r) + { + if (what) break ; + tain_add_g(&deadline, &tain_infinite_relative) ; + continue ; + } + if (x.revents & IOPAUSE_READ) + handle_stdin(&what, &deadline, &grace_time) ; + } + + + /* Stage 3 */ + + fd_close(1) ; + fd_close(0) ; + if (open_readb("/dev/null")) + strerr_diefu1sys(111, "open /dev/null for reading") ; + if (open_write("/dev/console") != 1 || ndelay_off(1) == -1) + strerr_diefu1sys(111, "open /dev/console for writing") ; + { + size_t basedirlen = strlen(basedir) ; + char stage3[basedirlen + sizeof("/" STAGE3)] ; + char const *stage3_argv[2] = { stage3, 0 } ; + memcpy(stage3, basedir, basedirlen) ; + memcpy(stage3 + basedirlen, "/" STAGE3, sizeof("/" STAGE3)) ; + pid = child_spawn0(stage3_argv[0], stage3_argv, envp) ; + if (pid) + { + int wstat ; + if (wait_pid(pid, &wstat) == -1) strerr_diefu1sys(111, "waitpid") ; + if (WIFSIGNALED(wstat)) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, WTERMSIG(wstat))] = 0 ; + strerr_warnw3x(stage3, " was killed by signal ", fmt) ; + } + else if (WEXITSTATUS(wstat)) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, WTERMSIG(wstat))] = 0 ; + strerr_warnw3x(stage3, " was killed by signal ", fmt) ; + } + else if (WEXITSTATUS(wstat)) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, WEXITSTATUS(wstat))] = 0 ; + strerr_warnw3x(stage3, " exited ", fmt) ; + } + } + else strerr_warnwu2sys("spawn ", stage3) ; + } + + + /* The end is coming! */ + + prepare_stage4(basedir, what) ; + if (fd_move(2, 1) == -1) strerr_warnwu1sys("fd_move") ; + sync() ; + if (sig_ignore(SIGTERM) == -1) strerr_warnwu1sys("sig_ignore SIGTERM") ; + strerr_warni1x("sending all processes the TERM signal...") ; + kill(-1, SIGTERM) ; + kill(-1, SIGCONT) ; + tain_from_millisecs(&deadline, grace_time) ; + tain_add_g(&deadline, &deadline) ; + deepsleepuntil_g(&deadline) ; + sync() ; + strerr_warni1x("sending all processes the KILL signal...") ; + kill(-1, SIGKILL) ; + return 0 ; +} |