VirtualBox

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

Last change on this file since 73768 was 73003, checked in by vboxsync, 6 years ago

Main: Use setErrorBoth when we've got a VBox status code handy. (The COM status codes aren't too specfic and this may help us decode error messages and provide an alternative to strstr for API clients. setErrorBoth isn't new, btw.)

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

© 2023 Oracle
ContactPrivacy policyTerms of Use