diff options
66 files changed, 3397 insertions, 1095 deletions
@@ -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 @@ -4,3 +4,4 @@ Main author: Thanks to: Colin Booth <colin@heliocat.net> multiplexd <multiplexd@gmx.com> + Luis Ressel <aranea@aixah.de> @@ -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 ------------- @@ -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: @@ -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. + @@ -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 @@ -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 ? </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 → +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 ? </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> ? </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 && 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 ? </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> : 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> : poweroff. The command will order a power off, which means -sending a SIGUSR1 to process 1. </li> - <li> <tt>-r</tt> : reboot. The command will order a reboot, which means -sending a SIGINT to process 1. </li> - <li> <tt>-f</tt> : 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> : do not output a trailing newline. </li> + <li> <tt>-s</tt> <em>sep</em> : 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> : 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> : halt. The system will be shut down, but the power will remain up. </li> + <li> <tt>-p</tt> : poweroff. The system will be shut down and the power turned off. </li> + <li> <tt>-r</tt> : reboot. The system will reboot. </li> + <li> <tt>-d</tt> : Do not write a wtmp shutdown entry. </li> + <li> <tt>-w</tt> : Only write a wtmp shutdown entry; do not actually shut down +the system. </li> + <li> <tt>-W</tt> : 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> <em>basedir</em> : 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> <em>tmpfsdir</em> : 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> <em>execline_bindir</em> : 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> <em>log_uid</em> : the catch-all -logger will run with the uid <em>log_uid</em>. Default is 0. </li> <p /> - - <li> <tt>-g</tt> <em>log_gid</em> : the catch-all -logger will run with the gid <em>log_gid</em>. Default is 0. </li> <p /> - - <li> <tt>-U</tt> : 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> <em>log_user</em> : the catch-all +logger will run as the <em>log_user</em> user. Default is <tt>root</tt>. </li> <p /> <li> <tt>-G</tt> <em>early_getty</em> : 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> <em>initscript</em> : <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> : 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> <em>shutdownscript</em> : -<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> <em>initial_path</em> : 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> : 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> : 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> <em>initial_path</em> : 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> <em>initial_umask</em> : 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> <em>timestamp_style</em> : 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> <em>dev_style</em> : 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> <em>slashdev</em> : 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> <em>env_store</em> : 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> <em>initial_envvar</em> : 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> : 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> <em>finalsleeptime</em> : 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> <em>initdefault</em> : 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> : 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> : 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> <em>utmp_user</em> : 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>[ now | [+]<em>mins</em> | <em>hh</em>:<em>mm</em> ] +</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> : 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> <em>sec</em> : 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> : 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> : at the end of the shutdown sequence, halt the system. </li> + <li> <tt>-p</tt> : 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> : at the end of the shutdown sequence, reboot the system. </li> + <li> <tt>-f</tt> : ignored. </li> + <li> <tt>-F</tt> : ignored. </li> + <li> <tt>-c</tt> l: 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 <em>basedir</em></tt> : 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 <em>gracetime</em></tt> : 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> <em>basedir</em> : 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> <em>initial_path</em> : the initial value for +the PATH environment variable. </li> + <li> <tt>-s</tt> <em>env_store</em> : the place where to dump +kernel environment variables. </li> + <li> <tt>-m</tt> <em>initial_umask</em> : the initial file umask. </li> + <li> <tt>-d</tt> <em>slashdev</em> : 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> <em>initdefault</em> : 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> : instead of unmounting <tt>/run</tt> and mounting +a tmpfs on it, just remount <tt>/run</tt>. </li> + <li> <tt>-N</tt> : 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> : 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> : 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> : reboot. The command will order a reboot, which means -sending a SIGINT to process 1. </li> - <li> <tt>-f</tt> : 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> : 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> : poweroff. The command will order a power off, which means -sending a SIGUSR1 to process 1. </li> - <li> <tt>-r</tt> : reboot. The command will order a reboot, which means -sending a SIGINT to process 1. This is the default. </li> - <li> <tt>-f</tt> : 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 ? </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, ×tamp_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" |