Last active
January 31, 2024 11:02
-
-
Save TobleMiner/9a06eaba59e37ed1c7d16edd8b10d41e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ACTION=="bind", SUBSYSTEMS=="usb", RUN="/usr/local/sbin/libvirt-hotplug.py usb" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import libvirt | |
import xml.etree.ElementTree as ET | |
import sys | |
import os | |
import re | |
RE_HEX_SUFFIX = re.compile('([0-9a-fA-F]+)$') | |
def hex_suffix(s): | |
match = RE_HEX_SUFFIX.search(s) | |
if match == None: | |
return '' | |
return match.groups()[0] | |
def usage(bin): | |
print("Usage: {} <bus>".format(bin)) | |
if len(sys.argv) < 2: | |
print("Too few arguments") | |
usage(sys.argv[0]) | |
sys.exit(1) | |
bus = sys.argv[1] | |
if bus != 'usb': | |
print('Sorry, atm only usb devices are supported') | |
sys.exit(1) | |
if not 'ID_VENDOR_ID' in os.environ or \ | |
not 'ID_MODEL_ID' in os.environ: | |
sys.exit() | |
vid = os.environ['ID_VENDOR_ID'].lower() | |
pid = os.environ['ID_MODEL_ID'].lower() | |
conn = libvirt.open(None) | |
if not conn: | |
raise Exception("Failed to connect to libvirt") | |
def handle_dev_usb(hostdev): | |
vendor = hostdev.find("./source/vendor") | |
product = hostdev.find("./source/product") | |
if vendor == None or product == None: | |
print("Invalid XML, needs 'vendor' and 'product'") | |
return False | |
if not 'id' in vendor.attrib or not 'id' in product.attrib: | |
return False | |
candidate_vid = hex_suffix(vendor.attrib['id']).lower() | |
candidate_pid = hex_suffix(product.attrib['id']).lower() | |
return candidate_vid == vid and candidate_pid == pid | |
def hostdev_purge_address(dev): | |
source = dev.find('./source') | |
if source != None: | |
address = source.find('./address') | |
if address != None: | |
source.remove(address) | |
# Iterate through active domains | |
for domid in conn.listDomainsID(): | |
dom = conn.lookupByID(domid) | |
xml = ET.fromstring(dom.XMLDesc()) | |
# Retrieve all host devices on the bus | |
hostdevs = xml.findall(".//hostdev[@type='{}']".format(bus)) | |
for dev in hostdevs: | |
if handle_dev_usb(dev): | |
print("Attaching device [{}:{}] to {}".format(vid, pid, dom.name())) | |
devxml = ET.tostring(dev, encoding='unicode') | |
dom.detachDevice(devxml) | |
# Remove address node, libvirt will figure that one out by itself | |
hostdev_purge_address(dev) | |
devxml = ET.tostring(dev, encoding='unicode') | |
dom.attachDevice(devxml) | |
break |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment