#!/bin/bash # make sure this was run as root if [ $UID -ne 0 ] ; then echo "Need to be root; trying sudo" exec sudo env BUILD_CONFIG=$BUILD_CONFIG $0 "$@" fi . config || exit 0 # Spawn a systemd container that boots the machine, then run the given # command. We can't execute systemd-nspawn directly, because that # only allows us to either boot the machine, or run a command (not # both). Instead let's execute systemd-nspawn in a transient systemd # unit, then enter it using machinectl. UNIT=nilmbuntu-run-$VERSION MACH=nilmbuntu-$VERSION setup_networking() { # We use a virtual ethernet adapter -- this requires that # systemd-networkd is installed and running on the host. if ! systemctl is-active systemd-networkd ; then echo "Starting systemd-networkd" systemctl start systemd-networkd fi # However, the current systemd-networkd from Debian is broken and # won't enable masquerading -- so do it manually IFACE=$(ip -4 route list default | head -1 | awk '{print $5}') echo 1 > /proc/sys/net/ipv4/ip_forward iptables -t nat -D POSTROUTING -o $IFACE -j MASQUERADE >/dev/null || true iptables -t nat -A POSTROUTING -o $IFACE -j MASQUERADE } kill_container() { # Kill any running container if systemctl --quiet is-active $UNIT ; then echo "Stopping container..." # We could use "machinectl terminate", but that will wait # for a clean shutdown or timeout; we don't need a clean # shutdown, so send a SIGTERM twice to get systemd-nspawn # to terminate pretty quickly systemctl kill $UNIT sleep 2 systemctl kill $UNIT # Then wait for it to really stop systemctl stop $UNIT fi # If systemd-nspawn returned with a failure code, # the transient service unit file will stick around, # so make sure we clear that. if systemctl --quiet is-failed $UNIT ; then systemctl reset-failed $UNIT fi } start_container() { kill_container echo "Starting container..." systemd-run --unit=$UNIT systemd-nspawn \ --quiet \ --keep-unit \ --boot \ --network-veth \ --directory $(realpath $FS) \ --machine $MACH echo "Waiting..." while ! env SYSTEMD_LOG_LEVEL=0 machinectl shell $MACH /bin/true ; do sleep 0.1 done } FAILED=0 run() { # Run a command inside the container echo "+" "$1" # machinectl doesn't propagate return codes, so append something # to the command that saves the result of what we ran. echo "99" > $FS/jim-cmd-result CMD="$1 ; echo \$? > /jim-cmd-result" # Run it env SYSTEMD_LOG_LEVEL=notice machinectl \ shell $MACH /usr/bin/env IN_CHROOT=1 \ bash -c "$CMD" # Check result RET=$(cat $FS/jim-cmd-result) rm -f $FS/jim-cmd-result if [ $RET -ne 0 ] && [ "$1" != "exec bash" ] ; then printf "%s\n" "----------- WARNING: failed with exit code $RET" FAILED=$RET sleep 5 fi } set -e setup_networking start_container run "resolvconf --disable-updates" run "echo 'nameserver 8.8.8.8' > /run/resolvconf/resolv.conf" run "echo '127.0.0.1 localhost' > /etc/hosts" run "cat /etc/hosts.nilm >>/etc/hosts 2>/dev/null || true" run "hostnamectl --transient set-hostname nilmbuntu" #run "dbus-uuidgen > /var/lib/dbus/machine-id" #run "dpkg-divert --local --rename --add /sbin/initctl" #run "ln -sf /bin/true /sbin/initctl" #run "dpkg-divert --local --rename --add /usr/sbin/update-grub" #run "ln -sf /bin/true /usr/sbin/update-grub" set +e if [ -z "$1" ] ; then run "exec bash" else run "$1" fi echo "Cleaning up..." # Manually clean up some things that show up after booting an image # and installing packages. This doesn't get everything, but what's # left should be fine. run "apt-get clean" run "> /etc/machine-id" run "rm -f /core /boot/grub/grubenv" run "rm -f /var/lib/systemd/random-seed" run "rm -f /var/lib/ubuntu-drivers-common/last_gfx_boot" run "rm -f /var/lib/NetworkManager/*" run "rm -f /root/.bash_history" #run "rm /sbin/initctl" #run "dpkg-divert --rename --remove /sbin/initctl" #run "rm /usr/sbin/update-grub" #run "dpkg-divert --rename --remove /usr/sbin/update-grub" #run "rm /var/lib/dbus/machine-id" #run "> /etc/resolv.conf" kill_container echo "Done" if [ $FAILED -ne 0 ] ; then exit $FAILED fi exit 0