From 244dddd0ada79388a27f33e73b173764c581fca1 Mon Sep 17 00:00:00 2001 From: Laurent Bercot Date: Mon, 19 Aug 2019 13:15:40 +0000 Subject: Add stage 4 hook support --- doc/overview.html | 4 ++ doc/s6-linux-init-umountall.html | 14 +++---- doc/upgrade.html | 4 ++ skel/rc.shutdown.final | 18 ++++++++ src/include-local/initctl.h | 1 + src/init/s6-linux-init-maker.c | 1 + src/misc/s6-linux-init-umountall.c | 76 +++++++++++++++------------------- src/shutdown/s6-linux-init-shutdownd.c | 6 +++ 8 files changed, 75 insertions(+), 49 deletions(-) create mode 100755 skel/rc.shutdown.final diff --git a/doc/overview.html b/doc/overview.html index 4d0b7ba..d779dd2 100644 --- a/doc/overview.html +++ b/doc/overview.html @@ -81,6 +81,10 @@ procedure when the admin runs a halt, poweroff,
  • runlevel: the script executing a machine state change at boot time (normally invoked by rc.init, towards the default runlevel) or when the administrator runs a telinit command.
  • +
  • rc.shutdown.final: a script that will be run at the +very end of the shutdown procedure, after all processes have been killed +and all filesystems have been unmounted, just before the system +is rebooted or the power turned off. This script normally remains empty.
  • diff --git a/doc/s6-linux-init-umountall.html b/doc/s6-linux-init-umountall.html index 2d1bf99..eff06c5 100644 --- a/doc/s6-linux-init-umountall.html +++ b/doc/s6-linux-init-umountall.html @@ -33,6 +33,10 @@ It processes /proc/mounts in the reverse order, starting with the most recently mounted partition and ending with the root filesystem ("unmounting" the root filesystem means remounting it read-only). +
  • It makes an exception for the first instances of devtmpfs, proc +and sysfs filesystem types, but not for later instances. In other words: it +does not attempt to unmount /dev, /proc and /sys, but it +will attempt to unmount duplicates of those pseudo-filesystems.
  • s6-linux-init-umountall does not touch /etc/mtab.
  • If a filesystem fails to unmount, a warning is printed to stderr, but s6-linux-init-umountall still attempts to unmount all the other ones.
  • @@ -42,7 +46,7 @@ it read-only).

    s6-linux-init-umountall returns the number of errors it encountered -when attempting to unmount all the filesystems listed in /proc/mounts. +when attempting to unmount the filesystems listed in /proc/mounts.

    Notes

    @@ -53,15 +57,11 @@ in "stage 4", i.e. after a SIGKILL has been sent to all the processes on the sys right before the system reboots (or halts, or is powered off). By that point, there is no possible process that could prevent real file systems from being unmounted.
  • It is likely that some filesystems will still fail to unmount, typically -/proc and /dev. That's okay: those are pseudo-filesystems, and +cgroups or tmpfses. That's okay: those are pseudo-filesystems, and will not cause data loss or a fsck if the system shuts down while they are still mounted.
  • Distributions usually provide a umount command with a -a option to unmount all filesystems. That command is usually bloated with historical artifacts -and relies on unsafe interfaces, so it was decided not to use it. The -s6-linux-utils package also -provides a s6-umount -command with a -a option, but adding a dependency to that package would be a -higher cost than simply reimplementing the specific functionality here.
  • +and relies on unsafe interfaces, so it was decided not to use it. diff --git a/doc/upgrade.html b/doc/upgrade.html index 98a1bc0..a1d1ab3 100644 --- a/doc/upgrade.html +++ b/doc/upgrade.html @@ -23,6 +23,10 @@

    in 1.0.2.1

    diff --git a/skel/rc.shutdown.final b/skel/rc.shutdown.final new file mode 100755 index 0000000..3f46b87 --- /dev/null +++ b/skel/rc.shutdown.final @@ -0,0 +1,18 @@ +#!/bin/sh -e + +### Things to do *right before* the machine gets rebooted or +### powered off, at the very end of the shutdown sequence, +### when all the filesystems are unmounted. + +### This is a last resort hook; normally nothing should be +### done here (your rc.shutdown script should have taken care +### of everything) and you should leave this script empty. + +### Some distributions, however, may need to perform some +### actions after unmounting the filesystems: typically if +### an additional teardown action is required on a filesystem +### after unmounting it, or if the system needs to be +### pivot_rooted before it can be shut down, etc. + +### Those are all exceptional cases. If you don't know for +### certain that you need to do something here, you don't. diff --git a/src/include-local/initctl.h b/src/include-local/initctl.h index 507aefe..9c67b06 100644 --- a/src/include-local/initctl.h +++ b/src/include-local/initctl.h @@ -30,5 +30,6 @@ #define ENVSTAGE1 "env" #define STAGE2 "rc.init" #define STAGE3 "rc.shutdown" +#define STAGE4 "rc.shutdown.final" #endif diff --git a/src/init/s6-linux-init-maker.c b/src/init/s6-linux-init-maker.c index 8de17fe..139926c 100644 --- a/src/init/s6-linux-init-maker.c +++ b/src/init/s6-linux-init-maker.c @@ -554,6 +554,7 @@ static inline void make_scripts (char const *base) copy_script(base, "runlevel") ; copy_script(base, STAGE2) ; copy_script(base, STAGE3) ; + copy_script(base, STAGE4) ; } static inline void make_bins (char const *base) diff --git a/src/misc/s6-linux-init-umountall.c b/src/misc/s6-linux-init-umountall.c index 24b22fc..93b0966 100644 --- a/src/misc/s6-linux-init-umountall.c +++ b/src/misc/s6-linux-init-umountall.c @@ -1,70 +1,62 @@ /* ISC license. */ +#include #include +#include +#include #include -#include -#include #include #include -#include #include -#define BUFSIZE 4096 -#define MAXLINES 512 +#define MAXLINES 99 + +#define EXCLUDEN 3 +static char const *exclude_type[EXCLUDEN] = { "devtmpfs", "proc", "sysfs" } ; int main (int argc, char const *const *argv) { - stralloc mountpoints[MAXLINES] ; - char buf[BUFSIZE] ; - buffer b ; + size_t mountpoints[MAXLINES] ; + unsigned int got[EXCLUDEN] = { 0, 0, 0 } ; stralloc sa = STRALLOC_ZERO ; unsigned int line = 0 ; + FILE *fp ; int e = 0 ; - int r ; - int fd ; PROG = "s6-linux-init-umountall" ; - - /* - We need to go through /proc/mounts *in reverse order*, because later mounts - may depend on earlier mounts. - The getmntent() family of functions has obviously not been designed for that - use case at all, and it is actually more difficult to use it than to do the - /proc/mounts parsing by hand. So, we do it by hand with skalibs functions. - That's how you can tell a good API from a terrible one. - */ - - fd = open_readb("/proc/mounts") ; - if (fd < 0) strerr_diefu1sys(111, "open /proc/mounts") ; - memset(mountpoints, 0, sizeof(mountpoints)) ; - buffer_init(&b, &buffer_read, fd, buf, BUFSIZE) ; + fp = setmntent("/proc/mounts", "r") ; + if (!fp) strerr_diefu1sys(111, "open /proc/mounts") ; for (;;) { - size_t n, p ; - if (line >= MAXLINES) strerr_dief1x(111, "/proc/mounts too big") ; - sa.len = 0 ; - r = skagetln(&b, &sa, '\n') ; - if (r <= 0) break ; - p = byte_chr(sa.s, sa.len, ' ') ; - if (p >= sa.len) strerr_dief1x(111, "bad /proc/mounts format") ; - p++ ; - n = byte_chr(sa.s + p, sa.len - p, ' ') ; - if (n == sa.len - p) strerr_dief1x(111, "bad /proc/mounts format") ; - if (!stralloc_catb(&mountpoints[line], sa.s + p, n) || !stralloc_0(&mountpoints[line])) - strerr_diefu1sys(111, "store mount point") ; - line++ ; + struct mntent *p ; + unsigned int i = 0 ; + errno = 0 ; + p = getmntent(fp) ; + if (!p) break ; + for (; i < EXCLUDEN ; i++) + if (!strcmp(p->mnt_type, exclude_type[i])) + { + got[i]++ ; + break ; + } + if (i < EXCLUDEN && got[i] == 1) continue ; + if (line >= MAXLINES) + strerr_dief1x(100, "too many mount points") ; + mountpoints[line++] = sa.len ; + if (!stralloc_cats(&sa, p->mnt_dir) || !stralloc_0(&sa)) + strerr_diefu1sys(111, "add mount point to list") ; } - fd_close(fd) ; - stralloc_free(&sa) ; - if (r < 0) strerr_diefu1sys(111, "read /proc/mounts") ; + if (errno) strerr_diefu1sys(111, "read /proc/mounts") ; + endmntent(fp) ; while (line--) - if (umount(mountpoints[line].s) == -1) + if (umount(sa.s + mountpoints[line]) == -1) { e++ ; - strerr_warnwu2sys("umount ", mountpoints[line].s) ; + strerr_warnwu2sys("umount ", sa.s + mountpoints[line]) ; } + stralloc_free(&sa) ; return e ; } diff --git a/src/shutdown/s6-linux-init-shutdownd.c b/src/shutdown/s6-linux-init-shutdownd.c index f988955..9707ade 100644 --- a/src/shutdown/s6-linux-init-shutdownd.c +++ b/src/shutdown/s6-linux-init-shutdownd.c @@ -143,6 +143,7 @@ static inline void prepare_stage4 (char const *basedir, char what) buffer b ; int fd ; char buf[512] ; + size_t sabase = satmp.len ; unlink_void(STAGE4_FILE ".new") ; fd = open_excl(STAGE4_FILE ".new") ; if (fd == -1) strerr_diefu3sys(111, "open ", STAGE4_FILE ".new", " for writing") ; @@ -152,10 +153,15 @@ static inline void prepare_stage4 (char const *basedir, char what) "#!" EXECLINE_SHEBANGPREFIX "execlineb -P\n\n" EXECLINE_EXTBINPREFIX "foreground { " S6_LINUX_INIT_BINPREFIX "s6-linux-init-umountall }\n" + EXECLINE_EXTBINPREFIX "foreground { ") < 0 + || !string_quote(&satmp, basedir, strlen(basedir)) + || buffer_put(&b, satmp.s + sabase, satmp.len - sabase) < 0 + || buffer_puts(&b, "/scripts/" STAGE4 " }\n" S6_LINUX_INIT_BINPREFIX "s6-linux-init-hpr -f -") < 0 || buffer_put(&b, &what, 1) < 0 || buffer_putsflush(&b, "\n") < 0) strerr_diefu2sys(111, "write to ", STAGE4_FILE ".new") ; + satmp.len = sabase ; if (fchmod(fd, S_IRWXU) == -1) strerr_diefu2sys(111, "fchmod ", STAGE4_FILE ".new") ; fd_close(fd) ; -- cgit v1.2.3