pax_global_header00006660000000000000000000000064152106453040014512gustar00rootroot0000000000000052 comment=ed099c980a67ff0aa87dd18fb65eff35db4f4596 overlay-boot-1.6/000077500000000000000000000000001521064530400140025ustar00rootroot00000000000000overlay-boot-1.6/Makefile000066400000000000000000000051221521064530400154420ustar00rootroot00000000000000ETCDIR = $(DESTDIR)/etc INITDIR = $(DESTDIR)/etc/init.d SRVDIR = $(DESTDIR)/usr/lib/systemd/system MAN8DIR = $(DESTDIR)/usr/share/man/man8 SBINDIR = $(DESTDIR)/usr/sbin VARLIBDIR = $(DESTDIR)/var/lib/overlay-boot MAN8FILES = overlay-boot.8 overlay-go.8 overlay-stop.8 overlay-diskfile.8 MAN8FILES += overlay-share.8 VARLIBFILES = overlay-boot overlay-go overlay-stop functions reaper VARLIBFILES += overlay-init overlay-postmount overlay-premount VARLIBFILES += overlay-diskfile VARLIBFILES += overlay-share INITFILES = subhosts ETCFILES = subhosts.conf SRVFILES = subhosts@.service # The default is to build asm/reaper REAPER = src/reaper all: $(REAPER) # Specific rule: $(REAPER) is ensured by "recursive make" $(REAPER): $(MAKE) -C $$(dirname $(REAPER)) reaper # Specific rule: the reaper program originates from $(REAPER) reaper: $(REAPER) mv $< $@ # Specific rule: the sysvinit init file $(INITDIR)/subhosts: subhosts.sh | $(INITDIR)/ cp $< $@ # Specific rule: generate changelog from version 1.2 changelog: git log --compact-summary 1.2..master > $@ # Specific rule: the sysvinit init configuration file $(ETCDIR)/subhosts.conf: subhosts.conf | $(ETCDIR)/ cp $< $@ # Generic rule: any dependee directory needs to be created %/: mkdir -p $@ # Generic rule: a local .8 file depends on the same .8.adoc file, if any $(MAN8FILES): %: %.adoc asciidoctor -b manpage $^ # Generic rule: an installed MAN8FILE file depends on a local the # same, and that the installation directory exists $(addprefix $(MAN8DIR)/,$(MAN8FILES)): $(MAN8DIR)/%: % | $(MAN8DIR)/ cp $< $@ # Generic rule: an installed VARLIBFILES file depends on a local the # same, and that the installation directory exists $(addprefix $(VARLIBDIR)/,$(VARLIBFILES)): $(VARLIBDIR)/%: % | $(VARLIBDIR)/ cp -p $< $@ # Generic rule: copy service files $(addprefix $(SRVDIR)/,$(SRVFILES)): $(SRVDIR)/%: % | $(SRVDIR)/ cp $< $@ # Make target: to clean up this workspace clean: rm -f reaper *.8 make -C $$(dirname $(REAPER)) clean .INTERMEDIATE: reaper $(MAN8FILES) # Make target: enumerates that which should be installed INSTALLTARGETS += $(addprefix $(MAN8DIR)/,$(MAN8FILES)) INSTALLTARGETS += $(addprefix $(VARLIBDIR)/,$(VARLIBFILES)) INSTALLTARGETS += $(addprefix $(INITDIR)/,$(INITFILES)) INSTALLTARGETS += $(addprefix $(SRVDIR)/,$(SRVFILES)) INSTALLTARGETS += $(addprefix $(ETCDIR)/,$(ETCFILES)) ifneq ($(shell ls -d .git 2>/dev/null),) INSTALLTARGETS += changelog endif install: $(INSTALLTARGETS) # Make target: make a .deb file in ../ BUILDPACKAGE = -us -uc --build=full -Iolle deb: dpkg-buildpackage $(BUILDPACKAGE) # PREFIX= INCLUDE_PREFIX=/usr overlay-boot-1.6/README.adoc000066400000000000000000000123251521064530400155720ustar00rootroot00000000000000= The overlay-boot Project :author: Ralph Ronnquist :revdate: Sun, 30 Apr 2023 23:46:31 +1000 The *overlay-boot* project implements a "minimalist approach" for dividing a single host into "subhosts" for administratively separated services. The project provides core support for "subhosts" that are independent operating system environments but using overlay root filesystems, and with their services executed with separated namespaces by a common kernel. The concept is similar to "containers" and "virtual machines", but with much lighter touch that is aimed at light-weight technical separation of service environments within a common adminstration framework. * *overlay-boot* implements a simple and efficient networking principle where networking is achived via network namspaces and virtual cabling. There is an overarching adminstrative control at the host end while the subhosts are adminstrated separately as if they were alone. * *overlay-boot* includes support for overlay root filesystem with persistent individual overlays for the subhosts. This is scripted to be open for any storage solutions, including the sharing of file system subtrees, disk and partition image files and logical volume set ups. * *overlay-boot* includes a scripted service oriented "subhost init" procedure that is open for all kinds of service management, including the trivial case of "no services" (as is necessary for installing and configuring the service or services of a subhost). == A usage example (minimal) A subhost is techincally defined as a directory that contains three mount points "work", "root" and "live", and a configuration file with at least a definition of the BASE variable with the pathname of the subhost directory. For convenience, the BASE pathname is understood as relative to its own directory, and thus, if the configuration resides in the subhost directory a simple "BASE=." assignment is a sufficient configuration. Refer to the overlay-boot manpage for all the configuration options. . The minimal overlay subhost setup ==== ---- # mkdir /ex1 /ex1/work /ex1/root /ex1/live # echo BASE=. > /ex1/ex1.conf ---- ==== The minimal overlay subhost may then be started with ==== ---- # overlay-boot /ex1/ex1.conf ---- ==== and it may be stopped with: ==== ---- # overlay-stop /ex1/ex1.conf ---- ==== The subhost environment may be "entered" with ==== ---- # overlay-go ex1 ---- ==== == Another usage example (MTA) This is an example setup at +/opt/mta+ of a larger overlay subhost for an MTA as primary service and with some additional useful companion services. .Initial setup for /opt/mta ==== ---- $ sudo mkdir -p /opt/mta/{live,root,work} # sudo tee /opt/mta/mta.conf > /etc/network/interfaces # echo "$MTANET.2 mta" >> /etc/hosts # echo "mta" > /opt/mta/root/etc/hostname # iptables -t nat -I PREROUTING -p tcp --dport 25 -j DNAT --to-destination $MTANET.2 # iptables -t nat -I POSTROUTING -s $MTANET.2 -j MASQUERADE # cat > /etc/network/interfaces.d/mta.conf /opt/mta/root/etc/network/interfaces Date: Sat Jun 6 08:15:00 2026 +1000 Revised subhost startup to use pivot_root so as to close the namespace escape hole via "nsenter -t 1 -m". overlay-boot | 19 +++++++++++++++---- overlay-init | 2 -- overlay-stop | 5 ++--- 3 files changed, 17 insertions(+), 9 deletions(-) commit c30d09f2708a7d743ad67b5fae35a1f97f0cb7b2 Author: Ralph Ronnquist Date: Sat May 16 21:07:13 2026 +1000 update changelog changelog | 11 +++++++++++ 1 file changed, 11 insertions(+) commit 5734794b29662dd9e807dd6e1e4a0cd873581ba0 Author: Ralph Ronnquist Date: Sat May 16 17:10:32 2026 +1000 Change mount label for /dev/to be "dev" Use chmodto set mode for /run/lock Avoid changing pre-existing $LIVE/.reaper overlay-postmount | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) commit 77751d20e60423ecc16abfbac65875421b11461c Author: Ralph Ronnquist Date: Wed Apr 22 21:11:02 2026 +1000 Generated changelog from 1.2 until here changelog (new) | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) commit 1204be5d8edbf2d0302266f36816afe17bf7280a Author: Ralph Ronnquist Date: Wed Apr 22 20:52:31 2026 +1000 Rename service file and add generated changelog as install target Makefile | 10 +++++++++- subhost@.service => subhosts@.service | 0 2 files changed, 9 insertions(+), 1 deletion(-) commit 2de016d2f4b518aa45a7cf74692390b0e4aa2f6d Author: Ralph Ronnquist Date: Sun Apr 19 23:20:18 2026 +1000 Revised SHARE handling to follow "postmount" and allowing both single-dir format and hostdir:subhostdir format. Updated documentation of revised SHARE configurations. functions | 24 ++++++++++++------------ overlay-boot.8.adoc | 17 ++++++++++++----- 2 files changed, 24 insertions(+), 17 deletions(-) commit dce634d902f55e06a055ddd6e0007463b87e9bb0 Author: Ralph Ronnquist Date: Sat Apr 18 22:15:06 2026 +1000 Correction to error messages functions | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) commit 29ad2a653c10bdc6cd4820f37cbb13db9d4486ce Author: Ralph Ronnquist Date: Fri Apr 17 18:43:47 2026 +1000 Corrected default log file pathname. overlay-boot.8.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) commit 781fb12bfbc15ed7ccb73991388f4267e03bcb3a Author: Ralph Ronnquist Date: Fri Apr 17 18:38:36 2026 +1000 Restored mounting /proc overlay-init | 1 + 1 file changed, 1 insertion(+) commit 137d3a386de041e7c125d6d90202a582d4f4ec69 Author: Ralph Ronnquist Date: Fri Apr 17 12:53:21 2026 +1000 Make restart into a noop subhosts.sh | 4 ++++ 1 file changed, 4 insertions(+) commit 62c29103b6f29d8717dd40d588d996fb19e78a81 Author: Ralph Ronnquist Date: Fri Apr 17 12:52:52 2026 +1000 Enter netns before unshare overlay-boot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) commit 49f70081a39dd4b36fe22f2ae215d644ff56f0bf Author: Ralph Ronnquist Date: Fri Apr 17 12:52:33 2026 +1000 Reworked is_live functions | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) commit c5f16c58a54e04eb9717d3f07c18a41e407d3a7b Author: Ralph Ronnquist Date: Tue Mar 10 12:25:48 2026 +1100 * move utility scripting into scripts sub directory * extend unshare to include time and direct netns * revise default startup to all full-pathname scripts, and use setsid for private service start sessions when available overlay-boot | 8 ++++--- overlay-init | 19 +++++++++++++---- devnodes-init.sh => scripts/devnodes-init.sh | 0 scripts/wait-online (new) | 32 ++++++++++++++++++++++++++++ 4 files changed, 52 insertions(+), 7 deletions(-) commit 7c436a4aa2299ff27db647ee30c1147270a6a02e Author: Ralph Ronnquist Date: Sun Jul 13 12:54:30 2025 +1000 Zap spurious sudo usage overlay-go | 3 +-- overlay-share | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) commit cecfed8bc930bc0d1f732ea87ff7e0f713551ede Author: Ralph Ronnquist Date: Fri Jul 11 18:19:55 2025 +1000 Avoid starting running subhosts. subhosts.sh | 1 + 1 file changed, 1 insertion(+) overlay-boot-1.6/functions000066400000000000000000000143131521064530400157370ustar00rootroot00000000000000# This file implements common functions for all boot scripts # Rerun with sudo if needed [ $(id -u) = 0 ] || exec sudo $0 $@ export ACTION="$(basename $0)" # Function to write a message and exit with error code die() { echo "$*" >&2 exit 1 } beginswith() { [ "$1" != "${1#$2}" ] } # 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() { local V W read V <&2 } # Install a default $1/etc/network/interfaces on the subhost root $1 setup_networking() { [ -r $1/etc/network/interfaces ] && return 0 mkdir -p $1/etc/network cat <> $1/etc/network/interfaces # Generated for $NAME subhost auto lo iface lo inet loopback EOF for IF in $(ip netns exec $NAME ip link show | grep "^eth") ; do cat <> $1/etc/network/interfaces auto eth$i iface eth$i inet manual EOF done } # Setup the network namespace for the given $CABLES # $1=netns ( $2="br=mac" .. ) # br is optional, mac is optional. # If mac is .N then it's taken as vlan tag on prior outer interface # (with ifup configuration) and the inner interface is left alone. setup_veth_cables() { local NETNS BR IF MAC C i ADD NETNS="$1" shift 1 i=0 for C in "$@" ; do IF=$NETNS$i MAC="${C#*=}" if ip link show $IF > /dev/null 2>&1 ; then : # The interface exists already (bad badness); let things fail elif ifquery --state $IF >/dev/null 2>&1 ; then # doesn't exist but has residue state; quiet cleanup ifdown -f $IF > /dev/null 2>&1 fi if [ -z "$MAC" ] ; then # set up veth with "random" mac address ip link add $IF type veth peer name eth$i netns $NETNS elif [ -z "${MAC%%.*}" ] ; then # set up a host vlan with specified tag on previous eth i=$((i-1)) IF=$NETNS$i$MAC ifup $IF else # set up veth with specified mac address ip link add $IF type veth peer name eth$i address $MAC netns $NETNS fi BR="${C%=*}" if [ -z "$BR" ] ; then ifup $IF || ip link set $IF up else ip link set $IF up brctl addif $BR $IF fi i=$((i+1)) done } # 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 NAME="$1" LIVE="$2" LOWER="$3" UPPER="$4" WORK="$5" echo setup_overlay "$NAME" "$LIVE" "$LOWER" "$UPPER" "$WORK" if grep -qE "^[^ ]+ $LIVE " /proc/mounts ; then die "$LIVE already has a mount" fi [ -d "$UPPER" ] || die "UPPER=$UPPER is not a directory" [ -d "$LOWER" ] || die "LOWER=$LOWER is not a directory" [ -d "$LIVE" ] || die "LIVE=$LIVE 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" env CONFIG="$CONFIG" $PREMOUNT "$UPPER" OLY="-olowerdir=$3,upperdir=$UPPER,workdir=$5" if ! mount -t overlay "$OLY" $1 $2 ; then umount -R "$UPPER/dev" 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 $LIVE fi env CONFIG="$CONFIG" $POSTMOUNT "LIVE" "$UPPER" ## Add shared paths from SHARE=dir or SHARE=hostdir:subhostdir formats grep '^SHARE\s*=' "$CONFIG" | while IFS="= " read A B ; do H="${B%%:*}" # hostdir is before first : S="${B#*:}" # subhostdir follows first : or is same as hostdir D="$(realpath "$H")" if [ "$D" != "$LOWER" ] && [ -d "$D" ] && mkdir -p $LIVE$S ; then echo bind mount $D onto $LIVE$S mount --bind $D $LIVE$S fi done } # Find the "unshare" process for $1 and echo the its pid and the pids # of its child processes. is_live() { local NAME=$1 # locate overlay-boot process for $NAME.conf local USPID="$(pgrep -f "overlay-boot.*/$NAME.conf")" [ -z "$USPID" ] && return 1 # pick first child of that USPID=$(ps -hopid --ppid="$((USPID))") [ -z "$USPID" ] && return 1 # then pick first child of that echo "$((USPID)) $(ps -hopid --ppid="$((USPID))")" } # Find all overlay-boot processes and list their config files list_running() { pgrep -a overlay-boot | awk '{print $4}' } # Start cgroup v2 cpuset accounting if enabled. # Needs manual enabling, with: # mount -t cgroup2 cgroup2 /sys/fs/cgroup setup_cgroup2_accounting() { local NAME="$1" ME="$2" local ACCDIR="$(awk '$3 == "cgroup2" {print $2; exit}' /proc/mounts)" [ -z "$ACCDIR" ] && return 0 mkdir -p "$ACCDIR/$NAME" echo "$ME" > $ACCDIR/$NAME/cgroup.procs } overlay-boot-1.6/overlay-boot000077500000000000000000000034221521064530400163530ustar00rootroot00000000000000#!/bin/sh # # This boot scripts runs a service subhost as defined by the # configuration file named on the command line. # See "man overlay-boot" for details. set -x PROGRAMDIR="$(dirname $(realpath $0))" . $PROGRAMDIR/functions subhost_name $1 if [ -z "$UNSHARED" ] ; then if [ ! -r /run/netns/$NAME ] ; then ip netns add $NAME ip netns exec $NAME ip link set lo up || exit 1 fi ## reinvoke this script with unhared mount namespace exec env UNSHARED=yes unshare -m $0 $@ > $LOG 2>&1 & echo "Logging to $LOG" >&2 exit 0 fi subhost_config setup_veth_cables $NAME $CABLES setup_overlay "$NAME" "$LIVE" "$LOWER" "$UPPER" "$WORK" setup_cgroup2_accounting "$NAME" "$$" exithandler() { [ -r /run/netns/$NAME ] && ip netns del $NAME [ "$UPPER" != "$LIVE" ] && umount -R "$LIVE" } trap "exithandler" 0 # This process has an unshared mount namespace, so we unmount almost # everything before chroot. Exceptions are: $LIVE and anything mounted # below that, "/run/netns/$NAME" and its parent paths (incidentally # including "/" as well) and "/proc". sort -rk2,2 < /proc/mounts | while read D P A2 ; do beginswith "$P" "$LIVE" && continue beginswith "$P" "$(realpath $LIVE)" && continue beginswith "/run/netns/$NAME" "$P" && continue [ "$P" = /proc ] && continue umount "$P" done echo "Starting $NAME (${REAPER:-/.reaper})" nsenter --net=/run/netns/$NAME unshare -fpiuT --kill-child /bin/sh < /opt/subhost/copy/copy.conf BASE=. EOF ---- This setup has a minimal configuration for a subhost that overlays the root filesystem but is without networking. The subhost must be entered with *overlay-go*, although the default start might have started sshd listening on a loopback interface in the subhost's network namespaces. Note that *overlay-go* runs a shell within the namespaces, but not as a child of the "subhost init" (aka +.reaper+). === /opt/subhost/tiny/tiny.conf **** ---- BASE=. CABLES= = START= none LOWER= base ---- **** The +tiny+ subhost would be for overlaying a separate +debootstrap+ root filesystem, without any services (since +START+ is "none"). This gets started with a +dummy_service+ to hold the overlay for access via +overlay-go+. The +dummy_service+ sets up and listens on a pipe at +/run/dummy_service+, and exits when anything is written to that. === /opt/subhost/mta/mta.conf **** ---- BASE=. CABLES= = START= rsyslog networking ssh saslauthd postfix dovecot ---- **** 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. Further, the running subhost will feature a virtual cable to the main host, where the subhost end is named +eth0+ and the main host end is named +mta0+, and upon start, an +ifup mta0+ is attempted at the host end while the subhost end is handled via its neworking service. SEE ALSO -------- *overlay-stop*, *overlay-go* overlay-boot-1.6/overlay-boot@.service000066400000000000000000000003241521064530400201050ustar00rootroot00000000000000[Unit] Description=Start overlay-boot subhost instance %i Documentation=man:overlay-boot(8) [Service] ExecStart=/usr/sbin/overlay-boot %i ExecStop=/usr/sbin/overlay-stop %i [Install] WantedBy=multi-user.target overlay-boot-1.6/overlay-clean-root000077500000000000000000000015151521064530400174540ustar00rootroot00000000000000#!/bin/bash # # This script "cleans" the UPPER directory tree for a subhost by # comparing it with the LOWER tree and remove all files that are equal # to content. PROGRAMDIR="$(dirname $(realpath $0))" . $PROGRAMDIR/functions subhost_name $1 subhost_config : ${LOWER:-/} if [ ! -d "$UPPER" ] || [ ! -d "$LOWER" ] ; then echo "*** needs a root path" >&2 exit 1 fi if is_live $NAME ; then echo "** Cannot clean running subhost **" >&2 exit 1 fi UPPER="${UPPER%/}" LOWER="${LOWER%/}" if [ "$UPPER" = "$LOWER" ] ; then echo "** UPER and LOWER are the same directory **" >&2 exit 1 fi du -sh $UPPER exit 0 DIFFS=/tmp/clean-$NAME.$$ rm -f $DIFFS find $UPPER -type f -printf '%P\n'| while read X ; do cmp "$UPPER/$X" "$LOWER/$X" >> $DIFFS 2>&1 && rm "$UPPER/$X" done du -sh $UPPER echo "(See details in $DIFFS)" overlay-boot-1.6/overlay-clean-root.8.adoc000066400000000000000000000007261521064530400205270ustar00rootroot00000000000000overlay-boot(8) =============== :doctype: manpage :revdate: {sys:date "+%Y-%m-%d %H:%M:%S"} :COLON: : :EQUALS: = NAME ---- overlay-clean-root - Clean UPPER relative LOWER by removing equal files. SYNOPSIS -------- *overlay-clean-root* _conf_ DESCRIPTION ----------- *overlay-clean-root* reduces the UPPER directory tree for an off-line subhost by removing all files that equal to the files they shadow in the LOWER directory tree. SEE ALSO -------- *overlay-boot* overlay-boot-1.6/overlay-diskfile000077500000000000000000000024061521064530400172030ustar00rootroot00000000000000#!/bin/sh # # Helper script for using a diskfile for the LOWER or UPPER+WORK # filesystems. # # Example use a whole diskfile filesystem as LOWER: # LOWER=!overlay-diskfile mnt/ disk.img # # Example use a diskfile partition 2 as LOWER: # LOWER=!overlay-diskfile mnt/ disk.img 2 # # Example use a subtree of a diskfile partition 2 as LOWER: # LOWER=!overlay-diskfile mnt/some/path disk.img 2 # # Example use a while diskfile as UPPER and WORK. # The diskfile is mounted on "mnt". # UPPER=!overlay-diskfile mnt/some/upper/path disk.img 2 # WORK= mnt/some/work/path # Helper function to determine the mount offset for the partition, if any partoffset() { local X if [ -z "$2" ] ; then echo 0 else fdisk -lo Start "$1" | grep -A$2 '^ Start' | \ sed '${s|^|512*|;b};d' | bc -l fi } # Only do something when invoked by overlay-boot if [ "$ACTION" = "overlay-boot" ] ; then DISKFILE="$2" # .. and a diskfile is given if [ -n "$DISKFILE" ] ; then MOUNTPOINT="$(realpath "${1%/*}")" PARTITIONINDEX="$3" # .. and it's not mounted yet (for this subhost) if ! grep -qE "^[^ ]* $MOUNTPOINT" /proc/mounts ; then OFFSET=$(partoffset "$DISKFILE" "$PARTITIONINDEX") mount -ooffset=$OFFSET "$DISKFILE" "$MOUNTPOINT" || exit 1 fi fi fi echo "${1#/}" overlay-boot-1.6/overlay-diskfile.8.adoc000066400000000000000000000031411521064530400202500ustar00rootroot00000000000000overlay-diskfile(8) =================== :doctype: manpage :revdate: {sys:date "+%Y-%m-%d %H:%M:%S"} :COLON: : :EQUALS: = :BANG: ! NAME ---- overlay-diskfile - Support for using diskfile filesystem. SYNOPSIS -------- *overlay-diskfile* _mount/path_ [ _diskfile_ [ _index_ ] ] DESCRIPTION ----------- *overlay-diskfile* is a support script for pre-mounting a diskfile to provide the LOWER or UPPER filesystem for a subhost. It is typically used in the subhost configuration to be invoked as part of determining the filesystem path. OPTIONS ------- no options. EXAMPLES -------- .Use a whole diskfile filesystem as LOWER ==== ---- LOWER=!overlay-diskfile mnt/ disk.img ---- ==== The above mounts the disk.img filesystem onto +mnt+ and provides that as the LOWER filesystem. .Use a diskfile partition 2 as LOWER ==== ---- LOWER=!overlay-diskfile mnt/ disk.img 2 ---- ==== The above mounts the second partition onto +mnt+ and provides that as the LOWER filesystem. .Use a subtree of a diskfile partition 2 as LOWER ==== ---- LOWER=!overlay-diskfile mnt/some/path disk.img 2 ---- ==== The above mounts the second partition onto +mnt+ and provides the path +mnt/some/path+ that as the LOWER filesystem. .Use second partition diskfile filesystem subtrees for UPPER and WORK. ==== ---- UPPER=!overlay-diskfile mnt/some/upper/path disk.img 2 WORK= mnt/some/work/path ---- ==== The above mounts the second partition of disk.img and uses +mnt/some/upper/path+ as UPPER and +mnt/some/work/path+ as WORK. Note that UPPER and WORK must always be subtrees from the same filesystem. SEE ALSO -------- *overlay-boot*, *overlay-stop* overlay-boot-1.6/overlay-go000077500000000000000000000012631521064530400160160ustar00rootroot00000000000000#!/bin/sh . $(dirname $(realpath $0))/functions XXX NAME="$1" [ -z "$NAME" ] && echo "Select subhost: $(list_running)" && exit 0 read USPID RSPID <&2 && exit 1 if [ -z "$RSPID" ] ; then cat <&2 *** $NAME is started (pid $USPID) but doesn't seem to be running *** /.reaper and might need manual fixing. EOF exit 1 fi nsenter -t "$RSPID" -n -m -p -r -w -i -u -C /bin/bash if [ $? = 137 ] ; then echo "######### please 'fg' this again (if stopped)" >&2 stty sane # a killed bash might leave the tty insane and stty # typically gets stopped trying to correct it fi echo "done" overlay-boot-1.6/overlay-go.8.adoc000066400000000000000000000011411521064530400170610ustar00rootroot00000000000000overlay-go(8) ============= :doctype: manpage :revdate: {sys:date "+%Y-%m-%d %H:%M:%S"} :COLON: : :EQUALS: = NAME ---- overlay-go - Start a shell within a subhost namespace environment. SYNOPSIS -------- *overlay-go* _name_ DESCRIPTION ----------- *overlay-go* is an adminstration utility for entering the namespace environment of a "booted" subhost started witb +overlay-boot+. This starts a +bash+ shell within the subhost namespace, though not a child of its pid 1. OPTIONS ------- no options. EXAMPLES -------- ==== ---- overlay-go tiny ---- ==== SEE ALSO -------- *overlay-boot*, *overlay-stop* overlay-boot-1.6/overlay-init000077500000000000000000000016561521064530400163620ustar00rootroot00000000000000#!/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 <&2 set +x [ -p /run/dummy_service ] || mkfifo /run/dummy_service ( printf dummy_service > /proc/self/comm ; read X < /run/dummy_service ) & set -x } for srv in $START ; do if [ -z "\${srv##/*}" ] ; then eval \$srv elif [ -z \$(command -v service) ] ; then echo "CANNOT START \$srv -- mssing service command" >&2 elif [ -z \$(command -v setsid) ] ; then echo "STARTING \$srv IN SESSION 0" >&2 service \$srv start else echo "STARTING \$srv IN PRIVATE SESSION" >&2 setsid -w service \$srv start fi done dummy_service /proc/*/comm exec ${REAPER:-/.reaper} $NAME EOF overlay-boot-1.6/overlay-postmount000077500000000000000000000025511521064530400174620ustar00rootroot00000000000000#!/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 $LIVE/dev mkdir -p "$LIVE/dev" mount -t tmpfs -osize=50M dev "$LIVE/dev" mknod -m 622 "$LIVE/dev/console" c 5 1 mknod -m 666 "$LIVE/dev/null" c 1 3 mknod -m 666 "$LIVE/dev/zero" c 1 5 mknod -m 666 "$LIVE/dev/ptmx" c 5 2 mknod -m 666 "$LIVE/dev/tty" c 5 0 mknod -m 444 "$LIVE/dev/random" c 1 8 mknod -m 444 "$LIVE/dev/urandom" c 1 9 chown root:tty "$LIVE/dev/console" chown root:tty "$LIVE/dev/ptmx" chown root:tty "$LIVE/dev/tty" ln -sTf /proc/self/fd "$LIVE/dev/fd" ln -sTf /proc/self/fd/0 "$LIVE/dev/stdin" ln -sTf /proc/self/fd/1 "$LIVE/dev/stdout" ln -sTf /proc/self/fd/2 "$LIVE/dev/stderr" ln -sTf /proc/kcore "$LIVE/dev/core" mkdir "$LIVE/dev/shm" mkdir "$LIVE/dev/pts" chmod 1777 "$LIVE/dev/shm" mount -t devpts devpts $LIVE/dev/pts mount -t sysfs sysfs $LIVE/sys if [ "$RAM_SIZE" != "none" ] ; then mount -t tmpfs -osize=$RAM_SIZE,mode=755 tmpfs $LIVE/run mkdir $LIVE/run/lock chmod 1777 $LIVE/run/lock fi echo "bind-mount /etc/adjtime into subhost, if possible" mount --bind /etc/adjtime $LIVE/etc/adjtime || true if [ ! -e $LIVE/.reaper ] ; then echo "install $OVERLAYDIR/reaper to $LIVE/.reaper" cp -p $OVERLAYDIR/reaper $LIVE/.reaper fi overlay-boot-1.6/overlay-premount000077500000000000000000000004501521064530400172570ustar00rootroot00000000000000#!/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 # all good so far ; now avoid using the host's networking setup setup_networking "$UPPER" overlay-boot-1.6/overlay-share000077500000000000000000000014661521064530400165200ustar00rootroot00000000000000#!/bin/bash # # Share a directory tree with an overlay-boot subhost # # $1 = directory $2 = subhost set -e . $(dirname $(realpath $0))/functions if [ ! -d "$1" ] ; then echo "** Not a directory: $1" >&2 exit 1 fi SHARE="$1" NAME="$2" [ -z "$NAME" ] && echo "Select subhost: $(list_running)" && exit 0 read USPID RSPID <&2 && exit 1 if [ -z "$RSPID" ] ; then cat <&2 *** $NAME is started (pid $USPID) but doesn't seem to be running *** /.reaper and might need manual fixing. EOF exit 1 fi nsenter -t "$RSPID" -n -m -p /bin/bash <&2 exit 1 fi if [ -z "$RSPID" ] ; then cat <&2 *** $NAME is started (pid $USPID) but doesn't seem to be running *** /.reaper and might need manual fixing. EOF exit 1 fi START="$(reverse "$START")" ip netns exec $NAME nsenter -t $RSPID -p -m -i -u /bin/sh \ -x -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 overlay-boot-1.6/overlay-stop.8.adoc000066400000000000000000000010331521064530400174410ustar00rootroot00000000000000overlay-stop(8) =============== :doctype: manpage :revdate: {sys:date "+%Y-%m-%d %H:%M:%S"} :COLON: : :EQUALS: = NAME ---- overlay-stop - Stop a subhost. SYNOPSIS -------- *overlay-stop* _conf_ DESCRIPTION ----------- *overlay-stop* is an adminstration utility for terminating a subhost. This will stop its START services in reverse order, then forcefully kill the +reaper+ process if needed . OPTIONS ------- no options. EXAMPLES -------- ==== ---- overlay-stop tiny.conf ---- ==== SEE ALSO -------- *overlay-boot*, *overlay-go* overlay-boot-1.6/scripts/000077500000000000000000000000001521064530400154715ustar00rootroot00000000000000overlay-boot-1.6/scripts/devnodes-init.sh000077500000000000000000000011471521064530400206030ustar00rootroot00000000000000#!/bin/sh /lib/init/init-d-script ### BEGIN INIT INFO # Provides: devices # Required-Start: # Required-Stop: # Default-Start: S # Default-Stop: 0 1 6 # Short-Description: set up devnodes # Description: Initialize /dev from /.devnodes.tgz ### END INIT INFO DAEMON=none DESC="devnodes: restore and capture /dev on startup and shutdown." TGZ=/.devnodes.tgz do_start_override() { [ -e $TGZ ] && tar -xzf $TGZ --skip-old-files -C /dev } do_stop_override() { cd /dev && tar czf $TGZ * } do_status_override() { find /dev -type d -exec ls -ld '{}' ';' stat -c %Y $TGZ } overlay-boot-1.6/scripts/wait-online000066400000000000000000000014001521064530400176350ustar00rootroot00000000000000#!/bin/sh /lib/init/init-d-script ### BEGIN INIT INFO # Provides: wait-online # Required-Start: $network # Required-Stop: # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: wait until network is online # Description: wait until the network gateway is reachable or timeout reached. ### END INIT INFO DAEMON=none NAME=wait-online do_start_override() { gw_dev=$(ip route | awk '/^default/ {print $3, $4, $5}') if [ "$gw_dev" ]; then log_daemon_msg "Waiting until $gw_dev is reachable" for i in $(seq 1 2 "${WAIT_ONLINE_TIMEOUT:=12}"); do if ip neigh get ${gw_dev} > /dev/null; then break else sleep 2 false # Signal error fi done vlog_end_msg $? fi } do_stop_override() { : # No-op } overlay-boot-1.6/src/000077500000000000000000000000001521064530400145715ustar00rootroot00000000000000overlay-boot-1.6/src/Makefile000066400000000000000000000002031521064530400162240ustar00rootroot00000000000000all: reaper CC = /usr/bin/gcc CFLAGS = -Wall -static -O3 -flto reaper: reaper.c $(CC) $(CFLAGS) -o $@ $^ clean: rm -f reaper overlay-boot-1.6/src/reaper.c000066400000000000000000000007421521064530400162160ustar00rootroot00000000000000/** * This program waits for child process and "reaps" them, i.e. read * off their status so that they can terminate. The program exits when * it runs out of children. */ #include #include #include int main(void) { sigset_t set; siginfo_t status; sigfillset(&set); sigprocmask(SIG_BLOCK,&set,NULL); do { memset( &status, 0, sizeof( status ) ); } while ( waitid( P_ALL, 0, &status, WEXITED ) == 0 ); return 0; } overlay-boot-1.6/src/reaper_nsl.c000066400000000000000000000012371521064530400170720ustar00rootroot00000000000000//#include //#include //#include //#include //#include //#include //int main(void) { // sigset_t set; // siginfo_t status; // // if (getpid()!=1) // return 1; // sigfillset(&set); // sigprocmask(SIG_BLOCK,&set,NULL); // memset(&status,0,sizeof status); // while (-ECHILD!=waitid(P_ALL,0,&status,WEXITED)); // return 1; //} #define _GNU_SOURCE #include #include #include #include static inline void nsl_exit1(void) { asm("mov $60,%eax"); asm("mov $1,%rdi"); asm("syscall"); } __attribute__((naked)) void _start(void) { nsl_exit1(); } overlay-boot-1.6/subhosts.conf000066400000000000000000000004331521064530400165230ustar00rootroot00000000000000# This file is used by subhosts boot facility to start overlay-boot(8) # subhosts upon system boot. Lines with '#' and blank lines are # ignored. Other lines should be an enumeration of subhosts to start # by means of the full pathnames for their overlay-boot configuration # files. overlay-boot-1.6/subhosts.sh000077500000000000000000000021571521064530400162200ustar00rootroot00000000000000#!/usr/bin/env /lib/init/init-d-script ### BEGIN INIT INFO # Provides: subhosts # Required-Start: $syslog $time $remote_fs # Required-Stop: $syslog $time $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Start subhosts # Description: Init script to start subhosts as per /etc/subhosts.conf ### END INIT INFO # Note that this facility only acts on start/stop/status, and it does # not have any running daemon of its own. The started subhosts will # "run" separately. DAEMON=none NAME=subhosts do_status_override() { pgrep -a overlay-boot | awk '{print $4}' } do_start_override() { if [ -r /etc/subhosts.conf ] ; then for SUB in $(grep -v '#' /etc/subhosts.conf) ; do pgrep -f "overlay-boot $SUB\$" > /dev/null && continue echo "overlay-boot $SUB" >&2 overlay-boot $SUB | logger -p boot.info -s done fi } do_restart_override() { echo "$NAME: restart is inhibited. Use stop and start" >&2 } do_stop_override() { for SUB in $(do_status_override) ; do echo "overlay-stop $SUB" >&2 overlay-stop $SUB | logger -p boot.info -s done } overlay-boot-1.6/subhosts@.service000066400000000000000000000003241521064530400173350ustar00rootroot00000000000000[Unit] Description=Start overlay-boot subhost instance %i Documentation=man:overlay-boot(8) [Service] ExecStart=/usr/sbin/overlay-boot %i ExecStop=/usr/sbin/overlay-stop %i [Install] WantedBy=multi-user.target