VirtualBox

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

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

*: RTFILE becomes a pointer, RTFileOpen++ expands it's flags paramter from uint32_t to uint64_t.

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