summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2019-08-19 13:15:40 +0000
committerLaurent Bercot <ska-skaware@skarnet.org>2019-08-19 13:15:40 +0000
commit244dddd0ada79388a27f33e73b173764c581fca1 (patch)
tree9534fd0cadc2418c57188647b73e4d69883f3443
parent01b815a073101521e0b53cf4ce8fa9c81b5fc5d8 (diff)
downloads6-linux-init-244dddd0ada79388a27f33e73b173764c581fca1.tar.xz
Add stage 4 hook support
-rw-r--r--doc/overview.html4
-rw-r--r--doc/s6-linux-init-umountall.html14
-rw-r--r--doc/upgrade.html4
-rwxr-xr-xskel/rc.shutdown.final18
-rw-r--r--src/include-local/initctl.h1
-rw-r--r--src/init/s6-linux-init-maker.c1
-rw-r--r--src/misc/s6-linux-init-umountall.c76
-rw-r--r--src/shutdown/s6-linux-init-shutdownd.c6
8 files changed, 75 insertions, 49 deletions
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 <tt>halt</tt>, <tt>poweroff</tt>,
<li> <em>runlevel</em>: the script executing a machine state change
at boot time (normally invoked by <em>rc.init</em>, towards the default
runlevel) or when the administrator runs a <tt>telinit</tt> command. </li>
+ <li> <em>rc.shutdown.final</em>: 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, <em>just before</em> the system
+is rebooted or the power turned off. This script normally remains empty. </li>
</ul> </li>
</ul>
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 <tt>/proc/mounts</tt> 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). </li>
+ <li> It makes an exception for the first instances of <em>devtmpfs</em>, <em>proc</em>
+and <em>sysfs</em> filesystem types, but not for later instances. In other words: it
+does not attempt to unmount <tt>/dev</tt>, <tt>/proc</tt> and <tt>/sys</tt>, but it
+will attempt to unmount duplicates of those pseudo-filesystems. </li>
<li> <tt>s6-linux-init-umountall</tt> does not touch <tt>/etc/mtab</tt>. </li>
<li> If a filesystem fails to unmount, a warning is printed to stderr, but
<tt>s6-linux-init-umountall</tt> still attempts to unmount all the other ones. </li>
@@ -42,7 +46,7 @@ it read-only). </li>
<p>
<tt>s6-linux-init-umountall</tt> returns the number of errors it encountered
-when attempting to unmount all the filesystems listed in <tt>/proc/mounts</tt>.
+when attempting to unmount the filesystems listed in <tt>/proc/mounts</tt>.
</p>
<h2> Notes </h2>
@@ -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. </li>
<li> It is likely that some filesystems will still fail to unmount, typically
-<tt>/proc</tt> and <tt>/dev</tt>. 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. </li>
<li> Distributions usually provide a <tt>umount</tt> command with a <tt>-a</tt> 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
-<a href="//skarnet.org/software/s6-linux-utils/">s6-linux-utils</a> package also
-provides a <a href="//skarnet.org/software/s6-linux-utils/s6-umount.html">s6-umount</a>
-command with a <tt>-a</tt> option, but adding a dependency to that package would be a
-higher cost than simply reimplementing the specific functionality here. </li>
+and relies on unsafe interfaces, so it was decided not to use it. </li>
</ul>
</body>
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 @@
<ul>
<li> Ctrl-Alt-Del management is now activated by default, instead
of requiring a sysctl setting in stage 2. </li>
+ <li> <a href="s6-linux-init-umountall.html">s6-linux-init-umountall</a>
+now skips the first instance of <tt>/dev</tt>, <tt>/proc</tt> and <tt>sys</tt>. </li>
+ <li> stage 4 (after every process has been killed and the filesystems have
+been unmounted) now calls a new user script, <tt>rc.shutdown.final</tt>.
</ul>
<h2> in 1.0.2.1 </h2>
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 <errno.h>
#include <string.h>
+#include <stdio.h>
+#include <mntent.h>
#include <sys/mount.h>
-#include <skalibs/bytestr.h>
-#include <skalibs/buffer.h>
#include <skalibs/strerr2.h>
#include <skalibs/stralloc.h>
-#include <skalibs/djbunix.h>
#include <skalibs/skamisc.h>
-#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) ;