dom0/qvm-usb: detach operation appears to work

This commit is contained in:
Alexandre Bezroutchko 2012-10-12 23:34:18 +02:00
parent 89b78d9426
commit 9d77b3dd3c
5 changed files with 91 additions and 28 deletions

View File

@ -74,7 +74,7 @@ Known issues
* Virtual USB devices (ones created by PVUSB frontend) may be listed * Virtual USB devices (ones created by PVUSB frontend) may be listed
* The installation/configuration is not persistent, not retained between reboots * The installation/configuration is not persistent, not retained between reboots
* No logging / audit trail? * No logging / audit trail?
* When an attached device is physically unplugged, USB port remains mapped but not displayed in the list * When an attached device is physically unplugged, USB port remains mapped but not displayed in the list. If device is plugged back it continues to work. Unlisted device cannot be detached.
* We are not attaching actual devices, but USB ports (different behavior from VMWare, might be confusing) * We are not attaching actual devices, but USB ports (different behavior from VMWare, might be confusing)
* After device is detached from the frontend and returned back to the backend it is not usable there * After device is detached from the frontend and returned back to the backend it is not usable there

View File

@ -494,7 +494,7 @@ def usb_list():
xs.transaction_end(xs_trans) xs.transaction_end(xs_trans)
return devices_list return devices_list
def usb_check_attached(backend_vm, device): def usb_check_attached(xs_trans, backend_vm, device):
""" """
Checks if the given device in the given backend attached to any frontend. Checks if the given device in the given backend attached to any frontend.
Parameters: Parameters:
@ -503,12 +503,11 @@ def usb_check_attached(backend_vm, device):
Returns None or a dictionary: Returns None or a dictionary:
vm - the name of the frontend domain vm - the name of the frontend domain
xid - xid of the frontend domain xid - xid of the frontend domain
frontend - frontend device number frontend - frontend device number FIXME
devid - frontend port number devid - frontend port number FIXME
""" """
# sample xs content: /local/domain/0/backend/vusb/4/0/port/1 = "7-5" # sample xs content: /local/domain/0/backend/vusb/4/0/port/1 = "7-5"
attached_dev = None attached_dev = None
xs_trans = xs.transaction_start()
vms = xs.ls(xs_trans, '/local/domain/%d/backend/vusb' % backend_vm) vms = xs.ls(xs_trans, '/local/domain/%d/backend/vusb' % backend_vm)
if vms is None: if vms is None:
xs.transaction_end(xs_trans) xs.transaction_end(xs_trans)
@ -540,7 +539,6 @@ def usb_check_attached(backend_vm, device):
continue continue
attached_dev = {"xid":int(vm), "frontend": frontend, "devid": device, "vm": vm_name} attached_dev = {"xid":int(vm), "frontend": frontend, "devid": device, "vm": vm_name}
break break
xs.transaction_end(xs_trans)
return attached_dev return attached_dev
#def usb_check_frontend_busy(vm, front_dev, port): #def usb_check_frontend_busy(vm, front_dev, port):
@ -551,12 +549,12 @@ def usb_check_attached(backend_vm, device):
# # return xs.read('', '/local/domain/%d/device/vusb/%d/state' % (vm.xid, frontend)) == '4' # # return xs.read('', '/local/domain/%d/device/vusb/%d/state' % (vm.xid, frontend)) == '4'
# return False # return False
def usb_find_unused_frontend(backend_vm_xid, vm_xid): def usb_find_unused_frontend(xs_trans, backend_vm_xid, vm_xid):
""" """
Find an unused frontend/port to link the given backend with the given frontend. Find an unused frontend/port to link the given backend with the given frontend.
Create new frontend if needed. Creates new frontend if needed.
Returns frontend specification in <device>-<port> format.
""" """
xs_trans='' # FIXME
last_frontend_dev = -1 last_frontend_dev = -1
frontend_devs = xs.ls(xs_trans, "/local/domain/%d/device/vusb" % vm_xid) frontend_devs = xs.ls(xs_trans, "/local/domain/%d/device/vusb" % vm_xid)
@ -589,19 +587,23 @@ def usb_find_unused_frontend(backend_vm_xid, vm_xid):
def usb_attach(vm, backend_vm, device, frontend=None, auto_detach=False, wait=True): def usb_attach(vm, backend_vm, device, frontend=None, auto_detach=False, wait=True):
device_attach_check(vm, backend_vm, device, frontend) device_attach_check(vm, backend_vm, device, frontend)
xs_trans = xs.transaction_start()
if frontend is None: if frontend is None:
frontend = usb_find_unused_frontend(backend_vm.xid, vm.xid) frontend = usb_find_unused_frontend(xs_trans, backend_vm.xid, vm.xid)
else: else:
# Check if any device attached at this frontend # Check if any device attached at this frontend
#if usb_check_frontend_busy(vm, frontend): #if usb_check_frontend_busy(vm, frontend):
# raise QubesException("Frontend %s busy in VM %s, detach it first" % (frontend, vm.name)) # raise QubesException("Frontend %s busy in VM %s, detach it first" % (frontend, vm.name))
xs.transaction_end(xs_trans)
raise NotImplementedError("Explicit USB frontend specification is not implemented yet") raise NotImplementedError("Explicit USB frontend specification is not implemented yet")
# Check if this device is attached to some domain # Check if this device is attached to some domain
attached_vm = usb_check_attached(backend_vm.xid, device) attached_vm = usb_check_attached(xs_trans, backend_vm.xid, device)
xs.transaction_end(xs_trans)
if attached_vm: if attached_vm:
if auto_detach: if auto_detach:
usb_detach(None, attached_vm['devid'], vm_xid=attached_vm['xid']) usb_detach(backend_vm, attached_vm)
else: else:
raise QubesException("Device %s from %s already connected to VM %s as %s" % (device, backend_vm.name, attached_vm['vm'], attached_vm['frontend'])) raise QubesException("Device %s from %s already connected to VM %s as %s" % (device, backend_vm.name, attached_vm['vm'], attached_vm['frontend']))
@ -609,11 +611,12 @@ def usb_attach(vm, backend_vm, device, frontend=None, auto_detach=False, wait=Tr
xl_cmd = [ '/usr/lib/qubes/xl-qvm-usb-attach.py', str(vm.xid), device, frontend, str(backend_vm.xid) ] xl_cmd = [ '/usr/lib/qubes/xl-qvm-usb-attach.py', str(vm.xid), device, frontend, str(backend_vm.xid) ]
subprocess.check_call(xl_cmd) subprocess.check_call(xl_cmd)
def usb_detach(vm, frontend, vm_xid = None): def usb_detach(backend_vm, attachment):
raise NotImplementedError() xl_cmd = [ '/usr/lib/qubes/xl-qvm-usb-detach.py', str(attachment['xid']), attachment['devid'], attachment['frontend'], str(backend_vm.xid) ]
subprocess.check_call(xl_cmd)
def usb_detach_all(vm, vm_xid = None): def usb_detach_all(vm):
raise NotImplementedError() raise NotImplementedError("Detaching all devices from a given VM is not implemented yet")
####### QubesWatch ###### ####### QubesWatch ######

View File

@ -30,8 +30,8 @@ def main():
usage = "usage: %prog -l [options]\n"\ usage = "usage: %prog -l [options]\n"\
"usage: %prog -a [options] <vm-name> <device-vm-name>:<device>\n"\ "usage: %prog -a [options] <vm-name> <device-vm-name>:<device>\n"\
"usage: %prog -d [options] <device-vm-name>:<device>\n"\ "usage: %prog -d [options] <device-vm-name>:<device>\n"\
"usage: %prog -d [options] <vm-name>\n"\
"List/set VM USB devices." "List/set VM USB devices."
# "usage: %prog -d [options] <vm-name>\n"\
parser = OptionParser (usage) parser = OptionParser (usage)
parser.add_option ("-l", "--list", action="store_true", dest="do_list", default=False) parser.add_option ("-l", "--list", action="store_true", dest="do_list", default=False)
@ -69,6 +69,7 @@ def main():
vm = qvm_collection.get_vm_by_name(args[0]) vm = qvm_collection.get_vm_by_name(args[0])
if vm is None: if vm is None:
parser.error ("Invalid VM name: %s" % args[0]) parser.error ("Invalid VM name: %s" % args[0])
# FIXME: here we assume that device is always in form "domain:dev", which can be changed in the future # FIXME: here we assume that device is always in form "domain:dev", which can be changed in the future
if args[1].find(":") < 0: if args[1].find(":") < 0:
parser.error ("Invalid device syntax: %s" % args[1]) parser.error ("Invalid device syntax: %s" % args[1])
@ -78,6 +79,7 @@ def main():
dev = dev_list[args[1]] dev = dev_list[args[1]]
backend_vm = qvm_collection.get_vm_by_name(dev['vm']) backend_vm = qvm_collection.get_vm_by_name(dev['vm'])
assert backend_vm is not None assert backend_vm is not None
kwargs = {} kwargs = {}
# if options.frontend: # if options.frontend:
# kwargs['frontend'] = options.frontend # kwargs['frontend'] = options.frontend
@ -95,29 +97,37 @@ def main():
# Check if provided name is VM # Check if provided name is VM
vm = qvm_collection.get_vm_by_name(args[0]) vm = qvm_collection.get_vm_by_name(args[0])
if vm is not None: if vm is not None:
kwargs = {} #kwargs = {}
if options.frontend: #if options.frontend:
kwargs['frontend'] = options.frontend # kwargs['frontend'] = options.frontend
usb_detach(vm, **kwargs) # usb_detach(vm, **kwargs)
else: #else:
usb_detach_all(vm) usb_detach_all(vm)
else: else:
# Maybe device? # Maybe usbvm:device?
# FIXME: nasty copy-paste from attach code half a page above
# FIXME: here we assume that device is always in form "domain:dev", which can be changed in the future
if args[0].find(":") < 0:
parser.error ("Invalid device syntax: %s" % args[0])
dev_list = usb_list() dev_list = usb_list()
if not args[0] in dev_list.keys(): if not args[0] in dev_list.keys():
parser.error ("Invalid VM or device name: %s" % args[0]) parser.error ("Invalid device name: %s" % args[0])
dev = dev_list[args[0]] dev = dev_list[args[0]]
attached_to = usb_check_attached(backend_xid, dev['device']) backend_vm = qvm_collection.get_vm_by_name(dev['vm'])
assert backend_vm is not None
attached_to = usb_check_attached('', backend_vm.xid, dev['device'])
if attached_to is None: if attached_to is None:
print >> sys.stderr, "WARNING: Device not connected to any VM" print >> sys.stderr, "WARNING: Device not connected to any VM"
exit(0) exit(0)
usb_detach(None, attached_to['devid'], vm_xid=attached_to['xid']) usb_detach(backend_vm, attached_to)
else: else:
if len(args) > 0: if len(args) > 0:
parser.error ("Too many parameters") parser.error ("Too many parameters")
# do_list # do_list
for dev in usb_list().values(): for dev in usb_list().values():
attached_to = usb_check_attached(dev['xid'], dev['device']) attached_to = usb_check_attached('', dev['xid'], dev['device'])
attached_to_str = "" attached_to_str = ""
if attached_to: if attached_to:
attached_to_str = " (attached to %s:%s)" % (attached_to['vm'], attached_to['frontend']) attached_to_str = " (attached to %s:%s)" % (attached_to['vm'], attached_to['frontend'])

View File

@ -7,5 +7,6 @@
# Copy files # Copy files
cp misc/xl-qvm-usb-attach.py /usr/lib/qubes/xl-qvm-usb-attach.py cp misc/xl-qvm-usb-attach.py /usr/lib/qubes/xl-qvm-usb-attach.py
cp misc/xl-qvm-usb-detach.py /usr/lib/qubes/xl-qvm-usb-detach.py
cp dom0/qvm-core/qubesutils.py /usr/lib64/python2.6/site-packages/qubes/qubesutils.py cp dom0/qvm-core/qubesutils.py /usr/lib64/python2.6/site-packages/qubes/qubesutils.py
cp dom0/qvm-tools/qvm-usb /usr/bin/qvm-usb cp dom0/qvm-tools/qvm-usb /usr/bin/qvm-usb

49
misc/xl-qvm-usb-detach.py Executable file
View File

@ -0,0 +1,49 @@
#!/usr/bin/python
##
## This script is for dom0
## The syntax is modelled after "xl block-attach"
## FIXME: should be modelled after block-detach instead
##
import sys
import os
import xen.lowlevel.xl
# parse command line
if (len(sys.argv)<4) or (len(sys.argv)>5):
print 'usage: xl-qvm-usb-detach.py <frontendvm-xid> <backendvm-device> <frontendvm-device> [<backendvm-xid>]'
sys.exit(1)
frontendvm_xid=sys.argv[1]
backendvm_device=sys.argv[2]
frontend=sys.argv[3].split('-')
if len(frontend)!=2:
print 'Error: frontendvm-device must be in <controller>-<port> format'
sys.exit(1)
(controller, port)=frontend
if len(sys.argv)>4:
backendvm_xid=int(sys.argv[4])
backendvm_name=xen.lowlevel.xl.ctx().domid_to_name(backendvm_xid)
else:
backendvm_xid=0
cmd = "/usr/lib/qubes/vusb-ctl.py unbind '%s'" % backendvm_device
if backendvm_xid == 0:
os.system("sudo %s" % cmd)
else:
from qubes.qubes import QubesVmCollection
qvm_collection = QubesVmCollection()
qvm_collection.lock_db_for_reading()
qvm_collection.load()
qvm_collection.unlock_db()
# launch
qvm_collection.get_vm_by_name(backendvm_name).run("root: %s" % cmd)
# FIXME: command injection
os.system("xenstore-write /local/domain/%s/backend/vusb/%s/%s/port/%s ''"
% (backendvm_xid, frontendvm_xid, controller, port))