Systemd overriding libvirt-guests shutdown behavior
libvirt-guets is a simple script that runs at startup and shutdown to either suspend, shutdown or resume all VMs registered with the host.
Usually this blanket approach works fine, but I have one VM that must be shutdown fully instead of suspended since it runs Home Assistant and needs to release the a USB Zigbee adapter otherwise the automatic lights won’t work again until I do this manually.
Modify the main script?
Changing /usr/lib/libvirt/libvirt-guests.sh
to special case my one VM would definitely work but this is a brittle fix and needs to be preserved over updates.
Build another service?
The first suggestion out of chatGPT was to just create an additional service file and have it execute before the libvirt-guest.service
kicks in - that way the VM would already be shutdown when it gets looked at.
I got pretty far with this but gave up. It’s not a great approach as it introduces complexity and timing issues since action is only needed on shutdown.
Systemd drop-in + script
Using the systemd “drop-in” mechanism, its possible to override service actions without changing the files placed on the system from upstream. I ended up doing this:
Drop-in: /etc/systemd/system/libvirt-guests.service.d/homeassistant.conf
[Service]
# reset because https://github.com/systemd/systemd/issues/4148
ExecStop=
ExecStop=/usr/local/bin/shutdown_homeassistant.sh
# upstream from /lib/systemd/system/libvirt-guests.service
ExecStop=/usr/lib/libvirt/libvirt-guests.sh stop
Script: /usr/local/bin/shutdown_homeassistant.sh
#!/bin/bash
MAX_WAIT=300
WAITED=0
COLS=40
VM_NAME=homeassistant
function vm_stopped() {
if virsh domstate $VM_NAME | grep -q "shut off" ; then
return 0
else
return 1
fi
}
if vm_stopped ; then
echo "vm already stopped: $VM_NAME"
else
virsh shutdown $VM_NAME
echo "waiting for VM shutdown: $VM_NAME"
while [ $WAITED -lt $MAX_WAIT ] ; do
if vm_stopped ; then
echo
echo "shutdown ok: $VM_NAME"
break
fi
WAITED=$((WAITED + 1))
echo -n "."
if [ $((WAITED % COLS)) == 0 ] ; then
echo
fi
sleep 1
done
echo
fi
Notes
virsh shutdown ...
returns immediately, You need to also poll VM status to watch for completed shutdown before lettinglibvirt-guests
run or else I observed a race condition- Follow the output on
journalctl -u libvirt-guests
ExitStop=
are executed sequentially within the context of the drop-in- You can have multiple drop-in files, and order can be controlled by prefixing files with numbers if needed
Closing
With the above in place, I can finally reboot the host with confidence and without breaking all Zigbee devices until I login to fix.