Index: trunk/src/VBox/Main/include/HostHardwareLinux.h
===================================================================
--- trunk/src/VBox/Main/include/HostHardwareLinux.h	(revision 15381)
+++ trunk/src/VBox/Main/include/HostHardwareLinux.h	(revision 15401)
@@ -7,5 +7,5 @@
 
 /*
- * Copyright (C) 2006-2007 Sun Microsystems, Inc.
+ * Copyright (C) 2008 Sun Microsystems, Inc.
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -21,4 +21,7 @@
  * additional information or have any questions.
  */
+
+#ifndef ____H_HOSTHARDWARELINUX
+# define ____H_HOSTHARDWARELINUX
 
 #include <iprt/err.h>
@@ -111,2 +114,86 @@
 typedef VBoxMainDriveInfo::DriveInfo DriveInfo;
 
+/**
+ * Class for probing and returning information about host USB devices
+ */
+class VBoxMainUSBDeviceInfo
+{
+public:
+    /** Structure describing a host USB device */
+    struct USBDeviceInfo
+    {
+        /** The device node of the device. */
+        std::string mDevice;
+        /** The sysfs path of the device. */
+        std::string mSysfsPath;
+        /** Type for the list of interfaces. */
+        typedef std::vector <std::string> InterfaceList;
+        /** The sysfs paths of the device's interfaces. */
+        InterfaceList mInterfaces;
+
+        /** Constructors */
+        USBDeviceInfo (std::string aDevice, std::string aSysfsPath)
+            : mDevice (aDevice), mSysfsPath (aSysfsPath) {}
+        USBDeviceInfo () {}
+    };
+    
+    /** List (resp vector) holding drive information */
+    typedef std::vector <USBDeviceInfo> DeviceInfoList;
+
+    /**
+     * Search for host USB devices and rebuild the list, which remains empty
+     * until the first time it is called.
+     * @returns iprt status code
+     */
+    int UpdateDevices ();
+
+    /** Get the first element in the list of USB devices. */
+    DeviceInfoList::const_iterator DevicesBegin()
+    {
+        return mDeviceList.begin();
+    }
+
+    /** Get the last element in the list of USB devices. */
+    DeviceInfoList::const_iterator DevicesEnd()
+    {
+        return mDeviceList.end();
+    }
+
+private:
+    /** The list of currently available USB devices */
+    DeviceInfoList mDeviceList;
+};
+
+typedef VBoxMainUSBDeviceInfo::DeviceInfoList USBDeviceInfoList;
+typedef VBoxMainUSBDeviceInfo::USBDeviceInfo USBDeviceInfo;
+typedef VBoxMainUSBDeviceInfo::USBDeviceInfo::InterfaceList USBInterfaceList;
+
+class VBoxMainHotplugWaiter
+{
+    /** Opaque context struct. */
+    struct Context;
+
+    /** Opaque waiter context. */
+    Context *mContext;
+public:
+    /** Constructor */
+    VBoxMainHotplugWaiter (void);
+    /** Destructor. */
+    ~VBoxMainHotplugWaiter (void);
+    /**
+     * Wait for a hotplug event.
+     *
+     * @returns  VINF_SUCCESS if an event occurred or if Interrupt() was called.
+     * @returns  VERR_TRY_AGAIN if the wait failed but this might (!) be a
+     *           temporary failure.
+     * @returns  VERR_NOT_SUPPORTED if the wait failed and will definitely not
+     *           succeed if retried.
+     * @returns  Possibly other iprt status codes otherwise.
+     */
+    int Wait (void);
+    /** Interrupts an active wait. */
+    void Interrupt (void);
+};
+
+#endif /* ____H_HOSTHARDWARELINUX */
+
Index: trunk/src/VBox/Main/include/vbox-dbus.h
===================================================================
--- trunk/src/VBox/Main/include/vbox-dbus.h	(revision 15381)
+++ trunk/src/VBox/Main/include/vbox-dbus.h	(revision 15401)
@@ -29,6 +29,6 @@
 #define LIB_DBUS_1_3 "libdbus-1.so.3"
 
