Revised pretend-boot description
[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=lan_br}
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/chimaera/base
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 address for given arguments pass through 40-bit b2sum
51 # and with 02: prefix.
52 macaddr() {
53     local V="$(b2sum -l 40 <<< "$*" )"
54     V="$( sed 's/\(..\)/\1:/g' <<< "${V}aaaaaaaaaa")"
55     echo "02:${V:0:14}"
56 }
57
58 # setup the subhost network namespace and link up the host side
59 setup_network() {
60     E=0
61     ip netns add $NSNAME
62     for BRIDGE in ${BRIDGES[@]} ; do
63         IF=$NAME$E
64         B=( ${BRIDGE/:/ } )
65         BRIDGE="${B[0]}"
66         MAC="${B[1]}"
67         [ -z "$MAC" ] && MAC="$(macaddr "$(hostname)" "$NAME" "$IF")"
68         brctl show $BRIDGE >& /dev/null || brctl addbr $BRIDGE
69         ip link add $IF type veth peer name eth$E address $MAC netns $NSNAME 
70         ip link set $IF up
71         [ -n "$BRIDGE" ] && brctl addif $BRIDGE $IF
72         E=$((E+1))
73     done
74 }
75
76 # check if $1 is mounted
77 is_mounted() {
78     grep -qE "^[^ ]+ $1 " /proc/mounts
79 }
80
81 # Setup or re-setup the subhost filesystem
82 setup_rootfs() {
83     if is_mounted $TARGET ; then
84         mount -oremount $TARGET
85     else
86         if [ -e "$IMAGE" ] ; then
87             # $IMAGE is either an image file or a link to a partition,
88             # with /root and /work in it.
89             is_mounted $MOUNT || mount $IMAGE $MOUNT || exit 1
90             UPPER=$MOUNT/root
91             WORK=$MOUNT/work
92         fi
93         if [ -z "$OSROOT" ] ; then
94             # No overlay
95             mount --rbind $UPPER $TARGET
96         else
97             # overlay of $UPPER (+$WORK) over $OSROOT onto $TARGET
98             mount -t overlay $NAME \
99                   -olowerdir=$OSROOT,upperdir=$UPPER,workdir=$WORK \
100                   $TARGET
101         fi
102     fi
103 }
104
105 # Set up cgroup CPU accounting (cpuacct)
106 accounting() {
107         # 1-- once, system wide
108         grep -q "/sys/fs/cgroup cgroup" /proc/mounts || \
109             mount -t cgroup -ocpuacct none /sys/fs/cgroup
110         if [ -d /sys/fs/cgroup/$NAME ] ; then
111             : # reset?
112         else
113             mkdir /sys/fs/cgroup/$NAME
114             # the following fixes things that sometimes are broken?
115             for f in cpuset.mems cpuset.cpus ; do
116                 [ -z "$(cat /sys/fs/cgroups/$NAME/$F)" ] && \
117                     echo 0 > /sys/fs/cgroups/$NAME/$F
118             done
119
120         fi
121         # Register this task for subhost accounting
122         echo $$ > /sys/fs/cgroup/$NAME/tasks
123 }
124
125 case "$CMD" in
126     start)
127         [ -e "/run/netns/$NSNAME" ] || setup_network
128         [ -d "$MOUNT" ] || create_subhost
129         setup_rootfs
130         START=/bin/bash
131         [ -x $TARGET/startup ] && START=/startup
132         accounting
133         exec ip netns exec $NSNAME unshare \
134              --fork --pid --mount-proc --kill-child \
135              --uts --ipc --mount --cgroup \
136              chroot $TARGET $START
137         ;;
138     stop)
139         umount $TARGET
140         [ -e $IMAGE ] && umount $MOUNT
141         ip netns del $NSNAME
142         rmdir /sys/fs/cgroup/$NAME
143         ;;
144     *)
145         usage
146         ;;
147 esac