VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/linux/USBProxyBackendLinux.cpp@ 60106

Last change on this file since 60106 was 60106, checked in by vboxsync, 9 years ago

USBProxyBackendLinux: Hopefully fix crashes introduced with last commit

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