VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/linux/USBProxyServiceLinux.cpp@ 37010

Last change on this file since 37010 was 36997, checked in by vboxsync, 14 years ago

Main/linux/usb: no reason not to check root paths passed in the environment

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.4 KB
Line 
1/* $Id: USBProxyServiceLinux.cpp 36997 2011-05-06 23:30:23Z vboxsync $ */
2/** @file
3 * VirtualBox USB Proxy Service, Linux Specialization.
4 */
5
6/*
7 * Copyright (C) 2006-2011 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include "USBProxyService.h"
23#include "USBGetDevices.h"
24#include "Logging.h"
25
26#include <VBox/usb.h>
27#include <VBox/usblib.h>
28#include <VBox/err.h>
29
30#include <iprt/string.h>
31#include <iprt/alloc.h>
32#include <iprt/assert.h>
33#include <iprt/ctype.h>
34#include <iprt/dir.h>
35#include <iprt/env.h>
36#include <iprt/file.h>
37#include <iprt/err.h>
38#include <iprt/mem.h>
39#include <iprt/param.h>
40#include <iprt/path.h>
41#include <iprt/stream.h>
42#include <iprt/linux/sysfs.h>
43
44#include <stdlib.h>
45#include <string.h>
46#include <stdio.h>
47#include <errno.h>
48#include <fcntl.h>
49#include <unistd.h>
50#include <sys/statfs.h>
51#include <sys/poll.h>
52#ifdef VBOX_WITH_LINUX_COMPILER_H
53# include <linux/compiler.h>
54#endif
55#include <linux/usbdevice_fs.h>
56
57
58/**
59 * Initialize data members.
60 */
61USBProxyServiceLinux::USBProxyServiceLinux(Host *aHost)
62 : USBProxyService(aHost), mFile(NIL_RTFILE), mWakeupPipeR(NIL_RTFILE),
63 mWakeupPipeW(NIL_RTFILE), mUsingUsbfsDevices(true /* see init */),
64 mUdevPolls(0), mpWaiter(NULL)
65#ifdef UNIT_TEST
66 , mpcszTestUsbfsRoot(NULL), mfTestUsbfsAccessible(false),
67 mpcszTestDevicesRoot(NULL), mfTestDevicesAccessible(false),
68 mrcTestMethodInitResult(VINF_SUCCESS), mpcszTestEnvUsb(NULL),
69 mpcszTestEnvUsbRoot(NULL)
70#endif
71{
72 LogFlowThisFunc(("aHost=%p\n", aHost));
73}
74
75#ifdef UNIT_TEST
76/* For testing we redefine anything that accesses the outside world to
77 * return test values. */
78# define RTEnvGet(a) \
79 ( !RTStrCmp(a, "VBOX_USB") ? mpcszTestEnvUsb \
80 : !RTStrCmp(a, "VBOX_USB_ROOT") ? mpcszTestEnvUsbRoot \
81 : NULL)
82# define USBProxyLinuxCheckDeviceRoot(pcszPath, fUseNodes) \
83 ( ((fUseNodes) && mfTestDevicesAccessible \
84 && !RTStrCmp(pcszPath, mpcszTestDevicesRoot)) \
85 || (!(fUseNodes) && mfTestUsbfsAccessible \
86 && !RTStrCmp(pcszPath, mpcszTestUsbfsRoot)))
87# define RTDirExists(pcszDir) \
88 ( (pcszDir) \
89 && ( !RTStrCmp(pcszDir, mpcszTestDevicesRoot) \
90 || !RTStrCmp(pcszDir, mpcszTestUsbfsRoot)))
91# define RTFileExists(pcszFile) \
92 ( (pcszFile) \
93 && mpcszTestUsbfsRoot \
94 && !RTStrNCmp(pcszFile, mpcszTestUsbfsRoot, strlen(mpcszTestUsbfsRoot)) \
95 && !RTStrCmp(pcszFile + strlen(mpcszTestUsbfsRoot), "/devices"))
96#endif
97
98/**
99 * Initializes the object (called right after construction).
100 *
101 * @returns S_OK on success and non-fatal failures, some COM error otherwise.
102 */
103HRESULT USBProxyServiceLinux::init(void)
104{
105 /*
106 * Call the superclass method first.
107 */
108 HRESULT hrc = USBProxyService::init();
109 AssertComRCReturn(hrc, hrc);
110
111 /*
112 * We have two methods available for getting host USB device data - using
113 * USBFS and using sysfs. The default choice is sysfs; if that is not
114 * available we fall back to USBFS.
115 * In the event of both failing, an appropriate error will be returned.
116 * The user may also specify a method and root using the VBOX_USB and
117 * VBOX_USB_ROOT environment variables. In this case we don't check
118 * the root they provide for validity.
119 */
120 bool fUsbfsChosen = false, fSysfsChosen = false;
121 const char *pcszUsbFromEnv = RTEnvGet("VBOX_USB");
122 const char *pcszUsbRoot = NULL;
123 if (pcszUsbFromEnv)
124 {
125 bool fValidVBoxUSB = true;
126
127 pcszUsbRoot = RTEnvGet("VBOX_USB_ROOT");
128 if (!RTStrICmp(pcszUsbFromEnv, "USBFS"))
129 {
130 LogRel(("Default USB access method set to \"usbfs\" from environment\n"));
131 fUsbfsChosen = true;
132 }
133 else if (!RTStrICmp(pcszUsbFromEnv, "SYSFS"))
134 {
135 LogRel(("Default USB method set to \"sysfs\" from environment\n"));
136 fSysfsChosen = true;
137 }
138 else
139 {
140 LogRel(("Invalid VBOX_USB environment variable setting \"%s\"\n",
141 pcszUsbFromEnv));
142 fValidVBoxUSB = false;
143 pcszUsbFromEnv = NULL;
144 }
145 if (!fValidVBoxUSB && pcszUsbRoot)
146 pcszUsbRoot = NULL;
147 }
148 if (!pcszUsbRoot)
149 {
150 if ( !fUsbfsChosen
151 && USBProxyLinuxCheckDeviceRoot("/dev/vboxusb", true))
152 {
153 fSysfsChosen = true;
154 pcszUsbRoot = "/dev/vboxusb";
155 }
156 else if ( !fSysfsChosen
157 && USBProxyLinuxCheckDeviceRoot("/proc/bus/usb", false))
158 {
159 fUsbfsChosen = true;
160 pcszUsbRoot = "/proc/bus/usb";
161 }
162 }
163 else if (!USBProxyLinuxCheckDeviceRoot(pcszUsbRoot, fSysfsChosen))
164 pcszUsbRoot = NULL;
165 if (pcszUsbRoot)
166 {
167 mUsingUsbfsDevices = fUsbfsChosen;
168 mDevicesRoot = pcszUsbRoot;
169#ifndef UNIT_TEST /* Hack for now */
170 int rc = mUsingUsbfsDevices ? initUsbfs() : initSysfs();
171#else
172 int rc = mrcTestMethodInitResult;
173#endif
174 /* For the day when we have VBoxSVC release logging... */
175 LogRel((RT_SUCCESS(rc) ? "Successfully initialised host USB using %s\n"
176 : "Failed to initialise host USB using %s\n",
177 mUsingUsbfsDevices ? "USBFS" : "sysfs"));
178 mLastError = rc;
179 }
180 else
181 mLastError = pcszUsbFromEnv ? VERR_NOT_FOUND
182 : RTDirExists("/dev/vboxusb") ? VERR_VUSB_USB_DEVICE_PERMISSION
183 : RTFileExists("/proc/bus/usb/devices") ? VERR_VUSB_USBFS_PERMISSION
184 : VERR_NOT_FOUND;
185 return S_OK;
186}
187
188#ifdef UNIT_TEST
189# undef RTEnvGet
190# undef USBProxyLinuxCheckDeviceRoot
191# undef RTDirExists
192# undef RTFileExists
193#endif
194
195/**
196 * Initialization routine for the usbfs based operation.
197 *
198 * @returns iprt status code.
199 */
200int USBProxyServiceLinux::initUsbfs(void)
201{
202 Assert(mUsingUsbfsDevices);
203
204 /*
205 * Open the devices file.
206 */
207 int rc;
208 char *pszDevices = RTPathJoinA(mDevicesRoot.c_str(), "devices");
209 if (pszDevices)
210 {
211 rc = RTFileOpen(&mFile, pszDevices, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
212 if (RT_SUCCESS(rc))
213 {
214 int pipes[2];
215 if (!pipe(pipes))
216 {
217 /* Set close on exec (race here!) */
218 if ( fcntl(pipes[0], F_SETFD, FD_CLOEXEC) >= 0
219 && fcntl(pipes[1], F_SETFD, FD_CLOEXEC) >= 0)
220 {
221 mWakeupPipeR = pipes[0];
222 mWakeupPipeW = pipes[1];
223 /*
224 * Start the poller thread.
225 */
226 rc = start();
227 if (RT_SUCCESS(rc))
228 {
229 RTStrFree(pszDevices);
230 LogFlowThisFunc(("returns successfully - mWakeupPipeR/W=%d/%d\n",
231 mWakeupPipeR, mWakeupPipeW));
232 return VINF_SUCCESS;
233 }
234
235 RTFileClose(mWakeupPipeR);
236 RTFileClose(mWakeupPipeW);
237 mWakeupPipeW = mWakeupPipeR = NIL_RTFILE;
238 }
239 else
240 {
241 rc = RTErrConvertFromErrno(errno);
242 Log(("USBProxyServiceLinux::USBProxyServiceLinux: fcntl failed, errno=%d\n", errno));
243 close(pipes[0]);
244 close(pipes[1]);
245 }
246 }
247 else
248 {
249 rc = RTErrConvertFromErrno(errno);
250 Log(("USBProxyServiceLinux::USBProxyServiceLinux: pipe failed, errno=%d\n", errno));
251 }
252 RTFileClose(mFile);
253 }
254
255 RTStrFree(pszDevices);
256 }
257 else
258 {
259 rc = VERR_NO_MEMORY;
260 Log(("USBProxyServiceLinux::USBProxyServiceLinux: out of memory!\n"));
261 }
262
263 LogFlowThisFunc(("returns failure!!! (rc=%Rrc)\n", rc));
264 return rc;
265}
266
267
268/**
269 * Initialization routine for the sysfs based operation.
270 *
271 * @returns iprt status code
272 */
273int USBProxyServiceLinux::initSysfs(void)
274{
275 Assert(!mUsingUsbfsDevices);
276
277#ifdef VBOX_USB_WITH_SYSFS
278 try
279 {
280 mpWaiter = new VBoxMainHotplugWaiter(mDevicesRoot.c_str());
281 }
282 catch(std::bad_alloc &e)
283 {
284 return VERR_NO_MEMORY;
285 }
286 int rc = mpWaiter->getStatus();
287 if (RT_SUCCESS(rc) || rc == VERR_TIMEOUT || rc == VERR_TRY_AGAIN)
288 rc = start();
289 else if (rc == VERR_NOT_SUPPORTED)
290 /* This can legitimately happen if hal or DBus are not running, but of
291 * course we can't start in this case. */
292 rc = VINF_SUCCESS;
293 return rc;
294
295#else /* !VBOX_USB_WITH_SYSFS */
296 return VERR_NOT_IMPLEMENTED;
297#endif /* !VBOX_USB_WITH_SYSFS */
298}
299
300
301/**
302 * Stop all service threads and free the device chain.
303 */
304USBProxyServiceLinux::~USBProxyServiceLinux()
305{
306 LogFlowThisFunc(("\n"));
307
308 /*
309 * Stop the service.
310 */
311 if (isActive())
312 stop();
313
314 /*
315 * Free resources.
316 */
317 doUsbfsCleanupAsNeeded();
318#ifdef VBOX_USB_WITH_SYSFS
319 if (mpWaiter)
320 delete mpWaiter;
321#endif
322}
323
324
325/**
326 * If any Usbfs-related resources are currently allocated, then free them
327 * and mark them as freed.
328 */
329void USBProxyServiceLinux::doUsbfsCleanupAsNeeded()
330{
331 /*
332 * Free resources.
333 */
334 if (mFile != NIL_RTFILE)
335 {
336 RTFileClose(mFile);
337 mFile = NIL_RTFILE;
338 }
339
340 if (mWakeupPipeR != NIL_RTFILE)
341 RTFileClose(mWakeupPipeR);
342 if (mWakeupPipeW != NIL_RTFILE)
343 RTFileClose(mWakeupPipeW);
344 mWakeupPipeW = mWakeupPipeR = NIL_RTFILE;
345}
346
347
348int USBProxyServiceLinux::captureDevice(HostUSBDevice *aDevice)
349{
350 Log(("USBProxyServiceLinux::captureDevice: %p {%s}\n", aDevice, aDevice->getName().c_str()));
351 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
352 AssertReturn(aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
353
354 /*
355 * Don't think we need to do anything when the device is held... fake it.
356 */
357 Assert(aDevice->getUnistate() == kHostUSBDeviceState_Capturing);
358 interruptWait();
359
360 return VINF_SUCCESS;
361}
362
363
364int USBProxyServiceLinux::releaseDevice(HostUSBDevice *aDevice)
365{
366 Log(("USBProxyServiceLinux::releaseDevice: %p\n", aDevice));
367 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
368 AssertReturn(aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
369
370 /*
371 * We're not really holding it atm., just fake it.
372 */
373 Assert(aDevice->getUnistate() == kHostUSBDeviceState_ReleasingToHost);
374 interruptWait();
375
376 return VINF_SUCCESS;
377}
378
379
380bool USBProxyServiceLinux::updateDeviceState(HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice, bool *aRunFilters, SessionMachine **aIgnoreMachine)
381{
382 if ( aUSBDevice->enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE
383 && aDevice->mUsb->enmState == USBDEVICESTATE_USED_BY_HOST)
384 LogRel(("USBProxy: Device %04x:%04x (%s) has become accessible.\n",
385 aUSBDevice->idVendor, aUSBDevice->idProduct, aUSBDevice->pszAddress));
386 return updateDeviceStateFake(aDevice, aUSBDevice, aRunFilters, aIgnoreMachine);
387}
388
389
390/**
391 * A device was added, we need to adjust mUdevPolls.
392 *
393 * See USBProxyService::deviceAdded for details.
394 */
395void USBProxyServiceLinux::deviceAdded(ComObjPtr<HostUSBDevice> &aDevice, SessionMachinesList &llOpenedMachines, PUSBDEVICE aUSBDevice)
396{
397 if (aUSBDevice->enmState == USBDEVICESTATE_USED_BY_HOST)
398 {
399 LogRel(("USBProxy: Device %04x:%04x (%s) isn't accessible. giving udev a few seconds to fix this...\n",
400 aUSBDevice->idVendor, aUSBDevice->idProduct, aUSBDevice->pszAddress));
401 mUdevPolls = 10; /* (10 * 500ms = 5s) */
402 }
403
404 USBProxyService::deviceAdded(aDevice, llOpenedMachines, aUSBDevice);
405}
406
407
408int USBProxyServiceLinux::wait(RTMSINTERVAL aMillies)
409{
410 int rc;
411 if (mUsingUsbfsDevices)
412 rc = waitUsbfs(aMillies);
413 else
414 rc = waitSysfs(aMillies);
415 return rc;
416}
417
418
419/** String written to the wakeup pipe. */
420#define WAKE_UP_STRING "WakeUp!"
421/** Length of the string written. */
422#define WAKE_UP_STRING_LEN ( sizeof(WAKE_UP_STRING) - 1 )
423
424int USBProxyServiceLinux::waitUsbfs(RTMSINTERVAL aMillies)
425{
426 struct pollfd PollFds[2];
427
428 /* Cap the wait interval if we're polling for udevd changing device permissions. */
429 if (aMillies > 500 && mUdevPolls > 0)
430 {
431 mUdevPolls--;
432 aMillies = 500;
433 }
434
435 memset(&PollFds, 0, sizeof(PollFds));
436 PollFds[0].fd = mFile;
437 PollFds[0].events = POLLIN;
438 PollFds[1].fd = mWakeupPipeR;
439 PollFds[1].events = POLLIN | POLLERR | POLLHUP;
440
441 int rc = poll(&PollFds[0], 2, aMillies);
442 if (rc == 0)
443 return VERR_TIMEOUT;
444 if (rc > 0)
445 {
446 /* drain the pipe */
447 if (PollFds[1].revents & POLLIN)
448 {
449 char szBuf[WAKE_UP_STRING_LEN];
450 rc = RTFileRead(mWakeupPipeR, szBuf, sizeof(szBuf), NULL);
451 AssertRC(rc);
452 }
453 return VINF_SUCCESS;
454 }
455 return RTErrConvertFromErrno(errno);
456}
457
458
459int USBProxyServiceLinux::waitSysfs(RTMSINTERVAL aMillies)
460{
461#ifdef VBOX_USB_WITH_SYSFS
462 int rc = mpWaiter->Wait(aMillies);
463 if (rc == VERR_TRY_AGAIN)
464 {
465 RTThreadYield();
466 rc = VINF_SUCCESS;
467 }
468 return rc;
469#else /* !VBOX_USB_WITH_SYSFS */
470 return USBProxyService::wait(aMillies);
471#endif /* !VBOX_USB_WITH_SYSFS */
472}
473
474
475int USBProxyServiceLinux::interruptWait(void)
476{
477#ifdef VBOX_USB_WITH_SYSFS
478 LogFlowFunc(("mUsingUsbfsDevices=%d\n", mUsingUsbfsDevices));
479 if (!mUsingUsbfsDevices)
480 {
481 mpWaiter->Interrupt();
482 LogFlowFunc(("Returning VINF_SUCCESS\n"));
483 return VINF_SUCCESS;
484 }
485#endif /* VBOX_USB_WITH_SYSFS */
486 int rc = RTFileWrite(mWakeupPipeW, WAKE_UP_STRING, WAKE_UP_STRING_LEN, NULL);
487 if (RT_SUCCESS(rc))
488 RTFileFlush(mWakeupPipeW);
489 LogFlowFunc(("returning %Rrc\n", rc));
490 return rc;
491}
492
493
494PUSBDEVICE USBProxyServiceLinux::getDevices(void)
495{
496 return USBProxyLinuxGetDevices(mDevicesRoot.c_str(), !mUsingUsbfsDevices);
497}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette