VirtualBox

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

Last change on this file was 98288, checked in by vboxsync, 16 months ago

Main/src-server: rc -> hrc/vrc (partial). bugref:10223

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.6 KB
Line 
1/* $Id: USBProxyBackendLinux.cpp 98288 2023-01-24 15:32:43Z vboxsync $ */
2/** @file
3 * VirtualBox USB Proxy Service, Linux Specialization.
4 */
5
6/*
7 * Copyright (C) 2005-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_MAIN_USBPROXYBACKEND
33#include "USBProxyService.h"
34#include "USBGetDevices.h"
35#include "LoggingNew.h"
36
37#include <VBox/usb.h>
38#include <VBox/usblib.h>
39#include <iprt/errcore.h>
40
41#include <iprt/string.h>
42#include <iprt/alloc.h>
43#include <iprt/assert.h>
44#include <iprt/ctype.h>
45#include <iprt/dir.h>
46#include <iprt/env.h>
47#include <iprt/file.h>
48#include <iprt/errcore.h>
49#include <iprt/mem.h>
50#include <iprt/param.h>
51#include <iprt/path.h>
52#include <iprt/pipe.h>
53#include <iprt/stream.h>
54#include <iprt/linux/sysfs.h>
55
56#include <stdlib.h>
57#include <string.h>
58#include <stdio.h>
59#include <errno.h>
60#include <fcntl.h>
61#include <unistd.h>
62#include <sys/statfs.h>
63#include <sys/poll.h>
64#ifdef VBOX_WITH_LINUX_COMPILER_H
65# include <linux/compiler.h>
66#endif
67#include <linux/usbdevice_fs.h>
68
69
70/**
71 * Initialize data members.
72 */
73USBProxyBackendLinux::USBProxyBackendLinux()
74 : USBProxyBackend(), mhFile(NIL_RTFILE), mhWakeupPipeR(NIL_RTPIPE), mhWakeupPipeW(NIL_RTPIPE), mpWaiter(NULL)
75{
76 LogFlowThisFunc(("\n"));
77}
78
79
80/**
81 * Stop all service threads and free the device chain.
82 */
83USBProxyBackendLinux::~USBProxyBackendLinux()
84{
85 LogFlowThisFunc(("\n"));
86}
87
88/**
89 * Initializes the object (called right after construction).
90 *
91 * @returns VBox status code.
92 */
93int USBProxyBackendLinux::init(USBProxyService *pUsbProxyService, const com::Utf8Str &strId,
94 const com::Utf8Str &strAddress, bool fLoadingSettings)
95{
96 USBProxyBackend::init(pUsbProxyService, strId, strAddress, fLoadingSettings);
97
98 unconst(m_strBackend) = Utf8Str("host");
99
100 const char *pcszDevicesRoot;
101 int vrc = USBProxyLinuxChooseMethod(&mUsingUsbfsDevices, &pcszDevicesRoot);
102 if (RT_SUCCESS(vrc))
103 {
104 mDevicesRoot = pcszDevicesRoot;
105 vrc = mUsingUsbfsDevices ? initUsbfs() : initSysfs();
106 /* For the day when we have VBoxSVC release logging... */
107 LogRel((RT_SUCCESS(vrc) ? "Successfully initialised host USB using %s\n"
108 : "Failed to initialise host USB using %s\n",
109 mUsingUsbfsDevices ? "USBFS" : "sysfs"));
110 }
111
112 return vrc;
113}
114
115void USBProxyBackendLinux::uninit()
116{
117 /*
118 * Stop the service.
119 */
120 if (isActive())
121 stop();
122
123 /*
124 * Free resources.
125 */
126 doUsbfsCleanupAsNeeded();
127#ifdef VBOX_USB_WITH_SYSFS
128 if (mpWaiter)
129 delete mpWaiter;
130#endif
131
132 USBProxyBackend::uninit();
133}
134
135
136/**
137 * Initialization routine for the usbfs based operation.
138 *
139 * @returns iprt status code.
140 */
141int USBProxyBackendLinux::initUsbfs(void)
142{
143 Assert(mUsingUsbfsDevices);
144
145 /*
146 * Open the devices file.
147 */
148 int vrc;
149 char *pszDevices = RTPathJoinA(mDevicesRoot.c_str(), "devices");
150 if (pszDevices)
151 {
152 vrc = RTFileOpen(&mhFile, pszDevices, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
153 if (RT_SUCCESS(vrc))
154 {
155 vrc = RTPipeCreate(&mhWakeupPipeR, &mhWakeupPipeW, 0 /*fFlags*/);
156 if (RT_SUCCESS(vrc))
157 {
158 /*
159 * Start the poller thread.
160 */
161 vrc = start();
162 if (RT_SUCCESS(vrc))
163 {
164 RTStrFree(pszDevices);
165 LogFlowThisFunc(("returns successfully\n"));
166 return VINF_SUCCESS;
167 }
168
169 RTPipeClose(mhWakeupPipeR);
170 RTPipeClose(mhWakeupPipeW);
171 mhWakeupPipeW = mhWakeupPipeR = NIL_RTPIPE;
172 }
173 else
174 Log(("USBProxyBackendLinux::USBProxyBackendLinux: RTFilePipe failed with vrc=%Rrc\n", vrc));
175 RTFileClose(mhFile);
176 }
177
178 RTStrFree(pszDevices);
179 }
180 else
181 {
182 vrc = VERR_NO_MEMORY;
183 Log(("USBProxyBackendLinux::USBProxyBackendLinux: out of memory!\n"));
184 }
185
186 LogFlowThisFunc(("returns failure!!! (vrc=%Rrc)\n", vrc));
187 return vrc;
188}
189
190
191/**
192 * Initialization routine for the sysfs based operation.
193 *
194 * @returns iprt status code
195 */
196int USBProxyBackendLinux::initSysfs(void)
197{
198 Assert(!mUsingUsbfsDevices);
199
200#ifdef VBOX_USB_WITH_SYSFS
201 try
202 {
203 mpWaiter = new VBoxMainHotplugWaiter(mDevicesRoot.c_str());
204 }
205 catch(std::bad_alloc &e)
206 {
207 return VERR_NO_MEMORY;
208 }
209 int vrc = mpWaiter->getStatus();
210 if (RT_SUCCESS(vrc) || vrc == VERR_TIMEOUT || vrc == VERR_TRY_AGAIN)
211 vrc = start();
212 else if (vrc == VERR_NOT_SUPPORTED)
213 /* This can legitimately happen if hal or DBus are not running, but of
214 * course we can't start in this case. */
215 vrc = VINF_SUCCESS;
216 return vrc;
217
218#else /* !VBOX_USB_WITH_SYSFS */
219 return VERR_NOT_IMPLEMENTED;
220#endif /* !VBOX_USB_WITH_SYSFS */
221}
222
223
224/**
225 * If any Usbfs-related resources are currently allocated, then free them
226 * and mark them as freed.
227 */
228void USBProxyBackendLinux::doUsbfsCleanupAsNeeded()
229{
230 /*
231 * Free resources.
232 */
233 if (mhFile != NIL_RTFILE)
234 RTFileClose(mhFile);
235 mhFile = NIL_RTFILE;
236
237 if (mhWakeupPipeR != NIL_RTPIPE)
238 RTPipeClose(mhWakeupPipeR);
239 if (mhWakeupPipeW != NIL_RTPIPE)
240 RTPipeClose(mhWakeupPipeW);
241 mhWakeupPipeW = mhWakeupPipeR = NIL_RTPIPE;
242}
243
244
245int USBProxyBackendLinux::captureDevice(HostUSBDevice *aDevice)
246{
247 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
248 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
249
250 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
251 LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));
252
253 /*
254 * Don't think we need to do anything when the device is held... fake it.
255 */
256 Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_Capturing);
257 devLock.release();
258 interruptWait();
259
260 return VINF_SUCCESS;
261}
262
263
264int USBProxyBackendLinux::releaseDevice(HostUSBDevice *aDevice)
265{
266 AssertReturn(aDevice, VERR_GENERAL_FAILURE);
267 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
268
269 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
270 LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));
271
272 /*
273 * We're not really holding it atm., just fake it.
274 */
275 Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_ReleasingToHost);
276 devLock.release();
277 interruptWait();
278
279 return VINF_SUCCESS;
280}
281
282
283/**
284 * A device was added, we need to adjust mUdevPolls.
285 */
286void USBProxyBackendLinux::deviceAdded(ComObjPtr<HostUSBDevice> &aDevice, PUSBDEVICE pDev)
287{
288 AssertReturnVoid(aDevice);
289 AssertReturnVoid(!aDevice->isWriteLockOnCurrentThread());
290 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
291 if (pDev->enmState == USBDEVICESTATE_USED_BY_HOST)
292 {
293 LogRel(("USBProxyBackendLinux: Device %04x:%04x (%s) isn't accessible. giving udev a few seconds to fix this...\n",
294 pDev->idVendor, pDev->idProduct, pDev->pszAddress));
295 mUdevPolls = 10; /* (10 * 500ms = 5s) */
296 }
297
298 devLock.release();
299}
300
301
302bool USBProxyBackendLinux::isFakeUpdateRequired()
303{
304 return true;
305}
306
307int USBProxyBackendLinux::wait(RTMSINTERVAL aMillies)
308{
309 int vrc;
310 if (mUsingUsbfsDevices)
311 vrc = waitUsbfs(aMillies);
312 else
313 vrc = waitSysfs(aMillies);
314 return vrc;
315}
316
317
318/** String written to the wakeup pipe. */
319#define WAKE_UP_STRING "WakeUp!"
320/** Length of the string written. */
321#define WAKE_UP_STRING_LEN ( sizeof(WAKE_UP_STRING) - 1 )
322
323int USBProxyBackendLinux::waitUsbfs(RTMSINTERVAL aMillies)
324{
325 struct pollfd PollFds[2];
326
327 /* Cap the wait interval if we're polling for udevd changing device permissions. */
328 if (aMillies > 500 && mUdevPolls > 0)
329 {
330 mUdevPolls--;
331 aMillies = 500;
332 }
333
334 RT_ZERO(PollFds);
335 PollFds[0].fd = (int)RTFileToNative(mhFile);
336 PollFds[0].events = POLLIN;
337 PollFds[1].fd = (int)RTPipeToNative(mhWakeupPipeR);
338 PollFds[1].events = POLLIN | POLLERR | POLLHUP;
339
340 int iRc = poll(&PollFds[0], 2, aMillies);
341 if (iRc == 0)
342 return VERR_TIMEOUT;
343 if (iRc > 0)
344 {
345 /* drain the pipe */
346 if (PollFds[1].revents & POLLIN)
347 {
348 char szBuf[WAKE_UP_STRING_LEN];
349 int vrc2 = RTPipeReadBlocking(mhWakeupPipeR, szBuf, sizeof(szBuf), NULL);
350 AssertRC(vrc2);
351 }
352 return VINF_SUCCESS;
353 }
354 return RTErrConvertFromErrno(errno);
355}
356
357
358int USBProxyBackendLinux::waitSysfs(RTMSINTERVAL aMillies)
359{
360#ifdef VBOX_USB_WITH_SYSFS
361 int vrc = mpWaiter->Wait(aMillies);
362 if (vrc == VERR_TRY_AGAIN)
363 {
364 RTThreadYield();
365 vrc = VINF_SUCCESS;
366 }
367 return vrc;
368#else /* !VBOX_USB_WITH_SYSFS */
369 return USBProxyService::wait(aMillies);
370#endif /* !VBOX_USB_WITH_SYSFS */
371}
372
373
374int USBProxyBackendLinux::interruptWait(void)
375{
376 AssertReturn(!isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
377
378 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
379#ifdef VBOX_USB_WITH_SYSFS
380 LogFlowFunc(("mUsingUsbfsDevices=%d\n", mUsingUsbfsDevices));
381 if (!mUsingUsbfsDevices)
382 {
383 mpWaiter->Interrupt();
384 LogFlowFunc(("Returning VINF_SUCCESS\n"));
385 return VINF_SUCCESS;
386 }
387#endif /* VBOX_USB_WITH_SYSFS */
388 int vrc = RTPipeWriteBlocking(mhWakeupPipeW, WAKE_UP_STRING, WAKE_UP_STRING_LEN, NULL);
389 if (RT_SUCCESS(vrc))
390 RTPipeFlush(mhWakeupPipeW);
391 LogFlowFunc(("returning %Rrc\n", vrc));
392 return vrc;
393}
394
395
396PUSBDEVICE USBProxyBackendLinux::getDevices(void)
397{
398 return USBProxyLinuxGetDevices(mDevicesRoot.c_str(), !mUsingUsbfsDevices);
399}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use