GuestProcessImpl.cpp revision ce46b563e3317d0c792fbe141e2978bfa0e91aae
af062818b47340eef15700d2f0211576ba3506eevboxsync * VirtualBox Main - XXX.
af062818b47340eef15700d2f0211576ba3506eevboxsync * Copyright (C) 2012 Oracle Corporation
af062818b47340eef15700d2f0211576ba3506eevboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
af062818b47340eef15700d2f0211576ba3506eevboxsync * available from http://www.virtualbox.org. This file is free software;
af062818b47340eef15700d2f0211576ba3506eevboxsync * you can redistribute it and/or modify it under the terms of the GNU
af062818b47340eef15700d2f0211576ba3506eevboxsync * General Public License (GPL) as published by the Free Software
af062818b47340eef15700d2f0211576ba3506eevboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
af062818b47340eef15700d2f0211576ba3506eevboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
af062818b47340eef15700d2f0211576ba3506eevboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
4b9d6701570cb98fd36e209314239d104ec584d3vboxsync * Locking rules:
4b9d6701570cb98fd36e209314239d104ec584d3vboxsync * - When the main dispatcher (callbackDispatcher) is called it takes the
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync * WriteLock while dispatching to the various on* methods.
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync * - All other outer functions (accessible by Main) must not own a lock
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync * while waiting for a callback or for an event.
589fd26cedb2b4ebbed14f2964cad03cc8ebbca2vboxsync * - Only keep Read/WriteLocks as short as possible and only when necessary.
af062818b47340eef15700d2f0211576ba3506eevboxsync/*******************************************************************************
af062818b47340eef15700d2f0211576ba3506eevboxsync* Header Files *
af062818b47340eef15700d2f0211576ba3506eevboxsync*******************************************************************************/
af062818b47340eef15700d2f0211576ba3506eevboxsync virtual ~GuestProcessTask(void) { }
af062818b47340eef15700d2f0211576ba3506eevboxsyncstruct GuestProcessStartTask : public GuestProcessTask
af062818b47340eef15700d2f0211576ba3506eevboxsync// constructor / destructor
af062818b47340eef15700d2f0211576ba3506eevboxsync/////////////////////////////////////////////////////////////////////////////
af062818b47340eef15700d2f0211576ba3506eevboxsync// public initializer/uninitializer for internal purposes only
af062818b47340eef15700d2f0211576ba3506eevboxsync/////////////////////////////////////////////////////////////////////////////
af062818b47340eef15700d2f0211576ba3506eevboxsyncint GuestProcess::init(Console *aConsole, GuestSession *aSession, ULONG aProcessID, const GuestProcessStartupInfo &aProcInfo)
af062818b47340eef15700d2f0211576ba3506eevboxsync LogFlowThisFunc(("aConsole=%p, aSession=%p, aProcessID=%RU32\n",
af062818b47340eef15700d2f0211576ba3506eevboxsync /* Enclose the state transition NotReady->InInit->Ready. */
af062818b47340eef15700d2f0211576ba3506eevboxsync AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
af062818b47340eef15700d2f0211576ba3506eevboxsync /* Everything else will be set by the actual starting routine. */
af062818b47340eef15700d2f0211576ba3506eevboxsync /* Confirm a successful initialization when it's the case. */
af062818b47340eef15700d2f0211576ba3506eevboxsync * Uninitializes the instance.
af062818b47340eef15700d2f0211576ba3506eevboxsync * Called from FinalRelease().
af062818b47340eef15700d2f0211576ba3506eevboxsync /* Enclose the state transition Ready->InUninit->NotReady. */
af062818b47340eef15700d2f0211576ba3506eevboxsync * Cancel all callbacks + waiters.
af062818b47340eef15700d2f0211576ba3506eevboxsync * Note: Deleting them is the job of the caller!
af062818b47340eef15700d2f0211576ba3506eevboxsync for (GuestCtrlCallbacks::iterator itCallbacks = mData.mCallbacks.begin();
af062818b47340eef15700d2f0211576ba3506eevboxsync itCallbacks != mData.mCallbacks.end(); ++itCallbacks)
af062818b47340eef15700d2f0211576ba3506eevboxsync GuestCtrlCallback *pCallback = itCallbacks->second;
5112e32d7072e280613921c982a6672f2c859cf3vboxsync mData.mStatus = ProcessStatus_Down; /** @todo Correct? */
af062818b47340eef15700d2f0211576ba3506eevboxsync /* Remove the reference from the session's process list. */
af062818b47340eef15700d2f0211576ba3506eevboxsync// implementation of public getters/setters for attributes
af062818b47340eef15700d2f0211576ba3506eevboxsync/////////////////////////////////////////////////////////////////////////////
af062818b47340eef15700d2f0211576ba3506eevboxsyncSTDMETHODIMP GuestProcess::COMGETTER(Arguments)(ComSafeArrayOut(BSTR, aArguments))
af062818b47340eef15700d2f0211576ba3506eevboxsync if (FAILED(autoCaller.rc())) return autoCaller.rc();
af062818b47340eef15700d2f0211576ba3506eevboxsync com::SafeArray<BSTR> collection(mData.mProcess.mArguments.size());
af062818b47340eef15700d2f0211576ba3506eevboxsync for (ProcessArguments::const_iterator it = mData.mProcess.mArguments.begin();
af062818b47340eef15700d2f0211576ba3506eevboxsync collection.detachTo(ComSafeArrayOutArg(aArguments));
af062818b47340eef15700d2f0211576ba3506eevboxsync#endif /* VBOX_WITH_GUEST_CONTROL */
af062818b47340eef15700d2f0211576ba3506eevboxsyncSTDMETHODIMP GuestProcess::COMGETTER(Environment)(ComSafeArrayOut(BSTR, aEnvironment))
af062818b47340eef15700d2f0211576ba3506eevboxsync if (FAILED(autoCaller.rc())) return autoCaller.rc();
af062818b47340eef15700d2f0211576ba3506eevboxsync com::SafeArray<BSTR> arguments(mData.mProcess.mEnvironment.Size());
af062818b47340eef15700d2f0211576ba3506eevboxsync arguments.detachTo(ComSafeArrayOutArg(aEnvironment));
af062818b47340eef15700d2f0211576ba3506eevboxsync#endif /* VBOX_WITH_GUEST_CONTROL */
af062818b47340eef15700d2f0211576ba3506eevboxsyncSTDMETHODIMP GuestProcess::COMGETTER(ExecutablePath)(BSTR *aExecutablePath)
af062818b47340eef15700d2f0211576ba3506eevboxsync if (FAILED(autoCaller.rc())) return autoCaller.rc();
af062818b47340eef15700d2f0211576ba3506eevboxsync#endif /* VBOX_WITH_GUEST_CONTROL */
af062818b47340eef15700d2f0211576ba3506eevboxsyncSTDMETHODIMP GuestProcess::COMGETTER(ExitCode)(LONG *aExitCode)
af062818b47340eef15700d2f0211576ba3506eevboxsync if (FAILED(autoCaller.rc())) return autoCaller.rc();
af062818b47340eef15700d2f0211576ba3506eevboxsync#endif /* VBOX_WITH_GUEST_CONTROL */
af062818b47340eef15700d2f0211576ba3506eevboxsyncSTDMETHODIMP GuestProcess::COMGETTER(Name)(BSTR *aName)
af062818b47340eef15700d2f0211576ba3506eevboxsync if (FAILED(autoCaller.rc())) return autoCaller.rc();
af062818b47340eef15700d2f0211576ba3506eevboxsync#endif /* VBOX_WITH_GUEST_CONTROL */
af062818b47340eef15700d2f0211576ba3506eevboxsyncSTDMETHODIMP GuestProcess::COMGETTER(PID)(ULONG *aPID)
af062818b47340eef15700d2f0211576ba3506eevboxsync if (FAILED(autoCaller.rc())) return autoCaller.rc();
af062818b47340eef15700d2f0211576ba3506eevboxsync#endif /* VBOX_WITH_GUEST_CONTROL */
af062818b47340eef15700d2f0211576ba3506eevboxsyncSTDMETHODIMP GuestProcess::COMGETTER(Status)(ProcessStatus_T *aStatus)
af062818b47340eef15700d2f0211576ba3506eevboxsync if (FAILED(autoCaller.rc())) return autoCaller.rc();
af062818b47340eef15700d2f0211576ba3506eevboxsync#endif /* VBOX_WITH_GUEST_CONTROL */
af062818b47340eef15700d2f0211576ba3506eevboxsync// private methods
af062818b47340eef15700d2f0211576ba3506eevboxsync/////////////////////////////////////////////////////////////////////////////
af062818b47340eef15700d2f0211576ba3506eevboxsyncinline int GuestProcess::callbackAdd(GuestCtrlCallback *pCallback, uint32_t *puContextID)
af062818b47340eef15700d2f0211576ba3506eevboxsync const ComObjPtr<GuestSession> pSession(mData.mParent);
af062818b47340eef15700d2f0211576ba3506eevboxsync /* Create a new context ID and assign it. */
af062818b47340eef15700d2f0211576ba3506eevboxsync /* Create a new context ID ... */
af062818b47340eef15700d2f0211576ba3506eevboxsync uNewContextID = VBOX_GUESTCTRL_CONTEXTID_MAKE(uSessionID,
af062818b47340eef15700d2f0211576ba3506eevboxsync /* Is the context ID already used? Try next ID ... */
af062818b47340eef15700d2f0211576ba3506eevboxsync /* Callback with context ID was not found. This means
af062818b47340eef15700d2f0211576ba3506eevboxsync * we can use this context ID for our new callback we want
af062818b47340eef15700d2f0211576ba3506eevboxsync * to add below. */
af062818b47340eef15700d2f0211576ba3506eevboxsync break; /* Don't try too hard. */
af062818b47340eef15700d2f0211576ba3506eevboxsync /* Add callback with new context ID to our callback map.
af062818b47340eef15700d2f0211576ba3506eevboxsync * Note: This is *not* uNewContextID (which also includes
af062818b47340eef15700d2f0211576ba3506eevboxsync * the session + process ID), just the context count
af062818b47340eef15700d2f0211576ba3506eevboxsync * will be used here. */
af062818b47340eef15700d2f0211576ba3506eevboxsync /* Report back new context ID. */
af062818b47340eef15700d2f0211576ba3506eevboxsync LogFlowThisFunc(("Added new callback (Session: %RU32, Process: %RU32, Count=%RU32) CID=%RU32\n",
af062818b47340eef15700d2f0211576ba3506eevboxsync uSessionID, mData.mProcessID, uCount, uNewContextID));
af062818b47340eef15700d2f0211576ba3506eevboxsyncint GuestProcess::callbackDispatcher(uint32_t uContextID, uint32_t uFunction, void *pvData, size_t cbData)
af062818b47340eef15700d2f0211576ba3506eevboxsync LogFlowThisFunc(("uPID=%RU32, uContextID=%RU32, uFunction=%RU32, pvData=%p, cbData=%RU32\n",
af062818b47340eef15700d2f0211576ba3506eevboxsync mData.mPID, uContextID, uFunction, pvData, cbData));
af062818b47340eef15700d2f0211576ba3506eevboxsync /* Get the optional callback associated to this context ID.
af062818b47340eef15700d2f0211576ba3506eevboxsync * The callback may not be around anymore if just kept locally by the caller when
af062818b47340eef15700d2f0211576ba3506eevboxsync * doing the actual HGCM sending stuff. */
af062818b47340eef15700d2f0211576ba3506eevboxsync = mData.mCallbacks.find(VBOX_GUESTCTRL_CONTEXTID_GET_COUNT(uContextID));
af062818b47340eef15700d2f0211576ba3506eevboxsync PCALLBACKDATACLIENTDISCONNECTED pCallbackData = reinterpret_cast<PCALLBACKDATACLIENTDISCONNECTED>(pvData);
af062818b47340eef15700d2f0211576ba3506eevboxsync AssertReturn(sizeof(CALLBACKDATACLIENTDISCONNECTED) == cbData, VERR_INVALID_PARAMETER);
af062818b47340eef15700d2f0211576ba3506eevboxsync AssertReturn(CALLBACKDATAMAGIC_CLIENT_DISCONNECTED == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
af062818b47340eef15700d2f0211576ba3506eevboxsync rc = onGuestDisconnected(pCallback, pCallbackData); /* Affects all callbacks. */
af062818b47340eef15700d2f0211576ba3506eevboxsync PCALLBACKDATAEXECSTATUS pCallbackData = reinterpret_cast<PCALLBACKDATAEXECSTATUS>(pvData);
af062818b47340eef15700d2f0211576ba3506eevboxsync AssertReturn(sizeof(CALLBACKDATAEXECSTATUS) == cbData, VERR_INVALID_PARAMETER);
af062818b47340eef15700d2f0211576ba3506eevboxsync AssertReturn(CALLBACKDATAMAGIC_EXEC_STATUS == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
af062818b47340eef15700d2f0211576ba3506eevboxsync rc = onProcessStatusChange(pCallback, pCallbackData);
af062818b47340eef15700d2f0211576ba3506eevboxsync PCALLBACKDATAEXECOUT pCallbackData = reinterpret_cast<PCALLBACKDATAEXECOUT>(pvData);
af062818b47340eef15700d2f0211576ba3506eevboxsync AssertReturn(sizeof(CALLBACKDATAEXECOUT) == cbData, VERR_INVALID_PARAMETER);
af062818b47340eef15700d2f0211576ba3506eevboxsync AssertReturn(CALLBACKDATAMAGIC_EXEC_OUT == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
AssertReturn(CALLBACKDATAMAGIC_EXEC_IN_STATUS == pCallbackData->hdr.u32Magic, VERR_INVALID_PARAMETER);
#ifdef DEBUG
return rc;
return VINF_SUCCESS;
return VERR_NOT_FOUND;
AssertReleaseMsg(mData.mPID == uPID, ("Unterminated guest process (PID %RU32) sent data to a newly started process (PID %RU32)\n",
return VINF_SUCCESS;
uninit();
int GuestProcess::onGuestDisconnected(GuestCtrlCallback *pCallback, PCALLBACKDATACLIENTDISCONNECTED pData)
if (pCallback)
return rc;
int GuestProcess::onProcessInputStatus(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECINSTATUS pData)
LogFlowThisFunc(("uPID=%RU32, uStatus=%RU32, uFlags=%RU32, cbProcessed=%RU32, pCallback=%p, pData=%p\n",
return rc;
if (pCallback)
return rc;
int GuestProcess::onProcessStatusChange(GuestCtrlCallback *pCallback, PCALLBACKDATAEXECSTATUS pData)
return rc;
case PROC_STS_STARTED:
case PROC_STS_TEN:
case PROC_STS_TES:
case PROC_STS_TEA:
case PROC_STS_TOK:
case PROC_STS_TOA:
case PROC_STS_DWN:
case PROC_STS_ERROR:
Utf8Str strError = Utf8StrFmt(tr("Guest process \"%s\" could not be started: "), mData.mProcess.mCommand.c_str());
case VERR_PATH_NOT_FOUND:
case VERR_BAD_EXE_FORMAT:
case VERR_INVALID_NAME:
case VERR_TIMEOUT:
case VERR_CANCELLED:
case VERR_PERMISSION_DENIED:
case VERR_MAX_PROCS_REACHED:
case PROC_STS_UNDEFINED:
if (pCallback)
if (fSignal)
return rc;
LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uFlags=%RU32, pvData=%p, cbData=%RU32, pCallback=%p, pData=%p\n",
return rc;
if (pCallback)
if (fSignal)
return rc;
LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uSize=%RU32, uTimeoutMS=%RU32, pvData=%p, cbData=%RU32\n",
if (!pCallbackRead)
return VERR_NO_MEMORY;
if (cbRead)
if (pcbRead)
return rc;
int rc2;
return rc;
#ifdef DEBUG
return rc2;
return rc;
int rc;
if (!pCallbackStart)
return VERR_NO_MEMORY;
if (cArgs)
paParms[i++].setPointer((void*)sessionCreds.mPassword.c_str(), (ULONG)sessionCreds.mPassword.length() + 1);
if (pszArgs)
return rc;
return rc;
return rc;
return VERR_NOT_SUPPORTED;
return VERR_NOT_IMPLEMENTED;
LogFlowThisFunc(("fWaitFlags=%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p\n",
return VINF_SUCCESS;
case ProcessStatus_Down:
case ProcessStatus_Error:
case ProcessStatus_Undefined:
case ProcessStatus_Starting:
case ProcessStatus_Started:
return VERR_NOT_IMPLEMENTED;
case ProcessStatus_Started:
case ProcessStatus_Paused:
case ProcessStatus_Down:
case ProcessStatus_Error:
case ProcessStatus_Undefined:
case ProcessStatus_Starting:
return VERR_NOT_IMPLEMENTED;
return VINF_SUCCESS;
return VERR_ALREADY_EXISTS;
return rc;
return rc;
LogFlowThisFunc(("uPID=%RU32, uHandle=%RU32, uFlags=%RU32, pvData=%p, cbData=%RU32, uTimeoutMS=%RU32, puWritten=%p\n",
if (!pCallbackWrite)
return VERR_NO_MEMORY;
case INPUT_STS_WRITTEN:
case INPUT_STS_ERROR:
case INPUT_STS_TERMINATED:
case INPUT_STS_OVERFLOW:
if (puWritten)
return rc;
STDMETHODIMP GuestProcess::Read(ULONG aHandle, ULONG aSize, ULONG aTimeoutMS, ComSafeArrayOut(BYTE, aData))
#ifndef VBOX_WITH_GUEST_CONTROL
if (aSize == 0)
return hr;
#ifndef VBOX_WITH_GUEST_CONTROL
return hr;
STDMETHODIMP GuestProcess::WaitFor(ULONG aWaitFlags, ULONG aTimeoutMS, ProcessWaitResult_T *aReason)
#ifndef VBOX_WITH_GUEST_CONTROL
return hr;
STDMETHODIMP GuestProcess::WaitForArray(ComSafeArrayIn(ProcessWaitForFlag_T, aFlags), ULONG aTimeoutMS, ProcessWaitResult_T *aReason)
#ifndef VBOX_WITH_GUEST_CONTROL
#ifndef VBOX_WITH_GUEST_CONTROL
return hr;
#ifndef VBOX_WITH_GUEST_CONTROL