[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

[virt-tools-list] [PATCH] Allow watching for USB devices hotplug and attaching them to VMs



Hi all,

this is the patch to check for the USB device hotplugging, it currently
allows 2 types of watching for USB device hotplug - using HAL and udev
itself. There is a plan for DeviceKit support but I am not currently
having option to make it for DeviceKit so this option will be added in
time. Nevertheless the HAL and udev implementations are working now
but you may have to choose the desired type in the Preferences menu.
UDev type is working by watching for /dev tree changes using pyinotify
and querying udevadm so if you want to use udev type, you need to have
pyinotify module installed.

Michal
# HG changeset patch
# User Michal Novotny <minovotn redhat com>
# Date 1250170983 -7200
# Node ID f884d53152b16653fa0212be3348f0c8475b4568
# Parent  3e7fd1794965cc00bff5203c39281a7a9c02ee00
Allow watching for USB device hotplug and attaching them to currently active VMs

This is the patch to check for the USB device hotplugging, it currently
allows 2 types of watching for USB device hotplug - using HAL and udev
itself. There is a plan for DeviceKit support but I am not currently
having option to make it for DeviceKit so this option will be added in
time. Nevertheless the HAL and udev implementations are working now
but you may have to choose the desired type in the Preferences menu.
UDev type is working by watching for /dev tree changes using pyinotify
and querying udevadm so if you want to use udev type, you need to have
pyinotify module installed.

diff -r 3e7fd1794965 -r f884d53152b1 src/virtManager/config.py
--- a/src/virtManager/config.py	Wed Aug 05 16:06:01 2009 +0200
+++ b/src/virtManager/config.py	Thu Aug 13 15:43:03 2009 +0200
@@ -338,6 +338,16 @@
     def set_console_popup(self, pref):
         self.conf.set_int(self.conf_dir + "/console/popup", pref)
 
+    def on_usbwatch_type_changed(self, callback):
+        self.conf.notify_add(self.conf_dir + "/usbwatch-type", callback)
+    def get_usbwatch_type(self):
+        typ = self.conf.get_int(self.conf_dir + "/usbwatch-type")
+        if typ == None:
+            typ = 0
+        return typ
+    def set_usbwatch_type(self, pref):
+        self.conf.set_int(self.conf_dir + "/usbwatch-type", pref)
+
     def on_console_keygrab_changed(self, callback):
         self.conf.notify_add(self.conf_dir + "/console/keygrab", callback)
     def get_console_keygrab(self):
diff -r 3e7fd1794965 -r f884d53152b1 src/virtManager/details.py
--- a/src/virtManager/details.py	Wed Aug 05 16:06:01 2009 +0200
+++ b/src/virtManager/details.py	Thu Aug 13 15:43:03 2009 +0200
@@ -30,9 +30,11 @@
 import os
 import socket
 import cairo
+import time
 
 from virtManager.error import vmmErrorDialog
 from virtManager.addhardware import vmmAddHardware
+from virtManager.usbdevhelper import vmmUSBDevHelper
 from virtManager.choosecd import vmmChooseCD
 from virtManager.manager import build_shutdown_button_menu
 from virtManager.serialcon import vmmSerialConsole
@@ -129,6 +131,7 @@
         self.serial_tabs = []
         self.last_console_page = PAGE_CONSOLE
         self.ignorePause = False
+        self.lastTime = 0
 
         # Don't allowing changing network/disks for Dom0
         if self.vm.is_management_domain():
@@ -229,6 +232,10 @@
         self.vncViewer.connect("vnc-keyboard-ungrab", self._enable_modifiers)
         self.vnc_connected = False
 
+        self.usbdevhelper = vmmUSBDevHelper(sig_add=self.notify_usbdev_added,
+                                            sig_remove=self.notify_usbdev_removed,
+                                            helperType=self.config.get_usbwatch_type())
+
         self.notifyID = None
         self.notifyInterface = None
         try:
@@ -399,11 +406,60 @@
             self.notifyID = None
 
     def notify_action(self, i, action):
-        if self.notifyID is None or self.notifyID != i:
+        if self.notifyID is not None and self.notifyID == i:
+            if action == "dismiss":
+                self.config.set_console_grab_notify(False)
             return
 
-        if action == "dismiss":
-            self.config.set_console_grab_notify(False)
+        if self.notifyUsbID is not None and self.notifyUsbID == i:
+            if action == 'connect':
+                self.usbdevhelper.UnmountDevice( self.usbdevhelper.udi )
+                logging.debug("Connecting USB device to Virtual Machine ...")
+
+                if self.vm.is_active():
+                    logging.debug("Adding new host device XML: %s" % self.usbdevhelper.getXml())
+                    self.vm.attach_device( self.usbdevhelper.getXml() )
+
+            if action == 'disconnect':
+                logging.debug("Connecting USB device to host machine ...")
+
+    def notify_usbdev_removed(self, src):
+        if self.usbdevhelper.product_id is None or self.usbdevhelper.vendor_id is None:
+            return
+
+        if self.vm.is_active():
+            try:
+                self.vm.detach_device( self.usbdevhelper.getXml() )
+            except libvirt.libvirtError, e:
+                logging.debug("Cannot detach device: %s" % str(e) )
+
+        self.remove_usb_device()
+
+    def notify_usbdev_added(self, src):
+        # We have to add device only to currently active window
+        if (not self.vncViewer.flags() & gtk.VISIBLE or not self.vm.is_active()
+            or not self.topwin.is_active()):
+            return
+
+        if time.time() < self.lastTime + 10:
+            return
+
+        self.lastTime = time.time()
+        if self.notifyInterface:
+            try:
+                (x, y) = self.topwin.window.get_origin()
+                self.notifyUsbID = self.notifyInterface.Notify(self.topwin.get_title(),
+                                                            0,
+                                                            '',
+                                                            _("New USB device detected"),
+                                                            _("New USB device has been detected: %s. Would you like to connect the device to host machine or virtual machine?") % self.usbdevhelper.getVolumeInfoString(),
+                                                            ["connect", _("Connect to Virtual Machine"),
+                                                             "disconnect", _("Connect to host machine")],
+                                                            {"desktop-entry": "virt-manager",
+                                                             "x": x+200, "y": y},
+                                                            8 * 1000)
+            except Exception, e:
+                logging.error("Cannot popup notification " + str(e))
 
     def keygrab_changed(self, src, ignore1=None,ignore2=None,ignore3=None):
         if self.config.get_console_keygrab() == 2:
@@ -613,6 +669,7 @@
         return 1
 
     def exit_app(self, src):
+        self.usbdevhelper.Close()
         self.emit("action-exit-app")
 
     def is_visible(self):
@@ -1253,11 +1310,28 @@
         # TODO: if nothing selected, what to select? auto change device?
 
 
+    def remove_usb_device(self):
+        #RD: hostdev, {'vendor': {'id': '0x1307'}, 'product': {'id': '0x0163'}, 'type': 'usb'}
+        if not self.usbdevhelper.deviceAdded:
+            return
+        vendor_id = self.usbdevhelper.vendor_id
+        product_id = self.usbdevhelper.product_id
+        if vendor_id is None or product_id is None:
+           return
+
+        logging.debug("Removing host device attached when domain was already running ...")
+        try:
+            self.vm.remove_device('hostdev', { 'vendor': {'id': vendor_id}, 'product': {'id': product_id}, 'type': 'usb'} )
+        except ValueError, e:
+            logging.debug("Cannot remove device: %s" % str(e))
 
     def view_vm_status(self):
         status = self.vm.status()
         if status == libvirt.VIR_DOMAIN_SHUTOFF:
             self.activate_unavailable_page(_("Guest not running"))
+
+            if self.usbdevhelper.deviceAdded:
+                self.remove_usb_device()
         else:
             if status == libvirt.VIR_DOMAIN_CRASHED:
                 self.activate_unavailable_page(_("Guest has crashed"))
diff -r 3e7fd1794965 -r f884d53152b1 src/virtManager/engine.py
--- a/src/virtManager/engine.py	Wed Aug 05 16:06:01 2009 +0200
+++ b/src/virtManager/engine.py	Thu Aug 13 15:43:03 2009 +0200
@@ -377,6 +377,10 @@
     def exit_app(self):
         conns = self.connections.values()
         for conn in conns:
+            for uuid in conn['windowDetails'].keys():
+                if conn['windowDetails'][uuid].usbdevhelper:
+                    logging.debug("Closing usbdevhelper for UUID %s" % uuid)
+                    conn['windowDetails'][uuid].usbdevhelper.Close()
             conn["connection"].close()
         logging.debug("Exiting app normally.")
         gtk.main_quit()
diff -r 3e7fd1794965 -r f884d53152b1 src/virtManager/preferences.py
--- a/src/virtManager/preferences.py	Wed Aug 05 16:06:01 2009 +0200
+++ b/src/virtManager/preferences.py	Thu Aug 13 15:43:03 2009 +0200
@@ -21,6 +21,8 @@
 import gtk.glade
 import gobject
 
+from virtManager.usbdevhelper import vmmUSBDevHelper
+
 PREFS_PAGE_STATS    = 0
 PREFS_PAGE_VM_PREFS = 1
 
@@ -38,6 +40,7 @@
         self.topwin.hide()
 
         self.config.on_view_system_tray_changed(self.refresh_view_system_tray)
+        self.config.on_usbwatch_type_changed(self.refresh_usbwatch_type)
         self.config.on_console_popup_changed(self.refresh_console_popup)
         self.config.on_console_keygrab_changed(self.refresh_console_keygrab)
         self.config.on_console_scaling_changed(self.refresh_console_scaling)
@@ -48,6 +51,11 @@
         self.config.on_stats_enable_disk_poll_changed(self.refresh_disk_poll)
         self.config.on_stats_enable_net_poll_changed(self.refresh_net_poll)
 
+        tmp = vmmUSBDevHelper( helperType = vmmUSBDevHelper.HELPER_NONE )
+        self.usbwatch_caps = tmp.checkForCapabilities()
+        tmp.Close()
+
+        self.refresh_usbwatch_type()
         self.refresh_view_system_tray()
         self.refresh_update_interval()
         self.refresh_history_length()
@@ -61,6 +69,7 @@
 
         self.window.signal_autoconnect({
             "on_prefs_system_tray_toggled" : self.change_view_system_tray,
+            "on_prefs_usbwatch_type_changed": self.change_usbwatch_type,
             "on_prefs_stats_update_interval_changed": self.change_update_interval,
             "on_prefs_stats_history_length_changed": self.change_history_length,
             "on_prefs_console_popup_changed": self.change_console_popup,
@@ -87,6 +96,10 @@
     # Config Change Options #
     #########################
 
+    def refresh_usbwatch_type(self,ignore1=None,ignore2=None,ignore3=None,
+                              ignore4=None):
+        self.window.get_widget("prefs-system-usbwatch-type").set_active(self.config.get_usbwatch_type())
+
     def refresh_view_system_tray(self, ignore1=None, ignore2=None,
                                  ignore3=None, ignore4=None):
         val = self.config.get_view_system_tray()
@@ -132,6 +145,43 @@
     def change_history_length(self, src):
         self.config.set_stats_history_length(src.get_value_as_int())
 
+    def change_usbwatch_type(self, box):
+        choice = box.get_active()
+        errmsg = None
+        # We have to check whether we're having desired type supported
+        if choice == vmmUSBDevHelper.HELPER_HAL:
+            if not self.usbwatch_caps['HAL']:
+               errmsg = "Cannot connect to HAL interface using DBus.\n" \
+                        "Please check if HAL daemon is running ..."
+
+        if choice == vmmUSBDevHelper.HELPER_DK:
+            if not self.usbwatch_caps['DeviceKit']:
+               errmsg = "Cannot connect to DeviceKit interface using DBus.\n" \
+                        "Please check if DeviceKit is installed..."
+
+        if choice == vmmUSBDevHelper.HELPER_UDEV:
+            if not self.usbwatch_caps['UDev']:
+               errmsg = "Cannot run udev administration program (udevadm) or\n" \
+                        "load pyinotify module. Please check installation..."
+
+        # If we have an error message, we set helper to NONE (ie. disable watch)
+        if errmsg:
+            choice = vmmUSBDevHelper.HELPER_NONE
+
+        if self.config.get_usbwatch_type() != choice:
+            self.config.set_usbwatch_type(choice)
+            self.refresh_usbwatch_type()
+            from virtManager.error import vmmErrorDialog
+            err = vmmErrorDialog()
+            err.show_info( "Your USB device watcher type has been changed.\n" \
+                           "Please restart Virtual Machine Manager...")
+
+        # We display error message now to be the first dialog shown
+        if errmsg:
+            from virtManager.error import vmmErrorDialog
+            err = vmmErrorDialog()
+            err.show_info( "%s\nSetting to 'Disable watch' ..." % errmsg )
+
     def change_console_popup(self, box):
         self.config.set_console_popup(box.get_active())
     def change_console_keygrab(self, box):
diff -r 3e7fd1794965 -r f884d53152b1 src/virtManager/usbdevhelper.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/virtManager/usbdevhelper.py	Thu Aug 13 15:43:03 2009 +0200
@@ -0,0 +1,270 @@
+#
+# Copyright (C) 2009 Red Hat, Inc.
+#
+# 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.
+#
+
+import logging
+import os
+import sys
+import traceback
+import glob
+import time
+
+import gobject
+import dbus
+
+class vmmUSBDevHelper(gobject.GObject):
+    __gsignals__ = {
+        "usbdev-added": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+                         [str]),
+        "usbdev-removed": (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+                           [str]),
+    }
+
+    # Helper type constants, UDEV will use pipe to `udevadm` command
+    HELPER_NONE = 0
+    HELPER_HAL  = 1
+    HELPER_DK   = 2
+    HELPER_UDEV = 3
+
+    def __init__(self, sig_add = None, sig_remove = None, auto_unmount = False, helperType = HELPER_HAL):
+        self.__gobject_init__()
+        self.helperType = helperType
+
+        self.volume = {}
+        self.sig_add = sig_add
+        self.sig_remove = sig_remove
+        self.auto_unmount = auto_unmount
+        self.udi = None
+        self.deviceAdded = False
+        self.vendor_id = None
+        self.product_id = None
+
+        # If we're using HAL, we need to established dbus connection
+        if self.helperType == vmmUSBDevHelper.HELPER_HAL:
+            try:
+                self.bus = dbus.SystemBus()
+                obj = self.bus.get_object("org.freedesktop.Hal", "/org/freedesktop/Hal/Manager")
+                iface = dbus.Interface(obj, "org.freedesktop.Hal.Manager")
+
+                iface.connect_to_signal("DeviceAdded", self._sig_add_hal)
+                iface.connect_to_signal("DeviceRemoved", self._sig_remove_hal)
+
+                logging.debug("USBDevHelper type is set to HAL")
+            except Exception, e:
+                self.helperType = vmmUSBDevHelper.HELPER_DK
+
+        if self.helperType == vmmUSBDevHelper.HELPER_DK:
+                self.logging("USBDevHelper type cannot be set to DeviceKit yet, setting to UDev")
+                self.helperType == vmmUSBDevHelper.HELPER_UDEV
+
+        # If we're using udevadm command for that we also need pyinotify to be loaded
+        if self.helperType == vmmUSBDevHelper.HELPER_UDEV:
+            if self._checkForPyinotify():
+                from pyinotify import WatchManager, Notifier, ThreadedNotifier, ProcessEvent, IN_DELETE, IN_CREATE
+                from virtManager.usbdevhelperIn import DevFunc
+                self.helperType = vmmUSBDevHelper.HELPER_UDEV
+                self.wm = WatchManager()
+                self.notifier = Notifier(self.wm, DevFunc(self._sig_add_udev,
+                                           sig_remove, self.sig_udev_info), timeout=1)
+                # We'll be watching for the new devices in /dev tree
+                self.wd = self.wm.add_watch("/dev", IN_CREATE | IN_DELETE)
+                gobject.timeout_add(50, self._quick_check)
+                logging.debug("USBDevHelper type is set to UDev")
+            else:
+                logging.debug("Cannot find pyinotify module...")
+                self.helperType = vmmUSBDevHelper.HELPER_NONE
+
+        if helperType == vmmUSBDevHelper.HELPER_NONE:
+            logging.debug("USBDevHelper is disabled, not watching for USB device attach")
+
+    def checkForCapabilities(self):
+        caps = {}
+        caps['HAL'] = self._checkForHAL()
+        caps['DeviceKit'] = self._checkForDeviceKit()
+        if self._cmdRunError("udevadm") is None and self._checkForPyinotify():
+            caps['UDev'] = True
+        else:
+            caps['UDev'] = False
+
+        return caps
+
+    def _checkForHAL(self):
+        try:
+            bus = dbus.SystemBus()
+            obj = bus.get_object("org.freedesktop.Hal", "/org/freedesktop/Hal/Manager")
+            iface = dbus.Interface(obj, "org.freedesktop.DBus.Introspectable")
+            if iface.Introspect().find("<interface") > 0:
+                return True
+            return False
+        except Exception, e:
+            return False
+
+    def _checkForPyinotify(self):
+        try:
+            from pyinotify import WatchManager, Notifier, ThreadedNotifier, ProcessEvent, IN_DELETE, IN_CREATE
+            return True
+        except Exception, e:
+            return False
+
+    def _checkForDeviceKit(self):
+        try:
+            bus = dbus.SystemBus()
+            obj = bus.get_object("org.freedesktop.DeviceKit.Disks", "/org/desktop/DeviceKit/Disks")
+            iface = dbus.Interface(obj, "org.freedesktop.DBus.Introspectable")
+            if iface.Introspect().find("<interface") > 0:
+                return True
+            return False
+        except Exception, e:
+            logging.debug("Exception when looking for DeviceKit: %s" % str(e) )
+            return False
+
+    def _cmdRunError(self, cmd, expectedRetCode = -1):
+        try:
+            import subprocess
+
+            process = subprocess.Popen([cmd], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+            process.stdout.close()
+            process.stderr.close()
+            retcode = process.wait()
+
+            if expectedRetCode > -1 and retcode != expectedRetCode:
+                return 1
+
+            return None
+
+        except OSError, e:
+            return -e.errno
+
+    def _sig_add_udev(self, param):
+            self.deviceAdded = True
+            self.sig_add(param)
+            logging.debug("[UDEV] New device has been added")
+
+    def _quick_check(self):
+            notifier = self.notifier
+            assert notifier._timeout is not None, 'Notifier must be constructed with a short timeout'
+            notifier.process_events()
+            while notifier.check_events():  #loop in case more events appear while we are processing
+                  notifier.read_events()
+                  notifier.process_events()
+            return True
+
+    def Close(self):
+         if self.helperType == vmmUSBDevHelper.HELPER_UDEV:
+            self.wm.rm_watch(self.wd.values())
+
+    def UnmountDevice(self, udiDev):
+        # For udev we can just unmount the device by an unmount command
+        # Not really much useful
+        if self.helperType == vmmUSBDevHelper.HELPER_UDEV:
+            cmd = "umount /dev/%s 2> /dev/null" % udiDev
+            os.system(cmd)
+            return None
+
+        obj = self.bus.get_object("org.freedesktop.Hal", udiDev)
+        dev = dbus.Interface(obj, "org.freedesktop.Hal.Device.Volume")
+        try:
+            dev.Unmount([])
+        except Exception, e:
+            return str(e)
+
+        return None
+
+    def sig_udev_info(self, info):
+        self.udevinfo = info
+
+    def getVolumeInfoString(self):
+        # For HAL we can get the label, size and fstype
+        if self.helperType == vmmUSBDevHelper.HELPER_HAL:
+            return "%s (%s, %s)" % (self.volume['label'],
+                                    self._prettyify(self.volume['size']),
+                                    self.volume['fstype'])
+
+        # For uDev we can get the manufacturer and product
+        if self.helperType == vmmUSBDevHelper.HELPER_UDEV:
+            self.vendor_id = self.udevinfo['vendor_id']
+            self.product_id = self.udevinfo['product_id']
+            return "%s %s" % (self.udevinfo['manufacturer'], self.udevinfo['product'])
+
+    # getXml() will format the XML part for libvirt to add this device
+    def getXml(self):
+        xml = "  <hostdev mode='subsystem' type='usb' managed='yes'>\n" \
+              "    <source>\n" \
+              "      <vendor id='%s'/>\n" \
+              "      <product id='%s'/>\n" \
+              "    </source>\n" \
+              "  </hostdev>\n" % (self.vendor_id, self.product_id)
+
+        return xml
+
+    def _sig_add_hal(self, udi):
+        self.udi = udi
+        try:
+            obj = self.bus.get_object("org.freedesktop.Hal", udi)
+            dev = dbus.Interface(obj, "org.freedesktop.Hal.Device")
+        except Exception, e:
+            logging.debug("Exception: %s" % str(e) )
+
+        try:
+            if dev.GetProperty("info.subsystem") == 'usb_device':
+                self.vendor_id = "0x%04x" % dev.GetProperty("usb_device.vendor_id")
+                self.product_id = "0x%04x" % dev.GetProperty("usb_device.product_id")
+                return
+        except Exception:
+            pass
+
+        if dev.QueryCapability("volume"):
+            time.sleep(0.5)  # To get proper bits, ie. after HAL auto-mount if enabled
+            self._getVolumeBits_hal(dev)
+            self.deviceAdded = True
+
+            if self.sig_add is not None:
+                self.sig_add(udi)
+                return
+
+            if self.auto_unmount:
+                self.UnmountDevice(udi)
+
+    def _sig_remove_hal(self, udi):
+        self.volume = {}
+        if self.sig_remove is not None:
+            self.sig_remove(udi)
+
+    def _getVolumeBits_hal(self, dev):
+         try:
+             self.volume['size'] = long(dev.GetProperty("volume.size"))
+         except:
+             self.volume['size'] = 0
+
+         self.volume['device'] = str(dev.GetProperty("block.device"))
+         self.volume['label']  = str(dev.GetProperty("volume.label"))
+         self.volume['fstype'] = str(dev.GetProperty("volume.fstype"))
+         self.volume['mounted'] = str(dev.GetProperty("volume.is_mounted"))
+         self.volume['mount_point'] = str(dev.GetProperty("volume.mount_point"))
+
+    def _prettyify(self, val):
+        if val > (1024 * 1024 * 1024):
+            return "%.2f GB" % round( float(val) / (1024 * 1024 * 1024), 2)
+        elif val > (1024 * 1024):
+            return "%s MB" % round( float(val) / (1024 * 1024), 1)
+        elif val > 1024:
+            return "%s kB" % round( float(val) / 1024, 1)
+        else:
+            return "%s B" % val
+
+gobject.type_register(vmmUSBDevHelper)
diff -r 3e7fd1794965 -r f884d53152b1 src/virtManager/usbdevhelperIn.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/virtManager/usbdevhelperIn.py	Thu Aug 13 15:43:03 2009 +0200
@@ -0,0 +1,150 @@
+#
+# Copyright (C) 2009 Red Hat, Inc.
+#
+# 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.
+#
+
+import logging
+import os
+import sys
+import traceback
+import glob
+import time
+from pyinotify import ProcessEvent
+
+class DevFunc(ProcessEvent):
+    # Logging constants
+    LOG_NONE     = 0
+    LOG_COMMAND  = 1
+    LOG_PROCESS  = 2
+    LOG_OUTPUT   = 4
+    LOG_DEBUG    = 8
+    LOG_EVENTS   = 16
+    LOG_NOOUTPUT = LOG_COMMAND | LOG_PROCESS | LOG_DEBUG | LOG_EVENTS
+
+    def __init__(self, cbFunc, cbFuncRemoved, cbUdevInfo, mask = LOG_NONE):
+        self.mask = DevFunc.LOG_NONE
+
+        self.devList = {}
+        self.cbFunc = cbFunc
+        self.cbFuncRemoved = cbFuncRemoved
+        self.cbUdevInfo = cbUdevInfo
+        self.vendor_id = None
+        self.product_id = None
+        self.manufacturer = None
+        self.product = None
+        self.log("Initing DevFunc class ...", DevFunc.LOG_DEBUG)
+
+    def log(self, msg, typ = LOG_NOOUTPUT):
+        if (self.mask & typ == typ):
+            logging.debug(msg)
+
+    def _udevQuery(self, dev):
+        try:
+            import subprocess
+
+            process = subprocess.Popen(['udevadm', 'info', '--attribute-walk', '--name=%s' % dev],
+                                        stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+            output = process.communicate()[0]
+            process.stdout.close()
+            process.stderr.close()
+            retcode = process.wait()
+
+            if retcode != 0:
+                return None
+
+            return output.split('\n')
+
+        except OSError, e:
+            if e.errno == 2:
+                self.log("Cannot find and execute `udevadm` command", DevFunc.LOG_PROCESS)
+            else:
+                self.log("Udevadm execution failed: %s" % str(e), DevFunc.LOG_PROCESS)
+            return None
+
+    def process_IN_CREATE(self, event):
+        if not event.name.startswith('.'):
+            self.log("Device creation detected: %s" % event.name, DevFunc.LOG_EVENTS)
+            self._getUdevInfo(event.name)
+
+    def process_IN_DELETE(self, event):
+        self.log("Device deletion detected: %s" % event.name, DevFunc.LOG_EVENTS)
+
+        if self.cbFuncRemoved:
+            self.cbFuncRemoved(None)
+            self.log("cbFuncRemoved called with None argument", DevFunc.LOG_DEBUG)
+
+    def _getUdevInfo(self, dev):
+        self.log("Found new device: %s" % dev, DevFunc.LOG_EVENTS)
+
+        res = self._udevQuery(dev)
+        if res is None:
+            self.log("No output string from udev found for %s. Skipping ..." % dev, DevFunc.LOG_PROCESS)
+            return
+
+        num = num2 = 0
+        product = ""
+        devinfo = None
+        for line in res:
+           self.log("LINE: %s" % line, DevFunc.LOG_OUTPUT)
+
+           if line.find("looking at") > 0:
+               num = num + 1
+               self.log("Num of 'looking at' string: %s" % num, DevFunc.LOG_DEBUG)
+
+           if line.find("ATTR{size}") > 0:
+               self.size = ((line.split('=')[2]).replace("\n", '')).replace('"', '')
+           if line.find("{idVendor}") > 0:
+               idVendor = "0x%s" % ((line.split('=')[2]).replace("\n", '')).replace('"', '')
+           if line.find("{idProduct}") > 0:
+               idProduct = "0x%s" % ((line.split('=')[2]).replace("\n", '')).replace('"', '')
+           if line.find("{manufacturer}") > 0:
+                manufacturer = ((line.split('=')[2]).replace("\n", '')).replace('"', '')
+           if line.find("{product}") > 0:
+                product = ((line.split('=')[2]).replace("\n", '')).replace('"', '')
+
+           if (len(product) > 0 and product.find("EHCI") == -1 and
+                product.find("Host Controller") == -1 and num > num2):
+                   self.log("Product found: %s, %s [%s,%s]" % (manufacturer, product, idVendor, idProduct),
+                             DevFunc.LOG_DEBUG)
+                   self.vendor_id = idVendor
+                   self.product_id = idProduct
+                   self.manufacturer = manufacturer
+                   self.product = product
+                   num2 = num
+                   self.log("Setting devinfo", DevFunc.LOG_DEBUG)
+                   devinfo = [idVendor, idProduct]
+                   self.log("Appending %s to devList" % devinfo, DevFunc.LOG_DEBUG)
+                   self.devList[dev] = devinfo
+                   self.log("DevInfo %s appended to devList" % devinfo, DevFunc.LOG_DEBUG)
+                   newDevice = True
+                   self.log("Device detected: %s, %s, info: %s" % (manufacturer, product, devinfo), DevFunc.LOG_DEBUG)
+
+        self.log("New device detected: %s, %s, info: %s" % (self.manufacturer, self.product, devinfo), DevFunc.LOG_PROCESS)
+        if self.cbUdevInfo:
+           self.cbUdevInfo( self._getInformationBits() )
+
+        if self.cbFunc:
+           self.cbFunc(None)
+           self.log("cbFunc called with None argument", DevFunc.LOG_DEBUG)
+
+    def _getInformationBits(self):
+        info = {}
+        info['manufacturer'] = self.manufacturer
+        info['product'] = self.product
+        info['vendor_id'] = self.vendor_id
+        info['product_id'] = self.product_id
+        return info
diff -r 3e7fd1794965 -r f884d53152b1 src/vmm-preferences.glade
--- a/src/vmm-preferences.glade	Wed Aug 05 16:06:01 2009 +0200
+++ b/src/vmm-preferences.glade	Thu Aug 13 15:43:03 2009 +0200
@@ -52,6 +52,40 @@
                                 <property name="y_options">GTK_FILL</property>
                               </packing>
                             </child>
+                            <child>
+                              <widget class="GtkLabel" id="prefs-system-usbwatch-label">
+                                <property name="visible">True</property>
+                                <property name="label" translatable="yes">Host machine USB device hotplug watcher type:</property>
+                                <signal name="toggled" handler="on_prefs_system_tray_toggled"/>
+                              </widget>
+                              <packing>
+                                <property name="x_options">GTK_FILL</property>
+                                <property name="y_options">GTK_FILL</property>
+                                <property name="left_attach">0</property>
+                                <property name="right_attach">1</property>
+                                <property name="x_padding">5</property>
+                                <property name="top_attach">1</property>
+                                <property name="bottom_attach">2</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <widget class="GtkComboBox" id="prefs-system-usbwatch-type">
+                                <property name="visible">True</property>
+                                <property name="items" translatable="yes">Disable watch
+Watch using HAL
+Watch using DeviceKit
+Watch using iNotify &amp; udevadm</property>
+                                <signal name="changed" handler="on_prefs_usbwatch_type_changed"/>
+                              </widget>
+                              <packing>
+                                <property name="x_options">GTK_FILL</property>
+                                <property name="y_options">GTK_FILL</property>
+                                <property name="left_attach">0</property>
+                                <property name="right_attach">1</property>
+                                <property name="top_attach">2</property>
+                                <property name="bottom_attach">3</property>
+                              </packing>
+                            </child>
                           </widget>
                         </child>
                       </widget>

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]