summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore16
-rw-r--r--AUTHORS1
-rw-r--r--INSTALL23
-rw-r--r--Makefile6
-rw-r--r--NEWS12
-rw-r--r--README2
-rwxr-xr-xconfigure72
-rw-r--r--doc/index.html102
-rw-r--r--doc/overview.html149
-rw-r--r--doc/quickstart.html178
-rw-r--r--doc/s6-halt.html55
-rw-r--r--doc/s6-linux-init-echo.html69
-rw-r--r--doc/s6-linux-init-hpr.html80
-rw-r--r--doc/s6-linux-init-logouthookd.html98
-rw-r--r--doc/s6-linux-init-maker.html558
-rw-r--r--doc/s6-linux-init-shutdown.html103
-rw-r--r--doc/s6-linux-init-shutdownd.html72
-rw-r--r--doc/s6-linux-init-telinit.html84
-rw-r--r--doc/s6-linux-init-umountall.html68
-rw-r--r--doc/s6-linux-init.html164
-rw-r--r--doc/s6-poweroff.html55
-rw-r--r--doc/s6-reboot.html55
-rw-r--r--doc/upgrade.html12
-rw-r--r--doc/why.html139
-rwxr-xr-xexamples/rc.init19
-rwxr-xr-xexamples/rc.shutdown16
-rw-r--r--package/deps-build4
-rw-r--r--package/deps.mak58
-rw-r--r--package/info2
-rw-r--r--package/modes13
-rw-r--r--package/targets.mak26
-rwxr-xr-xskel/rc.init34
-rwxr-xr-xskel/rc.shutdown17
-rwxr-xr-xskel/runlevel15
-rw-r--r--src/include-local/defaults.h9
-rw-r--r--src/include-local/initctl.h34
-rw-r--r--src/include/s6-linux-init/s6-linux-init.h8
-rw-r--r--src/init/deps-exe/s6-linux-init (renamed from src/init/deps-exe/s6-halt)0
-rw-r--r--src/init/deps-exe/s6-linux-init-maker2
-rw-r--r--src/init/deps-exe/s6-linux-init-telinit (renamed from src/init/deps-exe/s6-poweroff)0
-rw-r--r--src/init/hpr.c107
-rw-r--r--src/init/s6-halt.c7
-rw-r--r--src/init/s6-linux-init-maker.c666
-rw-r--r--src/init/s6-linux-init-telinit.c74
-rw-r--r--src/init/s6-linux-init.c189
-rw-r--r--src/init/s6-poweroff.c7
-rw-r--r--src/init/s6-reboot.c7
-rw-r--r--src/lib/deps-lib/s6_linux_init4
-rw-r--r--src/lib/s6_linux_init_logouthook.c19
-rw-r--r--src/misc/deps-exe/s6-linux-init-echo (renamed from src/init/deps-exe/s6-reboot)0
-rw-r--r--src/misc/deps-exe/s6-linux-init-logouthookd4
-rw-r--r--src/misc/deps-exe/s6-linux-init-umountall1
-rw-r--r--src/misc/s6-linux-init-echo.c39
-rw-r--r--src/misc/s6-linux-init-logouthookd.c69
-rw-r--r--src/misc/s6-linux-init-umountall.c70
-rw-r--r--src/shutdown/deps-exe/s6-linux-init-hpr5
-rw-r--r--src/shutdown/deps-exe/s6-linux-init-shutdown5
-rw-r--r--src/shutdown/deps-exe/s6-linux-init-shutdownd5
-rw-r--r--src/shutdown/deps-lib/hpr2
-rw-r--r--src/shutdown/hpr.h20
-rw-r--r--src/shutdown/hpr_shutdown.c16
-rw-r--r--src/shutdown/hpr_wall.c41
-rw-r--r--src/shutdown/s6-linux-init-hpr.c120
-rw-r--r--src/shutdown/s6-linux-init-shutdown.c265
-rw-r--r--src/shutdown/s6-linux-init-shutdownd.c309
-rwxr-xr-xtools/gen-deps.sh11
66 files changed, 3397 insertions, 1095 deletions
diff --git a/.gitignore b/.gitignore
index 6b2e3dd..d3aace8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,18 @@
*.o
*.lo
+/libhpr.a.xyzzy
+/libs6_linux_init.a.xyzzy
+/libs6_linux_init.so.xyzzy
/config.mak
/src/include/s6-linux-init/config.h
+/s6-linux-init
+/s6-linux-init-halt
+/s6-linux-init-poweroff
+/s6-linux-init-reboot
+/s6-linux-init-shutdown
+/s6-linux-init-shutdownd
+/s6-linux-init-telinit
+/s6-linux-init-logouthookd
+/s6-linux-init-echo
+/s6-linux-init-umountall
/s6-linux-init-maker
-/s6-halt
-/s6-poweroff
-/s6-reboot
diff --git a/AUTHORS b/AUTHORS
index 825df2c..f0bd677 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -4,3 +4,4 @@ Main author:
Thanks to:
Colin Booth <colin@heliocat.net>
multiplexd <multiplexd@gmx.com>
+ Luis Ressel <aranea@aixah.de>
diff --git a/INSTALL b/INSTALL
index 621b16b..a64f220 100644
--- a/INSTALL
+++ b/INSTALL
@@ -6,25 +6,16 @@ Build Instructions
- A Linux-based system with a standard C development environment
- GNU make version 3.81 or later
- - skalibs version 2.8.0.0 or later: http://skarnet.org/software/skalibs/
-
- The scripts generated by the s6-linux-init-maker program have some
-additional dependencies:
-
+ - skalibs version 2.8.0.0 or later: https://skarnet.org/software/skalibs/
- execline version 2.5.1.0 or later: https://skarnet.org/software/execline/
- - s6-portable-utils version 2.2.1.3 or later: https://skarnet.org/software/s6-portable-utils/
- - s6-linux-utils version 2.5.0.1 or later: https://skarnet.org/software/s6-linux-utils/
- s6 version 2.8.0.0 or later: https://skarnet.org/software/s6/
+ - Optional: nsss version 0.0.1.1 or later: https://skarnet.org/software/nsss/
+ - Optional: utmps version 0.0.2.0 or later: https://skarnet.org/software/utmps/
This software is Linux-specific. It will run on a Linux kernel,
version 2.6.32 or later. However, it should not be too hard to port to
other Unix-like operating systems.
- The s6-linux-init-maker binary will actually build *and run* on
-another operating system; however, the scripts it produces depend on
-s6-linux-utils binaries, which are really Linux-specific - so those
-scripts will not run properly on another OS.
-
* Standard usage
--------------
@@ -32,12 +23,18 @@ scripts will not run properly on another OS.
./configure && make && sudo make install
will work for most users.
- It will install the binaries in /bin.
+ It will install the exported binaries in /bin, the unexported
+binaries in /libexec, and the skeleton scripts in /etc/s6-linux-init/skel.
You can strip the binaries and libraries of their extra symbols via
"make strip" before the "make install" phase. It will shave a few bytes
off them.
+ Your default init binaries will not be overwritten. They will only be
+overwritten by a manual action after you invoke s6-linux-init-maker,
+check that you are happy with its output, and decide to use it as
+your default init.
+
* Customization
-------------
diff --git a/Makefile b/Makefile
index 610dbb6..45228b6 100644
--- a/Makefile
+++ b/Makefile
@@ -88,7 +88,6 @@ install-libexec: $(LIBEXEC_TARGETS:%=$(DESTDIR)$(libexecdir)/%)
install-bin: $(BIN_TARGETS:%=$(DESTDIR)$(bindir)/%)
install-lib: $(STATIC_LIBS:lib%.a.xyzzy=$(DESTDIR)$(libdir)/lib%.a)
install-include: $(ALL_INCLUDES:src/include/$(package)/%.h=$(DESTDIR)$(includedir)/$(package)/%.h)
-install-data: $(ALL_DATA:src/etc/%=$(DESTDIR)$(datadir)/%)
ifneq ($(exthome),)
@@ -109,9 +108,6 @@ $(DESTDIR)$(sproot)/library.so/lib%.so.$(version_M): $(DESTDIR)$(dynlibdir)/lib%
endif
-$(DESTDIR)$(datadir)/%: src/etc/%
- exec $(INSTALL) -D -m 644 $< $@
-
$(DESTDIR)$(dynlibdir)/lib%.so: lib%.so.xyzzy
$(INSTALL) -D -m 755 $< $@.$(version) && \
$(INSTALL) -l $(@F).$(version) $@.$(version_m) && \
@@ -146,6 +142,6 @@ lib%.a.xyzzy:
lib%.so.xyzzy:
exec $(REALCC) -o $@ $(CFLAGS_ALL) $(CFLAGS_SHARED) $(LDFLAGS_ALL) $(LDFLAGS_SHARED) -Wl,-soname,$(patsubst lib%.so.xyzzy,lib%.so.$(version_M),$@) $^ $(EXTRA_LIBS) $(LDLIBS)
-.PHONY: it all clean distclean tgz strip install install-dynlib install-bin install-lib install-include install-data
+.PHONY: it all clean distclean tgz strip install install-dynlib install-bin install-lib install-include
.DELETE_ON_ERROR:
diff --git a/NEWS b/NEWS
index 1320ec7..846edab 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,16 @@
Changelog for s6-linux-init.
+In 1.0.0.0
+----------
+
+ - Complete rework; see the documentation overview.
+ - s6-halt, s6-poweroff, s6-reboot removed.
+ - s6-linux-init-maker now produces a set of executable scripts
+that are a drop-in replacement for a sysvinit installation, and
+that respect the LSB-3.0.0 specification.
+ - utmp management, runlevel management.
+
+
In 0.4.0.1
----------
@@ -35,3 +46,4 @@ In 0.3.0.0
- Added this NEWS file. :)
- Major types overhaul to make them more POSIXly correct:
compatibility with skalibs-2.5.0.0.
+
diff --git a/README b/README
index a8ab37e..34d5c02 100644
--- a/README
+++ b/README
@@ -4,7 +4,7 @@ s6-linux-init - tools to create an s6-based Linux init system
s6-linux-init is a suite of tools designed to provide an init program
for the Linux kernel - i.e., the first program run at boot time.
- See http://skarnet.org/software/s6-linux-init/ for details.
+ See https://skarnet.org/software/s6-linux-init/ for details.
* Installation
diff --git a/configure b/configure
index 9e4dc0a..5d7d438 100755
--- a/configure
+++ b/configure
@@ -22,6 +22,8 @@ Fine tuning of the installation directories:
--libexecdir=DIR package-scoped executables [EPREFIX/libexec]
--libdir=DIR static library files [PREFIX/lib/$package]
--includedir=DIR C header files [PREFIX/include]
+ --skeldir=DIR script skeleton files [PREFIX/etc/s6-linux-init/skel]
+ --tmpfsdir=DIR assume the initial tmpfs will be mounted on DIR [/run]
If no --prefix option is given, by default libdir (but not dynlibdir) will be
/usr/lib/$package, and includedir will be /usr/include.
@@ -45,6 +47,9 @@ Optional features:
--enable-absolute-paths do not rely on PATH to access this package's binaries,
hardcode absolute BINDIR/foobar paths instead [disabled]
+ --enable-nsss use the nsss library for user information [disabled]
+ --enable-utmps link against the utmps library [disabled]
+
EOF
exit 0
}
@@ -71,6 +76,10 @@ fail () {
exit 1
}
+isprefix() {
+ case "$1" in "$2"*) return 0 ;; *) return 1 ;; esac ;
+}
+
fnmatch () {
eval "case \"\$2\" in $1) return 0 ;; *) return 1 ;; esac"
}
@@ -117,6 +126,13 @@ tryldflag () {
fi
}
+getmacro () {
+ echo "#include <$1>" | $CC_AUTO $CPPFLAGS_AUTO $CPPFLAGS $CPPFLAGS_POST $CFLAGS_AUTO $CFLAGS $CFLAGS_POST -E -dM - | grep -F -- "$2" | cut -d' ' -f3-
+}
+
+getmacrostring () {
+ getmacro "$1" "$2" | sed -e 's/^"//' -e 's/"$//'
+}
# Actual script
@@ -146,6 +162,7 @@ static=true
allpic=true
slashpackage=false
abspath=false
+usensss=false
sproot=
home=
exthome=
@@ -157,6 +174,9 @@ addlibdpath=''
vpaths=''
vpathd=''
build=
+skeldir='$prefix/etc/s6-linux-init/skel'
+tmpfsdir=/run
+utmps=false
for arg ; do
case "$arg" in
@@ -187,6 +207,12 @@ for arg ; do
--disable-slashpackage) sproot= ; slashpackage=false ;;
--enable-absolute-paths|--enable-absolute-paths=yes) abspath=true ;;
--disable-absolute-paths|--enable-absolute-paths=no) abspath=false ;;
+ --enable-nsss|--enable-nsss=yes) usensss=true ;;
+ --disable-nsss|--enable-nsss=no) usensss=false ;;
+ --skeldir=*) skeldir=${arg#*=} ;;
+ --tmpfsdir=*) tmpfsdir=${arg#*=} ;;
+ --enable-utmps|--enable-utmps=yes) utmps=true ;;
+ --disable-utmps|--enable-utmps=no) utmps=false ;;
--enable-*|--disable-*|--with-*|--without-*|--*dir=*) ;;
--host=*|--target=*) target=${arg#*=} ;;
--build=*) build=${arg#*=} ;;
@@ -211,7 +237,7 @@ fi
# Expand installation directories
stripdir prefix
-for i in exec_prefix dynlibdir libexecdir bindir libdir includedir sysdeps sproot ; do
+for i in exec_prefix dynlibdir libexecdir bindir libdir includedir sysdeps sproot tmpfsdir skeldir ; do
eval tmp=\${$i}
eval $i=$tmp
stripdir $i
@@ -246,6 +272,7 @@ if $slashpackage ; then
libdir=${home}/library
libexecdir=$bindir
includedir=${home}/include
+ skeldir=${home}/etc/skel
while read dep condvar ; do
if test -n "$condvar" ; then
eval "cond=$condvar"
@@ -311,7 +338,7 @@ timer_lib=$(cat $sysdeps/timer.lib)
util_lib=$(cat $sysdeps/util.lib)
if $allpic ; then
- tryflag CFLAGS_AUTO -fPIC
+ tryflag CPPFLAGS_AUTO -fPIC
fi
tryflag CFLAGS_AUTO -std=c99
tryflag CFLAGS -fomit-frame-pointer
@@ -324,6 +351,7 @@ tryflag CPPFLAGS_AUTO -Werror=implicit-function-declaration
tryflag CPPFLAGS_AUTO -Werror=implicit-int
tryflag CPPFLAGS_AUTO -Werror=pointer-sign
tryflag CPPFLAGS_AUTO -Werror=pointer-arith
+tryflag CPPFLAGS_AUTO -Wno-parentheses
tryflag CFLAGS_AUTO -ffunction-sections
tryflag CFLAGS_AUTO -fdata-sections
@@ -357,6 +385,23 @@ if test -z "$vpaths" ; then
done < package/deps-build
fi
+if $utmps ; then
+ echo "Sanity checking cross-package configuration..."
+ p=`getmacrostring utmps/config.h UTMPS_UTMPD_PATH`
+ if isprefix "$p" "$tmpfsdir"/ ; then : ; else
+ echo "UTMPS_UTMPD_PATH has been configured as $p which is not located under $tmpfsdir"
+ exit 1
+ fi
+ utmpd_path=${p##${tmpfsdir}/}
+ p=`getmacrostring utmps/config.h UTMPS_WTMPD_PATH`
+ if isprefix "$p" "$tmpfsdir"/ ; then : ; else
+ echo "UTMPS_WTMPD_PATH has been configured as $p which is not located under $tmpfsdir"
+ exit 1
+ fi
+ wtmpd_path=${p##${tmpfsdir}/}
+ echo " ... ok"
+fi
+
echo "creating config.mak..."
cmdline=$(quote "$0")
for i ; do cmdline="$cmdline $(quote "$i")" ; done
@@ -375,6 +420,7 @@ libexecdir := $libexecdir
bindir := $bindir
libdir := $libdir
includedir := $includedir
+skeldir := $skeldir
sysdeps := $sysdeps
slashpackage := $slashpackage
sproot := $sproot
@@ -423,6 +469,18 @@ if $allpic ; then
else
echo "STATIC_LIBS_ARE_PIC :="
fi
+if $usensss ; then
+ echo "LIBNSSS := -lnsss"
+ echo "MAYBEPTHREAD_LIB := -lpthread"
+else
+ echo "LIBNSSS :="
+ echo "MAYBEPTHREAD_LIB :="
+fi
+if $utmps ; then
+ echo "LIBUTMPS := -lutmps"
+else
+ echo "LIBUTMPS :="
+fi
exec 1>&3 3>&-
echo " ... done."
@@ -451,6 +509,16 @@ else
echo "#define ${package_macro_name}_EXTBINPREFIX \"\""
fi
echo "#define ${package_macro_name}_LIBEXECPREFIX \"$libexecdir/\""
+echo "#define ${package_macro_name}_SKELDIR \"$skeldir\""
+echo "#define ${package_macro_name}_TMPFS \"$tmpfsdir\""
+echo
+if $utmps ; then
+ echo "#define ${package_macro_name}_UTMPD_PATH \"$utmpd_path\""
+ echo "#define ${package_macro_name}_WTMPD_PATH \"$wtmpd_path\""
+else
+ echo "#undef ${package_macro_name}_UTMPD_PATH"
+ echo "#undef ${package_macro_name}_WTMPD_PATH"
+fi
echo
echo "#endif"
exec 1>&3 3>&-
diff --git a/doc/index.html b/doc/index.html
index 41c7139..83464df 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -20,26 +20,48 @@
<h2> What is it&nbsp;? </h2>
<p>
- s6-linux-init is a set of minimalistic tools to create a
+ s6-linux-init is a set of minimalistic tools used to create a
<a href="//skarnet.org/software/s6/">s6</a>-based init
system, including a <tt>/sbin/init</tt> binary, on a Linux kernel.
</p>
<p>
- s6-linux-init is meant to automate creation of scripts revolving
-around the use of other skarnet.org tools, especially s6, in order
-to provide a complete booting environment with integrated supervision
-and logging without having to hand-craft all the details.
+ The resulting architecture follows the Unix philosophy (one job &rarr;
+one tool) as closely as possible, and is fully dedicated to the s6 way of
+managing a system:
</p>
+<ul>
+ <li> <a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a>
+runs as process 1 for the whole machine lifetime. </li>
+ <li> Every daemon is supervised. </li>
+ <li> No logs are ever lost. </li>
+ <li> Policy is entirely left to the user. Typically, <em>any</em> service manager
+can be run on top of s6-linux-init. </li>
+</ul>
+
<p>
- Please read the documentation for the
-<a href="s6-linux-init-maker.html">s6-linux-init-maker</a> program
-carefully, but if you're impatient, you can also read this
-<a href="quickstart.html">quickstart guide</a>, which includes
-a small FAQ.
+ Nevertheless, the architecture is fully compliant with various
+empirical and historical specifications. For instance, it provides:
</p>
+<ul>
+ <li> utmp management compatible with sysvinit </li>
+ <li> runlevel management, with a configurable default, overridable from the
+kernel command line </li>
+ <li> sysvinit-like commands to shut the system down, including a
+<tt>shutdown</tt> command that follows the
+<a href="http://refspecs.linuxbase.org/LSB_3.0.0/LSB-PDA/LSB-PDA/shutdown.html">LSB specification</a> </li>
+</ul>
+
+<hr />
+
+<ul>
+ <li> <a href="why.html">Why</a> s6-linux-init&nbsp;? </li>
+ <li> <a href="overview.html">An overview</a> of s6-linux-init </li>
+ <li> <a href="quickstart.html">A quickstart guide</a> for the impatient </li>
+</ul>
+
<hr />
<h2> Installation </h2>
@@ -50,30 +72,31 @@ a small FAQ.
<li> A Linux-based system with a standard C development environment </li>
<li> GNU make, version 3.81 or later </li>
<li> <a href="//skarnet.org/software/skalibs/">skalibs</a> version
-2.8.0.0 or later </li>
+2.8.0.1 or later </li>
<li> <a href="//skarnet.org/software/execline/">execline</a> version
2.5.1.0 or later </li>
- <li> <a href="//skarnet.org/software/s6-portable-utils/">s6-portable-utils</a> version
-2.2.1.3 or later </li>
- <li> <a href="//skarnet.org/software/s6-linux-utils/">s6-linux-utils</a> version
-2.5.0.1 or later </li>
<li> <a href="//skarnet.org/software/s6/">s6</a> version
2.8.0.0 or later </li>
</ul>
<p>
-When you <em>build</em> s6-linux-init, the
- <a href="s6-linux-init-maker.html">s6-linux-init-maker</a> tool
-is created (as well as the shutdown commands);
- then you <em>run</em> that tool to create an init script,
-and then you can <em>boot</em> your system on that init script.
+ The following optional dependencies are also supported:
</p>
+<ul>
+ <li> If you're using <a href="https://www.musl-libc.org/">musl</a> and
+want nsswitch-like functionality:
+<a href="//skarnet.org/software/nsss/">nsss</a> version
+0.0.1.1 or later </li>
+ <li> If you want secure utmp functionality:
+<a href="//skarnet.org/software/utmps/">utmps</a> version
+0.0.2.1 or later </li>
+</ul>
+
<p>
- skalibs is a <em>build-time</em> dependency. If you are linking binaries
-against the shared version of the skalibs library, it also becomes a
-<em>run-time</em> and <em>boot-time</em> dependency. <br />
- All the other listed packages are <em>boot-time</em> dependencies only.
+ All those dependencies are <em>build-time</em> and <em>run-time</em>,
+except possibly skalibs, which is not needed at run time if you are linking
+all the other packages against the <em>static</em> version of libskarnet.
</p>
<h3> Licensing </h3>
@@ -87,7 +110,7 @@ against the shared version of the skalibs library, it also becomes a
<ul>
<li> The current released version of s6-linux-init is
-<a href="s6-linux-init-0.4.0.1.tar.gz">0.4.0.1</a>. </li>
+<a href="s6-linux-init-1.0.0.0.tar.gz">1.0.0.0</a>. </li>
<li> Alternatively, you can checkout a copy of the
<a href="//git.skarnet.org/cgi-bin/cgit.cgi/s6-linux-init/">s6-linux-init
git repository</a>:
@@ -114,25 +137,42 @@ the previous versions of s6-linux-init and the current one. </li>
<h2> Reference </h2>
+<h3> General </h3>
+
+<ul>
+ <li> A <a href="why.html">rationale</a> for this package </li>
+ <li> An <a href="overview.html">overview</a> of s6-linux-init </li>
+ <li> A <a href="quickstart.html">quickstart page</a> </li>
+</ul>
+
<h3> Commands </h3>
<p>
- All these commands exit 111 if they encounter a temporary error, and
-100 if they encounter a permanent error - such as a misuse.
+ Unless more details are provided in an <em>Exit codes</em> section
+of a specific page, all these commands exit 0 on success, 111 if they encounter a
+temporary error (such as a system call failure) and 100 if they encounter a
+permanent error (such as a misuse).
</p>
<ul>
<li><a href="s6-linux-init-maker.html">The <tt>s6-linux-init-maker</tt> program</a></li>
-<li><a href="s6-halt.html">The <tt>s6-halt</tt> program</a></li>
-<li><a href="s6-poweroff.html">The <tt>s6-poweroff</tt> program</a></li>
-<li><a href="s6-reboot.html">The <tt>s6-reboot</tt> program</a></li>
+<li><a href="s6-linux-init.html">The <tt>s6-linux-init</tt> program</a></li>
+<li><a href="s6-linux-init-hpr.html">The <tt>s6-linux-init-hpr</tt> program</a></li>
+<li><a href="s6-linux-init-shutdown.html">The <tt>s6-linux-init-shutdown</tt> program</a></li>
+<li><a href="s6-linux-init-shutdownd.html">The <tt>s6-linux-init-shutdownd</tt> program</a></li>
+<li><a href="s6-linux-init-telinit.html">The <tt>s6-linux-init-telinit</tt> program</a></li>
+<li><a href="s6-linux-init-logouthookd.html">The <tt>s6-linux-init-logouthookd</tt> program</a></li>
+<li><a href="s6-linux-init-echo.html">The <tt>s6-linux-init-echo</tt> program</a></li>
+<li><a href="s6-linux-init-umountall.html">The <tt>s6-linux-init-umountall</tt> program</a></li>
</ul>
<h2> Related resources </h2>
<ul>
<li> <tt>s6-linux-init</tt> is discussed on the
-<a href="//skarnet.org/lists.html#skaware">skaware</a> mailing-list. </li>
+<a href="//skarnet.org/lists.html#skaware">skaware</a> and
+<a href="//skarnet.org/lists.html#supervision">supervision</a>
+mailing-lists. </li>
<li> There is a <tt>#s6</tt> IRC channel on Freenode. Sometimes people
are there and even answer questions. </li>
</ul>
diff --git a/doc/overview.html b/doc/overview.html
new file mode 100644
index 0000000..8f19bcd
--- /dev/null
+++ b/doc/overview.html
@@ -0,0 +1,149 @@
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-linux-init: overview</title>
+ <meta name="Description" content="s6-linux-init: overview" />
+ <meta name="Keywords" content="s6-linux-init overview architecture design" />
+ <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-linux-init</a><br />
+<a href="//skarnet.org/software/">Software</a><br />
+<a href="//skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> An overview of s6-linux-init </h1>
+
+<h2> Organisation of the package </h2>
+
+<p>
+ When installed, the <tt>s6-linux-init</tt> package provides the
+following:
+</p>
+
+<ul>
+ <li> <em>Binaries</em>, that are typically installed in <tt>/bin</tt>:
+ <ul>
+ <li> <a href="s6-linux-init-maker.html">s6-linux-init-maker</a> is the main
+program of the package and is used to create <tt>/sbin/init</tt> scripts
+and their supporting environment depending on configuration parameters
+given on its command line. </li>
+ <li> <a href="s6-linux-init-hpr.html">s6-linux-init-hpr</a> is an
+implementation of the SysV <tt>halt</tt>, <tt>poweroff</tt> and <tt>reboot</tt>
+commands; <a href="s6-linux-init-telinit.html">s6-linux-init-telinit</a>
+is an implementation of the SysV <tt>telinit</tt> command; and
+ <a href="s6-linux-init-shutdown.html">s6-linux-init-shutdown</a>
+is an implementation of the
+<a href="http://refspecs.linuxbase.org/LSB_3.0.0/LSB-PDA/LSB-PDA/shutdown.html">shutdown</a>
+command. <a href="s6-linux-init.html">s6-linux-init</a> is an
+implementation of stage 1 <tt>/sbin/init</tt>, but it needs to be
+given command-line options in order to do what the user has chosen.
+An invocation of <a href="s6-linux-init-maker.html">s6-linux-init-maker</a>
+will create proper wrappers for all those commands, named after their
+short SysV names; the wrappers are directly usable as turnkey replacements
+for SysV commands. </li>
+ <li> Other binaries are support binaries, not meant to be called
+directly by the user. They are called internally, in scripts
+created by a
+<a href="s6-linux-init-maker.html">s6-linux-init-maker</a> invocation -
+typically in run scripts for early services. </li>
+ </ul> </li> <p />
+
+ <li> A <em>small library</em>, that for now contains a single symbol,
+<tt>s6_linux_init_logouthook()</tt>, intended for distributions using
+<tt>login</tt> programs that add utmp entries for users logging in and
+expect <tt>init</tt> to clean up after them when users log out. See
+the <a href="s6-linux-init-logouthookd.html">s6-linux-init-logouthookd</a>
+page for details. </li> <p />
+
+ <li> <em>Skeleton scripts</em>, installed by default in
+<tt>/etc/s6-linux-init/skel</tt>; that location can be changed at build
+time via the <tt>--skeldir</tt> configure option. At
+<tt>s6-linux-init-maker</tt> invocation time, the scripts are copied from the skeleton
+directory to the <tt>scripts</tt> subdirectory of the directory created
+by <tt>s6-linux-init-maker</tt>, and the copy is meant to be edited
+by the user. The skeleton scripts are commented and examples of
+interaction with various service manager are given; it is recommended
+to review them, and possibly edit them too.
+These scripts are the following:
+ <ul>
+ <li> <em>rc.init</em>: the script launching the system initialization
+procedure once stage 1 init is done and
+<a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a> is
+safely running as pid 1. </li>
+ <li> <em>rc.shutdown</em>: the script launching the system shutdown
+procedure when the admin runs a <tt>halt</tt>, <tt>poweroff</tt>,
+<tt>reboot</tt> or <tt>shutdown</tt> command. </li>
+ <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>
+ </ul> </li>
+</ul>
+
+<h2> Organisation of the booted system </h2>
+
+<p>
+ When a system has booted on an <tt>/sbin/init</tt> program created by
+<a href="s6-linux-init-maker.html">s6-linux-init-maker</a>, the following
+invariants are met:
+</p>
+
+<ul>
+ <li> A tmpfs is mounted on <tt>/run</tt> - that location can be changed
+at build-time via the <tt>--tmpfsdir</tt> option to configure. The rest
+of this document assumes it is <tt>/run</tt>. </li>
+ <li> <a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a> is
+running as pid 1 on the <tt>/run/service</tt> scandir. </li>
+ <li> Every process on the system is running with at least the environment
+defined in the <tt>/etc/s6-linux-init/current/env</tt> envdir. The
+<tt>/etc/s6-linux-init/current</tt> location can be changed at
+<a href="s6-linux-init-maker.html">s6-linux-init-maker</a> invocation time
+via the <tt>-c</tt> option. </li>
+ <li> Some early services are defined in <tt>/run/service</tt>, and running.
+They are not seen by the service manager and should remain up all the time,
+until the machine shuts down: they are considered a part of the init system,
+even if they're not process 1. Each of these services uses very few resources.
+The services are:
+ <ul>
+ <li> <tt>s6-svscan-log</tt>: the catch-all logger </li>
+ <li> <tt>s6-linux-init-shutdownd</tt>: the shutdown manager, running
+the shutdown sequence in a reproducible environment when a shutdown command
+is executed, then performing the last shutdown steps. </li>
+ <li> <tt>s6-linux-init-runleveld</tt>: the runlevel manager, running
+the <em>runlevel</em> script in a reproducible environment when a <tt>telinit</tt>
+command is executed. </li>
+ <li> (optionally) <tt>s6-linux-init-logouthookd</tt>: a local service performing
+utmp record cleanup duty for patched <tt>login</tt> programs. </li>
+ <li> (optionally) <tt>s6-linux-init-early-getty</tt>: the early getty,
+allowing the user to login even if <em>rc.init</em> fails early. </li>
+ <li> (optionally) <tt>utmpd</tt> and <tt>wtmpd</tt>: the services performing
+utmp and wtmp access when <a href="//skarnet.org/software/utmps/">utmps</a> is
+used. </li>
+ </ul> </li>
+</ul>
+
+ <h2> Integration with the service manager </h2>
+
+<p>
+ The s6-linux-init package's duties stop where the service manager's start.
+s6-linux-init simply brings the system up to the point where it is stable and
+operational enough for the service manager to take over; and at shutdown
+time, s6-linux-init just tells the service manager to bring down the services,
+and then performs the last steps of the shutdown: killing all the remaining
+processes, unmounting the file systems and halting/powering off/rebooting
+the machine.
+</p>
+
+<p>
+ All the interactions between s6-linux-init and the service manager are
+configurable: they happen in the <em>rc.init</em>, <em>rc.shutdown</em>
+and <em>runlevel</em> scripts. Examples are provided in the skeleton
+scripts, that you should review and edit.
+</p>
+
+</body>
+</html>
diff --git a/doc/quickstart.html b/doc/quickstart.html
index 6a0069b..b214443 100644
--- a/doc/quickstart.html
+++ b/doc/quickstart.html
@@ -3,9 +3,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Language" content="en" />
- <title>s6-linux-init: quickstart and FAQ</title>
- <meta name="Description" content="s6-linux-init: quickstart and FAQ" />
- <meta name="Keywords" content="s6-linux-init installation quickstart faq" />
+ <title>s6-linux-init: quickstart</title>
+ <meta name="Description" content="s6-linux-init: quickstart" />
+ <meta name="Keywords" content="s6-linux-init installation quickstart quick start" />
<!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
</head>
<body>
@@ -16,7 +16,7 @@
<a href="//skarnet.org/">skarnet.org</a>
</p>
-<h1> Quickstart and FAQ for s6-linux-init </h1>
+<h1> Quickstart for s6-linux-init </h1>
<h2> Quickstart </h2>
@@ -25,175 +25,27 @@
<ul>
<li> <a href="//skarnet.org/software/skalibs/">skalibs</a> </li>
<li> <a href="//skarnet.org/software/execline/">execline</a> </li>
- <li> <a href="//skarnet.org/software/s6-portable-utils/">s6-portable-utils</a> </li>
- <li> <a href="//skarnet.org/software/s6-linux-utils/">s6-linux-utils</a> </li>
<li> <a href="//skarnet.org/software/s6/">s6</a> </li>
</ul> </li>
- <li> Install <a href="index.html">s6-linux-init</a> itself. </li>
- <li> Save your old <tt>/sbin/init</tt> binary. </li>
<li> Save and remove your old <tt>/etc/s6-linux-init</tt> directory, if you have one. </li>
+ <li> Install <a href="index.html">s6-linux-init</a> itself. </li>
+ <li> Save your old <tt>/sbin/init</tt>, <tt>/sbin/telinit</tt>, <tt>/sbin/shutdown</tt>,
+<tt>/sbin/halt</tt>, <tt>/sbin/poweroff</tt> and <tt>/sbin/reboot</tt> binaries. </li>
<li> Make sure you have a <tt>/run</tt> directory. </li>
- <li> Write a machine initialization script in <tt>/etc/rc.init</tt> and
-a machine shutdown script in <tt>/etc/rc.shutdown</tt>. Make sure they are
-executable. See below for more information on how to write these scripts. </li>
+ <li> Edit the scripts in <tt>/etc/s6-linux-init/skel</tt>. </li>
<li> Check that your devtmpfs is automounted by your kernel at boot time. If it is not,
add the <tt>-d 1</tt> option to the <tt>s6-linux-init-maker</tt> command line below. </li>
<li> As root, run: <pre>
- rm -rf /tmp/s6-linux-init /tmp/init
- s6-linux-init-maker /tmp/s6-linux-init
- mv /tmp/s6-linux-init /etc/
- ln -sf /etc/s6-linux-init/init /sbin/init </pre> </li>
+ rm -rf /tmp/blah
+ s6-linux-init-maker -1 -G "/sbin/getty 38400 tty1" /tmp/blah
+ rm -rf /etc/s6-linux-init/current
+ mv /tmp/blah /etc/s6-linux-init/current
+ cp -a /etc/s6-linux-init/current/bin/* /sbin/ </pre> </li>
<li> Reboot. </li>
<li> Congratulations! your machine is now running a s6-based init system. </li>
- <li> To shut the machine down, use the
-<a href="s6-halt.html">s6-halt</a>,
-<a href="s6-poweroff.html">s6-poweroff</a> or
-<a href="s6-reboot.html">s6-reboot</a> command as appropriate. </li>
+ <li> To shut the machine down, use <tt>/sbin/shutdown</tt>, <tt>/sbin/halt</tt>,
+<tt>/sbin/poweroff</tt> or <tt>/sbin/reboot</tt> as usual. </li>
</ol>
-<h3> What should go into <tt>/etc/rc.init</tt> and <tt>/etc/rc.shutdown</tt>&nbsp;? </h3>
-
-<h4> <tt>/etc/rc.init</tt> </h4>
-
-<p>
- This script will be run after s6-linux-init has done is job, i.e.
-<a href="//skarnet.org/software/s6/">s6-svscan</a> is running as process 1, and it
-is now up to <tt>/etc/rc.init</tt> to get the machine to its usable state.
-It normally contains a call to the service manager to bring up all the services;
-for instance, if you're using
-<a href="//skarnet.org/software/s6-rc/">s6-rc</a> as your service manager, and
-your top bundle (containing all the services you want to bring up) is named
-<tt>ok-all</tt>, a proper <tt>/etc/rc.init</tt> could look like this:
-</p>
-
-<pre>#!/bin/sh
-s6-rc-init /run/service &amp;&amp; exec s6-rc -u change ok-all
-</pre>
-
-<p>
- The script can assume that:
-</p>
-
-<ul>
- <li> There is a tmpfs partition, only writable by root, mounted on <tt>/run</tt> </li>
- <li> There is a <a href="//skarnet.org/software/s6/">s6</a> supervision tree
-running on <tt>/run/service</tt> </li>
- <li> <tt>/dev</tt> is mounted, but <tt>/proc</tt> and <tt>/sys</tt> are not </li>
-</ul>
-
-<h4> <tt>/etc/rc.shutdown</tt> </h4>
-
-<p>
- This script is spawned by <a href="//skarnet.org/software/s6/">s6-svscan</a>
-when the administrator calls <a href="s6-halt.html">s6-halt</a>,
-<a href="s6-poweroff.html">s6-poweroff</a> or
-<a href="s6-reboot.html">s6-reboot</a>. When this script exits, the final
-shutdown sequence is run, which means that the supervision tree is dismantled,
-all processes are killed, the file systems are umounted and the system
-undergoes a hardware shutdown or reboot. So the goal of this script is to
-bring services down in an orderly fashion and perform all the necessary
-cleanups before all remaining processes are summarily killed.
-</p>
-
-<p>
- If you're using <a href="//skarnet.org/software/s6-rc/">s6-rc</a> as your
-service manager, a proper <tt>/etc/rc.shutdown</tt> could look like this:
-</p>
-
-<pre>#!/bin/sh
-exec s6-rc -da change
-</pre>
-
-<h2> FAQ </h2>
-
-<h4> Why is it so complicated to use s6 as an init process? It's much
-simpler with runit. </h4>
-
-<p>
- Yes, runit is simpler, because it provides a simple
-<a href="http://smarden.org/runit/runit.8.html">runit</a> binary
-suitable as a <tt>/sbin/init</tt> program and calls scripts to
-handle the three stages of init. However, the runit design has a
-few perfectible points:
-</p>
-
-<ul>
- <li> The one-time initialization is performed in <tt>/etc/runit/1</tt>, but
-the supervision tree is not run until <tt>/etc/runit/2</tt>, which means
-means that it is impossible to start supervised services during the
-one-time initialization. Early daemons such as <tt>udevd</tt>, for
-instance, have to remain unsupervised. </li>
- <li> runit runs with its descriptors pointing to <tt>/dev/console</tt>,
-which means that error messages from the supervision tree, and uncaught
-logs, will be displayed on the system console; they are not saved beyond
-the console buffer capabilities. </li>
- <li> The runit supervision tree is of height 3
-(runit, runsvdir, runsv), when height 2 is enough - some init
-systems, like sysvinit, systemd or launchd, even provide a
-supervision tree of height 1! (At the expense of complexity in the init
-process, of course.) Height 3 is a bit redundant, because the supervision
-capabilities of the root will be redundant with either those of the trunk
-or those of the branches. Its display is also aesthetically less pleasing than
-height 2: try out <tt>ps afuxww</tt> on a runit-based system.
-Yes, this point is extremely minor, but still deserves a mention. :-) </li>
-</ul>
-
-<p>
- Running a s6-based init addresses those issues:
-</p>
-
-<ul>
- <li> Save for the initial tmpfs mount, <em>all</em> of the machine
-initialization runs in the stage 2 script, i.e. <tt>/etc/rc.init</tt>,
-and the supervision tree is already available at that point. This
-makes it possible to start one-shot services as well as long-run
-services in the desired order while ensuring that every long-run service
-is properly supervised, i.e. it lays the ground for a proper dependency
-management system. </li>
- <li> s6-linux-init solves the problem of uncaught logs in a clean
-way, and any error message from any process in the system is
-guaranteed to end up in a logging directory. The <em>only</em>
-exception is error messages from the catch-all logger process itself:
-those naturally go to <tt>/dev/console</tt>. </li>
- <li> When s6-svscan runs as process 1, the supervision tree is of
-height 2, and <tt>ps afuxww</tt> looks clean. </li>
-</ul>
-
-<p>
- To sum up, a s6-based init is cleaner than a runit-based
-init; it's a bit more complex to set up, but it organizes the system
-in a better way, without using more resources. And the goal of
-s6-linux-init is to make the setup more accessible.
-</p>
-
-<h4> My <tt>/etc/rc.init</tt> script is not printing anything! </h4>
-
-<p>
- You probably gave the <tt>-r</tt> option to
-<a href="s6-linux-init-maker.html">s6-linux-init-maker</a>, and
-your <tt>/etc/rc.init</tt>'s output is being logged into the
-<tt>/run/uncaught-logs</tt> directory instead of printed to
-<tt>/dev/console</tt>.
-</p>
-
-<h4> I want to run s6 in a container, and I just want to log
-to stdout/stderr, without this tmpfs and <tt>/dev/console</tt>
-stuff and
-without having a catch-all logger inside the container. Is it
-possible&nbsp;? </h4>
-
-<p>
- Yes, it is possible, but then s6-linux-init may not be what you
-are looking for. For your case, it will be simpler to run s6-svscan
-directly!
-</p>
-
-<p>
- If you are using
-<a href="https://www.docker.com/">Docker</a>, there is a
-<a href="https://github.com/just-containers/s6-overlay">s6-overlay</a>
-project specifically made for integrating s6 into Docker images.
-</p>
-
</body>
</html>
diff --git a/doc/s6-halt.html b/doc/s6-halt.html
deleted file mode 100644
index 6f2a30e..0000000
--- a/doc/s6-halt.html
+++ /dev/null
@@ -1,55 +0,0 @@
-<html>
- <head>
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
- <meta http-equiv="Content-Language" content="en" />
- <title>s6-linux-init: the s6-halt program</title>
- <meta name="Description" content="s6-linux-init: the s6-halt program" />
- <meta name="Keywords" content="s6 linux init administration root utilities shutdown halt poweroff reboot" />
- <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
- </head>
-<body>
-
-<p>
-<a href="index.html">s6-linux-init</a><br />
-<a href="//skarnet.org/software/">Software</a><br />
-<a href="//skarnet.org/">skarnet.org</a>
-</p>
-
-<h1> The <tt>s6-halt</tt> program </h1>
-
-<p>
-<tt>s6-halt</tt> sends a signal to process 1 in order to halt the machine;
-or, with the <tt>-f</tt> option, it performs an immediate hard shutdown.
-</p>
-
-<h2> Interface </h2>
-
-<pre>
- s6-halt [ -h | -p | -r ] [ -f ]
-</pre>
-
-<ul>
- <li> s6-halt sends a signal to process 1. </li>
- <li> It then exits 0. </li>
-</ul>
-
-<h2> Options </h2>
-
-<ul>
- <li> <tt>-h</tt>&nbsp;: halt. The command will order a halt (i.e. the system will
-be shut down, but the power will remain up), which means
-sending a SIGUSR2 to process 1. This is the default. </li>
- <li> <tt>-p</tt>&nbsp;: poweroff. The command will order a power off, which means
-sending a SIGUSR1 to process 1. </li>
- <li> <tt>-r</tt>&nbsp;: reboot. The command will order a reboot, which means
-sending a SIGINT to process 1. </li>
- <li> <tt>-f</tt>&nbsp;: force. The command will not send any signal to process 1;
-it will just sync the filesystems then tell the kernel to halt, poweroff or reboot.
-<tt>s6-reboot -f</tt> or <tt>s6-poweroff -f</tt> should be the last program
-executed in the lifetime of a machine, at the end of the shutdown script called
-by process 1 when it receives a signal telling it to shut down. </li>
-</ul>
-
-</body>
-</html>
diff --git a/doc/s6-linux-init-echo.html b/doc/s6-linux-init-echo.html
new file mode 100644
index 0000000..837eab0
--- /dev/null
+++ b/doc/s6-linux-init-echo.html
@@ -0,0 +1,69 @@
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-linux-init: the s6-linux-init-echo program</title>
+ <meta name="Description" content="s6-linux-init: the s6-linux-init-echo program" />
+ <meta name="Keywords" content="s6-linux-init command s6-linux-init-echo echo" />
+ <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-linux-init</a><br />
+<a href="//skarnet.org/software/">Software</a><br />
+<a href="//skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>s6-linux-init-echo</tt> program </h1>
+
+<p>
+ s6-linux-init-echo writes its arguments to stdout.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-linux-init-echo [ -n ] [ -s sep ] <em>args...</em>
+</pre>
+
+<p>
+ s6-linux-init-echo writes its arguments <em>args</em> to stdout, separated with spaces.
+</p>
+
+<h2> Options </h2>
+
+<ul>
+ <li> <tt>-n</tt>&nbsp;: do not output a trailing newline. </li>
+ <li> <tt>-s</tt>&nbsp;<em>sep</em>&nbsp;: separate arguments with the <em>sep</em>
+character instead of a space. </li>
+</ul>
+
+<h2> Notes </h2>
+
+<ul>
+ <li> Strange, and appalling, as it may seem for such a simple task, there is
+no way to ensure that the <tt>echo</tt> program will behave consistently from
+Unix system to Unix system - and even from Linux distribution to Linux
+distribution. Despite there being a
+<a href="//pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html">standard</a>
+for it, the <tt>echo</tt> commands in GNU coreutils, busybox, toybox, sbase, and
+other implementations basically all exhibit different behaviours. Every shell has
+a built-in <tt>echo</tt> command, that fails to follow the POSIX standard. <tt>echo</tt>
+is the prime example of the consequences of the blatant disregard of early Unices
+for cross-system compatibility, and its followup as a turf war between GNU and the
+rest of the Linux world. As a distribution-agnostic software developer, it is ironically
+impossible to rely on a definite behaviour of the <tt>echo</tt> command on every
+supported system, and that is why s6-linux-init provides its own implementation.
+Fortunately, it is very easy to do so, with minimal overhead. </li>
+ <li> This command is an exact duplicate of the
+<a href="//skarnet.org/software/s6-portable-utils/s6-echo.html">s6-echo</a> command
+provided in the <a href="//skarnet.org/software/s6-portable-utils/">s6-portable-utils</a>
+package. It was decided <em>not</em> to have a dependency from s6-linux-init to
+s6-portable-utils: that dependency would arguably be a higher cost than the small
+amount of code duplication. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/s6-linux-init-hpr.html b/doc/s6-linux-init-hpr.html
new file mode 100644
index 0000000..5f09574
--- /dev/null
+++ b/doc/s6-linux-init-hpr.html
@@ -0,0 +1,80 @@
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-linux-init: the s6-linux-init-hpr program</title>
+ <meta name="Description" content="s6-linux-init: the s6-linux-init-hpr program" />
+ <meta name="Keywords" content="s6 linux init administration root utilities shutdown halt poweroff reboot s6-linux-init-hpr" />
+ <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-linux-init</a><br />
+<a href="//skarnet.org/software/">Software</a><br />
+<a href="//skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>s6-linux-init-hpr</tt> program </h1>
+</h1>
+
+<p>
+ <tt>s6-linux-init-hpr</tt> triggers the software shutdown procedure,
+or, with the <tt>-f</tt> option, it perform an immediate hardware shutdown.
+It is normally invoked through <tt>halt</tt>, <tt>poweroff</tt> or
+<tt>reboot</tt> wrappers created by
+<a href="s6-linux-init-maker.html">s6-linux-init-maker</a>.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-linux-init-hpr [ -f ] [ -h | -p | -r ] [ -d | -w ] [ -W ]
+</pre>
+
+<ul>
+ <li> If the <tt>-f</tt> option is present, the system is stopped or rebooted immediately. </li>
+ <li> Else, the machine's shutdown procedure is started.
+ <li> The command exits 0; the shutdown procedure happens asynchronously. </li>
+</ul>
+
+<p>
+ This interface is the traditional <em>sysvinit</em> interface for the
+<tt>halt</tt>, <tt>poweroff</tt> and <tt>reboot</tt> programs.
+<tt>s6-linux-init-hpr</tt> must always be called with one of the
+<tt>-h</tt>, <tt>-p</tt> or <tt>-r</tt> options.
+</p>
+
+<h2> Options </h2>
+
+<ul>
+ <li> <tt>-f</tt>&nbsp;: force. The command will not trigger a clean shutdown
+procedure; it will just sync the filesystems then tell the kernel to immediately
+halt, poweroff or reboot. This should be the last step in the lifetime of the
+machine. </li>
+ <li> <tt>-h</tt>&nbsp;: halt. The system will be shut down, but the power will remain up. </li>
+ <li> <tt>-p</tt>&nbsp;: poweroff. The system will be shut down and the power turned off. </li>
+ <li> <tt>-r</tt>&nbsp;: reboot. The system will reboot. </li>
+ <li> <tt>-d</tt>&nbsp;: Do not write a wtmp shutdown entry. </li>
+ <li> <tt>-w</tt>&nbsp;: Only write a wtmp shutdown entry; do not actually shut down
+the system. </li>
+ <li> <tt>-W</tt>&nbsp;: Do not send a <tt>wall</tt> message to users before shutting
+down the system. Some other implementations of the <tt>halt</tt>, <tt>poweroff</tt>
+and <tt>reboot</tt> commands use the <tt>--no-wall</tt> long option to achieve this. </li>
+</ul>
+
+<h2> Notes </h2>
+
+<ul>
+ <li> When an administrator runs
+<a href="s6-linux-init-maker.html">s6-linux-init-maker</a>, the resulting
+directory has a <tt>bin/</tt> subdirectory that contains <tt>halt</tt>,
+<tt>poweroff</tt> and <tt>reboot</tt> scripts that call <tt>s6-linux-init-hpr</tt>
+with the relevant option. The
+contents of this <tt>bin/</tt> subdirectory should then be copied by the
+administrator into <tt>/sbin</tt> for full interface compatibility with sysvinit. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/s6-linux-init-logouthookd.html b/doc/s6-linux-init-logouthookd.html
new file mode 100644
index 0000000..088f5d5
--- /dev/null
+++ b/doc/s6-linux-init-logouthookd.html
@@ -0,0 +1,98 @@
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-linux-init: the s6-linux-init-logouthookd program</title>
+ <meta name="Description" content="s6-linux-init: the s6-linux-init-logouthookd program" />
+ <meta name="Keywords" content="s6-linux-init command s6-linux-init-logouthookd login logout utmp hook sysvinit" />
+ <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-linux-init</a><br />
+<a href="//skarnet.org/software/">Software</a><br />
+<a href="//skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>s6-linux-init-logouthookd</tt> program </h1>
+
+<p>
+ s6-linux-init-logouthookd cleans up its client's utmp record when it dies.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-ipcserver <em>socket</em> s6-linux-init-logouthookd
+</pre>
+
+<p>
+ s6-linux-init-logouthookd implements a
+<a href="//skarnet.org/software/s6/localservice.html">local service</a>
+for getty programs that add an utmp record when a user logs in.
+</p>
+
+<p>
+ In the sysvinit model, <tt>getty</tt>/<tt>login</tt> and similar programs add an utmp
+record for every user that logs in, then exec into the user's shell.
+At logout time, the shell dies; sysvinit is supervising the <tt>getty</tt>
+program, so it's watching the pid, and respawns the <tt>getty</tt> when the
+shell dies. But before respawning the <tt>getty</tt>, it cleans up the
+utmp record, to correctly report that the user isn't logged in on this
+terminal anymore.
+</p>
+
+<p>
+ utmp is an old, clunky, insecure system (unless you're using
+<a href="//skarnet.org/software/utmps/">utmps</a>) and it is definitely
+not pid 1's job to have any knowledge of utmp and play janitor after
+<tt>getty</tt>. <a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a>
+definitely will not do it.
+</p>
+
+<p>
+ Some distributions use versions of <tt>login</tt> that fork the user's
+shell instead of execing it. When the user logs out, the <tt>login</tt>
+program cleans up after itself. This is a better model, but it's not
+always easy to patch <tt>login</tt> to go from a "exec the shell" model to a
+"fork the shell as a child" model.
+</p>
+
+<p>
+ <tt>s6-linux-init</tt> comes with a small library which makes it easy
+for a distribution to fully support utmp cleanup with an s6 init system
+if they so choose. Before execing into the user's shell, the <tt>login</tt>
+program should just make a call to <tt>s6_linux_init_logouthook()</tt>,
+and that's it. That function will call the <tt>s6-linux-init-logouthookd</tt>
+local service, which will do nothing but wait until the user's shell dies;
+and when it happens, the user's utmp record will automatically be cleaned up.
+</p>
+
+<h2> Exit codes </h2>
+
+<p>
+ <tt>s6-linux-init-logouthookd</tt>'s exit code does not matter, because
+no program uses it. However, here's the list for completeness:
+</p>
+
+<ul>
+ <li> 0: success, whether or not there was an utmp record to clean up. </li>
+ <li> 1: connection attempt from a non-root user. </li>
+ <li> 2: write attempt from a (misprogrammed) client. </li>
+ <li> 111: system call failed. </li>
+</ul>
+
+<h2> Notes </h2>
+
+<ul>
+ <li> The <a href="//skarnet.org/software/s6/localservice.html">local service</a>
+implementing logouthook support is automatically created at boot time when the
+<tt>-L</tt> option has been given to
+<a href="s6-linux-init-maker.html">s6-linux-init-maker</a>. Client-side, though,
+the various login programs must be patched at the source level. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/s6-linux-init-maker.html b/doc/s6-linux-init-maker.html
index 4c0b51d..02fd15d 100644
--- a/doc/s6-linux-init-maker.html
+++ b/doc/s6-linux-init-maker.html
@@ -21,27 +21,25 @@
<p>
<tt>s6-linux-init-maker</tt> reads configuration options on
the command line, and outputs a directory to place in the
-root filesystem. That directory contains a script suitable
-as an init program, as well as support file hierarchies to
-get a complete
+root filesystem. That directory contains
+a script that is suitable as an <tt>/sbin/init</tt> program
+as well as all the necessary files that this script needs
+to properly boot and bring up a full
<a href="//skarnet.org/software/s6/">s6</a>
-infrastructure running when the system is booted on that
-script.
+infrastructure.
</p>
<p>
s6-linux-init-maker only writes scripts. At boot time, these
scripts will call commands provided by other skarnet.org packages
such as
-<a href="//skarnet.org/software/execline/">execline</a> or
+<a href="//skarnet.org/software/execline/">execline</a> and
<a href="//skarnet.org/software/s6/">s6</a>. It is the
responsibility of the administrator to make sure that all the
dependencies are properly installed at boot time, and that the
-correct options have been given to s6-linux-init-maker so that
-the programs are found <em>on the root filesystem of the
-machine</em> - else the scripts will crash.
-</p>
-
+correct options have been given to <tt>s6-linux-init-maker</tt>
+so that the programs are found <em>on the root filesystem of the
+machine</em>. If it is not the case, the system will fail to boot.
</p>
<h2> Interface and usage </h2>
@@ -49,26 +47,26 @@ machine</em> - else the scripts will crash.
<pre>
s6-linux-init-maker \
[ -c <em>basedir</em> ] \
- [ -l <em>tmpfsdir</em> ] \
- [ -b <em>execline_bindir</em> ] \
- [ -u <em>log_uid</em> -g <em>log_gid</em> | -U ] \
+ [ -u <em>log_user</em> ] \
[ -G <em>early_getty</em> ] \
- [ -2 <em>initscript</em> ] \
- [ -r ] \
- [ -Z ] <em>shutdownscript</em> \
+ [ -1 ] \
+ [ -L ] \
[ -p <em>initial_path</em> ] \
[ -m <em>initial_umask</em> ] \
[ -t <em>timestamp_style</em> ] \
- [ -d <em>dev_style</em> ] \
+ [ -d <em>slashdev</em> ] \
[ -s <em>env_store</em> ] \
[ -e <em>initial_envvar</em> ] ... \
- [ -n ] \
- [ -q ] <em>finalsleeptime</em>
+ [ -q <em>finalsleeptime</em> ] \
+ [ -D <em>initdefault</em> ] \
+ [ -n | -N ]
+ [ -U <em>utmp_user</em> ] \
<em>dir</em>
</pre>
<ul>
- <li> s6-linux-init-maker should be run as root. </li>
+ <li> s6-linux-init-maker must be run as root, on the machine
+that will boot an s6-based system. </li>
<li> s6-linux-init-maker parses options on its command line. </li>
<li> It writes data into a directory <em>dir</em>, which must not
exist beforehand. </li>
@@ -78,161 +76,72 @@ or its contents. </li>
</ul>
<p>
- <em>dir</em> should then be copied by the administrator to the place
-declared as <em>basedir</em>. Be careful: it contains fifos, files with
+ Once the command has been run and <em>dir</em> has been created, there
+are a few manual steps to take:
+</p>
+
+<ol>
+ <li> <tt>s6-linux-init-maker</tt> has copied some scripts from the
+<tt>/etc/s6-linux-init/skel</tt> directory (or the directory you
+gave as an argument to the <tt>--skeldir</tt> configure option at
+build time) to the <em>dir</em><tt>/scripts</tt> directory. You
+should <strong>edit these scripts</strong> and adapt them to your use case.
+(Or you could edit the skeleton scripts before running
+<tt>s6-linux-init-maker</tt>.) The scripts are:
+ <ul>
+ <li> <tt>rc.init</tt>: this script will be run as <em>stage 2
+initialization</em>, i.e. the initialization that happens once
+<a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a>
+is running as process 1, and should contain all your normal
+system bootup tasks. Typically, it should initialize the service
+manager and then order it to bring the machine state to its
+fully operational state. <em>rc.init</em> is given the default
+<em>runlevel</em> as a first argument (i.e. the name of the state
+the machine should be brought to, traditionally <tt>default</tt>
+for OpenRC and <tt>2</tt> or <tt>5</tt> for sysv-rc), and the
+rest of the command line is made of the kernel's command line
+except for the kernel arguments of the <em>key=value</em> form,
+which have been stored into <em>env_store</em>. </li>
+ <li> <tt>rc.shutdown</tt>: this script will be run as the
+<em>shutdown sequence</em>, when the administrator runs the
+<tt>shutdown</tt>, <tt>halt</tt>, <tt>poweroff</tt> or <tt>reboot</tt>
+command. (As well as <tt>init 0</tt>, <tt>init 6</tt>,
+<tt>telinit 0</tt> and <tt>telinit 6</tt> for compatibility
+reasons.) It should ask the service manager to bring all the
+services down, and exit when it's done (in other words: it should
+not try to perform a hard halt/poweroff/reboot itself.)
+No arguments are given to this script. </li>
+ <li> <tt>runlevel</tt>: this script will be invoked for every
+<em>runlevel change</em>, i.e. change of machine states. It is
+given one argument: the name of the runlevel to change to.
+Typically, the <em>runlevel</em> script should just invoke the
+service manager, asking it to bring the machine state to the
+wanted runlevel. </li>
+ </ul> </li>
+ <li> Copy the <em>dir</em> directory to the place declared as
+<em>basedir</em> (<tt>/etc/s6-linux-init/current</tt> by default).
+ Be careful: it contains fifos, files with
precise uid/gid permissions, and files with non-standard access rights,
so be sure to copy it verbatim. The
<a href="//skarnet.org/software/s6-portable-utils/s6-hiercopy.html">s6-hiercopy</a>
-tool can do it, as well as the GNU or busybox <tt>cp -a</tt> or <tt>mv</tt> commands.
-</p>
-
-<p>
- The <tt><em>basedir</em>/init</tt> script
-is then suitable as a "stage 1" init program, i.e. the first program
-run by the kernel. The administrator should make a symbolic link
-from <tt>/sbin/init</tt> to <tt><em>basedir</em>/init</tt>; the
-machine will then be ready to boot
-</p>
+tool can do it, as well as the GNU or busybox <tt>cp -a</tt> or <tt>mv</tt> commands. </li>
+ <li> Back up your <tt>/sbin</tt>. Then copy, link or symlink all the scripts
+and symlinks in the <em>basedir</em><tt>/bin</tt> directory into <tt>/sbin</tt>.
+ In particular, the <tt><em>basedir</em>/bin/init</tt> script should
+be accessible as <tt>/sbin/init</tt>. </li>
+</ol>
<h2> Boot sequence </h2>
<p>
- When the kernel boots, it runs the <tt><em>basedir</em>/init</tt> script,
-also known as <em>stage 1</em>. and this is what happens:
-</p>
-
-<ul>
- <li> <em>stage 1</em> is an
-<a href="//skarnet.org/software/execline/">execline</a> script, so
-the first process run by the kernel is the
-<a href="//skarnet.org/software/execline/execlineb.html">execlineb</a>
-program launcher. </li>
- <li> <em>stage 1</em> mounts a
-<a href="https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt">tmpfs</a>
-filesystem on <em>tmpfsdir</em>. </li>
- <li> <em>stage 1</em> copies <tt><em>basedir</em>/run-image</tt> verbatim to
-<em>tmpfsdir</em>. </li>
- <li> <em>stage 1</em> empties its environment, then reads a global set of environment variables from the
-<tt><em>basedir</em>/env</tt>
-<a href="//skarnet.org/software/s6/s6-envdir.html">environment directory</a>. </li>
- <li> <em>stage 1</em> forks a child that will block until
-<a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a> is running. </li>
- <li> <em>stage 1</em> executes, as process 1, into
-<a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a>,
-with <tt><em>tmpfsdir</em>/service</tt> as a
-<a href="//skarnet.org/software/s6/scandir.html">scan directory</a>. </li>
- <li> This scan directory already contains at least one service, which is the
-<em>catch-all logger</em>: error messages from the supervision tree, and
-from services that do not have a dedicated logger, are handled by a
-special <a href="//skarnet.org/software/s6/s6-log.html">s6-log</a>
-instance and made available in <tt><em>tmpfsdir</em>/uncaught-logs</tt>
-instead of clogging the system console. </li>
- <li> If the <tt>-G</tt> option has been given to s6-linux-init-maker, the
-scan directory will also contain a service for an early getty. </li>
- <li> s6-svscan starts all the services defined in the scan directory,
-and unblocks the child forked by <em>stage 1</em>. </li>
- <li> This child executes into <em>initscript</em>. </li>
-</ul>
-
-<p>
- <em>initscript</em> is the responsibility of the administrator - it will
-not be written automatically!
-It should
-contain all the necessary initialization sequence to bring up a proper
-system. When <em>initscript</em> is executed, the machine state is as follows:
-</p>
-
-<ul>
- <li> <em>initscript</em>'s working directory is <tt>/</tt> and its stdin
-is <tt>/dev/null</tt>. Its
-stdout and stderr both point either to <tt>/dev/console</tt> or to the pipe
-to the catch-all logger, depending on the <tt>-r</tt> option. </li>
- <li> The system has a valid device directory mounted on <tt>/dev</tt>. </li>
- <li> Depending on the kernel boot command line, the root filesystem
-may be in read-only mode. </li>
- <li> There is a tmpfs available for root only in <em>tmpfsdir</em>. </li>
- <li> <a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a>
-is running as process 1. At any time, it is possible to make it supervise a long-lived
-process by linking the appropriate
-<a href="//skarnet.org/software/s6/servicedir.html">service directory</a>
-into <tt><em>tmpfsdir</em>/service</tt>, then running the command
-<tt>s6-svscanctl -a <em>tmpfsdir</em>/service</tt>. Services without a
-dedicated logger will send their output to the catch-all logger. </li>
- <li> A getty service may already be available. The point of this early
-getty is essentially to make it easier to debug if <em>initscript</em> fails. </li>
-</ul>
-
-<p>
- There is <em>nothing else</em>. In particular, no filesystem has been
-mounted yet, including <tt>/proc</tt> and <tt>/sys</tt>; and no one-time
-initialization
-has been performed. The point of <em>stage 1</em> is only to make it
-possible to run <em>initscript</em> with a logging infrastructure and a
-supervision infrastructure already available, and all the
-real machine and service initialization should happen in <em>initscript</em>,
-also known as <em>stage 2</em>.
-</p>
-
-<h2> Shutdown sequence </h2>
-
-<ul>
-
- <li> A shutdown is performed when the administrator runs one of the
-<a href="s6-halt.html">s6-halt</a>,
-<a href="s6-poweroff.html">s6-poweroff</a> or
-<a href="s6-reboot.html">s6-reboot</a> commands. </li>
-
- <li> Those commands send a signal to the
-<a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a>
-process running as pid 1; this signal is caught and s6-svscan runs the
-corresponding "signal handler" script that has been placed by
-s6-linux-init-maker into the
-<tt><em>basedir</em>/run-image/service/.s6-svscan</tt> directory (and that
-has been copied at boot time to <tt><em>tmpfsdir</em>/service/.s6-svscan</tt>). </li>
-
- <li> That script first spawns the <em>shutdownscript</em> script, who
-must have been written by the administrator. The purpose of
-<em>shutdownscript</em> is to perform the high-level shutdown sequence
-while the supervision tree is still alive. Typically, when using a
-service manager, <em>shutdownscript</em> would tell the service manager
-to bring all services down. When using
-<a href="//skarnet.org/software/s6-rc/">s6-rc</a>, a typical
-<em>stage2_finish</em> script just contains <tt>s6-rc -da change</tt>.
- More generally speaking, <em>shutdownscript</em> should undo what
-<em>stage2</em> has done at boot time. </li>
-
- <li> The "signal handler" script then tells s6-svscan to exit via an
-appropriate <a href="//skarnet.org/software/s6/s6-svscanctl.html">s6-svscanctl</a>
-command: s6-svscan then executes into the final shutdown sequence. This
-sequence is made of the following actions:
-
-<ul>
- <li> The supervision tree gets torn down. </li>
- <li> All data is flushed to disk. </li>
- <li> All processes get a SIGTERM, a SIGHUP, and a SIGCONT. This should
-allow all processes to die gracefully. Note that most processes should
-already have been killed during the <tt>/etc/rc.shutdown</tt> execution;
-this phase only catches stragglers, background processs, etc. </li>
- <li> The sequence sleeps for <em>finalsleeptime</em> milliseconds, to
-allow all processes to finish their clean exit routine. </li>
- <li> All processes get a SIGKILL. </li>
- <li> All zombies are reaped. </li>
- <li> All filesystems get unmounted, and the root filesystem is remounted
-read-only. </li>
- <li> The machine performs a hardware reboot, halt or poweroff, depending
-on the command that has been used. </li>
-</ul> </li>
-
-</ul>
-
-<p>
- The <tt>examples/</tt> subdirectory of the s6-linux-init package
-contains an example of <tt>/etc/rc.init</tt>
-and <tt>/etc/rc.shutdown</tt> scripts, suitable for
-<em>initscript</em> and <em>shutdownscript</em>
-respectively. Those scripts can practically be used as is if the machine
-is managed by the <a href="//skarnet.org/software/s6-rc/">s6-rc</a>
-service manager.
+ When the kernel boots, it may run an initramfs first, but in any
+case it then runs the <tt>/sbin/init</tt> script,
+also known as <em>stage 1</em>. This script is just an execution
+of the <a href="s6-linux-init.html">s6-linux-init</a> program with
+some command-line options that are directly transferred from the
+<tt>s6-linux-init-maker</tt> invocation. Refer to the
+<a href="s6-linux-init.html">s6-linux-init</a> man page to know
+exactly what it does.
</p>
<h2> s6-linux-init-maker options </h2>
@@ -241,88 +150,53 @@ service manager.
<li> <tt>-c</tt>&nbsp;<em>basedir</em>&nbsp;: at boot time, <em>stage 1</em>,
which should be accessible as <tt><em>basedir</em>/init</tt>,
will read its read-only data from <em>basedir</em>. After running
-s6-linux-init-maker, the administrator should make sure to copy the
+<tt>s6-linux-init-maker</tt>, you should make sure to copy the
created directory <em>dir</em> to <em>basedir</em>. <em>basedir</em>
must be absolute. Default is
-<strong><tt>/etc/s6-linux-init</tt></strong>. </li> <p />
-
- <li> <tt>-l</tt>&nbsp;<em>tmpfsdir</em>&nbsp;: at boot time, a tmpfs will
-be mounted on <em>tmpfsdir</em>. The directory should already exist in
-the root filesystem, and be empty. <em>tmpfsdir</em> must be absolute. Default is
-<strong><tt>/run</tt></strong>. </li> <p />
-
- <li> <tt>-b</tt>&nbsp;<em>execline_bindir</em>&nbsp;: init is run by the kernel
-without a PATH, and since it is a script, it is necessary to tell it where
-to find the
-<a href="//skarnet.org/software/execline/execlineb.html">execlineb</a>
-launcher and the first few early commands before PATH can be set.
-<em>execline_bindir</em> is the location where the execline binaries can be
-found. It must be absolute. Default is
-<strong><tt>/bin</tt></strong>. </li> <p />
-
- <li> <tt>-u</tt>&nbsp;<em>log_uid</em>&nbsp;: the catch-all
-logger will run with the uid <em>log_uid</em>. Default is 0. </li> <p />
-
- <li> <tt>-g</tt>&nbsp;<em>log_gid</em>&nbsp;: the catch-all
-logger will run with the gid <em>log_gid</em>. Default is 0. </li> <p />
-
- <li> <tt>-U</tt>&nbsp;: the correct <em>log_uid</em> and
-<em>log_gid</em> values for the catch-all logger will be read from the
-UID and GID environment variables that have been passed to
-s6-linux-init-maker. This allows for invocations such as
-<tt>s6-envuidgid nobody s6-linux-init-maker -U ...</tt> so that
-the catch-all logger runs as the <tt>nobody</tt> user. Be aware that
-this option is only safe when the user database on the
-<em>boot-time</em> machine is the same as on the <em>run-time</em>
-machine, else the catch-all logger may run with an unexpected uid
-and gid. </li> <p />
+<strong><tt>/etc/s6-linux-init/current</tt></strong>. </li> <p />
+
+ <li> <tt>-u</tt>&nbsp;<em>log_user</em>&nbsp;: the catch-all
+logger will run as the <em>log_user</em> user. Default is <tt>root</tt>. </li> <p />
<li> <tt>-G</tt>&nbsp;<em>early_getty</em>&nbsp;: if this option
-is set, s6-linux-init-maker will define a service that will run
-very early, before <em>stage2</em> is executed. This early service
-should be a getty, to allow logins even if <em>stage2</em> fails.
+is set, <tt>s6-linux-init-maker</tt> will define an additional s6 service
+that will be named <tt>s6-linux-init-early-getty</tt> and started
+at the same time <em>rc.init</em> is executed. This early service
+should be a getty, or equivalent, to allow logins even if <em>stage2</em> fails.
<em>early_getty</em> should be a simple command line: for instance,
<tt>"/sbin/getty 38400 tty1"</tt>. By default, no early service
is defined. </li> <p />
- <li> <tt>-2</tt>&nbsp;<em>initscript</em>&nbsp;: <em>initscript</em> is
-the location of the stage 2 script that will be run when the
-system has an operational supervision tree. It must be absolute. Default is
-<strong><tt>/etc/rc.init</tt></strong>. </li> <p />
-
- <li> <tt>-r</tt>&nbsp;: redirect. By default, <em>stage2</em> is
-run with stdout and stderr pointing to <tt>/dev/console</tt>, so that
-users can see what init scripts print. However, it may conflict
-with an early getty, or be undesirable for other reasons. The
-<tt>-r</tt> option redirects <em>stage2</em>'s stdout and stderr
-to the catch-all logger, so the output will be made available
-in the <tt><em>tmpfsdir</em>/uncaught-logs</tt> directory. </li> <p />
-
- <li> <tt>-Z</tt>&nbsp;<em>shutdownscript</em>&nbsp;:
-<em>shutdownscript</em> is the location of the script that will be
-run when s6-svscan receives a signal that tells it to stop the
-machine, before it executes into the final shutdown sequence. It must be
-absolute. Default is <strong><tt>/etc/rc.shutdown</tt></strong>.
-Note that this script is run with its stdout and stderr
-redirected to the <tt><em>tmpfsdir</em>/uncaught-logs</tt> logging
-directory, so its output will not appear on the system's console. </li> <p />
-
- <li> <tt>-p</tt>&nbsp;<em>initial_path</em>&nbsp;: the value to
-set the PATH environment variable to, for all the starting processes.
-This will be done as early as possible in <em>stage 1</em>. It is
-absolutely necessary for
-<a href="//skarnet.org/software/execline/">execline</a>,
-<a href="//skarnet.org/software/s6/">s6</a>,
-<a href="//skarnet.org/software/s6-portable-utils/">s6-portable-utils</a> and
-<a href="//skarnet.org/software/s6-linux-utils/">s6-linux-utils</a>
+ <li> <tt>-1</tt>&nbsp;: make it so that all the messages that are
+sent to the catch-all logger (i.e. all the error messages that are not
+caught by a dedicated logger, as well as the output from <em>rc.init</em>,
+<em>runlevel</em> and <em>rc.shutdown</em>)
+are also copied to <tt>/dev/console</tt>. (Timestamps are not
+copied to <tt>/dev/console</tt>.) This is generally useful to
+debug a system at a glance, but if a failing program keeps sending
+error messages, it may interfere with comfortable usage of an early
+getty. A common workaround is to make the early getty start on
+<tt>tty2</tt> and leave tty1 for <tt>/dev/console</tt> to print on. </li> <p />
+
+ <li> <tt>-L</tt>&nbsp;: add an early <tt>s6-linux-init-logouthookd</tt>
+service to clean up utmp records at user logout time. Check the
+<a href="s6-linux-init-logouthookd">s6-linux-init-logouthookd</a> page
+for details. </li> <p />
+
+ <li> <tt>-p</tt>&nbsp;<em>initial_path</em>&nbsp;: the initial value
+for the PATH environment variable, that will be transmitted to all the
+starting process unless it's overridden by a PATH declaration via the
+<tt>-e</tt> option.
+It is absolutely necessary for
+<a href="//skarnet.org/software/execline/">execline</a> and
+<a href="//skarnet.org/software/s6/">s6</a>
binaries to be accessible via <em>initial_path</em>, else the machine
will not boot. Default is
<strong><tt>/usr/bin:/bin</tt></strong>. </li> <p />
<li> <tt>-m</tt>&nbsp;<em>initial_umask</em>&nbsp;: the value of
the initial file umask for all the starting processes, in octal.
-Default is
-<strong><tt>022</tt></strong>. </li> <p />
+Default is <strong><tt>022</tt></strong>. </li> <p />
<li> <tt>-t</tt>&nbsp;<em>timestamp_style</em>&nbsp;: how
logs are timestamped by the catch-all logger. 0 means no
@@ -333,44 +207,46 @@ timestamp, 1 means
and 3 means both. Default is
<strong><tt>1</tt></strong>. </li> <p />
- <li> <tt>-d</tt>&nbsp;<em>dev_style</em>&nbsp;: how <tt>/dev</tt> is
-handled on this system. 0 means a static <tt>/dev</tt>, 1 means
-devtmpfs but not automounted by the kernel at boot time, and 2 means
-devtmpfs automounted by the kernel at boot time. Default is
-<strong><tt>2</tt></strong>. </li> <p />
+ <li> <tt>-d</tt>&nbsp;<em>slashdev</em>&nbsp;: mount a devtmpfs.
+If this option is given, <a href="s6-linux-init.html">s6-linux-init</a>
+will mount a devtmpfs pseudo-filesystem on <em>slashdev</em>. This
+is useful if the kernel has not been configured to mount
+the devtmpfs at boot time and there is no static <tt>/dev</tt>.
+By default, it is assumed that there is a suitable <tt>/dev</tt>
+at boot time, and no additional devtmpfs is mounted. </li> <p />
<li> <tt>-s</tt>&nbsp;<em>env_store</em>&nbsp;: stage 1 init sometimes
-inherits a few environment variables from the kernel. It empties its
-environment before spawning stage2 and executing into s6-svscan, in
+inherits a few environment variables from the kernel. (These variables
+correspond to the arguments on the kernel command line that are of the
+form <em>key=value</em>.) It empties its
+environment before spawning <em>rc.init</em> and executing into s6-svscan, in
order to prevent those "kernel" environment variables from leaking
into the whole process tree. However, sometimes those variables are
needed at a later time; in that case, giving the <tt>-s</tt> option
-to s6-linux-init-maker makes stage 1 init dump the "kernel" environment
-variables into the <em>env_store</em> directory, via the
-<a href="//skarnet.org/software/s6-portable-utils/s6-dumpenv.html">s6-dumpenv</a>
-program, before erasing them. <em>env_store</em> should obviously be
-a writable directory, so it should be located under <em>tmpfsdir</em>!
-If this option is not given (which is the default), the environment
-inherited from the kernel isn't saved anywhere. </li> <p />
+to <tt>s6-linux-init-maker</tt> makes stage 1 init dump the "kernel" environment
+variables into the <em>env_store</em> directory (under a format that is
+later readable with
+<a href="//skarnet.org/software/s6/s6-envdir.html">s6-envdir -fn</a>)
+before erasing them. <em>env_store</em> should obviously be
+a writable directory, so it should be located under <tt>/run</tt>
+(or your chosen tmpfsdir)!
+If this option is not given, the environment inherited from the kernel
+isn't saved anywhere - which is the default. </li> <p />
<li> <tt>-e</tt>&nbsp;<em>initial_envvar</em>&nbsp;: this option
-can be repeated. For every <em>initial_envvar</em>, s6-linux-init-maker
+can be repeated. For every <em>initial_envvar</em>, <tt>s6-linux-init-maker</tt>
will adjust the global environment directory in <em>dir</em>/env.
<em>initial_envvar</em> must either be of the form <em>VAR</em>,
to make sure that <em>VAR</em> does not appear in the global
environment, or of the form <em>VAR=VALUE</em>, to add an
environment variable <em>VAR</em> with the value <em>VALUE</em>.
+The global environment is the environment that every supervised
+process (as well as the <em>rc.init</em> script) will run with,
+so it will be inherited by default by every process running on
+the system.
The TZ variable, for instance, is a good candidate to be set in
the global environment. </li> <p />
- <li> <tt>-n</tt>&nbsp;: tells s6-linux-init-maker that the init script
-is going to run in a container, as pid 1 in a non-root namespace.
-This modifies the <tt>.s6-svscan/finish</tt>, <tt>.s6-svscan/SIGHUP</tt>
-and <tt>.s6-svscan/SIGINT</tt> scripts slightly, in order to provide
-adequate functionality when the containerized system is asked to
-shutdown. Do not add this option if the init script is going to run
-in the root pid namespace. </li> <p />
-
<li> <tt>-q</tt>&nbsp;<em>finalsleeptime</em>&nbsp;: when the machine
shuts down, all processes that have not already been killed during
<tt>shutdownscript</tt> will receive a SIGTERM or a SIGHUP to allow
@@ -380,11 +256,150 @@ will go on. This option configures the amount of time that will
elapse between the SIGTERM/SIGHUP and the SIGKILL.
Default is <strong>2000</strong>, meaning a grace period of 2 seconds. </li> <p />
+ <li> <tt>-D</tt>&nbsp;<em>initdefault</em>&nbsp;: boot the system with
+a runlevel set to <em>initdefault</em>, which can be an arbitrary
+string, but is usually <tt>2</tt>, <tt>3</tt>, <tt>5</tt> (traditional
+sysvinit behaviour) or <tt>default</tt> (OpenRC behaviour). Default is
+<tt>default</tt>. Note that if a <tt>2</tt>, <tt>3</tt>, <tt>4</tt>,
+<tt>5</tt>, or <tt>default</tt> argument is encountered in the kernel
+command line, it will be interpreted as the runlevel to boot the system
+on, and will override the default given here. </li> <p />
+
+ <li> <tt>-n</tt>&nbsp;: at boot time, assume that a tmpfs is already
+present on <tt>/run</tt> (or the argument that was given to the
+<tt>--tmpfsdir</tt> configure option at build time) and that its
+contents are essential. Instead of unmounting <tt>/run</tt> then
+mounting a tmpfs on it, <a href="s6-linux-init.html">s6-linux-init</a>
+will simply remount <tt>/run</tt>. This option is useful when
+s6-linux-init is used on a distribution that imposes its initramfs
+and said initramfs writes data to <tt>/run</tt> that is then used
+by the distribution's initialization scripts. (An initramfs should
+normally be transparent and leave no trace in the filesystem;
+unfortunately, a lot of distributions do not care.) By default,
+<tt>/run</tt> will be unmounted at boot time (just in case), and
+then a tmpfs will be mounted on it. <strong>Do not</strong> use
+this option if you are not sure: failure to remount <tt>/run</tt>
+will cause init to die and the kernel to panic. This option is
+incompatible with the <tt>-N</tt> option. </li> <p />
+
+ <li> <tt>-N</tt>&nbsp;: at boot time, do not perform
+mounting/unmounting/remounting on <tt>/run</tt> (or the <em>tmpfsdir</em>
+declared at build time) <strong>at all</strong>. By default,
+a tmpfs is mounted on <tt>/run</tt> at boot time. This option is
+useful when s6-linux-init is used to boot on an initramfs that
+will remain the de facto rootfs of the system (which is the case
+for instance in certain live CDs or certain embedded devices), in
+which case the rootfs is already read-write and in RAM and mounting
+an additional tmpfs is unnecessary. <strong>Do not</strong> use this
+option if your rootfs is read-only: failure to write to <tt>/run</tt>
+will cause init to die and the kernel to panic. This option is
+incompatible with the <tt>-n</tt> option. </li> <p />
+
+ <li> <tt>-U</tt>&nbsp;<em>utmp_user</em>&nbsp;: this option is only
+available when the s6-linux-init package has been built with the
+<tt>--enable-utmps</tt> configure option, that enables support for the
+<a href="//skarnet.org/software/utmps/">utmps</a> package. The option
+defines the user that the <tt>utmpd</tt> and <tt>wtmpd</tt> services
+will run as. Default is <tt>utmp</tt>. </li> <p />
+</ul>
+
+<h2> Organization of the created directory </h2>
+
+<p>
+ If <tt>s6-linux-init-maker</tt> returns successfully, <em>dir</em>
+contains data that will be used at boot time. (Actually,
+<em>basedir</em> will be used at boot time, not <em>dir</em>. Do not
+forget to copy <em>dir</em> to <em>basedir</em> once you have checked
+you are happy with what <tt>s6-linux-init-maker</tt> has created.)
+</p>
+
+<p>
+ This boot-time data is made of several subdirectories:
+</p>
+
+<ul>
+ <li> <tt>bin</tt>: this subdirectory contains scripts and symlinks
+that should be copied to <tt>/sbin</tt> or <tt>/bin</tt>. There is
+an <tt>init</tt> program performing stage 1 init, a <tt>telinit</tt>
+program to change runlevels, and utilities to order a machine shutdown. </li>
+ <li> <tt>env</tt>: this subdirectory is the envdir that is
+used to store the global environment. It will be read at boot time
+by stage 1 init, and transmitted to all spawned processes. </li>
+ <li> <tt>scripts</tt>: this subdirectory contains a copy of the
+skeleton scripts that have been installed in <tt>/etc/s6-linux-init/skel</tt>
+(or the argument to the <tt>--skeldir</tt> configure option at
+build time). These scripts should be edited before booting. They are
+described above. </li>
+ <li> <tt>run-image</tt>: this is a file hierarchy that will be
+copied verbatim at boot time to the newly made and mounted
+<tt>/run</tt> tmpfs (or whatever your <em>tmpfsdir</em> is). The
+subdirectories it contains are the following:
+ <ul>
+ <li> <tt>uncaught-logs</tt>: this is the directory where the
+catch-all logger will store and rotate the error messages produced
+by the s6 supervision tree and the services that do not redirect
+their own logs. </li>
+ <li> <tt>service</tt>: <tt>/run/service</tt> will be the scandir.
+It initially contains a <tt>.s6-svscan</tt> subdirectory that
+tells <a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a>
+what to do if it receives a signal (typically via the ctrlaltdel
+combination) and ensures a hard reboot if <tt>s6-svscan</tt> ever fails. It
+also contains a list of early services, i.e. s6 services that will
+be run at boot time as soon as <tt>s6-svscan</tt> is executed. These
+services are:
+ <ul>
+ <li> <tt>s6-svscan-log</tt>: the catch-all logger. </li>
+ <li> <tt>s6-linux-init-shutdownd</tt>: a service that listens
+to shutdown commands such as <tt>reboot</tt> and triggers the software
+shutdown procedure. </li>
+ <li> <tt>s6-linux-init-runleveld</tt>: a service that listens
+to runlevel change commands such as <tt>telinit</tt> and calls the
+<em>runlevel</em> script in a reproducible environment to bring the
+machine to the wanted state. </li>
+ <li> (If the <tt>-L</tt> option has been given to
+<tt>s6-linux-init-maker</tt>) <tt>s6-linux-init-logouthookd</tt>:
+the "clean up user utmp records at logout time" service. See the
+<a href="s6-linux-init-logouthookd.html">s6-linux-init-logouthookd</a>
+page for details. </li>
+ <li> (If the <tt>-G</tt> option has been given to
+<tt>s6-linux-init-maker</tt>) <tt>s6-linux-init-early-getty</tt>:
+the early getty service, that will allow a user to log in even if
+<em>rc.init</em> fails to bring the machine to a state where logins
+are possible. </li>
+ </ul> </li>
+ </ul> </li>
+</ul>
+
+<p>
+ If s6-linux-init has been built with
+<a href="//skarnet.org/software/utmps/">utmps</a> support, some more
+directories may exist:
+</p>
+
+<ul>
+ <li> A directory somewhere under <tt>run-image</tt>, by default <tt>utmps</tt>,
+that is the location where the utmp and wtmp files will be created. </li>
+ <li> Two additional early services named <tt>utmpd</tt> and <tt>wtmpd</tt>,
+that are the <a href="//skarnet.org/software/utmps/">utmps</a> way of
+providing secure utmp functionality. </li>
</ul>
<h2> Notes </h2>
<p>
+ A directory created by <tt>s6-linux-init-maker</tt> is only valid on
+the machine it has been created on. Pre-creating init directories for
+other machines is not supported.
+</p>
+
+<p>
+ After booting, <em>basedir</em> should remain untouched during the
+lifetime of the machine, because the machine state change and shutdown
+procedures will look for data in <em>basedir</em>. New invocations of
+<tt>s6-linux-init-maker</tt> should use a different <em>basedir</em>.
+</p>
+
+<p>
The difficult parts of
<a href="//skarnet.org/software/s6/s6-svscan-1.html">running
s6-svscan as process 1</a> are:
@@ -399,22 +414,27 @@ tree's output away from <tt>/dev/console</tt> (which is fine for a
first process invocation but impractical for log management of a
whole process tree) and into a logger that is itself managed by the
supervision tree it's reading data from. </li>
+ <li> Keeping appearances of compatibility with another init system
+is difficult: in particular, the mechanisms around the shutdown
+procedure are fundamentally different from about any other init
+system, so even a simple command such as <tt>reboot</tt> needs an
+ad-hoc implementation. </li>
</ul>
<p>
- The main benefit of s6-linux-init-maker is that it automates those
-parts. This means that it has been designed for <em>real hardware</em>
-where the above issues apply.
- If you are building an init system for a
-virtual machine, a container, or anything similar that does not
-have the <tt>/dev/console</tt> issue or the read-only rootfs issue,
-you will probably not reap much benefit from using s6-linux-init-maker:
+ The main benefit of <tt>s6-linux-init-maker</tt> is that it offers
+transparent compatibility while automating the tricky technical part.
+That means that <tt>s6-linux-init-maker</tt> has been designed for
+<em>real hardware</em>, or at least full-fledged Linux systems,
+where the above issues apply. If you are building an init system for a
+container, or anything similar that does not
+have the <tt>/dev/console</tt> issue, the read-only rootfs issue,
+or the need for sysvinit compatibility,
+you will probably not reap much benefit from using <tt>s6-linux-init-maker</tt>:
you could probably invoke
<a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a>
directly as your process 1, or build a script by hand, which
would result in a simpler init with less dependencies.
-Nevertheless, if you prefer using s6-linux-init-maker, it
-supports this case via the <tt>-n</tt> option.
</p>
</body>
diff --git a/doc/s6-linux-init-shutdown.html b/doc/s6-linux-init-shutdown.html
new file mode 100644
index 0000000..519d1e3
--- /dev/null
+++ b/doc/s6-linux-init-shutdown.html
@@ -0,0 +1,103 @@
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-linux-init: the s6-linux-init-shutdown program</title>
+ <meta name="Description" content="s6-linux-init: the s6-linux-init-shutdown program" />
+ <meta name="Keywords" content="s6 linux init administration root utilities shutdown halt poweroff reboot" />
+ <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-linux-init</a><br />
+<a href="//skarnet.org/software/">Software</a><br />
+<a href="//skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>s6-linux-init-shutdown</tt> program </h1>
+
+<p>
+<tt>s6-linux-init-shutdown</tt> triggers the system shutdown procedure.
+It is normally invoked as <tt>/sbin/shutdown</tt>.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-linux-init-shutdown [ -h | -p | -r | -k ] [ -a ] [ -t <em>sec</em> ] [ -f | -F ] <em>time</em> [ <em>message</em> ]
+ s6-linux-init-shutdown -c [ <em>message</em> ]
+</pre>
+
+<ul>
+ <li> If the <tt>-c</tt> option is present, a pending shutdown is cancelled. </li>
+ <li> Else, it plans the shutdown procedure at time <em>time</em>. </li>
+ <li> If a <em>message</em> argument has been given, <em>message</em> is
+broadcast to all logged in users (as tracked by utmp). </li>
+ <li> <tt>shutdown</tt> exits 0. The shutdown procedure happens asynchronously. </li>
+</ul>
+
+<p>
+ The <tt>s6-linux-init-shutdown</tt> program conforms to the LSB-3.0.0
+<a href="http://refspecs.linuxbase.org/LSB_3.0.0/LSB-PDA/LSB-PDA/shutdown.html">shutdown</a>
+interface.
+</p>
+
+<p>
+ <em>time</em> must follow the following format:
+<tt>[&nbsp;now&nbsp;|&nbsp;[+]<em>mins</em>&nbsp;|&nbsp;<em>hh</em>:<em>mm</em>&nbsp;]
+</p>
+
+<ul>
+ <li> <tt>now</tt> means: trigger the shutdown sequence immediately. </li>
+ <li> <em>hh</em>:<em>mm</em> means: absolute time. Trigger the shutdown sequence when the time
+<em>hh</em>:<em>mm</em> occurs. If that time has passed for the day, it will wait for the
+next day. <em>hh</em> must have 1 or 2 digits; <em>mm</em> must have 2 digits. </li>
+ <li> <em>mins</em> or <tt>+</tt><em>mins</em> means: relative time. Trigger the shutdown
+sequence after <em>mins</em> minutes. </li>
+</ul>
+
+<h2> Options </h2>
+
+<ul>
+ <li> <tt>-a</tt>&nbsp;: access control. The shutdown sequence will only be
+launched if one of the users listed in <tt>/etc/shutdown.allow</tt>
+is currently logged in (as tracked by utmp). <tt>/etc/shutdown.allow</tt>
+is a text file, one user per line, lines starting with <tt>#</tt> are comments. </li>
+ <li> <tt>-t</tt>&nbsp;<em>sec</em>&nbsp;: at the end of the shutdown sequence,
+when it's time to kill all processes, have a "grace time" period
+of <em>sec</em> seconds between the SIGTERM and the SIGKILL (to allow processes
+receiving SIGTERM to exit cleanly). Default is 3 seconds. </li>
+ <li> <tt>-k</tt>&nbsp;: warning only. <em>message</em> will be sent to all
+logged in users, but the shutdown sequence will not be triggered. </li>
+ <li> <tt>-h</tt>&nbsp;: at the end of the shutdown sequence, halt the system. </li>
+ <li> <tt>-p</tt>&nbsp;: at the end of the shutdown sequence, power off the system.
+(This option is provided as an extension, it is not required by the LSB interface.) </li>
+ <li> <tt>-r</tt>&nbsp;: at the end of the shutdown sequence, reboot the system. </li>
+ <li> <tt>-f</tt>&nbsp;: ignored. </li>
+ <li> <tt>-F</tt>&nbsp;: ignored. </li>
+ <li> <tt>-c</tt>&nbspl: cancel a planned shutdown (i.e. cancel the effect of a
+previous call to <tt>shutdown</tt> with a <em>time</em> argument that was not <tt>now</tt>).
+This cannot be used to interrupt a shutdown sequence that has already started. </li>
+</ul>
+
+<h2> Notes </h2>
+
+<ul>
+ <li> The <tt>s6-linux-init-shutdown</tt>
+binary is not meant to be called directly by administrators. Instead, after a
+<a href="s6-linux-init-maker.html">s6-linux-init-maker</a> invocation,
+the <tt>bin/</tt> subdirectory of the target will contain a <tt>shutdown</tt>
+symlink to <tt>s6-linux-init-shutdown</tt>. The <tt>bin/</tt> subdirectory
+should be copied by the administrator into <tt>/sbin</tt> for full
+interface compatibility with sysvinit. </li>
+ <li> The <tt>-f</tt> and <tt>-F</tt> options are only accepted for compatibility.
+LSB says they are used to advise the system to skip or enforce a <tt>fsck</tt>
+after rebooting. But they are only advisory, and for decades now systems have used
+other methods of evaluating whether they should perform filesystem checks, so these
+options are largely obsolete nowadays. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/s6-linux-init-shutdownd.html b/doc/s6-linux-init-shutdownd.html
new file mode 100644
index 0000000..8889a9e
--- /dev/null
+++ b/doc/s6-linux-init-shutdownd.html
@@ -0,0 +1,72 @@
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-linux-init: the s6-linux-init-shutdownd program</title>
+ <meta name="Description" content="s6-linux-init: the s6-linux-init-shutdownd program" />
+ <meta name="Keywords" content="s6 linux init administration root utilities shutdown halt poweroff reboot daemon shutdownd" />
+ <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-linux-init</a><br />
+<a href="//skarnet.org/software/">Software</a><br />
+<a href="//skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>s6-linux-init-shutdownd</tt> program </h1>
+
+<p>
+<tt>s6-linux-init-shutdownd</tt> is the daemon that manages the shutdown
+procedure for a s6-linux-init installation. It is not meant to be called
+directly by the user.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-linux-init-shutdownd [ -c <em>basedir</em> ] [ -g <em>gracetime</em> ] <em>fifo</em>
+</pre>
+
+<ul>
+ <li> <tt>s6-linux-init-shutdownd</tt> opens the <em>fifo</em> named pipe
+and listens to it. This is where programs such as
+<a href="s6-linux-init-shutdown.html">s6-linux-init-shutdown</a> send their
+commands when they are told to trigger the shutdown procedure.</li>
+ <li> When it receives a command to shut down, <tt>s6-linux-init-shutdownd</tt>
+first spawns the <em>rc.shutdown</em> script defined by the
+<a href="s6-linux-init-maker.html">s6-linux-init-maker</a> invocation that
+created the shutdownd service. </li>
+ <li> When this script exits, <tt>s6-linux-init-shutdownd</tt> kills all
+processes, first with a SIGTERM, then (after the grace time specified by
+the shutdown command) with a SIGKILL. </li>
+ <li> It then runs an automatically-generated script (called <em>stage 4</em>),
+which unmounts the file systems and halts, powers off or reboots the
+machine. </li>
+</ul>
+
+<h2> Options </h2>
+
+<ul>
+ <li> <tt>-c&nbsp;<em>basedir</em></tt>&nbsp;: look for the
+<em>rc.shutdown</em> script in the <em>basedir</em><tt>/scripts</tt>
+directory. Default is <tt>/etc/s6-linux-init/current</tt>. </li>
+ <li> <tt>-g&nbsp;<em>gracetime</em></tt>&nbsp;: if the shutdown command
+does not specify a grace time between the SIGTERM and the SIGKILL, use
+<em>gracetime</em> milliseconds. Default is 3000. </li>
+</ul>
+
+<h2> Notes </h2>
+
+<ul>
+ <li> The <a href="s6-linux-init-shutdownd.html">s6-linux-init-shutdownd</a>
+binary is not meant to be called directly by administrators. It is run
+by a <a href="//skarnet.org/software/s6/">s6</a> service that is automatically
+generated by
+<a href="s6-linux-init-maker.html">s6-linux-init-maker</a>. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/s6-linux-init-telinit.html b/doc/s6-linux-init-telinit.html
new file mode 100644
index 0000000..2bea4e5
--- /dev/null
+++ b/doc/s6-linux-init-telinit.html
@@ -0,0 +1,84 @@
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-linux-init: the s6-linux-init-telinit program</title>
+ <meta name="Description" content="s6-linux-init: the s6-linux-init-telinit program" />
+ <meta name="Keywords" content="s6 linux init administration root utilities telinit" />
+ <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-linux-init</a><br />
+<a href="//skarnet.org/software/">Software</a><br />
+<a href="//skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>s6-linux-init-telinit</tt> program </h1>
+
+<p>
+<tt>s6-linux-init-telinit</tt> changes runlevels, i.e. changes the
+system state. It is normally invoked as <tt>/sbin/telinit</tt> or
+just <tt>init</tt> with an argument.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-linux-init-telinit <tt>rl</tt>
+</pre>
+
+<ul>
+ <li> <tt>s6-linux-init-telinit</tt> may be called with the same options as
+<tt>s6-linux-init</tt>, but it ignores them all. </li>
+ <li> It calls the runleveld
+<a href="//skarnet.org/software/s6/localservice.html">local service</a> with
+the <em>rl</em> argument. This local service executes the user-provided
+<tt>runlevel</tt> script, which changes the system state to the state
+described by <em>rl</em>. </li>
+ <li> As a special case, if <em>rl</em> is <tt>0</tt> or <tt>6</tt>,
+<tt>s6-linux-init-telinit</tt> then executes into
+<a href="s6-linux-init-hpr.html">s6-linux-init-hpr</a> with the <tt>-p</tt>
+or <tt>-r</tt> option respectively,
+for compatibility with sysvinit's <em>0</em> and <em>6</em> runlevels
+that respectively halt and reboot the machine. </li>
+</ul>
+
+<h2> Exit codes </h2>
+
+<ul>
+ <li> 100: wrong usage </li>
+ <li> 111: system call failed </li>
+ <li> Else, <tt>s6-linux-init-telinit</tt> exits with the same exit code
+as the <em>runlevel</em> script called with the <em>rl</em> argument. </li>
+ <li> If <em>rl</em> is 0 or 6, in case of success
+<tt>s6-linux-init-telinit</tt> exits 0, but the system shuts down
+immediately as it returns. </li>
+</ul>
+
+<h2> Notes </h2>
+
+<ul>
+ <li> Traditional sysvinit only allows integer runlevels, from 0 to 6.
+More advanced service managers, like OpenRC or s6-rc, allow the admin to
+define alphanumerical runlevels, or <em>states</em>. <tt>s6-linux-init-telinit</tt>
+does not implement policy; it only makes sure the user-provided <tt>runlevel</tt>
+script is called with the <em>rl</em> argument, under a safe and reproducible
+environment. The <tt>runlevel</tt> script can then change the machine state
+as chosen by the user - typically by invoking the service manager. </li>
+ <li> The <tt>0</tt> and <tt>6</tt> special case has been added because
+some legacy programs may assume that calling <tt>init 0</tt> and <tt>init 6</tt>
+respectively halt and reboot the system. </li>
+ <li> The <tt>s6-linux-init-telinit</tt>
+binary is not meant to be called directly by administrators. Instead, after a
+<a href="s6-linux-init-maker.html">s6-linux-init-maker</a> invocation,
+the <tt>bin/</tt> subdirectory of the target will contain a <tt>telinit</tt>
+symlink to <tt>s6-linux-init-telinit</tt>. The <tt>bin/</tt> subdirectory
+should be copied by the administrator into <tt>/sbin</tt> for full
+interface compatibility with sysvinit. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/s6-linux-init-umountall.html b/doc/s6-linux-init-umountall.html
new file mode 100644
index 0000000..2d1bf99
--- /dev/null
+++ b/doc/s6-linux-init-umountall.html
@@ -0,0 +1,68 @@
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-linux-init: the s6-linux-init-umountall program</title>
+ <meta name="Description" content="s6-linux-init: the s6-linux-init-umountall program" />
+ <meta name="Keywords" content="s6 linux administration root linux utilities umount unmount filesystem" />
+ <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-linux-init</a><br />
+<a href="//skarnet.org/software/">Software</a><br />
+<a href="//skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>s6-linux-init-umountall</tt> program </h1>
+
+<p>
+ <tt>s6-linux-init-umountall</tt> unmounts all filesystems.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-linux-init-umountall
+</pre>
+
+<ul>
+ <li> <tt>s6-linux-init-umountall</tt> unmounts all filesystems according to <tt>/proc/mounts</tt>.
+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> <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>
+</ul>
+
+<h2> Exit codes </h2>
+
+<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>.
+</p>
+
+<h2> Notes </h2>
+
+<ul>
+ <li> <tt>s6-linux-init-umountall</tt> is automatically called at the very end of the shutdown procedure,
+in "stage 4", i.e. after a SIGKILL has been sent to all the processes on the system, and
+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
+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>
+</ul>
+
+</body>
+</html>
diff --git a/doc/s6-linux-init.html b/doc/s6-linux-init.html
new file mode 100644
index 0000000..f57adae
--- /dev/null
+++ b/doc/s6-linux-init.html
@@ -0,0 +1,164 @@
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-linux-init: the s6-linux-init program</title>
+ <meta name="Description" content="s6-linux-init: the s6-linux-init program" />
+ <meta name="Keywords" content="s6 linux init administration root init boot pid1 pid 1 /sbin/init" />
+ <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-linux-init</a><br />
+<a href="//skarnet.org/software/">Software</a><br />
+<a href="//skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>s6-linux-init</tt> program </h1>
+
+<p>
+<tt>s6-linux-init</tt> is a program that is meant to run as pid 1,
+as a <em>stage 1 init</em>: it performs the necessary early system preparation
+and execs into <a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a>.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+ s6-linux-init [ -c <em>basedir</em> ] [ -p <em>initial_path</em> ] [ -s <em>env_store</em> ] [ -m <em>umask</em> ] [ -d <em>dev_style</em> ] [ -D <em>initdefault</em> ] [ -n | -N ] [ <em>args...</em> ]
+</pre>
+
+<ul>
+ <li> If <tt>s6-linux-init</tt> isn't pid 1, it execs into
+<a href="s6-linux-init-telinit.html">s6-linux-init-telinit</a> with the
+same arguments. </li>
+ </li>
+ <li> Else, it performs some early preparation, spawns a process that
+will run the <em>rc.init</em> script, then execs into
+<a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a>.
+</ul>
+
+<h2> Options </h2>
+
+<p>
+ These options should exactly mirror the options with the same name that
+have been given to <a href="s6-linux-init-maker.html">s6-linux-init-maker</a>.
+If there is a discrepancy, the system might not boot.
+</p>
+
+<ul>
+ <li> <tt>-c</tt>&nbsp;<em>basedir</em>&nbsp;: read all its initialization
+data from <em>basedir</em>. If the data has not indeed been copied to
+<em>basedir</em>, <strong>the system will not boot</strong>. </li>
+ <li> <tt>-p</tt>&nbsp;<em>initial_path</em>&nbsp;: the initial value for
+the PATH environment variable. </li>
+ <li> <tt>-s</tt>&nbsp;<em>env_store</em>&nbsp;: the place where to dump
+kernel environment variables. </li>
+ <li> <tt>-m</tt>&nbsp;<em>initial_umask</em>&nbsp;: the initial file umask. </li>
+ <li> <tt>-d</tt>&nbsp;<em>slashdev</em>&nbsp;: mount a devtmpfs on
+<em>slashdev</em>. By default, no such mount is performed - it is assumed
+that a devtmpfs is automounted on <tt>/dev</tt> at boot time by the kernel. </li>
+ <li> <tt>-D</tt>&nbsp;<em>initdefault</em>&nbsp;: the initial runlevel
+to boot to, if it isn't overridden by the kernel command line.
+This is only given as a first argument to <em>rc.init</em>.
+Default is <tt>default</tt>. </li>
+ <li> <tt>-n</tt>&nbsp;: instead of unmounting <tt>/run</tt> and mounting
+a tmpfs on it, just remount <tt>/run</tt>. </li>
+ <li> <tt>-N</tt>&nbsp;: do not touch <tt>/run</tt> at all. </li>
+</ul>
+
+<h2> Early preparation </h2>
+
+<p>
+ When booting a system, <tt>s6-linux-init</tt> performs the following
+operations:
+</p>
+
+<ul>
+ <li> It prints a banner to <tt>/dev/console</tt>. </li>
+ <li> It chdirs to <tt>/</tt>. </li>
+ <li> It sets the umask to <em>initial_umask</em>. </li>
+ <li> It becomes a session leader. </li>
+ <li> It mounts a devtmpfs on <em>slashdev</em>, if requested. </li>
+ <li> It uses <tt>/dev/null</tt> as its stdin (instead of <tt>/dev/console</tt>).
+<tt>/dev/console</tt> is still used, for now, as stdout and stderr. </li>
+ <li> It unmounts <tt>/run</tt> (or the directory you have given to the
+<tt>--tmpfsdir</tt> configure option at package build time), just in case;
+then it creates a tmpfs on it. Alternatively, it remounts <tt>/run</tt>,
+or does not touch it at all. </li>
+ <li> It copies the whole <tt><em>basedir</em>/run-image</tt> hierarchy to
+<tt>/run</tt> (or your chosen tmpfsdir). </li>
+ <li> It reads the initial environment from <tt><em>basedir</em>/env</tt>. </li>
+ <li> If required, it stores the kernel environment into <em>env_store</em>. </li>
+ <li> It performs "the fifo trick", i.e. it redirects its stdout to the
+catch-all logger's fifo, without blocking, before the catch-all
+logger is even up (because it's a service that will be spawned a bit
+later, when <a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a>
+is executed). </li>
+ <li> It forks a child.
+ <ul>
+ <li> The child scans the kernel command line to find a suitable runlevel
+(<tt>default</tt>, <tt>2</tt>, <tt>3</tt>, <tt>4</tt>, or <tt>5</tt>). If
+it doesn't find any kernel command line argument that defines a runlevel,
+it uses <em>initdefault</em>. </li>
+ <li> The child becomes a session leader. </li>
+ <li> The child blocks until the catch-all logger runs. </li>
+ </ul> </li>
+ <li> It also makes the catch-all logger's fifo its stderr. </li>
+ <li> It execs into <a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a>
+with <tt>/run/service</tt> as its scandir (or <em>tmpfsdir</em>/service). </li>
+ <ul>
+ <li> <a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a>
+spawns the early services that are defined in
+<tt><em>basedir</em>/run-image/service</tt>, and have been copied into
+<tt>/run/service</tt> (or <em>tmpfsdir</em>/service). </li>
+ <li> One of those early services is <tt>s6-svscan-log</tt>, which is
+the catch-all logger. When this service is up, <tt>s6-linux-init</tt>'s
+child unblocks. </li>
+ <li> The child execs into <tt><em>basedir</em>/scripts/rc.init</tt>.
+The first argument to <em>rc.init</em> is the chosen runlevel. The kernel
+command line, as given by the kernel to <tt>s6-linux-init</tt> (i.e. without
+the <tt>key=value</tt> arguments, which were passed into <tt>s6-linux-init</tt>'s
+environment and were stored into <em>env_store</em>), makes for the rest of
+the arguments given to <em>rc.init</em>. </li>
+ </ul> </li>
+</ul>
+
+<p>
+ By the time <em>rc.init</em> runs,
+<a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a> is running
+as pid 1 and has spawned its early services - at least the catch-all logger,
+and the other services, including the early getty if it has been defined, are
+started in parallel and will be ready instantly. <em>rc.init</em> can then
+perform <em>stage 2</em> of the initialization process, i.e. the handoff to
+the service manager.
+</p>
+
+<h2> Exit codes </h2>
+
+<p>
+ <tt>s6-linux-init</tt> never exits. It spawns the <em>rc.init</em> script
+and execs into <a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a>,
+which runs forever until the machine stops or reboots.
+</p>
+
+<h2> Notes </h2>
+
+<ul>
+ <li> The <tt>s6-linux-init</tt>
+binary is not meant to be called directly, or be linked to <tt>/sbin/init</tt>
+directly, because it takes command-line options.
+ Instead, after a
+<a href="s6-linux-init-maker.html">s6-linux-init-maker</a> invocation,
+the <tt>bin/</tt> subdirectory of the target will contain a script called
+<tt>init</tt>, which execs into <tt>s6-linux-init</tt> with the appropriate
+command-line options, and <em>is</em> suitable as a <tt>/sbin/init</tt> program.
+The <tt>bin/</tt> subdirectory
+should be copied by the administrator into <tt>/sbin</tt> for full
+interface compatibility with sysvinit. </li>
+</ul>
+
+</body>
+</html>
diff --git a/doc/s6-poweroff.html b/doc/s6-poweroff.html
deleted file mode 100644
index e215c38..0000000
--- a/doc/s6-poweroff.html
+++ /dev/null
@@ -1,55 +0,0 @@
-<html>
- <head>
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
- <meta http-equiv="Content-Language" content="en" />
- <title>s6-linux-init: the s6-poweroff program</title>
- <meta name="Description" content="s6-linux-init: the s6-poweroff program" />
- <meta name="Keywords" content="s6 linux init administration root utilities shutdown halt poweroff reboot" />
- <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
- </head>
-<body>
-
-<p>
-<a href="index.html">s6-linux-init</a><br />
-<a href="//skarnet.org/software/">Software</a><br />
-<a href="//skarnet.org/">skarnet.org</a>
-</p>
-
-<h1> The <tt>s6-poweroff</tt> program </h1>
-
-<p>
-<tt>s6-poweroff</tt> sends a signal to process 1 in order to power off the machine;
-or, with the <tt>-f</tt> option, it performs an immediate hard shutdown.
-</p>
-
-<h2> Interface </h2>
-
-<pre>
- s6-poweroff [ -h | -p | -r ] [ -f ]
-</pre>
-
-<ul>
- <li> s6-poweroff sends a signal to process 1. </li>
- <li> It then exits 0. </li>
-</ul>
-
-<h2> Options </h2>
-
-<ul>
- <li> <tt>-h</tt>&nbsp;: halt. The command will order a halt (i.e. the system will
-be shut down, but the power will remain up), which means
-sending a SIGUSR2 to process 1. </li>
- <li> <tt>-p</tt>&nbsp;: poweroff. The command will order a power off, which means
-sending a SIGUSR1 to process 1. This is the default. </li>
- <li> <tt>-r</tt>&nbsp;: reboot. The command will order a reboot, which means
-sending a SIGINT to process 1. </li>
- <li> <tt>-f</tt>&nbsp;: force. The command will not send any signal to process 1;
-it will just sync the filesystems then tell the kernel to halt, poweroff or reboot.
-<tt>s6-reboot -f</tt> or <tt>s6-poweroff -f</tt> should be the last program
-executed in the lifetime of a machine, at the end of the shutdown script called
-by process 1 when it receives a signal telling it to shut down. </li>
-</ul>
-
-</body>
-</html>
diff --git a/doc/s6-reboot.html b/doc/s6-reboot.html
deleted file mode 100644
index 16043c5..0000000
--- a/doc/s6-reboot.html
+++ /dev/null
@@ -1,55 +0,0 @@
-<html>
- <head>
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
- <meta http-equiv="Content-Language" content="en" />
- <title>s6-linux-init: the s6-reboot program</title>
- <meta name="Description" content="s6-linux-init: the s6-reboot program" />
- <meta name="Keywords" content="s6 linux init administration root utilities shutdown halt poweroff reboot" />
- <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
- </head>
-<body>
-
-<p>
-<a href="index.html">s6-linux-init</a><br />
-<a href="//skarnet.org/software/">Software</a><br />
-<a href="//skarnet.org/">skarnet.org</a>
-</p>
-
-<h1> The <tt>s6-reboot</tt> program </h1>
-
-<p>
-<tt>s6-reboot</tt> sends a signal to process 1 in order to reboot the machine;
-or, with the <tt>-f</tt> option, it performs an immediate reboot.
-</p>
-
-<h2> Interface </h2>
-
-<pre>
- s6-reboot [ -h | -p | -r ] [ -f ]
-</pre>
-
-<ul>
- <li> s6-reboot sends a signal to process 1. </li>
- <li> It then exits 0. </li>
-</ul>
-
-<h2> Options </h2>
-
-<ul>
- <li> <tt>-h</tt>&nbsp;: halt. The command will order a halt (i.e. the system will
-be shut down, but the power will remain up), which means
-sending a SIGUSR2 to process 1. </li>
- <li> <tt>-p</tt>&nbsp;: poweroff. The command will order a power off, which means
-sending a SIGUSR1 to process 1. </li>
- <li> <tt>-r</tt>&nbsp;: reboot. The command will order a reboot, which means
-sending a SIGINT to process 1. This is the default. </li>
- <li> <tt>-f</tt>&nbsp;: force. The command will not send any signal to process 1;
-it will just sync the filesystems then tell the kernel to halt, poweroff or reboot.
-<tt>s6-reboot -f</tt> or <tt>s6-poweroff -f</tt> should be the last program
-executed in the lifetime of a machine, at the end of the shutdown script called
-by process 1 when it receives a signal telling it to shut down. </li>
-</ul>
-
-</body>
-</html>
diff --git a/doc/upgrade.html b/doc/upgrade.html
index f8ea901..02aaeea 100644
--- a/doc/upgrade.html
+++ b/doc/upgrade.html
@@ -18,6 +18,15 @@
<h1> What has changed in s6-linux-init </h1>
+
+<h2> in 1.0.0.0 </h2>
+
+<ul>
+ <li> This is a complete rewrite and redesign of s6-linux-init: the
+<em>lifetime</em> version number has increased. No compatibility
+whatsoever is retained with previous versions. </li>
+</ul>
+
<h2> in 0.4.0.1 </h2>
<ul>
@@ -48,6 +57,9 @@ anymore (stage 3 was previously the user-written <tt>/etc/rc.shutdown</tt>
script). </li>
<li> The default user-provided "end of stage 2, bring down services" script
was named <tt>/etc/rc.tini</tt> before; now it's named <tt>/etc/rc.shutdown</tt>. </li>
+ <li> Everything now builds as PIC by default no matter
+the toolchain's settings. Use the <tt>--disable-all-pic</tt> configure
+option to build executables and static libraries as non-PIC. </li>
</ul>
<h2> in 0.3.1.1 </h2>
diff --git a/doc/why.html b/doc/why.html
new file mode 100644
index 0000000..8898cbd
--- /dev/null
+++ b/doc/why.html
@@ -0,0 +1,139 @@
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>s6-linux-init: why?</title>
+ <meta name="Description" content="s6-linux-init: why?" />
+ <meta name="Keywords" content="s6-linux-init why rationale s6 software stack init pid 1" />
+ <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">s6-linux-init</a><br />
+<a href="//skarnet.org/software/">Software</a><br />
+<a href="//skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> Why s6-linux-init&nbsp;? </h1>
+
+<h2> The s6 software stack </h2>
+
+<p>
+ The s6 ecosystem is made of several parts, which are mainly the following:
+</p>
+
+<ul>
+ <li> <a href="//skarnet.org/software/skalibs/">skalibs</a>: a C system
+programming library that is used in all skarnet.org software. </li>
+ <li> <a href="//skarnet.org/software/execline/">execline</a>: a small
+scripting language that is mainly used in various parts of the s6
+ecosystem because:
+ <ul>
+ <li> It is very quick to launch, and efficient with small scripts, so it
+is a good choice for s6 run scripts. </li>
+ <li> It is much easier to programmatically generate execline scripts than
+shell scripts. execline allows programs such as
+<a href="s6-linux-init-maker.html">s6-linux-init-maker</a> to generate scripts
+quite easily, whereas using the shell syntax would require them to understand
+the full subleties of shell quoting. </li>
+ </ul>
+ <li> <a href="//skarnet.org/software/s6/">s6</a>, the main dish: a process
+supervision suite. </li>
+ <li> <a href="//skarnet.org/software/s6-rc/">s6-rc</a>: a service manager
+for s6. </li>
+ <li> and <a href="//skarnet.org/software/s6-linux-init/">s6-linux-init</a>: this
+package. </li>
+</ul>
+
+<h2> Providing a complete init system </h2>
+
+<p>
+ As explained in
+<a href="https://archive.fosdem.org/2017/schedule/event/s6_supervision/">this
+presentation</a>, an init system is made of four parts:
+</p>
+
+<ol>
+ <li> <tt>/sbin/init</tt>: the first userspace program that is run by the
+kernel at boot time (not counting an initramfs). </li>
+ <li> <em>pid 1</em>: the program that will run as process 1 for most of
+the lifetime of the machine. This is not necessarily the same executable
+as <tt>/sbin/init</tt>, because <tt>/sbin/init</tt> can exec into something
+else. </li>
+ <li> a <em>process supervisor</em>. </li>
+ <li> a <em>service manager</em>. </li>
+</ol>
+
+<p>
+ The <a href="//skarnet.org/software/s6/">s6</a> package obviously provides
+part 3. It also provides part 2, because
+<a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a> is suitable
+as being pid 1 after some small setup is performed.
+</p>
+
+<p>
+ Part 4, service management, can be provided in a variety of ways. The
+<a href="//skarnet.org/software/s6-rc/">s6-rc</a> service manager is the
+natural complement to the s6 process supervisor, but it is not the only
+possibility. The
+<a href="https://jjacky.com/anopa/">anopa</a> package also provides a
+service manager designed to work with s6. And, at the expense of
+tight integration with the supervisor, it is possible to run a "traditional"
+service manager, such as sysv-rc or OpenRC, with an s6-based init system.
+This flexibility is possible because service management is one layer above
+the mechanisms of init and process supervision.
+</p>
+
+<p>
+ Remains part 1. And that's where s6-linux-init enters the picture.
+</p>
+
+<h3> Portability </h3>
+
+<p>
+ Part 1 of an init system, the <tt>/sbin/init</tt> program, has been purposefully
+omitted from the main s6 package, for a simple reason: s6 aims to be portable
+to any flavor of Unix, and <em>it is impossible to implement <tt>/sbin/init</tt>
+in a portable way</em>.
+</p>
+
+<p>
+ For instance, to do its job,
+<a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a> needs
+a writable directory. Such a directory may not be available at boot time,
+before mounting filesystems, because the root filesystem may be read-only.
+So, at least one writable filesystem (typically a RAM-backed one) must be
+mounted before s6-svscan can be executed and be pid 1. And mounting a
+filesystem is a non-portable operation.
+</p>
+
+<h3> Complexity </h3>
+
+<p>
+ Moreover, the sequence of operations that a <tt>/sbin/init</tt> program
+needs to perform before executing into <tt>s6-svscan</tt> is a bit
+tricky. It can be scripted, but it's not easy, and since it's so early
+in the lifetime of the machine, there's no safety net at all (the
+supervision tree itself, and the early getty, are supposed to be the
+safety net, and they're not there yet). So it's better to automate
+these operations.
+</p>
+
+<h2> Conclusion </h2>
+
+<p>
+ <tt>s6-linux-init</tt> aims to provide a fully functional <tt>/sbin/init</tt>
+program that executes into an s6 supervision tree with all the necessary
+support services already in place, as well as the corresponding shutdown
+commands. It also aims to be flexible enough to accommodate various needs
+and be compatible with any user-chosen service manager.
+</p>
+
+<p>
+As usual, it is about <em>mechanism</em>, not <em>policy</em>.
+</p>
+
+</body>
+</html>
diff --git a/examples/rc.init b/examples/rc.init
deleted file mode 100755
index afc7735..0000000
--- a/examples/rc.init
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/execlineb -P
-
-# Set the system clock to something sensible,
-# to get coherent logs until the initial NTP exchange
-
-if { s6-clock @400000005a58000000000000 }
-
-
-# Initialize the service manager
-
-if { s6-rc-init /run/service }
-
-
-# Run the service manager.
-# Call it twice because the first time may spuriously "timeout"
-# due to the clock jumping forward.
-
-if -nt { s6-rc -t 600000 -- change ok-all }
-s6-rc -t 600000 -- change ok-all
diff --git a/examples/rc.shutdown b/examples/rc.shutdown
deleted file mode 100755
index 14b4692..0000000
--- a/examples/rc.shutdown
+++ /dev/null
@@ -1,16 +0,0 @@
-#!/bin/execlineb -P
-
-
-# Write a message to /var/log/syslogd
-
-foreground
-{
- s6-ipcclient -l0 /dev/log
- fdmove 1 7
- s6-echo "/etc/rc.shutdown: shutdown requested"
-}
-
-
-# Shut down all services
-
-s6-rc -da change
diff --git a/package/deps-build b/package/deps-build
index 05d5af4..f622849 100644
--- a/package/deps-build
+++ b/package/deps-build
@@ -1 +1,5 @@
/package/prog/skalibs
+/package/admin/nsss $usensss
+/package/admin/utmps $utmps
+/package/admin/execline
+/package/admin/s6
diff --git a/package/deps.mak b/package/deps.mak
index 95fbd4b..486a8af 100644
--- a/package/deps.mak
+++ b/package/deps.mak
@@ -2,17 +2,49 @@
# This file has been generated by tools/gen-deps.sh
#
-src/init/hpr.o src/init/hpr.lo: src/init/hpr.c
-src/init/s6-halt.o src/init/s6-halt.lo: src/init/s6-halt.c
-src/init/s6-linux-init-maker.o src/init/s6-linux-init-maker.lo: src/init/s6-linux-init-maker.c
-src/init/s6-poweroff.o src/init/s6-poweroff.lo: src/init/s6-poweroff.c
-src/init/s6-reboot.o src/init/s6-reboot.lo: src/init/s6-reboot.c
+src/include-local/initctl.h: src/include/s6-linux-init/config.h
+src/shutdown/hpr.h: src/include-local/initctl.h
+src/init/s6-linux-init-maker.o src/init/s6-linux-init-maker.lo: src/init/s6-linux-init-maker.c src/include-local/defaults.h src/include-local/initctl.h src/include/s6-linux-init/config.h
+src/init/s6-linux-init-telinit.o src/init/s6-linux-init-telinit.lo: src/init/s6-linux-init-telinit.c src/include-local/initctl.h src/include/s6-linux-init/config.h
+src/init/s6-linux-init.o src/init/s6-linux-init.lo: src/init/s6-linux-init.c src/include-local/defaults.h src/include-local/initctl.h src/include/s6-linux-init/config.h
+src/lib/s6_linux_init_logouthook.o src/lib/s6_linux_init_logouthook.lo: src/lib/s6_linux_init_logouthook.c src/include-local/initctl.h src/include/s6-linux-init/s6-linux-init.h
+src/misc/s6-linux-init-echo.o src/misc/s6-linux-init-echo.lo: src/misc/s6-linux-init-echo.c
+src/misc/s6-linux-init-logouthookd.o src/misc/s6-linux-init-logouthookd.lo: src/misc/s6-linux-init-logouthookd.c
+src/misc/s6-linux-init-umountall.o src/misc/s6-linux-init-umountall.lo: src/misc/s6-linux-init-umountall.c
+src/shutdown/hpr_shutdown.o src/shutdown/hpr_shutdown.lo: src/shutdown/hpr_shutdown.c src/shutdown/hpr.h
+src/shutdown/hpr_wall.o src/shutdown/hpr_wall.lo: src/shutdown/hpr_wall.c src/shutdown/hpr.h
+src/shutdown/s6-linux-init-hpr.o src/shutdown/s6-linux-init-hpr.lo: src/shutdown/s6-linux-init-hpr.c src/include-local/defaults.h src/shutdown/hpr.h
+src/shutdown/s6-linux-init-shutdown.o src/shutdown/s6-linux-init-shutdown.lo: src/shutdown/s6-linux-init-shutdown.c src/include-local/defaults.h src/shutdown/hpr.h src/include-local/initctl.h
+src/shutdown/s6-linux-init-shutdownd.o src/shutdown/s6-linux-init-shutdownd.lo: src/shutdown/s6-linux-init-shutdownd.c src/include-local/defaults.h src/shutdown/hpr.h src/include-local/initctl.h src/include/s6-linux-init/config.h
-s6-halt: EXTRA_LIBS :=
-s6-halt: src/init/s6-halt.o -lskarnet
-s6-linux-init-maker: EXTRA_LIBS :=
-s6-linux-init-maker: src/init/s6-linux-init-maker.o -lskarnet
-s6-poweroff: EXTRA_LIBS :=
-s6-poweroff: src/init/s6-poweroff.o -lskarnet
-s6-reboot: EXTRA_LIBS :=
-s6-reboot: src/init/s6-reboot.o -lskarnet
+s6-linux-init: EXTRA_LIBS :=
+s6-linux-init: src/init/s6-linux-init.o -lskarnet
+s6-linux-init-maker: EXTRA_LIBS := ${MAYBEPTHREAD_LIB}
+s6-linux-init-maker: src/init/s6-linux-init-maker.o ${LIBNSSS} -lskarnet
+s6-linux-init-telinit: EXTRA_LIBS :=
+s6-linux-init-telinit: src/init/s6-linux-init-telinit.o -lskarnet
+ifeq ($(strip $(STATIC_LIBS_ARE_PIC)),)
+libs6_linux_init.a.xyzzy: src/lib/s6_linux_init_logouthook.o
+else
+libs6_linux_init.a.xyzzy: src/lib/s6_linux_init_logouthook.lo
+endif
+libs6_linux_init.so.xyzzy: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${TAINNOW_LIB}
+libs6_linux_init.so.xyzzy: src/lib/s6_linux_init_logouthook.lo
+s6-linux-init-echo: EXTRA_LIBS :=
+s6-linux-init-echo: src/misc/s6-linux-init-echo.o -lskarnet
+s6-linux-init-logouthookd: EXTRA_LIBS := ${TAINNOW_LIB} ${SOCKET_LIB}
+s6-linux-init-logouthookd: src/misc/s6-linux-init-logouthookd.o ${LIBUTMPS} -lskarnet
+s6-linux-init-umountall: EXTRA_LIBS :=
+s6-linux-init-umountall: src/misc/s6-linux-init-umountall.o -lskarnet
+ifeq ($(strip $(STATIC_LIBS_ARE_PIC)),)
+libhpr.a.xyzzy: src/shutdown/hpr_shutdown.o src/shutdown/hpr_wall.o
+else
+libhpr.a.xyzzy: src/shutdown/hpr_shutdown.lo src/shutdown/hpr_wall.lo
+endif
+s6-linux-init-hpr: EXTRA_LIBS := ${TAINNOW_LIB} ${SOCKET_LIB}
+s6-linux-init-hpr: src/shutdown/s6-linux-init-hpr.o libhpr.a.xyzzy ${LIBUTMPS} -lskarnet
+s6-linux-init-shutdown: EXTRA_LIBS := ${TAINNOW_LIB} ${SOCKET_LIB}
+s6-linux-init-shutdown: src/shutdown/s6-linux-init-shutdown.o libhpr.a.xyzzy ${LIBUTMPS} -lskarnet
+s6-linux-init-shutdownd: EXTRA_LIBS := ${TAINNOW_LIB} ${SOCKET_LIB}
+s6-linux-init-shutdownd: src/shutdown/s6-linux-init-shutdownd.o -ls6 ${LIBUTMPS} -lskarnet
+INTERNAL_LIBS := libhpr.a.xyzzy
diff --git a/package/info b/package/info
index 8a7b49e..d58a5f1 100644
--- a/package/info
+++ b/package/info
@@ -1,4 +1,4 @@
package=s6-linux-init
-version=0.4.0.1
+version=1.0.0.0
category=admin
package_macro_name=S6_LINUX_INIT
diff --git a/package/modes b/package/modes
index f2fafad..ab52a84 100644
--- a/package/modes
+++ b/package/modes
@@ -1,4 +1,9 @@
-s6-linux-init-maker 0700
-s6-halt 0700
-s6-poweroff 0700
-s6-reboot 0700
+s6-linux-init 0700
+s6-linux-init-hpr 0750
+s6-linux-init-shutdown 0755
+s6-linux-init-shutdownd 0700
+s6-linux-init-telinit 0750
+s6-linux-init-logouthookd 0700
+s6-linux-init-echo 0755
+s6-linux-init-umountall 0750
+s6-linux-init-maker 0700
diff --git a/package/targets.mak b/package/targets.mak
index 26ef489..412e9e3 100644
--- a/package/targets.mak
+++ b/package/targets.mak
@@ -1,7 +1,23 @@
BIN_TARGETS := \
-s6-linux-init-maker \
-s6-halt \
-s6-poweroff \
-s6-reboot
+s6-linux-init \
+s6-linux-init-hpr \
+s6-linux-init-shutdown \
+s6-linux-init-shutdownd \
+s6-linux-init-telinit \
+s6-linux-init-logouthookd \
+s6-linux-init-echo \
+s6-linux-init-umountall \
+s6-linux-init-maker
-LIBEXEC_TARGETS :=
+LIB_DEFS := S6_LINUX_INIT=s6_linux_init
+
+SKELFILES := $(wildcard skel/*)
+
+$(DESTDIR)$(skeldir)/%: skel/%
+ ./tools/install.sh -D -m 0755 $< $@
+
+install-skel: $(SKELFILES:skel/%=$(DESTDIR)$(skeldir)/%)
+
+.PHONY: install-skel
+
+install: install-skel
diff --git a/skel/rc.init b/skel/rc.init
new file mode 100755
index 0000000..37c3ea9
--- /dev/null
+++ b/skel/rc.init
@@ -0,0 +1,34 @@
+#!/bin/sh -e
+
+rl="$1"
+shift
+
+### argv now contains the arguments of the kernel command line that are
+### not of the form key=value. (The key=value arguments were stored by
+### s6-linux-init into an envdir, if instructed so via the -s option.)
+### Normally this argv remains unused because programs that need the
+### kernel command line usually read it later on from /proc/cmdline -
+### but just in case, it's available here.
+
+
+### 1. Early preparation
+### This is done only once at boot time.
+### Ideally, this phase should just initialize the service manager.
+
+### If your services are managed by sysv-rc:
+# /etc/init.d/rcS
+
+### If your services are managed by OpenRC:
+# /sbin/openrc sysinit
+# /sbin/openrc boot
+
+### If your services are managed by s6-rc:
+### (replace /run/service with your scandir)
+# s6-rc-init /run/service
+
+
+### 2. Starting the wanted set of services
+### This is also called every time you change runlevels with telinit.
+### (edit the location to suit your installation)
+
+# exec /etc/s6-linux-init/current/scripts/runlevel "$rl"
diff --git a/skel/rc.shutdown b/skel/rc.shutdown
new file mode 100755
index 0000000..d72916e
--- /dev/null
+++ b/skel/rc.shutdown
@@ -0,0 +1,17 @@
+#!/bin/sh -e
+
+### Things to do before hardware halt/reboot/poweroff.
+### Ideally, it should be a single call to the service manager,
+### telling it to bring all the services down.
+
+### If your services are managed by sysv-rc:
+### also remove the K11reboot link from /etc/rc6.d to prevent
+### sysv-rc from rebooting prematurely - because sysvinit does
+### not properly separate state changes from system init/shutdown.
+# exec /etc/init.d/rc 6
+
+### If your services are managed by OpenRC:
+# exec /sbin/openrc shutdown
+
+### If your services are managed by s6-rc:
+# exec s6-rc -bda change
diff --git a/skel/runlevel b/skel/runlevel
new file mode 100755
index 0000000..6368b4b
--- /dev/null
+++ b/skel/runlevel
@@ -0,0 +1,15 @@
+#!/bin/sh -e
+
+### This script is called once at boot time by rc.init, and is
+### also called by the runleveld service every time the user
+### requests a machine state change via telinit.
+### Ideally, it should just be a call to the service manager.
+
+### If your services are managed by sysv-rc:
+# exec /etc/init.d/rc "$1"
+
+### If your services are managed by OpenRC:
+# exec /sbin/openrc "$1"
+
+### If your services are managed by s6-rc:
+# exec s6-rc -v2 change "$1"
diff --git a/src/include-local/defaults.h b/src/include-local/defaults.h
new file mode 100644
index 0000000..a28e2e5
--- /dev/null
+++ b/src/include-local/defaults.h
@@ -0,0 +1,9 @@
+/* ISC license. */
+
+#ifndef S6_LINUX_INIT_DEFAULTS_H
+#define S6_LINUX_INIT_DEFAULTS_H
+
+#define BASEDIR "/etc/s6-linux-init/current"
+#define INITPATH "/usr/bin:/bin"
+
+#endif
diff --git a/src/include-local/initctl.h b/src/include-local/initctl.h
new file mode 100644
index 0000000..507aefe
--- /dev/null
+++ b/src/include-local/initctl.h
@@ -0,0 +1,34 @@
+/* ISC license. */
+
+#ifndef S6_LINUX_INIT_INITCTL_H
+#define S6_LINUX_INIT_INITCTL_H
+
+#include <s6-linux-init/config.h>
+
+#define SCANDIR "service"
+
+#define SHUTDOWND_SERVICEDIR "s6-linux-init-shutdownd"
+#define SHUTDOWND_FIFO "fifo"
+#define INITCTL S6_LINUX_INIT_TMPFS "/" SCANDIR "/" SHUTDOWND_SERVICEDIR "/" SHUTDOWND_FIFO
+
+#define RUNLEVELD_SERVICEDIR "s6-linux-init-runleveld"
+#define RUNLEVELD_SOCKET "s"
+#define RUNLEVELD_PATH S6_LINUX_INIT_TMPFS "/" SCANDIR "/" RUNLEVELD_SERVICEDIR "/" RUNLEVELD_SOCKET
+
+#define LOGOUTHOOKD_SERVICEDIR "s6-linux-init-logouthookd"
+#define LOGOUTHOOKD_SOCKET "s"
+#define LOGOUTHOOKD_PATH S6_LINUX_INIT_TMPFS "/" SCANDIR "/" LOGOUTHOOKD_SERVICEDIR "/" LOGOUTHOOKD_SOCKET
+
+#define LOGGER_SERVICEDIR "s6-svscan-log"
+#define LOGGER_FIFO "fifo"
+#define LOGFIFO S6_LINUX_INIT_TMPFS "/" SCANDIR "/" LOGGER_SERVICEDIR "/" LOGGER_FIFO
+
+#define EARLYGETTY_SERVICEDIR "s6-linux-init-early-getty"
+#define EARLYGETTY S6_LINUX_INIT_TMPFS "/" SCANDIR "/" EARLYGETTY_SERVICEDIR
+
+#define RUNIMAGE "run-image"
+#define ENVSTAGE1 "env"
+#define STAGE2 "rc.init"
+#define STAGE3 "rc.shutdown"
+
+#endif
diff --git a/src/include/s6-linux-init/s6-linux-init.h b/src/include/s6-linux-init/s6-linux-init.h
new file mode 100644
index 0000000..ed8e684
--- /dev/null
+++ b/src/include/s6-linux-init/s6-linux-init.h
@@ -0,0 +1,8 @@
+/* ISC license. */
+
+#ifndef S6_LINUX_INIT_H
+#define S6_LINUX_INIT_H
+
+extern int s6_linux_init_logouthook (void) ;
+
+#endif
diff --git a/src/init/deps-exe/s6-halt b/src/init/deps-exe/s6-linux-init
index e7187fe..e7187fe 100644
--- a/src/init/deps-exe/s6-halt
+++ b/src/init/deps-exe/s6-linux-init
diff --git a/src/init/deps-exe/s6-linux-init-maker b/src/init/deps-exe/s6-linux-init-maker
index e7187fe..ee4ed0b 100644
--- a/src/init/deps-exe/s6-linux-init-maker
+++ b/src/init/deps-exe/s6-linux-init-maker
@@ -1 +1,3 @@
+${LIBNSSS}
-lskarnet
+${MAYBEPTHREAD_LIB}
diff --git a/src/init/deps-exe/s6-poweroff b/src/init/deps-exe/s6-linux-init-telinit
index e7187fe..e7187fe 100644
--- a/src/init/deps-exe/s6-poweroff
+++ b/src/init/deps-exe/s6-linux-init-telinit
diff --git a/src/init/hpr.c b/src/init/hpr.c
deleted file mode 100644
index db80ba2..0000000
--- a/src/init/hpr.c
+++ /dev/null
@@ -1,107 +0,0 @@
-/* ISC license. */
-
-#include <skalibs/sysdeps.h>
-#include <skalibs/nonposix.h>
-#include <unistd.h>
-#include <signal.h>
-#include <errno.h>
-#include <sys/reboot.h>
-#include <skalibs/strerr2.h>
-#include <skalibs/sgetopt.h>
-#include <skalibs/sig.h>
-#include <skalibs/djbunix.h>
-
-#define USAGE PROGNAME " [ -h | -p | -r ] [ -f ]"
-
-#ifdef SKALIBS_HASNSGETPARENT
-
-#include <sys/ioctl.h>
-#include <linux/nsfs.h>
-
-static int test_in_namespace (void)
-{
- int r ;
- int fd = open_read("/proc/1/ns/pid") ;
- if (fd < 0) return 0 ;
- r = ioctl(fd, NS_GET_PARENT) ;
- close(fd) ;
- return r >= 0 ;
-}
-
-#else
-
- /*
- When in doubt, always trap signals. This incurs a small race:
- if ctrl-alt-del is pressed at the wrong time, the process will
- exit and cause a kernel panic. But the alternatives are WAY
- more hackish than this.
- */
-
-static int test_in_namespace (void)
-{
- return 1 ;
-}
-
-#endif
-
-static void sigint_handler (int sig)
-{
- (void)sig ;
- _exit(1) ;
-}
-
-static void sighup_handler (int sig)
-{
- (void)sig ;
- _exit(0) ;
-}
-
-int main (int argc, char const *const *argv)
-{
- int what = WHATDEFAULT ;
- int force = 0 ;
- PROG = PROGNAME ;
-
- {
- subgetopt_t l = SUBGETOPT_ZERO ;
- for (;;)
- {
- int opt = subgetopt_r(argc, argv, "hprf", &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 ;
- default : strerr_dieusage(100, USAGE) ;
- }
- }
- argc -= l.ind ; argv += l.ind ;
- }
-
- if (geteuid())
- {
- errno = EPERM ;
- strerr_dief1sys(100, "nice try, peon") ;
- }
-
- if (force)
- {
- sync() ;
- if (getpid() == 1)
- {
- if (test_in_namespace())
- {
- if (sig_catch(SIGINT, &sigint_handler) < 0
- || sig_catch(SIGHUP, &sighup_handler) < 0)
- strerr_diefu1sys(111, "catch signals") ;
- }
- }
- reboot(what == 3 ? RB_AUTOBOOT : what == 2 ? RB_POWER_OFF : RB_HALT_SYSTEM) ;
- strerr_diefu1sys(111, "reboot()") ;
- }
- else if (kill(1, what == 3 ? SIGINT : what == 2 ? SIGUSR1 : SIGUSR2) < 0)
- strerr_diefu1sys(111, "signal process 1") ;
- return 0 ;
-}
diff --git a/src/init/s6-halt.c b/src/init/s6-halt.c
deleted file mode 100644
index d758030..0000000
--- a/src/init/s6-halt.c
+++ /dev/null
@@ -1,7 +0,0 @@
-/* ISC license. */
-
-#undef PROGNAME
-#define PROGNAME "s6-halt"
-#undef WHATDEFAULT
-#define WHATDEFAULT 1
-#include "hpr.c"
diff --git a/src/init/s6-linux-init-maker.c b/src/init/s6-linux-init-maker.c
index 30866fe..1005811 100644
--- a/src/init/s6-linux-init-maker.c
+++ b/src/init/s6-linux-init-maker.c
@@ -4,6 +4,8 @@
#include <string.h>
#include <unistd.h>
#include <errno.h>
+#include <pwd.h>
+
#include <skalibs/uint64.h>
#include <skalibs/types.h>
#include <skalibs/bytestr.h>
@@ -16,260 +18,235 @@
#include <skalibs/sgetopt.h>
#include <skalibs/skamisc.h>
-#define USAGE "s6-linux-init-maker [ -c basedir ] [ -l tmpfsdir ] [ -b execline_bindir ] [ -u log_uid -g log_gid | -U ] [ -G early_getty_cmd ] [ -2 stage2_script ] [ -r ] [ -Z shutdownscript ] [ -p initial_path ] [ -m initial_umask ] [ -t timestamp_style ] [ -d dev_style ] [ -s env_store ] [ -e initial_envvar ... ] [ -n ] [ -q final_sleep_time ] dir"
+#include <execline/config.h>
+
+#include <s6/config.h>
+
+#include <s6-linux-init/config.h>
+#include "defaults.h"
+#include "initctl.h"
+
+#ifdef S6_LINUX_INIT_UTMPD_PATH
+# include <utmps/config.h>
+# define USAGE "s6-linux-init-maker [ -c basedir ] [ -u log_user ] [ -G early_getty_cmd ] [ -1 ] [ -L ] [ -p initial_path ] [ -m initial_umask ] [ -t timestamp_style ] [ -d slashdev ] [ -s env_store ] [ -e initial_envvar ... ] [ -q default_grace_time ] [ -D initdefault ] [ -n | -N ] [ -U utmp_user ] dir"
+# define OPTION_STRING "c:u:G:1Lp:m:t:d:s:e:E:q:D:nNU:"
+# define UTMPS_DIR "utmps"
+#else
+# define USAGE "s6-linux-init-maker [ -c basedir ] [ -u log_user ] [ -G early_getty_cmd ] [ -1 ] [ -L ] [ -p initial_path ] [ -m initial_umask ] [ -t timestamp_style ] [ -d slashdev ] [ -s env_store ] [ -e initial_envvar ... ] [ -q default_grace_time ] [ -D initdefault ] [ -n | -N ] dir"
+# define OPTION_STRING "c:u:G:1Lp:m:t:d:s:e:E:q:D:nN"
+#endif
+
#define dieusage() strerr_dieusage(100, USAGE)
#define dienomem() strerr_diefu1sys(111, "stralloc_catb") ;
-#define BANNER "\n init created by s6-linux-init-maker\n see http://skarnet.org/software/s6-linux-init/\n\n"
-#define EXITCODENAME "file\\ created\\ by\\ s6-linux-init,\\ storing\\ a\\ container's\\ exit\\ code"
-
-#define CRASH_SCRIPT \
-"redirfd -r 0 /dev/console\n" \
-"redirfd -w 1 /dev/console\n" \
-"fdmove -c 2 1\n" \
-"foreground { s6-echo -- " \
-"\"s6-svscan crashed. Dropping to an interactive shell.\" }\n" \
-"/bin/sh -i\n"
-
-static char const *slashrun = "/run" ;
-static char const *robase = "/etc/s6-linux-init" ;
-static char const *init_script = "/etc/rc.init" ;
-static char const *tini_script = "/etc/rc.shutdown" ;
-static char const *bindir = "/bin" ;
-static char const *initial_path = "/usr/bin:/usr/sbin:/bin:/sbin" ;
+#define UNCAUGHT_DIR "uncaught-logs"
+
+static char const *robase = BASEDIR ;
+static char const *initial_path = INITPATH ;
static char const *env_store = 0 ;
static char const *early_getty = 0 ;
-static uid_t uncaught_logs_uid = 0 ;
-static gid_t uncaught_logs_gid = 0 ;
-static unsigned int initial_umask = 022 ;
+static char const *slashdev = 0 ;
+static char const *log_user = "root" ;
+static char const *initdefault = 0 ;
+static unsigned int initial_umask = 0022 ;
static unsigned int timestamp_style = 1 ;
-static unsigned int slashdev_style = 2 ;
-static unsigned int finalsleep = 2000 ;
-static int redirect_stage2 = 0 ;
-static int in_namespace = 0 ;
+static unsigned int finalsleep = 3000 ;
+static int mounttype = 1 ;
+static int console = 0 ;
+static int logouthookd = 0 ;
-typedef int writetobuf_func_t (buffer *) ;
+#ifdef S6_LINUX_INIT_UTMPD_PATH
+static char const *utmp_user = "utmp" ;
+#endif
+
+typedef int writetobuf_func_t (buffer *, char const *) ;
typedef writetobuf_func_t *writetobuf_func_t_ref ;
-static int put_shebang (buffer *b)
+#define put_shebang(b) put_shebang_options((b), "-P")
+
+static int put_shebang_options (buffer *b, char const *options)
{
- return buffer_puts(b, "#!") >= 0
- && buffer_puts(b, bindir) >= 0
- && buffer_puts(b, "/execlineb -P\n\n") >= 0 ;
+ return buffer_puts(b, "#!" EXECLINE_SHEBANGPREFIX "execlineb ") >= 0
+ && buffer_puts(b, options && options[0] ? options : "-P") >= 0
+ && buffer_puts(b, "\n\n") >= 0 ;
}
-static int early_getty_script (buffer *b)
+static int line_script (buffer *b, char const *line)
{
return put_shebang(b)
- && buffer_puts(b, early_getty) >= 0
+ && buffer_puts(b, line) >= 0
&& buffer_put(b, "\n", 1) >= 0 ;
}
-static int crash_script (buffer *b)
+static int linewithargs_script (buffer *b, char const *line)
+{
+ return put_shebang_options(b, "-S0")
+ && buffer_puts(b, line) >= 0
+ && buffer_puts(b, " $@\n") >= 0 ;
+}
+
+static int hpr_script (buffer *b, char const *what)
+{
+ return put_shebang_options(b, "-S0")
+ && buffer_puts(b, S6_LINUX_INIT_BINPREFIX "s6-linux-init-hpr -") >= 0
+ && buffer_puts(b, what) >= 0
+ && buffer_puts(b, " $@\n") >= 0 ;
+}
+
+static int death_script (buffer *b, char const *s)
{
return put_shebang(b)
- && buffer_puts(b, CRASH_SCRIPT) >= 0 ;
+ && buffer_puts(b,
+ EXECLINE_EXTBINPREFIX "redirfd -w 1 /dev/console\n"
+ EXECLINE_EXTBINPREFIX "fdmove -c 2 1\n"
+ EXECLINE_EXTBINPREFIX "foreground { "
+ S6_LINUX_INIT_BINPREFIX "s6-linux-init-echo -- \"s6-svscan ") >= 0
+ && buffer_puts(b, s) >= 0
+ && buffer_puts(b,
+ ". Rebooting.\" }\n"
+ S6_LINUX_INIT_BINPREFIX "s6-linux-init-hpr -r -f\n") >= 0 ;
}
-static int s6_svscan_log_script (buffer *b)
+static int s6_svscan_log_script (buffer *b, char const *data)
{
size_t sabase = satmp.len ;
- char fmt[UINT64_FMT] ;
if (!put_shebang(b)
|| buffer_puts(b,
- "redirfd -w 2 /dev/console\n"
- "redirfd -w 1 /dev/null\n"
- "redirfd -rnb 0 fifo\n"
- "s6-applyuidgid -u ") < 0
- || buffer_put(b, fmt, uid_fmt(fmt, uncaught_logs_uid)) < 0
- || buffer_puts(b, " -g ") < 0
- || buffer_put(b, fmt, gid_fmt(fmt, uncaught_logs_gid)) < 0
- || buffer_puts(b, " --\ns6-log -bp -- ") < 0
- || buffer_puts(b, timestamp_style & 1 ? "t " : "") < 0
- || buffer_puts(b, timestamp_style & 2 ? "T " : "") < 0) return 0 ;
- if (!string_quote(&satmp, slashrun, strlen(slashrun))) return 0 ;
- if (buffer_put(b, satmp.s + sabase, satmp.len - sabase) < 0)
- {
- satmp.len = sabase ;
- return 0 ;
- }
+ EXECLINE_EXTBINPREFIX "redirfd -w 2 /dev/console\n"
+ EXECLINE_EXTBINPREFIX "redirfd -w 1 /dev/") < 0
+ || buffer_puts(b, console ? "console" : "null") < 0
+ || buffer_puts(b, "\n"
+ EXECLINE_EXTBINPREFIX "redirfd -rnb 0 " LOGGER_FIFO "\n"
+ S6_EXTBINPREFIX "s6-setuidgid ") < 0
+ || !string_quote(&satmp, log_user, strlen(log_user))) return 0 ;
+ if (buffer_put(b, satmp.s + sabase, satmp.len - sabase) < 0) goto err ;
satmp.len = sabase ;
- if (buffer_puts(b, "/uncaught-logs\n") < 0) return 0 ;
+ if (buffer_puts(b, "\ns6-log -bpd3 -- ") < 0) return 0 ;
+ if (console && buffer_puts(b, "1 ") < 0) return 0 ;
+ if (timestamp_style & 1 && buffer_puts(b, "t ") < 0
+ || timestamp_style & 2 && buffer_puts(b, "T ") < 0
+ || buffer_puts(b, S6_LINUX_INIT_TMPFS "/" UNCAUGHT_DIR "\n") < 0)
+ return 0 ;
+ (void)data ;
return 1 ;
-}
-static int finish_script (buffer *b)
-{
- size_t sabase = satmp.len ;
- char fmt[UINT_FMT] ;
- fmt[uint_fmt(fmt, finalsleep)] = 0 ;
- if (buffer_puts(b, "#!") < 0
- || buffer_puts(b, bindir) < 0
- || buffer_puts(b, "/execlineb -S0\n\n") < 0
- || !string_quote(&satmp, slashrun, strlen(slashrun))) return 0 ;
-
- if (in_namespace)
- {
- if (buffer_puts(b, "ifelse { redirfd -w 2 /dev/null redirfd -r 0 ") < 0
- || buffer_put(b, satmp.s + sabase, satmp.len - sabase) < 0
- || buffer_puts(b, "/" EXITCODENAME " exit 0 }\n{ redirfd -r 0 ") < 0
- || buffer_put(b, satmp.s + sabase, satmp.len - sabase) < 0
- || buffer_puts(b, "/" EXITCODENAME " withstdinas -in CODE foreground { s6-rmrf ") < 0
- || buffer_put(b, satmp.s + sabase, satmp.len - sabase) < 0
- || buffer_puts(b, "/" EXITCODENAME " } importas -ui CODE CODE exit ${CODE} }\n") < 0) goto err ;
- }
-
- if (buffer_puts(b, "cd /\nredirfd -w 2 /dev/console\nfdmove -c 1 2\nforeground { s6-svc -X -- ") < 0
- || buffer_put(b, satmp.s + sabase, satmp.len - sabase) < 0) goto err ;
- satmp.len = sabase ;
- if (buffer_puts(b, "/service/s6-svscan-log }\n"
- "unexport ?\nwait -r -- { }\n"
- "foreground { s6-echo \"Syncing disks.\" }\n"
- "foreground { s6-sync }\n"
- "foreground { s6-echo \"Sending all processes the TERM signal.\" }\n"
- "foreground { s6-nuke -th }\n"
- "s6-sleep -m -- ") < 0
- || buffer_puts(b, fmt) < 0
- || buffer_puts(b, "\nforeground { s6-echo \"Sending all processes the KILL signal.\" }\n"
- "foreground { s6-nuke -k }\n"
- "wait { }\n"
- "foreground { s6-echo \"Unmounting disks.\" }\n"
- "foreground { s6-umount -a }\n"
- "foreground { s6-mount -ro remount /dev/root / }\n"
- "s6-${1} -f\n") < 0) return 0 ;
- return 1 ;
err:
satmp.len = sabase ;
return 0 ;
}
-static int sig_script (buffer *b, char c)
+static int logouthookd_script (buffer *b, char const *data)
+{
+ (void)data ;
+ return put_shebang(b)
+ && buffer_puts(b,
+ S6_EXTBINPREFIX "s6-ipcserver -1 -a 0700 -c 1000 -C 1000 -- " LOGOUTHOOKD_SOCKET "\n"
+ S6_LINUX_INIT_BINPREFIX "s6-linux-init-logouthookd\n") >= 0 ;
+}
+
+static int shutdownd_script (buffer *b, char const *data)
{
size_t sabase = satmp.len ;
+ char fmt[UINT_FMT] ;
if (!put_shebang(b)
- || buffer_puts(b, "foreground { ") < 0
- || !string_quote(&satmp, tini_script, strlen(tini_script))) return 0 ;
- if (buffer_put(b, satmp.s + sabase, satmp.len - sabase) < 0) goto err ;
- satmp.len = sabase ;
- if (buffer_puts(b, " }\ns6-svscanctl -") < 0
- || buffer_put(b, &c, 1) < 0
- || buffer_puts(b, " -- ") < 0
- || !string_quote(&satmp, slashrun, strlen(slashrun))) return 0 ;
+ || buffer_puts(b, S6_LINUX_INIT_BINPREFIX "s6-linux-init-shutdownd -c ") < 0
+ || !string_quote(&satmp, robase, strlen(robase))) return 0 ;
if (buffer_put(b, satmp.s + sabase, satmp.len - sabase) < 0) goto err ;
satmp.len = sabase ;
- if (buffer_puts(b, "/service\n") < 0) return 0 ;
+ if (buffer_puts(b, " -g ") < 0
+ || buffer_put(b, fmt, uint_fmt(fmt, finalsleep)) < 0
+ || buffer_puts(b, "\n") < 0) return 0 ;
+ (void)data ;
return 1 ;
+
err:
satmp.len = sabase ;
- return 0 ;
+ return 0 ;
}
-static int onlyexit (buffer *b, char c)
+static int runleveld_script (buffer *b, char const *data)
{
size_t sabase = satmp.len ;
if (!put_shebang(b)
- || buffer_puts(b, "foreground { redirfd -w 1 ") < 0
- || !string_quote(&satmp, slashrun, strlen(slashrun))
- || buffer_put(b, satmp.s + sabase, satmp.len - sabase) < 0
- || buffer_puts(b, "/" EXITCODENAME " s6-echo -- ") < 0
- || buffer_put(b, &c, 1) < 0
- || buffer_puts(b, " }\ns6-svscanctl -b ") < 0
- || buffer_put(b, satmp.s + sabase, satmp.len - sabase) < 0
- || buffer_puts(b, "/service\n") < 0) goto err ;
+ || buffer_puts(b,
+ EXECLINE_EXTBINPREFIX "fdmove -c 2 1\n"
+ EXECLINE_EXTBINPREFIX "fdmove 1 3\n"
+ S6_EXTBINPREFIX "s6-ipcserver -1 -a 0700 -c 1 -- " RUNLEVELD_SOCKET "\n"
+ S6_EXTBINPREFIX "s6-sudod -0 -1 -2 -t 30000 --\n") < 0
+ || !string_quote(&satmp, robase, strlen(robase))) return 0 ;
+ if (buffer_put(b, satmp.s + sabase, satmp.len - sabase) < 0) goto err ;
satmp.len = sabase ;
+ if (buffer_puts(b, "/scripts/runlevel\n") < 0) return 0 ;
+ (void)data ;
return 1 ;
+
err:
satmp.len = sabase ;
return 0 ;
}
-static int sigterm_script (buffer *b)
-{
- return sig_script(b, 't') ;
-}
-
-static int sighup_script (buffer *b)
-{
- return in_namespace ? onlyexit(b, '0') : sig_script(b, 'h') ;
-}
-
-static int sigquit_script (buffer *b)
-{
- return sig_script(b, 'q') ;
-}
-
-static int sigint_script (buffer *b)
-{
- return in_namespace ? onlyexit(b, '1') : sig_script(b, '6') ;
-}
-
-static int sigusr1_script (buffer *b)
+static int sig_script (buffer *b, char const *option)
{
- return sig_script(b, '7') ;
-}
-
-static int sigusr2_script (buffer *b)
-{
- return sig_script(b, '0') ;
+ return put_shebang(b)
+ && buffer_puts(b, S6_LINUX_INIT_BINPREFIX "s6-linux-init-shutdown -a ") >= 0
+ && buffer_puts(b, option) >= 0
+ && buffer_puts(b, " -- now\n") >= 0 ;
}
-static inline int stage1_script (buffer *b)
+static inline int stage1_script (buffer *b, char const *data)
{
- size_t sabase = satmp.len, pos, pos2 ;
- char fmt[UINT_OFMT] ;
- if (!put_shebang(b)
- || buffer_puts(b, bindir) < 0
- || buffer_puts(b, "/export PATH ") < 0
- || !string_quote(&satmp, initial_path, strlen(initial_path))) return 0 ;
+ size_t sabase = satmp.len ;
+ if (!put_shebang_options(b, "-S0")
+ || buffer_puts(b, S6_LINUX_INIT_BINPREFIX "s6-linux-init -c ") < 0
+ || !string_quote(&satmp, robase, strlen(robase))) return 0 ;
if (buffer_put(b, satmp.s + sabase, satmp.len - sabase) < 0) goto err ;
satmp.len = sabase ;
- if (buffer_put(b, "\n", 1) < 0
- || buffer_puts(b, bindir) < 0
- || buffer_puts(b, "/cd /\ns6-setsid -qb --\numask 0") < 0
- || buffer_put(b, fmt, uint_ofmt(fmt, initial_umask)) < 0
- || buffer_puts(b, "\nif { s6-echo -- ") < 0
- || !string_quote(&satmp, BANNER, sizeof(BANNER) - 1)) return 0 ;
- if (buffer_put(b, satmp.s, satmp.len) < 0) goto err ;
- satmp.len = sabase ;
- if (buffer_puts(b, " }\nif { s6-mount -nwt tmpfs -o mode=0755 tmpfs ") < 0
- || !string_quote(&satmp, slashrun, strlen(slashrun))) return 0 ;
- pos = satmp.len ;
- if (buffer_put(b, satmp.s + sabase, pos - sabase) < 0
- || buffer_puts(b, " }\nif { s6-hiercopy ") < 0
- || !string_quote(&satmp, robase, strlen(robase))) return 0 ;
- pos2 = satmp.len ;
- if (buffer_put(b, satmp.s + pos, pos2 - pos) < 0
- || buffer_puts(b, "/run-image ") < 0
- || buffer_put(b, satmp.s + sabase, pos - sabase) < 0
- || buffer_puts(b, " }\n") < 0) goto err ;
- if (slashdev_style == 1)
{
- if (buffer_puts(b, "if { s6-mount -nt devtmpfs dev /dev }\n") < 0) goto err ;
+ char fmt[UINT_OFMT] ;
+ if (buffer_puts(b, " -m 00") < 0
+ || buffer_put(b, fmt, uint_ofmt(fmt, initial_umask)) < 0) return 0 ;
+ }
+ if (initial_path)
+ {
+ if (buffer_puts(b, " -p ") < 0
+ || !string_quote(&satmp, initial_path, strlen(initial_path))) return 0 ;
+ if (buffer_put(b, satmp.s + sabase, satmp.len - sabase) < 0) goto err ;
+ satmp.len = sabase ;
}
if (env_store)
{
- size_t base = satmp.len ;
- if (!string_quote(&satmp, env_store, strlen(env_store))) return 0 ;
- if (buffer_puts(b, "if { unexport PATH s6-dumpenv -- ") < 0
- || buffer_put(b, satmp.s + base, satmp.len - base) < 0
- || buffer_puts(b, " }\n") < 0) goto err ;
- satmp.len = base ;
+ if (buffer_puts(b, " -s ") < 0
+ || !string_quote(&satmp, env_store, strlen(env_store))) return 0 ;
+ if (buffer_put(b, satmp.s + sabase, satmp.len - sabase) < 0) goto err ;
+ satmp.len = sabase ;
+ }
+ if (slashdev)
+ {
+ if (buffer_puts(b, " -d ") < 0
+ || !string_quote(&satmp, slashdev, strlen(slashdev))) return 0 ;
+ if (buffer_put(b, satmp.s + sabase, satmp.len - sabase) < 0) goto err ;
+ satmp.len = sabase ;
+ }
+ if (initdefault)
+ {
+ if (buffer_puts(b, " -D ") < 0
+ || !string_quote(&satmp, initdefault, strlen(initdefault))) return 0 ;
+ if (buffer_put(b, satmp.s + sabase, satmp.len - sabase) < 0) goto err ;
+ satmp.len = sabase ;
+ }
+ if (mounttype == 2)
+ {
+ if (buffer_puts(b, " -n") < 0) return 0 ;
+ }
+ else if (!mounttype)
+ {
+ if (buffer_puts(b, " -N") < 0) return 0 ;
}
- if (buffer_puts(b, "emptyenv -p\ns6-envdir -I -- ") < 0
- || buffer_put(b, satmp.s + pos, pos2 - pos) < 0
- || buffer_puts(b, "/env\nredirfd -r 0 /dev/null\nredirfd -wnb 1 ") < 0
- || buffer_put(b, satmp.s + sabase, pos - sabase) < 0
- || buffer_puts(b, "/service/s6-svscan-log/fifo\nbackground\n{\n s6-setsid --\n redirfd -w 1 ") < 0
- || buffer_put(b, satmp.s + sabase, pos - sabase) < 0
- || buffer_puts(b, "/service/s6-svscan-log/fifo\n fdmove -c ") < 0
- || buffer_puts(b, redirect_stage2 ? "2 1" : "1 2") < 0
- || buffer_puts(b, "\n ") < 0
- || !string_quote(&satmp, init_script, strlen(init_script))
- || buffer_put(b, satmp.s + pos2, satmp.len - pos2) < 0
- || buffer_puts(b, "\n}\nunexport !\ncd ") < 0
- || buffer_put(b, satmp.s + sabase, pos - sabase) < 0
- || buffer_puts(b, "/service\nfdmove -c 2 1\ns6-svscan -st0\n") < 0) goto err ;
+
+ if (buffer_puts(b, "\n") < 0) return 0 ;
+ (void)data ;
return 1 ;
+
err:
satmp.len = sabase ;
return 0 ;
@@ -282,7 +259,7 @@ static void cleanup (char const *base)
errno = e ;
}
-static void auto_dir (char const *base, char const *dir, uid_t uid, gid_t gid, unsigned int mode)
+static void auto_dir_internal (char const *base, char const *dir, uid_t uid, gid_t gid, unsigned int mode, int strict)
{
size_t clen = strlen(base) ;
size_t dlen = strlen(dir) ;
@@ -290,15 +267,26 @@ static void auto_dir (char const *base, char const *dir, uid_t uid, gid_t gid, u
memcpy(fn, base, clen) ;
fn[clen] = dlen ? '/' : 0 ;
memcpy(fn + clen + 1, dir, dlen + 1) ;
- if (mkdir(fn, mode) < 0
- || ((uid || gid) && (chown(fn, uid, gid) < 0 || chmod(fn, mode) < 0)))
+
+ if (mkdir(fn, mode) < 0)
{
- cleanup(base) ;
- strerr_diefu2sys(111, "mkdir ", fn) ;
+ if (errno != EEXIST || strict) goto err ;
+ }
+ else if (uid || gid)
+ {
+ if (chown(fn, uid, gid) < 0
+ || chmod(fn, mode) < 0) goto err ;
}
+ return ;
+
+ err:
+ cleanup(base) ;
+ strerr_diefu2sys(111, "mkdir ", fn) ;
}
-static void auto_file (char const *base, char const *file, char const *s, unsigned int n, int executable)
+#define auto_dir(base, dir, uid, gid, mode) auto_dir_internal(base, dir, uid, gid, (mode), 1)
+
+static void auto_file (char const *base, char const *file, char const *s, unsigned int n)
{
size_t clen = strlen(base) ;
size_t flen = strlen(file) ;
@@ -307,13 +295,28 @@ static void auto_file (char const *base, char const *file, char const *s, unsign
fn[clen] = '/' ;
memcpy(fn + clen + 1, file, flen + 1) ;
if (!openwritenclose_unsafe(fn, s, n)
- || (executable && chmod(fn, 0755) < 0))
+ || chmod(fn, 0644) == -1)
{
cleanup(base) ;
strerr_diefu2sys(111, "write to ", fn) ;
}
}
+static void auto_symlink (char const *base, char const *name, char const *target)
+{
+ size_t clen = strlen(base) ;
+ size_t dlen = strlen(name) ;
+ char fn[clen + dlen + 2] ;
+ memcpy(fn, base, clen) ;
+ fn[clen] = '/' ;
+ memcpy(fn + clen + 1, name, dlen + 1) ;
+ if (symlink(target, fn) == -1)
+ {
+ cleanup(base) ;
+ strerr_diefu4sys(111, "make a symlink named ", fn, " pointing to ", target) ;
+ }
+}
+
static void auto_fifo (char const *base, char const *fifo)
{
size_t baselen = strlen(base) ;
@@ -329,7 +332,7 @@ static void auto_fifo (char const *base, char const *fifo)
}
}
-static void auto_script (char const *base, char const *file, writetobuf_func_t_ref scriptf)
+static void auto_script (char const *base, char const *file, writetobuf_func_t_ref scriptf, char const *data)
{
char buf[4096] ;
buffer b ;
@@ -347,56 +350,215 @@ static void auto_script (char const *base, char const *file, writetobuf_func_t_r
strerr_diefu3sys(111, "open ", fn, " for script writing") ;
}
buffer_init(&b, &fd_writev, fd, buf, 4096) ;
- if (!(*scriptf)(&b) || !buffer_flush(&b))
+ if (!(*scriptf)(&b, data) || !buffer_flush(&b))
{
cleanup(base) ;
strerr_diefu2sys(111, "write to ", fn) ;
}
- close(fd) ;
+ fd_close(fd) ;
}
-static inline void make_env (char const *base, char const *modif, size_t modiflen)
+static void copy_script (char const *base, char const *src, char const *dst)
{
- auto_dir(base, "env", 0, 0, 0755) ;
+ size_t baselen = strlen(base) ;
+ size_t dstlen = strlen(dst) ;
+ char fn[baselen + dstlen + 2] ;
+ memcpy(fn, base, baselen) ;
+ fn[baselen] = '/' ;
+ memcpy(fn + baselen + 1, dst, dstlen + 1) ;
+ if (!filecopy_unsafe(src, fn, 0755))
+ {
+ cleanup(base) ;
+ strerr_diefu4sys(111, "copy ", src, " to ", fn) ;
+ }
+}
+
+static void auto_exec (char const *base, char const *name, char const *target)
+{
+ if (S6_LINUX_INIT_BINPREFIX[0] == '/')
+ {
+ size_t len = strlen(target) ;
+ char fn[sizeof(S6_LINUX_INIT_BINPREFIX) + len] ;
+ memcpy(fn, S6_LINUX_INIT_BINPREFIX, sizeof(S6_LINUX_INIT_BINPREFIX) - 1) ;
+ memcpy(fn + sizeof(S6_LINUX_INIT_BINPREFIX) - 1, target, len + 1) ;
+ auto_symlink(base, name, fn) ;
+ }
+ else
+ auto_script(base, name, &linewithargs_script, target) ;
+}
+
+static void make_env (char const *base, char const *envname, char *modif, size_t modiflen)
+{
+ size_t envnamelen = strlen(envname) ;
+ auto_dir(base, envname, 0, 0, 0755) ;
while (modiflen)
{
size_t len = strlen(modif) ;
size_t pos = byte_chr(modif, len, '=') ;
- char fn[5 + pos] ;
- memcpy(fn, "env/", 4) ;
- memcpy(fn + 4, modif, pos) ;
- fn[4 + pos] = 0 ;
+ char fn[envnamelen + pos + 2] ;
+ memcpy(fn, envname, envnamelen) ;
+ fn[envnamelen] = '/' ;
+ memcpy(fn + envnamelen + 1, modif, pos) ;
+ fn[envnamelen + 1 + pos] = 0 ;
- if (pos + 1 < len) auto_file(base, fn, modif + pos + 1, len - pos - 1, 0) ;
- else if (pos + 1 == len) auto_file(base, fn, "\n", 1, 0) ;
- else auto_file(base, fn, "", 0, 0) ;
+ if (pos + 1 < len)
+ {
+ modif[len] = '\n' ;
+ auto_file(base, fn, modif + pos + 1, len - pos) ;
+ modif[len] = 0 ;
+ }
+ else if (pos + 1 == len) auto_file(base, fn, "\n", 1) ;
+ else auto_file(base, fn, "", 0) ;
modif += len+1 ; modiflen -= len+1 ;
}
}
+static void getug (char const *base, char const *s, uid_t *uid, gid_t *gid)
+{
+ struct passwd *pw ;
+ errno = 0 ;
+ pw = getpwnam(s) ;
+ if (!pw)
+ {
+ cleanup(base) ;
+ if (!errno) strerr_diefu3x(100, "find user ", s, " in passwd database") ;
+ else strerr_diefu2sys(111, "getpwnam for ", s) ;
+ }
+ *uid = pw->pw_uid ;
+ *gid = pw->pw_gid ;
+}
+
+#ifdef S6_LINUX_INIT_UTMPD_PATH
+
+static inline void auto_basedir (char const *base, char const *dir, uid_t uid, gid_t gid, unsigned int mode)
+{
+ size_t n = strlen(dir) ;
+ char tmp[n + 1] ;
+ for (size_t i = 0 ; i < n ; i++)
+ {
+ if ((dir[i] == '/') && i)
+ {
+ tmp[i] = 0 ;
+ auto_dir_internal(base, tmp, uid, gid, mode, 0) ;
+ }
+ tmp[i] = dir[i] ;
+ }
+}
+
+static int utmpd_script (buffer *b, char const *uw)
+{
+ size_t sabase = satmp.len ;
+ if (!put_shebang(b)
+ || buffer_puts(b,
+ EXECLINE_EXTBINPREFIX "fdmove -c 2 1\n"
+ S6_EXTBINPREFIX "s6-setuidgid ") < 0
+ || !string_quote(&satmp, utmp_user, strlen(utmp_user))) return 0 ;
+ if (buffer_put(b, satmp.s + sabase, satmp.len - sabase) < 0) goto err ;
+ satmp.len = sabase ;
+ if (buffer_puts(b, "\n"
+ EXECLINE_EXTBINPREFIX "cd " S6_LINUX_INIT_TMPFS "/" UTMPS_DIR "\n"
+ EXECLINE_EXTBINPREFIX "fdmove 1 3\n"
+ S6_EXTBINPREFIX "s6-ipcserver -1 -c 1000 -- ") < 0) return 0 ;
+ if (buffer_puts(b, uw[0] == 'u' ? UTMPS_UTMPD_PATH : UTMPS_WTMPD_PATH) < 0
+ || buffer_puts(b, "\n"
+ UTMPS_EXTBINPREFIX "utmps-") < 0
+ || buffer_puts(b, uw) < 0
+ || buffer_puts(b, "tmpd\n") < 0) return 0 ;
+ return 1 ;
+
+ err:
+ satmp.len = sabase ;
+ return 0 ;
+}
+
+static inline void make_utmps (char const *base)
+{
+ auto_dir(base, "run-image/" SCANDIR "/utmpd", 0, 0, 0755) ;
+ auto_file(base, "run-image/" SCANDIR "/utmpd/notification-fd", "3\n", 2) ;
+ auto_script(base, "run-image/" SCANDIR "/utmpd/run", &utmpd_script, "u") ;
+ auto_dir(base, "run-image/" SCANDIR "/wtmpd", 0, 0, 0755) ;
+ auto_file(base, "run-image/" SCANDIR "/wtmpd/notification-fd", "3\n", 2) ;
+ auto_script(base, "run-image/" SCANDIR "/wtmpd/run", &utmpd_script, "w") ;
+ {
+ uid_t uid ;
+ gid_t gid ;
+ getug(base, utmp_user, &uid, &gid) ;
+ auto_dir(base, "run-image/" UTMPS_DIR, uid, gid, 0755) ;
+ auto_basedir(base, "run-image/" S6_LINUX_INIT_UTMPD_PATH, uid, gid, 0755) ;
+ auto_basedir(base, "run-image/" S6_LINUX_INIT_WTMPD_PATH, uid, gid, 0755) ;
+ }
+}
+
+#endif
+
static inline void make_image (char const *base)
{
auto_dir(base, "run-image", 0, 0, 0755) ;
- auto_dir(base, "run-image/uncaught-logs", uncaught_logs_uid, uncaught_logs_gid, 02700) ;
- auto_dir(base, "run-image/service", 0, 0, 0755) ;
- auto_dir(base, "run-image/service/.s6-svscan", 0, 0, 0755) ;
- auto_script(base, "run-image/service/.s6-svscan/crash", &crash_script) ;
- auto_script(base, "run-image/service/.s6-svscan/finish", &finish_script) ;
- auto_script(base, "run-image/service/.s6-svscan/SIGTERM", &sigterm_script) ;
- auto_script(base, "run-image/service/.s6-svscan/SIGHUP", &sighup_script) ;
- auto_script(base, "run-image/service/.s6-svscan/SIGQUIT", &sigquit_script) ;
- auto_script(base, "run-image/service/.s6-svscan/SIGINT", &sigint_script) ;
- auto_script(base, "run-image/service/.s6-svscan/SIGUSR1", &sigusr1_script) ;
- auto_script(base, "run-image/service/.s6-svscan/SIGUSR2", &sigusr2_script) ;
- auto_dir(base, "run-image/service/s6-svscan-log", 0, 0, 0755) ;
- auto_fifo(base, "run-image/service/s6-svscan-log/fifo") ;
- auto_script(base, "run-image/service/s6-svscan-log/run", &s6_svscan_log_script) ;
+ {
+ uid_t uid ;
+ gid_t gid ;
+ getug(base, log_user, &uid, &gid) ;
+ auto_dir(base, "run-image/" UNCAUGHT_DIR, uid, gid, 02700) ;
+ }
+ auto_dir(base, "run-image/" SCANDIR, 0, 0, 0755) ;
+ auto_dir(base, "run-image/" SCANDIR "/.s6-svscan", 0, 0, 0755) ;
+ auto_script(base, "run-image/" SCANDIR "/.s6-svscan/crash", &death_script, "crashed") ;
+ auto_script(base, "run-image/" SCANDIR "/.s6-svscan/finish", &death_script, "exited") ;
+ auto_script(base, "run-image/" SCANDIR "/.s6-svscan/SIGTERM", &put_shebang_options, 0) ;
+ auto_script(base, "run-image/" SCANDIR "/.s6-svscan/SIGHUP", &put_shebang_options, 0) ;
+ auto_script(base, "run-image/" SCANDIR "/.s6-svscan/SIGQUIT", &put_shebang_options, 0) ;
+ auto_script(base, "run-image/" SCANDIR "/.s6-svscan/SIGINT", &sig_script, "-r") ;
+ auto_script(base, "run-image/" SCANDIR "/.s6-svscan/SIGUSR1", &sig_script, "-p") ;
+ auto_script(base, "run-image/" SCANDIR "/.s6-svscan/SIGUSR2", &sig_script, "-h") ;
+
+ auto_dir(base, "run-image/" SCANDIR "/" LOGGER_SERVICEDIR, 0, 0, 0755) ;
+ auto_fifo(base, "run-image/" SCANDIR "/" LOGGER_SERVICEDIR "/" LOGGER_FIFO) ;
+ auto_file(base, "run-image/" SCANDIR "/" LOGGER_SERVICEDIR "/notification-fd", "3\n", 2) ;
+ auto_script(base, "run-image/" SCANDIR "/" LOGGER_SERVICEDIR "/run", &s6_svscan_log_script, 0) ;
+
+ auto_dir(base, "run-image/" SCANDIR "/" SHUTDOWND_SERVICEDIR, 0, 0, 0755) ;
+ auto_fifo(base, "run-image/" SCANDIR "/" SHUTDOWND_SERVICEDIR "/" SHUTDOWND_FIFO) ;
+ auto_script(base, "run-image/" SCANDIR "/" SHUTDOWND_SERVICEDIR "/run", &shutdownd_script, 0) ;
+
+ auto_dir(base, "run-image/" SCANDIR "/" RUNLEVELD_SERVICEDIR, 0, 0, 0755) ;
+ auto_file(base, "run-image/" SCANDIR "/" RUNLEVELD_SERVICEDIR "/notification-fd", "3\n", 2) ;
+ auto_script(base, "run-image/" SCANDIR "/" RUNLEVELD_SERVICEDIR "/run", &runleveld_script, 0) ;
+
+ if (logouthookd)
+ {
+ auto_dir(base, "run-image/" SCANDIR "/" LOGOUTHOOKD_SERVICEDIR, 0, 0, 0755) ;
+ auto_file(base, "run-image/" SCANDIR "/" LOGOUTHOOKD_SERVICEDIR "/notification-fd", "1\n", 2) ;
+ auto_script(base, "run-image/" SCANDIR "/" LOGOUTHOOKD_SERVICEDIR "/run", &logouthookd_script, 0) ;
+ }
+
if (early_getty)
{
- auto_dir(base, "run-image/service/s6-linux-init-early-getty", 0, 0, 0755) ;
- auto_script(base, "run-image/service/s6-linux-init-early-getty/run", &early_getty_script) ;
+ auto_dir(base, "run-image/" SCANDIR "/" EARLYGETTY_SERVICEDIR, 0, 0, 0755) ;
+ auto_script(base, "run-image/" SCANDIR "/" EARLYGETTY_SERVICEDIR "/run", &line_script, early_getty) ;
}
- auto_script(base, "init", &stage1_script) ;
+
+#ifdef S6_LINUX_INIT_UTMPD_PATH
+ if (utmp_user[0]) make_utmps(base) ;
+#endif
+}
+
+static inline void make_scripts (char const *base)
+{
+ auto_dir(base, "scripts", 0, 0, 0755) ;
+ copy_script(base, S6_LINUX_INIT_SKELDIR "/runlevel", "scripts/runlevel") ;
+ copy_script(base, S6_LINUX_INIT_SKELDIR "/" STAGE2, "scripts/" STAGE2) ;
+ copy_script(base, S6_LINUX_INIT_SKELDIR "/" STAGE3, "scripts/" STAGE3) ;
+}
+
+static inline void make_bins (char const *base)
+{
+ auto_dir(base, "bin", 0, 0, 0755) ;
+ auto_script(base, "bin/init", &stage1_script, 0) ;
+ auto_script(base, "bin/halt", &hpr_script, "h") ;
+ auto_script(base, "bin/poweroff", &hpr_script, "p") ;
+ auto_script(base, "bin/reboot", &hpr_script, "r") ;
+ auto_exec(base, "bin/shutdown", "s6-linux-init-shutdown") ;
+ auto_exec(base, "bin/telinit", "s6-linux-init-telinit") ;
}
int main (int argc, char const *const *argv, char const *const *envp)
@@ -406,36 +568,28 @@ int main (int argc, char const *const *argv, char const *const *envp)
subgetopt_t l = SUBGETOPT_ZERO ;
for (;;)
{
- int opt = subgetopt_r(argc, argv, "c:l:b:u:g:UG:2:rZ:p:m:t:d:s:e:nq:", &l) ;
+ int opt = subgetopt_r(argc, argv, OPTION_STRING, &l) ;
if (opt == -1) break ;
switch (opt)
{
case 'c' : robase = l.arg ; break ;
- case 'l' : slashrun = l.arg ; break ;
- case 'b' : bindir = l.arg ; break ;
- case 'u' : if (!uint0_scan(l.arg, &uncaught_logs_uid)) dieusage() ; break ;
- case 'g' : if (!uint0_scan(l.arg, &uncaught_logs_gid)) dieusage() ; break ;
- case 'U' :
- {
- char const *x = env_get2(envp, "UID") ;
- if (!x) strerr_dienotset(100, "UID") ;
- if (!uint0_scan(x, &uncaught_logs_uid)) strerr_dieinvalid(100, "UID") ;
- x = env_get2(envp, "GID") ;
- if (!x) strerr_dienotset(100, "GID") ;
- if (!uint0_scan(x, &uncaught_logs_gid)) strerr_dieinvalid(100, "GID") ;
- }
+ case 'u' : log_user = l.arg ; break ;
case 'G' : early_getty = l.arg ; break ;
- case '2' : init_script = l.arg ; break ;
- case 'r' : redirect_stage2 = 1 ; break ;
- case 'Z' : tini_script = l.arg ; break ;
+ case '1' : console = 1 ; break ;
+ case 'L' : logouthookd = 1 ; break ;
case 'p' : initial_path = l.arg ; break ;
case 'm' : if (!uint0_oscan(l.arg, &initial_umask)) dieusage() ; break ;
case 't' : if (!uint0_scan(l.arg, &timestamp_style)) dieusage() ; break ;
- case 'd' : if (!uint0_scan(l.arg, &slashdev_style)) dieusage() ; break ;
+ case 'd' : slashdev = l.arg ; break ;
case 's' : env_store = l.arg ; break ;
case 'e' : if (!stralloc_catb(&satmp, l.arg, strlen(l.arg) + 1)) dienomem() ; break ;
- case 'n' : in_namespace = 1 ; break ;
case 'q' : if (!uint0_scan(l.arg, &finalsleep)) dieusage() ; break ;
+ case 'D' : initdefault = l.arg ; break ;
+ case 'n' : mounttype = 2 ; break ;
+ case 'N' : mounttype = 0 ; break ;
+#ifdef S6_LINUX_INIT_UTMPD_PATH
+ case 'U' : utmp_user = l.arg ; break ;
+#endif
default : dieusage() ;
}
}
@@ -445,24 +599,26 @@ int main (int argc, char const *const *argv, char const *const *envp)
if (robase[0] != '/')
strerr_dief3x(100, "base directory ", robase, " is not absolute") ;
- if (slashrun[0] != '/')
- strerr_dief3x(100, "tmpfs directory ", slashrun, " is not absolute") ;
- if (bindir[0] != '/')
- strerr_dief3x(100, "initial location for binaries ", bindir, " is not absolute") ;
- if (init_script[0] != '/')
- strerr_dief3x(100, "stage 2 script location ", init_script, " is not absolute") ;
- if (tini_script[0] != '/')
- strerr_dief3x(100, "shutdown script location ", tini_script, " is not absolute") ;
+ if (slashdev && slashdev[0] != '/')
+ strerr_dief3x(100, "devtmpfs directory ", slashdev, " is not absolute") ;
+ if (env_store)
+ {
+ if (env_store[0] != '/')
+ strerr_dief3x(100, "kernel environment store ", env_store, " is not absolute") ;
+ if (!str_start(env_store, S6_LINUX_INIT_TMPFS "/"))
+ strerr_warnw3x("kernel environment store ", env_store, " is not located under initial tmpfs " S6_LINUX_INIT_TMPFS) ;
+ }
if (timestamp_style > 3)
strerr_dief1x(100, "-t timestamp_style must be 0, 1, 2 or 3") ;
- if (slashdev_style > 2)
- strerr_dief1x(100, "-d dev_style must be 0, 1 or 2") ;
+ umask(0) ;
if (mkdir(argv[0], 0755) < 0)
strerr_diefu2sys(111, "mkdir ", argv[0]) ;
- make_env(argv[0], satmp.s, satmp.len) ;
+ make_env(argv[0], ENVSTAGE1, satmp.s, satmp.len) ;
satmp.len = 0 ;
make_image(argv[0]) ;
+ make_scripts(argv[0]) ;
+ make_bins(argv[0]) ;
return 0 ;
}
diff --git a/src/init/s6-linux-init-telinit.c b/src/init/s6-linux-init-telinit.c
new file mode 100644
index 0000000..42441d6
--- /dev/null
+++ b/src/init/s6-linux-init-telinit.c
@@ -0,0 +1,74 @@
+/* ISC license. */
+
+#include <string.h>
+#include <sys/wait.h>
+
+#include <skalibs/types.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+
+#include <s6/config.h>
+
+#include <s6-linux-init/config.h>
+#include "initctl.h"
+
+#define USAGE "s6-linux-init-telinit runlevel"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ char const *newargv[8] = { S6_EXTBINPREFIX "s6-sudo", "-e", "-T", "3600000", "--", RUNLEVELD_PATH, 0, 0 } ;
+ PROG = "s6-linux-init-telinit" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ int opt = subgetopt_r(argc, argv, "c:p:s:m:d:D:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'c' : /* s6-linux-init may be called with these options, don't choke on them */
+ case 'p' :
+ case 's' :
+ case 'm' :
+ case 'd' :
+ case 'D' : break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+
+ if (!argc) dieusage() ;
+ newargv[6] = argv[0] ;
+
+
+ /* specialcase 0 and 6: fork runlevel call then exec shutdown, instead of execing runlevel call */
+
+ if (!strcmp(argv[0], "0") || !strcmp(argv[0], "6"))
+ {
+ int wstat ;
+ pid_t pid = child_spawn0(newargv[0], newargv, envp) ;
+ if (!pid) strerr_diefu2sys(111, "spawn ", newargv[0]) ;
+ if (wait_pid(pid, &wstat) < 0) strerr_diefu1sys(111, "wait_pid") ;
+ if (WIFSIGNALED(wstat))
+ {
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, WTERMSIG(wstat))] = 0 ;
+ strerr_dief3x(wait_estatus(wstat), newargv[0], " crashed with signal ", fmt) ;
+ }
+ else if (WEXITSTATUS(wstat))
+ {
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, WEXITSTATUS(wstat))] = 0 ;
+ strerr_dief3x(wait_estatus(wstat), newargv[0], " died with exitcode ", fmt) ;
+ }
+
+ newargv[0] = S6_LINUX_INIT_BINPREFIX "s6-linux-init-hpr" ;
+ newargv[1] = argv[0][0] == '6' ? "-r" : "-p" ;
+ newargv[2] = 0 ;
+ }
+
+ xpathexec_run(newargv[0], newargv, envp) ;
+}
diff --git a/src/init/s6-linux-init.c b/src/init/s6-linux-init.c
new file mode 100644
index 0000000..e322fa4
--- /dev/null
+++ b/src/init/s6-linux-init.c
@@ -0,0 +1,189 @@
+/* ISC license. */
+
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+
+#include <skalibs/types.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/env.h>
+#include <skalibs/djbunix.h>
+
+#include <s6/config.h>
+
+#include <s6-linux-init/config.h>
+
+#include "defaults.h"
+#include "initctl.h"
+
+#define USAGE "s6-linux-init [ -c basedir ] [ -p initpath ] [ -s envdumpdir ] [ -m umask ] [ -d devtmpfs ] [ -D initdefault ] [ -n | -N ]"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+#define BANNER "\n s6-linux-init version " S6_LINUX_INIT_VERSION "\n\n"
+
+static inline char const *scan_cmdline (char const *initdefault, char const *const *argv, unsigned int argc)
+{
+ static char const *valid[] = { "default", "2", "3", "4", "5", 0 } ;
+ for (unsigned int i = 0 ; i < argc ; i++)
+ for (char const *const *p = valid ; *p ; p++)
+ if (!strcmp(argv[i], *p)) return argv[i] ;
+ return initdefault ;
+}
+
+static inline void run_stage2 (char const *basedir, char const **argv, unsigned int argc, char const *const *envp, size_t envlen, char const *modifs, size_t modiflen, char const *initdefault)
+{
+ size_t dirlen = strlen(basedir) ;
+ char const *childargv[argc + 3] ;
+ char fn[dirlen + sizeof("/scripts/" STAGE2)] ;
+ PROG = "s6-linux-init (child)" ;
+ argv[0] = PROG ;
+ memcpy(fn, basedir, dirlen) ;
+ memcpy(fn + dirlen, "/scripts/" STAGE2, sizeof("/scripts/" STAGE2)) ;
+ childargv[0] = fn ;
+ childargv[1] = scan_cmdline(initdefault, argv, argc) ;
+ for (unsigned int i = 0 ; i < argc ; i++)
+ childargv[i+2] = argv[i] ;
+ childargv[argc + 2] = 0 ;
+ setsid() ;
+ fd_close(1) ;
+ if (open(LOGFIFO, O_WRONLY) != 1) /* blocks until catch-all logger is up */
+ strerr_diefu1sys(111, "open " LOGFIFO " for writing") ;
+ if (fd_copy(2, 1) == -1)
+ strerr_diefu1sys(111, "fd_copy stdout to stderr") ;
+ xpathexec_r(childargv, envp, envlen, modifs, modiflen) ;
+}
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+ mode_t mask = 0022 ;
+ char const *basedir = BASEDIR ;
+ char const *path = INITPATH ;
+ char const *slashdev = 0 ;
+ char const *envdumpdir = 0 ;
+ char const *initdefault = "default" ;
+ int mounttype = 1 ;
+ stralloc envmodifs = STRALLOC_ZERO ;
+ PROG = "s6-linux-init" ;
+
+ if (getpid() != 1)
+ {
+ argv[0] = S6_LINUX_INIT_BINPREFIX "s6-linux-init-telinit" ;
+ pathexec_run(argv[0], argv, envp) ;
+ strerr_dieexec(111, argv[0]) ;
+ }
+
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ int opt = subgetopt_r(argc, argv, "c:p:s:m:d:D:nN", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'c' : basedir = l.arg ; break ;
+ case 'p' : path = l.arg ; break ;
+ case 's' : envdumpdir = l.arg ; break ;
+ case 'm' : if (!uint0_oscan(l.arg, &mask)) dieusage() ; break ;
+ case 'd' : slashdev = l.arg ; break ;
+ case 'D' : initdefault = l.arg ; break ;
+ case 'n' : mounttype = 2 ; break ;
+ case 'N' : mounttype = 0 ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+
+ allwrite(1, BANNER, sizeof(BANNER) - 1) ;
+ if (chdir("/") == -1) strerr_diefu1sys(111, "chdir to /") ;
+ umask(mask) ;
+ setpgid(0, 0) ;
+ fd_close(0) ;
+ if (slashdev)
+ {
+ fd_close(1) ;
+ fd_close(2) ;
+ if (mount("dev", slashdev, "devtmpfs", MS_NOSUID | MS_NOEXEC, "") == -1)
+ {
+ int e = errno ;
+ open("/dev/null", O_RDONLY) ;
+ open("/dev/console", O_WRONLY) ;
+ fd_copy(2, 1) ;
+ errno = e ;
+ strerr_diefu2sys(111, "mount ", slashdev) ;
+ }
+ if (open("/dev/console", O_WRONLY)
+ || fd_copy(1, 0) == -1
+ || fd_move(2, 0) == -1) return 111 ;
+ }
+ if (open("/dev/null", O_RDONLY)) strerr_diefu1sys(111, "open /dev/null") ;
+
+ if (mounttype)
+ {
+ if (mounttype == 2)
+ {
+ if (mount("tmpfs", S6_LINUX_INIT_TMPFS, "tmpfs", MS_REMOUNT | MS_NODEV | MS_NOSUID, "mode=0755") == -1)
+ strerr_diefu1sys(111, "remount " S6_LINUX_INIT_TMPFS) ;
+ }
+ else
+ {
+ if (umount(S6_LINUX_INIT_TMPFS) == -1)
+ {
+ if (errno != EINVAL)
+ strerr_warnwu1sys("umount " S6_LINUX_INIT_TMPFS) ;
+ }
+ if (mount("tmpfs", S6_LINUX_INIT_TMPFS, "tmpfs", MS_NODEV | MS_NOSUID, "mode=0755") == -1)
+ strerr_diefu1sys(111, "mount tmpfs on " S6_LINUX_INIT_TMPFS) ;
+ }
+ }
+
+ {
+ size_t dirlen = strlen(basedir) ;
+ char fn[dirlen + 1 + (sizeof(RUNIMAGE) > sizeof(ENVSTAGE1) ? sizeof(RUNIMAGE) : sizeof(ENVSTAGE1))] ;
+ memcpy(fn, basedir, dirlen) ;
+ fn[dirlen] = '/' ;
+ memcpy(fn + dirlen + 1, RUNIMAGE, sizeof(RUNIMAGE)) ;
+ if (!hiercopy(fn, S6_LINUX_INIT_TMPFS))
+ strerr_diefu3sys(111, "copy ", fn, " to " S6_LINUX_INIT_TMPFS) ;
+ memcpy(fn + dirlen + 1, ENVSTAGE1, sizeof(ENVSTAGE1)) ;
+ if (envdir(fn, &envmodifs) == -1)
+ strerr_warnwu2sys("envdir ", fn) ;
+ }
+ if (envdumpdir && !env_dump(envdumpdir, 0700, envp))
+ strerr_warnwu2sys("dump kernel environment to ", envdumpdir) ;
+ {
+ int fdr = open_read(LOGFIFO) ;
+ if (fdr == -1) strerr_diefu1sys(111, "open " LOGFIFO) ;
+ fd_close(1) ;
+ if (open(LOGFIFO, O_WRONLY) != 1) strerr_diefu1sys(111, "open " LOGFIFO) ;
+ fd_close(fdr) ;
+ }
+ {
+ static char const *const newargv[5] = { S6_EXTBINPREFIX "s6-svscan", "-st0", "--", S6_LINUX_INIT_TMPFS "/" SCANDIR, 0 } ;
+ char const *newenvp[2] = { 0, 0 } ;
+ pid_t pid ;
+ size_t pathlen = path ? strlen(path) : 0 ;
+ char pathvar[6 + pathlen] ;
+ if (path)
+ {
+ if (setenv("PATH", path, 1) == -1)
+ strerr_diefu1sys(111, "set initial PATH") ;
+ memcpy(pathvar, "PATH=", 5) ;
+ memcpy(pathvar + 5, path, pathlen + 1) ;
+ newenvp[0] = pathvar ;
+ }
+ pid = fork() ;
+ if (pid == -1) strerr_diefu1sys(111, "fork") ;
+ if (!pid) run_stage2(basedir, argv, argc, newenvp, !!path, envmodifs.s, envmodifs.len, initdefault) ;
+ if (fd_copy(2, 1) == -1)
+ strerr_diefu1sys(111, "redirect output file descriptor") ;
+ xpathexec_r(newargv, newenvp, !!path, envmodifs.s, envmodifs.len) ;
+ }
+}
diff --git a/src/init/s6-poweroff.c b/src/init/s6-poweroff.c
deleted file mode 100644
index f9f7747..0000000
--- a/src/init/s6-poweroff.c
+++ /dev/null
@@ -1,7 +0,0 @@
-/* ISC license. */
-
-#undef PROGNAME
-#define PROGNAME "s6-poweroff"
-#undef WHATDEFAULT
-#define WHATDEFAULT 2
-#include "hpr.c"
diff --git a/src/init/s6-reboot.c b/src/init/s6-reboot.c
deleted file mode 100644
index 0bf8785..0000000
--- a/src/init/s6-reboot.c
+++ /dev/null
@@ -1,7 +0,0 @@
-/* ISC license. */
-
-#undef PROGNAME
-#define PROGNAME "s6-reboot"
-#undef WHATDEFAULT
-#define WHATDEFAULT 3
-#include "hpr.c"
diff --git a/src/lib/deps-lib/s6_linux_init b/src/lib/deps-lib/s6_linux_init
new file mode 100644
index 0000000..4c849c7
--- /dev/null
+++ b/src/lib/deps-lib/s6_linux_init
@@ -0,0 +1,4 @@
+s6_linux_init_logouthook.o
+-lskarnet
+${SOCKET_LIB}
+${TAINNOW_LIB}
diff --git a/src/lib/s6_linux_init_logouthook.c b/src/lib/s6_linux_init_logouthook.c
new file mode 100644
index 0000000..810f29b
--- /dev/null
+++ b/src/lib/s6_linux_init_logouthook.c
@@ -0,0 +1,19 @@
+/* ISC license. */
+
+#include <skalibs/djbunix.h>
+#include <skalibs/webipc.h>
+
+#include <s6-linux-init/s6-linux-init.h>
+#include "initctl.h"
+
+int s6_linux_init_logouthook (void)
+{
+ int fd = ipc_stream_nbcoe() ;
+ if (fd < 0) return -1 ;
+ if (!ipc_timed_connect(fd, LOGOUTHOOKD_PATH, 0, 0))
+ {
+ fd_close(fd) ;
+ return -1 ;
+ }
+ return fd ;
+}
diff --git a/src/init/deps-exe/s6-reboot b/src/misc/deps-exe/s6-linux-init-echo
index e7187fe..e7187fe 100644
--- a/src/init/deps-exe/s6-reboot
+++ b/src/misc/deps-exe/s6-linux-init-echo
diff --git a/src/misc/deps-exe/s6-linux-init-logouthookd b/src/misc/deps-exe/s6-linux-init-logouthookd
new file mode 100644
index 0000000..5a7d2fd
--- /dev/null
+++ b/src/misc/deps-exe/s6-linux-init-logouthookd
@@ -0,0 +1,4 @@
+${LIBUTMPS}
+-lskarnet
+${TAINNOW_LIB}
+${SOCKET_LIB}
diff --git a/src/misc/deps-exe/s6-linux-init-umountall b/src/misc/deps-exe/s6-linux-init-umountall
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/misc/deps-exe/s6-linux-init-umountall
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/misc/s6-linux-init-echo.c b/src/misc/s6-linux-init-echo.c
new file mode 100644
index 0000000..483efe2
--- /dev/null
+++ b/src/misc/s6-linux-init-echo.c
@@ -0,0 +1,39 @@
+/* ISC license. */
+
+#include <skalibs/sgetopt.h>
+#include <skalibs/buffer.h>
+#include <skalibs/strerr2.h>
+
+#define USAGE "s6-linux-init-echo [ -n ] [ -s sep ] args..."
+
+int main (int argc, char const *const *argv)
+{
+ char sep = ' ' ;
+ char donl = 1 ;
+ PROG = "s6-linux-init-echo" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ int opt = subgetopt_r(argc, argv, "ns:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'n': donl = 0 ; break ;
+ case 's': sep = *l.arg ; break ;
+ default : strerr_dieusage(100, USAGE) ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ for ( ; *argv ; argv++)
+ if ((buffer_puts(buffer_1small, *argv) < 0)
+ || (argv[1] && (buffer_put(buffer_1small, &sep, 1) < 0)))
+ goto err ;
+ if (donl && (buffer_put(buffer_1small, "\n", 1) < 0)) goto err ;
+ if (!buffer_flush(buffer_1small)) goto err ;
+ return 0 ;
+
+ err:
+ strerr_diefu1sys(111, "write to stdout") ;
+}
diff --git a/src/misc/s6-linux-init-logouthookd.c b/src/misc/s6-linux-init-logouthookd.c
new file mode 100644
index 0000000..cc542ba
--- /dev/null
+++ b/src/misc/s6-linux-init-logouthookd.c
@@ -0,0 +1,69 @@
+/* ISC license. */
+
+#include <skalibs/sysdeps.h>
+
+#ifndef SKALIBS_HASSOPEERCRED
+#error "The SO_PEERCRED option to getsockopt() is required."
+#endif
+
+#include <skalibs/nonposix.h>
+
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
+#include <utmpx.h>
+
+#include <skalibs/allreadwrite.h>
+#include <skalibs/strerr2.h>
+
+#ifndef UT_NAMESIZE
+#define UT_NAMESIZE 32
+#endif
+
+#ifndef UT_HOSTSIZE
+#define UT_HOSTSIZE 256
+#endif
+
+int main (void)
+{
+ struct utmpx *utx ;
+ struct ucred client ;
+ socklen_t len = sizeof(client) ;
+ ssize_t r ;
+ char c ;
+
+ close(1) ;
+ if (getsockopt(0, SOL_SOCKET, SO_PEERCRED, &client, &len) == -1)
+ strerr_diefu1sys(111, "getsockopt") ;
+
+ /* Only take connections from root. */
+ if (client.uid) return 1 ;
+
+ /* Wait for the client to die. */
+ r = fd_read(0, &c, 1) ;
+ if (r < 0) strerr_diefu1sys(111, "read from stdin") ;
+ if (r) strerr_dief1x(2, "client attempted to write") ;
+
+ /* Clean up utmpx record for the client's pid, then exit. */
+ for (;;)
+ {
+ errno = 0 ;
+ utx = getutxent() ;
+ if (!utx) break ;
+ if (utx->ut_pid == client.pid) goto gotit ;
+ }
+ if (errno) strerr_diefu1sys(111, "getutxent") ;
+ return 0 ;
+
+ gotit:
+ utx->ut_type = DEAD_PROCESS ;
+ memset(utx->ut_user, 0, UT_NAMESIZE) ;
+ memset(utx->ut_host, 0, UT_HOSTSIZE) ;
+ utx->ut_tv.tv_sec = 0 ;
+ utx->ut_tv.tv_usec = 0 ;
+ setutxent() ;
+ if (!pututxline(utx)) strerr_diefu1sys(111, "pututxline") ;
+ return 0 ;
+}
diff --git a/src/misc/s6-linux-init-umountall.c b/src/misc/s6-linux-init-umountall.c
new file mode 100644
index 0000000..24b22fc
--- /dev/null
+++ b/src/misc/s6-linux-init-umountall.c
@@ -0,0 +1,70 @@
+/* ISC license. */
+
+#include <string.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
+
+int main (int argc, char const *const *argv)
+{
+ stralloc mountpoints[MAXLINES] ;
+ char buf[BUFSIZE] ;
+ buffer b ;
+ stralloc sa = STRALLOC_ZERO ;
+ unsigned int line = 0 ;
+ 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) ;
+ 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++ ;
+ }
+ fd_close(fd) ;
+ stralloc_free(&sa) ;
+ if (r < 0) strerr_diefu1sys(111, "read /proc/mounts") ;
+
+ while (line--)
+ if (umount(mountpoints[line].s) == -1)
+ {
+ e++ ;
+ strerr_warnwu2sys("umount ", mountpoints[line].s) ;
+ }
+ return e ;
+}
diff --git a/src/shutdown/deps-exe/s6-linux-init-hpr b/src/shutdown/deps-exe/s6-linux-init-hpr
new file mode 100644
index 0000000..0c4376c
--- /dev/null
+++ b/src/shutdown/deps-exe/s6-linux-init-hpr
@@ -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..01c9db2
--- /dev/null
+++ b/src/shutdown/deps-exe/s6-linux-init-shutdownd
@@ -0,0 +1,5 @@
+-ls6
+${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.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..3ede92d
--- /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] = { "Shpr"[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..e63ddea
--- /dev/null
+++ b/src/shutdown/hpr_wall.c
@@ -0,0 +1,41 @@
+/* ISC license. */
+
+#include <string.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 ;
+ int fd ;
+ struct utmpx *utx = getutxent() ;
+ if (!utx) break ;
+ if (utx->ut_type != USER_PROCESS) continue ;
+ linelen = strnlen(utx->ut_line, UT_LINESIZE) ;
+ memcpy(tty + 5, utx->ut_line, linelen) ;
+ tty[5 + linelen] = 0 ;
+ fd = open_append(tty) ;
+ if (fd == -1) continue ;
+ allwrite(fd, msg, n) ;
+ fd_close(fd) ;
+ }
+ endutxent() ;
+}
diff --git a/src/shutdown/s6-linux-init-hpr.c b/src/shutdown/s6-linux-init-hpr.c
new file mode 100644
index 0000000..886f10b
--- /dev/null
+++ b/src/shutdown/s6-linux-init-hpr.c
@@ -0,0 +1,120 @@
+/* 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 "s6-linux-init-hpr [ -h | -p | -r ] [ -d | -w ] [ -W ] [ -f ]"
+
+int main (int argc, char const *const *argv)
+{
+ int what = 0 ;
+ int force = 0 ;
+ int dowtmp = 1 ;
+ int dowall = 1 ;
+ PROG = "s6-linux-init-hpr" ;
+
+ {
+ 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 (!what)
+ strerr_dief1x(100, "one of the -h, -p or -r options must be given") ;
+
+ 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 ;
+
+/* glibc multilib can go fuck itself */
+#ifdef __WORDSIZE_TIME64_COMPAT32
+ {
+ struct timeval tv ;
+ if (!timeval_from_tain(&tv, &STAMP))
+ strerr_warnwu1sys("timeval_from_tain") ;
+ utx.ut_tv.tv_sec = tv.tv_sec ;
+ utx.ut_tv.tv_usec = tv.tv_usec ;
+ }
+#else
+ if (!timeval_from_tain(&utx.ut_tv, &STAMP))
+ strerr_warnwu1sys("timeval_from_tain") ;
+#endif
+
+ 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/s6-linux-init-shutdown.c b/src/shutdown/s6-linux-init-shutdown.c
new file mode 100644
index 0000000..2779537
--- /dev/null
+++ b/src/shutdown/s6-linux-init-shutdown.c
@@ -0,0 +1,265 @@
+/* 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 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(&tmthen, tain_secp(&STAMP), 1))
+ strerr_diefu1sys(111, "break down current time into struct tm") ;
+ tmthen.tm_hour = hour ;
+ tmthen.tm_min = minute ;
+ tmthen.tm_sec = 0 ;
+ 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") ;
+ }
+ when->sec = taithen ;
+ when->nano = 0 ;
+}
+
+static void parse_mins (tain_t *when, char const *s)
+{
+ unsigned int mins ;
+ if (!uint0_scan(s, &mins)) dieusage() ;
+ tain_addsec_g(when, mins * 60) ;
+}
+
+static inline void parse_time (tain_t *when, char const *s)
+{
+ if (!strcmp(s, "now")) tain_copynow(when) ;
+ else if (s[0] == '+') parse_mins(when, s+1) ;
+ else if (strchr(s, ':')) parse_hourmin(when, s) ;
+ else parse_mins(when, s) ;
+}
+
+
+ /* 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..7f86e66
--- /dev/null
+++ b/src/shutdown/s6-linux-init-shutdownd.c
@@ -0,0 +1,309 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.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/direntry.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/iopause.h>
+#include <skalibs/skamisc.h>
+
+#include <execline/config.h>
+
+#include <s6/s6-supervise.h>
+
+#include <s6-linux-init/config.h>
+#include "defaults.h"
+#include "initctl.h"
+#include "hpr.h"
+
+#define STAGE4_FILE "stage 4"
+#define SCANPREFIX S6_LINUX_INIT_TMPFS "/" SCANDIR "/"
+#define SCANPREFIXLEN (sizeof(SCANPREFIX) - 1)
+#define DOTPREFIX ".s6-linux-init-shutdownd:"
+#define DOTPREFIXLEN (sizeof(DOTPREFIX) - 1)
+#define DOTSUFFIX ":XXXXXX"
+#define DOTSUFFIXLEN (sizeof(DOTSUFFIX) - 1)
+
+#define USAGE "s6-linux-init-shutdownd [ -c basedir ] [ -g gracetime ]"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+static char const *basedir = BASEDIR ;
+
+struct at_s
+{
+ int fd ;
+ char const *name ;
+} ;
+
+static int renametemp (char const *s, mode_t mode, void *data)
+{
+ struct at_s *at = data ;
+ (void)mode ;
+ return renameat(at->fd, at->name, at->fd, s) ;
+}
+
+static int mkrenametemp (int fd, char const *src, char *dst)
+{
+ struct at_s at = { .fd = fd, .name = src } ;
+ return mkfiletemp(dst, &renametemp, 0700, &at) ;
+}
+
+static inline void run_stage3 (char const *basedir, char const *const *envp)
+{
+ pid_t pid ;
+ size_t basedirlen = strlen(basedir) ;
+ char stage3[basedirlen + sizeof("/scripts/" STAGE3)] ;
+ char const *stage3_argv[2] = { stage3, 0 } ;
+ memcpy(stage3, basedir, basedirlen) ;
+ memcpy(stage3 + basedirlen, "/scripts/" STAGE3, sizeof("/scripts/" 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) ;
+}
+
+static inline void prepare_shutdown (buffer *b, tain_t *deadline, unsigned int *grace_time)
+{
+ uint32_t u ;
+ char pack[TAIN_PACK + 4] ;
+ ssize_t r = sanitize_read(buffer_get(b, pack, TAIN_PACK + 4)) ;
+ 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 ;
+}
+
+static inline void handle_fifo (buffer *b, char *what, tain_t *deadline, unsigned int *grace_time)
+{
+ for (;;)
+ {
+ char c ;
+ ssize_t r = sanitize_read(buffer_get(b, &c, 1)) ;
+ if (r == -1) strerr_diefu1sys(111, "read from pipe") ;
+ else if (!r) break ;
+ switch (c)
+ {
+ case 'S' :
+ case 'h' :
+ case 'p' :
+ case 'r' :
+ *what = c ;
+ prepare_shutdown(b, deadline, grace_time) ;
+ break ;
+ case 'c' :
+ *what = 'S' ;
+ 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, char what)
+{
+ 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_BINPREFIX "s6-linux-init-umountall }\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") ;
+ 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) ;
+}
+
+static inline void unsupervise_tree (void)
+{
+ static char const *except[] =
+ {
+ LOGGER_SERVICEDIR,
+ SHUTDOWND_SERVICEDIR,
+ /* EARLYGETTY_SERVICEDIR, */
+ 0
+ } ;
+ DIR *dir = opendir(S6_LINUX_INIT_TMPFS "/" SCANDIR) ;
+ int fdd ;
+ if (!dir)
+ strerr_diefu1sys(111, "opendir " S6_LINUX_INIT_TMPFS "/" SCANDIR) ;
+ fdd = dirfd(dir) ;
+ if (fdd == -1)
+ strerr_diefu1sys(111, "dir_fd " S6_LINUX_INIT_TMPFS "/" SCANDIR) ;
+ for (;;)
+ {
+ char const *const *p = except ;
+ direntry *d ;
+ errno = 0 ;
+ d = readdir(dir) ;
+ if (!d) break ;
+ if (d->d_name[0] == '.') continue ;
+ for (; *p ; p++) if (!strcmp(*p, d->d_name)) break ;
+ if (!*p)
+ {
+ size_t dlen = strlen(d->d_name) ;
+ char fn[SCANPREFIXLEN + DOTPREFIXLEN + dlen + DOTSUFFIXLEN + 1] ;
+ memcpy(fn, SCANPREFIX DOTPREFIX, SCANPREFIXLEN + DOTPREFIXLEN) ;
+ memcpy(fn + SCANPREFIXLEN + DOTPREFIXLEN, d->d_name, dlen) ;
+ memcpy(fn + SCANPREFIXLEN + DOTPREFIXLEN + dlen, DOTSUFFIX, DOTSUFFIXLEN + 1) ;
+ if (mkrenametemp(fdd, d->d_name, fn + SCANPREFIXLEN) == -1)
+ {
+ strerr_warnwu4sys("rename " SCANPREFIX, d->d_name, " to something based on ", fn) ;
+ unlinkat(fdd, d->d_name, 0) ;
+ /* if it still fails, too bad, it will restart in stage 4 and race */
+ }
+ else
+ s6_svc_writectl(fn, S6_SUPERVISE_CTLDIR, "dx", 2) ;
+ }
+ }
+ if (errno)
+ strerr_diefu1sys(111, "readdir " S6_LINUX_INIT_TMPFS "/" SCANDIR) ;
+ dir_close(dir) ;
+}
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ char what = 'S' ;
+ unsigned int grace_time = 3000 ;
+ tain_t deadline ;
+ int fdr, fdw ;
+ buffer b ;
+ char buf[64] ;
+ 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 } ;
+ execve(stage4_argv[0], (char **)stage4_argv, (char *const *)envp) ;
+ if (errno != ENOENT)
+ strerr_warnwu2sys("exec ", stage4_argv[0]) ;
+ }
+
+ fdr = open_read(SHUTDOWND_FIFO) ;
+ if (fdr == -1 || coe(fdr) == -1)
+ strerr_diefu3sys(111, "open ", SHUTDOWND_FIFO, " for reading") ;
+ fdw = open_write(SHUTDOWND_FIFO) ;
+ if (fdw == -1 || coe(fdw) == -1)
+ strerr_diefu3sys(111, "open ", SHUTDOWND_FIFO, " for writing") ;
+ if (sig_ignore(SIGPIPE) == -1)
+ strerr_diefu1sys(111, "sig_ignore SIGPIPE") ;
+ buffer_init(&b, &buffer_read, fdr, buf, 64) ;
+ tain_now_g() ;
+ tain_add_g(&deadline, &tain_infinite_relative) ;
+
+ for (;;)
+ {
+ iopause_fd x = { .fd = fdr, .events = IOPAUSE_READ } ;
+ int r = iopause_g(&x, 1, &deadline) ;
+ if (r == -1) strerr_diefu1sys(111, "iopause") ;
+ if (!r)
+ {
+ run_stage3(basedir, envp) ;
+ tain_now_g() ;
+ if (what != 'S') break ;
+ tain_add_g(&deadline, &tain_infinite_relative) ;
+ continue ;
+ }
+ if (x.revents & IOPAUSE_READ)
+ handle_fifo(&b, &what, &deadline, &grace_time) ;
+ }
+
+ fd_close(fdw) ;
+ fd_close(fdr) ;
+ fd_close(1) ;
+ if (open("/dev/console", O_WRONLY) != 1)
+ strerr_diefu1sys(111, "open /dev/console for writing") ;
+ if (fd_copy(2, 1) == -1) strerr_warnwu1sys("fd_copy") ;
+
+
+ /* The end is coming! */
+
+ prepare_stage4(basedir, what) ;
+ unsupervise_tree() ;
+ 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 ;
+}
diff --git a/tools/gen-deps.sh b/tools/gen-deps.sh
index 6383ac2..c6d6971 100755
--- a/tools/gen-deps.sh
+++ b/tools/gen-deps.sh
@@ -7,6 +7,8 @@ echo '# This file has been generated by tools/gen-deps.sh'
echo '#'
echo
+internal_libs=
+
for dir in src/include/${package} src/* ; do
for file in $(ls -1 $dir | grep -- \\.h$) ; do
{
@@ -70,8 +72,12 @@ for dir in $(ls -1 src | grep -v ^include) ; do
echo else
echo "lib${file}.a.xyzzy:$(echo "$deps" | sed 's/\.o/.lo/g')"
echo endif
- echo "lib${file}.so.xyzzy: EXTRA_LIBS :=$libs"
- echo "lib${file}.so.xyzzy:$(echo "$deps" | sed 's/\.o/.lo/g')"
+ if grep -F "LIB_DEFS :=" package/targets.mak | grep -qF "$file" ; then
+ echo "lib${file}.so.xyzzy: EXTRA_LIBS :=$libs"
+ echo "lib${file}.so.xyzzy:$(echo "$deps" | sed 's/\.o/.lo/g')"
+ else
+ internal_libs="$internal_libs lib${file}.a.xyzzy"
+ fi
done
for file in $(ls -1 src/$dir/deps-exe) ; do
@@ -91,3 +97,4 @@ for dir in $(ls -1 src | grep -v ^include) ; do
echo "$file: src/$dir/$file.o$deps"
done
done
+echo "INTERNAL_LIBS :=$internal_libs"