/* $Id: GuestSessionImpl.cpp 55588 2015-05-01 19:37:46Z vboxsync $ */ /** @file * VirtualBox Main - Guest session handling. */ /* * Copyright (C) 2012-2014 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ /******************************************************************************* * Header Files * *******************************************************************************/ #include "GuestImpl.h" #include "GuestSessionImpl.h" #include "GuestCtrlImplPrivate.h" #include "VirtualBoxErrorInfoImpl.h" #include "Global.h" #include "AutoCaller.h" #include "ProgressImpl.h" #include "VBoxEvents.h" #include "VMMDev.h" #include /* For auto_ptr. */ #include /* For unconst(). */ #include #include /* For CopyTo/From. */ #include #include #include #ifdef LOG_GROUP #undef LOG_GROUP #endif #define LOG_GROUP LOG_GROUP_GUEST_CONTROL #include /** * Base class representing an internal * asynchronous session task. */ class GuestSessionTaskInternal { public: GuestSessionTaskInternal(GuestSession *pSession) : mSession(pSession), mRC(VINF_SUCCESS) { } virtual ~GuestSessionTaskInternal(void) { } int rc(void) const { return mRC; } bool isOk(void) const { return RT_SUCCESS(mRC); } const ComObjPtr &Session(void) const { return mSession; } protected: const ComObjPtr mSession; int mRC; }; /** * Class for asynchronously opening a guest session. */ class GuestSessionTaskInternalOpen : public GuestSessionTaskInternal { public: GuestSessionTaskInternalOpen(GuestSession *pSession) : GuestSessionTaskInternal(pSession) { } }; /** * Internal listener class to serve events in an * active manner, e.g. without polling delays. */ class GuestSessionListener { public: GuestSessionListener(void) { } HRESULT init(GuestSession *pSession) { AssertPtrReturn(pSession, E_POINTER); mSession = pSession; return S_OK; } void uninit(void) { mSession = NULL; } STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent) { switch (aType) { case VBoxEventType_OnGuestSessionStateChanged: { AssertPtrReturn(mSession, E_POINTER); int rc2 = mSession->signalWaitEvent(aType, aEvent); #ifdef DEBUG_andy LogFlowFunc(("Signalling events of type=%RU32, session=%p resulted in rc=%Rrc\n", aType, mSession, rc2)); #endif break; } default: AssertMsgFailed(("Unhandled event %RU32\n", aType)); break; } return S_OK; } private: GuestSession *mSession; }; typedef ListenerImpl GuestSessionListenerImpl; VBOX_LISTENER_DECLARE(GuestSessionListenerImpl) // constructor / destructor ///////////////////////////////////////////////////////////////////////////// DEFINE_EMPTY_CTOR_DTOR(GuestSession) HRESULT GuestSession::FinalConstruct(void) { LogFlowThisFuncEnter(); return BaseFinalConstruct(); } void GuestSession::FinalRelease(void) { LogFlowThisFuncEnter(); uninit(); BaseFinalRelease(); LogFlowThisFuncLeave(); } // public initializer/uninitializer for internal purposes only ///////////////////////////////////////////////////////////////////////////// /** * Initializes a guest session but does *not* open in on the guest side * yet. This needs to be done via the openSession() / openSessionAsync calls. * * @return IPRT status code. ** @todo Docs! */ int GuestSession::init(Guest *pGuest, const GuestSessionStartupInfo &ssInfo, const GuestCredentials &guestCreds) { LogFlowThisFunc(("pGuest=%p, ssInfo=%p, guestCreds=%p\n", pGuest, &ssInfo, &guestCreds)); /* Enclose the state transition NotReady->InInit->Ready. */ AutoInitSpan autoInitSpan(this); AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED); #ifndef VBOX_WITH_GUEST_CONTROL autoInitSpan.setSucceeded(); return VINF_SUCCESS; #else AssertPtrReturn(pGuest, VERR_INVALID_POINTER); /* * Initialize our data members from the input. */ mParent = pGuest; /* Copy over startup info. */ /** @todo Use an overloaded copy operator. Later. */ mData.mSession.mID = ssInfo.mID; mData.mSession.mIsInternal = ssInfo.mIsInternal; mData.mSession.mName = ssInfo.mName; mData.mSession.mOpenFlags = ssInfo.mOpenFlags; mData.mSession.mOpenTimeoutMS = ssInfo.mOpenTimeoutMS; /** @todo Use an overloaded copy operator. Later. */ mData.mCredentials.mUser = guestCreds.mUser; mData.mCredentials.mPassword = guestCreds.mPassword; mData.mCredentials.mDomain = guestCreds.mDomain; /* Initialize the remainder of the data. */ mData.mRC = VINF_SUCCESS; mData.mStatus = GuestSessionStatus_Undefined; mData.mNumObjects = 0; int rc = mData.mEnvironment.initNormal(); if (RT_SUCCESS(rc)) { rc = RTCritSectInit(&mWaitEventCritSect); AssertRC(rc); } if (RT_SUCCESS(rc)) rc = i_determineProtocolVersion(); if (RT_SUCCESS(rc)) { /* * */ HRESULT hr = unconst(mEventSource).createObject(); if (SUCCEEDED(hr)) hr = mEventSource->init(); if (SUCCEEDED(hr)) { try { GuestSessionListener *pListener = new GuestSessionListener(); ComObjPtr thisListener; hr = thisListener.createObject(); if (SUCCEEDED(hr)) hr = thisListener->init(pListener, this); if (SUCCEEDED(hr)) { com::SafeArray eventTypes; eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged); hr = mEventSource->RegisterListener(thisListener, ComSafeArrayAsInParam(eventTypes), TRUE /* Active listener */); if (SUCCEEDED(hr)) { mLocalListener = thisListener; /* * Mark this object as operational and return success. */ autoInitSpan.setSucceeded(); LogFlowThisFunc(("mName=%s mID=%RU32 mIsInternal=%RTbool rc=VINF_SUCCESS\n", mData.mSession.mName.c_str(), mData.mSession.mID, mData.mSession.mIsInternal)); return VINF_SUCCESS; } } } catch (std::bad_alloc &) { hr = E_OUTOFMEMORY; } } rc = Global::vboxStatusCodeFromCOM(hr); } autoInitSpan.setFailed(); LogThisFunc(("Failed! mName=%s mID=%RU32 mIsInternal=%RTbool => rc=%Rrc\n", mData.mSession.mName.c_str(), mData.mSession.mID, mData.mSession.mIsInternal, rc)); return rc; #endif /* VBOX_WITH_GUEST_CONTROL */ } /** * Uninitializes the instance. * Called from FinalRelease(). */ void GuestSession::uninit(void) { /* Enclose the state transition Ready->InUninit->NotReady. */ AutoUninitSpan autoUninitSpan(this); if (autoUninitSpan.uninitDone()) return; LogFlowThisFuncEnter(); int rc = VINF_SUCCESS; #ifdef VBOX_WITH_GUEST_CONTROL AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); LogFlowThisFunc(("Closing directories (%zu total)\n", mData.mDirectories.size())); for (SessionDirectories::iterator itDirs = mData.mDirectories.begin(); itDirs != mData.mDirectories.end(); ++itDirs) { Assert(mData.mNumObjects); mData.mNumObjects--; itDirs->second->i_onRemove(); itDirs->second->uninit(); } mData.mDirectories.clear(); LogFlowThisFunc(("Closing files (%zu total)\n", mData.mFiles.size())); for (SessionFiles::iterator itFiles = mData.mFiles.begin(); itFiles != mData.mFiles.end(); ++itFiles) { Assert(mData.mNumObjects); mData.mNumObjects--; itFiles->second->i_onRemove(); itFiles->second->uninit(); } mData.mFiles.clear(); LogFlowThisFunc(("Closing processes (%zu total)\n", mData.mProcesses.size())); for (SessionProcesses::iterator itProcs = mData.mProcesses.begin(); itProcs != mData.mProcesses.end(); ++itProcs) { Assert(mData.mNumObjects); mData.mNumObjects--; itProcs->second->i_onRemove(); itProcs->second->uninit(); } mData.mProcesses.clear(); mData.mEnvironment.reset(); AssertMsg(mData.mNumObjects == 0, ("mNumObjects=%RU32 when it should be 0\n", mData.mNumObjects)); baseUninit(); #endif /* VBOX_WITH_GUEST_CONTROL */ LogFlowFuncLeaveRC(rc); } // implementation of public getters/setters for attributes ///////////////////////////////////////////////////////////////////////////// HRESULT GuestSession::getUser(com::Utf8Str &aUser) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); aUser = mData.mCredentials.mUser; LogFlowThisFuncLeave(); return S_OK; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::getDomain(com::Utf8Str &aDomain) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); aDomain = mData.mCredentials.mDomain; LogFlowThisFuncLeave(); return S_OK; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::getName(com::Utf8Str &aName) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); aName = mData.mSession.mName; LogFlowThisFuncLeave(); return S_OK; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::getId(ULONG *aId) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); *aId = mData.mSession.mID; LogFlowThisFuncLeave(); return S_OK; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::getStatus(GuestSessionStatus_T *aStatus) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); *aStatus = mData.mStatus; LogFlowThisFuncLeave(); return S_OK; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::getTimeout(ULONG *aTimeout) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); *aTimeout = mData.mTimeout; LogFlowThisFuncLeave(); return S_OK; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::setTimeout(ULONG aTimeout) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); mData.mTimeout = aTimeout; LogFlowThisFuncLeave(); return S_OK; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::getProtocolVersion(ULONG *aProtocolVersion) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); *aProtocolVersion = mData.mProtocolVersion; LogFlowThisFuncLeave(); return S_OK; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::getEnvironment(std::vector &aEnvironment) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); int vrc = mData.mEnvironment.queryPutEnvArray(&aEnvironment); LogFlowFuncLeaveRC(vrc); return Global::vboxStatusCodeToCOM(vrc); #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::setEnvironment(const std::vector &aEnvironment) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); mData.mEnvironment.reset(); int vrc = mData.mEnvironment.applyPutEnvArray(aEnvironment); LogFlowFuncLeaveRC(vrc); return Global::vboxStatusCodeToCOM(vrc); #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::getProcesses(std::vector > &aProcesses) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); aProcesses.resize(mData.mProcesses.size()); size_t i = 0; for(SessionProcesses::iterator it = mData.mProcesses.begin(); it != mData.mProcesses.end(); ++it, ++i) { it->second.queryInterfaceTo(aProcesses[i].asOutParam()); } LogFlowFunc(("mProcesses=%zu\n", aProcesses.size())); return S_OK; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::getDirectories(std::vector > &aDirectories) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); aDirectories.resize(mData.mDirectories.size()); size_t i = 0; for(SessionDirectories::iterator it = mData.mDirectories.begin(); it != mData.mDirectories.end(); ++it, ++i) { it->second.queryInterfaceTo(aDirectories[i].asOutParam()); } LogFlowFunc(("mDirectories=%zu\n", aDirectories.size())); return S_OK; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::getFiles(std::vector > &aFiles) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); aFiles.resize(mData.mFiles.size()); size_t i = 0; for(SessionFiles::iterator it = mData.mFiles.begin(); it != mData.mFiles.end(); ++it, ++i) it->second.queryInterfaceTo(aFiles[i].asOutParam()); LogFlowFunc(("mDirectories=%zu\n", aFiles.size())); return S_OK; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::getEventSource(ComPtr &aEventSource) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); // no need to lock - lifetime constant mEventSource.queryInterfaceTo(aEventSource.asOutParam()); LogFlowThisFuncLeave(); return S_OK; #endif /* VBOX_WITH_GUEST_CONTROL */ } // private methods /////////////////////////////////////////////////////////////////////////////// int GuestSession::i_closeSession(uint32_t uFlags, uint32_t uTimeoutMS, int *pGuestRc) { LogFlowThisFunc(("uFlags=%x, uTimeoutMS=%RU32\n", uFlags, uTimeoutMS)); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); /* Guest Additions < 4.3 don't support closing dedicated guest sessions, skip. */ if (mData.mProtocolVersion < 2) { LogFlowThisFunc(("Installed Guest Additions don't support closing dedicated sessions, skipping\n")); return VINF_SUCCESS; } /** @todo uFlags validation. */ if (mData.mStatus != GuestSessionStatus_Started) { LogFlowThisFunc(("Session ID=%RU32 not started (anymore), status now is: %RU32\n", mData.mSession.mID, mData.mStatus)); return VINF_SUCCESS; } int vrc; GuestWaitEvent *pEvent = NULL; GuestEventTypes eventTypes; try { eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged); vrc = registerWaitEvent(mData.mSession.mID, 0 /* Object ID */, eventTypes, &pEvent); } catch (std::bad_alloc) { vrc = VERR_NO_MEMORY; } if (RT_FAILURE(vrc)) return vrc; LogFlowThisFunc(("Sending closing request to guest session ID=%RU32, uFlags=%x\n", mData.mSession.mID, uFlags)); VBOXHGCMSVCPARM paParms[4]; int i = 0; paParms[i++].setUInt32(pEvent->ContextID()); paParms[i++].setUInt32(uFlags); alock.release(); /* Drop the write lock before waiting. */ vrc = i_sendCommand(HOST_SESSION_CLOSE, i, paParms); if (RT_SUCCESS(vrc)) vrc = i_waitForStatusChange(pEvent, GuestSessionWaitForFlag_Terminate, uTimeoutMS, NULL /* Session status */, pGuestRc); unregisterWaitEvent(pEvent); LogFlowFuncLeaveRC(vrc); return vrc; } int GuestSession::i_directoryCreateInternal(const Utf8Str &strPath, uint32_t uMode, uint32_t uFlags, int *pGuestRc) { LogFlowThisFunc(("strPath=%s, uMode=%x, uFlags=%x\n", strPath.c_str(), uMode, uFlags)); int vrc = VINF_SUCCESS; GuestProcessStartupInfo procInfo; procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_MKDIR); procInfo.mFlags = ProcessCreateFlag_Hidden; try { /* Construct arguments. */ if (uFlags) { if (uFlags & DirectoryCreateFlag_Parents) procInfo.mArguments.push_back(Utf8Str("--parents")); /* We also want to create the parent directories. */ else vrc = VERR_INVALID_PARAMETER; } if (uMode) { procInfo.mArguments.push_back(Utf8Str("--mode")); /* Set the creation mode. */ char szMode[16]; if (RTStrPrintf(szMode, sizeof(szMode), "%o", uMode)) { procInfo.mArguments.push_back(Utf8Str(szMode)); } else vrc = VERR_BUFFER_OVERFLOW; } procInfo.mArguments.push_back(strPath); /* The directory we want to create. */ } catch (std::bad_alloc) { vrc = VERR_NO_MEMORY; } if (RT_SUCCESS(vrc)) vrc = GuestProcessTool::i_run(this, procInfo, pGuestRc); LogFlowFuncLeaveRC(vrc); return vrc; } inline bool GuestSession::i_directoryExists(uint32_t uDirID, ComObjPtr *pDir) { SessionDirectories::const_iterator it = mData.mDirectories.find(uDirID); if (it != mData.mDirectories.end()) { if (pDir) *pDir = it->second; return true; } return false; } int GuestSession::i_directoryQueryInfoInternal(const Utf8Str &strPath, GuestFsObjData &objData, int *pGuestRc) { LogFlowThisFunc(("strPath=%s\n", strPath.c_str())); int vrc = i_fsQueryInfoInternal(strPath, objData, pGuestRc); if (RT_SUCCESS(vrc)) { vrc = objData.mType == FsObjType_Directory ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY; } LogFlowFuncLeaveRC(vrc); return vrc; } int GuestSession::i_directoryRemoveFromList(GuestDirectory *pDirectory) { AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); int rc = VERR_NOT_FOUND; SessionDirectories::iterator itDirs = mData.mDirectories.begin(); while (itDirs != mData.mDirectories.end()) { if (pDirectory == itDirs->second) { /* Make sure to consume the pointer before the one of the * iterator gets released. */ ComObjPtr pDir = pDirectory; Bstr strName; HRESULT hr = itDirs->second->COMGETTER(DirectoryName)(strName.asOutParam()); ComAssertComRC(hr); Assert(mData.mDirectories.size()); Assert(mData.mNumObjects); LogFlowFunc(("Removing directory \"%s\" (Session: %RU32) (now total %zu processes, %RU32 objects)\n", Utf8Str(strName).c_str(), mData.mSession.mID, mData.mDirectories.size() - 1, mData.mNumObjects - 1)); rc = pDirectory->i_onRemove(); mData.mDirectories.erase(itDirs); mData.mNumObjects--; pDir.setNull(); break; } itDirs++; } LogFlowFuncLeaveRC(rc); return rc; } int GuestSession::i_directoryRemoveInternal(const Utf8Str &strPath, uint32_t uFlags, int *pGuestRc) { AssertReturn(!(uFlags & ~DIRREMOVE_FLAG_VALID_MASK), VERR_INVALID_PARAMETER); LogFlowThisFunc(("strPath=%s, uFlags=0x%x\n", strPath.c_str(), uFlags)); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); GuestWaitEvent *pEvent = NULL; int vrc = registerWaitEvent(mData.mSession.mID, 0 /* Object ID */, &pEvent); if (RT_FAILURE(vrc)) return vrc; /* Prepare HGCM call. */ VBOXHGCMSVCPARM paParms[8]; int i = 0; paParms[i++].setUInt32(pEvent->ContextID()); paParms[i++].setPointer((void*)strPath.c_str(), (ULONG)strPath.length() + 1); paParms[i++].setUInt32(uFlags); alock.release(); /* Drop write lock before sending. */ vrc = i_sendCommand(HOST_DIR_REMOVE, i, paParms); if (RT_SUCCESS(vrc)) { vrc = pEvent->Wait(30 * 1000); if ( vrc == VERR_GSTCTL_GUEST_ERROR && pGuestRc) *pGuestRc = pEvent->GuestResult(); } unregisterWaitEvent(pEvent); LogFlowFuncLeaveRC(vrc); return vrc; } int GuestSession::i_objectCreateTempInternal(const Utf8Str &strTemplate, const Utf8Str &strPath, bool fDirectory, Utf8Str &strName, int *pGuestRc) { LogFlowThisFunc(("strTemplate=%s, strPath=%s, fDirectory=%RTbool\n", strTemplate.c_str(), strPath.c_str(), fDirectory)); int vrc = VINF_SUCCESS; GuestProcessStartupInfo procInfo; procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_MKTEMP); procInfo.mFlags = ProcessCreateFlag_WaitForStdOut; try { procInfo.mArguments.push_back(Utf8Str("--machinereadable")); if (fDirectory) procInfo.mArguments.push_back(Utf8Str("-d")); if (strPath.length()) /* Otherwise use /tmp or equivalent. */ { procInfo.mArguments.push_back(Utf8Str("-t")); procInfo.mArguments.push_back(strPath); } procInfo.mArguments.push_back(strTemplate); } catch (std::bad_alloc) { vrc = VERR_NO_MEMORY; } /** @todo Use an internal HGCM command for this operation, since * we now can run in a user-dedicated session. */ int guestRc; GuestCtrlStreamObjects stdOut; if (RT_SUCCESS(vrc)) vrc = GuestProcessTool::i_runEx(this, procInfo, &stdOut, 1 /* cStrmOutObjects */, &guestRc); if ( RT_SUCCESS(vrc) && RT_SUCCESS(guestRc)) { GuestFsObjData objData; if (!stdOut.empty()) { vrc = objData.FromMkTemp(stdOut.at(0)); if (RT_FAILURE(vrc)) { guestRc = vrc; vrc = VERR_GSTCTL_GUEST_ERROR; } } else vrc = VERR_NO_DATA; if (RT_SUCCESS(vrc)) strName = objData.mName; } if (pGuestRc) *pGuestRc = guestRc; LogFlowFuncLeaveRC(vrc); return vrc; } int GuestSession::i_directoryOpenInternal(const GuestDirectoryOpenInfo &openInfo, ComObjPtr &pDirectory, int *pGuestRc) { LogFlowThisFunc(("strPath=%s, strPath=%s, uFlags=%x\n", openInfo.mPath.c_str(), openInfo.mFilter.c_str(), openInfo.mFlags)); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); int rc = VERR_MAX_PROCS_REACHED; if (mData.mNumObjects >= VBOX_GUESTCTRL_MAX_OBJECTS) return rc; /* Create a new (host-based) directory ID and assign it. */ uint32_t uNewDirID = 0; ULONG uTries = 0; for (;;) { /* Is the directory ID already used? */ if (!i_directoryExists(uNewDirID, NULL /* pDirectory */)) { /* Callback with context ID was not found. This means * we can use this context ID for our new callback we want * to add below. */ rc = VINF_SUCCESS; break; } uNewDirID++; if (uNewDirID == VBOX_GUESTCTRL_MAX_OBJECTS) uNewDirID = 0; if (++uTries == UINT32_MAX) break; /* Don't try too hard. */ } if (RT_FAILURE(rc)) return rc; /* Create the directory object. */ HRESULT hr = pDirectory.createObject(); if (FAILED(hr)) return VERR_COM_UNEXPECTED; Console *pConsole = mParent->i_getConsole(); AssertPtr(pConsole); int vrc = pDirectory->init(pConsole, this /* Parent */, uNewDirID, openInfo); if (RT_FAILURE(vrc)) return vrc; /* * Since this is a synchronous guest call we have to * register the file object first, releasing the session's * lock and then proceed with the actual opening command * -- otherwise the file's opening callback would hang * because the session's lock still is in place. */ try { /* Add the created directory to our map. */ mData.mDirectories[uNewDirID] = pDirectory; mData.mNumObjects++; Assert(mData.mNumObjects <= VBOX_GUESTCTRL_MAX_OBJECTS); LogFlowFunc(("Added new guest directory \"%s\" (Session: %RU32) (now total %zu dirs, %RU32 objects)\n", openInfo.mPath.c_str(), mData.mSession.mID, mData.mFiles.size(), mData.mNumObjects)); alock.release(); /* Release lock before firing off event. */ /** @todo Fire off a VBoxEventType_OnGuestDirectoryRegistered event? */ } catch (std::bad_alloc &) { rc = VERR_NO_MEMORY; } if (RT_SUCCESS(rc)) { /* Nothing further to do here yet. */ if (pGuestRc) *pGuestRc = VINF_SUCCESS; } LogFlowFuncLeaveRC(vrc); return vrc; } int GuestSession::i_dispatchToDirectory(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb) { LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb)); AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER); AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER); if (pSvcCb->mParms < 3) return VERR_INVALID_PARAMETER; AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); uint32_t uDirID = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCtxCb->uContextID); #ifdef DEBUG LogFlowFunc(("uDirID=%RU32 (%zu total)\n", uDirID, mData.mFiles.size())); #endif int rc; SessionDirectories::const_iterator itDir = mData.mDirectories.find(uDirID); if (itDir != mData.mDirectories.end()) { ComObjPtr pDirectory(itDir->second); Assert(!pDirectory.isNull()); alock.release(); rc = pDirectory->i_callbackDispatcher(pCtxCb, pSvcCb); } else rc = VERR_NOT_FOUND; LogFlowFuncLeaveRC(rc); return rc; } int GuestSession::i_dispatchToFile(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb) { LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb)); AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER); AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); uint32_t uFileID = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCtxCb->uContextID); #ifdef DEBUG LogFlowFunc(("uFileID=%RU32 (%zu total)\n", uFileID, mData.mFiles.size())); #endif int rc; SessionFiles::const_iterator itFile = mData.mFiles.find(uFileID); if (itFile != mData.mFiles.end()) { ComObjPtr pFile(itFile->second); Assert(!pFile.isNull()); alock.release(); rc = pFile->i_callbackDispatcher(pCtxCb, pSvcCb); } else rc = VERR_NOT_FOUND; LogFlowFuncLeaveRC(rc); return rc; } int GuestSession::i_dispatchToObject(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb) { LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb)); AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER); AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER); int rc; uint32_t uObjectID = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCtxCb->uContextID); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); /* Since we don't know which type the object is, we need to through all * all objects. */ /** @todo Speed this up by adding an object type to the callback context! */ SessionProcesses::const_iterator itProc = mData.mProcesses.find(uObjectID); if (itProc == mData.mProcesses.end()) { SessionFiles::const_iterator itFile = mData.mFiles.find(uObjectID); if (itFile != mData.mFiles.end()) { alock.release(); rc = i_dispatchToFile(pCtxCb, pSvcCb); } else { SessionDirectories::const_iterator itDir = mData.mDirectories.find(uObjectID); if (itDir != mData.mDirectories.end()) { alock.release(); rc = i_dispatchToDirectory(pCtxCb, pSvcCb); } else rc = VERR_NOT_FOUND; } } else { alock.release(); rc = i_dispatchToProcess(pCtxCb, pSvcCb); } LogFlowFuncLeaveRC(rc); return rc; } int GuestSession::i_dispatchToProcess(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb) { LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb)); AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER); AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); uint32_t uProcessID = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCtxCb->uContextID); #ifdef DEBUG LogFlowFunc(("uProcessID=%RU32 (%zu total)\n", uProcessID, mData.mProcesses.size())); #endif int rc; SessionProcesses::const_iterator itProc = mData.mProcesses.find(uProcessID); if (itProc != mData.mProcesses.end()) { #ifdef DEBUG_andy ULONG cRefs = itProc->second->AddRef(); Assert(cRefs >= 2); LogFlowFunc(("pProcess=%p, cRefs=%RU32\n", &itProc->second, cRefs - 1)); itProc->second->Release(); #endif ComObjPtr pProcess(itProc->second); Assert(!pProcess.isNull()); /* Set protocol version so that pSvcCb can * be interpreted right. */ pCtxCb->uProtocol = mData.mProtocolVersion; alock.release(); rc = pProcess->i_callbackDispatcher(pCtxCb, pSvcCb); } else rc = VERR_NOT_FOUND; LogFlowFuncLeaveRC(rc); return rc; } int GuestSession::i_dispatchToThis(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb) { AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER); AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); #ifdef DEBUG LogFlowThisFunc(("sessionID=%RU32, CID=%RU32, uFunction=%RU32, pSvcCb=%p\n", mData.mSession.mID, pCbCtx->uContextID, pCbCtx->uFunction, pSvcCb)); #endif int rc; switch (pCbCtx->uFunction) { case GUEST_DISCONNECTED: /** @todo Handle closing all guest objects. */ rc = VERR_INTERNAL_ERROR; break; case GUEST_SESSION_NOTIFY: /* Guest Additions >= 4.3.0. */ { rc = i_onSessionStatusChange(pCbCtx, pSvcCb); break; } default: /* Silently skip unknown callbacks. */ rc = VERR_NOT_SUPPORTED; break; } LogFlowFuncLeaveRC(rc); return rc; } inline bool GuestSession::i_fileExists(uint32_t uFileID, ComObjPtr *pFile) { SessionFiles::const_iterator it = mData.mFiles.find(uFileID); if (it != mData.mFiles.end()) { if (pFile) *pFile = it->second; return true; } return false; } int GuestSession::i_fileRemoveFromList(GuestFile *pFile) { AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); int rc = VERR_NOT_FOUND; SessionFiles::iterator itFiles = mData.mFiles.begin(); while (itFiles != mData.mFiles.end()) { if (pFile == itFiles->second) { /* Make sure to consume the pointer before the one of thfe * iterator gets released. */ ComObjPtr pCurFile = pFile; Bstr strName; HRESULT hr = pCurFile->COMGETTER(FileName)(strName.asOutParam()); ComAssertComRC(hr); Assert(mData.mNumObjects); LogFlowThisFunc(("Removing guest file \"%s\" (Session: %RU32) (now total %zu files, %RU32 objects)\n", Utf8Str(strName).c_str(), mData.mSession.mID, mData.mFiles.size() - 1, mData.mNumObjects - 1)); rc = pFile->i_onRemove(); mData.mFiles.erase(itFiles); mData.mNumObjects--; alock.release(); /* Release lock before firing off event. */ fireGuestFileRegisteredEvent(mEventSource, this, pCurFile, false /* Unregistered */); pCurFile.setNull(); break; } itFiles++; } LogFlowFuncLeaveRC(rc); return rc; } int GuestSession::i_fileRemoveInternal(const Utf8Str &strPath, int *pGuestRc) { LogFlowThisFunc(("strPath=%s\n", strPath.c_str())); int vrc = VINF_SUCCESS; GuestProcessStartupInfo procInfo; GuestProcessStream streamOut; procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_RM); procInfo.mFlags = ProcessCreateFlag_WaitForStdOut; try { procInfo.mArguments.push_back(Utf8Str("--machinereadable")); procInfo.mArguments.push_back(strPath); /* The file we want to remove. */ } catch (std::bad_alloc) { vrc = VERR_NO_MEMORY; } if (RT_SUCCESS(vrc)) vrc = GuestProcessTool::i_run(this, procInfo, pGuestRc); LogFlowFuncLeaveRC(vrc); return vrc; } int GuestSession::i_fileOpenInternal(const GuestFileOpenInfo &openInfo, ComObjPtr &pFile, int *pGuestRc) { LogFlowThisFunc(("strPath=%s, strOpenMode=%s, strDisposition=%s, uCreationMode=%x, uOffset=%RU64\n", openInfo.mFileName.c_str(), openInfo.mOpenMode.c_str(), openInfo.mDisposition.c_str(), openInfo.mCreationMode, openInfo.mInitialOffset)); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); /* Guest Additions < 4.3 don't support handling guest files, skip. */ if (mData.mProtocolVersion < 2) { LogFlowThisFunc(("Installed Guest Additions don't support handling guest files, skipping\n")); return VERR_NOT_SUPPORTED; } int rc = VERR_MAX_PROCS_REACHED; if (mData.mNumObjects >= VBOX_GUESTCTRL_MAX_OBJECTS) return rc; /* Create a new (host-based) file ID and assign it. */ uint32_t uNewFileID = 0; ULONG uTries = 0; for (;;) { /* Is the file ID already used? */ if (!i_fileExists(uNewFileID, NULL /* pFile */)) { /* Callback with context ID was not found. This means * we can use this context ID for our new callback we want * to add below. */ rc = VINF_SUCCESS; break; } uNewFileID++; if (uNewFileID == VBOX_GUESTCTRL_MAX_OBJECTS) uNewFileID = 0; if (++uTries == UINT32_MAX) break; /* Don't try too hard. */ } if (RT_FAILURE(rc)) return rc; /* Create the directory object. */ HRESULT hr = pFile.createObject(); if (FAILED(hr)) return VERR_COM_UNEXPECTED; Console *pConsole = mParent->i_getConsole(); AssertPtr(pConsole); rc = pFile->init(pConsole, this /* GuestSession */, uNewFileID, openInfo); if (RT_FAILURE(rc)) return rc; /* * Since this is a synchronous guest call we have to * register the file object first, releasing the session's * lock and then proceed with the actual opening command * -- otherwise the file's opening callback would hang * because the session's lock still is in place. */ try { /* Add the created file to our vector. */ mData.mFiles[uNewFileID] = pFile; mData.mNumObjects++; Assert(mData.mNumObjects <= VBOX_GUESTCTRL_MAX_OBJECTS); LogFlowFunc(("Added new guest file \"%s\" (Session: %RU32) (now total %zu files, %RU32 objects)\n", openInfo.mFileName.c_str(), mData.mSession.mID, mData.mFiles.size(), mData.mNumObjects)); alock.release(); /* Release lock before firing off event. */ fireGuestFileRegisteredEvent(mEventSource, this, pFile, true /* Registered */); } catch (std::bad_alloc &) { rc = VERR_NO_MEMORY; } if (RT_SUCCESS(rc)) { int guestRc; rc = pFile->i_openFile(30 * 1000 /* 30s timeout */, &guestRc); if ( rc == VERR_GSTCTL_GUEST_ERROR && pGuestRc) { *pGuestRc = guestRc; } } LogFlowFuncLeaveRC(rc); return rc; } int GuestSession::i_fileQueryInfoInternal(const Utf8Str &strPath, GuestFsObjData &objData, int *pGuestRc) { LogFlowThisFunc(("strPath=%s\n", strPath.c_str())); int vrc = i_fsQueryInfoInternal(strPath, objData, pGuestRc); if (RT_SUCCESS(vrc)) { vrc = objData.mType == FsObjType_File ? VINF_SUCCESS : VERR_NOT_A_FILE; } LogFlowFuncLeaveRC(vrc); return vrc; } int GuestSession::i_fileQuerySizeInternal(const Utf8Str &strPath, int64_t *pllSize, int *pGuestRc) { AssertPtrReturn(pllSize, VERR_INVALID_POINTER); GuestFsObjData objData; int vrc = i_fileQueryInfoInternal(strPath, objData, pGuestRc); if (RT_SUCCESS(vrc)) *pllSize = objData.mObjectSize; return vrc; } int GuestSession::i_fsQueryInfoInternal(const Utf8Str &strPath, GuestFsObjData &objData, int *pGuestRc) { LogFlowThisFunc(("strPath=%s\n", strPath.c_str())); int vrc = VINF_SUCCESS; /** @todo Merge this with IGuestFile::queryInfo(). */ GuestProcessStartupInfo procInfo; procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_STAT); procInfo.mFlags = ProcessCreateFlag_WaitForStdOut; try { /* Construct arguments. */ procInfo.mArguments.push_back(Utf8Str("--machinereadable")); procInfo.mArguments.push_back(strPath); } catch (std::bad_alloc) { vrc = VERR_NO_MEMORY; } int guestRc; GuestCtrlStreamObjects stdOut; if (RT_SUCCESS(vrc)) vrc = GuestProcessTool::i_runEx(this, procInfo, &stdOut, 1 /* cStrmOutObjects */, &guestRc); if ( RT_SUCCESS(vrc) && RT_SUCCESS(guestRc)) { if (!stdOut.empty()) vrc = objData.FromStat(stdOut.at(0)); else vrc = VERR_NO_DATA; } if ( vrc == VERR_GSTCTL_GUEST_ERROR && pGuestRc) *pGuestRc = guestRc; LogFlowThisFunc(("Returning rc=%Rrc, guestRc=%Rrc\n", vrc, guestRc)); return vrc; } const GuestCredentials& GuestSession::i_getCredentials(void) { return mData.mCredentials; } Utf8Str GuestSession::i_getName(void) { return mData.mSession.mName; } /* static */ Utf8Str GuestSession::i_guestErrorToString(int guestRc) { Utf8Str strError; /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */ switch (guestRc) { case VERR_INVALID_VM_HANDLE: strError += Utf8StrFmt(tr("VMM device is not available (is the VM running?)")); break; case VERR_HGCM_SERVICE_NOT_FOUND: strError += Utf8StrFmt(tr("The guest execution service is not available")); break; case VERR_AUTHENTICATION_FAILURE: strError += Utf8StrFmt(tr("The specified user was not able to logon on guest")); break; case VERR_TIMEOUT: strError += Utf8StrFmt(tr("The guest did not respond within time")); break; case VERR_CANCELLED: strError += Utf8StrFmt(tr("The session operation was canceled")); break; case VERR_PERMISSION_DENIED: strError += Utf8StrFmt(tr("Invalid user/password credentials")); break; case VERR_MAX_PROCS_REACHED: strError += Utf8StrFmt(tr("Maximum number of concurrent guest processes has been reached")); break; case VERR_NOT_EQUAL: /** @todo Imprecise to the user; can mean anything and all. */ strError += Utf8StrFmt(tr("Unable to retrieve requested information")); break; case VERR_NOT_FOUND: strError += Utf8StrFmt(tr("The guest execution service is not ready (yet)")); break; default: strError += Utf8StrFmt("%Rrc", guestRc); break; } return strError; } /** * Checks if this session is ready state where it can handle * all session-bound actions (like guest processes, guest files). * Only used by official API methods. Will set an external * error when not ready. */ HRESULT GuestSession::i_isReadyExternal(void) { AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); /** @todo Be a bit more informative. */ if (mData.mStatus != GuestSessionStatus_Started) return setError(E_UNEXPECTED, tr("Session is not in started state")); return S_OK; } /** * Called by IGuest right before this session gets removed from * the public session list. */ int GuestSession::i_onRemove(void) { LogFlowThisFuncEnter(); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); int vrc = VINF_SUCCESS; /* * Note: The event source stuff holds references to this object, * so make sure that this is cleaned up *before* calling uninit. */ if (!mEventSource.isNull()) { mEventSource->UnregisterListener(mLocalListener); mLocalListener.setNull(); unconst(mEventSource).setNull(); } LogFlowFuncLeaveRC(vrc); return vrc; } /** No locking! */ int GuestSession::i_onSessionStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData) { AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER); /* pCallback is optional. */ AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER); if (pSvcCbData->mParms < 3) return VERR_INVALID_PARAMETER; CALLBACKDATA_SESSION_NOTIFY dataCb; /* pSvcCb->mpaParms[0] always contains the context ID. */ int vrc = pSvcCbData->mpaParms[1].getUInt32(&dataCb.uType); AssertRCReturn(vrc, vrc); vrc = pSvcCbData->mpaParms[2].getUInt32(&dataCb.uResult); AssertRCReturn(vrc, vrc); LogFlowThisFunc(("ID=%RU32, uType=%RU32, guestRc=%Rrc\n", mData.mSession.mID, dataCb.uType, dataCb.uResult)); GuestSessionStatus_T sessionStatus = GuestSessionStatus_Undefined; int guestRc = dataCb.uResult; /** @todo uint32_t vs. int. */ switch (dataCb.uType) { case GUEST_SESSION_NOTIFYTYPE_ERROR: sessionStatus = GuestSessionStatus_Error; break; case GUEST_SESSION_NOTIFYTYPE_STARTED: sessionStatus = GuestSessionStatus_Started; break; case GUEST_SESSION_NOTIFYTYPE_TEN: case GUEST_SESSION_NOTIFYTYPE_TES: case GUEST_SESSION_NOTIFYTYPE_TEA: sessionStatus = GuestSessionStatus_Terminated; break; case GUEST_SESSION_NOTIFYTYPE_TOK: sessionStatus = GuestSessionStatus_TimedOutKilled; break; case GUEST_SESSION_NOTIFYTYPE_TOA: sessionStatus = GuestSessionStatus_TimedOutAbnormally; break; case GUEST_SESSION_NOTIFYTYPE_DWN: sessionStatus = GuestSessionStatus_Down; break; case GUEST_SESSION_NOTIFYTYPE_UNDEFINED: default: vrc = VERR_NOT_SUPPORTED; break; } if (RT_SUCCESS(vrc)) { if (RT_FAILURE(guestRc)) sessionStatus = GuestSessionStatus_Error; } /* Set the session status. */ if (RT_SUCCESS(vrc)) vrc = i_setSessionStatus(sessionStatus, guestRc); LogFlowThisFunc(("ID=%RU32, guestRc=%Rrc\n", mData.mSession.mID, guestRc)); LogFlowFuncLeaveRC(vrc); return vrc; } int GuestSession::i_startSessionInternal(int *pGuestRc) { AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); LogFlowThisFunc(("mID=%RU32, mName=%s, uProtocolVersion=%RU32, openFlags=%x, openTimeoutMS=%RU32\n", mData.mSession.mID, mData.mSession.mName.c_str(), mData.mProtocolVersion, mData.mSession.mOpenFlags, mData.mSession.mOpenTimeoutMS)); /* Guest Additions < 4.3 don't support opening dedicated guest sessions. Simply return success here. */ if (mData.mProtocolVersion < 2) { mData.mStatus = GuestSessionStatus_Started; LogFlowThisFunc(("Installed Guest Additions don't support opening dedicated sessions, skipping\n")); return VINF_SUCCESS; } if (mData.mStatus != GuestSessionStatus_Undefined) return VINF_SUCCESS; /** @todo mData.mSession.uFlags validation. */ /* Set current session status. */ mData.mStatus = GuestSessionStatus_Starting; mData.mRC = VINF_SUCCESS; /* Clear previous error, if any. */ int vrc; GuestWaitEvent *pEvent = NULL; GuestEventTypes eventTypes; try { eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged); vrc = registerWaitEvent(mData.mSession.mID, 0 /* Object ID */, eventTypes, &pEvent); } catch (std::bad_alloc) { vrc = VERR_NO_MEMORY; } if (RT_FAILURE(vrc)) return vrc; VBOXHGCMSVCPARM paParms[8]; int i = 0; paParms[i++].setUInt32(pEvent->ContextID()); paParms[i++].setUInt32(mData.mProtocolVersion); paParms[i++].setPointer((void*)mData.mCredentials.mUser.c_str(), (ULONG)mData.mCredentials.mUser.length() + 1); paParms[i++].setPointer((void*)mData.mCredentials.mPassword.c_str(), (ULONG)mData.mCredentials.mPassword.length() + 1); paParms[i++].setPointer((void*)mData.mCredentials.mDomain.c_str(), (ULONG)mData.mCredentials.mDomain.length() + 1); paParms[i++].setUInt32(mData.mSession.mOpenFlags); alock.release(); /* Drop write lock before sending. */ vrc = i_sendCommand(HOST_SESSION_CREATE, i, paParms); if (RT_SUCCESS(vrc)) { vrc = i_waitForStatusChange(pEvent, GuestSessionWaitForFlag_Start, 30 * 1000 /* 30s timeout */, NULL /* Session status */, pGuestRc); } else { /* * Unable to start guest session - update its current state. * Since there is no (official API) way to recover a failed guest session * this also marks the end state. Internally just calling this * same function again will work though. */ mData.mStatus = GuestSessionStatus_Error; mData.mRC = vrc; } unregisterWaitEvent(pEvent); LogFlowFuncLeaveRC(vrc); return vrc; } int GuestSession::i_startSessionAsync(void) { LogFlowThisFuncEnter(); int vrc; try { /* Asynchronously open the session on the guest by kicking off a * worker thread. */ std::auto_ptr pTask(new GuestSessionTaskInternalOpen(this)); AssertReturn(pTask->isOk(), pTask->rc()); vrc = RTThreadCreate(NULL, GuestSession::i_startSessionThread, (void *)pTask.get(), 0, RTTHREADTYPE_MAIN_WORKER, 0, "gctlSesStart"); if (RT_SUCCESS(vrc)) { /* pTask is now owned by openSessionThread(), so release it. */ pTask.release(); } } catch(std::bad_alloc &) { vrc = VERR_NO_MEMORY; } LogFlowFuncLeaveRC(vrc); return vrc; } /* static */ DECLCALLBACK(int) GuestSession::i_startSessionThread(RTTHREAD Thread, void *pvUser) { LogFlowFunc(("pvUser=%p\n", pvUser)); std::auto_ptr pTask(static_cast(pvUser)); AssertPtr(pTask.get()); const ComObjPtr pSession(pTask->Session()); Assert(!pSession.isNull()); AutoCaller autoCaller(pSession); if (FAILED(autoCaller.rc())) return autoCaller.rc(); int vrc = pSession->i_startSessionInternal(NULL /* Guest rc, ignored */); /* Nothing to do here anymore. */ LogFlowFuncLeaveRC(vrc); return vrc; } int GuestSession::i_pathRenameInternal(const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags, int *pGuestRc) { AssertReturn(!(uFlags & ~PATHRENAME_FLAG_VALID_MASK), VERR_INVALID_PARAMETER); LogFlowThisFunc(("strSource=%s, strDest=%s, uFlags=0x%x\n", strSource.c_str(), strDest.c_str(), uFlags)); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); GuestWaitEvent *pEvent = NULL; int vrc = registerWaitEvent(mData.mSession.mID, 0 /* Object ID */, &pEvent); if (RT_FAILURE(vrc)) return vrc; /* Prepare HGCM call. */ VBOXHGCMSVCPARM paParms[8]; int i = 0; paParms[i++].setUInt32(pEvent->ContextID()); paParms[i++].setPointer((void*)strSource.c_str(), (ULONG)strSource.length() + 1); paParms[i++].setPointer((void*)strDest.c_str(), (ULONG)strDest.length() + 1); paParms[i++].setUInt32(uFlags); alock.release(); /* Drop write lock before sending. */ vrc = i_sendCommand(HOST_PATH_RENAME, i, paParms); if (RT_SUCCESS(vrc)) { vrc = pEvent->Wait(30 * 1000); if ( vrc == VERR_GSTCTL_GUEST_ERROR && pGuestRc) *pGuestRc = pEvent->GuestResult(); } unregisterWaitEvent(pEvent); LogFlowFuncLeaveRC(vrc); return vrc; } int GuestSession::i_processRemoveFromList(GuestProcess *pProcess) { AssertPtrReturn(pProcess, VERR_INVALID_POINTER); LogFlowThisFunc(("pProcess=%p\n", pProcess)); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); int rc = VERR_NOT_FOUND; ULONG uPID; HRESULT hr = pProcess->COMGETTER(PID)(&uPID); ComAssertComRC(hr); LogFlowFunc(("Removing process (PID=%RU32) ...\n", uPID)); SessionProcesses::iterator itProcs = mData.mProcesses.begin(); while (itProcs != mData.mProcesses.end()) { if (pProcess == itProcs->second) { #ifdef DEBUG_andy ULONG cRefs = pProcess->AddRef(); Assert(cRefs >= 2); LogFlowFunc(("pProcess=%p, cRefs=%RU32\n", pProcess, cRefs - 1)); pProcess->Release(); #endif /* Make sure to consume the pointer before the one of the * iterator gets released. */ ComObjPtr pProc = pProcess; hr = pProc->COMGETTER(PID)(&uPID); ComAssertComRC(hr); Assert(mData.mProcesses.size()); Assert(mData.mNumObjects); LogFlowFunc(("Removing process ID=%RU32 (Session: %RU32), guest PID=%RU32 (now total %zu processes, %RU32 objects)\n", pProcess->getObjectID(), mData.mSession.mID, uPID, mData.mProcesses.size() - 1, mData.mNumObjects - 1)); rc = pProcess->i_onRemove(); mData.mProcesses.erase(itProcs); mData.mNumObjects--; alock.release(); /* Release lock before firing off event. */ fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProc, uPID, false /* Process unregistered */); pProc.setNull(); break; } itProcs++; } LogFlowFuncLeaveRC(rc); return rc; } /** * Creates but does *not* start the process yet. * * See GuestProcess::startProcess() or GuestProcess::startProcessAsync() for * starting the process. * * @return IPRT status code. * @param procInfo * @param pProcess */ int GuestSession::i_processCreateExInternal(GuestProcessStartupInfo &procInfo, ComObjPtr &pProcess) { LogFlowFunc(("mExe=%s, mFlags=%x, mTimeoutMS=%RU32\n", procInfo.mExecutable.c_str(), procInfo.mFlags, procInfo.mTimeoutMS)); #ifdef DEBUG if (procInfo.mArguments.size()) { LogFlowFunc(("Arguments:")); ProcessArguments::const_iterator it = procInfo.mArguments.begin(); while (it != procInfo.mArguments.end()) { LogFlow((" %s", (*it).c_str())); it++; } LogFlow(("\n")); } #endif /* Validate flags. */ if (procInfo.mFlags) { if ( !(procInfo.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses) && !(procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly) && !(procInfo.mFlags & ProcessCreateFlag_Hidden) && !(procInfo.mFlags & ProcessCreateFlag_NoProfile) && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdOut) && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdErr)) { return VERR_INVALID_PARAMETER; } } if ( (procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly) && ( (procInfo.mFlags & ProcessCreateFlag_WaitForStdOut) || (procInfo.mFlags & ProcessCreateFlag_WaitForStdErr) ) ) { return VERR_INVALID_PARAMETER; } /* Adjust timeout. If set to 0, we define * an infinite timeout. */ if (procInfo.mTimeoutMS == 0) procInfo.mTimeoutMS = UINT32_MAX; /** @tood Implement process priority + affinity. */ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); int rc = VERR_MAX_PROCS_REACHED; if (mData.mNumObjects >= VBOX_GUESTCTRL_MAX_OBJECTS) return rc; /* Create a new (host-based) process ID and assign it. */ uint32_t uNewProcessID = 0; ULONG uTries = 0; for (;;) { /* Is the context ID already used? */ if (!i_processExists(uNewProcessID, NULL /* pProcess */)) { /* Callback with context ID was not found. This means * we can use this context ID for our new callback we want * to add below. */ rc = VINF_SUCCESS; break; } uNewProcessID++; if (uNewProcessID == VBOX_GUESTCTRL_MAX_OBJECTS) uNewProcessID = 0; if (++uTries == VBOX_GUESTCTRL_MAX_OBJECTS) break; /* Don't try too hard. */ } if (RT_FAILURE(rc)) return rc; /* Create the process object. */ HRESULT hr = pProcess.createObject(); if (FAILED(hr)) return VERR_COM_UNEXPECTED; rc = pProcess->init(mParent->i_getConsole() /* Console */, this /* Session */, uNewProcessID, procInfo); if (RT_FAILURE(rc)) return rc; /* Add the created process to our map. */ try { mData.mProcesses[uNewProcessID] = pProcess; mData.mNumObjects++; Assert(mData.mNumObjects <= VBOX_GUESTCTRL_MAX_OBJECTS); LogFlowFunc(("Added new process (Session: %RU32) with process ID=%RU32 (now total %zu processes, %RU32 objects)\n", mData.mSession.mID, uNewProcessID, mData.mProcesses.size(), mData.mNumObjects)); alock.release(); /* Release lock before firing off event. */ fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProcess, 0 /* PID */, true /* Process registered */); } catch (std::bad_alloc &) { rc = VERR_NO_MEMORY; } return rc; } inline bool GuestSession::i_processExists(uint32_t uProcessID, ComObjPtr *pProcess) { SessionProcesses::const_iterator it = mData.mProcesses.find(uProcessID); if (it != mData.mProcesses.end()) { if (pProcess) *pProcess = it->second; return true; } return false; } inline int GuestSession::i_processGetByPID(ULONG uPID, ComObjPtr *pProcess) { AssertReturn(uPID, false); /* pProcess is optional. */ SessionProcesses::iterator itProcs = mData.mProcesses.begin(); for (; itProcs != mData.mProcesses.end(); itProcs++) { ComObjPtr pCurProc = itProcs->second; AutoCaller procCaller(pCurProc); if (procCaller.rc()) return VERR_COM_INVALID_OBJECT_STATE; ULONG uCurPID; HRESULT hr = pCurProc->COMGETTER(PID)(&uCurPID); ComAssertComRC(hr); if (uCurPID == uPID) { if (pProcess) *pProcess = pCurProc; return VINF_SUCCESS; } } return VERR_NOT_FOUND; } int GuestSession::i_sendCommand(uint32_t uFunction, uint32_t uParms, PVBOXHGCMSVCPARM paParms) { LogFlowThisFuncEnter(); #ifndef VBOX_GUESTCTRL_TEST_CASE ComObjPtr pConsole = mParent->i_getConsole(); Assert(!pConsole.isNull()); /* Forward the information to the VMM device. */ VMMDev *pVMMDev = pConsole->i_getVMMDev(); AssertPtr(pVMMDev); LogFlowThisFunc(("uFunction=%RU32, uParms=%RU32\n", uFunction, uParms)); int vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uFunction, uParms, paParms); if (RT_FAILURE(vrc)) { /** @todo What to do here? */ } #else /* Not needed within testcases. */ int vrc = VINF_SUCCESS; #endif LogFlowFuncLeaveRC(vrc); return vrc; } /* static */ HRESULT GuestSession::i_setErrorExternal(VirtualBoxBase *pInterface, int guestRc) { AssertPtr(pInterface); AssertMsg(RT_FAILURE(guestRc), ("Guest rc does not indicate a failure when setting error\n")); return pInterface->setError(VBOX_E_IPRT_ERROR, GuestSession::i_guestErrorToString(guestRc).c_str()); } /* Does not do locking; caller is responsible for that! */ int GuestSession::i_setSessionStatus(GuestSessionStatus_T sessionStatus, int sessionRc) { LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, sessionRc=%Rrc\n", mData.mStatus, sessionStatus, sessionRc)); if (sessionStatus == GuestSessionStatus_Error) { AssertMsg(RT_FAILURE(sessionRc), ("Guest rc must be an error (%Rrc)\n", sessionRc)); /* Do not allow overwriting an already set error. If this happens * this means we forgot some error checking/locking somewhere. */ AssertMsg(RT_SUCCESS(mData.mRC), ("Guest rc already set (to %Rrc)\n", mData.mRC)); } else AssertMsg(RT_SUCCESS(sessionRc), ("Guest rc must not be an error (%Rrc)\n", sessionRc)); if (mData.mStatus != sessionStatus) { mData.mStatus = sessionStatus; mData.mRC = sessionRc; ComObjPtr errorInfo; HRESULT hr = errorInfo.createObject(); ComAssertComRC(hr); int rc2 = errorInfo->initEx(VBOX_E_IPRT_ERROR, sessionRc, COM_IIDOF(IGuestSession), getComponentName(), i_guestErrorToString(sessionRc)); AssertRC(rc2); fireGuestSessionStateChangedEvent(mEventSource, this, mData.mSession.mID, sessionStatus, errorInfo); } return VINF_SUCCESS; } int GuestSession::i_signalWaiters(GuestSessionWaitResult_T enmWaitResult, int rc /*= VINF_SUCCESS */) { /*LogFlowThisFunc(("enmWaitResult=%d, rc=%Rrc, mWaitCount=%RU32, mWaitEvent=%p\n", enmWaitResult, rc, mData.mWaitCount, mData.mWaitEvent));*/ /* Note: No write locking here -- already done in the caller. */ int vrc = VINF_SUCCESS; /*if (mData.mWaitEvent) vrc = mData.mWaitEvent->Signal(enmWaitResult, rc);*/ LogFlowFuncLeaveRC(vrc); return vrc; } int GuestSession::i_startTaskAsync(const Utf8Str &strTaskDesc, GuestSessionTask *pTask, ComObjPtr &pProgress) { LogFlowThisFunc(("strTaskDesc=%s, pTask=%p\n", strTaskDesc.c_str(), pTask)); AssertPtrReturn(pTask, VERR_INVALID_POINTER); /* Create the progress object. */ HRESULT hr = pProgress.createObject(); if (FAILED(hr)) return VERR_COM_UNEXPECTED; hr = pProgress->init(static_cast(this), Bstr(strTaskDesc).raw(), TRUE /* aCancelable */); if (FAILED(hr)) return VERR_COM_UNEXPECTED; /* Initialize our worker task. */ std::auto_ptr task(pTask); int rc = task->RunAsync(strTaskDesc, pProgress); if (RT_FAILURE(rc)) return rc; /* Don't destruct on success. */ task.release(); LogFlowFuncLeaveRC(rc); return rc; } /** * Determines the protocol version (sets mData.mProtocolVersion). * * This is called from the init method prior to to establishing a guest * session. * * @return IPRT status code. */ int GuestSession::i_determineProtocolVersion(void) { /* * We currently do this based on the reported guest additions version, * ASSUMING that VBoxService and VBoxDrv are at the same version. */ ComObjPtr pGuest = mParent; AssertReturn(!pGuest.isNull(), VERR_NOT_SUPPORTED); uint32_t uGaVersion = pGuest->i_getAdditionsVersion(); /* Everyone supports version one, if they support anything at all. */ mData.mProtocolVersion = 1; /* Guest control 2.0 was introduced with 4.3.0. */ if (uGaVersion >= VBOX_FULL_VERSION_MAKE(4,3,0)) mData.mProtocolVersion = 2; /* Guest control 2.0. */ LogFlowThisFunc(("uGaVersion=%u.%u.%u => mProtocolVersion=%u\n", VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion), VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion)); /* * Inform the user about outdated guest additions (VM release log). */ if (mData.mProtocolVersion < 2) LogRelMax(3, (tr("Warning: Guest Additions v%u.%u.%u only supports the older guest control protocol version %u.\n" " Please upgrade GAs to the current version to get full guest control capabilities.\n"), VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion), VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion)); return VINF_SUCCESS; } int GuestSession::i_waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, GuestSessionWaitResult_T &waitResult, int *pGuestRc) { LogFlowThisFuncEnter(); AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER); /*LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p, pGuestRc=%p\n", fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent, pGuestRc));*/ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); /* Did some error occur before? Then skip waiting and return. */ if (mData.mStatus == GuestSessionStatus_Error) { waitResult = GuestSessionWaitResult_Error; AssertMsg(RT_FAILURE(mData.mRC), ("No error rc (%Rrc) set when guest session indicated an error\n", mData.mRC)); if (pGuestRc) *pGuestRc = mData.mRC; /* Return last set error. */ return VERR_GSTCTL_GUEST_ERROR; } /* Guest Additions < 4.3 don't support session handling, skip. */ if (mData.mProtocolVersion < 2) { waitResult = GuestSessionWaitResult_WaitFlagNotSupported; LogFlowThisFunc(("Installed Guest Additions don't support waiting for dedicated sessions, skipping\n")); return VINF_SUCCESS; } waitResult = GuestSessionWaitResult_None; if (fWaitFlags & GuestSessionWaitForFlag_Terminate) { switch (mData.mStatus) { case GuestSessionStatus_Terminated: case GuestSessionStatus_Down: waitResult = GuestSessionWaitResult_Terminate; break; case GuestSessionStatus_TimedOutKilled: case GuestSessionStatus_TimedOutAbnormally: waitResult = GuestSessionWaitResult_Timeout; break; case GuestSessionStatus_Error: /* Handled above. */ break; case GuestSessionStatus_Started: waitResult = GuestSessionWaitResult_Start; break; case GuestSessionStatus_Undefined: case GuestSessionStatus_Starting: /* Do the waiting below. */ break; default: AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus)); return VERR_NOT_IMPLEMENTED; } } else if (fWaitFlags & GuestSessionWaitForFlag_Start) { switch (mData.mStatus) { case GuestSessionStatus_Started: case GuestSessionStatus_Terminating: case GuestSessionStatus_Terminated: case GuestSessionStatus_Down: waitResult = GuestSessionWaitResult_Start; break; case GuestSessionStatus_Error: waitResult = GuestSessionWaitResult_Error; break; case GuestSessionStatus_TimedOutKilled: case GuestSessionStatus_TimedOutAbnormally: waitResult = GuestSessionWaitResult_Timeout; break; case GuestSessionStatus_Undefined: case GuestSessionStatus_Starting: /* Do the waiting below. */ break; default: AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus)); return VERR_NOT_IMPLEMENTED; } } LogFlowThisFunc(("sessionStatus=%RU32, sessionRc=%Rrc, waitResult=%RU32\n", mData.mStatus, mData.mRC, waitResult)); /* No waiting needed? Return immediately using the last set error. */ if (waitResult != GuestSessionWaitResult_None) { if (pGuestRc) *pGuestRc = mData.mRC; /* Return last set error (if any). */ return RT_SUCCESS(mData.mRC) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR; } int vrc; GuestWaitEvent *pEvent = NULL; GuestEventTypes eventTypes; try { eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged); vrc = registerWaitEvent(mData.mSession.mID, 0 /* Object ID */, eventTypes, &pEvent); } catch (std::bad_alloc) { vrc = VERR_NO_MEMORY; } if (RT_FAILURE(vrc)) return vrc; alock.release(); /* Release lock before waiting. */ GuestSessionStatus_T sessionStatus; vrc = i_waitForStatusChange(pEvent, fWaitFlags, uTimeoutMS, &sessionStatus, pGuestRc); if (RT_SUCCESS(vrc)) { switch (sessionStatus) { case GuestSessionStatus_Started: waitResult = GuestSessionWaitResult_Start; break; case GuestSessionStatus_Terminated: waitResult = GuestSessionWaitResult_Terminate; break; case GuestSessionStatus_TimedOutKilled: case GuestSessionStatus_TimedOutAbnormally: waitResult = GuestSessionWaitResult_Timeout; break; case GuestSessionStatus_Down: waitResult = GuestSessionWaitResult_Terminate; break; case GuestSessionStatus_Error: waitResult = GuestSessionWaitResult_Error; break; default: waitResult = GuestSessionWaitResult_Status; break; } } unregisterWaitEvent(pEvent); LogFlowFuncLeaveRC(vrc); return vrc; } int GuestSession::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t fWaitFlags, uint32_t uTimeoutMS, GuestSessionStatus_T *pSessionStatus, int *pGuestRc) { AssertPtrReturn(pEvent, VERR_INVALID_POINTER); VBoxEventType_T evtType; ComPtr pIEvent; int vrc = waitForEvent(pEvent, uTimeoutMS, &evtType, pIEvent.asOutParam()); if (RT_SUCCESS(vrc)) { Assert(evtType == VBoxEventType_OnGuestSessionStateChanged); ComPtr pChangedEvent = pIEvent; Assert(!pChangedEvent.isNull()); GuestSessionStatus_T sessionStatus; pChangedEvent->COMGETTER(Status)(&sessionStatus); if (pSessionStatus) *pSessionStatus = sessionStatus; ComPtr errorInfo; HRESULT hr = pChangedEvent->COMGETTER(Error)(errorInfo.asOutParam()); ComAssertComRC(hr); LONG lGuestRc; hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc); ComAssertComRC(hr); if (RT_FAILURE((int)lGuestRc)) vrc = VERR_GSTCTL_GUEST_ERROR; if (pGuestRc) *pGuestRc = (int)lGuestRc; LogFlowThisFunc(("Status changed event for session ID=%RU32, new status is: %RU32 (%Rrc)\n", mData.mSession.mID, sessionStatus, RT_SUCCESS((int)lGuestRc) ? VINF_SUCCESS : (int)lGuestRc)); } LogFlowFuncLeaveRC(vrc); return vrc; } // implementation of public methods ///////////////////////////////////////////////////////////////////////////// HRESULT GuestSession::close() { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); /* Close session on guest. */ int guestRc = VINF_SUCCESS; int rc = i_closeSession(0 /* Flags */, 30 * 1000 /* Timeout */, &guestRc); /* On failure don't return here, instead do all the cleanup * work first and then return an error. */ /* Remove ourselves from the session list. */ int rc2 = mParent->i_sessionRemove(this); if (rc2 == VERR_NOT_FOUND) /* Not finding the session anymore isn't critical. */ rc2 = VINF_SUCCESS; if (RT_SUCCESS(rc)) rc = rc2; LogFlowThisFunc(("Returning rc=%Rrc, guestRc=%Rrc\n", rc, guestRc)); if (RT_FAILURE(rc)) { if (rc == VERR_GSTCTL_GUEST_ERROR) return GuestSession::i_setErrorExternal(this, guestRc); return setError(VBOX_E_IPRT_ERROR, tr("Closing guest session failed with %Rrc"), rc); } return S_OK; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::copyFrom(const com::Utf8Str &aSource, const com::Utf8Str &aDest, const std::vector &aFlags, ComPtr &aProgress) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); if (RT_UNLIKELY((aSource.c_str()) == NULL || *(aSource.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No source specified")); if (RT_UNLIKELY((aDest.c_str()) == NULL || *(aDest.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No destination specified")); uint32_t fFlags = CopyFileFlag_None; if (aFlags.size()) { for (size_t i = 0; i < aFlags.size(); i++) fFlags |= aFlags[i]; } AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); HRESULT hr = S_OK; try { ComObjPtr pProgress; SessionTaskCopyFrom *pTask = new SessionTaskCopyFrom(this /* GuestSession */, aSource, aDest, fFlags); int rc = i_startTaskAsync(Utf8StrFmt(tr("Copying \"%s\" from guest to \"%s\" on the host"), aSource.c_str(), aDest.c_str()), pTask, pProgress); if (RT_SUCCESS(rc)) /* Return progress to the caller. */ hr = pProgress.queryInterfaceTo(aProgress.asOutParam()); else hr = setError(VBOX_E_IPRT_ERROR, tr("Starting task for copying file \"%s\" from guest to \"%s\" on the host failed: %Rrc"), rc); } catch(std::bad_alloc &) { hr = E_OUTOFMEMORY; } return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::copyTo(const com::Utf8Str &aSource, const com::Utf8Str &aDest, const std::vector &aFlags, ComPtr &aProgress) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); if (RT_UNLIKELY((aSource.c_str()) == NULL || *(aSource.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No source specified")); if (RT_UNLIKELY((aDest.c_str()) == NULL || *(aDest.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No destination specified")); uint32_t fFlags = CopyFileFlag_None; if (aFlags.size()) { for (size_t i = 0; i < aFlags.size(); i++) fFlags |= aFlags[i]; } AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); HRESULT hr = S_OK; try { ComObjPtr pProgress; SessionTaskCopyTo *pTask = new SessionTaskCopyTo(this /* GuestSession */, aSource, aDest, fFlags); AssertPtrReturn(pTask, E_OUTOFMEMORY); int rc = i_startTaskAsync(Utf8StrFmt(tr("Copying \"%s\" from host to \"%s\" on the guest"), aSource.c_str(), aDest.c_str()), pTask, pProgress); if (RT_SUCCESS(rc)) { /* Return progress to the caller. */ hr = pProgress.queryInterfaceTo(aProgress.asOutParam()); } else hr = setError(VBOX_E_IPRT_ERROR, tr("Starting task for copying file \"%s\" from host to \"%s\" on the guest failed: %Rrc"), rc); } catch(std::bad_alloc &) { hr = E_OUTOFMEMORY; } return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::directoryCreate(const com::Utf8Str &aPath, ULONG aMode, const std::vector &aFlags) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No directory to create specified")); uint32_t fFlags = DirectoryCreateFlag_None; if (aFlags.size()) { for (size_t i = 0; i < aFlags.size(); i++) fFlags |= aFlags[i]; if (fFlags) if (!(fFlags & DirectoryCreateFlag_Parents)) return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), fFlags); } HRESULT hr = S_OK; ComObjPtr pDirectory; int guestRc; int rc = i_directoryCreateInternal(aPath, (uint32_t)aMode, fFlags, &guestRc); if (RT_FAILURE(rc)) { switch (rc) { case VERR_GSTCTL_GUEST_ERROR: /** @todo Handle VERR_NOT_EQUAL (meaning process exit code <> 0). */ hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Could not create directory")); break; case VERR_INVALID_PARAMETER: hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Invalid parameters given")); break; case VERR_BROKEN_PIPE: hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Unexpectedly aborted")); break; default: hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: %Rrc"), rc); break; } } return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::directoryCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath, BOOL aSecure, com::Utf8Str &aDirectory) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); if (RT_UNLIKELY((aTemplateName.c_str()) == NULL || *(aTemplateName.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No template specified")); if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No directory name specified")); HRESULT hr = S_OK; int guestRc; int rc = i_objectCreateTempInternal(aTemplateName, aPath, true /* Directory */, aDirectory, &guestRc); if (!RT_SUCCESS(rc)) { switch (rc) { case VERR_GSTCTL_GUEST_ERROR: hr = GuestProcess::i_setErrorExternal(this, guestRc); break; default: hr = setError(VBOX_E_IPRT_ERROR, tr("Temporary directory creation \"%s\" with template \"%s\" failed: %Rrc"), aPath.c_str(), aTemplateName.c_str(), rc); break; } } return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::directoryExists(const com::Utf8Str &aPath, BOOL *aExists) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No directory to check existence for specified")); HRESULT hr = S_OK; GuestFsObjData objData; int guestRc; int rc = i_directoryQueryInfoInternal(aPath, objData, &guestRc); if (RT_SUCCESS(rc)) *aExists = objData.mType == FsObjType_Directory; else { switch (rc) { case VERR_GSTCTL_GUEST_ERROR: hr = GuestProcess::i_setErrorExternal(this, guestRc); break; default: hr = setError(VBOX_E_IPRT_ERROR, tr("Querying directory existence \"%s\" failed: %Rrc"), aPath.c_str(), rc); break; } } return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::directoryOpen(const com::Utf8Str &aPath, const com::Utf8Str &aFilter, const std::vector &aFlags, ComPtr &aDirectory) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No directory to open specified")); if (RT_UNLIKELY((aFilter.c_str()) != NULL && *(aFilter.c_str()) != '\0')) return setError(E_INVALIDARG, tr("Directory filters are not implemented yet")); uint32_t fFlags = DirectoryOpenFlag_None; if (aFlags.size()) { for (size_t i = 0; i < aFlags.size(); i++) fFlags |= aFlags[i]; if (fFlags) return setError(E_INVALIDARG, tr("Open flags (%#x) not implemented yet"), fFlags); } HRESULT hr = S_OK; GuestDirectoryOpenInfo openInfo; openInfo.mPath = aPath; openInfo.mFilter = aFilter; openInfo.mFlags = fFlags; ComObjPtr pDirectory; int guestRc; int rc = i_directoryOpenInternal(openInfo, pDirectory, &guestRc); if (RT_SUCCESS(rc)) { /* Return directory object to the caller. */ hr = pDirectory.queryInterfaceTo(aDirectory.asOutParam()); } else { switch (rc) { case VERR_INVALID_PARAMETER: hr = setError(VBOX_E_IPRT_ERROR, tr("Opening directory \"%s\" failed; invalid parameters given"), aPath.c_str()); break; case VERR_GSTCTL_GUEST_ERROR: hr = GuestDirectory::i_setErrorExternal(this, guestRc); break; default: hr = setError(VBOX_E_IPRT_ERROR, tr("Opening directory \"%s\" failed: %Rrc"), aPath.c_str(),rc); break; } } return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::directoryQueryInfo(const com::Utf8Str &aPath, ComPtr &aInfo) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No directory to query information for specified")); HRESULT hr = S_OK; GuestFsObjData objData; int guestRc; int vrc = i_directoryQueryInfoInternal(aPath, objData, &guestRc); if (RT_SUCCESS(vrc)) { if (objData.mType == FsObjType_Directory) { ComObjPtr pFsObjInfo; hr = pFsObjInfo.createObject(); if (FAILED(hr)) return hr; vrc = pFsObjInfo->init(objData); if (RT_SUCCESS(vrc)) { hr = pFsObjInfo.queryInterfaceTo(aInfo.asOutParam()); if (FAILED(hr)) return hr; } } } if (RT_FAILURE(vrc)) { switch (vrc) { case VERR_GSTCTL_GUEST_ERROR: hr = GuestProcess::i_setErrorExternal(this, guestRc); break; case VERR_NOT_A_DIRECTORY: hr = setError(VBOX_E_IPRT_ERROR, tr("Element \"%s\" exists but is not a directory"), aPath.c_str()); break; default: hr = setError(VBOX_E_IPRT_ERROR, tr("Querying directory information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc); break; } } return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::directoryRemove(const com::Utf8Str &aPath) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No directory to remove specified")); HRESULT hr = i_isReadyExternal(); if (FAILED(hr)) return hr; /* No flags; only remove the directory when empty. */ uint32_t uFlags = 0; int guestRc; int vrc = i_directoryRemoveInternal(aPath, uFlags, &guestRc); if (RT_FAILURE(vrc)) { switch (vrc) { case VERR_NOT_SUPPORTED: hr = setError(VBOX_E_IPRT_ERROR, tr("Handling removing guest directories not supported by installed Guest Additions")); break; case VERR_GSTCTL_GUEST_ERROR: hr = GuestDirectory::i_setErrorExternal(this, guestRc); break; default: hr = setError(VBOX_E_IPRT_ERROR, tr("Removing guest directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc); break; } } return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::directoryRemoveRecursive(const com::Utf8Str &aPath, const std::vector &aFlags, ComPtr &aProgress) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No directory to remove recursively specified")); HRESULT hr = i_isReadyExternal(); if (FAILED(hr)) return hr; ComObjPtr pProgress; hr = pProgress.createObject(); if (SUCCEEDED(hr)) hr = pProgress->init(static_cast(this), Bstr(tr("Removing guest directory")).raw(), TRUE /*aCancelable*/); if (FAILED(hr)) return hr; /* Note: At the moment we don't supply progress information while * deleting a guest directory recursively. So just complete * the progress object right now. */ /** @todo Implement progress reporting on guest directory deletion! */ hr = pProgress->i_notifyComplete(S_OK); if (FAILED(hr)) return hr; /* Remove the directory + all its contents. */ uint32_t uFlags = DIRREMOVE_FLAG_RECURSIVE | DIRREMOVE_FLAG_CONTENT_AND_DIR; int guestRc; int vrc = i_directoryRemoveInternal(aPath, uFlags, &guestRc); if (RT_FAILURE(vrc)) { switch (vrc) { case VERR_NOT_SUPPORTED: hr = setError(VBOX_E_IPRT_ERROR, tr("Handling removing guest directories recursively not supported by installed Guest Additions")); break; case VERR_GSTCTL_GUEST_ERROR: hr = GuestFile::i_setErrorExternal(this, guestRc); break; default: hr = setError(VBOX_E_IPRT_ERROR, tr("Recursively removing guest directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc); break; } } else { pProgress.queryInterfaceTo(aProgress.asOutParam()); } return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::directoryRename(const com::Utf8Str &aSource, const com::Utf8Str &aDest, const std::vector &aFlags) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); if (RT_UNLIKELY((aSource.c_str()) == NULL || *(aSource.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No source directory to rename specified")); if (RT_UNLIKELY((aDest.c_str()) == NULL || *(aDest.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No destination directory to rename the source to specified")); HRESULT hr = i_isReadyExternal(); if (FAILED(hr)) return hr; /* No flags; only remove the directory when empty. */ uint32_t uFlags = 0; int guestRc; int vrc = i_pathRenameInternal(aSource, aDest, uFlags, &guestRc); if (RT_FAILURE(vrc)) { switch (vrc) { case VERR_NOT_SUPPORTED: hr = setError(VBOX_E_IPRT_ERROR, tr("Handling renaming guest directories not supported by installed Guest Additions")); break; case VERR_GSTCTL_GUEST_ERROR: hr = setError(VBOX_E_IPRT_ERROR, tr("Renaming guest directory failed: %Rrc"), guestRc); break; default: hr = setError(VBOX_E_IPRT_ERROR, tr("Renaming guest directory \"%s\" failed: %Rrc"), aSource.c_str(), vrc); break; } } return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::directorySetACL(const com::Utf8Str &aPath, const com::Utf8Str &aAcl) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); ReturnComNotImplemented(); #endif /* VBOX_WITH_GUEST_CONTROL */ } /** @todo remove this it duplicates the 'environment' attribute. */ HRESULT GuestSession::environmentClear() { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); mData.mEnvironment.reset(); LogFlowThisFuncLeave(); return S_OK; #endif /* VBOX_WITH_GUEST_CONTROL */ } /** @todo Remove this because the interface isn't suitable for returning 'unset' * or empty values, and it can easily be misunderstood. Besides there * is hardly a usecase for it as long as it just works on * environment changes and there is the 'environment' attribute. */ HRESULT GuestSession::environmentGet(const com::Utf8Str &aName, com::Utf8Str &aValue) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); HRESULT hrc; if (RT_LIKELY(aName.isNotEmpty())) { if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL)) { AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); mData.mEnvironment.getVariable(aName, &aValue); hrc = S_OK; } else hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names")); } else hrc = setError(E_INVALIDARG, tr("No variable name specified")); LogFlowThisFuncLeave(); return hrc; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::environmentSet(const com::Utf8Str &aName, const com::Utf8Str &aValue) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); HRESULT hrc; if (RT_LIKELY(aName.isNotEmpty())) { if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL)) { AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); int vrc = mData.mEnvironment.setVariable(aName, aValue); if (RT_SUCCESS(vrc)) hrc = S_OK; else hrc = setErrorVrc(vrc); } else hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names")); } else hrc = setError(E_INVALIDARG, tr("No variable name specified")); LogFlowThisFuncLeave(); return hrc; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::environmentUnset(const com::Utf8Str &aName) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); HRESULT hrc; if (RT_LIKELY(aName.isNotEmpty())) { if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL)) { AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); int vrc = mData.mEnvironment.unsetVariable(aName); if (RT_SUCCESS(vrc)) hrc = S_OK; else hrc = setErrorVrc(vrc); } else hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names")); } else hrc = setError(E_INVALIDARG, tr("No variable name specified")); LogFlowThisFuncLeave(); return hrc; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::fileCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath, BOOL aSecure, ComPtr &aFile) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); ReturnComNotImplemented(); #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::fileExists(const com::Utf8Str &aPath, BOOL *aExists) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No file to check existence for specified")); GuestFsObjData objData; int guestRc; int vrc = i_fileQueryInfoInternal(aPath, objData, &guestRc); if (RT_SUCCESS(vrc)) { *aExists = TRUE; return S_OK; } HRESULT hr = S_OK; switch (vrc) { case VERR_GSTCTL_GUEST_ERROR: hr = GuestProcess::i_setErrorExternal(this, guestRc); break; case VERR_NOT_A_FILE: *aExists = FALSE; break; default: hr = setError(VBOX_E_IPRT_ERROR, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc); break; } return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::fileRemove(const com::Utf8Str &aPath) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No file to remove specified")); HRESULT hr = S_OK; int guestRc; int vrc = i_fileRemoveInternal(aPath, &guestRc); if (RT_FAILURE(vrc)) { switch (vrc) { case VERR_GSTCTL_GUEST_ERROR: hr = GuestProcess::i_setErrorExternal(this, guestRc); break; default: hr = setError(VBOX_E_IPRT_ERROR, tr("Removing file \"%s\" failed: %Rrc"), aPath.c_str(), vrc); break; } } return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::fileOpen(const com::Utf8Str &aPath, const com::Utf8Str &aOpenMode, const com::Utf8Str &aDisposition, ULONG aCreationMode, ComPtr &aFile) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); Utf8Str strSharingMode = ""; /* Sharing mode is ignored. */ return fileOpenEx(aPath, aOpenMode, aDisposition, strSharingMode, aCreationMode, 0 /* aOffset */, aFile); #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::fileOpenEx(const com::Utf8Str &aPath, const com::Utf8Str &aOpenMode, const com::Utf8Str &aDisposition, const com::Utf8Str &aSharingMode, ULONG aCreationMode, LONG64 aOffset, ComPtr &aFile) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No file to open specified")); if (RT_UNLIKELY((aOpenMode.c_str()) == NULL || *(aOpenMode.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No open mode specified")); if (RT_UNLIKELY((aDisposition.c_str()) == NULL || *(aDisposition.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No disposition mode specified")); /* aSharingMode is optional. */ HRESULT hr = i_isReadyExternal(); if (FAILED(hr)) return hr; /** @todo Validate creation mode. */ uint32_t uCreationMode = 0; GuestFileOpenInfo openInfo; openInfo.mFileName = aPath; openInfo.mOpenMode = aOpenMode; openInfo.mDisposition = aDisposition; openInfo.mSharingMode = aSharingMode; openInfo.mCreationMode = aCreationMode; openInfo.mInitialOffset = aOffset; uint64_t uFlagsIgnored; int vrc = RTFileModeToFlagsEx(openInfo.mOpenMode.c_str(), openInfo.mDisposition.c_str(), openInfo.mSharingMode.c_str(), &uFlagsIgnored); if (RT_FAILURE(vrc)) return setError(E_INVALIDARG, tr("Invalid open mode / disposition / sharing mode specified")); ComObjPtr pFile; int guestRc; vrc = i_fileOpenInternal(openInfo, pFile, &guestRc); if (RT_SUCCESS(vrc)) /* Return directory object to the caller. */ hr = pFile.queryInterfaceTo(aFile.asOutParam()); else { switch (vrc) { case VERR_NOT_SUPPORTED: hr = setError(VBOX_E_IPRT_ERROR, tr("Handling guest files not supported by installed Guest Additions")); break; case VERR_GSTCTL_GUEST_ERROR: hr = GuestFile::i_setErrorExternal(this, guestRc); break; default: hr = setError(VBOX_E_IPRT_ERROR, tr("Opening guest file \"%s\" failed: %Rrc"), aPath.c_str(), vrc); break; } } return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::fileQueryInfo(const com::Utf8Str &aPath, ComPtr &aInfo) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No file to query information for specified")); HRESULT hr = S_OK; GuestFsObjData objData; int guestRc; int vrc = i_fileQueryInfoInternal(aPath, objData, &guestRc); if (RT_SUCCESS(vrc)) { ComObjPtr pFsObjInfo; hr = pFsObjInfo.createObject(); if (FAILED(hr)) return hr; vrc = pFsObjInfo->init(objData); if (RT_SUCCESS(vrc)) { hr = pFsObjInfo.queryInterfaceTo(aInfo.asOutParam()); if (FAILED(hr)) return hr; } } if (RT_FAILURE(vrc)) { switch (vrc) { case VERR_GSTCTL_GUEST_ERROR: hr = GuestProcess::i_setErrorExternal(this, guestRc); break; case VERR_NOT_A_FILE: hr = setError(VBOX_E_IPRT_ERROR, tr("Element exists but is not a file")); break; default: hr = setError(VBOX_E_IPRT_ERROR, tr("Querying file information failed: %Rrc"), vrc); break; } } return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::fileQuerySize(const com::Utf8Str &aPath, LONG64 *aSize) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No file to query size for specified")); HRESULT hr = S_OK; int64_t llSize; int guestRc; int vrc = i_fileQuerySizeInternal(aPath, &llSize, &guestRc); if (RT_SUCCESS(vrc)) *aSize = llSize; else { switch (vrc) { case VERR_GSTCTL_GUEST_ERROR: hr = GuestProcess::i_setErrorExternal(this, guestRc); break; default: hr = setError(VBOX_E_IPRT_ERROR, tr("Querying file size failed: %Rrc"), vrc); break; } } return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::fileRename(const com::Utf8Str &aSource, const com::Utf8Str &aDest, const std::vector &aFlags) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); if (RT_UNLIKELY((aSource.c_str()) == NULL || *(aSource.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No source file to rename specified")); if (RT_UNLIKELY((aDest.c_str()) == NULL || *(aDest.c_str()) == '\0')) return setError(E_INVALIDARG, tr("No destination file to rename the source to specified")); HRESULT hr = i_isReadyExternal(); if (FAILED(hr)) return hr; /* No flags; only remove the directory when empty. */ uint32_t uFlags = 0; int guestRc; int vrc = i_pathRenameInternal(aSource, aDest, uFlags, &guestRc); if (RT_FAILURE(vrc)) { switch (vrc) { case VERR_NOT_SUPPORTED: hr = setError(VBOX_E_IPRT_ERROR, tr("Handling renaming guest files not supported by installed Guest Additions")); break; case VERR_GSTCTL_GUEST_ERROR: /** @todo Proper guestRc to text translation needed. */ hr = setError(VBOX_E_IPRT_ERROR, tr("Renaming guest file failed: %Rrc"), guestRc); break; default: hr = setError(VBOX_E_IPRT_ERROR, tr("Renaming guest file \"%s\" failed: %Rrc"), aSource.c_str(), vrc); break; } } return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::fileSetACL(const com::Utf8Str &aFile, const com::Utf8Str &aAcl) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); ReturnComNotImplemented(); #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::processCreate(const com::Utf8Str &aExecutable, const std::vector &aArguments, const std::vector &aEnvironment, const std::vector &aFlags, ULONG aTimeoutMS, ComPtr &aGuestProcess) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); std::vector affinityIgnored; return processCreateEx(aExecutable, aArguments, aEnvironment, aFlags, aTimeoutMS, ProcessPriority_Default, affinityIgnored, aGuestProcess); #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::processCreateEx(const com::Utf8Str &aExecutable, const std::vector &aArguments, const std::vector &aEnvironment, const std::vector &aFlags, ULONG aTimeoutMS, ProcessPriority_T aPriority, const std::vector &aAffinity, ComPtr &aGuestProcess) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); /** @todo r=bird: Check input better? aPriority is passed on to the guest * without any validation. Flags not existing in this vbox version are * ignored, potentially doing something entirely different than what the * caller had in mind. */ /* * Must have an executable to execute. If none is given, we try use the * zero'th argument. */ const char *pszExecutable = aExecutable.c_str(); if (RT_UNLIKELY(pszExecutable == NULL || *pszExecutable == '\0')) { if (aArguments.size() > 0) pszExecutable = aArguments[0].c_str(); if (pszExecutable == NULL || *pszExecutable == '\0') return setError(E_INVALIDARG, tr("No command to execute specified")); } /* * Check the session. */ HRESULT hr = i_isReadyExternal(); if (FAILED(hr)) return hr; /* * Build the process startup info. */ GuestProcessStartupInfo procInfo; /* Executable and arguments. */ procInfo.mExecutable = pszExecutable; if (aArguments.size()) for (size_t i = 0; i < aArguments.size(); i++) procInfo.mArguments.push_back(aArguments[i]); /* Combine the environment changes associated with the ones passed in by the caller, giving priority to the latter. The changes are putenv style and will be applied to the standard environment for the guest user. */ int vrc = procInfo.mEnvironment.copy(mData.mEnvironment); if (RT_SUCCESS(vrc)) vrc = procInfo.mEnvironment.applyPutEnvArray(aEnvironment); if (RT_SUCCESS(vrc)) { /* Convert the flag array into a mask. */ if (aFlags.size()) for (size_t i = 0; i < aFlags.size(); i++) procInfo.mFlags |= aFlags[i]; procInfo.mTimeoutMS = aTimeoutMS; /** @todo use RTCPUSET instead of archaic 64-bit variables! */ if (aAffinity.size()) for (size_t i = 0; i < aAffinity.size(); i++) if (aAffinity[i]) procInfo.mAffinity |= (uint64_t)1 << i; procInfo.mPriority = aPriority; /* * Create a guest process object. */ ComObjPtr pProcess; vrc = i_processCreateExInternal(procInfo, pProcess); if (RT_SUCCESS(vrc)) { /* Return guest session to the caller. */ hr = pProcess.queryInterfaceTo(aGuestProcess.asOutParam()); if (SUCCEEDED(hr)) { /* * Start the process. */ vrc = pProcess->i_startProcessAsync(); if (RT_SUCCESS(vrc)) { LogFlowFuncLeaveRC(vrc); return S_OK; } hr = setErrorVrc(vrc, tr("Failed to start guest process: %Rrc"), vrc); /** @todo r=bird: What happens to the interface that *aGuestProcess points to * now? Looks like a leak or an undocument hack of sorts... */ } } else if (vrc == VERR_MAX_PROCS_REACHED) hr = setErrorVrc(vrc, tr("Maximum number of concurrent guest processes per session (%u) reached"), VBOX_GUESTCTRL_MAX_OBJECTS); else hr = setErrorVrc(vrc, tr("Failed to create guest process object: %Rrc"), vrc); } else hr = setErrorVrc(vrc, tr("Failed to set up the environment: %Rrc"), vrc); LogFlowFuncLeaveRC(vrc); return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::processGet(ULONG aPid, ComPtr &aGuestProcess) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFunc(("PID=%RU32\n", aPid)); if (aPid == 0) return setError(E_INVALIDARG, tr("No valid process ID (PID) specified")); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); HRESULT hr = S_OK; ComObjPtr pProcess; int rc = i_processGetByPID(aPid, &pProcess); if (RT_FAILURE(rc)) hr = setError(E_INVALIDARG, tr("No process with PID %RU32 found"), aPid); /* This will set (*aProcess) to NULL if pProgress is NULL. */ HRESULT hr2 = pProcess.queryInterfaceTo(aGuestProcess.asOutParam()); if (SUCCEEDED(hr)) hr = hr2; LogFlowThisFunc(("aProcess=%p, hr=%Rhrc\n", (IGuestProcess*)aGuestProcess, hr)); return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::symlinkCreate(const com::Utf8Str &aSource, const com::Utf8Str &aTarget, SymlinkType_T aType) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); ReturnComNotImplemented(); #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::symlinkExists(const com::Utf8Str &aSymlink, BOOL *aExists) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); ReturnComNotImplemented(); #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::symlinkRead(const com::Utf8Str &aSymlink, const std::vector &aFlags, com::Utf8Str &aTarget) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); ReturnComNotImplemented(); #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::symlinkRemoveDirectory(const com::Utf8Str &aPath) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); ReturnComNotImplemented(); #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::symlinkRemoveFile(const com::Utf8Str &aFile) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); ReturnComNotImplemented(); #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); /* * Note: Do not hold any locks here while waiting! */ HRESULT hr = S_OK; int guestRc; GuestSessionWaitResult_T waitResult; int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &guestRc); if (RT_SUCCESS(vrc)) *aReason = waitResult; else { switch (vrc) { case VERR_GSTCTL_GUEST_ERROR: hr = GuestSession::i_setErrorExternal(this, guestRc); break; case VERR_TIMEOUT: *aReason = GuestSessionWaitResult_Timeout; break; default: { const char *pszSessionName = mData.mSession.mName.c_str(); hr = setError(VBOX_E_IPRT_ERROR, tr("Waiting for guest session \"%s\" failed: %Rrc"), pszSessionName ? pszSessionName : tr("Unnamed"), vrc); break; } } } LogFlowFuncLeaveRC(vrc); return hr; #endif /* VBOX_WITH_GUEST_CONTROL */ } HRESULT GuestSession::waitForArray(const std::vector &aWaitFor, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason) { #ifndef VBOX_WITH_GUEST_CONTROL ReturnComNotImplemented(); #else LogFlowThisFuncEnter(); /* * Note: Do not hold any locks here while waiting! */ uint32_t fWaitFor = GuestSessionWaitForFlag_None; for (size_t i = 0; i < aWaitFor.size(); i++) fWaitFor |= aWaitFor[i]; return WaitFor(fWaitFor, aTimeoutMS, aReason); #endif /* VBOX_WITH_GUEST_CONTROL */ }