104 lines
2.6 KiB
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
|