commit c896225cbdac42ded0b11d39d41a8eb199bdd11c Author: David Thurstenson Date: Sat Jul 4 20:19:50 2020 -0500 Initial Commit diff --git a/PKGBUILD b/PKGBUILD new file mode 100644 index 0000000..fb3a521 --- /dev/null +++ b/PKGBUILD @@ -0,0 +1,33 @@ +# Maintainer: David Thurstenson (thurstylark@gmail.com) +pkgname=libvirt-usb-hotplug +pkgver=0.1 +pkgrel=1 +pkgdesc="Attach and detach USB devices from libvirt guests based on udev rules" +arch=(any) +url="https://git.thurstylark.com/libvirt-usb-hotplug.git" +license=('GPL') +depends=('systemd' + 'udev' + 'libvirt') +backup=() +source=("libvirt-usb-hotplug.bash" + "libvirt-usb-hotplug.conf" + "libvirt-usb-hotplug.rules" + "libvirt-usb-hotplug@.service" +) +sha256sums=() + +package() { + install -Dm755 \ + "${srcdir}"/libvirt-usb-hotplug.bash \ + "${pkgdir}"/usr/bin/libvirt-usb-hotplug + install -Dm644 \ + "${srcdir}"/libvirt-usb-hotplug.conf \ + "${pkgdir}"/etc/libvirt-usb-hotplug.conf + install -Dm644 \ + "${srcdir}"/libvirt-usb-hotplug.rules \ + "${pkgdir}"/usr/lib/udev/rules.d/99-libvirt-usb-hotplug.rules + install -Dm644 \ + "${srcdir}"/libvirt-usb-hotplug@.service \ + "${pkgdir}"/usr/lib/systemd/system/libvirt-usb-hotplug@.service +} diff --git a/libvirt-usb-hotplug.bash b/libvirt-usb-hotplug.bash new file mode 100644 index 0000000..67abf4f --- /dev/null +++ b/libvirt-usb-hotplug.bash @@ -0,0 +1,103 @@ +#!/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 diff --git a/libvirt-usb-hotplug.conf b/libvirt-usb-hotplug.conf new file mode 100644 index 0000000..75cf237 --- /dev/null +++ b/libvirt-usb-hotplug.conf @@ -0,0 +1,25 @@ +# libvirt-usb-hotplug.conf + +# Name of libvirt domain to which the device in question will be attached +# Default if unset: (disables matching) +targetdomain=win10 + +# Match against every usb device +# Valid values: true, false +# Default if unset: false +matchall=true + +# Should the list be a whitelist, or blacklist? +# Valid values: whitelist, blacklist +# Default if unset: (disables matching) +listmode=blacklist + +# List of devices to match against +# Value format: ':' +devlist=( + '0b05:1872' # ASUSTek Computer, Inc. AURA LED Controller + '0b05:185c' # ASUSTek Computer, Inc. Bluetooth Radio +) + + +# vim: set ft=bash: diff --git a/libvirt-usb-hotplug.rules b/libvirt-usb-hotplug.rules new file mode 100644 index 0000000..7f75732 --- /dev/null +++ b/libvirt-usb-hotplug.rules @@ -0,0 +1,13 @@ +# libvirt-usb-hotplug.rules + +ACTION=="add", \ + SUBSYSTEM=="usb", \ + PROGRAM=="/usr/bin/libvirt-usb-hotplug match" \ + SYMLINK+="libvirt_%c_%E{ID_VENDOR_ID}_%E{ID_MODEL_ID}", \ + TAG+="systemd", \ + ENV{SYSTEMD_WANTS}="libvirt_%c_%E{ID_VENDOR_ID}_%E{ID_MODEL_ID}" + +ACTION=="remove", \ + SUBSYSTEM=="usb", \ + ENV{PRODUCT}=="46d/c215/*", \ + TAG+="systemd" diff --git a/libvirt-usb-hotplug@.service b/libvirt-usb-hotplug@.service new file mode 100644 index 0000000..c85dfe9 --- /dev/null +++ b/libvirt-usb-hotplug@.service @@ -0,0 +1,15 @@ +[Unit] +Description=Libvirt guest usb hotplug: %I +After=libvirtd.service +BindsTo=dev-libvirt_%i.device +After=dev-libvirt_%i.device +Requisite=dev-libvirt_%i.device + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/usr/bin/libvirt-usb-hotplug add %I +ExecStop=/usr/bin/libvirt-usb-hotplug remove %I + +[Install] +WantedBy=dev-libvirt_%i.device