libvirt-usb-hotplug/libvirt-usb-hotplug.bash

104 lines
2.6 KiB
Bash

#!/bin/bash
# libvirt-usb-hotplug.bash
# This script can be used to hotplug USB devices to libvirt virtual
# machines from udev rules with systemd services.
# References:
# - https://github.com/olavmrk/usb-libvirt-hotplug
# - http://kicherer.org/joomla/index.php/en/blog/48-automatic-hotplugging-of-usb-devices-for-libvirt-managed-vms
# - https://blog.tjll.net/systemd-for-device-activation-and-media-archiving/
# Abort script execution on errors
set -e
readvars() {
PROG="$(basename "$0")"
ACTION=$1
IFS='_' read DOMAIN ID_VENDOR_ID ID_MODEL_ID <<< "$2"
if [ -z "${DOMAIN}" ]; then
echo "Missing libvirt domain parameter for ${PROG}." >&2
exit 1
fi
}
findmatch() {
# Iterate through devlist to find match
for i in "${devlist[@]}"; do
# If we find a match, return successfully
[ "${ID_VENDOR_ID}:${ID_MODEL_ID}" = "$i" ] && return 0
# If a match can't be found, keep looping
continue
done
# If no match was found, return unsuccessfully
return 1
}
case ${ACTION} in
add)
readvars
COMMAND='attach-device'
;;
remove)
readvars
COMMAND='detach-device'
;;
match)
# This section is responsible for determining if the corresponding udev rule
# matches against the device in question. Udev watches exit code to determine
# if a match was made.
#
# Ref: udev(7) manpage
# Read the config file
source /etc/libvirt-usb-hotplug.conf
# If we should match against any usb device, just exit successfully
[ "$matchall" = "true" ] && exit 0
# If the devlist array is empty, no matches can be made
[ ${#devlist[@]} = 0 ] && exit 1
# If targetdomain is unset or empty, exit unsuccessfully
[ ! -v targetdomain ] && exit 1
[ "$targetdomain" = "" ] && exit 1
# Print the targetdomain so it can be picked back up by the udev rule
printf "%s" "$targetdomain"
case $listmode in
whitelist)
# findmatch() returns 0 if match found, and 1 if match not found
findmatch
exit $?
;;
blacklist)
# Reverse the exit status of findmatch() for blacklist mode
! findmatch
exit $?
;;
*)
# If listmode is unset, or set improperly, exit unsuccessfully
exit 1
;;
esac
;;
*)
echo "Invalid script ACTION: ${ACTION}" >&2
exit 1
;;
esac
# Run the appropriate virsh-command, and ask it to read the update XML from stdin.
echo "Running virsh ${COMMAND} ${DOMAIN} --persistent for USB vendor=0x${ID_VENDOR_ID} product=0x${ID_MODEL_ID}:" >&2
(virsh "${COMMAND}" "${DOMAIN}" /dev/stdin --persistent || true) <<END
<hostdev mode='subsystem' type='usb' managed='yes'>
<source startupPolicy='optional'>
<vendor id='0x${ID_VENDOR_ID}' />
<product id='0x${ID_MODEL_ID}' />
</source>
</hostdev>
END