VirtualBox

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

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

Main/USBProxyService: implementation inheritance is not so great that we have to pretend to do it when we are not

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