commit
c896225cbd
5 changed files with 189 additions and 0 deletions
@ -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 |
|||
} |
@ -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 |
|||
<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 |
@ -0,0 +1,25 @@ |
|||
# libvirt-usb-hotplug.conf |
|||
|
|||
# Name of libvirt domain to which the device in question will be attached |
|||
# Default if unset: <none> (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: <none> (disables matching) |
|||
listmode=blacklist |
|||
|
|||
# List of devices to match against |
|||
# Value format: '<vendor_id>:<device_id>' |
|||
devlist=( |
|||
'0b05:1872' # ASUSTek Computer, Inc. AURA LED Controller |
|||
'0b05:185c' # ASUSTek Computer, Inc. Bluetooth Radio |
|||
) |
|||
|
|||
|
|||
# vim: set ft=bash: |
@ -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" |
@ -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 |
Loading…
Reference in new issue