-/** Types from the dbus header files which we need.  These are taken more or less
-    verbatim from the DBus public interface header files. */
+/** Types and defines from the dbus header files which we need.  These are
+ * taken more or less verbatim from the DBus public interface header files. */
 struct DBusError
 {
@@ -68,10 +68,23 @@
 typedef struct DBusMessageIter DBusMessageIter;
 
-/** Defines from the dbus header files which we need.  These are taken more or less
-    verbatim from the DBus public interface header files. */
 #define DBUS_ERROR_NO_MEMORY                  "org.freedesktop.DBus.Error.NoMemory"
 #define DBUS_TYPE_STRING        ((int) 's')
 #define DBUS_TYPE_ARRAY         ((int) 'a')
 #define DBUS_TYPE_DICT_ENTRY    ((int) 'e')
+
+typedef enum
+{
+  DBUS_HANDLER_RESULT_HANDLED,         /**< Message has had its effect - no need
+ to run more handlers. */ 
+  DBUS_HANDLER_RESULT_NOT_YET_HANDLED, /**< Message has not had any effect - see
+ if other handlers want it. */
+  DBUS_HANDLER_RESULT_NEED_MEMORY      /**< Need more memory in order to return 
+#DBUS_HANDLER_RESULT_HANDLED or #DBUS_HANDLER_RESULT_NOT_YET_HANDLED. Please try
+ again later with more memory. */
+} DBusHandlerResult;
+
+typedef DBusHandlerResult (*DBusHandleMessageFunction) (DBusConnection *,
+                                                        DBusMessage *, void *);
+typedef void (*DBusFreeFunction) (void *);
 
 /** The following are the symbols which we need from libdbus-1. */
@@ -100,4 +113,10 @@
 extern void (*dbus_message_iter_get_basic) (DBusMessageIter *, void *);
 extern dbus_bool_t (*dbus_message_iter_next) (DBusMessageIter *);
+extern dbus_bool_t (*dbus_connection_add_filter) (DBusConnection *, DBusHandleMessageFunction,
+                                                  void *, DBusFreeFunction);
+extern void (*dbus_connection_remove_filter) (DBusConnection *, DBusHandleMessageFunction,
+                                              void *);
+extern dbus_bool_t (*dbus_connection_read_write_dispatch) (DBusConnection *, int);
+extern dbus_bool_t (*dbus_message_is_signal) (DBusMessage *, const char *, const char *);
 
 extern bool VBoxDBusCheckPresence(void);
Index: trunk/src/VBox/Main/linux/HostHardwareLinux.cpp
===================================================================
--- trunk/src/VBox/Main/linux/HostHardwareLinux.cpp	(revision 15381)
+++ trunk/src/VBox/Main/linux/HostHardwareLinux.cpp	(revision 15401)
@@ -7,5 +7,5 @@
 
 /*
- * Copyright (C) 2006-2007 Sun Microsystems, Inc.
+ * Copyright (C) 2008 Sun Microsystems, Inc.
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -62,9 +62,9 @@
 
 /*******************************************************************************
-*   Defines and Typedefs                                                       *
+*   Typedefs and Defines                                                       *
 *******************************************************************************/
 
-typedef VBoxMainDriveInfo::DriveInfoList DriveInfoList;
-typedef VBoxMainDriveInfo::DriveInfo DriveInfo;
+enum { DBUS_POLL_TIMEOUT = 2000 /* ms */ };
+
 
 static bool validateDevice(const char *deviceNode, bool isDVD);
@@ -73,16 +73,22 @@
 static int getDVDInfoFromMTab(char *mountTable, DriveInfoList *pList);
 #ifdef VBOX_WITH_DBUS
-static int halInit(DBusConnection **ppConnection);
 /* This must be extern to be used in the RTMemAutoPtr template */
 extern void halShutdown (DBusConnection *pConnection);
+
+static int halInit(RTMemAutoPtr <DBusConnection, halShutdown> *pConnection);
 static int halFindDeviceStringMatch (DBusConnection *pConnection,
                                      const char *pszKey, const char *pszValue,
-                                     DBusMessage **ppMessage);
+                                     RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage);
 static int halGetPropertyStrings (DBusConnection *pConnection,
                                   const char *pszUdi, size_t cKeys,
                                   const char **papszKeys, char **papszValues,
-                                  DBusMessage **ppMessage);
+                                  RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage);
 static int getDriveInfoFromHal(DriveInfoList *pList, bool isDVD,
                                bool *pfSuccess);
+static int getUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess);
+static int getUSBInterfacesFromHal(std::vector <std::string> *pList,
+                                   const char *pcszUdi, bool *pfSuccess);
+static DBusHandlerResult dbusFilterFunction (DBusConnection *pConnection,
+                                             DBusMessage *pMessage, void *pvUser);
 #endif  /* VBOX_WITH_DBUS */
 
@@ -182,4 +188,115 @@
     LogFlowThisFunc (("rc=%Rrc\n", rc));
     return rc;
