VirtualBox

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

Last change on this file since 43446 was 41528, checked in by vboxsync, 13 years ago

Main/HostUSBDevice(all platforms)+USBProxyService: redo USB locking, fixes major regression, added lots of assertions to catch locking flaws early, whitespace cleanup
Main/Machine: small USB locking fix to be consistent with the remaining code
Main/Host+glue/AutoLock: replace USB list lock by host lock, small numbering cleanup
Main/Console: redo USB locking, do less in USB callbacks/EMT (addresses long standing todo items), eliminate unsafe iterator parameters

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.6 KB
Line 
1/* $Id: USBProxyServiceLinux.cpp 41528 2012-05-31 16:48:33Z vboxsync $ */
2/** @file
3 * VirtualBox USB Proxy Service, Linux Specialization.
4 */
5
6/*
7 * Copyright (C) 2005-2012 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/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{
67 LogFlowThisFunc(("aHost=%p\n", aHost));
68}
69
70/**
71 * Initializes the object (called right after construction).
72 *
73 * @returns S_OK on success and non-fatal failures, some COM error otherwise.
74 */
75HRESULT USBProxyServiceLinux::init(void)
76{
77 const char *pcszDevicesRoot;
78 int rc = USBProxyLinuxChooseMethod(&mUsingUsbfsDevices, &pcszDevicesRoot);
79 if (RT_SUCCESS(rc))
80 {
81 mDevicesRoot = pcszDevicesRoot;
82 rc = mUsingUsbfsDevices ? initUsbfs() : initSysfs();
83 /* For the day when we have VBoxSVC release logging... */
84 LogRel((RT_SUCCESS(rc) ? "Successfully initialised host USB using %s\n"
85 : "Failed to initialise host USB using %s\n",
86 mUsingUsbfsDevices ? "USBFS" : "sysfs"));
87 }
88 mLastError = rc;
89 return S_OK;
90}
91
92/**
93 * Initialization routine for the usbfs based operation.
94 *
95 * @returns iprt status code.
96 */
97int USBProxyServiceLinux::initUsbfs(void)
98{
99 Assert(mUsingUsbfsDevices);
100
101 /*
102 * Open the devices file.
103 */
104 int rc;
105 char *pszDevices = RTPathJoinA(mDevicesRoot.c_str(), "devices");
106 if (pszDevices)
107 {
108 rc = RTFileOpen(&mhFile, pszDevices, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
109 if (RT_SUCCESS(rc))
110 {
111 rc = RTPipeCreate(&mhWakeupPipeR, &mhWakeupPipeW, 0 /*fFlags*/);
112 if (RT_SUCCESS(rc))
113 {
114 /*
115 * Start the poller thread.
116 */
117 rc = start();
118 if (RT_SUCCESS(rc))
119 {
120 RTStrFree(pszDevices);
121 LogFlowThisFunc(("returns successfully\n"));
122 return VINF_SUCCESS;
123 }
124
125 RTPipeClose(mhWakeupPipeR);
126 RTPipeClose(mhWakeupPipeW);
127 mhWakeupPipeW = mhWakeupPipeR = NIL_RTPIPE;
128 }
129 else
130 Log(("USBProxyServiceLinux::USBProxyServiceLinux: RTFilePipe failed with rc=%Rrc\n", rc));
131 RTFileClose(mhFile);
132 }
133
134 RTStrFree(pszDevices);
135 }
136 else
137 {
138 rc = VERR_NO_MEMORY;
139 Log(("USBProxyServiceLinux::USBProxyServiceLinux: out of memory!\n"));
140 }
141
142 LogFlowThisFunc(("returns failure!!! (rc=%Rrc)\n", rc));
143 return rc;
144}
145
146
147/**
148 * Initialization routine for the sysfs based operation.
149 *
150 * @returns iprt status code
151 */
152int USBProxyServiceLinux::initSysfs(void)
153{
154 Assert(!mUsingUsbfsDevices);
155
156#ifdef VBOX_USB_WITH_SYSFS
157 try
158 {
159 mpWaiter = new VBoxMainHotplugWaiter(mDevicesRoot.c_str());
160 }
161 catch(std::bad_alloc &e)
162 {
163 return VERR_NO_MEMORY;
164 }
165 int rc = mpWaiter->getStatus();
166 if (RT_SUCCESS(rc) || rc == VERR_TIMEOUT || rc == VERR_TRY_AGAIN)
167 rc = start();
168 else if (rc == VERR_NOT_SUPPORTED)
169 /* This can legitimately happen if hal or DBus are not running, but of
170 * course we can't start in this case. */
171 rc = VINF_SUCCESS;
172 return rc;
173
174#else /* !VBOX_USB_WITH_SYSFS */
175 return VERR_NOT_IMPLEMENTED;
176#endif /* !VBOX_USB_WITH_SYSFS */
177}
178
179
180/**
181 * Stop all service threads and free the device chain.
182 */
183USBProxyServiceLinux::~USBProxyServiceLinux()
184{
185 LogFlowThisFunc(("\n"));
186
187 /*
188 * Stop the service.
189 */
190 if (isActive())
191 stop();
192
193 /*
194 * Free resources.
195 */
196 doUsbfsCleanupAsNeeded();
197#ifdef VBOX_USB_WITH_SYSFS
198 if (mpWaiter)
199 delete mpWaiter;
200#endif
201}
202
203
204/**
205 * If any Usbfs-related resources are currently allocated, then free them
206 * and mark them as freed.
207 */
208void USBProxyServiceLinux::doUsbfsCleanupAsNeeded()
209{
210 /*
211 * Free resources.
212 */
213 RTFileClose(mhFile);
214 mhFile = NIL_RTFILE;
215
216 RTPipeClose(mhWakeupPipeR);
217 RTPipeClose(mhWakeupPipeW);
218 mhWakeupPipeW = mhWakeupPipeR = NIL_RTPIPE;
219}
220
221
222int USBProxyServiceLinux::captureDevice(HostUSBDevice *aDevice)
223{
224 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
225 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
226
227 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
228 LogFlowThisFunc(("aDevice=%s\n", aDevice->getName().c_str()));
229
230 /*
231 * Don't think we need to do anything when the device is held... fake it.
232 */
233 Assert(aDevice->getUnistate() == kHostUSBDeviceState_Capturing);
234 devLock.release();
235 interruptWait();
236
237 return VINF_SUCCESS;
238}
239
240
241int USBProxyServiceLinux::releaseDevice(HostUSBDevice *aDevice)
242{
243 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
244 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
245
246 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
247 LogFlowThisFunc(("aDevice=%s\n", aDevice->getName().c_str()));
248
249 /*
250 * We're not really holding it atm., just fake it.
251 */
252 Assert(aDevice->getUnistate() == kHostUSBDeviceState_ReleasingToHost);
253 devLock.release();
254 interruptWait();
255
256 return VINF_SUCCESS;
257}
258
259
260bool USBProxyServiceLinux::updateDeviceState(HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice, bool *aRunFilters, SessionMachine **aIgnoreMachine)
261{
262 AssertReturn(aDevice, false);
263 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), false);
264 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
265 if ( aUSBDevice->enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE
266 && aDevice->mUsb->enmState == USBDEVICESTATE_USED_BY_HOST)
267 LogRel(("USBProxy: Device %04x:%04x (%s) has become accessible.\n",
268 aUSBDevice->idVendor, aUSBDevice->idProduct, aUSBDevice->pszAddress));
269 devLock.release();
270 return updateDeviceStateFake(aDevice, aUSBDevice, aRunFilters, aIgnoreMachine);
271}
272
273
274/**
275 * A device was added, we need to adjust mUdevPolls.
276 *
277 * See USBProxyService::deviceAdded for details.
278 */
279void USBProxyServiceLinux::deviceAdded(ComObjPtr<HostUSBDevice> &aDevice, SessionMachinesList &llOpenedMachines, PUSBDEVICE aUSBDevice)
280{
281 AssertReturnVoid(aDevice);
282 AssertReturnVoid(!aDevice->isWriteLockOnCurrentThread());
283 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
284 if (aUSBDevice->enmState == USBDEVICESTATE_USED_BY_HOST)
285 {
286 LogRel(("USBProxy: Device %04x:%04x (%s) isn't accessible. giving udev a few seconds to fix this...\n",
287 aUSBDevice->idVendor, aUSBDevice->idProduct, aUSBDevice->pszAddress));
288 mUdevPolls = 10; /* (10 * 500ms = 5s) */
289 }
290
291 devLock.release();
292 USBProxyService::deviceAdded(aDevice, llOpenedMachines, aUSBDevice);
293}
294
295
296int USBProxyServiceLinux::wait(RTMSINTERVAL aMillies)
297{
298 int rc;
299 if (mUsingUsbfsDevices)
300 rc = waitUsbfs(aMillies);
301 else
302 rc = waitSysfs(aMillies);
303 return rc;
304}
305
306
307/** String written to the wakeup pipe. */
308#define WAKE_UP_STRING "WakeUp!"
309/** Length of the string written. */
310#define WAKE_UP_STRING_LEN ( sizeof(WAKE_UP_STRING) - 1 )
311
312int USBProxyServiceLinux::waitUsbfs(RTMSINTERVAL aMillies)
313{
314 struct pollfd PollFds[2];
315
316 /* Cap the wait interval if we're polling for udevd changing device permissions. */
317 if (aMillies > 500 && mUdevPolls > 0)
318 {
319 mUdevPolls--;
320 aMillies = 500;
321 }
322
323 memset(&PollFds, 0, sizeof(PollFds));
324 PollFds[0].fd = RTFileToNative(mhFile);
325 PollFds[0].events = POLLIN;
326 PollFds[1].fd = RTPipeToNative(mhWakeupPipeR);
327 PollFds[1].events = POLLIN | POLLERR | POLLHUP;
328
329 int rc = poll(&PollFds[0], 2, aMillies);
330 if (rc == 0)
331 return VERR_TIMEOUT;
332 if (rc > 0)
333 {
334 /* drain the pipe */
335 if (PollFds[1].revents & POLLIN)
336 {
337 char szBuf[WAKE_UP_STRING_LEN];
338 rc = RTPipeReadBlocking(mhWakeupPipeR, szBuf, sizeof(szBuf), NULL);
339 AssertRC(rc);
340 }
341 return VINF_SUCCESS;
342 }
343 return RTErrConvertFromErrno(errno);
344}
345
346
347int USBProxyServiceLinux::waitSysfs(RTMSINTERVAL aMillies)
348{
349#ifdef VBOX_USB_WITH_SYSFS
350 int rc = mpWaiter->Wait(aMillies);
351 if (rc == VERR_TRY_AGAIN)
352 {
353 RTThreadYield();
354 rc = VINF_SUCCESS;
355 }
356 return rc;
357#else /* !VBOX_USB_WITH_SYSFS */
358 return USBProxyService::wait(aMillies);
359#endif /* !VBOX_USB_WITH_SYSFS */
360}
361
362
363int USBProxyServiceLinux::interruptWait(void)
364{
365 AssertReturn(!isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
366
367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
368#ifdef VBOX_USB_WITH_SYSFS
369 LogFlowFunc(("mUsingUsbfsDevices=%d\n", mUsingUsbfsDevices));
370 if (!mUsingUsbfsDevices)
371 {
372 mpWaiter->Interrupt();
373 LogFlowFunc(("Returning VINF_SUCCESS\n"));
374 return VINF_SUCCESS;
375 }
376#endif /* VBOX_USB_WITH_SYSFS */
377 int rc = RTPipeWriteBlocking(mhWakeupPipeW, WAKE_UP_STRING, WAKE_UP_STRING_LEN, NULL);
378 if (RT_SUCCESS(rc))
379 RTPipeFlush(mhWakeupPipeW);
380 LogFlowFunc(("returning %Rrc\n", rc));
381 return rc;
382}
383
384
385PUSBDEVICE USBProxyServiceLinux::getDevices(void)
386{
387 return USBProxyLinuxGetDevices(mDevicesRoot.c_str(), !mUsingUsbfsDevices);
388}
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