refactoring for more flexible config
authorRalph Ronnquist <ralph.ronnquist@gmail.com>
Sun, 27 Mar 2022 07:11:43 +0000 (18:11 +1100)
committerRalph Ronnquist <ralph.ronnquist@gmail.com>
Sun, 27 Mar 2022 07:11:43 +0000 (18:11 +1100)
functions
overlay-boot
overlay-boot.8.adoc [new file with mode: 0644]
overlay-init [new file with mode: 0755]
overlay-postmount [new file with mode: 0755]
overlay-premount [new file with mode: 0755]
overlay-stop

index 67ea3ad51b143476c0ba5f3a82bfaa52d438c4ee..2c819fae3454b4ab96854aa508cb2c16a8657511 100644 (file)
--- a/functions
+++ b/functions
@@ -1,18 +1,69 @@
-# This file implements common functions for all boot methods
+# This file implements common functions for all boot scripts
 
+# Rerun with sudo if needed
+[ $(id -u) = 0 ] || exec sudo $0 $@
+
+# Function to write a message and exit with error code
 die() {
     echo "$*" >&2
     exit 1
 }
 
+# Function to setup subhost name and log file
+subhost_name() {
+    CONFIG="$1"
+    [ -r "$CONFIG" ] || die "Cannot use $CONFIG"
+    config NAME "$(basename $CONFIG .conf)"
+    config LOG /tmp/oly-$NAME.log
+}
+
+# Function to set up all subhost configuration
+subhost_config() {
+
+    config BASE
+    BASE="$(cd $(dirname $CONFIG); realpath $BASE)"
+    [ -z "$BASE" ] && die "BASE is unset; bogus $CONFIG ?"
+    [ -d "$BASE" ] || die "$BASE is not a directory; bogus $CONFIG ?"
+    cd "$BASE" || die "$BASE is inaccessible"
+
+    config CABLES ""
+    config LIVE "$BASE/live"
+    config UPPER "$BASE/root"
+    config WORK "$BASE/work"
+    config LOWER "/"
+    config START "!networking ssh"
+    config PREMOUNT "$PROGRAMDIR/overlay-premount"
+    config POSTMOUNT "$PROGRAMDIR/overlay-postmount"
+    config INIT "$PROGRAMDIR/overlay-init"
+    config RAM_SIZE 50M
+}
+
+# function to reverse the $* words
+reverse() {
+    local OUT=""
+    for w in $* ; do OUT="$w $OUT" ; done
+    echo "${OUT% }"
+}
+
 # grab and set a configuration variable
 # $1 = variable, [ $2 = default .. error otherwise ]
 config() {
-    eval $1="'$(sed "/^$1=.*/{s|^$1=||;b};d" $CONFIG)'"
-    [ -z "$(eval echo "\$$1")" ] || return 0
-    [ $# -lt 2 ] && die "Missing $1=... in $CONFIG"
-    eval $1="'$2'"
-    eval echo "$1=\$$1"
+    local V W
+    read V <<EOF
+$(sed "/^$1=.*/{s|^$1=||;s|^\\s*||;s|\\s*\$||;b};d" $CONFIG)
+EOF
+    if [ -z "$V" ] ; then
+       [ $# -lt 2 ] && die "Missing $1=... in $CONFIG"
+       V="$2" # use the given default
+    elif [ -z "${V##!*}" ] ; then
+       read W <<EOF
+$(${V#!})
+EOF
+       [ -z "$W" ] && die "bad $1 config: $V"
+       V="$W"
+    fi
+    eval $1="'$V'"
+    eval echo "$1=$V" >&2
 }
 
 # Install a default $1/etc/network/interfaces on the subhost root $1
@@ -57,67 +108,33 @@ setup_veth_cables() {
     done
 }
 
-# (name live system root work)
-# Set up an overlay fmr $name on $live, with a new tmpfs on its /run,
+# Set up an overlay for $name on $live, with a new tmpfs on its /run,
 # and "install" a "reaper" as the upcoming pid 1
 setup_overlay() {
-    local LIVE="$2" LOWER="$3" UPPER="$4" ROOT
+    local NAME="$1" LIVE="$2" LOWER="$3" UPPER="$4" WORK="$5"
 
-    if grep -q "$1 $2" /proc/mounts ; then
-       die "$1 is already mounted"
-    fi
+    echo setup_overlay "$NAME" "$LIVE" "$LOWER" "$UPPER" "$WORK"
 
-    if [ -f "${UPPER%% *}" ] ; then
-       if [ -x "${UPPER%% *}" ] ; then
-           echo "${UPPER%% *} appears to be executable" >&2
-           # Giving a program/script as UPPER= asks for running this
-           # first, to make a root filesystem available. The script takes
-           # ACTION "setup" and "teardown", and on "setup" it must tell
-           # where the ROOT is set up.
-           ROOT="$(env ACTION=setup $UPPER)"
-           if [ ! -d "$ROOT" ] ; then
-               # setup failed
-               die "root setup failed: $UPPER"
-           fi
-           UPPER="$ROOT"
-           ## Now falling down to "normal overlay" setup
-       else
-           die "${UPPER%% *} (root setup program/script) is not executable"
-       fi
+    if grep -qE "^[^ ]+ $LIVE " /proc/mounts ; then
+       die "$LIVE already has a mount"
     fi
 
-    # LIVE is the same as LOWER then skip the overlay; just assume
-    # a proper chroot system exists at LIVE.
-    if [ "$LIVE" != "$LOWER" ] ; then
+    [ -d "$UPPER" ] || die "UPPER=$UPPER is not a directory"
+    [ -d "$LOWER" ] || die "LOWER=LOWPER is not a directory"
+    [ -d "$LIVE" ] || die "LOWER=LOWPER is not a directory"
+    [ -x "${PREMOUNT%% *}" ] || die "PREMOUNT=${PREMOUNT%% *} not executable"
+    [ -f "${PREMOUNT%% *}" ] || die "PREMOUNT='$PREMOUNT' is not a command"
+    [ -x "${POSTMOUNT%% *}" ] || \
+       die "POSTMOUNT=${POSTMOUNT%% *} not executable"
+    [ -f "${POSTMOUNT%% *}" ] || \
+       die "POSTMOUNT='$POSTMOUNT' is not a command"
+
+    # UPPER is the same as LOWER then skip the overlay mount
+    if [ "$UPPER" != "$LOWER" ] ; then
        # sanity check
        [ -d "$WORK" ] || die "WORK=$WORK is not a directory"
-       [ -d "$UPPER" ] || die "UPPER=$UPPER is not a directory"
-       [ -d "$LOWER" ] || die "LOWER=LOWPER is not a directory"
-       [ -d "$LIVE" ] || die "LOWER=LOWPER is not a directory"
-       # setup $UPPER/dev
-       mkdir -p "$UPPER/dev"
-       mount -t tmpfs -osize=50M tmpfs "$UPPER/dev"
-       mknod -m 622 "$UPPER/dev/console" c 5 1
-       mknod -m 666 "$UPPER/dev/null" c 1 3
-       mknod -m 666 "$UPPER/dev/zero" c 1 5
-       mknod -m 666 "$UPPER/dev/ptmx" c 5 2
-       mknod -m 666 "$UPPER/dev/tty" c 5 0
-       mknod -m 444 "$UPPER/dev/random" c 1 8
-       mknod -m 444 "$UPPER/dev/urandom" c 1 9
-       chown root:tty "$UPPER/dev/console"
-       chown root:tty "$UPPER/dev/ptmx"
-       chown root:tty "$UPPER/dev/tty"
-       ln -sTf /proc/self/fd "$UPPER/dev/fd"
-       ln -sTf /proc/self/fd/0 "$UPPER/dev/stdin"
-       ln -sTf /proc/self/fd/1 "$UPPER/dev/stdout"
-       ln -sTf /proc/self/fd/2 "$UPPER/dev/stderr"
-       ln -sTf /proc/kcore "$UPPER/dev/core"
-       mkdir "$UPPER/dev/shm"
-       mkdir "$UPPER/dev/pts"
-       chmod 1777 "$UPPER/dev/shm"
-
-       # all good so far ; now avoid using the host's networking setup
-       setup_networking "$UPPER"
+
+       env CONFIG="$CONFIG" $PREMOUNT "$UPPER"
 
        OLY="-olowerdir=$3,upperdir=$UPPER,workdir=$5"
        if ! mount -t overlay "$OLY" $1 $2 ; then
@@ -125,36 +142,25 @@ setup_overlay() {
            umount "$UPPER/run"
            die "Cannot set up the overlay mount $2"
        fi
+    elif [ "$LIVE" != "$UPPER" ] ; then
+       # With UPPER = LOWER we rather make a bind mount to LIVE
+       env CONFIG="$CONFIG" $PREMOUNT "$UPPER"
+       mount --bind $UPPER $LOWER
     fi
 
-    echo "Installing $OVERLAYDIR/reaper to $LIVE/.reaper"
-    cp -p $OVERLAYDIR/reaper $LIVE/.reaper
-}
-
-start_services() {
-    for S in "$@" ; do
-       service $S start
-    done
+    env CONFIG="$CONFIG" $POSTMOUNT "LIVE" "$UPPER" 
 }
 
-# find the upperdir option for an overlay mount line
-getupper() {
-    sed 's/.*upperdir=\([^,]*\).*/\1/'
-}
-
-# Check if $1 is "live" and echo the
-# unshare and reaper process pids
+# Find the "unshare" process for $1 and echo the its pid and the pids
+# of its child processes.
 is_live() {
     local NAME=$1
     local USPID="$(pgrep -f "unshare.* $NAME ")"
     [ -z "$USPID" ] && return 1
-    echo $USPID $(pgrep -f ".reaper $NAME")
+    echo "$USPID $(ps -hopid --ppid=$USPID)"
 }
 
+# Find all overlay-boot processes and list their config files
 list_running() {
-    for C in $(pgrep -a overlay-boot | awk '{print $4}') ; do
-       eval NAME="$(sed "/^NAME=.*/{s|^NAME=||;b};d" $C)"
-       [ -z "$NAME" ] && NAME=$(basename $C .conf)
-       echo $NAME
-    done
+    pgrep -a overlay-boot | awk '{print $4}'
 }
index e94620ec70746b22f14fc61373811b9ef39e8090..d635da4347739db34020cd6856382d5c6f7827f9 100755 (executable)
@@ -1,88 +1,35 @@
 #!/bin/sh
 #
-# This boot method runs a service subhost with a root filesystem that
-# is an overlay of the subhost's root and an OS root. The service
-# subhost is defined by a configuration file named on teh command line
+# This boot scripts runs a service subhost as defined by the
+# configuration file named on the command line.
+# See "man overlay-boot" for details.
 
-OVERLAYDIR="$(dirname $(realpath $0))"
+PROGRAMDIR="$(dirname $(realpath $0))"
+. $PROGRAMDIR/functions
 
-[ $(id -u) = 0 ] || exec sudo $0 $@
-. $OVERLAYDIR/functions $*
-
-CONFIG="$1"
-[ -r "$CONFIG" ] || die "Missing configuration $CONFIG"
-
-config NAME $(basename $1 .${1##*.})
-config LOG /tmp/oly-$NAME.log
+subhost_name $1
 
 if [ -z "$UNSHARED" ] ; then
-    # Pre-unsharing:
-    #
-    # Create the network namespace for the subhost, then trigger
-    # detached re-run with unshared mount namespace
-    [ -r /run/netns/$NAME ] || {
+    if [ ! -r /run/netns/$NAME ] ; then
        ip netns add $NAME
-       ip netns exec $NAME ip link set lo up
-    }
+       ip netns exec $NAME ip link set lo up || exit 1
+    fi
     exec env UNSHARED=yes unshare -m $0 $@ > $LOG 2>&1 &
     echo "Logging to $LOG" >&2
     exit 0
 fi
 
-config BASE
-BASE="$(cd $(dirname $CONFIG); realpath $BASE)"
-
-[ -z "$BASE" ] && die "BASE is unset; bogus $CONFIG ?"
-[ -d "$BASE" ] || die "$BASE is not a directory; bogus $CONFIG ?"
-cd "$BASE" || die "$BASE is inaccessible"
-
-config LIVE "$BASE/live"
-config UPPER "$BASE/root"
-config WORK "$BASE/work"
-config LOWER "/"
-config CABLES ""
-config START "networking ssh"
-config SUBSHELL /bin/sh
-
-# Setup virtual cabling
+subhost_config
 setup_veth_cables $NAME $CABLES
-
-# Set up the mount for this subhost, including a new tmpfs on its /run
-# and a default $UPPER/etc/network/interfaces if needed
-echo setup_overlay "$NAME" "$LIVE" "$LOWER" "$UPPER" "$WORK"
 setup_overlay "$NAME" "$LIVE" "$LOWER" "$UPPER" "$WORK"
 
 exithandler() {
     ip netns del $NAME
-    [ "$LOWER" != "$LIVE" ] && umount -R "$LIVE"
-    [ -f "${UPPER%% *}" ] && [ -x "${UPPER%% *}" ] && \
-       env ACTION=teardown $UPPER
+    [ "$UPPER" != "$LIVE" ] && umount -R "$LIVE"
 }
 trap "exithandler" 0
 
 CMD="unshare -fp --mount-proc -i -u ip netns exec $NAME chroot $LIVE /bin/sh"
 echo "$CMD"
-
-config RAM_SIZE 50M
-
-cat <<EOF | $CMD
-set -x
-mount -t proc proc /proc
-mount -t devpts devpts /dev/pts
-mount -t sysfs sysfs /sys
-if [ "$RAM_SIZE" != "none" ] && ! grep -q '/run tmpfs' /proc/mounts ; then
-    mount -t tmpfs -osize=$RAM_SIZE,mode=755 tmpfs /run
-fi
-for srv in $START ; do service \$srv start ; done
-dummy_service() {
-    [ \$# -gt 3 ] && return 0
-    echo "Starting dummy service" >&2
-    set +x
-    [ -p /run/dummy_service ] || mkfifo /run/dummy_service
-    ( printf dummy_service > /proc/self/comm ; read X < /run/dummy_service ) &
-    set -x
-}
-dummy_service /proc/*/comm
-exec /.reaper $NAME
-EOF
+env CONFIG="$CONFIG" $INIT | $CMD
 echo "EXITED $CMD"
diff --git a/overlay-boot.8.adoc b/overlay-boot.8.adoc
new file mode 100644 (file)
index 0000000..9e39eec
--- /dev/null
@@ -0,0 +1,189 @@
+overlay-boot(8)
+===============
+:doctype: manpage
+:revdate: {sys:date "+%Y-%m-%d %H:%M:%S"}
+:COLON: :
+:EQUALS: =
+
+NAME
+----
+overlay-boot - Start a subhost with overlay root filesystem.
+
+SYNOPSIS
+--------
+*overlay-boot* _conf_
+
+DESCRIPTION
+-----------
+*overlay-boot* is the main script on a small collection of
+administration scripts for containerizing services with minimal ado.
+The script starts a "subhost" with a dedicated network namespace, and
+the mount and pid namespaces separated from the main host by means of
++unshare+. A subhost root file system may in particular be set up as
+an overlay of the main host filesystem to keep the specifics of a
+service distinctly separate from the main host while sharing files
+wherever sensible.
+
+A subhost is started by identifyinf its configuration file on the
+command line for *overlay-boot*. The configuration file is a plain
+text file with a small collection of "variables" that tell how the
+subhost is set up. When all is good, *overlay-boot* spawns a
+subprocess that invokes a command shell within an chroot into
+"unshared" subhost root filesystem, all similar to the bootup of any
+odd computer.
+
+The subhost execution environment may be "entered" to perform
+adminstrative tasks with *overlay-go*, and it is later stopped with
+*overlay-stop*.
+
+OPTIONS
+-------
+
+An overlay-boot subhost is defined in the configuration file, which is
+a plain text file with a number of variable assignments. Each
+assignment is written with the varable name flush left and immediately
+followed by an equal sign, The rest of that line (ignoring leading and
+trailing spaces) is its value, or if that value startes with an
+exclamation mark, then the line is a command to run so as to generate
+the value. See examples below.
+
+*NAME*::
+
+This variable declares a short name for the subhost, and should be no
+more than 12 printable ascii characters. The base name of the
+configuration file is used by default. I.e., a configuration file
+named +foo.conf+ by default names its subhost +foo+ unless there is a
++NAME+ variable says differently.
+
+*BASE*::
+
+This variable declares a pathname for a directory that is considered
+to be a "base" for the subhost setup. This is the only required
+variable.
+
+*CABLES*::
+
+This variable declares the subhost networking in terms of its virtual
+cables. The value is a space separated list of "virtual cable
+specifiers", each consisting of an equal sign optionally with a bridge
+name to the left and optinally a MAC address to the right. See the
+section on Networking below for more details.
+
+INIT::
+
+This variable is a command line to run, with envirnment variable
+CONFIG set, for producing the initial commands to the running subhost,
+similar to the initrd phase of a computer bootup. The default value
+for this variable is +overlay-init+.
+
+*LIVE*::
+
+This variable nominates the mount point for the running subhost's root
+file system. It defaults to +$BASE/live+ The nominated directory must
+exist, and depending on the directory pathnames in the +UPPER+ and
++LOWER+ variables, the subhost root filesystem is either of a
+pre-mounted directory, bind mounted or overlay mounted directory. See
+the details of this with the UPPER variable below.
+
+*LOG*::
+
+This variable nominates the logfile to use by +overlay-boot+ when
+running the subhost. The default is +/tmp/overlay-$NAME.log+.
+
+*LOWER*::
+
+This variable nominates the "lower" filesystem of an overlay mount.
+This will be accessed read-only, an it is intended to be the operating
+system root file system. The default is +/+, i.e. the main host root
+filesystem. When overlay is not desired, then LOWER should be the smae
+as UPPER.
+
+*POSTMOUNT*::
+
+This variable is a command line to run, with envirnment variable
+CONFIG set, just after the setup of the subhost root filesystem and
+before the services are started. The default for this variable is
++overlay-postmount+
+
+*PREMOUNT*::
+
+This variable is a command line to run, with envirnment variable
+CONFIG set, just before the setup of the subhost root filesystem and
+before the services are started. The default for this variable is
++overlay-premount+
+
+*UPPER*::
+
+This variable nominates the "upper" filesystem for an overlay mount.
+This will be accessed read-write and it constitutes the "private"
+files of the subhost.
+
+If UPPER is different from LOWER, then the root file system is set up
+as an overlay mount. In that case WORK (below) must be defined as a
+directory outside of but on the same mount as UPPER.
+
+If UPPER is the same as LOWER, then the subhost root filesystem is not
+setup as an overla. Rather it is set up as a bind mount, if UPPER is
+different from LIVE, or just as a pre-mounted LIVE filesystem.
+
+*WORK*::
+
+This variable nominates the "work" directory for an overlay mount. It
+has to be a writable directory on the same mount device as the UPPER
+directory.
+
+*START*::
+
+This variable tells which services should be started on the
+overlay-boot startup. The default value is +networking ssh+.
+
+Note that if no services are started, then +overlay-init+ starts a
++dummy_service+ so as to keep +/.reaper+ from running out of child
+processes, as it otherwise will exit and thereby terminate
++overlay-boot+.
+
+*RAM_SIZE*::
+
+This variable may be used to configure the +/run+ directory which by
+defult gets mounted on an overlay mount as a +tmpfs+ of 50M. Use
++none+ to avoid that mount, and otherwise the desired +tmpfs+ size.
+
+Networking
+----------
+
+*overlay-boot* sets up a nework namespace named by the subhost NAME,
+and uses the CABLES variable to set up +veth+ virtual cables. The host
+end of such cables are named by NAME followed by a number from 0 and
+up while the subhost end are named by +eth+ followed by the same
+number.
+
+As mentioned above, CABLES consists of a space separated list of cable
+specifiers, each consisting of a bridge interface name and a with an
+equal sign ("=") between them. The equal sign is required while either
+or both of the bridge and MAC address may be left empty.
+
+The bridge interface name, if given, will be given control of the host
+end cable interface. A cable specification with empty bridge name part
+results in that the host end is brought up at link level and then
++ifup $IF+ is attempted.
+
+The MAC address, if given, is used for the subhost end cable
+interface. A cable specification with empty MAC address part results
+in that the interface get its MAC address from the kernel, possibly a
+different one upon each start.
+
+EXAMPLES
+--------
+
+./opt/subhost/mta/mta.conf
+****
+BASE=.
+START= rsyslog newtorking ssh postfix
+****
+
+The above example assumes a directory +/opt/subhost/mta+ that contains
+the configuration file +mta.conf+ and directories +root+, +work+ and
++live+. *overlay-boot* will set up an overlay mount on +live+ with
++root+ as UPPER, +work+ as WORK and +/+ as LOWER, i.e. an overlay of
+the main host filesystem.
+
diff --git a/overlay-init b/overlay-init
new file mode 100755 (executable)
index 0000000..60e2634
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+# This script performs default actions. It is invoked with CONFIG set
+# for the subhost.
+
+OVERLAYDIR="$(dirname $(realpath $0))"
+. $OVERLAYDIR/functions
+
+subhost_name "$CONFIG"
+subhost_config
+
+# Print the default init script
+cat <<EOF
+set -x
+mount -t proc proc /proc
+mount -t devpts devpts /dev/pts
+mount -t sysfs sysfs /sys
+if [ "$RAM_SIZE" != "none" ] && ! grep -q '/run tmpfs' /proc/mounts ; then
+    mount -t tmpfs -osize=$RAM_SIZE,mode=755 tmpfs /run
+fi
+for srv in $START ; do service \$srv start ; done
+dummy_service() {
+    [ \$# -gt 3 ] && return 0
+    echo "Starting dummy service" >&2
+    set +x
+    [ -p /run/dummy_service ] || mkfifo /run/dummy_service
+    ( printf dummy_service > /proc/self/comm ; read X < /run/dummy_service ) &
+    set -x
+}
+dummy_service /proc/*/comm
+exec /.reaper $NAME
+EOF
+
+
diff --git a/overlay-postmount b/overlay-postmount
new file mode 100755 (executable)
index 0000000..d1a3c0b
--- /dev/null
@@ -0,0 +1,13 @@
+#!/bin/sh
+#
+# This script performs default actions. It is invoked with CONFIG set
+# for the subhost.
+
+OVERLAYDIR="$(dirname $(realpath $0))"
+. $OVERLAYDIR/functions
+
+subhost_name "$CONFIG"
+subhost_config
+
+echo "Installing $OVERLAYDIR/reaper to $LIVE/.reaper"
+cp -p $OVERLAYDIR/reaper $LIVE/.reaper
diff --git a/overlay-premount b/overlay-premount
new file mode 100755 (executable)
index 0000000..607c1ea
--- /dev/null
@@ -0,0 +1,35 @@
+#!/bin/sh
+#
+# This script performs default actions. It is invoked with CONFIG set
+# for the subhost.
+
+OVERLAYDIR="$(dirname $(realpath $0))"
+. $OVERLAYDIR/functions
+
+subhost_name "$CONFIG"
+subhost_config
+
+# setup $UPPER/dev
+mkdir -p "$UPPER/dev"
+mount -t tmpfs -osize=50M tmpfs "$UPPER/dev"
+mknod -m 622 "$UPPER/dev/console" c 5 1
+mknod -m 666 "$UPPER/dev/null" c 1 3
+mknod -m 666 "$UPPER/dev/zero" c 1 5
+mknod -m 666 "$UPPER/dev/ptmx" c 5 2
+mknod -m 666 "$UPPER/dev/tty" c 5 0
+mknod -m 444 "$UPPER/dev/random" c 1 8
+mknod -m 444 "$UPPER/dev/urandom" c 1 9
+chown root:tty "$UPPER/dev/console"
+chown root:tty "$UPPER/dev/ptmx"
+chown root:tty "$UPPER/dev/tty"
+ln -sTf /proc/self/fd "$UPPER/dev/fd"
+ln -sTf /proc/self/fd/0 "$UPPER/dev/stdin"
+ln -sTf /proc/self/fd/1 "$UPPER/dev/stdout"
+ln -sTf /proc/self/fd/2 "$UPPER/dev/stderr"
+ln -sTf /proc/kcore "$UPPER/dev/core"
+mkdir "$UPPER/dev/shm"
+mkdir "$UPPER/dev/pts"
+chmod 1777 "$UPPER/dev/shm"
+
+# all good so far ; now avoid using the host's networking setup
+setup_networking "$UPPER"
index 2788413e97900fb13329056a051f5408240852d3..132da7600e7b37453e94a10a748367fb04896055 100755 (executable)
@@ -3,22 +3,20 @@
 # Script to stop the nominated overlay subhost
 
 OVERLAYDIR="$(dirname $(realpath $0))"
-
-[ $(id -u) = 0 ] || exec sudo $0 $@
 . $OVERLAYDIR/functions $*
 
-CONFIG="$1"
-[ -r "$CONFIG" ] || die "Missing configuration $CONFIG"
-
-config NAME $(basename $1 .${1##*.})
-config START "ssh networking"
-config LIVE
+subhost_name "$1"
+subhost_config
 
 read USPID RSPID <<EOF
 $(is_live $NAME)
 EOF
 
-[ -z "$USPID" ] && echo "$NAME is snot running" >&2 && exit 1
+if [ -z "$USPID" ] ; then
+    [ -r /run/netns/$NAME ] && ip netns del $NAME
+    echo "$NAME is not running" >&2
+    exit 1
+fi
 
 if [ -z "$RSPID" ] ; then
     cat <<EOF >&2
@@ -28,16 +26,18 @@ EOF
     exit 1
 fi
 
-# function to reverse the $* words
-reverse() {
-    local OUT=""
-    for w in $* ; do OUT="$w $OUT" ; done
-    echo "${OUT% }"
-}
-
 START="$(reverse "$START")"
-if nsenter -t $RSPID -p -m -i -u ip netns exec $NAME chroot $LIVE /bin/sh \
-          -c "for srv in $START ; do service \$srv stop ; done" ; then
-    CHILDPIDS="$(nsenter -t $RSPID -p -m ps -hopid --ppid 1)"
-    nsenter -t $RSPID -p -m kill $CHILDPIDS
-fi
+nsenter -t $RSPID -p -m -i -u \
+       ip netns exec $NAME chroot $(realpath $LIVE) \
+       /bin/sh -c "for srv in $START ; do service \$srv stop ; done"
+
+for p in $RSPID $USPID ; do
+    for S in 15 1 2 9 ; do
+       ps -hocmd $p || break
+       kill -$S $p
+    done
+done
+
+[ -r /run/netns/$NAME ] && ip netns del $NAME
+
+true