VirtualBox

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

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

Main: fix log message format string bugs

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