/** * Runs all the filters on the specified device. * * All filters mean global and active VM, with the exception of those * belonging to \a aMachine. If a global ignore filter matched or if * none of the filters matched, the device will be released back to * the host. * * The device calling us here will be in the HeldByProxy, Unused, or * Capturable state. The caller is aware that locks held might have * to be abandond because of IPC and that the device might be in * almost any state upon return. * * * @returns COM status code (only parameter & state checks will fail). * @param aDevice The USB device to apply filters to. * @param aIgnoreMachine The machine to ignore filters from (we've just * detached the device from this machine). * * @note The caller is expected to own no locks. */ HRESULT USBProxyService::runAllFiltersOnDevice(ComObjPtr<HostUSBDevice> &aDevice, SessionMachinesList &llOpenedMachines, SessionMachine *aIgnoreMachine) { LogFlowThisFunc(("{%s} ignoring=%p\n", aDevice->getName().c_str(), aIgnoreMachine)); /* * Verify preconditions. */ AssertReturn(!isWriteLockOnCurrentThread(), E_FAIL); AssertReturn(!aDevice->isWriteLockOnCurrentThread(), E_FAIL); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); AutoWriteLock devLock(aDevice COMMA_LOCKVAL_SRC_POS); AssertMsgReturn(aDevice->isCapturableOrHeld(), ("{%s} %s\n", aDevice->getName().c_str(), aDevice->getStateName()), E_FAIL); /* * Get the lists we'll iterate. */ Host::USBDeviceFilterList globalFilters; mHost->getUSBFilters(&globalFilters); /* * Run global filters filters first. */ bool fHoldIt = false; for (Host::USBDeviceFilterList::const_iterator it = globalFilters.begin(); it != globalFilters.end(); ++it) { AutoWriteLock filterLock(*it COMMA_LOCKVAL_SRC_POS); const HostUSBDeviceFilter::Data &data = (*it)->getData(); if (aDevice->isMatch(data)) { USBDeviceFilterAction_T action = USBDeviceFilterAction_Null; (*it)->COMGETTER(Action)(&action); if (action == USBDeviceFilterAction_Ignore) { /* * Release the device to the host and we're done. */ filterLock.release(); devLock.release(); alock.release(); aDevice->requestReleaseToHost(); return S_OK; } if (action == USBDeviceFilterAction_Hold) { /* * A device held by the proxy needs to be subjected * to the machine filters. */ fHoldIt = true; break; } AssertMsgFailed(("action=%d\n", action)); } } globalFilters.clear(); /* * Run the per-machine filters. */ for (SessionMachinesList::const_iterator it = llOpenedMachines.begin(); it != llOpenedMachines.end(); ++it) { ComObjPtr<SessionMachine> pMachine = *it; /* Skip the machine the device was just detached from. */ if ( aIgnoreMachine && pMachine == aIgnoreMachine) continue; /* runMachineFilters takes care of checking the machine state. */ devLock.release(); alock.release(); if (runMachineFilters(pMachine, aDevice)) { LogFlowThisFunc(("{%s} attached to %p\n", aDevice->getName().c_str(), (void *)pMachine)); return S_OK; } alock.acquire(); devLock.acquire(); } /* * No matching machine, so request hold or release depending * on global filter match. */ devLock.release(); alock.release(); if (fHoldIt) aDevice->requestHold(); else aDevice->requestReleaseToHost(); return S_OK; }
void HostPowerService::notify(Reason_T aReason) { SessionMachinesList machines; VirtualBox::InternalControlList controls; HRESULT rc = S_OK; switch (aReason) { case Reason_HostSuspend: { LogFunc(("HOST SUSPEND\n")); #ifdef VBOX_WITH_RESOURCE_USAGE_API /* Suspend performance sampling to avoid unnecessary callbacks due to jumps in time. */ PerformanceCollector *perfcollector = mVirtualBox->i_performanceCollector(); if (perfcollector) perfcollector->suspendSampling(); #endif mVirtualBox->i_getOpenedMachines(machines, &controls); /* pause running VMs */ for (VirtualBox::InternalControlList::const_iterator it = controls.begin(); it != controls.end(); ++it) { ComPtr<IInternalSessionControl> pControl = *it; /* PauseWithReason() will simply return a failure if * the VM is in an inappropriate state */ rc = pControl->PauseWithReason(Reason_HostSuspend); if (FAILED(rc)) continue; /* save the control to un-pause the VM later */ mSessionControls.push_back(pControl); } LogRel(("Host suspending: Paused %d VMs\n", mSessionControls.size())); break; } case Reason_HostResume: { LogFunc(("HOST RESUME\n")); size_t resumed = 0; /* go through VMs we paused on Suspend */ for (size_t i = 0; i < mSessionControls.size(); ++i) { /* note that Resume() will simply return a failure if the VM is * in an inappropriate state (it will also fail if the VM has * been somehow closed by this time already so that the * console reference we have is dead) */ rc = mSessionControls[i]->ResumeWithReason(Reason_HostResume); if (FAILED(rc)) continue; ++resumed; } LogRel(("Host resumed: Resumed %d VMs\n", resumed)); #ifdef VBOX_WITH_RESOURCE_USAGE_API /* Resume the performance sampling. */ PerformanceCollector *perfcollector = mVirtualBox->i_performanceCollector(); if (perfcollector) perfcollector->resumeSampling(); #endif mSessionControls.clear(); break; } case Reason_HostBatteryLow: { LogFunc(("BATTERY LOW\n")); Bstr value; rc = mVirtualBox->GetExtraData(Bstr("VBoxInternal2/SavestateOnBatteryLow").raw(), value.asOutParam()); int fGlobal = 0; if (SUCCEEDED(rc) && !value.isEmpty()) { if (value != "0") fGlobal = 1; else if (value == "0") fGlobal = -1; } mVirtualBox->i_getOpenedMachines(machines, &controls); size_t saved = 0; /* save running VMs */ for (SessionMachinesList::const_iterator it = machines.begin(); it != machines.end(); ++it) { ComPtr<SessionMachine> pMachine = *it; rc = pMachine->GetExtraData(Bstr("VBoxInternal2/SavestateOnBatteryLow").raw(), value.asOutParam()); int fPerVM = 0; if (SUCCEEDED(rc) && !value.isEmpty()) { /* per-VM overrides global */ if (value != "0") fPerVM = 2; else if (value == "0") fPerVM = -2; } /* default is true */ if (fGlobal + fPerVM >= 0) { ComPtr<IProgress> progress; /* SessionMachine::i_saveStateWithReason() will return * a failure if the VM is in an inappropriate state */ rc = pMachine->i_saveStateWithReason(Reason_HostBatteryLow, progress); if (FAILED(rc)) { LogRel(("SaveState '%s' failed with %Rhrc\n", pMachine->i_getName().c_str(), rc)); continue; } /* Wait until the operation has been completed. */ rc = progress->WaitForCompletion(-1); if (SUCCEEDED(rc)) { LONG iRc; progress->COMGETTER(ResultCode)(&iRc); rc = iRc; } AssertMsg(SUCCEEDED(rc), ("SaveState WaitForCompletion failed with %Rhrc (%#08X)\n", rc, rc)); if (SUCCEEDED(rc)) { LogRel(("SaveState '%s' succeeded\n", pMachine->i_getName().c_str())); ++saved; } } } LogRel(("Battery Low: saved %d VMs\n", saved)); break; } default: /* nothing */; } }