+}
+
+/**
+ * Updates the list of host USB devices.
+ *
+ * @returns iprt status code
+ */
+int VBoxMainUSBDeviceInfo::UpdateDevices ()
+{
+    LogFlowThisFunc (("entered\n"));
+    int rc = VINF_SUCCESS;
+    bool success = false;  /* Have we succeeded in finding anything yet? */
+    try
+    {
+        mDeviceList.clear();
+#if defined(RT_OS_LINUX)
+#ifdef VBOX_WITH_DBUS
+        if (RT_SUCCESS (rc) && VBoxDBusCheckPresence() && (!success || testing()))
+            rc = getUSBDeviceInfoFromHal(&mDeviceList, &success);
+#endif /* VBOX_WITH_DBUS defined */
+#endif /* RT_OS_LINUX */
+    }
+    catch (std::bad_alloc)
+    {
+        rc = VERR_NO_MEMORY;
+    }
+    LogFlowThisFunc (("rc=%Rrc\n", rc));
+    return rc;
+}
+
+struct VBoxMainHotplugWaiter::Context
+{
+#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
+    /** The connection to DBus */
+    RTMemAutoPtr <DBusConnection, halShutdown> mConnection;
+    /** Semaphore which is set when a device is hotplugged and reset when
+     * it is read. */
+    bool mTriggered;
+    /** A flag to say that we wish to interrupt the current wait. */
+    bool mInterrupt;
+#endif  /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
+};
+
+/** Constructor */
+VBoxMainHotplugWaiter::VBoxMainHotplugWaiter ()
+{
+#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
+    int rc = VINF_SUCCESS;
+
+    mContext = new Context;
+    for (unsigned i = 0; RT_SUCCESS(rc) && i < 5 && !mContext->mConnection; ++i)
+    {
+        rc = halInit (&mContext->mConnection);
+    }
+    if (!mContext->mConnection)
+        rc = VERR_NOT_SUPPORTED;
+    if (   RT_SUCCESS (rc)
+        && !dbus_connection_add_filter (mContext->mConnection.get(),
+                                        dbusFilterFunction,
+                                        &mContext->mTriggered, NULL))
+        rc = VERR_NO_MEMORY;
+    if (RT_FAILURE (rc))
+        mContext->mConnection.reset();
+#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
+}
+
+/** Destructor */
+VBoxMainHotplugWaiter::~VBoxMainHotplugWaiter ()
+{
+#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
+    if (!!mContext->mConnection)
+        dbus_connection_remove_filter (mContext->mConnection.get(), dbusFilterFunction,
+                                       &mContext->mTriggered);
+    delete mContext;
+#endif /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
+}
+
+/**
+ * Wait for a hotplug event.
+ *
+ * @returns  VINF_SUCCESS if an event occurred or if Exit() was called.
+ * @returns  VERR_TRY_AGAIN if something failed at the DBus level.
+ * @returns  VERR_NOT_SUPPORTED if the service could not be contacted.
+ * @returns  Possibly other iprt status codes otherwise.
+ */
+int VBoxMainHotplugWaiter::Wait()
+{
+    int rc = VINF_SUCCESS;
+#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
+    if (!mContext->mConnection)
+        rc = VERR_NOT_SUPPORTED;
+    bool connected = true;
+    mContext->mTriggered = false;
+    mContext->mInterrupt = false;
+    while (   RT_SUCCESS (rc) && connected && !mContext->mTriggered
+           && !mContext->mInterrupt)
+        connected = dbus_connection_read_write_dispatch (mContext->mConnection.get(),
+                                                         DBUS_POLL_TIMEOUT);
+    if (!connected)
+        rc = VERR_TRY_AGAIN;
+#else  /* !(defined RT_OS_LINUX && defined VBOX_WITH_DBUS) */
+    rc = VERR_NOT_IMPLEMENTED;
+#endif  /* !(defined RT_OS_LINUX && defined VBOX_WITH_DBUS) */
+    return rc;
+}
+
+void VBoxMainHotplugWaiter::Interrupt()
+{
+#if defined RT_OS_LINUX && defined VBOX_WITH_DBUS
+    mContext->mInterrupt = true;
+#endif  /* defined RT_OS_LINUX && defined VBOX_WITH_DBUS */
 }
 
@@ -429,8 +546,8 @@
  */
 /* static */
