1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
|
# 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 <<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
setup_networking() {
[ -r $1/etc/network/interfaces ] && return 0
mkdir -p $1/etc/network
cat <<EOF >> $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 <<EOF >> $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
}
|