some write-up
[rrq/subhost.git] / control
1 #!/bin/bash
2 #
3 # Control program for bespoke arbitrary services through unshared sub-hosts
4 #
5 # $1 = the command: start or stop
6 # $2 = the sub-host to start or stop
7 # $3... = the optional chroot-ed command (/startup by default)
8
9 set -x
10
11 SCRIPT=$0
12 CMD=$1
13 NAME=$2
14 shift 2
15
16 usage() {
17     echo "$SCRIPT (start|stop) <subhost>" >&2
18     exit 1
19 }
20
21 [ -z "$NAME" ] && usage
22
23 : ${SUBHOST=/opt/subhost}
24 : ${OSROOT=}
25 : ${TOP=$SUBHOST/$NAME}
26 : ${TARGET=$TOP/live}
27 : ${IMAGE=$TOP/$NAME.img}
28 : ${UPPER=$TOP/root}
29 : ${WORK=$TOP/work}
30 : ${MOUNT=$TOP/mnt}
31 : ${NSNAME=$NAME}
32 : ${BRIDGES=homenet}
33 : ${CONFIG=$TOP/config}
34
35 [ -e "$CONFIG" ] && . "$CONFIG"
36
37 cd "$SUBHOST" || exit 1
38
39 # Create a simple overlay subhost without its own image file
40 create_subhost() {
41     mkdir -p $TARGET $MOUNT $UPPER $WORK
42     [ -d "$OSROOT" ] || OSROOT=$SUBHOST/daedalus/root
43     [ -e $CONFIG ] || cat <<EOF > "$CONFIG"
44 # Subhost $NAME is an autogenerated overlay subhost with shared filesystem
45 OSROOT="$OSROOT"
46 BRIDGES="$BRIDGES"
47 EOF
48 }
49
50 # generate a mac for given $1 (interface) using the last 5 characters
51 macaddr() {
52     local M="$(xxd -p <<< "${1:$(( ${#1} - 5)):5}")66666666"
53     echo "0a:${M:0:2}:${M:2:2}:${M:4:2}:${M:6:2}:${M:8:2}"
54 }
55
56 # setup the subhost network namespace and link up the host side
57 setup_network() {
58     E=0
59     ip netns add $NSNAME
60     for BRIDGE in ${BRIDGES[@]} ; do
61         brctl show $BRIDGE >& /dev/null || brctl addbr $BRIDGE
62         IF=$NAME$E
63         MAC=$(macaddr $IF)
64         ip link add $IF type veth peer name eth$E address $MAC netns $NSNAME 
65         ip link set $IF up
66         [ -n "$BRIDGE" ] && brctl addif $BRIDGE $IF
67         E=$((E+1))
68     done
69 }
70
71 # check if $1 is mounted
72 is_mounted() {
73     grep -qE "^[^ ]+ $1 " /proc/mounts
74 }
75
76 # Setup or re-setup the subhost filesystem
77 setup_rootfs() {
78     if is_mounted $TARGET ; then
79         mount -oremount $TARGET
80     else
81         if [ -f "$IMAGE" ] ; then
82             # The subhost has an image file with /root and /work in it
83             is_mounted $MOUNT || mount $IMAGE $MOUNT || exit 1
84             UPPER=$MOUNT/root
85             WORK=$MOUNT/work
86         fi
87         if [ -z "$OSROOT" ] ; then
88             # No overlay
89             mount --rbind $UPPER $TARGET
90         else
91             # overlay of $UPPER (+$WORK) over $OSROOT onto $TARGET
92             mount -t overlay $NAME \
93                   -olowerdir=$OSROOT,upperdir=$UPPER,workdir=$WORK \
94                   $TARGET
95         fi
96     fi
97 }
98
99 case "$CMD" in
100     start)
101         [ -e "/run/netns/$NSNAME" ] || setup_network
102         [ -d "$MOUNT" ] || create_subhost
103         setup_rootfs
104         START=/bin/bash
105         [ -x $TARGET/startup ] && START=/startup
106         exec ip netns exec $NSNAME unshare \
107              --fork --pid --mount-proc --kill-child \
108              --uts --ipc --mount --cgroup \
109              "--root=$TARGET" $START
110         ;;
111     stop)
112         umount $TARGET
113         [ -e $IMAGE ] && umount $MOUNT
114         ip netns del $NSNAME
115         ;;
116     *)
117         usage
118         ;;
119 esac