summaryrefslogtreecommitdiff
path: root/src/VBox/Main/src-server/linux/USBGetDevices.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Main/src-server/linux/USBGetDevices.cpp')
-rw-r--r--src/VBox/Main/src-server/linux/USBGetDevices.cpp181
1 files changed, 179 insertions, 2 deletions
diff --git a/src/VBox/Main/src-server/linux/USBGetDevices.cpp b/src/VBox/Main/src-server/linux/USBGetDevices.cpp
index 63b45f37f..78d6063d4 100644
--- a/src/VBox/Main/src-server/linux/USBGetDevices.cpp
+++ b/src/VBox/Main/src-server/linux/USBGetDevices.cpp
@@ -1,4 +1,4 @@
-/* $Id: USBGetDevices.cpp $ */
+/* $Id: USBGetDevices.cpp 37624 2011-06-24 08:57:35Z vboxsync $ */
/** @file
* VirtualBox Linux host USB device enumeration.
*/
@@ -22,13 +22,16 @@
#include "USBGetDevices.h"
+#include <VBox/err.h>
#include <VBox/usb.h>
#include <VBox/usblib.h>
#include <iprt/linux/sysfs.h>
#include <iprt/cdefs.h>
#include <iprt/ctype.h>
-#include <iprt/err.h>
+#include <iprt/dir.h>
+#include <iprt/env.h>
+#include <iprt/file.h>
#include <iprt/fs.h>
#include <iprt/log.h>
#include <iprt/mem.h>
@@ -1477,6 +1480,11 @@ static PUSBDEVICE testGetUsbfsDevices(const char *pcszUsbfsRoot, bool testfs)
}
# define getDevicesFromUsbfs testGetUsbfsDevices
+/**
+ * Specify the list of devices that will appear to be available through
+ * usbfs during unit testing (of USBProxyLinuxGetDevices)
+ * @param pacszDeviceAddresses NULL terminated array of usbfs device addresses
+ */
void TestUSBSetAvailableUsbfsDevices(const char **pacszDeviceAddresses)
{
s_pacszUsbfsDeviceAddresses = pacszDeviceAddresses;
@@ -1496,12 +1504,175 @@ static int testAccess(const char *pcszPath, int mode)
}
# define access testAccess
+/**
+ * Specify the list of files that access will report as accessible (at present
+ * we only do accessible or not accessible) during unit testing (of
+ * USBProxyLinuxGetDevices)
+ * @param pacszAccessibleFiles NULL terminated array of file paths to be
+ * reported accessible
+ */
void TestUSBSetAccessibleFiles(const char **pacszAccessibleFiles)
{
s_pacszAccessibleFiles = pacszAccessibleFiles;
}
#endif
+#ifdef UNIT_TEST
+# ifdef UNIT_TEST
+ /** The path we pretend the usbfs root is located at, or NULL. */
+ const char *s_pcszTestUsbfsRoot;
+ /** Should usbfs be accessible to the current user? */
+ bool s_fTestUsbfsAccessible;
+ /** The path we pretend the device node tree root is located at, or NULL. */
+ const char *s_pcszTestDevicesRoot;
+ /** Should the device node tree be accessible to the current user? */
+ bool s_fTestDevicesAccessible;
+ /** The result of the usbfs/inotify-specific init */
+ int s_rcTestMethodInitResult;
+ /** The value of the VBOX_USB environment variable. */
+ const char *s_pcszTestEnvUsb;
+ /** The value of the VBOX_USB_ROOT environment variable. */
+ const char *s_pcszTestEnvUsbRoot;
+# endif
+
+/** Select which access methods will be available to the @a init method
+ * during unit testing, and (hack!) what return code it will see from
+ * the access method-specific initialisation. */
+void TestUSBSetupInit(const char *pcszUsbfsRoot, bool fUsbfsAccessible,
+ const char *pcszDevicesRoot, bool fDevicesAccessible,
+ int rcMethodInitResult)
+{
+ s_pcszTestUsbfsRoot = pcszUsbfsRoot;
+ s_fTestUsbfsAccessible = fUsbfsAccessible;
+ s_pcszTestDevicesRoot = pcszDevicesRoot;
+ s_fTestDevicesAccessible = fDevicesAccessible;
+ s_rcTestMethodInitResult = rcMethodInitResult;
+}
+
+/** Specify the environment that the @a init method will see during unit
+ * testing. */
+void TestUSBSetEnv(const char *pcszEnvUsb, const char *pcszEnvUsbRoot)
+{
+ s_pcszTestEnvUsb = pcszEnvUsb;
+ s_pcszTestEnvUsbRoot = pcszEnvUsbRoot;
+}
+
+/* For testing we redefine anything that accesses the outside world to
+ * return test values. */
+# define RTEnvGet(a) \
+ ( !RTStrCmp(a, "VBOX_USB") ? s_pcszTestEnvUsb \
+ : !RTStrCmp(a, "VBOX_USB_ROOT") ? s_pcszTestEnvUsbRoot \
+ : NULL)
+# define USBProxyLinuxCheckDeviceRoot(pcszPath, fUseNodes) \
+ ( ((fUseNodes) && s_fTestDevicesAccessible \
+ && !RTStrCmp(pcszPath, s_pcszTestDevicesRoot)) \
+ || (!(fUseNodes) && s_fTestUsbfsAccessible \
+ && !RTStrCmp(pcszPath, s_pcszTestUsbfsRoot)))
+# define RTDirExists(pcszDir) \
+ ( (pcszDir) \
+ && ( !RTStrCmp(pcszDir, s_pcszTestDevicesRoot) \
+ || !RTStrCmp(pcszDir, s_pcszTestUsbfsRoot)))
+# define RTFileExists(pcszFile) \
+ ( (pcszFile) \
+ && s_pcszTestUsbfsRoot \
+ && !RTStrNCmp(pcszFile, s_pcszTestUsbfsRoot, strlen(s_pcszTestUsbfsRoot)) \
+ && !RTStrCmp(pcszFile + strlen(s_pcszTestUsbfsRoot), "/devices"))
+#endif
+
+/**
+ * Selects the access method that will be used to access USB devices based on
+ * what is available on the host and what if anything the user has specified
+ * in the environment.
+ * @returns iprt status value
+ * @param pfUsingUsbfsDevices on success this will be set to true if
+ * the prefered access method is USBFS-like and to
+ * false if it is sysfs/device node-like
+ * @param ppcszDevicesRoot on success the root of the tree of USBFS-like
+ * device nodes will be stored here
+ */
+int USBProxyLinuxChooseMethod(bool *pfUsingUsbfsDevices,
+ const char **ppcszDevicesRoot)
+{
+ /*
+ * We have two methods available for getting host USB device data - using
+ * USBFS and using sysfs. The default choice is sysfs; if that is not
+ * available we fall back to USBFS.
+ * In the event of both failing, an appropriate error will be returned.
+ * The user may also specify a method and root using the VBOX_USB and
+ * VBOX_USB_ROOT environment variables. In this case we don't check
+ * the root they provide for validity.
+ */
+ bool fUsbfsChosen = false, fSysfsChosen = false;
+ const char *pcszUsbFromEnv = RTEnvGet("VBOX_USB");
+ const char *pcszUsbRoot = NULL;
+ if (pcszUsbFromEnv)
+ {
+ bool fValidVBoxUSB = true;
+
+ pcszUsbRoot = RTEnvGet("VBOX_USB_ROOT");
+ if (!RTStrICmp(pcszUsbFromEnv, "USBFS"))
+ {
+ LogRel(("Default USB access method set to \"usbfs\" from environment\n"));
+ fUsbfsChosen = true;
+ }
+ else if (!RTStrICmp(pcszUsbFromEnv, "SYSFS"))
+ {
+ LogRel(("Default USB method set to \"sysfs\" from environment\n"));
+ fSysfsChosen = true;
+ }
+ else
+ {
+ LogRel(("Invalid VBOX_USB environment variable setting \"%s\"\n",
+ pcszUsbFromEnv));
+ fValidVBoxUSB = false;
+ pcszUsbFromEnv = NULL;
+ }
+ if (!fValidVBoxUSB && pcszUsbRoot)
+ pcszUsbRoot = NULL;
+ }
+ if (!pcszUsbRoot)
+ {
+ if ( !fUsbfsChosen
+ && USBProxyLinuxCheckDeviceRoot("/dev/vboxusb", true))
+ {
+ fSysfsChosen = true;
+ pcszUsbRoot = "/dev/vboxusb";
+ }
+ else if ( !fSysfsChosen
+ && USBProxyLinuxCheckDeviceRoot("/proc/bus/usb", false))
+ {
+ fUsbfsChosen = true;
+ pcszUsbRoot = "/proc/bus/usb";
+ }
+ }
+ else if (!USBProxyLinuxCheckDeviceRoot(pcszUsbRoot, fSysfsChosen))
+ pcszUsbRoot = NULL;
+ if (pcszUsbRoot)
+ {
+ *pfUsingUsbfsDevices = fUsbfsChosen;
+ *ppcszDevicesRoot = pcszUsbRoot;
+ return VINF_SUCCESS;
+ }
+ /* else */
+ return pcszUsbFromEnv ? VERR_NOT_FOUND
+ : RTDirExists("/dev/vboxusb") ? VERR_VUSB_USB_DEVICE_PERMISSION
+ : RTFileExists("/proc/bus/usb/devices") ? VERR_VUSB_USBFS_PERMISSION
+ : VERR_NOT_FOUND;
+}
+
+#ifdef UNIT_TEST
+# undef RTEnvGet
+# undef USBProxyLinuxCheckDeviceRoot
+# undef RTDirExists
+# undef RTFileExists
+#endif
+
+/**
+ * Check whether a USB device tree root is usable
+ * @param pcszRoot the path to the root of the device tree
+ * @param fIsDeviceNodes whether this is a device node (or usbfs) tree
+ * @note returns a pointer into a static array so it will stay valid
+ */
bool USBProxyLinuxCheckDeviceRoot(const char *pcszRoot, bool fIsDeviceNodes)
{
bool fOK = false;
@@ -1535,6 +1706,12 @@ bool USBProxyLinuxCheckDeviceRoot(const char *pcszRoot, bool fIsDeviceNodes)
# undef access
#endif
+/**
+ * Get the list of USB devices supported by the system. Should be freed using
+ * @a deviceFree or something equivalent.
+ * @param pcszDevicesRoot the path to the root of the device tree
+ * @param fUseSysfs whether to use sysfs (or usbfs) for enumeration
+ */
PUSBDEVICE USBProxyLinuxGetDevices(const char *pcszDevicesRoot,
bool fUseSysfs)
{