Skip to content

runlevel(8) reads whatever utmp you point it at

 2 min A container supervisor read a guest’s shutdown intent from the guest’s own utmp, because runlevel accepts an alternate path.
Published

Feb 10, 2010

Before LXC had a native graceful-shutdown command, my container supervisor used an argument to runlevel(8) to read the guest’s shutdown intent.

runlevel(8) takes an argument: a path to an alternate utmp file. Point it at another system’s utmp and it reports that system’s runlevel. A container’s rootfs/var/run/utmp is exactly such a file, and this guest’s SysV init writes its halt/reboot runlevel there from inside.

In the LXC thread I opened by asking how to make container init finish dying and had already proposed treating one remaining process as init at the end of shutdown. Michael H. Warfield supplied the guest-utmp ? 0 / ? 6 distinction and combined it with that test in a periodic host script. Daniel Lezcano then replaced the polling with inotifywait on the utmp file while retaining the cgroup process-count check. The initial public vps-lxc commit combined those pieces.

So the container supervisor watched that file and asked it the question:

inotifywait -qqt 300 ${utmp}
if [ "$(wc -l 2>/dev/null < ${VPS_DEV}/${VPS_NAME}/${VPS_MONITOR_TARGET}/tasks)" = 1 ]; then
    runlevel="$(runlevel ${utmp})"
    case "${runlevel}" in
    ??0)  lxc-stop -n "${VPS_MONITOR_TARGET}"; break ;;
    ??6)  lxc-stop -n ${VPS_MONITOR_TARGET}
          lxc-wait -n ${VPS_MONITOR_TARGET} -s STOPPED
          lxc-start -d -n ${VPS_MONITOR_TARGET} -o ... ;;
    esac
fi

Three surfaces, composed: inotifywait waits for a guest-utmp change or a five-minute timeout; the guest cgroup’s tasks file dropping to one line means every process but init has drained, i.e. shutdown is essentially complete; and runlevel on the guest’s utmp then names which shutdown it was. ??0 means halt, so the host stops the container. ??6 means reboot—and since a container’s init cannot reboot the kernel, the host performs the reboot for it: stop, wait for STOPPED, start again.

The other end of that channel is built by the same tool. The template’s inittab is rewritten at build time so signals mean what the host expects:

sed -i -e "s,agetty,\\0 -n -l /bin/autologin,g" \
       -e "/^[^irsc]\|^c[^1]\|^$/d" rootfs/etc/inittab
cat <<EOF >> rootfs/etc/inittab
p6::ctrlaltdel:/sbin/init 6
p0::powerfail:/sbin/init 0
EOF

The monitor is spawned per container by start, via nohup ... monitor $target &, one daemon watching one guest.

The monitor depended on SysV init and on init being the final task in the guest. Within that arrangement, shutdown intent was already in a file the host could read with a standard tool.