VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/USBProxyService.cpp@ 94134

Last change on this file since 94134 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.0 KB
Line 
1/* $Id: USBProxyService.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * VirtualBox USB Proxy Service (base) class.
4 */
5
6/*
7 * Copyright (C) 2006-2022 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#define LOG_GROUP LOG_GROUP_MAIN_USBPROXYBACKEND
19#include "USBProxyService.h"
20#include "HostUSBDeviceImpl.h"
21#include "HostImpl.h"
22#include "MachineImpl.h"
23#include "VirtualBoxImpl.h"
24
25#include "AutoCaller.h"
26#include "LoggingNew.h"
27
28#include <VBox/com/array.h>
29#include <iprt/errcore.h>
30#include <iprt/asm.h>
31#include <iprt/semaphore.h>
32#include <iprt/thread.h>
33#include <iprt/mem.h>
34#include <iprt/string.h>
35
36/** Pair of a USB proxy backend and the opaque filter data assigned by the backend. */
37typedef std::pair<ComObjPtr<USBProxyBackend> , void *> USBFilterPair;
38/** List of USB filter pairs. */
39typedef std::list<USBFilterPair> USBFilterList;
40
41/**
42 * Data for a USB device filter.
43 */
44struct USBFilterData
45{
46 USBFilterData()
47 : llUsbFilters()
48 { }
49
50 USBFilterList llUsbFilters;
51};
52
53/**
54 * Initialize data members.
55 */
56USBProxyService::USBProxyService(Host *aHost)
57 : mHost(aHost), mDevices(), mBackends()
58{
59 LogFlowThisFunc(("aHost=%p\n", aHost));
60}
61
62
63/**
64 * Stub needed as long as the class isn't virtual
65 */
66HRESULT USBProxyService::init(void)
67{
68# if defined(RT_OS_DARWIN)
69 ComObjPtr<USBProxyBackendDarwin> UsbProxyBackendHost;
70# elif defined(RT_OS_LINUX)
71 ComObjPtr<USBProxyBackendLinux> UsbProxyBackendHost;
72# elif defined(RT_OS_OS2)
73 ComObjPtr<USBProxyBackendOs2> UsbProxyBackendHost;
74# elif defined(RT_OS_SOLARIS)
75 ComObjPtr<USBProxyBackendSolaris> UsbProxyBackendHost;
76# elif defined(RT_OS_WINDOWS)
77 ComObjPtr<USBProxyBackendWindows> UsbProxyBackendHost;
78# elif defined(RT_OS_FREEBSD)
79 ComObjPtr<USBProxyBackendFreeBSD> UsbProxyBackendHost;
80# else
81 ComObjPtr<USBProxyBackend> UsbProxyBackendHost;
82# endif
83 UsbProxyBackendHost.createObject();
84 int vrc = UsbProxyBackendHost->init(this, Utf8Str("host"), Utf8Str(""), false /* fLoadingSettings */);
85 if (RT_FAILURE(vrc))
86 {
87 mLastError = vrc;
88 }
89 else
90 mBackends.push_back(static_cast<ComObjPtr<USBProxyBackend> >(UsbProxyBackendHost));
91
92 return S_OK;
93}
94
95
96/**
97 * Empty destructor.
98 */
99USBProxyService::~USBProxyService()
100{
101 LogFlowThisFunc(("\n"));
102 while (!mBackends.empty())
103 mBackends.pop_front();
104
105 mDevices.clear();
106 mBackends.clear();
107 mHost = NULL;
108}
109
110
111/**
112 * Query if the service is active and working.
113 *
114 * @returns true if the service is up running.
115 * @returns false if the service isn't running.
116 */
117bool USBProxyService::isActive(void)
118{
119 return mBackends.size() > 0;
120}
121
122
123/**
124 * Get last error.
125 * Can be used to check why the proxy !isActive() upon construction.
126 *
127 * @returns VBox status code.
128 */
129int USBProxyService::getLastError(void)
130{
131 return mLastError;
132}
133
134
135/**
136 * We're using the Host object lock.
137 *
138 * This is just a temporary measure until all the USB refactoring is
139 * done, probably... For now it help avoiding deadlocks we don't have
140 * time to fix.
141 *
142 * @returns Lock handle.
143 */
144RWLockHandle *USBProxyService::lockHandle() const
145{
146 return mHost->lockHandle();
147}
148
149
150void *USBProxyService::insertFilter(PCUSBFILTER aFilter)
151{
152 USBFilterData *pFilterData = new USBFilterData();
153
154 for (USBProxyBackendList::iterator it = mBackends.begin();
155 it != mBackends.end();
156 ++it)
157 {
158 ComObjPtr<USBProxyBackend> pUsbProxyBackend = *it;
159 void *pvId = pUsbProxyBackend->insertFilter(aFilter);
160
161 pFilterData->llUsbFilters.push_back(USBFilterPair(pUsbProxyBackend, pvId));
162 }
163
164 return pFilterData;
165}
166
167void USBProxyService::removeFilter(void *aId)
168{
169 USBFilterData *pFilterData = (USBFilterData *)aId;
170
171 for (USBFilterList::iterator it = pFilterData->llUsbFilters.begin();
172 it != pFilterData->llUsbFilters.end();
173 ++it)
174 {
175 ComObjPtr<USBProxyBackend> pUsbProxyBackend = it->first;
176 pUsbProxyBackend->removeFilter(it->second);
177 }
178
179 pFilterData->llUsbFilters.clear();
180 delete pFilterData;
181}
182
183/**
184 * Gets the collection of USB devices, slave of Host::USBDevices.
185 *
186 * This is an interface for the HostImpl::USBDevices property getter.
187 *
188 *
189 * @param aUSBDevices Where to store the pointer to the collection.
190 *
191 * @returns COM status code.
192 *
193 * @remarks The caller must own the write lock of the host object.
194 */
195HRESULT USBProxyService::getDeviceCollection(std::vector<ComPtr<IHostUSBDevice> > &aUSBDevices)
196{
197 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
198
199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
200
201 aUSBDevices.resize(mDevices.size());
202 size_t i = 0;
203 for (HostUSBDeviceList::const_iterator it = mDevices.begin(); it != mDevices.end(); ++it, ++i)
204 aUSBDevices[i] = *it;
205
206 return S_OK;
207}
208
209
210HRESULT USBProxyService::addUSBDeviceSource(const com::Utf8Str &aBackend, const com::Utf8Str &aId, const com::Utf8Str &aAddress,
211 const std::vector<com::Utf8Str> &aPropertyNames, const std::vector<com::Utf8Str> &aPropertyValues)
212{
213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
214
215 HRESULT hrc = createUSBDeviceSource(aBackend, aId, aAddress, aPropertyNames,
216 aPropertyValues, false /* fLoadingSettings */);
217 if (SUCCEEDED(hrc))
218 {
219 alock.release();
220 AutoWriteLock vboxLock(mHost->i_parent() COMMA_LOCKVAL_SRC_POS);
221 return mHost->i_parent()->i_saveSettings();
222 }
223
224 return hrc;
225}
226
227HRESULT USBProxyService::removeUSBDeviceSource(const com::Utf8Str &aId)
228{
229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
230
231 for (USBProxyBackendList::iterator it = mBackends.begin();
232 it != mBackends.end();
233 ++it)
234 {
235 ComObjPtr<USBProxyBackend> UsbProxyBackend = *it;
236
237 if (aId.equals(UsbProxyBackend->i_getId()))
238 {
239 mBackends.erase(it);
240
241 /*
242 * The proxy backend uninit method will be called when the pointer goes
243 * out of scope.
244 */
245
246 alock.release();
247 AutoWriteLock vboxLock(mHost->i_parent() COMMA_LOCKVAL_SRC_POS);
248 return mHost->i_parent()->i_saveSettings();
249 }
250 }
251
252 return setError(VBOX_E_OBJECT_NOT_FOUND,
253 tr("The USB device source \"%s\" could not be found"), aId.c_str());
254}
255
256/**
257 * Request capture of a specific device.
258 *
259 * This is in an interface for SessionMachine::CaptureUSBDevice(), which is
260 * an internal worker used by Console::AttachUSBDevice() from the VM process.
261 *
262 * When the request is completed, SessionMachine::onUSBDeviceAttach() will
263 * be called for the given machine object.
264 *
265 *
266 * @param aMachine The machine to attach the device to.
267 * @param aId The UUID of the USB device to capture and attach.
268 * @param aCaptureFilename
269 *
270 * @returns COM status code and error info.
271 *
272 * @remarks This method may operate synchronously as well as asynchronously. In the
273 * former case it will temporarily abandon locks because of IPC.
274 */
275HRESULT USBProxyService::captureDeviceForVM(SessionMachine *aMachine, IN_GUID aId, const com::Utf8Str &aCaptureFilename)
276{
277 ComAssertRet(aMachine, E_INVALIDARG);
278 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
279
280 /*
281 * Translate the device id into a device object.
282 */
283 ComObjPtr<HostUSBDevice> pHostDevice = findDeviceById(aId);
284 if (pHostDevice.isNull())
285 return setError(E_INVALIDARG,
286 tr("The USB device with UUID {%RTuuid} is not currently attached to the host"), Guid(aId).raw());
287
288 /*
289 * Try to capture the device
290 */
291 alock.release();
292 return pHostDevice->i_requestCaptureForVM(aMachine, true /* aSetError */, aCaptureFilename);
293}
294
295
296/**
297 * Notification from VM process about USB device detaching progress.
298 *
299 * This is in an interface for SessionMachine::DetachUSBDevice(), which is
300 * an internal worker used by Console::DetachUSBDevice() from the VM process.
301 *
302 * @param aMachine The machine which is sending the notification.
303 * @param aId The UUID of the USB device is concerns.
304 * @param aDone \a false for the pre-action notification (necessary
305 * for advancing the device state to avoid confusing
306 * the guest).
307 * \a true for the post-action notification. The device
308 * will be subjected to all filters except those of
309 * of \a Machine.
310 *
311 * @returns COM status code.
312 *
313 * @remarks When \a aDone is \a true this method may end up doing IPC to other
314 * VMs when running filters. In these cases it will temporarily
315 * abandon its locks.
316 */
317HRESULT USBProxyService::detachDeviceFromVM(SessionMachine *aMachine, IN_GUID aId, bool aDone)
318{
319 LogFlowThisFunc(("aMachine=%p{%s} aId={%RTuuid} aDone=%RTbool\n",
320 aMachine,
321 aMachine->i_getName().c_str(),
322 Guid(aId).raw(),
323 aDone));
324
325 // get a list of all running machines while we're outside the lock
326 // (getOpenedMachines requests locks which are incompatible with the lock of the machines list)
327 SessionMachinesList llOpenedMachines;
328 mHost->i_parent()->i_getOpenedMachines(llOpenedMachines);
329
330 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
331
332 ComObjPtr<HostUSBDevice> pHostDevice = findDeviceById(aId);
333 ComAssertRet(!pHostDevice.isNull(), E_FAIL);
334 AutoWriteLock devLock(pHostDevice COMMA_LOCKVAL_SRC_POS);
335
336 /*
337 * Work the state machine.
338 */
339 LogFlowThisFunc(("id={%RTuuid} state=%s aDone=%RTbool name={%s}\n",
340 pHostDevice->i_getId().raw(), pHostDevice->i_getStateName(), aDone, pHostDevice->i_getName().c_str()));
341 bool fRunFilters = false;
342 HRESULT hrc = pHostDevice->i_onDetachFromVM(aMachine, aDone, &fRunFilters);
343
344 /*
345 * Run filters if necessary.
346 */
347 if ( SUCCEEDED(hrc)
348 && fRunFilters)
349 {
350 Assert(aDone && pHostDevice->i_getUnistate() == kHostUSBDeviceState_HeldByProxy && pHostDevice->i_getMachine().isNull());
351 devLock.release();
352 alock.release();
353 HRESULT hrc2 = runAllFiltersOnDevice(pHostDevice, llOpenedMachines, aMachine);
354 ComAssertComRC(hrc2);
355 }
356 return hrc;
357}
358
359
360/**
361 * Apply filters for the machine to all eligible USB devices.
362 *
363 * This is in an interface for SessionMachine::CaptureUSBDevice(), which
364 * is an internal worker used by Console::AutoCaptureUSBDevices() from the
365 * VM process at VM startup.
366 *
367 * Matching devices will be attached to the VM and may result IPC back
368 * to the VM process via SessionMachine::onUSBDeviceAttach() depending
369 * on whether the device needs to be captured or not. If capture is
370 * required, SessionMachine::onUSBDeviceAttach() will be called
371 * asynchronously by the USB proxy service thread.
372 *
373 * @param aMachine The machine to capture devices for.
374 *
375 * @returns COM status code, perhaps with error info.
376 *
377 * @remarks Temporarily locks this object, the machine object and some USB
378 * device, and the called methods will lock similar objects.
379 */
380HRESULT USBProxyService::autoCaptureDevicesForVM(SessionMachine *aMachine)
381{
382 LogFlowThisFunc(("aMachine=%p{%s}\n",
383 aMachine,
384 aMachine->i_getName().c_str()));
385
386 /*
387 * Make a copy of the list because we cannot hold the lock protecting it.
388 * (This will not make copies of any HostUSBDevice objects, only reference them.)
389 */
390 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
391 HostUSBDeviceList ListCopy = mDevices;
392 alock.release();
393
394 for (HostUSBDeviceList::iterator it = ListCopy.begin();
395 it != ListCopy.end();
396 ++it)
397 {
398 ComObjPtr<HostUSBDevice> pHostDevice = *it;
399 AutoReadLock devLock(pHostDevice COMMA_LOCKVAL_SRC_POS);
400 if ( pHostDevice->i_getUnistate() == kHostUSBDeviceState_HeldByProxy
401 || pHostDevice->i_getUnistate() == kHostUSBDeviceState_Unused
402 || pHostDevice->i_getUnistate() == kHostUSBDeviceState_Capturable)
403 {
404 devLock.release();
405 runMachineFilters(aMachine, pHostDevice);
406 }
407 }
408
409 return S_OK;
410}
411
412
413/**
414 * Detach all USB devices currently attached to a VM.
415 *
416 * This is in an interface for SessionMachine::DetachAllUSBDevices(), which
417 * is an internal worker used by Console::powerDown() from the VM process
418 * at VM startup, and SessionMachine::uninit() at VM abend.
419 *
420 * This is, like #detachDeviceFromVM(), normally a two stage journey
421 * where \a aDone indicates where we are. In addition we may be called
422 * to clean up VMs that have abended, in which case there will be no
423 * preparatory call. Filters will be applied to the devices in the final
424 * call with the risk that we have to do some IPC when attaching them
425 * to other VMs.
426 *
427 * @param aMachine The machine to detach devices from.
428 * @param aDone
429 * @param aAbnormal
430 *
431 * @returns COM status code, perhaps with error info.
432 *
433 * @remarks Write locks the host object and may temporarily abandon
434 * its locks to perform IPC.
435 */
436HRESULT USBProxyService::detachAllDevicesFromVM(SessionMachine *aMachine, bool aDone, bool aAbnormal)
437{
438 // get a list of all running machines while we're outside the lock
439 // (getOpenedMachines requests locks which are incompatible with the host object lock)
440 SessionMachinesList llOpenedMachines;
441 mHost->i_parent()->i_getOpenedMachines(llOpenedMachines);
442
443 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
444
445 /*
446 * Make a copy of the device list (not the HostUSBDevice objects, just
447 * the list) since we may end up performing IPC and temporarily have
448 * to abandon locks when applying filters.
449 */
450 HostUSBDeviceList ListCopy = mDevices;
451
452 for (HostUSBDeviceList::iterator it = ListCopy.begin();
453 it != ListCopy.end();
454 ++it)
455 {
456 ComObjPtr<HostUSBDevice> pHostDevice = *it;
457 AutoWriteLock devLock(pHostDevice COMMA_LOCKVAL_SRC_POS);
458 if (pHostDevice->i_getMachine() == aMachine)
459 {
460 /*
461 * Same procedure as in detachUSBDevice().
462 */
463 bool fRunFilters = false;
464 HRESULT hrc = pHostDevice->i_onDetachFromVM(aMachine, aDone, &fRunFilters, aAbnormal);
465 if ( SUCCEEDED(hrc)
466 && fRunFilters)
467 {
468 Assert( aDone
469 && pHostDevice->i_getUnistate() == kHostUSBDeviceState_HeldByProxy
470 && pHostDevice->i_getMachine().isNull());
471 devLock.release();
472 alock.release();
473 HRESULT hrc2 = runAllFiltersOnDevice(pHostDevice, llOpenedMachines, aMachine);
474 ComAssertComRC(hrc2);
475 alock.acquire();
476 }
477 }
478 }
479
480 return S_OK;
481}
482
483
484// Internals
485/////////////////////////////////////////////////////////////////////////////
486
487
488/**
489 * Loads the given settings and constructs the additional USB device sources.
490 *
491 * @returns COM status code.
492 * @param llUSBDeviceSources The list of additional device sources.
493 */
494HRESULT USBProxyService::i_loadSettings(const settings::USBDeviceSourcesList &llUSBDeviceSources)
495{
496 HRESULT hrc = S_OK;
497
498 for (settings::USBDeviceSourcesList::const_iterator it = llUSBDeviceSources.begin();
499 it != llUSBDeviceSources.end() && SUCCEEDED(hrc);
500 ++it)
501 {
502 std::vector<com::Utf8Str> vecPropNames, vecPropValues;
503 const settings::USBDeviceSource &src = *it;
504 hrc = createUSBDeviceSource(src.strBackend, src.strName, src.strAddress,
505 vecPropNames, vecPropValues, true /* fLoadingSettings */);
506 }
507
508 return hrc;
509}
510
511/**
512 * Saves the additional device sources in the given settings.
513 *
514 * @returns COM status code.
515 * @param llUSBDeviceSources The list of additional device sources.
516 */
517HRESULT USBProxyService::i_saveSettings(settings::USBDeviceSourcesList &llUSBDeviceSources)
518{
519 for (USBProxyBackendList::iterator it = mBackends.begin();
520 it != mBackends.end();
521 ++it)
522 {
523 USBProxyBackend *pUsbProxyBackend = *it;
524
525 /* Host backends are not saved as they are always created during startup. */
526 if (!pUsbProxyBackend->i_getBackend().equals("host"))
527 {
528 settings::USBDeviceSource src;
529
530 src.strBackend = pUsbProxyBackend->i_getBackend();
531 src.strName = pUsbProxyBackend->i_getId();
532 src.strAddress = pUsbProxyBackend->i_getAddress();
533
534 llUSBDeviceSources.push_back(src);
535 }
536 }
537
538 return S_OK;
539}
540
541/**
542 * Performs the required actions when a device has been added.
543 *
544 * This means things like running filters and subsequent capturing and
545 * VM attaching. This may result in IPC and temporary lock abandonment.
546 *
547 * @param aDevice The device in question.
548 * @param pDev The USB device structure.
549 */
550void USBProxyService::i_deviceAdded(ComObjPtr<HostUSBDevice> &aDevice,
551 PUSBDEVICE pDev)
552{
553 /*
554 * Validate preconditions.
555 */
556 AssertReturnVoid(!isWriteLockOnCurrentThread());
557 AssertReturnVoid(!aDevice->isWriteLockOnCurrentThread());
558 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
559 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
560 LogFlowThisFunc(("aDevice=%p name={%s} state=%s id={%RTuuid}\n",
561 (HostUSBDevice *)aDevice,
562 aDevice->i_getName().c_str(),
563 aDevice->i_getStateName(),
564 aDevice->i_getId().raw()));
565
566 /* Add to our list. */
567 HostUSBDeviceList::iterator it = mDevices.begin();
568 while (it != mDevices.end())
569 {
570 ComObjPtr<HostUSBDevice> pHostDevice = *it;
571
572 /* Assert that the object is still alive. */
573 AutoCaller devCaller(pHostDevice);
574 AssertComRC(devCaller.rc());
575
576 AutoWriteLock curLock(pHostDevice COMMA_LOCKVAL_SRC_POS);
577 if ( pHostDevice->i_getUsbProxyBackend() == aDevice->i_getUsbProxyBackend()
578 && pHostDevice->i_compare(pDev) < 0)
579 break;
580
581 ++it;
582 }
583
584 mDevices.insert(it, aDevice);
585
586 /*
587 * Run filters on the device.
588 */
589 if (aDevice->i_isCapturableOrHeld())
590 {
591 devLock.release();
592 alock.release();
593 SessionMachinesList llOpenedMachines;
594 mHost->i_parent()->i_getOpenedMachines(llOpenedMachines);
595 HRESULT rc = runAllFiltersOnDevice(aDevice, llOpenedMachines, NULL /* aIgnoreMachine */);
596 AssertComRC(rc);
597 }
598}
599
600/**
601 * Remove device notification hook for the USB proxy service.
602 *
603 * @param aDevice The device in question.
604 */
605void USBProxyService::i_deviceRemoved(ComObjPtr<HostUSBDevice> &aDevice)
606{
607 /*
608 * Validate preconditions.
609 */
610 AssertReturnVoid(!isWriteLockOnCurrentThread());
611 AssertReturnVoid(!aDevice->isWriteLockOnCurrentThread());
612 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
613 AutoWriteLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
614 LogFlowThisFunc(("aDevice=%p name={%s} state=%s id={%RTuuid}\n",
615 (HostUSBDevice *)aDevice,
616 aDevice->i_getName().c_str(),
617 aDevice->i_getStateName(),
618 aDevice->i_getId().raw()));
619
620 mDevices.remove(aDevice);
621
622 /*
623 * Detach the device from any machine currently using it,
624 * reset all data and uninitialize the device object.
625 */
626 devLock.release();
627 alock.release();
628 aDevice->i_onPhysicalDetached();
629}
630
631/**
632 * Updates the device state.
633 *
634 * This is responsible for calling HostUSBDevice::updateState().
635 *
636 * @returns true if there is a state change.
637 * @param aDevice The device in question.
638 * @param aUSBDevice The USB device structure for the last enumeration.
639 * @param fFakeUpdate Flag whether to fake updating state.
640 */
641void USBProxyService::i_updateDeviceState(ComObjPtr<HostUSBDevice> &aDevice, PUSBDEVICE aUSBDevice, bool fFakeUpdate)
642{
643 AssertReturnVoid(aDevice);
644 AssertReturnVoid(!aDevice->isWriteLockOnCurrentThread());
645
646 bool fRunFilters = false;
647 SessionMachine *pIgnoreMachine = NULL;
648 bool fDevChanged = false;
649 if (fFakeUpdate)
650 fDevChanged = aDevice->i_updateStateFake(aUSBDevice, &fRunFilters, &pIgnoreMachine);
651 else
652 fDevChanged = aDevice->i_updateState(aUSBDevice, &fRunFilters, &pIgnoreMachine);
653
654 if (fDevChanged)
655 deviceChanged(aDevice, fRunFilters, pIgnoreMachine);
656}
657
658
659/**
660 * Handle a device which state changed in some significant way.
661 *
662 * This means things like running filters and subsequent capturing and
663 * VM attaching. This may result in IPC and temporary lock abandonment.
664 *
665 * @param aDevice The device.
666 * @param fRunFilters Flag whether to run filters.
667 * @param aIgnoreMachine Machine to ignore when running filters.
668 */
669void USBProxyService::deviceChanged(ComObjPtr<HostUSBDevice> &aDevice, bool fRunFilters,
670 SessionMachine *aIgnoreMachine)
671{
672 /*
673 * Validate preconditions.
674 */
675 AssertReturnVoid(!isWriteLockOnCurrentThread());
676 AssertReturnVoid(!aDevice->isWriteLockOnCurrentThread());
677 AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
678 LogFlowThisFunc(("aDevice=%p name={%s} state=%s id={%RTuuid} aRunFilters=%RTbool aIgnoreMachine=%p\n",
679 (HostUSBDevice *)aDevice,
680 aDevice->i_getName().c_str(),
681 aDevice->i_getStateName(),
682 aDevice->i_getId().raw(),
683 fRunFilters,
684 aIgnoreMachine));
685 devLock.release();
686
687 /*
688 * Run filters if requested to do so.
689 */
690 if (fRunFilters)
691 {
692 SessionMachinesList llOpenedMachines;
693 mHost->i_parent()->i_getOpenedMachines(llOpenedMachines);
694 HRESULT rc = runAllFiltersOnDevice(aDevice, llOpenedMachines, aIgnoreMachine);
695 AssertComRC(rc);
696 }
697}
698
699
700/**
701 * Runs all the filters on the specified device.
702 *
703 * All filters mean global and active VM, with the exception of those
704 * belonging to \a aMachine. If a global ignore filter matched or if
705 * none of the filters matched, the device will be released back to
706 * the host.
707 *
708 * The device calling us here will be in the HeldByProxy, Unused, or
709 * Capturable state. The caller is aware that locks held might have
710 * to be abandond because of IPC and that the device might be in
711 * almost any state upon return.
712 *
713 *
714 * @returns COM status code (only parameter & state checks will fail).
715 * @param aDevice The USB device to apply filters to.
716 * @param llOpenedMachines The list of opened machines.
717 * @param aIgnoreMachine The machine to ignore filters from (we've just
718 * detached the device from this machine).
719 *
720 * @note The caller is expected to own no locks.
721 */
722HRESULT USBProxyService::runAllFiltersOnDevice(ComObjPtr<HostUSBDevice> &aDevice,
723 SessionMachinesList &llOpenedMachines,
724 SessionMachine *aIgnoreMachine)
725{
726 LogFlowThisFunc(("{%s} ignoring=%p\n", aDevice->i_getName().c_str(), aIgnoreMachine));
727
728 /*
729 * Verify preconditions.
730 */
731 AssertReturn(!isWriteLockOnCurrentThread(), E_FAIL);
732 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), E_FAIL);
733
734 /*
735 * Get the lists we'll iterate.
736 */
737 Host::USBDeviceFilterList globalFilters;
738 mHost->i_getUSBFilters(&globalFilters);
739
740 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
741 AutoWriteLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
742 AssertMsgReturn(aDevice->i_isCapturableOrHeld(), ("{%s} %s\n", aDevice->i_getName().c_str(),
743 aDevice->i_getStateName()), E_FAIL);
744
745 /*
746 * Run global filters filters first.
747 */
748 bool fHoldIt = false;
749 for (Host::USBDeviceFilterList::const_iterator it = globalFilters.begin();
750 it != globalFilters.end();
751 ++it)
752 {
753 AutoWriteLock filterLock(*it COMMA_LOCKVAL_SRC_POS);
754 const HostUSBDeviceFilter::BackupableUSBDeviceFilterData &data = (*it)->i_getData();
755 if (aDevice->i_isMatch(data))
756 {
757 USBDeviceFilterAction_T action = USBDeviceFilterAction_Null;
758 (*it)->COMGETTER(Action)(&action);
759 if (action == USBDeviceFilterAction_Ignore)
760 {
761 /*
762 * Release the device to the host and we're done.
763 */
764 filterLock.release();
765 devLock.release();
766 alock.release();
767 aDevice->i_requestReleaseToHost();
768 return S_OK;
769 }
770 if (action == USBDeviceFilterAction_Hold)
771 {
772 /*
773 * A device held by the proxy needs to be subjected
774 * to the machine filters.
775 */
776 fHoldIt = true;
777 break;
778 }
779 AssertMsgFailed(("action=%d\n", action));
780 }
781 }
782 globalFilters.clear();
783
784 /*
785 * Run the per-machine filters.
786 */
787 for (SessionMachinesList::const_iterator it = llOpenedMachines.begin();
788 it != llOpenedMachines.end();
789 ++it)
790 {
791 ComObjPtr<SessionMachine> pMachine = *it;
792
793 /* Skip the machine the device was just detached from. */
794 if ( aIgnoreMachine
795 && pMachine == aIgnoreMachine)
796 continue;
797
798 /* runMachineFilters takes care of checking the machine state. */
799 devLock.release();
800 alock.release();
801 if (runMachineFilters(pMachine, aDevice))
802 {
803 LogFlowThisFunc(("{%s} attached to %p\n", aDevice->i_getName().c_str(), (void *)pMachine));
804 return S_OK;
805 }
806 alock.acquire();
807 devLock.acquire();
808 }
809
810 /*
811 * No matching machine, so request hold or release depending
812 * on global filter match.
813 */
814 devLock.release();
815 alock.release();
816 if (fHoldIt)
817 aDevice->i_requestHold();
818 else
819 aDevice->i_requestReleaseToHost();
820 return S_OK;
821}
822
823
824/**
825 * Runs the USB filters of the machine on the device.
826 *
827 * If a match is found we will request capture for VM. This may cause
828 * us to temporary abandon locks while doing IPC.
829 *
830 * @param aMachine Machine whose filters are to be run.
831 * @param aDevice The USB device in question.
832 * @returns @c true if the device has been or is being attached to the VM, @c false otherwise.
833 *
834 * @note Locks several objects temporarily for reading or writing.
835 */
836bool USBProxyService::runMachineFilters(SessionMachine *aMachine, ComObjPtr<HostUSBDevice> &aDevice)
837{
838 LogFlowThisFunc(("{%s} aMachine=%p \n", aDevice->i_getName().c_str(), aMachine));
839
840 /*
841 * Validate preconditions.
842 */
843 AssertReturn(aMachine, false);
844 AssertReturn(!isWriteLockOnCurrentThread(), false);
845 AssertReturn(!aMachine->isWriteLockOnCurrentThread(), false);
846 AssertReturn(!aDevice->isWriteLockOnCurrentThread(), false);
847 /* Let HostUSBDevice::requestCaptureToVM() validate the state. */
848
849 /*
850 * Do the job.
851 */
852 ULONG ulMaskedIfs;
853 if (aMachine->i_hasMatchingUSBFilter(aDevice, &ulMaskedIfs))
854 {
855 /* try to capture the device */
856 HRESULT hrc = aDevice->i_requestCaptureForVM(aMachine, false /* aSetError */, Utf8Str(), ulMaskedIfs);
857 return SUCCEEDED(hrc)
858 || hrc == E_UNEXPECTED /* bad device state, give up */;
859 }
860
861 return false;
862}
863
864
865/**
866 * Searches the list of devices (mDevices) for the given device.
867 *
868 *
869 * @returns Smart pointer to the device on success, NULL otherwise.
870 * @param aId The UUID of the device we're looking for.
871 */
872ComObjPtr<HostUSBDevice> USBProxyService::findDeviceById(IN_GUID aId)
873{
874 Guid Id(aId);
875 ComObjPtr<HostUSBDevice> Dev;
876 for (HostUSBDeviceList::iterator it = mDevices.begin();
877 it != mDevices.end();
878 ++it)
879 if ((*it)->i_getId() == Id)
880 {
881 Dev = (*it);
882 break;
883 }
884
885 return Dev;
886}
887
888/**
889 * Creates a new USB device source.
890 *
891 * @returns COM status code.
892 * @param aBackend The backend to use.
893 * @param aId The ID of the source.
894 * @param aAddress The backend specific address.
895 * @param aPropertyNames Vector of optional property keys the backend supports.
896 * @param aPropertyValues Vector of optional property values the backend supports.
897 * @param fLoadingSettings Flag whether the USB device source is created while the
898 * settings are loaded or through the Main API.
899 */
900HRESULT USBProxyService::createUSBDeviceSource(const com::Utf8Str &aBackend, const com::Utf8Str &aId,
901 const com::Utf8Str &aAddress, const std::vector<com::Utf8Str> &aPropertyNames,
902 const std::vector<com::Utf8Str> &aPropertyValues,
903 bool fLoadingSettings)
904{
905 HRESULT hrc = S_OK;
906
907 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
908
909 /** @todo */
910 NOREF(aPropertyNames);
911 NOREF(aPropertyValues);
912
913 /* Check whether the ID is used first. */
914 for (USBProxyBackendList::iterator it = mBackends.begin();
915 it != mBackends.end();
916 ++it)
917 {
918 USBProxyBackend *pUsbProxyBackend = *it;
919
920 if (aId.equals(pUsbProxyBackend->i_getId()))
921 return setError(VBOX_E_OBJECT_IN_USE,
922 tr("The USB device source \"%s\" exists already"), aId.c_str());
923 }
924
925 /* Create appropriate proxy backend. */
926 if (aBackend.equalsIgnoreCase("USBIP"))
927 {
928 ComObjPtr<USBProxyBackendUsbIp> UsbProxyBackend;
929
930 UsbProxyBackend.createObject();
931 int vrc = UsbProxyBackend->init(this, aId, aAddress, fLoadingSettings);
932 if (RT_FAILURE(vrc))
933 hrc = setError(E_FAIL,
934 tr("Creating the USB device source \"%s\" using backend \"%s\" failed with %Rrc"),
935 aId.c_str(), aBackend.c_str(), vrc);
936 else
937 mBackends.push_back(static_cast<ComObjPtr<USBProxyBackend> >(UsbProxyBackend));
938 }
939 else
940 hrc = setError(VBOX_E_OBJECT_NOT_FOUND,
941 tr("The USB backend \"%s\" is not supported"), aBackend.c_str());
942
943 return hrc;
944}
945
946/*static*/
947HRESULT USBProxyService::setError(HRESULT aResultCode, const char *aText, ...)
948{
949 va_list va;
950 va_start(va, aText);
951 HRESULT rc = VirtualBoxBase::setErrorInternalV(aResultCode,
952 COM_IIDOF(IHost),
953 "USBProxyService",
954 aText, va,
955 false /* aWarning*/,
956 true /* aLogIt*/);
957 va_end(va);
958 return rc;
959}
960
961/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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