-int halInit (DBusConnection **ppConnection)
-{
-    AssertReturn(VALID_PTR (ppConnection), VERR_INVALID_POINTER);
-    LogFlowFunc (("ppConnection=%p\n", ppConnection));
+int halInit (RTMemAutoPtr <DBusConnection, halShutdown> *pConnection)
+{
+    AssertReturn(VALID_PTR (pConnection), VERR_INVALID_POINTER);
+    LogFlowFunc (("pConnection=%p\n", pConnection));
     int rc = VINF_SUCCESS;
     bool halSuccess = true;
@@ -459,6 +576,7 @@
     if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
         rc = VERR_NO_MEMORY;
-    *ppConnection = halSuccess ? dbusConnection.release() : NULL;
-    LogFlowFunc(("rc=%Rrc, *ppConnection=%p\n", rc, *ppConnection));
+    if (halSuccess)
+        *pConnection = dbusConnection.release();
+    LogFlowFunc(("rc=%Rrc, (*pConnection).get()=%p\n", rc, (*pConnection).get()));
     dbusError.FlowLog();
     return rc;
@@ -489,26 +607,27 @@
 /**
  * Find the UDIs of hal entries that contain Key=Value property.
- * @returns iprt status code
+ * @returns iprt status code.  If a non-fatal error occurs, we return success
+ *          but reset pMessage to NULL.
  * @param   pConnection an initialised connection DBus
  * @param   pszKey      the property key
  * @param   pszValue    the property value
- * @param   ppMessage   where to store the return DBus message.  This must be
- *                      parsed to get at the UDIs.  NOT optional.  The caller
- *                      is responsible for freeing this.
+ * @param   pMessage    where to store the return DBus message.  This must be
+ *                      parsed to get at the UDIs.  NOT optional.
  */
 /* static */
 int halFindDeviceStringMatch (DBusConnection *pConnection, const char *pszKey,
-                              const char *pszValue, DBusMessage **ppMessage)
+                              const char *pszValue,
+                              RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage)
 {
     AssertReturn(   VALID_PTR (pConnection) && VALID_PTR (pszKey)
-                 && VALID_PTR (pszValue) && VALID_PTR (ppMessage),
+                 && VALID_PTR (pszValue) && VALID_PTR (pMessage),
                  VERR_INVALID_POINTER);
-    LogFlowFunc (("pConnection=%p, pszKey=%s, pszValue=%s, ppMessage=%p\n",
-                  pConnection, pszKey, pszValue, ppMessage));
-    int rc = VINF_SUCCESS;
-    bool halSuccess = true;
+    LogFlowFunc (("pConnection=%p, pszKey=%s, pszValue=%s, pMessage=%p\n",
+                  pConnection, pszKey, pszValue, pMessage));
+    int rc = VINF_SUCCESS;  /* We set this to failure on fatal errors. */
+    bool halSuccess = true;  /* We set this to false to abort the operation. */
     autoDBusError dbusError;
-    RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message;
-    DBusMessage *pReply = NULL;
+
+    RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, reply;
     if (halSuccess && RT_SUCCESS (rc))
     {
@@ -526,12 +645,12 @@
         dbus_message_iter_append_basic (&iterAppend, DBUS_TYPE_STRING, &pszKey);
         dbus_message_iter_append_basic (&iterAppend, DBUS_TYPE_STRING, &pszValue);
-        pReply = dbus_connection_send_with_reply_and_block (pConnection,
+        reply = dbus_connection_send_with_reply_and_block (pConnection,
                                                             message.get(), -1,
                                                             &dbusError.get());
-        if (pReply == NULL)
-            halSuccess = false;
-    }
-    *ppMessage = pReply;
-    LogFlowFunc (("rc=%Rrc, *ppMessage=%p\n", rc, *ppMessage));
+        if (!reply)
+            halSuccess = false;
+    }
+    *pMessage = reply.release ();
+    LogFlowFunc (("rc=%Rrc, *pMessage.value()=%p\n", rc, (*pMessage).get()));
     dbusError.FlowLog();
     return rc;
@@ -541,5 +660,7 @@
  * Read a set of string properties for a device.  If some of the properties are
  * not of type DBUS_TYPE_STRING then a NULL pointer will be returned for them.
- * @returns iprt status code
+ * @returns iprt status code.  If the operation failed for non-fatal reasons
+ *          then we return success and leave pMessage untouched - reset it
+ *          before the call to detect this.
  * @param   pConnection  an initialised connection DBus
  * @param   pszUdi       the Udi of the device
@@ -550,5 +671,5 @@
  *                       returned in @a ppMessage is freed.  Undefined if
  *                       the message is NULL.
- * @param   ppMessage    where to store the return DBus message.  The caller
+ * @param   pMessage     where to store the return DBus message.  The caller
  *                       is responsible for freeing this once they have
  *                       finished with the value strings.  NOT optional.
@@ -557,15 +678,17 @@
 int halGetPropertyStrings (DBusConnection *pConnection, const char *pszUdi,
                            size_t cProps, const char **papszKeys,
-                           char **papszValues, DBusMessage **ppMessage)
+                           char **papszValues,
+                           RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> *pMessage)
 {
     AssertReturn(   VALID_PTR (pConnection) && VALID_PTR (pszUdi)
                  && VALID_PTR (papszKeys) && VALID_PTR (papszValues)
-                 && VALID_PTR (ppMessage),
+                 && VALID_PTR (pMessage),
                  VERR_INVALID_POINTER);
-    LogFlowFunc (("pConnection=%p, pszUdi=%s, cProps=%llu, papszKeys=%p, papszValues=%p, ppMessage=%p\n",
-                  pConnection, pszUdi, cProps, papszKeys, papszValues, ppMessage));
-    int rc = VINF_SUCCESS;
-    bool halSuccess = true;
+    LogFlowFunc (("pConnection=%p, pszUdi=%s, cProps=%llu, papszKeys=%p, papszValues=%p, pMessage=%p\n",
+                  pConnection, pszUdi, cProps, papszKeys, papszValues, pMessage));
+    int rc = VINF_SUCCESS;  /* We set this to failure on fatal errors. */
+    bool halSuccess = true;  /* We set this to false to abort the operation. */
     autoDBusError dbusError;
+
     RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, reply;
     DBusMessageIter iterGet, iterProps, iterKey, iterValue;
@@ -574,4 +697,6 @@
     for (size_t i = 0; i < cProps; ++i)
         papszValues[i] = NULL;
+
+    /* Send a GetAllProperties message to hald */
     message = dbus_message_new_method_call ("org.freedesktop.Hal", pszUdi,
                                             "org.freedesktop.Hal.Device",
@@ -587,4 +712,6 @@
             halSuccess = false;
     }
+
+    /* Parse the reply */
     if (halSuccess && RT_SUCCESS (rc))
     {
@@ -596,4 +723,5 @@
     if (halSuccess && RT_SUCCESS (rc))
         dbus_message_iter_recurse (&iterGet, &iterProps);
+    /* Go through all entries in the reply and see if any match our keys. */
     while (   halSuccess && RT_SUCCESS (rc)
            &&    dbus_message_iter_get_arg_type (&iterProps)
@@ -606,4 +734,5 @@
         dbus_message_iter_next (&iterEntry);
         dbus_message_iter_recurse (&iterEntry, &iterValue);
+        /* Fill in any matches. */
         for (size_t i = 0; i < cProps; ++i)
             if (strcmp (pszKey, papszKeys[i]) == 0)
@@ -615,10 +744,8 @@
     }
     if (RT_SUCCESS (rc) && halSuccess)
-        *ppMessage = reply.release();
-    else
-        *ppMessage = NULL;
+        *pMessage = reply.release();
     if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
-            rc = VERR_NO_MEMORY;
-    LogFlowFunc (("rc=%Rrc, *ppMessage=%p\n", rc, *ppMessage));
+        rc = VERR_NO_MEMORY;
+    LogFlowFunc (("rc=%Rrc, *pMessage.value()=%p\n", rc, (*pMessage).get()));
     dbusError.FlowLog();
     return rc;
@@ -642,22 +769,19 @@
                  VERR_INVALID_POINTER);
     LogFlowFunc (("pList=%p, isDVD=%d, pfSuccess=%p\n", pList, isDVD, pfSuccess));
-    DBusConnection *pConnection;
+    int rc = VINF_SUCCESS;  /* We set this to failure on fatal errors. */
+    bool halSuccess = true;  /* We set this to false to abort the operation. */
     autoDBusError dbusError;
-    DBusMessage *pReply;
+
     RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
+    RTMemAutoPtr <DBusConnection, halShutdown> dbusConnection;
     DBusMessageIter iterFind, iterUdis;
-    bool halSuccess = true;  /* Did something go wrong with hal or DBus? */
-    int rc = VINF_SUCCESS;  /* Did a fatal error occur? */
-
-    rc = halInit (&pConnection);
-    RTMemAutoPtr <DBusConnection, halShutdown> dbusConnection;
-    dbusConnection = pConnection;
+
+    rc = halInit (&dbusConnection);
     if (!dbusConnection)
         halSuccess = false;
     if (halSuccess && RT_SUCCESS (rc))
     {
-        rc = halFindDeviceStringMatch (pConnection, "storage.drive_type",
-                                       isDVD ? "cdrom" : "floppy", &pReply);
-        replyFind = pReply;
+        rc = halFindDeviceStringMatch (dbusConnection.get(), "storage.drive_type",
+                                       isDVD ? "cdrom" : "floppy", &replyFind);
         if (!replyFind)
             halSuccess = false;
@@ -681,11 +805,12 @@
                 { "block.device", "info.product", "info.vendor" };
         char *papszValues[RT_ELEMENTS (papszKeys)];
-        rc = halGetPropertyStrings (pConnection, pszUdi, RT_ELEMENTS (papszKeys),
-                                    papszKeys, papszValues, &pReply);
-        replyGet = pReply;
+        rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
+                                    papszKeys, papszValues, &replyGet);
         std::string description;
         const char *pszDevice = papszValues[0], *pszProduct = papszValues[1],
                    *pszVendor = papszValues[2];
-        if (!!replyGet && pszDevice)
+        if (!!replyGet && pszDevice == NULL)
+            halSuccess = false;
+        if (!!replyGet && pszDevice != NULL)
         {
             if ((pszVendor != NULL) && (pszVendor[0] != '\0'))
@@ -697,5 +822,5 @@
     }
     if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
-            rc = VERR_NO_MEMORY;
+        rc = VERR_NO_MEMORY;
     if (pfSuccess != NULL)
         *pfSuccess = halSuccess;
@@ -704,4 +829,185 @@
     return rc;
 }
+
+/**
+ * Helper function to query the hal subsystem for information about USB devices
+ * attached to the system.
+ * @returns iprt status code
+ * @param   pList      where to add information about the devices detected
+ * @param   pfSuccess  will be set to true if all interactions with hal
+ *                     succeeded and to false otherwise.  Optional.
+ *
+ * @returns IPRT status code
+ */
+/* static */
+int getUSBDeviceInfoFromHal(USBDeviceInfoList *pList, bool *pfSuccess)
+{
+    AssertReturn(VALID_PTR (pList) && (pfSuccess == NULL || VALID_PTR (pfSuccess)),
+                 VERR_INVALID_POINTER);
+    LogFlowFunc (("pList=%p, pfSuccess=%p\n", pList, pfSuccess));
+    int rc = VINF_SUCCESS;  /* We set this to failure on fatal errors. */
+    bool halSuccess = true;  /* We set this to false to abort the operation. */
+    autoDBusError dbusError;
+
+    RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
+    RTMemAutoPtr <DBusConnection, halShutdown> dbusConnection;
+    DBusMessageIter iterFind, iterUdis;
+
+    rc = halInit (&dbusConnection);
+    if (!dbusConnection)
+        halSuccess = false;
+    if (halSuccess && RT_SUCCESS (rc))
+    {
+        rc = halFindDeviceStringMatch (dbusConnection.get(), "info.subsystem",
+                                       "usb_device", &replyFind);
+        if (!replyFind)
+            halSuccess = false;
+    }
+    if (halSuccess && RT_SUCCESS (rc))
+    {
+        dbus_message_iter_init (replyFind.get(), &iterFind);
+        if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
+            halSuccess = false;
+    }
+    if (halSuccess && RT_SUCCESS (rc))
+        dbus_message_iter_recurse (&iterFind, &iterUdis);
+    for (;    halSuccess && RT_SUCCESS (rc)
+           && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
+         dbus_message_iter_next(&iterUdis))
+    {
+        /* Now get the device node and the sysfs path from the iterator */
+        const char *pszUdi;
+        dbus_message_iter_get_basic (&iterUdis, &pszUdi);
+        static const char *papszKeys[] = { "linux.device_file", "linux.sysfs_path" };
+        char *papszValues[RT_ELEMENTS (papszKeys)];
+        rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
+                                    papszKeys, papszValues, &replyGet);
+        std::string description;
+        const char *pszDevice = papszValues[0], *pszSysfsPath = papszValues[1];
+        /* Get the interfaces for this device. */
+        if (!!replyGet && pszDevice && pszSysfsPath)
+        {
+            USBDeviceInfo info (pszDevice, pszSysfsPath);
+            bool ifaceSuccess = true;  /* If we can't get the interfaces, just
+                                        * skip this one device. */            
+            rc = getUSBInterfacesFromHal (&info.mInterfaces, pszUdi, &ifaceSuccess);
+            if (RT_SUCCESS(rc) && halSuccess && ifaceSuccess)
+                pList->push_back (info);
+        }
+    }
+    if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
+        rc = VERR_NO_MEMORY;
+    if (pfSuccess != NULL)
+        *pfSuccess = halSuccess;
+    LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
+    dbusError.FlowLog();
+    return rc;
+}
+
+/**
+ * Helper function to query the hal subsystem for information about USB devices
+ * attached to the system.
+ * @returns iprt status code
+ * @param   pList      where to add information about the devices detected.  If
+ *                     certain interfaces are not found (@a pfFound is false on
+ *                     return) this may contain invalid information.
+ * @param   pcszUdi    the hal UDI of the device
+ * @param   pfSuccess  will be set to true if the operation succeeds and to
+ *                     false if it fails for non-critical reasons.  Optional.
+ *
+ * @returns IPRT status code
+ */
+/* static */
+int getUSBInterfacesFromHal(std::vector <std::string> *pList,
+                            const char *pcszUdi, bool *pfSuccess)
+{
+    AssertReturn(VALID_PTR (pList) && VALID_PTR (pcszUdi) &&
+                 (pfSuccess == NULL || VALID_PTR (pfSuccess)),
+                 VERR_INVALID_POINTER);
+    LogFlowFunc (("pList=%p, pcszUdi=%s, pfSuccess=%p\n", pList, pcszUdi,
+                  pfSuccess));
+    int rc = VINF_SUCCESS;  /* We set this to failure on fatal errors. */
+    bool halSuccess = true;  /* We set this to false to abort the operation. */
+    autoDBusError dbusError;
+
+    RTMemAutoPtr <DBusMessage, VBoxDBusMessageUnref> message, replyFind, replyGet;
+    RTMemAutoPtr <DBusConnection, halShutdown> dbusConnection;
+    DBusMessageIter iterFind, iterUdis;
+
+    rc = halInit (&dbusConnection);
+    if (!dbusConnection)
+        halSuccess = false;
+    if (halSuccess && RT_SUCCESS (rc))
+    {
+        /* Look for children of the current UDI. */
+        rc = halFindDeviceStringMatch (dbusConnection.get(), "info.parent",
+                                       pcszUdi, &replyFind);
+        if (!replyFind)
+            halSuccess = false;
+    }
+    if (halSuccess && RT_SUCCESS (rc))
+    {
+        dbus_message_iter_init (replyFind.get(), &iterFind);
+        if (dbus_message_iter_get_arg_type (&iterFind) != DBUS_TYPE_ARRAY)
+            halSuccess = false;
+    }
+    if (halSuccess && RT_SUCCESS (rc))
+        dbus_message_iter_recurse (&iterFind, &iterUdis);
+    for (;    halSuccess && RT_SUCCESS (rc)
+           && dbus_message_iter_get_arg_type (&iterUdis) == DBUS_TYPE_STRING;
+         dbus_message_iter_next(&iterUdis))
+    {
+        /* Now get the sysfs path and the subsystem from the iterator */
+        const char *pszUdi;
+        dbus_message_iter_get_basic (&iterUdis, &pszUdi);
+        static const char *papszKeys[] = { "linux.sysfs_path", "info.subsystem" };
+        char *papszValues[RT_ELEMENTS (papszKeys)];
+        rc = halGetPropertyStrings (dbusConnection.get(), pszUdi, RT_ELEMENTS (papszKeys),
+                                    papszKeys, papszValues, &replyGet);
+        std::string description;
+        const char *pszSysfsPath = papszValues[0], *pszSubsystem = papszValues[1];
+        if (!replyGet)
+            halSuccess = false;
+        if (!!replyGet && pszSysfsPath == NULL)
+            halSuccess = false;
+        if (   halSuccess && RT_SUCCESS (rc)
+            && RTStrCmp (pszSubsystem, "usb_device") != 0)  /* Children of buses can also be devices. */
+            pList->push_back (pszSysfsPath);
+    }
+    if (dbusError.HasName (DBUS_ERROR_NO_MEMORY))
+        rc = VERR_NO_MEMORY;
+    if (pfSuccess != NULL)
+        *pfSuccess = halSuccess;
+    LogFlow (("rc=%Rrc, halSuccess=%d\n", rc, halSuccess));
+    dbusError.FlowLog();
+    return rc;
+}
+
+/**
+ * When it is registered with DBus, this function will be called by
+ * dbus_connection_read_write_dispatch each time a message is received over the
+ * DBus connection.  We check whether that message was caused by a hal device
+ * hotplug event, and if so we set a flag.  dbus_connection_read_write_dispatch
+ * will return after calling its filter functions, and its caller should then
+ * check the status of the flag passed to the filter function.
+ *
+ * @param   pConnection The DBus connection we are using.
+ * @param   pMessage    The DBus message which just arrived.
+ * @param   pvUser      A pointer to the flag variable we are to set.
+ */
+/* static */
+DBusHandlerResult dbusFilterFunction (DBusConnection *pConnection,
+                                      DBusMessage *pMessage, void *pvUser)
+{
+    bool *pTriggered = reinterpret_cast<bool *> (pvUser);
+    if (   dbus_message_is_signal (pMessage, "org.freedesktop.Hal.Manager",
+                                   "DeviceAdded")
+        || dbus_message_is_signal (pMessage, "org.freedesktop.Hal.Manager",
+                                   "DeviceRemoved"))
+    {
+        *pTriggered = true;
+    }
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
 #endif  /* RT_OS_LINUX && VBOX_WITH_DBUS */
 
Index: trunk/src/VBox/Main/linux/vbox-dbus.cpp
===================================================================
--- trunk/src/VBox/Main/linux/vbox-dbus.cpp	(revision 15381)
+++ trunk/src/VBox/Main/linux/vbox-dbus.cpp	(revision 15401)
@@ -64,4 +64,10 @@
 void (*dbus_message_iter_get_basic) (DBusMessageIter *, void *);
 dbus_bool_t (*dbus_message_iter_next) (DBusMessageIter *);
+dbus_bool_t (*dbus_connection_add_filter) (DBusConnection *, DBusHandleMessageFunction,
+                                           void *, DBusFreeFunction);
+void (*dbus_connection_remove_filter) (DBusConnection *, DBusHandleMessageFunction,
+                                       void *);
+dbus_bool_t (*dbus_connection_read_write_dispatch) (DBusConnection *, int);
+dbus_bool_t (*dbus_message_is_signal) (DBusMessage *, const char *, const char *);
 
 bool VBoxDBusCheckPresence(void)
@@ -116,4 +122,12 @@
         && RT_SUCCESS(RTLdrGetSymbol(hLibDBus, "dbus_message_iter_next",
                                      (void **) &dbus_message_iter_next))
+        && RT_SUCCESS(RTLdrGetSymbol(hLibDBus, "dbus_connection_add_filter",
+                                     (void **) &dbus_connection_add_filter))
+        && RT_SUCCESS(RTLdrGetSymbol(hLibDBus, "dbus_connection_remove_filter",
+                                     (void **) &dbus_connection_remove_filter))
+        && RT_SUCCESS(RTLdrGetSymbol(hLibDBus, "dbus_connection_read_write_dispatch",
+                                     (void **) &dbus_connection_read_write_dispatch))
+        && RT_SUCCESS(RTLdrGetSymbol(hLibDBus, "dbus_message_is_signal",
+                                     (void **) &dbus_message_is_signal))
        )
     {
Index: trunk/src/VBox/Main/testcase/Makefile.kmk
===================================================================
--- trunk/src/VBox/Main/testcase/Makefile.kmk	(revision 15381)
+++ trunk/src/VBox/Main/testcase/Makefile.kmk	(revision 15401)
@@ -33,5 +33,5 @@
 	$(if $(VBOX_WITH_RESOURCE_USAGE_API),tstCollector,)
   PROGRAMS.linux += \
-	$(if $(VBOX_OSE),,tstHostHardwareLinux)
+	tstHostHardwareLinux
  endif # !VBOX_WITH_TESTCASES
 endif # !VBOX_ONLY_SDK
@@ -125,5 +125,4 @@
 tstHostHardwareLinux_SOURCES   = \
 	tstHostHardwareLinux.cpp \
-	../linux/USBProxyServiceLinux.cpp \
 	../linux/HostHardwareLinux.cpp \
 	../linux/vbox-dbus.cpp
Index: trunk/src/VBox/Main/testcase/tstHostHardwareLinux.cpp
===================================================================
--- trunk/src/VBox/Main/testcase/tstHostHardwareLinux.cpp	(revision 15381)
+++ trunk/src/VBox/Main/testcase/tstHostHardwareLinux.cpp	(revision 15401)
@@ -1,7 +1,6 @@
 /* $Id$ */
-
 /** @file
  *
- * Test executable for quickly excercising/debugging the hal-based Linux USB
+ * Test executable for quickly excercising/debugging the Linux host hardware
  * bits.
  */
@@ -10,11 +9,16 @@
  * Copyright (C) 2008 Sun Microsystems, Inc.
  *
- * Sun Microsystems, Inc. confidential
- * All rights reserved
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
+ * Clara, CA 95054 USA or visit http://www.sun.com if you need
+ * additional information or have any questions.
  */
-
-#ifdef VBOX_USB_WITH_SYSFS
-# include "tstUSBLinux.h"
-#endif
 
 #include <HostHardwareLinux.h>
@@ -23,64 +27,22 @@
 
 #include <iprt/initterm.h>
+#include <iprt/param.h>
 #include <iprt/stream.h>
+#include <iprt/linux/sysfs.h>
 
 #include <iprt/cdefs.h>
 #include <iprt/types.h>
 
+#include <errno.h>
+#include <string.h>
+
 int main()
 {
     RTR3Init();
-#ifdef VBOX_USB_WITH_SYSFS
-    USBProxyServiceLinux service;
-    service.initSysfs();
-    if (RT_FAILURE(service.getLastError()))
-    {
-        RTPrintf("Failed to initialise USBProxyServiceLinux, error %Rrc\n",
-                 service.getLastError());
-        return 1;
-    }
-    PUSBDEVICE pChain = service.getDevicesFromSysfs();
-    if (pChain == NULL)
-        RTPrintf("Failed to get any devices from sysfs\n.");
-    else
-    {
-        PUSBDEVICE pNext = pChain;
-        while (pNext != NULL)
-        {
-            RTPrintf("Device: %s (product string)\n", pNext->pszProduct);
-            RTPrintf("  Manufacturer: %s\n", pNext->pszManufacturer);
-            RTPrintf("  Serial number: %s\n", pNext->pszSerialNumber);
-            RTPrintf("  Address: %s\n", pNext->pszAddress);
-            RTPrintf("  Vendor ID: %d\n", pNext->idVendor);
-            RTPrintf("  Product ID: %d\n", pNext->idProduct);
-            RTPrintf("  Revision: %d.%d\n", pNext->bcdDevice >> 8, pNext->bcdDevice & 255);
-            RTPrintf("  USB Version: %d.%d\n", pNext->bcdUSB >> 8, pNext->bcdUSB & 255);
-            RTPrintf("  Device class: %d\n", pNext->bDeviceClass);
-            RTPrintf("  Device subclass: %d\n", pNext->bDeviceSubClass);
-            RTPrintf("  Device protocol: %d\n", pNext->bDeviceProtocol);
-            RTPrintf("  Number of configurations: %d\n", pNext->bNumConfigurations);
-            RTPrintf("  Device state: %s\n",
-                     pNext->enmState == USBDEVICESTATE_UNUSED ? "unused"
-                         : pNext->enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE ? "used by host"
-                         : "unknown"
-                    );
-            RTPrintf("  Device speed: %s\n",
-                     pNext->enmSpeed == USBDEVICESPEED_LOW ? "low"
-                         : pNext->enmSpeed == USBDEVICESPEED_FULL ? "full"
-                         : pNext->enmSpeed == USBDEVICESPEED_HIGH ? "high"
-                         : "unknown"
-                    );
-            RTPrintf("  Serial hash: 0x%llx\n", pNext->u64SerialHash);
-            RTPrintf("  Bus number: %d\n", pNext->bBus);
-            RTPrintf("  Port number: %d\n", pNext->bPort);
-            RTPrintf("  Device number: %d\n", pNext->bDevNum);
-            RTPrintf("\n");
-            pNext = pNext->pNext;
-        }
-    }
-#endif  /* VBOX_USB_WITH_SYSFS */
+    g_testHostHardwareLinux = true;
+    int rc = VINF_SUCCESS;
     VBoxMainDriveInfo driveInfo;
     g_testHostHardwareLinux = true;
-    int rc = driveInfo.updateFloppies();
+    rc = driveInfo.updateFloppies();
     if (RT_SUCCESS (rc))
         rc = driveInfo.updateDVDs();
@@ -92,5 +54,5 @@
     }
     RTPrintf ("Listing floppy drives detected:\n");
-    for (VBoxMainDriveInfo::DriveInfoList::const_iterator it = driveInfo.FloppyBegin();
+    for (DriveInfoList::const_iterator it = driveInfo.FloppyBegin();
          it != driveInfo.FloppyEnd(); ++it)
     {
@@ -103,5 +65,5 @@
     }
     RTPrintf ("Listing DVD drives detected:\n");
-    for (VBoxMainDriveInfo::DriveInfoList::const_iterator it = driveInfo.DVDBegin();
+    for (DriveInfoList::const_iterator it = driveInfo.DVDBegin();
          it != driveInfo.DVDEnd(); ++it)
     {
@@ -113,4 +75,63 @@
         RTPrintf ("\n");
     }
+#ifdef VBOX_USB_WITH_SYSFS
+    VBoxMainUSBDeviceInfo deviceInfo;
+    rc = deviceInfo.UpdateDevices();
+    if (RT_FAILURE (rc))
+    {
+        RTPrintf ("Failed to update the host USB device information, error %Rrc\n",
+                 rc);
+        return 1;
+    }
+    RTPrintf ("Listing USB devices detected:\n");
+    for (USBDeviceInfoList::const_iterator it = deviceInfo.DevicesBegin();
+         it != deviceInfo.DevicesEnd(); ++it)
+    {
+        char szProduct[1024];
+        if (RTLinuxSysFsReadStrFile(szProduct, sizeof(szProduct),
+                                    "%s/product", it->mSysfsPath.c_str()) == -1)
+        {
+            if (errno != ENOENT)
+            {
+                RTPrintf ("Failed to get the product name for device %s: error %s\n",
+                          it->mDevice.c_str(), strerror(errno));
+                return 1;
+            }
+            else
+                szProduct[0] = '\0';
+        }
+        RTPrintf ("  device: %s (%s), sysfs path: %s\n", szProduct, it->mDevice.c_str(),
+                  it->mSysfsPath.c_str());
+        RTPrintf ("    interfaces:\n");
+        for (USBInterfaceList::const_iterator it2 = it->mInterfaces.begin();
+             it2 != it->mInterfaces.end(); ++it2)
+        {
+            char szDriver[RTPATH_MAX];
+            strcpy(szDriver, "none");
+            ssize_t size = RTLinuxSysFsGetLinkDest(szDriver, sizeof(szDriver),
+                                                   "%s/driver", it2->c_str());
+            if (size == -1 && errno != ENOENT)
+            {
+                RTPrintf ("Failed to get the driver for interface %s of device %s: error %s\n",
+                          it2->c_str(), it->mDevice.c_str(), strerror(errno));
+                return 1;
+            }
+            if (RTLinuxSysFsExists("%s/driver", it2->c_str()) != (size != -1))            
+            {
+                RTPrintf ("RTLinuxSysFsExists did not return the expected value for the driver link of interface %s of device %s.\n",
+                          it2->c_str(), it->mDevice.c_str());
+                return 1;
+            }
+            uint64_t u64InterfaceClass;
+            u64InterfaceClass = RTLinuxSysFsReadIntFile(16, "%s/bInterfaceClass",
+                                                        it2->c_str());
+            RTPrintf ("      sysfs path: %s, driver: %s, interface class: 0x%x\n",
+                      it2->c_str(), szDriver, u64InterfaceClass);
+        }
+    }
+    RTPrintf ("Waiting for a hotplug event, Ctrl-C to abort...\n");
+    VBoxMainHotplugWaiter waiter;
+    waiter.Wait();
+#endif  /* VBOX_USB_WITH_SYSFS */
     return 0;
 }
