VirtualBox

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

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

Main/linux/USB: better permissions checking and code cleanup

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