VirtualBox

source: vbox/trunk/src/VBox/Main/linux/USBProxyServiceLinux.cpp@ 33181

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

Main/linux/USB enumeration: set close-on-exec and close files

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.3 KB
Line 
1/* $Id: USBProxyServiceLinux.cpp 33100 2010-10-13 12:09:23Z vboxsync $ */
2/** @file
3 * VirtualBox USB Proxy Service, Linux Specialization.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. 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/env.h>
35#include <iprt/file.h>
36#include <iprt/err.h>
37#include <iprt/mem.h>
38#include <iprt/param.h>
39#include <iprt/path.h>
40#include <iprt/stream.h>
41#include <iprt/linux/sysfs.h>
42
43#include <stdlib.h>
44#include <string.h>
45#include <stdio.h>
46#include <errno.h>
47#include <fcntl.h>
48#include <unistd.h>
49#include <sys/statfs.h>
50#include <sys/poll.h>
51#ifdef VBOX_WITH_LINUX_COMPILER_H
52# include <linux/compiler.h>
53#endif
54#include <linux/usbdevice_fs.h>
55
56
57/*******************************************************************************
58* Structures and Typedefs *
59*******************************************************************************/
60/** Suffix translation. */
61typedef struct USBSUFF
62{
63 char szSuff[4];
64 unsigned cchSuff;
65 unsigned uMul;
66 unsigned uDiv;
67} USBSUFF, *PUSBSUFF;
68typedef const USBSUFF *PCUSBSUFF;
69
70
71/*******************************************************************************
72* Global Variables *
73*******************************************************************************/
74/**
75 * Suffixes for the endpoint polling interval.
76 */
77static const USBSUFF s_aIntervalSuff[] =
78{
79 { "ms", 2, 1, 0 },
80 { "us", 2, 1, 1000 },
81 { "ns", 2, 1, 1000000 },
82 { "s", 1, 1000, 0 },
83 { "", 0, 0, 0 } /* term */
84};
85
86
87/**
88 * Initialize data members.
89 */
90USBProxyServiceLinux::USBProxyServiceLinux(Host *aHost, const char *aUsbfsRoot /* = "/proc/bus/usb" */)
91 : USBProxyService(aHost), mFile(NIL_RTFILE), mWakeupPipeR(NIL_RTFILE),
92 mWakeupPipeW(NIL_RTFILE), mUsbfsRoot(aUsbfsRoot), mUsingUsbfsDevices(true /* see init */), mUdevPolls(0)
93{
94 LogFlowThisFunc(("aHost=%p aUsbfsRoot=%p:{%s}\n", aHost, aUsbfsRoot, aUsbfsRoot));
95}
96
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/hal. The default choice depends on build-time
114 * settings and an environment variable; if the default is not available
115 * we fall back to the second.
116 * In the event of both failing, the error from the second method tried
117 * will be presented to the user.
118 */
119#ifdef VBOX_WITH_SYSFS_BY_DEFAULT
120 mUsingUsbfsDevices = false;
121#else
122 mUsingUsbfsDevices = true;
123#endif
124 const char *pszUsbFromEnv = RTEnvGet("VBOX_USB");
125 if (pszUsbFromEnv)
126 {
127 if (!RTStrICmp(pszUsbFromEnv, "USBFS"))
128 {
129 LogRel(("Default USB access method set to \"usbfs\" from environment\n"));
130 mUsingUsbfsDevices = true;
131 }
132 else if (!RTStrICmp(pszUsbFromEnv, "SYSFS"))
133 {
134 LogRel(("Default USB method set to \"sysfs\" from environment\n"));
135 mUsingUsbfsDevices = false;
136 }
137 else
138 LogRel(("Invalid VBOX_USB environment variable setting \"%s\"\n",
139 pszUsbFromEnv));
140 }
141 int rc = mUsingUsbfsDevices ? initUsbfs() : initSysfs();
142 if (RT_FAILURE(rc))
143 {
144 /* For the day when we have VBoxSVC release logging... */
145 LogRel(("Failed to initialise host USB using %s\n",
146 mUsingUsbfsDevices ? "USBFS" : "sysfs/hal"));
147 mUsingUsbfsDevices = !mUsingUsbfsDevices;
148 rc = mUsingUsbfsDevices ? initUsbfs() : initSysfs();
149 }
150 LogRel((RT_SUCCESS(rc) ? "Successfully initialised host USB using %s\n"
151 : "Failed to initialise host USB using %s\n",
152 mUsingUsbfsDevices ? "USBFS" : "sysfs/hal"));
153 mLastError = rc;
154 return S_OK;
155}
156
157
158/**
159 * Initializiation routine for the usbfs based operation.
160 *
161 * @returns iprt status code.
162 */
163int USBProxyServiceLinux::initUsbfs(void)
164{
165 Assert(mUsingUsbfsDevices);
166
167 /*
168 * Open the devices file.
169 */
170 int rc;
171 char *pszDevices;
172 RTStrAPrintf(&pszDevices, "%s/devices", mUsbfsRoot.c_str());
173 if (pszDevices)
174 {
175 rc = USBProxyLinuxCheckForUsbfs(pszDevices);
176 if (RT_SUCCESS(rc))
177 {
178 rc = RTFileOpen(&mFile, pszDevices, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
179 if (RT_SUCCESS(rc))
180 {
181 int pipes[2];
182 if (!pipe(pipes))
183 {
184 /* Set close on exec (race here!) */
185 if ( fcntl(pipes[0], F_SETFD, FD_CLOEXEC) >= 0
186 && fcntl(pipes[1], F_SETFD, FD_CLOEXEC) >= 0)
187 {
188 mWakeupPipeR = pipes[0];
189 mWakeupPipeW = pipes[1];
190 /*
191 * Start the poller thread.
192 */
193 rc = start();
194 if (RT_SUCCESS(rc))
195 {
196 RTStrFree(pszDevices);
197 LogFlowThisFunc(("returns successfully - mWakeupPipeR/W=%d/%d\n",
198 mWakeupPipeR, mWakeupPipeW));
199 return VINF_SUCCESS;
200 }
201
202 RTFileClose(mWakeupPipeR);
203 RTFileClose(mWakeupPipeW);
204 mWakeupPipeW = mWakeupPipeR = NIL_RTFILE;
205 }
206 else
207 {
208 rc = RTErrConvertFromErrno(errno);
209 Log(("USBProxyServiceLinux::USBProxyServiceLinux: fcntl failed, errno=%d\n", errno));
210 close(pipes[0]);
211 close(pipes[1]);
212 }
213 }
214 else
215 {
216 rc = RTErrConvertFromErrno(errno);
217 Log(("USBProxyServiceLinux::USBProxyServiceLinux: pipe failed, errno=%d\n", errno));
218 }
219 RTFileClose(mFile);
220 }
221
222 }
223 RTStrFree(pszDevices);
224 }
225 else
226 {
227 rc = VERR_NO_MEMORY;
228 Log(("USBProxyServiceLinux::USBProxyServiceLinux: out of memory!\n"));
229 }
230
231 LogFlowThisFunc(("returns failure!!! (rc=%Rrc)\n", rc));
232 return rc;
233}
234
235
236/**
237 * Initializiation routine for the sysfs based operation.
238 *
239 * @returns iprt status code
240 */
241int USBProxyServiceLinux::initSysfs(void)
242{
243 Assert(!mUsingUsbfsDevices);
244
245#ifdef VBOX_USB_WITH_SYSFS
246 int rc = mWaiter.getStatus();
247 if (RT_SUCCESS(rc) || rc == VERR_TIMEOUT || rc == VERR_TRY_AGAIN)
248 rc = start();
249 else if (rc == VERR_NOT_SUPPORTED)
250 /* This can legitimately happen if hal or DBus are not running, but of
251 * course we can't start in this case. */
252 rc = VINF_SUCCESS;
253 return rc;
254
255#else /* !VBOX_USB_WITH_SYSFS */
256 return VERR_NOT_IMPLEMENTED;
257#endif /* !VBOX_USB_WITH_SYSFS */
258}
259
260
261/**
262 * Stop all service threads and free the device chain.
263 */
264USBProxyServiceLinux::~USBProxyServiceLinux()
265{
266 LogFlowThisFunc(("\n"));
267
268 /*
269 * Stop the service.
270 */
271 if (isActive())
272 stop();
273
274 /*
275 * Free resources.
276 */
277 doUsbfsCleanupAsNeeded();
278
279 /* (No extra work for !mUsingUsbfsDevices.) */
280}
281
282
283/**
284 * If any Usbfs-releated resources are currently allocated, then free them
285 * and mark them as freed.
286 */
287void USBProxyServiceLinux::doUsbfsCleanupAsNeeded()
288{
289 /*
290 * Free resources.
291 */
292 if (mFile != NIL_RTFILE)
293 {
294 RTFileClose(mFile);
295 mFile = NIL_RTFILE;
296 }
297
298 if (mWakeupPipeR != NIL_RTFILE)
299 RTFileClose(mWakeupPipeR);
300 if (mWakeupPipeW != NIL_RTFILE)
301 RTFileClose(mWakeupPipeW);
302 mWakeupPipeW = mWakeupPipeR = NIL_RTFILE;
303}
304
305
306int USBProxyServiceLinux::captureDevice(HostUSBDevice *aDevice)
307{
308 Log(("USBProxyServiceLinux::captureDevice: %p {%s}\n", aDevice, aDevice->getName().c_str()));
309 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
310 AssertReturn(aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
311
312 /*
313 * Don't think we need to do anything when the device is held... fake it.
314 */
315 Assert(aDevice->getUnistate() == kHostUSBDeviceState_Capturing);
316 interruptWait();
317
318 return VINF_SUCCESS;
319}
320
321
322int USBProxyServiceLinux::releaseDevice(HostUSBDevice *aDevice)
323{
324 Log(("USBProxyServiceLinux::releaseDevice: %p\n", aDevice));
325 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
326 AssertReturn(aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
327
328 /*
329 * We're not really holding it atm., just fake it.
330 */
331 Assert(aDevice->getUnistate() == kHostUSBDeviceState_ReleasingToHost);
332 interruptWait();
333
334 return VINF_SUCCESS;
335}
336
337
338bool USBProxyServiceLinux::updateDeviceState(HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice, bool *aRunFilters, SessionMachine **aIgnoreMachine)
339{
340 if ( aUSBDevice->enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE
341 && aDevice->mUsb->enmState == USBDEVICESTATE_USED_BY_HOST)
342 LogRel(("USBProxy: Device %04x:%04x (%s) has become accessible.\n",
343 aUSBDevice->idVendor, aUSBDevice->idProduct, aUSBDevice->pszAddress));
344 return updateDeviceStateFake(aDevice, aUSBDevice, aRunFilters, aIgnoreMachine);
345}
346
347
348/**
349 * A device was added, we need to adjust mUdevPolls.
350 *
351 * See USBProxyService::deviceAdded for details.
352 */
353void USBProxyServiceLinux::deviceAdded(ComObjPtr<HostUSBDevice> &aDevice, SessionMachinesList &llOpenedMachines, PUSBDEVICE aUSBDevice)
354{
355 if (aUSBDevice->enmState == USBDEVICESTATE_USED_BY_HOST)
356 {
357 LogRel(("USBProxy: Device %04x:%04x (%s) isn't accessible. giving udev a few seconds to fix this...\n",
358 aUSBDevice->idVendor, aUSBDevice->idProduct, aUSBDevice->pszAddress));
359 mUdevPolls = 10; /* (10 * 500ms = 5s) */
360 }
361
362 USBProxyService::deviceAdded(aDevice, llOpenedMachines, aUSBDevice);
363}
364
365
366int USBProxyServiceLinux::wait(RTMSINTERVAL aMillies)
367{
368 int rc;
369 if (mUsingUsbfsDevices)
370 rc = waitUsbfs(aMillies);
371 else
372 rc = waitSysfs(aMillies);
373 return rc;
374}
375
376
377/** String written to the wakeup pipe. */
378#define WAKE_UP_STRING "WakeUp!"
379/** Length of the string written. */
380#define WAKE_UP_STRING_LEN ( sizeof(WAKE_UP_STRING) - 1 )
381
382int USBProxyServiceLinux::waitUsbfs(RTMSINTERVAL aMillies)
383{
384 struct pollfd PollFds[2];
385
386 /* Cap the wait interval if we're polling for udevd changing device permissions. */
387 if (aMillies > 500 && mUdevPolls > 0)
388 {
389 mUdevPolls--;
390 aMillies = 500;
391 }
392
393 memset(&PollFds, 0, sizeof(PollFds));
394 PollFds[0].fd = mFile;
395 PollFds[0].events = POLLIN;
396 PollFds[1].fd = mWakeupPipeR;
397 PollFds[1].events = POLLIN | POLLERR | POLLHUP;
398
399 int rc = poll(&PollFds[0], 2, aMillies);
400 if (rc == 0)
401 return VERR_TIMEOUT;
402 if (rc > 0)
403 {
404 /* drain the pipe */
405 if (PollFds[1].revents & POLLIN)
406 {
407 char szBuf[WAKE_UP_STRING_LEN];
408 rc = RTFileRead(mWakeupPipeR, szBuf, sizeof(szBuf), NULL);
409 AssertRC(rc);
410 }
411 return VINF_SUCCESS;
412 }
413 return RTErrConvertFromErrno(errno);
414}
415
416
417int USBProxyServiceLinux::waitSysfs(RTMSINTERVAL aMillies)
418{
419#ifdef VBOX_USB_WITH_SYSFS
420 int rc = mWaiter.Wait(aMillies);
421 if (rc == VERR_TRY_AGAIN)
422 {
423 RTThreadYield();
424 rc = VINF_SUCCESS;
425 }
426 return rc;
427#else /* !VBOX_USB_WITH_SYSFS */
428 return USBProxyService::wait(aMillies);
429#endif /* !VBOX_USB_WITH_SYSFS */
430}
431
432
433int USBProxyServiceLinux::interruptWait(void)
434{
435#ifdef VBOX_USB_WITH_SYSFS
436 LogFlowFunc(("mUsingUsbfsDevices=%d\n", mUsingUsbfsDevices));
437 if (!mUsingUsbfsDevices)
438 {
439 mWaiter.Interrupt();
440 LogFlowFunc(("Returning VINF_SUCCESS\n"));
441 return VINF_SUCCESS;
442 }
443#endif /* VBOX_USB_WITH_SYSFS */
444 int rc = RTFileWrite(mWakeupPipeW, WAKE_UP_STRING, WAKE_UP_STRING_LEN, NULL);
445 if (RT_SUCCESS(rc))
446 RTFileFlush(mWakeupPipeW);
447 LogFlowFunc(("returning %Rrc\n", rc));
448 return rc;
449}
450
451
452PUSBDEVICE USBProxyServiceLinux::getDevices(void)
453{
454 if (mUsingUsbfsDevices)
455 return USBProxyLinuxGetDevices(mUsbfsRoot.c_str());
456 else
457 return USBProxyLinuxGetDevices(NULL);
458}
459
Note: See TracBrowser for help on using the repository browser.

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