pax_global_header00006660000000000000000000000064147236106540014522gustar00rootroot0000000000000052 comment=7cca7165d81a6059af4faa17f1201b44bc956643 overlay-boot-1.2/000077500000000000000000000000001472361065400140065ustar00rootroot00000000000000overlay-boot-1.2/Makefile000066400000000000000000000046351472361065400154560ustar00rootroot00000000000000ETCDIR = $(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 = overlay-boot@.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: 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)) 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.2/README.adoc000066400000000000000000000123251472361065400155760ustar00rootroot00000000000000= 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 &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=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" 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 grep ^SHARE= "$CONFIG" | while read A ; do B="$(echo ${A#SHARE=})" D="$(realpath "$B")" [ "$D" = "$LOWER" ] && continue if [ -d "$D" ] ; then echo bind mount $D onto $LIVE$B mkdir -p $LIVE$D mount --bind $D $LIVE$B fi done env CONFIG="$CONFIG" $POSTMOUNT "LIVE" "$UPPER" } # 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 $(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.2/overlay-boot000077500000000000000000000025721472361065400163640ustar00rootroot00000000000000#!/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 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() { 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" env CONFIG="$CONFIG" $INIT | \ unshare -fp --mount-proc -i -u \ ip netns exec $NAME chroot $LIVE /bin/sh echo "Exited $NAME" overlay-boot-1.2/overlay-boot.8.adoc000066400000000000000000000235521472361065400174350ustar00rootroot00000000000000overlay-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 in a small collection of administration scripts for containerizing services with minimal ado. The script starts a "subhost" whose root filesystem is an overlay on the main host filesystem, and with separate mount, network and pid namespaces. In effect the default use case is merely an administrative sandboxing that is pre-populated with a copy of the overlaid filesystem. Each subhost is defined by means of a configuration file, __conf__, that is a simple text file with small collection of "variables" telling how the subhost is set up. *overlay-boot* spawns a subprocess that performs the boot-up of the "subhost" as an init script that ends with a pid 1 +reaper+ that simply "reaps" any terminated child processes. An administrator may "enter" the subhost execution environment to perform adminstrative tasks by means of *overlay-go*, which uses +chroot+ to start an interactive shell within the subhost namespaces. Such a shell however is not a child of the subhost +pid 1+ and it would normally only be used initially for configuring the subhost's network, and set up network based services, such as _sshd_, for subsequent access. Subhost execution is stopped with *overlay-stop*. OPTIONS ------- An *overlay-boot* subhost is defined in a configuration file that 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. But if that value startes with an exclamation mark, then the line is a command to run during start-up 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. + A typical subhost setup is defined by a dedicated directory that contains the configuration file and the three mount points "root", "work" and "live". *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 optionally a MAC address or VLAN tag to the right. See the section on Networking below for more details. INIT:: This variable is a command to run (with environment variable CONFIG set) that outputs on its standard output the series of commands for the running subhost. The default value for this variable is +/var/lib/overlay-boot/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 one of + 1. a pre-mounted directory, 2. bind mounted with UPPER, or 3. and overlay mount of UPPER (and WORK) over LOWER. See also 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, and 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 environment variable CONFIG set) following the setup of the subhost root filesystem and before the services are started. The default for this variable is +/var/lib/overlay-mount/overlay-postmount+. Note that this command is executed within the mount namespace of the subhost, and still with the main host as root filesystem. The POSTMOUNT command is executed for all of the three different root filesystem setup variants. *PREMOUNT*:: This variable is a command line to run (with environment 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+ Note that this command is executed within the mount namespace of the subhost, and still with the main host as root filesystem. The PREMOUNT command is executed for both the overlay and bind mount variants of root filesystem setup but not for plain root filesystem, which does not involve mounting LIVE. *RAM_SIZE*:: This variable configures the subhost +/run+ directory which by default is mounted as a +tmpfs+ of 50M. Use +none+ to avoid that mount, and otherwise the desired +tmpfs+ size. *START*:: This variable names the services to 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+. *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 overlay. Rather, if UPPER is different from LIVE, the root filesystem is set up as a bind mount, and if UPPER and LIVE are also the same, then LIVE is used as root filesystem without additional mounting. *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. *SHARE*:: This variable nominates a pathname under $LOWER to bind-mount onto the same pathname under $LIVE so as to share that directory tree into the overlay. The SHARE variable is a special configuration variable in that the value does not allow exclamation mark evaluation, and that it may occur many times for declaring multiple shared subdirectories. Networking ---------- *overlay-boot* sets up a nework namespace named by the subhost +$NAME+, and uses the +CABLES+ variable to set up _veth_ type 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+ is a space separated list of cable specifiers, each consisting of an optional bridge interface name, an optional MAC adddress or VLAN tag and with a required equal sign ("=") between them. The bridge interface name, when given, will be given control of the host end cable interface. When the bridge interface name is omitted (empty) then the host end interface attracts an _ifup $IF_ attempt instead. The MAC address, if given, is used for the subhost end cable interface, which otherise gets its MAC address from the kernel, possibly a different one upon each start. A VLAN tag has the format ".N" where N is a number between 1 and 4095. This modifies the cable function to set up a VLAN host interface on the prior host interface, and skip subhost side setup. E.g. if the prior host interface is +example1+ and the tag is +.302+ then the VLAN interface would be +example1.302+. The host side setup uses _ifup $IF_ and thus, the host needs to have a supporting configuration entry in +/etc/network/interfaces+ for the VLAN interface. Note that it may be a good practice to keep a local +interfaces+ file as sibling to the subhost configuration file on the host and use a _source_ statement to include this into the system networking configuration. EXAMPLES -------- === Smallest possible ---- # mkdir -p /opt/subhost/copy/{root,work,live} # cat << EOF > /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.2/overlay-boot@.service000066400000000000000000000003241472361065400201110ustar00rootroot00000000000000[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.2/overlay-clean-root000077500000000000000000000015151472361065400174600ustar00rootroot00000000000000#!/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.2/overlay-clean-root.8.adoc000066400000000000000000000007261472361065400205330ustar00rootroot00000000000000overlay-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.2/overlay-diskfile000077500000000000000000000024061472361065400172070ustar00rootroot00000000000000#!/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.2/overlay-diskfile.8.adoc000066400000000000000000000031411472361065400202540ustar00rootroot00000000000000overlay-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.2/overlay-go000077500000000000000000000013341472361065400160210ustar00rootroot00000000000000#!/bin/sh [ $(id -u) = 0 ] || exec sudo $0 $@ . $(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 sudo 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.2/overlay-go.8.adoc000066400000000000000000000011411472361065400170650ustar00rootroot00000000000000overlay-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.2/overlay-init000077500000000000000000000011641472361065400163600ustar00rootroot00000000000000#!/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 } dummy_service /proc/*/comm exec /.reaper $NAME EOF overlay-boot-1.2/overlay-postmount000077500000000000000000000025011472361065400174610ustar00rootroot00000000000000#!/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 devtmpfs "$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 cmod 1777 $LIVE/run/lock fi echo "bind-mount /etc/adjtime into subhost, if possible" mount --bind /etc/adjtime $LIVE/etc/adjtime || true echo "install $OVERLAYDIR/reaper to $LIVE/.reaper" cp -p $OVERLAYDIR/reaper $LIVE/.reaper overlay-boot-1.2/overlay-premount000077500000000000000000000004501472361065400172630ustar00rootroot00000000000000#!/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.2/overlay-share000077500000000000000000000014731472361065400165220ustar00rootroot00000000000000#!/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 sudo 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")" 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 overlay-boot-1.2/overlay-stop.8.adoc000066400000000000000000000010331472361065400174450ustar00rootroot00000000000000overlay-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.2/src/000077500000000000000000000000001472361065400145755ustar00rootroot00000000000000overlay-boot-1.2/src/Makefile000066400000000000000000000002031472361065400162300ustar00rootroot00000000000000all: reaper CC = /usr/bin/gcc CFLAGS = -Wall -static -O3 -flto reaper: reaper.c $(CC) $(CFLAGS) -o $@ $^ clean: rm -f reaper overlay-boot-1.2/src/reaper.c000066400000000000000000000007421472361065400162220ustar00rootroot00000000000000/** * 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.2/src/reaper_nsl.c000066400000000000000000000012371472361065400170760ustar00rootroot00000000000000//#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.2/subhost@.service000066400000000000000000000003241472361065400171560ustar00rootroot00000000000000[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.2/subhosts.conf000066400000000000000000000004331472361065400165270ustar00rootroot00000000000000# 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.2/subhosts.sh000077500000000000000000000017311472361065400162210ustar00rootroot00000000000000#!/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 echo "overlay-boot $SUB" >&2 overlay-boot $SUB | logger -p boot.info -s done fi } do_stop_override() { for SUB in $(do_status_override) ; do echo "overlay-stop $SUB" >&2 overlay-stop $SUB | logger -p boot.info -s done }