From fc74c6a85bb499dddfe27b46eb8e9f575efc9d01 Mon Sep 17 00:00:00 2001 From: Alexandre Bezroutchko Date: Mon, 8 Oct 2012 00:44:40 +0200 Subject: [PATCH] dom0/qvm-usb: first implementation of qvm-usb --- dom0/qvm-core/qubesutils.py | 37 +++++++++++ dom0/qvm-tools/qvm-usb | 126 ++++++++++++++++++++++++++++++++++++ install_pvusb_hacks.sh | 7 +- 3 files changed, 168 insertions(+), 2 deletions(-) create mode 100755 dom0/qvm-tools/qvm-usb diff --git a/dom0/qvm-core/qubesutils.py b/dom0/qvm-core/qubesutils.py index d51dd2cc..2435df6a 100644 --- a/dom0/qvm-core/qubesutils.py +++ b/dom0/qvm-core/qubesutils.py @@ -398,6 +398,43 @@ def block_detach_all(vm, vm_xid = None): xl_cmd = [ '/usr/sbin/xl', 'block-detach', str(vm_xid), devid] subprocess.check_call(xl_cmd) +####### USB devices ###### + +def usb_check_attached(backend_vm, device, backend_xid = None): + return None + +def usb_list(): + device_re = re.compile(r"^[0-9]+-[0-9]+$") + # FIXME: any better idea of desc_re? + desc_re = re.compile(r"^.{1,255}$") + + devices_list = {} + + xs_trans = xs.transaction_start() + vm_list = xs.ls('', '/local/domain') + + for xid in vm_list: + vm_name = xs.read(xs_trans, '/local/domain/%s/name' % xid) + vm_devices = xs.ls(xs_trans, '/local/domain/%s/qubes-usb-devices' % xid) + if vm_devices is None: + continue + for device in vm_devices: + # Sanitize device id + if not device_re.match(device): + print >> sys.stderr, "Invalid device id in VM '%s'" % vm_name + continue + device_desc = xs.read(xs_trans, '/local/domain/%s/qubes-usb-devices/%s/desc' % (xid, device)) + if not desc_re.match(device_desc): + print >> sys.stderr, "Invalid %s device desc in VM '%s'" % (device, vm_name) + continue + visible_name = "%s:%s" % (vm_name, device) + devices_list[visible_name] = {"name": visible_name, "xid":int(xid), + "vm": vm_name, "device":device, + "desc":device_desc} + + xs.transaction_end(xs_trans) + return devices_list + ####### QubesWatch ###### def only_in_first_list(l1, l2): diff --git a/dom0/qvm-tools/qvm-usb b/dom0/qvm-tools/qvm-usb new file mode 100755 index 00000000..12c06ef9 --- /dev/null +++ b/dom0/qvm-tools/qvm-usb @@ -0,0 +1,126 @@ +#!/usr/bin/python2 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2010 Marek Marczykowski +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# + +from qubes.qubes import QubesVmCollection, QubesException +#from qubes.qubesutils import block_list,block_attach,block_detach,block_detach_all,block_check_attached +from qubes.qubesutils import usb_list,usb_check_attached +#from qubes.qubesutils import kbytes_to_kmg, bytes_to_kmg +from optparse import OptionParser +#import subprocess +import sys +import os + +def main(): + usage = "usage: %prog -l [options]\n"\ + "usage: %prog -a [options] :\n"\ + "usage: %prog -d [options] :\n"\ + "usage: %prog -d [options] \n"\ + "List/set VM USB devices." + + parser = OptionParser (usage) + parser.add_option ("-l", "--list", action="store_true", dest="do_list", default=False) + parser.add_option ("-a", "--attach", action="store_true", dest="do_attach", default=False) + parser.add_option ("-d", "--detach", action="store_true", dest="do_detach", default=False) +# parser.add_option ("-f", "--frontend", dest="frontend", +# help="Specify device name at destination VM [default: xvdi]") +# parser.add_option ("--no-auto-detach", dest="auto_detach", action="store_false", default=True, +# help="Fail when device already connected to other VM") + parser.add_option ("--force-root", action="store_true", dest="force_root", default=False, + help="Force to run, even with root privileges") + + (options, args) = parser.parse_args () + + if os.geteuid() == 0: + if not options.force_root: + print >> sys.stderr, "*** Running this tool as root is strongly discouraged, this will lead you in permissions problems." + print >> sys.stderr, "Retry as unprivileged user." + print >> sys.stderr, "... or use --force-root to continue anyway." + exit(1) + + if options.do_list + options.do_attach + options.do_detach > 1: + print >> sys.stderr, "Only one of -l -a -d is allowed!" + exit (1) + + if options.do_attach or options.do_detach: + qvm_collection = QubesVmCollection() + qvm_collection.lock_db_for_reading() + qvm_collection.load() + qvm_collection.unlock_db() + + if options.do_attach: + if (len (args) < 2): + parser.error ("You must provide device and vm name!") + vm = qvm_collection.get_vm_by_name(args[0]) + if vm is None: + 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 + if args[1].find(":") < 0: + parser.error ("Invalid device syntax" % args[1]) + dev_list = block_list() + if not args[1] in dev_list.keys(): + parser.error ("Invalid device name: %s" % args[1]) + dev = dev_list[args[1]] + backend_vm = qvm_collection.get_vm_by_name(dev['vm']) + assert backend_vm is not None + kwargs = {} + if options.frontend: + kwargs['frontend'] = options.frontend + kwargs['auto_detach'] = options.auto_detach + try: + usb_attach(vm, backend_vm, dev['device'], **kwargs) + except QubesException as e: + print >> sys.stderr, "ERROR: %s" % str(e) + sys.exit(1) + elif options.do_detach: + if (len (args) < 1): + parser.error ("You must provide device or vm name!") + # Check if provided name is VM + vm = qvm_collection.get_vm_by_name(args[0]) + if vm is not None: + kwargs = {} + if options.frontend: + kwargs['frontend'] = options.frontend + block_detach(vm, **kwargs) + else: + block_detach_all(vm) + else: + # Maybe device? + dev_list = block_list() + if not args[0] in dev_list.keys(): + parser.error ("Invalid VM or device name: %s" % args[0]) + dev = dev_list[args[0]] + attached_to = usb_check_attached(None, dev['device'], backend_xid = dev['xid']) + if attached_to is None: + print >> sys.stderr, "WARNING: Device not connected to any VM" + exit(0) + usb_detach(None, attached_to['devid'], vm_xid=attached_to['xid']) + else: + # do_list + for dev in usb_list().values(): + attached_to = usb_check_attached(None, dev['device'], backend_xid = dev['xid']) + attached_to_str = "" + if attached_to: + attached_to_str = " (attached to '%s' as '%s')" % (attached_to['vm'], attached_to['frontend']) + print "%s\t%s%s" % (dev['name'], dev['desc'], attached_to_str) + exit (0) + +main() diff --git a/install_pvusb_hacks.sh b/install_pvusb_hacks.sh index a12ba6e2..79635350 100755 --- a/install_pvusb_hacks.sh +++ b/install_pvusb_hacks.sh @@ -3,12 +3,15 @@ d=/home/abb/qubes-core # Install -ln -sf $d/misc/qubes_usb.rules /etc/udev/rules.d/99-qubes_usb.rules +cp $d/misc/qubes_usb.rules /etc/udev/rules.d/99-qubes_usb.rules for f in usb_add_change usb_remove ; do - ln -sf $d/misc/$f /usr/lib/qubes/$f + cp $d/misc/$f /usr/lib/qubes/$f done +cp $d/dom0/qvm-core/qubesutils.py /usr/lib64/python2.6/site-packages/qubes/qubesutils.py +cp $d/dom0/qvm-tools/qvm-usb /usr/bin/qvm-usb + udevadm control --reload-rules # Rerun