VirtualBox

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

Last change on this file since 68033 was 65854, checked in by vboxsync, 8 years ago

Main/USBProxyBackendUsbIp: Don't prevent VBoxSVC from starting when the server is not reachable, just try to reconnect every 3 seconds. Smaller other fixes for stale USB device lists when the server is not reachable and a fix for saving the same USB source multiple times

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.2 KB
Line 
1/* $Id: USBProxyBackendLinux.cpp 65854 2017-02-23 11:48:49Z vboxsync $ */
2/** @file
3 * VirtualBox USB Proxy Service, Linux Specialization.
4 */
5
6/*
7 * Copyright (C) 2005-2016 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 */
62USBProxyBackendLinux::USBProxyBackendLinux()
63 : USBProxyBackend(), mhFile(NIL_RTFILE), 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,
83 const com::Utf8Str &strAddress, bool fLoadingSettings)
84{
85 USBProxyBackend::init(pUsbProxyService, strId, strAddress, fLoadingSettings);
86
87 unconst(m_strBackend) = Utf8Str("host");
88
89 const char *pcszDevicesRoot;
90 int rc = USBProxyLinuxChooseMethod(&mUsingUsbfsDevices, &pcszDevicesRoot);
91 if (RT_SUCCESS(rc))
92 {
93 mDevicesRoot = pcszDevicesRoot;
94 rc = mUsingUsbfsDevices ? initUsbfs() : initSysfs();
95 /* For the day when we have VBoxSVC release logging... */
96 LogRel((RT_SUCCESS(rc) ? "Successfully initialised host USB using %s\n"
97 : "Failed to initialise host USB using %s\n",
98 mUsingUsbfsDevices ? "USBFS" : "sysfs"));
99 }
100
101 return rc;
102}
103
104void USBProxyBackendLinux::uninit()
105{
106 /*
107 * Stop the service.
108 */
109 if (isActive())
110 stop();
111
112 /*
113 * Free resources.
114 */
115 doUsbfsCleanupAsNeeded();
116#ifdef VBOX_USB_WITH_SYSFS
117 if (mpWaiter)
118 delete mpWaiter;
119#endif
120
121 USBProxyBackend::uninit();
122}
123
124
125/**
126 * Initialization routine for the usbfs based operation.
127 *
128 * @returns iprt status code.
129 */
130int USBProxyBackendLinux::initUsbfs(void)
131{
132 Assert(mUsingUsbfsDevices);
133
134 /*
135 * Open the devices file.
136 */
137 int rc;
138 char *pszDevices = RTPathJoinA(mDevicesRoot.c_str(), "devices");
139 if (pszDevices)
140 {
141 rc = RTFileOpen(&mhFile, pszDevices, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
142 if (RT_SUCCESS(rc))
143 {
144 rc = RTPipeCreate(&mhWakeupPipeR, &mhWakeupPipeW, 0 /*fFlags*/);
145 if (RT_SUCCESS(rc))
146 {
147 /*
148 * Start the poller thread.
149 */
150 rc = start();
151 if (RT_SUCCESS(rc))
152 {
153 RTStrFree(pszDevices);
154 LogFlowThisFunc(("returns successfully\n"));
155 return VINF_SUCCESS;
156 }
157
158 RTPipeClose(mhWakeupPipeR);
159 RTPipeClose(mhWakeupPipeW);
160 mhWakeupPipeW = mhWakeupPipeR = NIL_RTPIPE;
161 }
162 else
163 Log(("USBProxyBackendLinux::USBProxyBackendLinux: RTFilePipe failed with rc=%Rrc\n", rc));
164 RTFileClose(mhFile);
165 }
166
167 RTStrFree(pszDevices);
168 }
169 else
170 {
171 rc = VERR_NO_MEMORY;
172 Log(("USBProxyBackendLinux::USBProxyBackendLinux: out of memory!\n"));
173 }
174
175 LogFlowThisFunc(("returns failure!!! (rc=%Rrc)\n", rc));
176 return rc;
177}
178
179
180/**
181 * Initialization routine for the sysfs based operation.
182 *
183 * @returns iprt status code
184 */
185int USBProxyBackendLinux::initSysfs(void)
186{
187 Assert(!mUsingUsbfsDevices);
188
189#ifdef VBOX_USB_WITH_SYSFS
190 try
191 {
192 mpWaiter = new VBoxMainHotplugWaiter(mDevicesRoot.c_str());
193 }
194 catch(std::bad_alloc &e)
195 {
196 return VERR_NO_MEMORY;
197 }
198 int rc = mpWaiter->getStatus();
199 if (RT_SUCCESS(rc) || rc == VERR_TIMEOUT || rc == VERR_TRY_AGAIN)
200 rc = start();
201 else if (rc == VERR_NOT_SUPPORTED)
202 /* This can legitimately happen if hal or DBus are not running, but of
203 * course we can't start in this case. */
204 rc = VINF_SUCCESS;
205 return rc;
206
207#else /* !VBOX_USB_WITH_SYSFS */
208 return VERR_NOT_IMPLEMENTED;
209#endif /* !VBOX_USB_WITH_SYSFS */
210}
211
212
213/**
214 * If any Usbfs-related resources are currently allocated, then free them
215 * and mark them as freed.
216 */
217void USBProxyBackendLinux::doUsbfsCleanupAsNeeded()
218{
219 /*
220 * Free resources.
221 */
222 if (mhFile != NIL_RTFILE)
223 RTFileClose(mhFile);
224 mhFile = NIL_RTFILE;
225
226 if (mhWakeupPipeR != NIL_RTPIPE)
227 RTPipeClose(mhWakeupPipeR);
228 if (mhWakeupPipeW != NIL_RTPIPE)
229 RTPipeClose(mhWakeupPipeW);
230 mhWakeupPipeW = mhWakeupPipeR = NIL_RTPIPE;
231}
232
233
234int USBProxyBackendLinux::captureDevice(HostUSBDevice *aDevice)
235{
236 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
237 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
238
239 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
240 LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));
241
242 /*
243 * Don't think we need to do anything when the device is held... fake it.
244 */
245 Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_Capturing);
246 devLock.release();
247 interruptWait();
248
249 return VINF_SUCCESS;
250}
251
252
253int USBProxyBackendLinux::releaseDevice(HostUSBDevice *aDevice)
254{
255 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
256 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
257
258 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
259 LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));
260
261 /*
262 * We're not really holding it atm., just fake it.
263 */
264 Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_ReleasingToHost);
265 devLock.release();
266 interruptWait();
267
268 return VINF_SUCCESS;
269}
270
271
272/**
273 * A device was added, we need to adjust mUdevPolls.
274 */
275void USBProxyBackendLinux::deviceAdded(ComObjPtr<HostUSBDevice> &aDevice, PUSBDEVICE pDev)
276{
277 AssertReturnVoid(aDevice);
278 AssertReturnVoid(!aDevice->isWriteLockOnCurrentThread());
279 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
280 if (pDev->enmState == USBDEVICESTATE_USED_BY_HOST)
281 {
282 LogRel(("USBProxyBackendLinux: Device %04x:%04x (%s) isn't accessible. giving udev a few seconds to fix this...\n",
283 pDev->idVendor, pDev->idProduct, pDev->pszAddress));
284 mUdevPolls = 10; /* (10 * 500ms = 5s) */
285 }
286
287 devLock.release();
288}
289
290
291bool USBProxyBackendLinux::isFakeUpdateRequired()
292{
293 return true;
294}
295
296int USBProxyBackendLinux::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 USBProxyBackendLinux::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 RT_ZERO(PollFds);
324 PollFds[0].fd = (int)RTFileToNative(mhFile);
325 PollFds[0].events = POLLIN;
326 PollFds[1].fd = (int)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 USBProxyBackendLinux::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 USBProxyBackendLinux::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 USBProxyBackendLinux::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