VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestSessionImpl.cpp@ 55633

Last change on this file since 55633 was 55633, checked in by vboxsync, 10 years ago

typo in open action conversion.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 114.1 KB
Line 
1/* $Id: GuestSessionImpl.cpp 55633 2015-05-04 04:21:53Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Guest session handling.
4 */
5
6/*
7 * Copyright (C) 2012-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include "GuestImpl.h"
23#include "GuestSessionImpl.h"
24#include "GuestCtrlImplPrivate.h"
25#include "VirtualBoxErrorInfoImpl.h"
26
27#include "Global.h"
28#include "AutoCaller.h"
29#include "ProgressImpl.h"
30#include "VBoxEvents.h"
31#include "VMMDev.h"
32
33#include <memory> /* For auto_ptr. */
34
35#include <iprt/cpp/utils.h> /* For unconst(). */
36#include <iprt/env.h>
37#include <iprt/file.h> /* For CopyTo/From. */
38
39#include <VBox/com/array.h>
40#include <VBox/com/listeners.h>
41#include <VBox/version.h>
42
43#ifdef LOG_GROUP
44 #undef LOG_GROUP
45#endif
46#define LOG_GROUP LOG_GROUP_GUEST_CONTROL
47#include <VBox/log.h>
48
49
50/**
51 * Base class representing an internal
52 * asynchronous session task.
53 */
54class GuestSessionTaskInternal
55{
56public:
57
58 GuestSessionTaskInternal(GuestSession *pSession)
59 : mSession(pSession),
60 mRC(VINF_SUCCESS) { }
61
62 virtual ~GuestSessionTaskInternal(void) { }
63
64 int rc(void) const { return mRC; }
65 bool isOk(void) const { return RT_SUCCESS(mRC); }
66 const ComObjPtr<GuestSession> &Session(void) const { return mSession; }
67
68protected:
69
70 const ComObjPtr<GuestSession> mSession;
71 int mRC;
72};
73
74/**
75 * Class for asynchronously opening a guest session.
76 */
77class GuestSessionTaskInternalOpen : public GuestSessionTaskInternal
78{
79public:
80
81 GuestSessionTaskInternalOpen(GuestSession *pSession)
82 : GuestSessionTaskInternal(pSession) { }
83};
84
85/**
86 * Internal listener class to serve events in an
87 * active manner, e.g. without polling delays.
88 */
89class GuestSessionListener
90{
91public:
92
93 GuestSessionListener(void)
94 {
95 }
96
97 HRESULT init(GuestSession *pSession)
98 {
99 AssertPtrReturn(pSession, E_POINTER);
100 mSession = pSession;
101 return S_OK;
102 }
103
104 void uninit(void)
105 {
106 mSession = NULL;
107 }
108
109 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
110 {
111 switch (aType)
112 {
113 case VBoxEventType_OnGuestSessionStateChanged:
114 {
115 AssertPtrReturn(mSession, E_POINTER);
116 int rc2 = mSession->signalWaitEvent(aType, aEvent);
117#ifdef DEBUG_andy
118 LogFlowFunc(("Signalling events of type=%RU32, session=%p resulted in rc=%Rrc\n",
119 aType, mSession, rc2));
120#endif
121 break;
122 }
123
124 default:
125 AssertMsgFailed(("Unhandled event %RU32\n", aType));
126 break;
127 }
128
129 return S_OK;
130 }
131
132private:
133
134 GuestSession *mSession;
135};
136typedef ListenerImpl<GuestSessionListener, GuestSession*> GuestSessionListenerImpl;
137
138VBOX_LISTENER_DECLARE(GuestSessionListenerImpl)
139
140// constructor / destructor
141/////////////////////////////////////////////////////////////////////////////
142
143DEFINE_EMPTY_CTOR_DTOR(GuestSession)
144
145HRESULT GuestSession::FinalConstruct(void)
146{
147 LogFlowThisFuncEnter();
148 return BaseFinalConstruct();
149}
150
151void GuestSession::FinalRelease(void)
152{
153 LogFlowThisFuncEnter();
154 uninit();
155 BaseFinalRelease();
156 LogFlowThisFuncLeave();
157}
158
159// public initializer/uninitializer for internal purposes only
160/////////////////////////////////////////////////////////////////////////////
161
162/**
163 * Initializes a guest session but does *not* open in on the guest side
164 * yet. This needs to be done via the openSession() / openSessionAsync calls.
165 *
166 * @return IPRT status code.
167 ** @todo Docs!
168 */
169int GuestSession::init(Guest *pGuest, const GuestSessionStartupInfo &ssInfo,
170 const GuestCredentials &guestCreds)
171{
172 LogFlowThisFunc(("pGuest=%p, ssInfo=%p, guestCreds=%p\n",
173 pGuest, &ssInfo, &guestCreds));
174
175 /* Enclose the state transition NotReady->InInit->Ready. */
176 AutoInitSpan autoInitSpan(this);
177 AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
178
179#ifndef VBOX_WITH_GUEST_CONTROL
180 autoInitSpan.setSucceeded();
181 return VINF_SUCCESS;
182#else
183 AssertPtrReturn(pGuest, VERR_INVALID_POINTER);
184
185 /*
186 * Initialize our data members from the input.
187 */
188 mParent = pGuest;
189
190 /* Copy over startup info. */
191 /** @todo Use an overloaded copy operator. Later. */
192 mData.mSession.mID = ssInfo.mID;
193 mData.mSession.mIsInternal = ssInfo.mIsInternal;
194 mData.mSession.mName = ssInfo.mName;
195 mData.mSession.mOpenFlags = ssInfo.mOpenFlags;
196 mData.mSession.mOpenTimeoutMS = ssInfo.mOpenTimeoutMS;
197
198 /** @todo Use an overloaded copy operator. Later. */
199 mData.mCredentials.mUser = guestCreds.mUser;
200 mData.mCredentials.mPassword = guestCreds.mPassword;
201 mData.mCredentials.mDomain = guestCreds.mDomain;
202
203 /* Initialize the remainder of the data. */
204 mData.mRC = VINF_SUCCESS;
205 mData.mStatus = GuestSessionStatus_Undefined;
206 mData.mNumObjects = 0;
207 mData.mpBaseEnvironment = NULL;
208 int rc = mData.mEnvironmentChanges.initChangeRecord();
209 if (RT_SUCCESS(rc))
210 {
211 rc = RTCritSectInit(&mWaitEventCritSect);
212 AssertRC(rc);
213 }
214 if (RT_SUCCESS(rc))
215 rc = i_determineProtocolVersion();
216 if (RT_SUCCESS(rc))
217 {
218 /*
219 * <Replace this if you figure out what the code is doing.>
220 */
221 HRESULT hr = unconst(mEventSource).createObject();
222 if (SUCCEEDED(hr))
223 hr = mEventSource->init();
224 if (SUCCEEDED(hr))
225 {
226 try
227 {
228 GuestSessionListener *pListener = new GuestSessionListener();
229 ComObjPtr<GuestSessionListenerImpl> thisListener;
230 hr = thisListener.createObject();
231 if (SUCCEEDED(hr))
232 hr = thisListener->init(pListener, this);
233 if (SUCCEEDED(hr))
234 {
235 com::SafeArray <VBoxEventType_T> eventTypes;
236 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
237 hr = mEventSource->RegisterListener(thisListener,
238 ComSafeArrayAsInParam(eventTypes),
239 TRUE /* Active listener */);
240 if (SUCCEEDED(hr))
241 {
242 mLocalListener = thisListener;
243
244 /*
245 * Mark this object as operational and return success.
246 */
247 autoInitSpan.setSucceeded();
248 LogFlowThisFunc(("mName=%s mID=%RU32 mIsInternal=%RTbool rc=VINF_SUCCESS\n",
249 mData.mSession.mName.c_str(), mData.mSession.mID, mData.mSession.mIsInternal));
250 return VINF_SUCCESS;
251 }
252 }
253 }
254 catch (std::bad_alloc &)
255 {
256 hr = E_OUTOFMEMORY;
257 }
258 }
259 rc = Global::vboxStatusCodeFromCOM(hr);
260 }
261
262 autoInitSpan.setFailed();
263 LogThisFunc(("Failed! mName=%s mID=%RU32 mIsInternal=%RTbool => rc=%Rrc\n",
264 mData.mSession.mName.c_str(), mData.mSession.mID, mData.mSession.mIsInternal, rc));
265 return rc;
266#endif /* VBOX_WITH_GUEST_CONTROL */
267}
268
269/**
270 * Uninitializes the instance.
271 * Called from FinalRelease().
272 */
273void GuestSession::uninit(void)
274{
275 /* Enclose the state transition Ready->InUninit->NotReady. */
276 AutoUninitSpan autoUninitSpan(this);
277 if (autoUninitSpan.uninitDone())
278 return;
279
280 LogFlowThisFuncEnter();
281
282 int rc = VINF_SUCCESS;
283
284#ifdef VBOX_WITH_GUEST_CONTROL
285 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
286
287 LogFlowThisFunc(("Closing directories (%zu total)\n",
288 mData.mDirectories.size()));
289 for (SessionDirectories::iterator itDirs = mData.mDirectories.begin();
290 itDirs != mData.mDirectories.end(); ++itDirs)
291 {
292 Assert(mData.mNumObjects);
293 mData.mNumObjects--;
294 itDirs->second->i_onRemove();
295 itDirs->second->uninit();
296 }
297 mData.mDirectories.clear();
298
299 LogFlowThisFunc(("Closing files (%zu total)\n",
300 mData.mFiles.size()));
301 for (SessionFiles::iterator itFiles = mData.mFiles.begin();
302 itFiles != mData.mFiles.end(); ++itFiles)
303 {
304 Assert(mData.mNumObjects);
305 mData.mNumObjects--;
306 itFiles->second->i_onRemove();
307 itFiles->second->uninit();
308 }
309 mData.mFiles.clear();
310
311 LogFlowThisFunc(("Closing processes (%zu total)\n",
312 mData.mProcesses.size()));
313 for (SessionProcesses::iterator itProcs = mData.mProcesses.begin();
314 itProcs != mData.mProcesses.end(); ++itProcs)
315 {
316 Assert(mData.mNumObjects);
317 mData.mNumObjects--;
318 itProcs->second->i_onRemove();
319 itProcs->second->uninit();
320 }
321 mData.mProcesses.clear();
322
323 mData.mEnvironmentChanges.reset();
324
325 if (mData.mpBaseEnvironment)
326 {
327 mData.mpBaseEnvironment->releaseConst();
328 mData.mpBaseEnvironment = NULL;
329 }
330
331 AssertMsg(mData.mNumObjects == 0,
332 ("mNumObjects=%RU32 when it should be 0\n", mData.mNumObjects));
333
334 baseUninit();
335#endif /* VBOX_WITH_GUEST_CONTROL */
336 LogFlowFuncLeaveRC(rc);
337}
338
339// implementation of public getters/setters for attributes
340/////////////////////////////////////////////////////////////////////////////
341
342HRESULT GuestSession::getUser(com::Utf8Str &aUser)
343{
344#ifndef VBOX_WITH_GUEST_CONTROL
345 ReturnComNotImplemented();
346#else
347
348 LogFlowThisFuncEnter();
349
350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
351
352 aUser = mData.mCredentials.mUser;
353
354 LogFlowThisFuncLeave();
355 return S_OK;
356#endif /* VBOX_WITH_GUEST_CONTROL */
357}
358
359HRESULT GuestSession::getDomain(com::Utf8Str &aDomain)
360{
361#ifndef VBOX_WITH_GUEST_CONTROL
362 ReturnComNotImplemented();
363#else
364 LogFlowThisFuncEnter();
365
366 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
367
368 aDomain = mData.mCredentials.mDomain;
369
370 LogFlowThisFuncLeave();
371 return S_OK;
372#endif /* VBOX_WITH_GUEST_CONTROL */
373}
374
375HRESULT GuestSession::getName(com::Utf8Str &aName)
376{
377#ifndef VBOX_WITH_GUEST_CONTROL
378 ReturnComNotImplemented();
379#else
380 LogFlowThisFuncEnter();
381
382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
383
384 aName = mData.mSession.mName;
385
386 LogFlowThisFuncLeave();
387 return S_OK;
388#endif /* VBOX_WITH_GUEST_CONTROL */
389}
390
391HRESULT GuestSession::getId(ULONG *aId)
392{
393#ifndef VBOX_WITH_GUEST_CONTROL
394 ReturnComNotImplemented();
395#else
396 LogFlowThisFuncEnter();
397
398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
399
400 *aId = mData.mSession.mID;
401
402 LogFlowThisFuncLeave();
403 return S_OK;
404#endif /* VBOX_WITH_GUEST_CONTROL */
405}
406
407HRESULT GuestSession::getStatus(GuestSessionStatus_T *aStatus)
408{
409#ifndef VBOX_WITH_GUEST_CONTROL
410 ReturnComNotImplemented();
411#else
412 LogFlowThisFuncEnter();
413
414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
415
416 *aStatus = mData.mStatus;
417
418 LogFlowThisFuncLeave();
419 return S_OK;
420#endif /* VBOX_WITH_GUEST_CONTROL */
421}
422
423HRESULT GuestSession::getTimeout(ULONG *aTimeout)
424{
425#ifndef VBOX_WITH_GUEST_CONTROL
426 ReturnComNotImplemented();
427#else
428 LogFlowThisFuncEnter();
429
430 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
431
432 *aTimeout = mData.mTimeout;
433
434 LogFlowThisFuncLeave();
435 return S_OK;
436#endif /* VBOX_WITH_GUEST_CONTROL */
437}
438
439HRESULT GuestSession::setTimeout(ULONG aTimeout)
440{
441#ifndef VBOX_WITH_GUEST_CONTROL
442 ReturnComNotImplemented();
443#else
444 LogFlowThisFuncEnter();
445
446 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
447
448 mData.mTimeout = aTimeout;
449
450 LogFlowThisFuncLeave();
451 return S_OK;
452#endif /* VBOX_WITH_GUEST_CONTROL */
453}
454
455HRESULT GuestSession::getProtocolVersion(ULONG *aProtocolVersion)
456{
457#ifndef VBOX_WITH_GUEST_CONTROL
458 ReturnComNotImplemented();
459#else
460 LogFlowThisFuncEnter();
461
462 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
463
464 *aProtocolVersion = mData.mProtocolVersion;
465
466 LogFlowThisFuncLeave();
467 return S_OK;
468#endif /* VBOX_WITH_GUEST_CONTROL */
469}
470
471HRESULT GuestSession::getEnvironmentChanges(std::vector<com::Utf8Str> &aEnvironmentChanges)
472{
473#ifndef VBOX_WITH_GUEST_CONTROL
474 ReturnComNotImplemented();
475#else
476 LogFlowThisFuncEnter();
477
478 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
479
480 int vrc = mData.mEnvironmentChanges.queryPutEnvArray(&aEnvironmentChanges);
481
482 LogFlowFuncLeaveRC(vrc);
483 return Global::vboxStatusCodeToCOM(vrc);
484#endif /* VBOX_WITH_GUEST_CONTROL */
485}
486
487HRESULT GuestSession::setEnvironmentChanges(const std::vector<com::Utf8Str> &aEnvironmentChanges)
488{
489#ifndef VBOX_WITH_GUEST_CONTROL
490 ReturnComNotImplemented();
491#else
492 LogFlowThisFuncEnter();
493
494 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
495
496 mData.mEnvironmentChanges.reset();
497 int vrc = mData.mEnvironmentChanges.applyPutEnvArray(aEnvironmentChanges);
498
499 LogFlowFuncLeaveRC(vrc);
500 return Global::vboxStatusCodeToCOM(vrc);
501#endif /* VBOX_WITH_GUEST_CONTROL */
502}
503
504HRESULT GuestSession::getEnvironmentBase(std::vector<com::Utf8Str> &aEnvironmentBase)
505{
506#ifndef VBOX_WITH_GUEST_CONTROL
507 ReturnComNotImplemented();
508#else
509 LogFlowThisFuncEnter();
510
511 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
512 HRESULT hrc;
513 if (mData.mpBaseEnvironment)
514 {
515 int vrc = mData.mpBaseEnvironment->queryPutEnvArray(&aEnvironmentBase);
516 hrc = Global::vboxStatusCodeToCOM(vrc);
517 }
518 else if (mData.mProtocolVersion < 99999)
519 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
520 else
521 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
522
523 LogFlowFuncLeave();
524 return hrc;
525#endif /* VBOX_WITH_GUEST_CONTROL */
526}
527
528HRESULT GuestSession::getProcesses(std::vector<ComPtr<IGuestProcess> > &aProcesses)
529{
530#ifndef VBOX_WITH_GUEST_CONTROL
531 ReturnComNotImplemented();
532#else
533 LogFlowThisFuncEnter();
534
535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
536
537 aProcesses.resize(mData.mProcesses.size());
538 size_t i = 0;
539 for(SessionProcesses::iterator it = mData.mProcesses.begin();
540 it != mData.mProcesses.end();
541 ++it, ++i)
542 {
543 it->second.queryInterfaceTo(aProcesses[i].asOutParam());
544 }
545
546 LogFlowFunc(("mProcesses=%zu\n", aProcesses.size()));
547 return S_OK;
548#endif /* VBOX_WITH_GUEST_CONTROL */
549}
550
551HRESULT GuestSession::getPathStyle(PathStyle_T *aPathStyle)
552{
553#ifndef VBOX_WITH_GUEST_CONTROL
554 ReturnComNotImplemented();
555#else
556 VBOXOSTYPE enmOsType = mParent->i_getGuestOSType();
557 if ( enmOsType < VBOXOSTYPE_DOS)
558 {
559 *aPathStyle = PathStyle_Unknown;
560 LogFlowFunc(("returns PathStyle_Unknown\n"));
561 }
562 else if (enmOsType < VBOXOSTYPE_Linux)
563 {
564 *aPathStyle = PathStyle_DOS;
565 LogFlowFunc(("returns PathStyle_DOS\n"));
566 }
567 else
568 {
569 *aPathStyle = PathStyle_UNIX;
570 LogFlowFunc(("returns PathStyle_UNIX\n"));
571 }
572 return S_OK;
573#endif
574}
575
576HRESULT GuestSession::getCurrentDirectory(com::Utf8Str &aCurrentDirectory)
577{
578 ReturnComNotImplemented();
579}
580
581HRESULT GuestSession::setCurrentDirectory(const com::Utf8Str &aCurrentDirectory)
582{
583 ReturnComNotImplemented();
584}
585
586HRESULT GuestSession::getDirectories(std::vector<ComPtr<IGuestDirectory> > &aDirectories)
587{
588#ifndef VBOX_WITH_GUEST_CONTROL
589 ReturnComNotImplemented();
590#else
591 LogFlowThisFuncEnter();
592
593 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
594
595 aDirectories.resize(mData.mDirectories.size());
596 size_t i = 0;
597 for(SessionDirectories::iterator it = mData.mDirectories.begin();
598 it != mData.mDirectories.end();
599 ++it, ++i)
600 {
601 it->second.queryInterfaceTo(aDirectories[i].asOutParam());
602 }
603
604 LogFlowFunc(("mDirectories=%zu\n", aDirectories.size()));
605 return S_OK;
606#endif /* VBOX_WITH_GUEST_CONTROL */
607}
608
609HRESULT GuestSession::getFiles(std::vector<ComPtr<IGuestFile> > &aFiles)
610{
611#ifndef VBOX_WITH_GUEST_CONTROL
612 ReturnComNotImplemented();
613#else
614 LogFlowThisFuncEnter();
615
616 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
617
618 aFiles.resize(mData.mFiles.size());
619 size_t i = 0;
620 for(SessionFiles::iterator it = mData.mFiles.begin(); it != mData.mFiles.end(); ++it, ++i)
621 it->second.queryInterfaceTo(aFiles[i].asOutParam());
622
623 LogFlowFunc(("mDirectories=%zu\n", aFiles.size()));
624
625 return S_OK;
626#endif /* VBOX_WITH_GUEST_CONTROL */
627}
628
629HRESULT GuestSession::getEventSource(ComPtr<IEventSource> &aEventSource)
630{
631#ifndef VBOX_WITH_GUEST_CONTROL
632 ReturnComNotImplemented();
633#else
634 LogFlowThisFuncEnter();
635
636 // no need to lock - lifetime constant
637 mEventSource.queryInterfaceTo(aEventSource.asOutParam());
638
639 LogFlowThisFuncLeave();
640 return S_OK;
641#endif /* VBOX_WITH_GUEST_CONTROL */
642}
643
644// private methods
645///////////////////////////////////////////////////////////////////////////////
646
647int GuestSession::i_closeSession(uint32_t uFlags, uint32_t uTimeoutMS, int *pGuestRc)
648{
649 LogFlowThisFunc(("uFlags=%x, uTimeoutMS=%RU32\n", uFlags, uTimeoutMS));
650
651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
652
653 /* Guest Additions < 4.3 don't support closing dedicated
654 guest sessions, skip. */
655 if (mData.mProtocolVersion < 2)
656 {
657 LogFlowThisFunc(("Installed Guest Additions don't support closing dedicated sessions, skipping\n"));
658 return VINF_SUCCESS;
659 }
660
661 /** @todo uFlags validation. */
662
663 if (mData.mStatus != GuestSessionStatus_Started)
664 {
665 LogFlowThisFunc(("Session ID=%RU32 not started (anymore), status now is: %RU32\n",
666 mData.mSession.mID, mData.mStatus));
667 return VINF_SUCCESS;
668 }
669
670 int vrc;
671
672 GuestWaitEvent *pEvent = NULL;
673 GuestEventTypes eventTypes;
674 try
675 {
676 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
677
678 vrc = registerWaitEvent(mData.mSession.mID, 0 /* Object ID */,
679 eventTypes, &pEvent);
680 }
681 catch (std::bad_alloc)
682 {
683 vrc = VERR_NO_MEMORY;
684 }
685
686 if (RT_FAILURE(vrc))
687 return vrc;
688
689 LogFlowThisFunc(("Sending closing request to guest session ID=%RU32, uFlags=%x\n",
690 mData.mSession.mID, uFlags));
691
692 VBOXHGCMSVCPARM paParms[4];
693 int i = 0;
694 paParms[i++].setUInt32(pEvent->ContextID());
695 paParms[i++].setUInt32(uFlags);
696
697 alock.release(); /* Drop the write lock before waiting. */
698
699 vrc = i_sendCommand(HOST_SESSION_CLOSE, i, paParms);
700 if (RT_SUCCESS(vrc))
701 vrc = i_waitForStatusChange(pEvent, GuestSessionWaitForFlag_Terminate, uTimeoutMS,
702 NULL /* Session status */, pGuestRc);
703
704 unregisterWaitEvent(pEvent);
705
706 LogFlowFuncLeaveRC(vrc);
707 return vrc;
708}
709
710int GuestSession::i_directoryCreateInternal(const Utf8Str &strPath, uint32_t uMode,
711 uint32_t uFlags, int *pGuestRc)
712{
713 LogFlowThisFunc(("strPath=%s, uMode=%x, uFlags=%x\n",
714 strPath.c_str(), uMode, uFlags));
715
716 int vrc = VINF_SUCCESS;
717
718 GuestProcessStartupInfo procInfo;
719 procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_MKDIR);
720 procInfo.mFlags = ProcessCreateFlag_Hidden;
721
722 try
723 {
724 /* Construct arguments. */
725 if (uFlags)
726 {
727 if (uFlags & DirectoryCreateFlag_Parents)
728 procInfo.mArguments.push_back(Utf8Str("--parents")); /* We also want to create the parent directories. */
729 else
730 vrc = VERR_INVALID_PARAMETER;
731 }
732
733 if (uMode)
734 {
735 procInfo.mArguments.push_back(Utf8Str("--mode")); /* Set the creation mode. */
736
737 char szMode[16];
738 if (RTStrPrintf(szMode, sizeof(szMode), "%o", uMode))
739 {
740 procInfo.mArguments.push_back(Utf8Str(szMode));
741 }
742 else
743 vrc = VERR_BUFFER_OVERFLOW;
744 }
745 procInfo.mArguments.push_back("--"); /* '--version' is a valid directory name. */
746 procInfo.mArguments.push_back(strPath); /* The directory we want to create. */
747 }
748 catch (std::bad_alloc)
749 {
750 vrc = VERR_NO_MEMORY;
751 }
752
753 if (RT_SUCCESS(vrc))
754 vrc = GuestProcessTool::i_run(this, procInfo, pGuestRc);
755
756 LogFlowFuncLeaveRC(vrc);
757 return vrc;
758}
759
760inline bool GuestSession::i_directoryExists(uint32_t uDirID, ComObjPtr<GuestDirectory> *pDir)
761{
762 SessionDirectories::const_iterator it = mData.mDirectories.find(uDirID);
763 if (it != mData.mDirectories.end())
764 {
765 if (pDir)
766 *pDir = it->second;
767 return true;
768 }
769 return false;
770}
771
772int GuestSession::i_directoryQueryInfoInternal(const Utf8Str &strPath, bool fFollowSymlinks,
773 GuestFsObjData &objData, int *pGuestRc)
774{
775 LogFlowThisFunc(("strPath=%s fFollowSymlinks=%RTbool\n", strPath.c_str(), fFollowSymlinks));
776
777 int vrc = i_fsQueryInfoInternal(strPath, fFollowSymlinks, objData, pGuestRc);
778 if (RT_SUCCESS(vrc))
779 {
780 vrc = objData.mType == FsObjType_Directory
781 ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
782 }
783
784 LogFlowFuncLeaveRC(vrc);
785 return vrc;
786}
787
788int GuestSession::i_directoryRemoveFromList(GuestDirectory *pDirectory)
789{
790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
791
792 int rc = VERR_NOT_FOUND;
793
794 SessionDirectories::iterator itDirs = mData.mDirectories.begin();
795 while (itDirs != mData.mDirectories.end())
796 {
797 if (pDirectory == itDirs->second)
798 {
799 /* Make sure to consume the pointer before the one of the
800 * iterator gets released. */
801 ComObjPtr<GuestDirectory> pDir = pDirectory;
802
803 Bstr strName;
804 HRESULT hr = itDirs->second->COMGETTER(DirectoryName)(strName.asOutParam());
805 ComAssertComRC(hr);
806
807 Assert(mData.mDirectories.size());
808 Assert(mData.mNumObjects);
809 LogFlowFunc(("Removing directory \"%s\" (Session: %RU32) (now total %zu processes, %RU32 objects)\n",
810 Utf8Str(strName).c_str(), mData.mSession.mID, mData.mDirectories.size() - 1, mData.mNumObjects - 1));
811
812 rc = pDirectory->i_onRemove();
813 mData.mDirectories.erase(itDirs);
814 mData.mNumObjects--;
815
816 pDir.setNull();
817 break;
818 }
819
820 itDirs++;
821 }
822
823 LogFlowFuncLeaveRC(rc);
824 return rc;
825}
826
827int GuestSession::i_directoryRemoveInternal(const Utf8Str &strPath, uint32_t uFlags,
828 int *pGuestRc)
829{
830 AssertReturn(!(uFlags & ~DIRREMOVE_FLAG_VALID_MASK), VERR_INVALID_PARAMETER);
831
832 LogFlowThisFunc(("strPath=%s, uFlags=0x%x\n", strPath.c_str(), uFlags));
833
834 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
835
836 GuestWaitEvent *pEvent = NULL;
837 int vrc = registerWaitEvent(mData.mSession.mID, 0 /* Object ID */,
838 &pEvent);
839 if (RT_FAILURE(vrc))
840 return vrc;
841
842 /* Prepare HGCM call. */
843 VBOXHGCMSVCPARM paParms[8];
844 int i = 0;
845 paParms[i++].setUInt32(pEvent->ContextID());
846 paParms[i++].setPointer((void*)strPath.c_str(),
847 (ULONG)strPath.length() + 1);
848 paParms[i++].setUInt32(uFlags);
849
850 alock.release(); /* Drop write lock before sending. */
851
852 vrc = i_sendCommand(HOST_DIR_REMOVE, i, paParms);
853 if (RT_SUCCESS(vrc))
854 {
855 vrc = pEvent->Wait(30 * 1000);
856 if ( vrc == VERR_GSTCTL_GUEST_ERROR
857 && pGuestRc)
858 *pGuestRc = pEvent->GuestResult();
859 }
860
861 unregisterWaitEvent(pEvent);
862
863 LogFlowFuncLeaveRC(vrc);
864 return vrc;
865}
866
867int GuestSession::i_objectCreateTempInternal(const Utf8Str &strTemplate, const Utf8Str &strPath,
868 bool fDirectory, Utf8Str &strName, int *pGuestRc)
869{
870 LogFlowThisFunc(("strTemplate=%s, strPath=%s, fDirectory=%RTbool\n",
871 strTemplate.c_str(), strPath.c_str(), fDirectory));
872
873 int vrc = VINF_SUCCESS;
874
875 GuestProcessStartupInfo procInfo;
876 procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_MKTEMP);
877 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
878
879 try
880 {
881 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
882 if (fDirectory)
883 procInfo.mArguments.push_back(Utf8Str("-d"));
884 if (strPath.length()) /* Otherwise use /tmp or equivalent. */
885 {
886 procInfo.mArguments.push_back(Utf8Str("-t"));
887 procInfo.mArguments.push_back(strPath);
888 }
889 procInfo.mArguments.push_back("--"); /* strTemplate could be '--help'. */
890 procInfo.mArguments.push_back(strTemplate);
891 }
892 catch (std::bad_alloc)
893 {
894 vrc = VERR_NO_MEMORY;
895 }
896
897 /** @todo Use an internal HGCM command for this operation, since
898 * we now can run in a user-dedicated session. */
899 int guestRc; GuestCtrlStreamObjects stdOut;
900 if (RT_SUCCESS(vrc))
901 vrc = GuestProcessTool::i_runEx(this, procInfo,
902 &stdOut, 1 /* cStrmOutObjects */,
903 &guestRc);
904 if ( RT_SUCCESS(vrc)
905 && RT_SUCCESS(guestRc))
906 {
907 GuestFsObjData objData;
908 if (!stdOut.empty())
909 {
910 vrc = objData.FromMkTemp(stdOut.at(0));
911 if (RT_FAILURE(vrc))
912 {
913 guestRc = vrc;
914 vrc = VERR_GSTCTL_GUEST_ERROR;
915 }
916 }
917 else
918 vrc = VERR_NO_DATA;
919
920 if (RT_SUCCESS(vrc))
921 strName = objData.mName;
922 }
923
924 if (pGuestRc)
925 *pGuestRc = guestRc;
926
927 LogFlowFuncLeaveRC(vrc);
928 return vrc;
929}
930
931int GuestSession::i_directoryOpenInternal(const GuestDirectoryOpenInfo &openInfo,
932 ComObjPtr<GuestDirectory> &pDirectory, int *pGuestRc)
933{
934 LogFlowThisFunc(("strPath=%s, strPath=%s, uFlags=%x\n",
935 openInfo.mPath.c_str(), openInfo.mFilter.c_str(), openInfo.mFlags));
936
937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
938
939 int rc = VERR_MAX_PROCS_REACHED;
940 if (mData.mNumObjects >= VBOX_GUESTCTRL_MAX_OBJECTS)
941 return rc;
942
943 /* Create a new (host-based) directory ID and assign it. */
944 uint32_t uNewDirID = 0;
945 ULONG uTries = 0;
946
947 for (;;)
948 {
949 /* Is the directory ID already used? */
950 if (!i_directoryExists(uNewDirID, NULL /* pDirectory */))
951 {
952 /* Callback with context ID was not found. This means
953 * we can use this context ID for our new callback we want
954 * to add below. */
955 rc = VINF_SUCCESS;
956 break;
957 }
958 uNewDirID++;
959 if (uNewDirID == VBOX_GUESTCTRL_MAX_OBJECTS)
960 uNewDirID = 0;
961
962 if (++uTries == UINT32_MAX)
963 break; /* Don't try too hard. */
964 }
965
966 if (RT_FAILURE(rc))
967 return rc;
968
969 /* Create the directory object. */
970 HRESULT hr = pDirectory.createObject();
971 if (FAILED(hr))
972 return VERR_COM_UNEXPECTED;
973
974 Console *pConsole = mParent->i_getConsole();
975 AssertPtr(pConsole);
976
977 int vrc = pDirectory->init(pConsole, this /* Parent */,
978 uNewDirID, openInfo);
979 if (RT_FAILURE(vrc))
980 return vrc;
981
982 /*
983 * Since this is a synchronous guest call we have to
984 * register the file object first, releasing the session's
985 * lock and then proceed with the actual opening command
986 * -- otherwise the file's opening callback would hang
987 * because the session's lock still is in place.
988 */
989 try
990 {
991 /* Add the created directory to our map. */
992 mData.mDirectories[uNewDirID] = pDirectory;
993 mData.mNumObjects++;
994 Assert(mData.mNumObjects <= VBOX_GUESTCTRL_MAX_OBJECTS);
995
996 LogFlowFunc(("Added new guest directory \"%s\" (Session: %RU32) (now total %zu dirs, %RU32 objects)\n",
997 openInfo.mPath.c_str(), mData.mSession.mID, mData.mFiles.size(), mData.mNumObjects));
998
999 alock.release(); /* Release lock before firing off event. */
1000
1001 /** @todo Fire off a VBoxEventType_OnGuestDirectoryRegistered event? */
1002 }
1003 catch (std::bad_alloc &)
1004 {
1005 rc = VERR_NO_MEMORY;
1006 }
1007
1008 if (RT_SUCCESS(rc))
1009 {
1010 /* Nothing further to do here yet. */
1011 if (pGuestRc)
1012 *pGuestRc = VINF_SUCCESS;
1013 }
1014
1015 LogFlowFuncLeaveRC(vrc);
1016 return vrc;
1017}
1018
1019int GuestSession::i_dispatchToDirectory(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
1020{
1021 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
1022
1023 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
1024 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
1025
1026 if (pSvcCb->mParms < 3)
1027 return VERR_INVALID_PARAMETER;
1028
1029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1030
1031 uint32_t uDirID = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCtxCb->uContextID);
1032#ifdef DEBUG
1033 LogFlowFunc(("uDirID=%RU32 (%zu total)\n",
1034 uDirID, mData.mFiles.size()));
1035#endif
1036 int rc;
1037 SessionDirectories::const_iterator itDir
1038 = mData.mDirectories.find(uDirID);
1039 if (itDir != mData.mDirectories.end())
1040 {
1041 ComObjPtr<GuestDirectory> pDirectory(itDir->second);
1042 Assert(!pDirectory.isNull());
1043
1044 alock.release();
1045
1046 rc = pDirectory->i_callbackDispatcher(pCtxCb, pSvcCb);
1047 }
1048 else
1049 rc = VERR_NOT_FOUND;
1050
1051 LogFlowFuncLeaveRC(rc);
1052 return rc;
1053}
1054
1055int GuestSession::i_dispatchToFile(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
1056{
1057 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
1058
1059 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
1060 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
1061
1062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1063
1064 uint32_t uFileID = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCtxCb->uContextID);
1065#ifdef DEBUG
1066 LogFlowFunc(("uFileID=%RU32 (%zu total)\n",
1067 uFileID, mData.mFiles.size()));
1068#endif
1069 int rc;
1070 SessionFiles::const_iterator itFile
1071 = mData.mFiles.find(uFileID);
1072 if (itFile != mData.mFiles.end())
1073 {
1074 ComObjPtr<GuestFile> pFile(itFile->second);
1075 Assert(!pFile.isNull());
1076
1077 alock.release();
1078
1079 rc = pFile->i_callbackDispatcher(pCtxCb, pSvcCb);
1080 }
1081 else
1082 rc = VERR_NOT_FOUND;
1083
1084 LogFlowFuncLeaveRC(rc);
1085 return rc;
1086}
1087
1088int GuestSession::i_dispatchToObject(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
1089{
1090 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
1091
1092 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
1093 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
1094
1095 int rc;
1096 uint32_t uObjectID = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCtxCb->uContextID);
1097
1098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1099
1100 /* Since we don't know which type the object is, we need to through all
1101 * all objects. */
1102 /** @todo Speed this up by adding an object type to the callback context! */
1103 SessionProcesses::const_iterator itProc = mData.mProcesses.find(uObjectID);
1104 if (itProc == mData.mProcesses.end())
1105 {
1106 SessionFiles::const_iterator itFile = mData.mFiles.find(uObjectID);
1107 if (itFile != mData.mFiles.end())
1108 {
1109 alock.release();
1110
1111 rc = i_dispatchToFile(pCtxCb, pSvcCb);
1112 }
1113 else
1114 {
1115 SessionDirectories::const_iterator itDir = mData.mDirectories.find(uObjectID);
1116 if (itDir != mData.mDirectories.end())
1117 {
1118 alock.release();
1119
1120 rc = i_dispatchToDirectory(pCtxCb, pSvcCb);
1121 }
1122 else
1123 rc = VERR_NOT_FOUND;
1124 }
1125 }
1126 else
1127 {
1128 alock.release();
1129
1130 rc = i_dispatchToProcess(pCtxCb, pSvcCb);
1131 }
1132
1133 LogFlowFuncLeaveRC(rc);
1134 return rc;
1135}
1136
1137int GuestSession::i_dispatchToProcess(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
1138{
1139 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
1140
1141 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
1142 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
1143
1144 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1145
1146 uint32_t uProcessID = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCtxCb->uContextID);
1147#ifdef DEBUG
1148 LogFlowFunc(("uProcessID=%RU32 (%zu total)\n",
1149 uProcessID, mData.mProcesses.size()));
1150#endif
1151 int rc;
1152 SessionProcesses::const_iterator itProc
1153 = mData.mProcesses.find(uProcessID);
1154 if (itProc != mData.mProcesses.end())
1155 {
1156#ifdef DEBUG_andy
1157 ULONG cRefs = itProc->second->AddRef();
1158 Assert(cRefs >= 2);
1159 LogFlowFunc(("pProcess=%p, cRefs=%RU32\n", &itProc->second, cRefs - 1));
1160 itProc->second->Release();
1161#endif
1162 ComObjPtr<GuestProcess> pProcess(itProc->second);
1163 Assert(!pProcess.isNull());
1164
1165 /* Set protocol version so that pSvcCb can
1166 * be interpreted right. */
1167 pCtxCb->uProtocol = mData.mProtocolVersion;
1168
1169 alock.release();
1170 rc = pProcess->i_callbackDispatcher(pCtxCb, pSvcCb);
1171 }
1172 else
1173 rc = VERR_NOT_FOUND;
1174
1175 LogFlowFuncLeaveRC(rc);
1176 return rc;
1177}
1178
1179int GuestSession::i_dispatchToThis(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
1180{
1181 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
1182 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
1183
1184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1185
1186#ifdef DEBUG
1187 LogFlowThisFunc(("sessionID=%RU32, CID=%RU32, uFunction=%RU32, pSvcCb=%p\n",
1188 mData.mSession.mID, pCbCtx->uContextID, pCbCtx->uFunction, pSvcCb));
1189#endif
1190
1191 int rc;
1192 switch (pCbCtx->uFunction)
1193 {
1194 case GUEST_DISCONNECTED:
1195 /** @todo Handle closing all guest objects. */
1196 rc = VERR_INTERNAL_ERROR;
1197 break;
1198
1199 case GUEST_SESSION_NOTIFY: /* Guest Additions >= 4.3.0. */
1200 {
1201 rc = i_onSessionStatusChange(pCbCtx, pSvcCb);
1202 break;
1203 }
1204
1205 default:
1206 /* Silently skip unknown callbacks. */
1207 rc = VERR_NOT_SUPPORTED;
1208 break;
1209 }
1210
1211 LogFlowFuncLeaveRC(rc);
1212 return rc;
1213}
1214
1215inline bool GuestSession::i_fileExists(uint32_t uFileID, ComObjPtr<GuestFile> *pFile)
1216{
1217 SessionFiles::const_iterator it = mData.mFiles.find(uFileID);
1218 if (it != mData.mFiles.end())
1219 {
1220 if (pFile)
1221 *pFile = it->second;
1222 return true;
1223 }
1224 return false;
1225}
1226
1227int GuestSession::i_fileRemoveFromList(GuestFile *pFile)
1228{
1229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1230
1231 int rc = VERR_NOT_FOUND;
1232
1233 SessionFiles::iterator itFiles = mData.mFiles.begin();
1234 while (itFiles != mData.mFiles.end())
1235 {
1236 if (pFile == itFiles->second)
1237 {
1238 /* Make sure to consume the pointer before the one of thfe
1239 * iterator gets released. */
1240 ComObjPtr<GuestFile> pCurFile = pFile;
1241
1242 Bstr strName;
1243 HRESULT hr = pCurFile->COMGETTER(FileName)(strName.asOutParam());
1244 ComAssertComRC(hr);
1245
1246 Assert(mData.mNumObjects);
1247 LogFlowThisFunc(("Removing guest file \"%s\" (Session: %RU32) (now total %zu files, %RU32 objects)\n",
1248 Utf8Str(strName).c_str(), mData.mSession.mID, mData.mFiles.size() - 1, mData.mNumObjects - 1));
1249
1250 rc = pFile->i_onRemove();
1251 mData.mFiles.erase(itFiles);
1252 mData.mNumObjects--;
1253
1254 alock.release(); /* Release lock before firing off event. */
1255
1256 fireGuestFileRegisteredEvent(mEventSource, this, pCurFile,
1257 false /* Unregistered */);
1258 pCurFile.setNull();
1259 break;
1260 }
1261
1262 itFiles++;
1263 }
1264
1265 LogFlowFuncLeaveRC(rc);
1266 return rc;
1267}
1268
1269int GuestSession::i_fileRemoveInternal(const Utf8Str &strPath, int *pGuestRc)
1270{
1271 LogFlowThisFunc(("strPath=%s\n", strPath.c_str()));
1272
1273 int vrc = VINF_SUCCESS;
1274
1275 GuestProcessStartupInfo procInfo;
1276 GuestProcessStream streamOut;
1277
1278 procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_RM);
1279 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
1280
1281 try
1282 {
1283 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
1284 procInfo.mArguments.push_back("--"); /* strPath could be '--help', which is a valid filename. */
1285 procInfo.mArguments.push_back(strPath); /* The file we want to remove. */
1286 }
1287 catch (std::bad_alloc)
1288 {
1289 vrc = VERR_NO_MEMORY;
1290 }
1291
1292 if (RT_SUCCESS(vrc))
1293 vrc = GuestProcessTool::i_run(this, procInfo, pGuestRc);
1294
1295 LogFlowFuncLeaveRC(vrc);
1296 return vrc;
1297}
1298
1299int GuestSession::i_fileOpenInternal(const GuestFileOpenInfo &openInfo,
1300 ComObjPtr<GuestFile> &pFile, int *pGuestRc)
1301{
1302 LogFlowThisFunc(("strFile=%s, enmAccessMode=%d (%s) enmOpenAction=%d (%s) uCreationMode=%RU32, uOffset=%RU64\n",
1303 openInfo.mFileName.c_str(), openInfo.mAccessMode, openInfo.mpszAccessMode,
1304 openInfo.mOpenAction, openInfo.mpszOpenAction, openInfo.mCreationMode, openInfo.mInitialOffset));
1305
1306 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1307
1308 /* Guest Additions < 4.3 don't support handling
1309 guest files, skip. */
1310 if (mData.mProtocolVersion < 2)
1311 {
1312 LogFlowThisFunc(("Installed Guest Additions don't support handling guest files, skipping\n"));
1313 return VERR_NOT_SUPPORTED;
1314 }
1315
1316 int rc = VERR_MAX_PROCS_REACHED;
1317 if (mData.mNumObjects >= VBOX_GUESTCTRL_MAX_OBJECTS)
1318 return rc;
1319
1320 /* Create a new (host-based) file ID and assign it. */
1321 uint32_t uNewFileID = 0;
1322 ULONG uTries = 0;
1323
1324 for (;;)
1325 {
1326 /* Is the file ID already used? */
1327 if (!i_fileExists(uNewFileID, NULL /* pFile */))
1328 {
1329 /* Callback with context ID was not found. This means
1330 * we can use this context ID for our new callback we want
1331 * to add below. */
1332 rc = VINF_SUCCESS;
1333 break;
1334 }
1335 uNewFileID++;
1336 if (uNewFileID == VBOX_GUESTCTRL_MAX_OBJECTS)
1337 uNewFileID = 0;
1338
1339 if (++uTries == UINT32_MAX)
1340 break; /* Don't try too hard. */
1341 }
1342
1343 if (RT_FAILURE(rc))
1344 return rc;
1345
1346 /* Create the directory object. */
1347 HRESULT hr = pFile.createObject();
1348 if (FAILED(hr))
1349 return VERR_COM_UNEXPECTED;
1350
1351 Console *pConsole = mParent->i_getConsole();
1352 AssertPtr(pConsole);
1353
1354 rc = pFile->init(pConsole, this /* GuestSession */,
1355 uNewFileID, openInfo);
1356 if (RT_FAILURE(rc))
1357 return rc;
1358
1359 /*
1360 * Since this is a synchronous guest call we have to
1361 * register the file object first, releasing the session's
1362 * lock and then proceed with the actual opening command
1363 * -- otherwise the file's opening callback would hang
1364 * because the session's lock still is in place.
1365 */
1366 try
1367 {
1368 /* Add the created file to our vector. */
1369 mData.mFiles[uNewFileID] = pFile;
1370 mData.mNumObjects++;
1371 Assert(mData.mNumObjects <= VBOX_GUESTCTRL_MAX_OBJECTS);
1372
1373 LogFlowFunc(("Added new guest file \"%s\" (Session: %RU32) (now total %zu files, %RU32 objects)\n",
1374 openInfo.mFileName.c_str(), mData.mSession.mID, mData.mFiles.size(), mData.mNumObjects));
1375
1376 alock.release(); /* Release lock before firing off event. */
1377
1378 fireGuestFileRegisteredEvent(mEventSource, this, pFile,
1379 true /* Registered */);
1380 }
1381 catch (std::bad_alloc &)
1382 {
1383 rc = VERR_NO_MEMORY;
1384 }
1385
1386 if (RT_SUCCESS(rc))
1387 {
1388 int guestRc;
1389 rc = pFile->i_openFile(30 * 1000 /* 30s timeout */, &guestRc);
1390 if ( rc == VERR_GSTCTL_GUEST_ERROR
1391 && pGuestRc)
1392 {
1393 *pGuestRc = guestRc;
1394 }
1395 }
1396
1397 LogFlowFuncLeaveRC(rc);
1398 return rc;
1399}
1400
1401int GuestSession::i_fileQueryInfoInternal(const Utf8Str &strPath, bool fFollowSymlinks, GuestFsObjData &objData, int *pGuestRc)
1402{
1403 LogFlowThisFunc(("strPath=%s fFollowSymlinks=%RTbool\n", strPath.c_str(), fFollowSymlinks));
1404
1405 int vrc = i_fsQueryInfoInternal(strPath, fFollowSymlinks, objData, pGuestRc);
1406 if (RT_SUCCESS(vrc))
1407 {
1408 vrc = objData.mType == FsObjType_File
1409 ? VINF_SUCCESS : VERR_NOT_A_FILE;
1410 }
1411
1412 LogFlowFuncLeaveRC(vrc);
1413 return vrc;
1414}
1415
1416int GuestSession::i_fileQuerySizeInternal(const Utf8Str &strPath, bool fFollowSymlinks, int64_t *pllSize, int *pGuestRc)
1417{
1418 AssertPtrReturn(pllSize, VERR_INVALID_POINTER);
1419
1420 GuestFsObjData objData;
1421 int vrc = i_fileQueryInfoInternal(strPath, fFollowSymlinks, objData, pGuestRc);
1422 if (RT_SUCCESS(vrc))
1423 *pllSize = objData.mObjectSize;
1424
1425 return vrc;
1426}
1427
1428/**
1429 * <Someone write documentation, pretty please!>
1430 *
1431 * @param pGuestRc Optional. Will be set to VINF_SUCCESS,
1432 * VERR_NOT_EQUAL or VERR_INVALID_STATE if the
1433 * process completed. May probably be set to a lot of
1434 * other things. Not sure if these things are related
1435 * to the process we ran or what, really. :-(
1436 */
1437int GuestSession::i_fsQueryInfoInternal(const Utf8Str &strPath, bool fFollowSymlinks, GuestFsObjData &objData, int *pGuestRc)
1438{
1439 LogFlowThisFunc(("strPath=%s\n", strPath.c_str()));
1440
1441 int vrc = VINF_SUCCESS;
1442
1443 /** @todo Merge this with IGuestFile::queryInfo(). */
1444 GuestProcessStartupInfo procInfo;
1445 procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_STAT);
1446 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
1447
1448 try
1449 {
1450 /* Construct arguments. */
1451 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
1452 if (fFollowSymlinks)
1453 procInfo.mArguments.push_back(Utf8Str("-L"));
1454 procInfo.mArguments.push_back("--"); /* strPath could be '--help', which is a valid filename. */
1455 procInfo.mArguments.push_back(strPath);
1456 }
1457 catch (std::bad_alloc)
1458 {
1459 vrc = VERR_NO_MEMORY;
1460 }
1461
1462 int guestRc;
1463 GuestCtrlStreamObjects stdOut;
1464 if (RT_SUCCESS(vrc))
1465 vrc = GuestProcessTool::i_runEx(this, procInfo,
1466 &stdOut, 1 /* cStrmOutObjects */,
1467 &guestRc);
1468 if ( RT_SUCCESS(vrc)
1469 && RT_SUCCESS(guestRc))
1470 {
1471 if (!stdOut.empty())
1472 vrc = objData.FromStat(stdOut.at(0));
1473 else
1474 vrc = VERR_NO_DATA;
1475 }
1476
1477 if ( vrc == VERR_GSTCTL_GUEST_ERROR
1478 && pGuestRc)
1479 *pGuestRc = guestRc;
1480
1481 LogFlowThisFunc(("Returning rc=%Rrc, guestRc=%Rrc\n",
1482 vrc, guestRc));
1483 return vrc;
1484}
1485
1486const GuestCredentials& GuestSession::i_getCredentials(void)
1487{
1488 return mData.mCredentials;
1489}
1490
1491Utf8Str GuestSession::i_getName(void)
1492{
1493 return mData.mSession.mName;
1494}
1495
1496/* static */
1497Utf8Str GuestSession::i_guestErrorToString(int guestRc)
1498{
1499 Utf8Str strError;
1500
1501 /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
1502 switch (guestRc)
1503 {
1504 case VERR_INVALID_VM_HANDLE:
1505 strError += Utf8StrFmt(tr("VMM device is not available (is the VM running?)"));
1506 break;
1507
1508 case VERR_HGCM_SERVICE_NOT_FOUND:
1509 strError += Utf8StrFmt(tr("The guest execution service is not available"));
1510 break;
1511
1512 case VERR_AUTHENTICATION_FAILURE:
1513 strError += Utf8StrFmt(tr("The specified user was not able to logon on guest"));
1514 break;
1515
1516 case VERR_TIMEOUT:
1517 strError += Utf8StrFmt(tr("The guest did not respond within time"));
1518 break;
1519
1520 case VERR_CANCELLED:
1521 strError += Utf8StrFmt(tr("The session operation was canceled"));
1522 break;
1523
1524 case VERR_PERMISSION_DENIED:
1525 strError += Utf8StrFmt(tr("Invalid user/password credentials"));
1526 break;
1527
1528 case VERR_MAX_PROCS_REACHED:
1529 strError += Utf8StrFmt(tr("Maximum number of concurrent guest processes has been reached"));
1530 break;
1531
1532 case VERR_NOT_EQUAL: /** @todo Imprecise to the user; can mean anything and all. */
1533 strError += Utf8StrFmt(tr("Unable to retrieve requested information"));
1534 break;
1535
1536 case VERR_NOT_FOUND:
1537 strError += Utf8StrFmt(tr("The guest execution service is not ready (yet)"));
1538 break;
1539
1540 default:
1541 strError += Utf8StrFmt("%Rrc", guestRc);
1542 break;
1543 }
1544
1545 return strError;
1546}
1547
1548/**
1549 * Checks if this session is ready state where it can handle
1550 * all session-bound actions (like guest processes, guest files).
1551 * Only used by official API methods. Will set an external
1552 * error when not ready.
1553 */
1554HRESULT GuestSession::i_isReadyExternal(void)
1555{
1556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1557
1558 /** @todo Be a bit more informative. */
1559 if (mData.mStatus != GuestSessionStatus_Started)
1560 return setError(E_UNEXPECTED, tr("Session is not in started state"));
1561
1562 return S_OK;
1563}
1564
1565/**
1566 * Called by IGuest right before this session gets removed from
1567 * the public session list.
1568 */
1569int GuestSession::i_onRemove(void)
1570{
1571 LogFlowThisFuncEnter();
1572
1573 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1574
1575 int vrc = VINF_SUCCESS;
1576
1577 /*
1578 * Note: The event source stuff holds references to this object,
1579 * so make sure that this is cleaned up *before* calling uninit.
1580 */
1581 if (!mEventSource.isNull())
1582 {
1583 mEventSource->UnregisterListener(mLocalListener);
1584
1585 mLocalListener.setNull();
1586 unconst(mEventSource).setNull();
1587 }
1588
1589 LogFlowFuncLeaveRC(vrc);
1590 return vrc;
1591}
1592
1593/** No locking! */
1594int GuestSession::i_onSessionStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
1595{
1596 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
1597 /* pCallback is optional. */
1598 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
1599
1600 if (pSvcCbData->mParms < 3)
1601 return VERR_INVALID_PARAMETER;
1602
1603 CALLBACKDATA_SESSION_NOTIFY dataCb;
1604 /* pSvcCb->mpaParms[0] always contains the context ID. */
1605 int vrc = pSvcCbData->mpaParms[1].getUInt32(&dataCb.uType);
1606 AssertRCReturn(vrc, vrc);
1607 vrc = pSvcCbData->mpaParms[2].getUInt32(&dataCb.uResult);
1608 AssertRCReturn(vrc, vrc);
1609
1610 LogFlowThisFunc(("ID=%RU32, uType=%RU32, guestRc=%Rrc\n",
1611 mData.mSession.mID, dataCb.uType, dataCb.uResult));
1612
1613 GuestSessionStatus_T sessionStatus = GuestSessionStatus_Undefined;
1614
1615 int guestRc = dataCb.uResult; /** @todo uint32_t vs. int. */
1616 switch (dataCb.uType)
1617 {
1618 case GUEST_SESSION_NOTIFYTYPE_ERROR:
1619 sessionStatus = GuestSessionStatus_Error;
1620 break;
1621
1622 case GUEST_SESSION_NOTIFYTYPE_STARTED:
1623 sessionStatus = GuestSessionStatus_Started;
1624#if 0 /** @todo If we get some environment stuff along with this kind notification: */
1625 const char *pszzEnvBlock = ...;
1626 uint32_t cbEnvBlock = ...;
1627 if (!mData.mpBaseEnvironment)
1628 {
1629 GuestEnvironment *pBaseEnv;
1630 try { pBaseEnv = new GuestEnvironment(); } catch (std::bad_alloc &) { pBaseEnv = NULL; }
1631 if (pBaseEnv)
1632 {
1633 int vrc = pBaseEnv->initNormal();
1634 if (RT_SUCCESS(vrc))
1635 vrc = pBaseEnv->copyUtf8Block(pszzEnvBlock, cbEnvBlock);
1636 if (RT_SUCCESS(vrc))
1637 mData.mpBaseEnvironment = pBaseEnv;
1638 else
1639 pBaseEnv->release();
1640 }
1641 }
1642#endif
1643 break;
1644
1645 case GUEST_SESSION_NOTIFYTYPE_TEN:
1646 case GUEST_SESSION_NOTIFYTYPE_TES:
1647 case GUEST_SESSION_NOTIFYTYPE_TEA:
1648 sessionStatus = GuestSessionStatus_Terminated;
1649 break;
1650
1651 case GUEST_SESSION_NOTIFYTYPE_TOK:
1652 sessionStatus = GuestSessionStatus_TimedOutKilled;
1653 break;
1654
1655 case GUEST_SESSION_NOTIFYTYPE_TOA:
1656 sessionStatus = GuestSessionStatus_TimedOutAbnormally;
1657 break;
1658
1659 case GUEST_SESSION_NOTIFYTYPE_DWN:
1660 sessionStatus = GuestSessionStatus_Down;
1661 break;
1662
1663 case GUEST_SESSION_NOTIFYTYPE_UNDEFINED:
1664 default:
1665 vrc = VERR_NOT_SUPPORTED;
1666 break;
1667 }
1668
1669 if (RT_SUCCESS(vrc))
1670 {
1671 if (RT_FAILURE(guestRc))
1672 sessionStatus = GuestSessionStatus_Error;
1673 }
1674
1675 /* Set the session status. */
1676 if (RT_SUCCESS(vrc))
1677 vrc = i_setSessionStatus(sessionStatus, guestRc);
1678
1679 LogFlowThisFunc(("ID=%RU32, guestRc=%Rrc\n", mData.mSession.mID, guestRc));
1680
1681 LogFlowFuncLeaveRC(vrc);
1682 return vrc;
1683}
1684
1685int GuestSession::i_startSessionInternal(int *pGuestRc)
1686{
1687 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1688
1689 LogFlowThisFunc(("mID=%RU32, mName=%s, uProtocolVersion=%RU32, openFlags=%x, openTimeoutMS=%RU32\n",
1690 mData.mSession.mID, mData.mSession.mName.c_str(), mData.mProtocolVersion,
1691 mData.mSession.mOpenFlags, mData.mSession.mOpenTimeoutMS));
1692
1693 /* Guest Additions < 4.3 don't support opening dedicated
1694 guest sessions. Simply return success here. */
1695 if (mData.mProtocolVersion < 2)
1696 {
1697 mData.mStatus = GuestSessionStatus_Started;
1698
1699 LogFlowThisFunc(("Installed Guest Additions don't support opening dedicated sessions, skipping\n"));
1700 return VINF_SUCCESS;
1701 }
1702
1703 if (mData.mStatus != GuestSessionStatus_Undefined)
1704 return VINF_SUCCESS;
1705
1706 /** @todo mData.mSession.uFlags validation. */
1707
1708 /* Set current session status. */
1709 mData.mStatus = GuestSessionStatus_Starting;
1710 mData.mRC = VINF_SUCCESS; /* Clear previous error, if any. */
1711
1712 int vrc;
1713
1714 GuestWaitEvent *pEvent = NULL;
1715 GuestEventTypes eventTypes;
1716 try
1717 {
1718 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
1719
1720 vrc = registerWaitEvent(mData.mSession.mID, 0 /* Object ID */,
1721 eventTypes, &pEvent);
1722 }
1723 catch (std::bad_alloc)
1724 {
1725 vrc = VERR_NO_MEMORY;
1726 }
1727
1728 if (RT_FAILURE(vrc))
1729 return vrc;
1730
1731 VBOXHGCMSVCPARM paParms[8];
1732
1733 int i = 0;
1734 paParms[i++].setUInt32(pEvent->ContextID());
1735 paParms[i++].setUInt32(mData.mProtocolVersion);
1736 paParms[i++].setPointer((void*)mData.mCredentials.mUser.c_str(),
1737 (ULONG)mData.mCredentials.mUser.length() + 1);
1738 paParms[i++].setPointer((void*)mData.mCredentials.mPassword.c_str(),
1739 (ULONG)mData.mCredentials.mPassword.length() + 1);
1740 paParms[i++].setPointer((void*)mData.mCredentials.mDomain.c_str(),
1741 (ULONG)mData.mCredentials.mDomain.length() + 1);
1742 paParms[i++].setUInt32(mData.mSession.mOpenFlags);
1743
1744 alock.release(); /* Drop write lock before sending. */
1745
1746 vrc = i_sendCommand(HOST_SESSION_CREATE, i, paParms);
1747 if (RT_SUCCESS(vrc))
1748 {
1749 vrc = i_waitForStatusChange(pEvent, GuestSessionWaitForFlag_Start,
1750 30 * 1000 /* 30s timeout */,
1751 NULL /* Session status */, pGuestRc);
1752 }
1753 else
1754 {
1755 /*
1756 * Unable to start guest session - update its current state.
1757 * Since there is no (official API) way to recover a failed guest session
1758 * this also marks the end state. Internally just calling this
1759 * same function again will work though.
1760 */
1761 mData.mStatus = GuestSessionStatus_Error;
1762 mData.mRC = vrc;
1763 }
1764
1765 unregisterWaitEvent(pEvent);
1766
1767 LogFlowFuncLeaveRC(vrc);
1768 return vrc;
1769}
1770
1771int GuestSession::i_startSessionAsync(void)
1772{
1773 LogFlowThisFuncEnter();
1774
1775 int vrc;
1776
1777 try
1778 {
1779 /* Asynchronously open the session on the guest by kicking off a
1780 * worker thread. */
1781 std::auto_ptr<GuestSessionTaskInternalOpen> pTask(new GuestSessionTaskInternalOpen(this));
1782 AssertReturn(pTask->isOk(), pTask->rc());
1783
1784 vrc = RTThreadCreate(NULL, GuestSession::i_startSessionThread,
1785 (void *)pTask.get(), 0,
1786 RTTHREADTYPE_MAIN_WORKER, 0,
1787 "gctlSesStart");
1788 if (RT_SUCCESS(vrc))
1789 {
1790 /* pTask is now owned by openSessionThread(), so release it. */
1791 pTask.release();
1792 }
1793 }
1794 catch(std::bad_alloc &)
1795 {
1796 vrc = VERR_NO_MEMORY;
1797 }
1798
1799 LogFlowFuncLeaveRC(vrc);
1800 return vrc;
1801}
1802
1803/* static */
1804DECLCALLBACK(int) GuestSession::i_startSessionThread(RTTHREAD Thread, void *pvUser)
1805{
1806 LogFlowFunc(("pvUser=%p\n", pvUser));
1807
1808 std::auto_ptr<GuestSessionTaskInternalOpen> pTask(static_cast<GuestSessionTaskInternalOpen*>(pvUser));
1809 AssertPtr(pTask.get());
1810
1811 const ComObjPtr<GuestSession> pSession(pTask->Session());
1812 Assert(!pSession.isNull());
1813
1814 AutoCaller autoCaller(pSession);
1815 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1816
1817 int vrc = pSession->i_startSessionInternal(NULL /* Guest rc, ignored */);
1818 /* Nothing to do here anymore. */
1819
1820 LogFlowFuncLeaveRC(vrc);
1821 return vrc;
1822}
1823
1824int GuestSession::i_pathRenameInternal(const Utf8Str &strSource, const Utf8Str &strDest,
1825 uint32_t uFlags, int *pGuestRc)
1826{
1827 AssertReturn(!(uFlags & ~PATHRENAME_FLAG_VALID_MASK), VERR_INVALID_PARAMETER);
1828
1829 LogFlowThisFunc(("strSource=%s, strDest=%s, uFlags=0x%x\n",
1830 strSource.c_str(), strDest.c_str(), uFlags));
1831
1832 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1833
1834 GuestWaitEvent *pEvent = NULL;
1835 int vrc = registerWaitEvent(mData.mSession.mID, 0 /* Object ID */,
1836 &pEvent);
1837 if (RT_FAILURE(vrc))
1838 return vrc;
1839
1840 /* Prepare HGCM call. */
1841 VBOXHGCMSVCPARM paParms[8];
1842 int i = 0;
1843 paParms[i++].setUInt32(pEvent->ContextID());
1844 paParms[i++].setPointer((void*)strSource.c_str(),
1845 (ULONG)strSource.length() + 1);
1846 paParms[i++].setPointer((void*)strDest.c_str(),
1847 (ULONG)strDest.length() + 1);
1848 paParms[i++].setUInt32(uFlags);
1849
1850 alock.release(); /* Drop write lock before sending. */
1851
1852 vrc = i_sendCommand(HOST_PATH_RENAME, i, paParms);
1853 if (RT_SUCCESS(vrc))
1854 {
1855 vrc = pEvent->Wait(30 * 1000);
1856 if ( vrc == VERR_GSTCTL_GUEST_ERROR
1857 && pGuestRc)
1858 *pGuestRc = pEvent->GuestResult();
1859 }
1860
1861 unregisterWaitEvent(pEvent);
1862
1863 LogFlowFuncLeaveRC(vrc);
1864 return vrc;
1865}
1866
1867int GuestSession::i_processRemoveFromList(GuestProcess *pProcess)
1868{
1869 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
1870
1871 LogFlowThisFunc(("pProcess=%p\n", pProcess));
1872
1873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1874
1875 int rc = VERR_NOT_FOUND;
1876
1877 ULONG uPID;
1878 HRESULT hr = pProcess->COMGETTER(PID)(&uPID);
1879 ComAssertComRC(hr);
1880
1881 LogFlowFunc(("Removing process (PID=%RU32) ...\n", uPID));
1882
1883 SessionProcesses::iterator itProcs = mData.mProcesses.begin();
1884 while (itProcs != mData.mProcesses.end())
1885 {
1886 if (pProcess == itProcs->second)
1887 {
1888#ifdef DEBUG_andy
1889 ULONG cRefs = pProcess->AddRef();
1890 Assert(cRefs >= 2);
1891 LogFlowFunc(("pProcess=%p, cRefs=%RU32\n", pProcess, cRefs - 1));
1892 pProcess->Release();
1893#endif
1894 /* Make sure to consume the pointer before the one of the
1895 * iterator gets released. */
1896 ComObjPtr<GuestProcess> pProc = pProcess;
1897
1898 hr = pProc->COMGETTER(PID)(&uPID);
1899 ComAssertComRC(hr);
1900
1901 Assert(mData.mProcesses.size());
1902 Assert(mData.mNumObjects);
1903 LogFlowFunc(("Removing process ID=%RU32 (Session: %RU32), guest PID=%RU32 (now total %zu processes, %RU32 objects)\n",
1904 pProcess->getObjectID(), mData.mSession.mID, uPID, mData.mProcesses.size() - 1, mData.mNumObjects - 1));
1905
1906 rc = pProcess->i_onRemove();
1907 mData.mProcesses.erase(itProcs);
1908 mData.mNumObjects--;
1909
1910 alock.release(); /* Release lock before firing off event. */
1911
1912 fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProc,
1913 uPID, false /* Process unregistered */);
1914 pProc.setNull();
1915 break;
1916 }
1917
1918 itProcs++;
1919 }
1920
1921 LogFlowFuncLeaveRC(rc);
1922 return rc;
1923}
1924
1925/**
1926 * Creates but does *not* start the process yet.
1927 *
1928 * See GuestProcess::startProcess() or GuestProcess::startProcessAsync() for
1929 * starting the process.
1930 *
1931 * @return IPRT status code.
1932 * @param procInfo
1933 * @param pProcess
1934 */
1935int GuestSession::i_processCreateExInternal(GuestProcessStartupInfo &procInfo, ComObjPtr<GuestProcess> &pProcess)
1936{
1937 LogFlowFunc(("mExe=%s, mFlags=%x, mTimeoutMS=%RU32\n",
1938 procInfo.mExecutable.c_str(), procInfo.mFlags, procInfo.mTimeoutMS));
1939#ifdef DEBUG
1940 if (procInfo.mArguments.size())
1941 {
1942 LogFlowFunc(("Arguments:"));
1943 ProcessArguments::const_iterator it = procInfo.mArguments.begin();
1944 while (it != procInfo.mArguments.end())
1945 {
1946 LogFlow((" %s", (*it).c_str()));
1947 it++;
1948 }
1949 LogFlow(("\n"));
1950 }
1951#endif
1952
1953 /* Validate flags. */
1954 if (procInfo.mFlags)
1955 {
1956 if ( !(procInfo.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
1957 && !(procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
1958 && !(procInfo.mFlags & ProcessCreateFlag_Hidden)
1959 && !(procInfo.mFlags & ProcessCreateFlag_NoProfile)
1960 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
1961 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdErr))
1962 {
1963 return VERR_INVALID_PARAMETER;
1964 }
1965 }
1966
1967 if ( (procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
1968 && ( (procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
1969 || (procInfo.mFlags & ProcessCreateFlag_WaitForStdErr)
1970 )
1971 )
1972 {
1973 return VERR_INVALID_PARAMETER;
1974 }
1975
1976 /* Adjust timeout. If set to 0, we define
1977 * an infinite timeout. */
1978 if (procInfo.mTimeoutMS == 0)
1979 procInfo.mTimeoutMS = UINT32_MAX;
1980
1981 /** @tood Implement process priority + affinity. */
1982
1983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1984
1985 int rc = VERR_MAX_PROCS_REACHED;
1986 if (mData.mNumObjects >= VBOX_GUESTCTRL_MAX_OBJECTS)
1987 return rc;
1988
1989 /* Create a new (host-based) process ID and assign it. */
1990 uint32_t uNewProcessID = 0;
1991 ULONG uTries = 0;
1992
1993 for (;;)
1994 {
1995 /* Is the context ID already used? */
1996 if (!i_processExists(uNewProcessID, NULL /* pProcess */))
1997 {
1998 /* Callback with context ID was not found. This means
1999 * we can use this context ID for our new callback we want
2000 * to add below. */
2001 rc = VINF_SUCCESS;
2002 break;
2003 }
2004 uNewProcessID++;
2005 if (uNewProcessID == VBOX_GUESTCTRL_MAX_OBJECTS)
2006 uNewProcessID = 0;
2007
2008 if (++uTries == VBOX_GUESTCTRL_MAX_OBJECTS)
2009 break; /* Don't try too hard. */
2010 }
2011
2012 if (RT_FAILURE(rc))
2013 return rc;
2014
2015 /* Create the process object. */
2016 HRESULT hr = pProcess.createObject();
2017 if (FAILED(hr))
2018 return VERR_COM_UNEXPECTED;
2019
2020 rc = pProcess->init(mParent->i_getConsole() /* Console */, this /* Session */,
2021 uNewProcessID, procInfo, mData.mpBaseEnvironment);
2022 if (RT_FAILURE(rc))
2023 return rc;
2024
2025 /* Add the created process to our map. */
2026 try
2027 {
2028 mData.mProcesses[uNewProcessID] = pProcess;
2029 mData.mNumObjects++;
2030 Assert(mData.mNumObjects <= VBOX_GUESTCTRL_MAX_OBJECTS);
2031
2032 LogFlowFunc(("Added new process (Session: %RU32) with process ID=%RU32 (now total %zu processes, %RU32 objects)\n",
2033 mData.mSession.mID, uNewProcessID, mData.mProcesses.size(), mData.mNumObjects));
2034
2035 alock.release(); /* Release lock before firing off event. */
2036
2037 fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProcess,
2038 0 /* PID */, true /* Process registered */);
2039 }
2040 catch (std::bad_alloc &)
2041 {
2042 rc = VERR_NO_MEMORY;
2043 }
2044
2045 return rc;
2046}
2047
2048inline bool GuestSession::i_processExists(uint32_t uProcessID, ComObjPtr<GuestProcess> *pProcess)
2049{
2050 SessionProcesses::const_iterator it = mData.mProcesses.find(uProcessID);
2051 if (it != mData.mProcesses.end())
2052 {
2053 if (pProcess)
2054 *pProcess = it->second;
2055 return true;
2056 }
2057 return false;
2058}
2059
2060inline int GuestSession::i_processGetByPID(ULONG uPID, ComObjPtr<GuestProcess> *pProcess)
2061{
2062 AssertReturn(uPID, false);
2063 /* pProcess is optional. */
2064
2065 SessionProcesses::iterator itProcs = mData.mProcesses.begin();
2066 for (; itProcs != mData.mProcesses.end(); itProcs++)
2067 {
2068 ComObjPtr<GuestProcess> pCurProc = itProcs->second;
2069 AutoCaller procCaller(pCurProc);
2070 if (procCaller.rc())
2071 return VERR_COM_INVALID_OBJECT_STATE;
2072
2073 ULONG uCurPID;
2074 HRESULT hr = pCurProc->COMGETTER(PID)(&uCurPID);
2075 ComAssertComRC(hr);
2076
2077 if (uCurPID == uPID)
2078 {
2079 if (pProcess)
2080 *pProcess = pCurProc;
2081 return VINF_SUCCESS;
2082 }
2083 }
2084
2085 return VERR_NOT_FOUND;
2086}
2087
2088int GuestSession::i_sendCommand(uint32_t uFunction,
2089 uint32_t uParms, PVBOXHGCMSVCPARM paParms)
2090{
2091 LogFlowThisFuncEnter();
2092
2093#ifndef VBOX_GUESTCTRL_TEST_CASE
2094 ComObjPtr<Console> pConsole = mParent->i_getConsole();
2095 Assert(!pConsole.isNull());
2096
2097 /* Forward the information to the VMM device. */
2098 VMMDev *pVMMDev = pConsole->i_getVMMDev();
2099 AssertPtr(pVMMDev);
2100
2101 LogFlowThisFunc(("uFunction=%RU32, uParms=%RU32\n", uFunction, uParms));
2102 int vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uFunction, uParms, paParms);
2103 if (RT_FAILURE(vrc))
2104 {
2105 /** @todo What to do here? */
2106 }
2107#else
2108 /* Not needed within testcases. */
2109 int vrc = VINF_SUCCESS;
2110#endif
2111 LogFlowFuncLeaveRC(vrc);
2112 return vrc;
2113}
2114
2115/* static */
2116HRESULT GuestSession::i_setErrorExternal(VirtualBoxBase *pInterface, int guestRc)
2117{
2118 AssertPtr(pInterface);
2119 AssertMsg(RT_FAILURE(guestRc), ("Guest rc does not indicate a failure when setting error\n"));
2120
2121 return pInterface->setError(VBOX_E_IPRT_ERROR, GuestSession::i_guestErrorToString(guestRc).c_str());
2122}
2123
2124/* Does not do locking; caller is responsible for that! */
2125int GuestSession::i_setSessionStatus(GuestSessionStatus_T sessionStatus, int sessionRc)
2126{
2127 LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, sessionRc=%Rrc\n",
2128 mData.mStatus, sessionStatus, sessionRc));
2129
2130 if (sessionStatus == GuestSessionStatus_Error)
2131 {
2132 AssertMsg(RT_FAILURE(sessionRc), ("Guest rc must be an error (%Rrc)\n", sessionRc));
2133 /* Do not allow overwriting an already set error. If this happens
2134 * this means we forgot some error checking/locking somewhere. */
2135 AssertMsg(RT_SUCCESS(mData.mRC), ("Guest rc already set (to %Rrc)\n", mData.mRC));
2136 }
2137 else
2138 AssertMsg(RT_SUCCESS(sessionRc), ("Guest rc must not be an error (%Rrc)\n", sessionRc));
2139
2140 if (mData.mStatus != sessionStatus)
2141 {
2142 mData.mStatus = sessionStatus;
2143 mData.mRC = sessionRc;
2144
2145 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
2146 HRESULT hr = errorInfo.createObject();
2147 ComAssertComRC(hr);
2148 int rc2 = errorInfo->initEx(VBOX_E_IPRT_ERROR, sessionRc,
2149 COM_IIDOF(IGuestSession), getComponentName(),
2150 i_guestErrorToString(sessionRc));
2151 AssertRC(rc2);
2152
2153 fireGuestSessionStateChangedEvent(mEventSource, this,
2154 mData.mSession.mID, sessionStatus, errorInfo);
2155 }
2156
2157 return VINF_SUCCESS;
2158}
2159
2160int GuestSession::i_signalWaiters(GuestSessionWaitResult_T enmWaitResult, int rc /*= VINF_SUCCESS */)
2161{
2162 /*LogFlowThisFunc(("enmWaitResult=%d, rc=%Rrc, mWaitCount=%RU32, mWaitEvent=%p\n",
2163 enmWaitResult, rc, mData.mWaitCount, mData.mWaitEvent));*/
2164
2165 /* Note: No write locking here -- already done in the caller. */
2166
2167 int vrc = VINF_SUCCESS;
2168 /*if (mData.mWaitEvent)
2169 vrc = mData.mWaitEvent->Signal(enmWaitResult, rc);*/
2170 LogFlowFuncLeaveRC(vrc);
2171 return vrc;
2172}
2173
2174int GuestSession::i_startTaskAsync(const Utf8Str &strTaskDesc,
2175 GuestSessionTask *pTask, ComObjPtr<Progress> &pProgress)
2176{
2177 LogFlowThisFunc(("strTaskDesc=%s, pTask=%p\n", strTaskDesc.c_str(), pTask));
2178
2179 AssertPtrReturn(pTask, VERR_INVALID_POINTER);
2180
2181 /* Create the progress object. */
2182 HRESULT hr = pProgress.createObject();
2183 if (FAILED(hr))
2184 return VERR_COM_UNEXPECTED;
2185
2186 hr = pProgress->init(static_cast<IGuestSession*>(this),
2187 Bstr(strTaskDesc).raw(),
2188 TRUE /* aCancelable */);
2189 if (FAILED(hr))
2190 return VERR_COM_UNEXPECTED;
2191
2192 /* Initialize our worker task. */
2193 std::auto_ptr<GuestSessionTask> task(pTask);
2194
2195 int rc = task->RunAsync(strTaskDesc, pProgress);
2196 if (RT_FAILURE(rc))
2197 return rc;
2198
2199 /* Don't destruct on success. */
2200 task.release();
2201
2202 LogFlowFuncLeaveRC(rc);
2203 return rc;
2204}
2205
2206/**
2207 * Determines the protocol version (sets mData.mProtocolVersion).
2208 *
2209 * This is called from the init method prior to to establishing a guest
2210 * session.
2211 *
2212 * @return IPRT status code.
2213 */
2214int GuestSession::i_determineProtocolVersion(void)
2215{
2216 /*
2217 * We currently do this based on the reported guest additions version,
2218 * ASSUMING that VBoxService and VBoxDrv are at the same version.
2219 */
2220 ComObjPtr<Guest> pGuest = mParent;
2221 AssertReturn(!pGuest.isNull(), VERR_NOT_SUPPORTED);
2222 uint32_t uGaVersion = pGuest->i_getAdditionsVersion();
2223
2224 /* Everyone supports version one, if they support anything at all. */
2225 mData.mProtocolVersion = 1;
2226
2227 /* Guest control 2.0 was introduced with 4.3.0. */
2228 if (uGaVersion >= VBOX_FULL_VERSION_MAKE(4,3,0))
2229 mData.mProtocolVersion = 2; /* Guest control 2.0. */
2230
2231 LogFlowThisFunc(("uGaVersion=%u.%u.%u => mProtocolVersion=%u\n",
2232 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
2233 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
2234
2235 /*
2236 * Inform the user about outdated guest additions (VM release log).
2237 */
2238 if (mData.mProtocolVersion < 2)
2239 LogRelMax(3, (tr("Warning: Guest Additions v%u.%u.%u only supports the older guest control protocol version %u.\n"
2240 " Please upgrade GAs to the current version to get full guest control capabilities.\n"),
2241 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
2242 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
2243
2244 return VINF_SUCCESS;
2245}
2246
2247int GuestSession::i_waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, GuestSessionWaitResult_T &waitResult, int *pGuestRc)
2248{
2249 LogFlowThisFuncEnter();
2250
2251 AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
2252
2253 /*LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p, pGuestRc=%p\n",
2254 fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent, pGuestRc));*/
2255
2256 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2257
2258 /* Did some error occur before? Then skip waiting and return. */
2259 if (mData.mStatus == GuestSessionStatus_Error)
2260 {
2261 waitResult = GuestSessionWaitResult_Error;
2262 AssertMsg(RT_FAILURE(mData.mRC), ("No error rc (%Rrc) set when guest session indicated an error\n", mData.mRC));
2263 if (pGuestRc)
2264 *pGuestRc = mData.mRC; /* Return last set error. */
2265 return VERR_GSTCTL_GUEST_ERROR;
2266 }
2267
2268 /* Guest Additions < 4.3 don't support session handling, skip. */
2269 if (mData.mProtocolVersion < 2)
2270 {
2271 waitResult = GuestSessionWaitResult_WaitFlagNotSupported;
2272
2273 LogFlowThisFunc(("Installed Guest Additions don't support waiting for dedicated sessions, skipping\n"));
2274 return VINF_SUCCESS;
2275 }
2276
2277 waitResult = GuestSessionWaitResult_None;
2278 if (fWaitFlags & GuestSessionWaitForFlag_Terminate)
2279 {
2280 switch (mData.mStatus)
2281 {
2282 case GuestSessionStatus_Terminated:
2283 case GuestSessionStatus_Down:
2284 waitResult = GuestSessionWaitResult_Terminate;
2285 break;
2286
2287 case GuestSessionStatus_TimedOutKilled:
2288 case GuestSessionStatus_TimedOutAbnormally:
2289 waitResult = GuestSessionWaitResult_Timeout;
2290 break;
2291
2292 case GuestSessionStatus_Error:
2293 /* Handled above. */
2294 break;
2295
2296 case GuestSessionStatus_Started:
2297 waitResult = GuestSessionWaitResult_Start;
2298 break;
2299
2300 case GuestSessionStatus_Undefined:
2301 case GuestSessionStatus_Starting:
2302 /* Do the waiting below. */
2303 break;
2304
2305 default:
2306 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
2307 return VERR_NOT_IMPLEMENTED;
2308 }
2309 }
2310 else if (fWaitFlags & GuestSessionWaitForFlag_Start)
2311 {
2312 switch (mData.mStatus)
2313 {
2314 case GuestSessionStatus_Started:
2315 case GuestSessionStatus_Terminating:
2316 case GuestSessionStatus_Terminated:
2317 case GuestSessionStatus_Down:
2318 waitResult = GuestSessionWaitResult_Start;
2319 break;
2320
2321 case GuestSessionStatus_Error:
2322 waitResult = GuestSessionWaitResult_Error;
2323 break;
2324
2325 case GuestSessionStatus_TimedOutKilled:
2326 case GuestSessionStatus_TimedOutAbnormally:
2327 waitResult = GuestSessionWaitResult_Timeout;
2328 break;
2329
2330 case GuestSessionStatus_Undefined:
2331 case GuestSessionStatus_Starting:
2332 /* Do the waiting below. */
2333 break;
2334
2335 default:
2336 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
2337 return VERR_NOT_IMPLEMENTED;
2338 }
2339 }
2340
2341 LogFlowThisFunc(("sessionStatus=%RU32, sessionRc=%Rrc, waitResult=%RU32\n",
2342 mData.mStatus, mData.mRC, waitResult));
2343
2344 /* No waiting needed? Return immediately using the last set error. */
2345 if (waitResult != GuestSessionWaitResult_None)
2346 {
2347 if (pGuestRc)
2348 *pGuestRc = mData.mRC; /* Return last set error (if any). */
2349 return RT_SUCCESS(mData.mRC) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
2350 }
2351
2352 int vrc;
2353
2354 GuestWaitEvent *pEvent = NULL;
2355 GuestEventTypes eventTypes;
2356 try
2357 {
2358 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
2359
2360 vrc = registerWaitEvent(mData.mSession.mID, 0 /* Object ID */,
2361 eventTypes, &pEvent);
2362 }
2363 catch (std::bad_alloc)
2364 {
2365 vrc = VERR_NO_MEMORY;
2366 }
2367
2368 if (RT_FAILURE(vrc))
2369 return vrc;
2370
2371 alock.release(); /* Release lock before waiting. */
2372
2373 GuestSessionStatus_T sessionStatus;
2374 vrc = i_waitForStatusChange(pEvent, fWaitFlags,
2375 uTimeoutMS, &sessionStatus, pGuestRc);
2376 if (RT_SUCCESS(vrc))
2377 {
2378 switch (sessionStatus)
2379 {
2380 case GuestSessionStatus_Started:
2381 waitResult = GuestSessionWaitResult_Start;
2382 break;
2383
2384 case GuestSessionStatus_Terminated:
2385 waitResult = GuestSessionWaitResult_Terminate;
2386 break;
2387
2388 case GuestSessionStatus_TimedOutKilled:
2389 case GuestSessionStatus_TimedOutAbnormally:
2390 waitResult = GuestSessionWaitResult_Timeout;
2391 break;
2392
2393 case GuestSessionStatus_Down:
2394 waitResult = GuestSessionWaitResult_Terminate;
2395 break;
2396
2397 case GuestSessionStatus_Error:
2398 waitResult = GuestSessionWaitResult_Error;
2399 break;
2400
2401 default:
2402 waitResult = GuestSessionWaitResult_Status;
2403 break;
2404 }
2405 }
2406
2407 unregisterWaitEvent(pEvent);
2408
2409 LogFlowFuncLeaveRC(vrc);
2410 return vrc;
2411}
2412
2413int GuestSession::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t fWaitFlags, uint32_t uTimeoutMS,
2414 GuestSessionStatus_T *pSessionStatus, int *pGuestRc)
2415{
2416 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
2417
2418 VBoxEventType_T evtType;
2419 ComPtr<IEvent> pIEvent;
2420 int vrc = waitForEvent(pEvent, uTimeoutMS,
2421 &evtType, pIEvent.asOutParam());
2422 if (RT_SUCCESS(vrc))
2423 {
2424 Assert(evtType == VBoxEventType_OnGuestSessionStateChanged);
2425
2426 ComPtr<IGuestSessionStateChangedEvent> pChangedEvent = pIEvent;
2427 Assert(!pChangedEvent.isNull());
2428
2429 GuestSessionStatus_T sessionStatus;
2430 pChangedEvent->COMGETTER(Status)(&sessionStatus);
2431 if (pSessionStatus)
2432 *pSessionStatus = sessionStatus;
2433
2434 ComPtr<IVirtualBoxErrorInfo> errorInfo;
2435 HRESULT hr = pChangedEvent->COMGETTER(Error)(errorInfo.asOutParam());
2436 ComAssertComRC(hr);
2437
2438 LONG lGuestRc;
2439 hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
2440 ComAssertComRC(hr);
2441 if (RT_FAILURE((int)lGuestRc))
2442 vrc = VERR_GSTCTL_GUEST_ERROR;
2443 if (pGuestRc)
2444 *pGuestRc = (int)lGuestRc;
2445
2446 LogFlowThisFunc(("Status changed event for session ID=%RU32, new status is: %RU32 (%Rrc)\n",
2447 mData.mSession.mID, sessionStatus,
2448 RT_SUCCESS((int)lGuestRc) ? VINF_SUCCESS : (int)lGuestRc));
2449 }
2450
2451 LogFlowFuncLeaveRC(vrc);
2452 return vrc;
2453}
2454
2455// implementation of public methods
2456/////////////////////////////////////////////////////////////////////////////
2457
2458HRESULT GuestSession::close()
2459{
2460#ifndef VBOX_WITH_GUEST_CONTROL
2461 ReturnComNotImplemented();
2462#else
2463 LogFlowThisFuncEnter();
2464
2465 AutoCaller autoCaller(this);
2466 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2467
2468 /* Close session on guest. */
2469 int guestRc = VINF_SUCCESS;
2470 int rc = i_closeSession(0 /* Flags */, 30 * 1000 /* Timeout */,
2471 &guestRc);
2472 /* On failure don't return here, instead do all the cleanup
2473 * work first and then return an error. */
2474
2475 /* Remove ourselves from the session list. */
2476 int rc2 = mParent->i_sessionRemove(this);
2477 if (rc2 == VERR_NOT_FOUND) /* Not finding the session anymore isn't critical. */
2478 rc2 = VINF_SUCCESS;
2479
2480 if (RT_SUCCESS(rc))
2481 rc = rc2;
2482
2483 LogFlowThisFunc(("Returning rc=%Rrc, guestRc=%Rrc\n",
2484 rc, guestRc));
2485 if (RT_FAILURE(rc))
2486 {
2487 if (rc == VERR_GSTCTL_GUEST_ERROR)
2488 return GuestSession::i_setErrorExternal(this, guestRc);
2489
2490 return setError(VBOX_E_IPRT_ERROR,
2491 tr("Closing guest session failed with %Rrc"), rc);
2492 }
2493
2494 return S_OK;
2495#endif /* VBOX_WITH_GUEST_CONTROL */
2496}
2497
2498HRESULT GuestSession::fileCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2499 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
2500{
2501 ReturnComNotImplemented();
2502}
2503
2504HRESULT GuestSession::fileCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDest,
2505 const std::vector<FileCopyFlag_T> &aFlags,
2506 ComPtr<IProgress> &aProgress)
2507{
2508#ifndef VBOX_WITH_GUEST_CONTROL
2509 ReturnComNotImplemented();
2510#else
2511
2512 LogFlowThisFuncEnter();
2513
2514 if (RT_UNLIKELY((aSource.c_str()) == NULL || *(aSource.c_str()) == '\0'))
2515 return setError(E_INVALIDARG, tr("No source specified"));
2516 if (RT_UNLIKELY((aDest.c_str()) == NULL || *(aDest.c_str()) == '\0'))
2517 return setError(E_INVALIDARG, tr("No destination specified"));
2518
2519 uint32_t fFlags = FileCopyFlag_None;
2520 if (aFlags.size())
2521 {
2522 for (size_t i = 0; i < aFlags.size(); i++)
2523 fFlags |= aFlags[i];
2524 }
2525/** @todo r=bird: fend off flags we don't implement here! */
2526
2527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2528
2529 HRESULT hr = S_OK;
2530
2531 try
2532 {
2533 ComObjPtr<Progress> pProgress;
2534 SessionTaskCopyFrom *pTask = new SessionTaskCopyFrom(this /* GuestSession */,
2535 aSource, aDest, fFlags);
2536 int rc = i_startTaskAsync(Utf8StrFmt(tr("Copying \"%s\" from guest to \"%s\" on the host"), aSource.c_str(),
2537 aDest.c_str()), pTask, pProgress);
2538 if (RT_SUCCESS(rc))
2539 /* Return progress to the caller. */
2540 hr = pProgress.queryInterfaceTo(aProgress.asOutParam());
2541 else
2542 hr = setError(VBOX_E_IPRT_ERROR,
2543 tr("Starting task for copying file \"%s\" from guest to \"%s\" on the host failed: %Rrc"), rc);
2544 }
2545 catch(std::bad_alloc &)
2546 {
2547 hr = E_OUTOFMEMORY;
2548 }
2549
2550 return hr;
2551#endif /* VBOX_WITH_GUEST_CONTROL */
2552}
2553
2554HRESULT GuestSession::fileCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDest,
2555 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
2556{
2557#ifndef VBOX_WITH_GUEST_CONTROL
2558 ReturnComNotImplemented();
2559#else
2560
2561 LogFlowThisFuncEnter();
2562
2563 if (RT_UNLIKELY((aSource.c_str()) == NULL || *(aSource.c_str()) == '\0'))
2564 return setError(E_INVALIDARG, tr("No source specified"));
2565 if (RT_UNLIKELY((aDest.c_str()) == NULL || *(aDest.c_str()) == '\0'))
2566 return setError(E_INVALIDARG, tr("No destination specified"));
2567
2568 uint32_t fFlags = FileCopyFlag_None;
2569 if (aFlags.size())
2570 {
2571 for (size_t i = 0; i < aFlags.size(); i++)
2572 fFlags |= aFlags[i];
2573 }
2574/** @todo r=bird: fend off flags we don't implement here! */
2575
2576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2577
2578 HRESULT hr = S_OK;
2579
2580 try
2581 {
2582 ComObjPtr<Progress> pProgress;
2583 SessionTaskCopyTo *pTask = new SessionTaskCopyTo(this /* GuestSession */,
2584 aSource, aDest, fFlags);
2585 AssertPtrReturn(pTask, E_OUTOFMEMORY);
2586 int rc = i_startTaskAsync(Utf8StrFmt(tr("Copying \"%s\" from host to \"%s\" on the guest"), aSource.c_str(),
2587 aDest.c_str()), pTask, pProgress);
2588 if (RT_SUCCESS(rc))
2589 {
2590 /* Return progress to the caller. */
2591 hr = pProgress.queryInterfaceTo(aProgress.asOutParam());
2592 }
2593 else
2594 hr = setError(VBOX_E_IPRT_ERROR,
2595 tr("Starting task for copying file \"%s\" from host to \"%s\" on the guest failed: %Rrc"), rc);
2596 }
2597 catch(std::bad_alloc &)
2598 {
2599 hr = E_OUTOFMEMORY;
2600 }
2601
2602 return hr;
2603#endif /* VBOX_WITH_GUEST_CONTROL */
2604}
2605
2606HRESULT GuestSession::directoryCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2607 const std::vector<DirectoryCopyFlags_T> &aFlags, ComPtr<IProgress> &aProgress)
2608{
2609 ReturnComNotImplemented();
2610}
2611
2612HRESULT GuestSession::directoryCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2613 const std::vector<DirectoryCopyFlags_T> &aFlags, ComPtr<IProgress> &aProgress)
2614{
2615 ReturnComNotImplemented();
2616}
2617
2618HRESULT GuestSession::directoryCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2619 const std::vector<DirectoryCopyFlags_T> &aFlags, ComPtr<IProgress> &aProgress)
2620{
2621 ReturnComNotImplemented();
2622}
2623
2624HRESULT GuestSession::directoryCreate(const com::Utf8Str &aPath, ULONG aMode,
2625 const std::vector<DirectoryCreateFlag_T> &aFlags)
2626{
2627#ifndef VBOX_WITH_GUEST_CONTROL
2628 ReturnComNotImplemented();
2629#else
2630 LogFlowThisFuncEnter();
2631
2632 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
2633 return setError(E_INVALIDARG, tr("No directory to create specified"));
2634
2635 uint32_t fFlags = DirectoryCreateFlag_None;
2636 if (aFlags.size())
2637 {
2638 for (size_t i = 0; i < aFlags.size(); i++)
2639 fFlags |= aFlags[i];
2640
2641 if (fFlags)
2642 if (!(fFlags & DirectoryCreateFlag_Parents))
2643 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), fFlags);
2644 }
2645
2646 HRESULT hr = S_OK;
2647
2648 ComObjPtr <GuestDirectory> pDirectory; int guestRc;
2649 int rc = i_directoryCreateInternal(aPath, (uint32_t)aMode, fFlags, &guestRc);
2650 if (RT_FAILURE(rc))
2651 {
2652 switch (rc)
2653 {
2654 case VERR_GSTCTL_GUEST_ERROR:
2655 /** @todo Handle VERR_NOT_EQUAL (meaning process exit code <> 0). */
2656 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Could not create directory"));
2657 break;
2658
2659 case VERR_INVALID_PARAMETER:
2660 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Invalid parameters given"));
2661 break;
2662
2663 case VERR_BROKEN_PIPE:
2664 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Unexpectedly aborted"));
2665 break;
2666
2667 default:
2668 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: %Rrc"), rc);
2669 break;
2670 }
2671 }
2672
2673 return hr;
2674#endif /* VBOX_WITH_GUEST_CONTROL */
2675}
2676
2677HRESULT GuestSession::directoryCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath,
2678 BOOL aSecure, com::Utf8Str &aDirectory)
2679{
2680#ifndef VBOX_WITH_GUEST_CONTROL
2681 ReturnComNotImplemented();
2682#else
2683 LogFlowThisFuncEnter();
2684
2685 if (RT_UNLIKELY((aTemplateName.c_str()) == NULL || *(aTemplateName.c_str()) == '\0'))
2686 return setError(E_INVALIDARG, tr("No template specified"));
2687 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
2688 return setError(E_INVALIDARG, tr("No directory name specified"));
2689
2690 HRESULT hr = S_OK;
2691
2692 int guestRc;
2693 int rc = i_objectCreateTempInternal(aTemplateName,
2694 aPath,
2695 true /* Directory */, aDirectory, &guestRc);
2696 if (!RT_SUCCESS(rc))
2697 {
2698 switch (rc)
2699 {
2700 case VERR_GSTCTL_GUEST_ERROR:
2701 hr = GuestProcess::i_setErrorExternal(this, guestRc);
2702 break;
2703
2704 default:
2705 hr = setError(VBOX_E_IPRT_ERROR, tr("Temporary directory creation \"%s\" with template \"%s\" failed: %Rrc"),
2706 aPath.c_str(), aTemplateName.c_str(), rc);
2707 break;
2708 }
2709 }
2710
2711 return hr;
2712#endif /* VBOX_WITH_GUEST_CONTROL */
2713}
2714
2715HRESULT GuestSession::directoryExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
2716{
2717#ifndef VBOX_WITH_GUEST_CONTROL
2718 ReturnComNotImplemented();
2719#else
2720 LogFlowThisFuncEnter();
2721
2722 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
2723 return setError(E_INVALIDARG, tr("No directory to check existence for specified"));
2724
2725 HRESULT hr = S_OK;
2726
2727 GuestFsObjData objData; int guestRc;
2728 int rc = i_directoryQueryInfoInternal(aPath, aFollowSymlinks != FALSE, objData, &guestRc);
2729 if (RT_SUCCESS(rc))
2730 *aExists = objData.mType == FsObjType_Directory;
2731 else
2732 {
2733 /** @todo r=bird: Looks like this code raises errors if the directory doesn't
2734 * exist... That's of course not right. */
2735 switch (rc)
2736 {
2737 case VERR_GSTCTL_GUEST_ERROR:
2738 hr = GuestProcess::i_setErrorExternal(this, guestRc);
2739 break;
2740
2741 default:
2742 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying directory existence \"%s\" failed: %Rrc"),
2743 aPath.c_str(), rc);
2744 break;
2745 }
2746 }
2747
2748 return hr;
2749#endif /* VBOX_WITH_GUEST_CONTROL */
2750}
2751
2752HRESULT GuestSession::directoryOpen(const com::Utf8Str &aPath, const com::Utf8Str &aFilter,
2753 const std::vector<DirectoryOpenFlag_T> &aFlags, ComPtr<IGuestDirectory> &aDirectory)
2754{
2755#ifndef VBOX_WITH_GUEST_CONTROL
2756 ReturnComNotImplemented();
2757#else
2758 LogFlowThisFuncEnter();
2759
2760 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
2761 return setError(E_INVALIDARG, tr("No directory to open specified"));
2762 if (RT_UNLIKELY((aFilter.c_str()) != NULL && *(aFilter.c_str()) != '\0'))
2763 return setError(E_INVALIDARG, tr("Directory filters are not implemented yet"));
2764
2765 uint32_t fFlags = DirectoryOpenFlag_None;
2766 if (aFlags.size())
2767 {
2768 for (size_t i = 0; i < aFlags.size(); i++)
2769 fFlags |= aFlags[i];
2770
2771 if (fFlags)
2772 return setError(E_INVALIDARG, tr("Open flags (%#x) not implemented yet"), fFlags);
2773 }
2774
2775 HRESULT hr = S_OK;
2776
2777 GuestDirectoryOpenInfo openInfo;
2778 openInfo.mPath = aPath;
2779 openInfo.mFilter = aFilter;
2780 openInfo.mFlags = fFlags;
2781
2782 ComObjPtr <GuestDirectory> pDirectory; int guestRc;
2783 int rc = i_directoryOpenInternal(openInfo, pDirectory, &guestRc);
2784 if (RT_SUCCESS(rc))
2785 {
2786 /* Return directory object to the caller. */
2787 hr = pDirectory.queryInterfaceTo(aDirectory.asOutParam());
2788 }
2789 else
2790 {
2791 switch (rc)
2792 {
2793 case VERR_INVALID_PARAMETER:
2794 hr = setError(VBOX_E_IPRT_ERROR, tr("Opening directory \"%s\" failed; invalid parameters given"),
2795 aPath.c_str());
2796 break;
2797
2798 case VERR_GSTCTL_GUEST_ERROR:
2799 hr = GuestDirectory::i_setErrorExternal(this, guestRc);
2800 break;
2801
2802 default:
2803 hr = setError(VBOX_E_IPRT_ERROR, tr("Opening directory \"%s\" failed: %Rrc"),
2804 aPath.c_str(),rc);
2805 break;
2806 }
2807 }
2808
2809 return hr;
2810#endif /* VBOX_WITH_GUEST_CONTROL */
2811}
2812
2813HRESULT GuestSession::directoryRemove(const com::Utf8Str &aPath)
2814{
2815#ifndef VBOX_WITH_GUEST_CONTROL
2816 ReturnComNotImplemented();
2817#else
2818 LogFlowThisFuncEnter();
2819
2820 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
2821 return setError(E_INVALIDARG, tr("No directory to remove specified"));
2822
2823 HRESULT hr = i_isReadyExternal();
2824 if (FAILED(hr))
2825 return hr;
2826
2827 /* No flags; only remove the directory when empty. */
2828 uint32_t uFlags = 0;
2829
2830 int guestRc;
2831 int vrc = i_directoryRemoveInternal(aPath, uFlags, &guestRc);
2832 if (RT_FAILURE(vrc))
2833 {
2834 switch (vrc)
2835 {
2836 case VERR_NOT_SUPPORTED:
2837 hr = setError(VBOX_E_IPRT_ERROR,
2838 tr("Handling removing guest directories not supported by installed Guest Additions"));
2839 break;
2840
2841 case VERR_GSTCTL_GUEST_ERROR:
2842 hr = GuestDirectory::i_setErrorExternal(this, guestRc);
2843 break;
2844
2845 default:
2846 hr = setError(VBOX_E_IPRT_ERROR, tr("Removing guest directory \"%s\" failed: %Rrc"),
2847 aPath.c_str(), vrc);
2848 break;
2849 }
2850 }
2851
2852 return hr;
2853#endif /* VBOX_WITH_GUEST_CONTROL */
2854}
2855
2856HRESULT GuestSession::directoryRemoveRecursive(const com::Utf8Str &aPath, const std::vector<DirectoryRemoveRecFlag_T> &aFlags,
2857 ComPtr<IProgress> &aProgress)
2858{
2859#ifndef VBOX_WITH_GUEST_CONTROL
2860 ReturnComNotImplemented();
2861#else
2862 LogFlowThisFuncEnter();
2863
2864 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
2865 return setError(E_INVALIDARG, tr("No directory to remove recursively specified"));
2866
2867/** @todo r=bird: Must check that the flags matches the hardcoded behavior
2868 * further down!! */
2869
2870 HRESULT hr = i_isReadyExternal();
2871 if (FAILED(hr))
2872 return hr;
2873
2874 ComObjPtr<Progress> pProgress;
2875 hr = pProgress.createObject();
2876 if (SUCCEEDED(hr))
2877 hr = pProgress->init(static_cast<IGuestSession *>(this),
2878 Bstr(tr("Removing guest directory")).raw(),
2879 TRUE /*aCancelable*/);
2880 if (FAILED(hr))
2881 return hr;
2882
2883 /* Note: At the moment we don't supply progress information while
2884 * deleting a guest directory recursively. So just complete
2885 * the progress object right now. */
2886 /** @todo Implement progress reporting on guest directory deletion! */
2887 hr = pProgress->i_notifyComplete(S_OK);
2888 if (FAILED(hr))
2889 return hr;
2890
2891 /* Remove the directory + all its contents. */
2892 uint32_t uFlags = DIRREMOVE_FLAG_RECURSIVE
2893 | DIRREMOVE_FLAG_CONTENT_AND_DIR;
2894 int guestRc;
2895 int vrc = i_directoryRemoveInternal(aPath, uFlags, &guestRc);
2896 if (RT_FAILURE(vrc))
2897 {
2898 switch (vrc)
2899 {
2900 case VERR_NOT_SUPPORTED:
2901 hr = setError(VBOX_E_IPRT_ERROR,
2902 tr("Handling removing guest directories recursively not supported by installed Guest Additions"));
2903 break;
2904
2905 case VERR_GSTCTL_GUEST_ERROR:
2906 hr = GuestFile::i_setErrorExternal(this, guestRc);
2907 break;
2908
2909 default:
2910 hr = setError(VBOX_E_IPRT_ERROR, tr("Recursively removing guest directory \"%s\" failed: %Rrc"),
2911 aPath.c_str(), vrc);
2912 break;
2913 }
2914 }
2915 else
2916 {
2917 pProgress.queryInterfaceTo(aProgress.asOutParam());
2918 }
2919
2920 return hr;
2921#endif /* VBOX_WITH_GUEST_CONTROL */
2922}
2923
2924HRESULT GuestSession::environmentScheduleSet(const com::Utf8Str &aName, const com::Utf8Str &aValue)
2925{
2926#ifndef VBOX_WITH_GUEST_CONTROL
2927 ReturnComNotImplemented();
2928#else
2929 LogFlowThisFuncEnter();
2930
2931 HRESULT hrc;
2932 if (RT_LIKELY(aName.isNotEmpty()))
2933 {
2934 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
2935 {
2936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2937 int vrc = mData.mEnvironmentChanges.setVariable(aName, aValue);
2938 if (RT_SUCCESS(vrc))
2939 hrc = S_OK;
2940 else
2941 hrc = setErrorVrc(vrc);
2942 }
2943 else
2944 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
2945 }
2946 else
2947 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
2948
2949 LogFlowThisFuncLeave();
2950 return hrc;
2951#endif /* VBOX_WITH_GUEST_CONTROL */
2952}
2953
2954HRESULT GuestSession::environmentScheduleUnset(const com::Utf8Str &aName)
2955{
2956#ifndef VBOX_WITH_GUEST_CONTROL
2957 ReturnComNotImplemented();
2958#else
2959 LogFlowThisFuncEnter();
2960 HRESULT hrc;
2961 if (RT_LIKELY(aName.isNotEmpty()))
2962 {
2963 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
2964 {
2965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2966 int vrc = mData.mEnvironmentChanges.unsetVariable(aName);
2967 if (RT_SUCCESS(vrc))
2968 hrc = S_OK;
2969 else
2970 hrc = setErrorVrc(vrc);
2971 }
2972 else
2973 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
2974 }
2975 else
2976 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
2977
2978 LogFlowThisFuncLeave();
2979 return hrc;
2980#endif /* VBOX_WITH_GUEST_CONTROL */
2981}
2982
2983HRESULT GuestSession::environmentGetBaseVariable(const com::Utf8Str &aName, com::Utf8Str &aValue)
2984{
2985#ifndef VBOX_WITH_GUEST_CONTROL
2986 ReturnComNotImplemented();
2987#else
2988 LogFlowThisFuncEnter();
2989 HRESULT hrc;
2990 if (RT_LIKELY(aName.isNotEmpty()))
2991 {
2992 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
2993 {
2994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2995 if (mData.mpBaseEnvironment)
2996 {
2997 int vrc = mData.mpBaseEnvironment->getVariable(aName, &aValue);
2998 if (RT_SUCCESS(vrc))
2999 hrc = S_OK;
3000 else
3001 hrc = setErrorVrc(vrc);
3002 }
3003 else if (mData.mProtocolVersion < 99999)
3004 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
3005 else
3006 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
3007 }
3008 else
3009 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3010 }
3011 else
3012 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3013
3014 LogFlowThisFuncLeave();
3015 return hrc;
3016#endif /* VBOX_WITH_GUEST_CONTROL */
3017}
3018
3019HRESULT GuestSession::environmentDoesBaseVariableExist(const com::Utf8Str &aName, BOOL *aExists)
3020{
3021#ifndef VBOX_WITH_GUEST_CONTROL
3022 ReturnComNotImplemented();
3023#else
3024 LogFlowThisFuncEnter();
3025 *aExists = FALSE;
3026 HRESULT hrc;
3027 if (RT_LIKELY(aName.isNotEmpty()))
3028 {
3029 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3030 {
3031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3032 if (mData.mpBaseEnvironment)
3033 {
3034 hrc = S_OK;
3035 *aExists = mData.mpBaseEnvironment->doesVariableExist(aName);
3036 }
3037 else if (mData.mProtocolVersion < 99999)
3038 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
3039 else
3040 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
3041 }
3042 else
3043 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3044 }
3045 else
3046 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3047
3048 LogFlowThisFuncLeave();
3049 return hrc;
3050#endif /* VBOX_WITH_GUEST_CONTROL */
3051}
3052
3053HRESULT GuestSession::fileCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath, BOOL aSecure,
3054 ComPtr<IGuestFile> &aFile)
3055{
3056#ifndef VBOX_WITH_GUEST_CONTROL
3057 ReturnComNotImplemented();
3058#else
3059 LogFlowThisFuncEnter();
3060
3061
3062 ReturnComNotImplemented();
3063#endif /* VBOX_WITH_GUEST_CONTROL */
3064}
3065
3066HRESULT GuestSession::fileExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3067{
3068#ifndef VBOX_WITH_GUEST_CONTROL
3069 ReturnComNotImplemented();
3070#else
3071 LogFlowThisFuncEnter();
3072
3073/** @todo r=bird: Treat empty file with a FALSE return. */
3074 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3075 return setError(E_INVALIDARG, tr("No file to check existence for specified"));
3076
3077 GuestFsObjData objData; int guestRc;
3078 int vrc = i_fileQueryInfoInternal(aPath, aFollowSymlinks != FALSE, objData, &guestRc);
3079 if (RT_SUCCESS(vrc))
3080 {
3081 *aExists = TRUE;
3082 return S_OK;
3083 }
3084
3085 HRESULT hr = S_OK;
3086
3087 switch (vrc)
3088 {
3089 case VERR_GSTCTL_GUEST_ERROR:
3090 hr = GuestProcess::i_setErrorExternal(this, guestRc);
3091 break;
3092
3093/** @todo r=bird: what about VERR_PATH_NOT_FOUND and VERR_FILE_NOT_FOUND?
3094 * Where does that get converted to *aExists = FALSE? */
3095 case VERR_NOT_A_FILE:
3096 *aExists = FALSE;
3097 break;
3098
3099 default:
3100 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying file information for \"%s\" failed: %Rrc"),
3101 aPath.c_str(), vrc);
3102 break;
3103 }
3104
3105 return hr;
3106#endif /* VBOX_WITH_GUEST_CONTROL */
3107}
3108
3109HRESULT GuestSession::fileOpen(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3110 ULONG aCreationMode, ComPtr<IGuestFile> &aFile)
3111{
3112#ifndef VBOX_WITH_GUEST_CONTROL
3113 ReturnComNotImplemented();
3114#else
3115 LogFlowThisFuncEnter();
3116 return fileOpenEx(aPath, aAccessMode, aOpenAction, FileSharingMode_All, aCreationMode, 0 /* aOffset */, aFile);
3117#endif /* VBOX_WITH_GUEST_CONTROL */
3118}
3119
3120HRESULT GuestSession::fileOpenEx(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3121 FileSharingMode_T aSharingMode, ULONG aCreationMode, LONG64 aOffset, ComPtr<IGuestFile> &aFile)
3122{
3123#ifndef VBOX_WITH_GUEST_CONTROL
3124 ReturnComNotImplemented();
3125#else
3126 LogFlowThisFuncEnter();
3127
3128 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3129 return setError(E_INVALIDARG, tr("No file to open specified"));
3130
3131 HRESULT hr = i_isReadyExternal();
3132 if (FAILED(hr))
3133 return hr;
3134
3135 GuestFileOpenInfo openInfo;
3136 openInfo.mFileName = aPath;
3137 openInfo.mCreationMode = aCreationMode;
3138 openInfo.mInitialOffset = aOffset;
3139
3140 /* convert + validate aAccessMode to the old format. */
3141 openInfo.mAccessMode = aAccessMode;
3142 switch (aAccessMode)
3143 {
3144 case (FileAccessMode_T)FileAccessMode_ReadOnly: openInfo.mpszAccessMode = "r"; break;
3145 case (FileAccessMode_T)FileAccessMode_WriteOnly: openInfo.mpszAccessMode = "w"; break;
3146 case (FileAccessMode_T)FileAccessMode_ReadWrite: openInfo.mpszAccessMode = "r+"; break;
3147 case (FileAccessMode_T)FileAccessMode_AppendOnly:
3148 /* fall thru */
3149 case (FileAccessMode_T)FileAccessMode_AppendRead:
3150 return setError(E_NOTIMPL, tr("Append access modes are not yet implemented"));
3151 default:
3152 return setError(E_INVALIDARG, tr("Unknown FileAccessMode value %u (%#x)"), aAccessMode, aAccessMode);
3153 }
3154
3155 /* convert + validate aOpenAction to the old format. */
3156 openInfo.mOpenAction = aOpenAction;
3157 switch (aOpenAction)
3158 {
3159 case (FileOpenAction_T)FileOpenAction_OpenExisting: openInfo.mpszOpenAction = "oe"; break;
3160 case (FileOpenAction_T)FileOpenAction_OpenOrCreate: openInfo.mpszOpenAction = "oc"; break;
3161 case (FileOpenAction_T)FileOpenAction_CreateNew: openInfo.mpszOpenAction = "ce"; break;
3162 case (FileOpenAction_T)FileOpenAction_CreateOrReplace: openInfo.mpszOpenAction = "ca"; break;
3163 case (FileOpenAction_T)FileOpenAction_OpenExistingTruncated: openInfo.mpszOpenAction = "ot"; break;
3164 case (FileOpenAction_T)FileOpenAction_AppendOrCreate:
3165 openInfo.mpszOpenAction = "oa"; /** @todo get rid of this one and implement AppendOnly/AppendRead. */
3166 break;
3167 default:
3168 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3169 }
3170
3171 /* validate aSharingMode */
3172 openInfo.mSharingMode = aSharingMode;
3173 switch (aSharingMode)
3174 {
3175 case (FileSharingMode_T)FileSharingMode_All: /* OK */ break;
3176 case (FileSharingMode_T)FileSharingMode_Read:
3177 case (FileSharingMode_T)FileSharingMode_Write:
3178 case (FileSharingMode_T)FileSharingMode_ReadWrite:
3179 case (FileSharingMode_T)FileSharingMode_Delete:
3180 case (FileSharingMode_T)FileSharingMode_ReadDelete:
3181 case (FileSharingMode_T)FileSharingMode_WriteDelete:
3182 return setError(E_NOTIMPL, tr("Only FileSharingMode_All is currently implemented"));
3183
3184 default:
3185 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3186 }
3187
3188 ComObjPtr <GuestFile> pFile;
3189 int guestRc;
3190 int vrc = i_fileOpenInternal(openInfo, pFile, &guestRc);
3191 if (RT_SUCCESS(vrc))
3192 /* Return directory object to the caller. */
3193 hr = pFile.queryInterfaceTo(aFile.asOutParam());
3194 else
3195 {
3196 switch (vrc)
3197 {
3198 case VERR_NOT_SUPPORTED:
3199 hr = setError(VBOX_E_IPRT_ERROR,
3200 tr("Handling guest files not supported by installed Guest Additions"));
3201 break;
3202
3203 case VERR_GSTCTL_GUEST_ERROR:
3204 hr = GuestFile::i_setErrorExternal(this, guestRc);
3205 break;
3206
3207 default:
3208 hr = setError(VBOX_E_IPRT_ERROR, tr("Opening guest file \"%s\" failed: %Rrc"),
3209 aPath.c_str(), vrc);
3210 break;
3211 }
3212 }
3213
3214 return hr;
3215#endif /* VBOX_WITH_GUEST_CONTROL */
3216}
3217
3218HRESULT GuestSession::fileQuerySize(const com::Utf8Str &aPath, BOOL aFollowSymlinks, LONG64 *aSize)
3219{
3220#ifndef VBOX_WITH_GUEST_CONTROL
3221 ReturnComNotImplemented();
3222#else
3223 LogFlowThisFuncEnter();
3224
3225 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3226 return setError(E_INVALIDARG, tr("No file to query size for specified"));
3227
3228 HRESULT hr = S_OK;
3229
3230 int64_t llSize; int guestRc;
3231 int vrc = i_fileQuerySizeInternal(aPath, aFollowSymlinks != FALSE, &llSize, &guestRc);
3232 if (RT_SUCCESS(vrc))
3233 *aSize = llSize;
3234 else
3235 {
3236 switch (vrc)
3237 {
3238 case VERR_GSTCTL_GUEST_ERROR:
3239 hr = GuestProcess::i_setErrorExternal(this, guestRc);
3240 break;
3241
3242 default:
3243 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying file size failed: %Rrc"), vrc);
3244 break;
3245 }
3246 }
3247
3248 return hr;
3249#endif /* VBOX_WITH_GUEST_CONTROL */
3250}
3251
3252HRESULT GuestSession::fsObjExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3253{
3254#ifndef VBOX_WITH_GUEST_CONTROL
3255 ReturnComNotImplemented();
3256#else
3257 LogFlowThisFuncEnter();
3258
3259 HRESULT hrc = S_OK;
3260 *aExists = false;
3261 if (RT_LIKELY(aPath.isNotEmpty()))
3262 {
3263 GuestFsObjData objData;
3264 int rcGuest;
3265 int vrc = i_fsQueryInfoInternal(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3266 if (RT_SUCCESS(vrc))
3267 *aExists = TRUE;
3268 else if ( vrc == VERR_NOT_A_FILE
3269 || vrc == VERR_PATH_NOT_FOUND
3270 || vrc == VERR_FILE_NOT_FOUND
3271 || vrc == VERR_INVALID_NAME)
3272 hrc = S_OK; /* Ignore these vrc values. */
3273 else if (vrc == VERR_GSTCTL_GUEST_ERROR) /** @todo What _is_ rcGuest, really? Stuff like VERR_NOT_A_FILE too?? */
3274 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3275 else
3276 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3277 }
3278 /* else: If the file name is empty, there is no way it can exists. So, don't
3279 be a tedious and return E_INVALIDARG, simply return FALSE. */
3280 LogFlowThisFuncLeave();
3281 return hrc;
3282#endif /* VBOX_WITH_GUEST_CONTROL */
3283}
3284
3285HRESULT GuestSession::fsObjQueryInfo(const com::Utf8Str &aPath, BOOL aFollowSymlinks, ComPtr<IGuestFsObjInfo> &aInfo)
3286{
3287#ifndef VBOX_WITH_GUEST_CONTROL
3288 ReturnComNotImplemented();
3289#else
3290 LogFlowThisFuncEnter();
3291
3292 HRESULT hrc = S_OK;
3293 if (RT_LIKELY(aPath.isNotEmpty()))
3294 {
3295 GuestFsObjData Info;
3296 int rcGuest;
3297 int vrc = i_fsQueryInfoInternal(aPath, aFollowSymlinks != FALSE, Info, &rcGuest);
3298 if (RT_SUCCESS(vrc))
3299 {
3300 ComObjPtr<GuestFsObjInfo> ptrFsObjInfo;
3301 hrc = ptrFsObjInfo.createObject();
3302 if (SUCCEEDED(hrc))
3303 {
3304 vrc = ptrFsObjInfo->init(Info);
3305 if (RT_SUCCESS(vrc))
3306 hrc = ptrFsObjInfo.queryInterfaceTo(aInfo.asOutParam());
3307 else
3308 hrc = setErrorVrc(vrc);
3309 }
3310 }
3311 else if (vrc == VERR_GSTCTL_GUEST_ERROR)
3312 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3313 else
3314 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3315 }
3316 /* else: If the file name is empty, there is no way it can exists. So, don't
3317 be a tedious and return E_INVALIDARG, simply return FALSE. */
3318 LogFlowThisFuncLeave();
3319 return hrc;
3320#endif /* VBOX_WITH_GUEST_CONTROL */
3321}
3322
3323HRESULT GuestSession::fsObjRemove(const com::Utf8Str &aPath)
3324{
3325#ifndef VBOX_WITH_GUEST_CONTROL
3326 ReturnComNotImplemented();
3327#else
3328 LogFlowThisFuncEnter();
3329
3330 if (RT_UNLIKELY(aPath.isEmpty()))
3331 return setError(E_INVALIDARG, tr("Empty path specified"));
3332
3333 HRESULT hr = S_OK;
3334
3335 int guestRc;
3336 int vrc = i_fileRemoveInternal(aPath, &guestRc);
3337 if (RT_FAILURE(vrc))
3338 {
3339 switch (vrc)
3340 {
3341 case VERR_GSTCTL_GUEST_ERROR:
3342 hr = GuestProcess::i_setErrorExternal(this, guestRc);
3343 break;
3344
3345 default:
3346 hr = setError(VBOX_E_IPRT_ERROR, tr("Removing file \"%s\" failed: %Rrc"),
3347 aPath.c_str(), vrc);
3348 break;
3349 }
3350 }
3351
3352 return hr;
3353#endif /* VBOX_WITH_GUEST_CONTROL */
3354}
3355
3356HRESULT GuestSession::fsObjRename(const com::Utf8Str &aSource,
3357 const com::Utf8Str &aDestination,
3358 const std::vector<FsObjRenameFlag_T> &aFlags)
3359{
3360#ifndef VBOX_WITH_GUEST_CONTROL
3361 ReturnComNotImplemented();
3362#else
3363 LogFlowThisFuncEnter();
3364
3365 if (RT_UNLIKELY(aSource.isEmpty()))
3366 return setError(E_INVALIDARG, tr("No source path specified"));
3367
3368 if (RT_UNLIKELY(aDestination.isEmpty()))
3369 return setError(E_INVALIDARG, tr("No destination path specified"));
3370
3371 HRESULT hr = i_isReadyExternal();
3372 if (FAILED(hr))
3373 return hr;
3374
3375 /* Combine, validate and convert flags. */
3376 uint32_t fApiFlags = 0;
3377 for (size_t i = 0; i < aFlags.size(); i++)
3378 fApiFlags |= aFlags[i];
3379 if (fApiFlags & ~((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace))
3380 return setError(E_INVALIDARG, tr("Unknown rename flag: %#x"), fApiFlags);
3381
3382 AssertCompile(FsObjRenameFlag_NoReplace == 0);
3383 AssertCompile(FsObjRenameFlag_Replace != 0);
3384 uint32_t fBackend;
3385 if ((fApiFlags & ((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace)) == FsObjRenameFlag_Replace)
3386 fBackend = PATHRENAME_FLAG_REPLACE;
3387 else
3388 fBackend = PATHRENAME_FLAG_NO_REPLACE;
3389
3390 /* Call worker to do the job. */
3391 int guestRc;
3392 int vrc = i_pathRenameInternal(aSource, aDestination, fBackend, &guestRc);
3393 if (RT_FAILURE(vrc))
3394 {
3395 switch (vrc)
3396 {
3397 case VERR_NOT_SUPPORTED:
3398 hr = setError(VBOX_E_IPRT_ERROR,
3399 tr("Handling renaming guest directories not supported by installed Guest Additions"));
3400 break;
3401
3402 case VERR_GSTCTL_GUEST_ERROR:
3403 hr = setError(VBOX_E_IPRT_ERROR,
3404 tr("Renaming guest directory failed: %Rrc"), guestRc);
3405 break;
3406
3407 default:
3408 hr = setError(VBOX_E_IPRT_ERROR, tr("Renaming guest directory \"%s\" failed: %Rrc"),
3409 aSource.c_str(), vrc);
3410 break;
3411 }
3412 }
3413
3414 return hr;
3415#endif /* VBOX_WITH_GUEST_CONTROL */
3416}
3417
3418HRESULT GuestSession::fsObjMove(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3419 const std::vector<FsObjMoveFlags_T> &aFlags, ComPtr<IProgress> &aProgress)
3420{
3421 ReturnComNotImplemented();
3422}
3423
3424HRESULT GuestSession::fsObjSetACL(const com::Utf8Str &aPath, BOOL aFollowSymlinks, const com::Utf8Str &aAcl, ULONG aMode)
3425{
3426 ReturnComNotImplemented();
3427}
3428
3429
3430HRESULT GuestSession::processCreate(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
3431 const std::vector<com::Utf8Str> &aEnvironment,
3432 const std::vector<ProcessCreateFlag_T> &aFlags,
3433 ULONG aTimeoutMS, ComPtr<IGuestProcess> &aGuestProcess)
3434{
3435#ifndef VBOX_WITH_GUEST_CONTROL
3436 ReturnComNotImplemented();
3437#else
3438 LogFlowThisFuncEnter();
3439
3440 std::vector<LONG> affinityIgnored;
3441
3442 return processCreateEx(aExecutable, aArguments, aEnvironment, aFlags, aTimeoutMS, ProcessPriority_Default,
3443 affinityIgnored, aGuestProcess);
3444#endif /* VBOX_WITH_GUEST_CONTROL */
3445}
3446
3447HRESULT GuestSession::processCreateEx(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
3448 const std::vector<com::Utf8Str> &aEnvironment,
3449 const std::vector<ProcessCreateFlag_T> &aFlags, ULONG aTimeoutMS,
3450 ProcessPriority_T aPriority, const std::vector<LONG> &aAffinity,
3451 ComPtr<IGuestProcess> &aGuestProcess)
3452{
3453#ifndef VBOX_WITH_GUEST_CONTROL
3454 ReturnComNotImplemented();
3455#else
3456 LogFlowThisFuncEnter();
3457
3458 /** @todo r=bird: Check input better? aPriority is passed on to the guest
3459 * without any validation. Flags not existing in this vbox version are
3460 * ignored, potentially doing something entirely different than what the
3461 * caller had in mind. */
3462
3463 /*
3464 * Must have an executable to execute. If none is given, we try use the
3465 * zero'th argument.
3466 */
3467 const char *pszExecutable = aExecutable.c_str();
3468 if (RT_UNLIKELY(pszExecutable == NULL || *pszExecutable == '\0'))
3469 {
3470 if (aArguments.size() > 0)
3471 pszExecutable = aArguments[0].c_str();
3472 if (pszExecutable == NULL || *pszExecutable == '\0')
3473 return setError(E_INVALIDARG, tr("No command to execute specified"));
3474 }
3475
3476 /*
3477 * Check the session.
3478 */
3479 HRESULT hr = i_isReadyExternal();
3480 if (FAILED(hr))
3481 return hr;
3482
3483 /*
3484 * Build the process startup info.
3485 */
3486 GuestProcessStartupInfo procInfo;
3487
3488 /* Executable and arguments. */
3489 procInfo.mExecutable = pszExecutable;
3490 if (aArguments.size())
3491 for (size_t i = 0; i < aArguments.size(); i++)
3492 procInfo.mArguments.push_back(aArguments[i]);
3493
3494 /* Combine the environment changes associated with the ones passed in by
3495 the caller, giving priority to the latter. The changes are putenv style
3496 and will be applied to the standard environment for the guest user. */
3497 int vrc = procInfo.mEnvironmentChanges.copy(mData.mEnvironmentChanges);
3498 if (RT_SUCCESS(vrc))
3499 vrc = procInfo.mEnvironmentChanges.applyPutEnvArray(aEnvironment);
3500 if (RT_SUCCESS(vrc))
3501 {
3502 /* Convert the flag array into a mask. */
3503 if (aFlags.size())
3504 for (size_t i = 0; i < aFlags.size(); i++)
3505 procInfo.mFlags |= aFlags[i];
3506
3507 procInfo.mTimeoutMS = aTimeoutMS;
3508
3509 /** @todo use RTCPUSET instead of archaic 64-bit variables! */
3510 if (aAffinity.size())
3511 for (size_t i = 0; i < aAffinity.size(); i++)
3512 if (aAffinity[i])
3513 procInfo.mAffinity |= (uint64_t)1 << i;
3514
3515 procInfo.mPriority = aPriority;
3516
3517 /*
3518 * Create a guest process object.
3519 */
3520 ComObjPtr<GuestProcess> pProcess;
3521 vrc = i_processCreateExInternal(procInfo, pProcess);
3522 if (RT_SUCCESS(vrc))
3523 {
3524 /* Return guest session to the caller. */
3525 hr = pProcess.queryInterfaceTo(aGuestProcess.asOutParam());
3526 if (SUCCEEDED(hr))
3527 {
3528 /*
3529 * Start the process.
3530 */
3531 vrc = pProcess->i_startProcessAsync();
3532 if (RT_SUCCESS(vrc))
3533 {
3534 LogFlowFuncLeaveRC(vrc);
3535 return S_OK;
3536 }
3537
3538 hr = setErrorVrc(vrc, tr("Failed to start guest process: %Rrc"), vrc);
3539 /** @todo r=bird: What happens to the interface that *aGuestProcess points to
3540 * now? Looks like a leak or an undocument hack of sorts... */
3541 }
3542 }
3543 else if (vrc == VERR_MAX_PROCS_REACHED)
3544 hr = setErrorVrc(vrc, tr("Maximum number of concurrent guest processes per session (%u) reached"),
3545 VBOX_GUESTCTRL_MAX_OBJECTS);
3546 else
3547 hr = setErrorVrc(vrc, tr("Failed to create guest process object: %Rrc"), vrc);
3548 }
3549 else
3550 hr = setErrorVrc(vrc, tr("Failed to set up the environment: %Rrc"), vrc);
3551
3552 LogFlowFuncLeaveRC(vrc);
3553 return hr;
3554#endif /* VBOX_WITH_GUEST_CONTROL */
3555}
3556
3557HRESULT GuestSession::processGet(ULONG aPid, ComPtr<IGuestProcess> &aGuestProcess)
3558
3559{
3560#ifndef VBOX_WITH_GUEST_CONTROL
3561 ReturnComNotImplemented();
3562#else
3563 LogFlowThisFunc(("PID=%RU32\n", aPid));
3564
3565 if (aPid == 0)
3566 return setError(E_INVALIDARG, tr("No valid process ID (PID) specified"));
3567
3568 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3569
3570 HRESULT hr = S_OK;
3571
3572 ComObjPtr<GuestProcess> pProcess;
3573 int rc = i_processGetByPID(aPid, &pProcess);
3574 if (RT_FAILURE(rc))
3575 hr = setError(E_INVALIDARG, tr("No process with PID %RU32 found"), aPid);
3576
3577 /* This will set (*aProcess) to NULL if pProgress is NULL. */
3578 HRESULT hr2 = pProcess.queryInterfaceTo(aGuestProcess.asOutParam());
3579 if (SUCCEEDED(hr))
3580 hr = hr2;
3581
3582 LogFlowThisFunc(("aProcess=%p, hr=%Rhrc\n", (IGuestProcess*)aGuestProcess, hr));
3583 return hr;
3584#endif /* VBOX_WITH_GUEST_CONTROL */
3585}
3586
3587HRESULT GuestSession::symlinkCreate(const com::Utf8Str &aSource, const com::Utf8Str &aTarget, SymlinkType_T aType)
3588{
3589#ifndef VBOX_WITH_GUEST_CONTROL
3590 ReturnComNotImplemented();
3591#else
3592 LogFlowThisFuncEnter();
3593
3594 ReturnComNotImplemented();
3595#endif /* VBOX_WITH_GUEST_CONTROL */
3596}
3597
3598HRESULT GuestSession::symlinkExists(const com::Utf8Str &aSymlink, BOOL *aExists)
3599
3600{
3601#ifndef VBOX_WITH_GUEST_CONTROL
3602 ReturnComNotImplemented();
3603#else
3604 LogFlowThisFuncEnter();
3605
3606 ReturnComNotImplemented();
3607#endif /* VBOX_WITH_GUEST_CONTROL */
3608}
3609
3610HRESULT GuestSession::symlinkRead(const com::Utf8Str &aSymlink, const std::vector<SymlinkReadFlag_T> &aFlags,
3611 com::Utf8Str &aTarget)
3612{
3613#ifndef VBOX_WITH_GUEST_CONTROL
3614 ReturnComNotImplemented();
3615#else
3616 LogFlowThisFuncEnter();
3617
3618 ReturnComNotImplemented();
3619#endif /* VBOX_WITH_GUEST_CONTROL */
3620}
3621
3622HRESULT GuestSession::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
3623{
3624#ifndef VBOX_WITH_GUEST_CONTROL
3625 ReturnComNotImplemented();
3626#else
3627 LogFlowThisFuncEnter();
3628
3629 /*
3630 * Note: Do not hold any locks here while waiting!
3631 */
3632 HRESULT hr = S_OK;
3633
3634 int guestRc; GuestSessionWaitResult_T waitResult;
3635 int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &guestRc);
3636 if (RT_SUCCESS(vrc))
3637 *aReason = waitResult;
3638 else
3639 {
3640 switch (vrc)
3641 {
3642 case VERR_GSTCTL_GUEST_ERROR:
3643 hr = GuestSession::i_setErrorExternal(this, guestRc);
3644 break;
3645
3646 case VERR_TIMEOUT:
3647 *aReason = GuestSessionWaitResult_Timeout;
3648 break;
3649
3650 default:
3651 {
3652 const char *pszSessionName = mData.mSession.mName.c_str();
3653 hr = setError(VBOX_E_IPRT_ERROR,
3654 tr("Waiting for guest session \"%s\" failed: %Rrc"),
3655 pszSessionName ? pszSessionName : tr("Unnamed"), vrc);
3656 break;
3657 }
3658 }
3659 }
3660
3661 LogFlowFuncLeaveRC(vrc);
3662 return hr;
3663#endif /* VBOX_WITH_GUEST_CONTROL */
3664}
3665
3666HRESULT GuestSession::waitForArray(const std::vector<GuestSessionWaitForFlag_T> &aWaitFor, ULONG aTimeoutMS,
3667 GuestSessionWaitResult_T *aReason)
3668{
3669#ifndef VBOX_WITH_GUEST_CONTROL
3670 ReturnComNotImplemented();
3671#else
3672 LogFlowThisFuncEnter();
3673
3674 /*
3675 * Note: Do not hold any locks here while waiting!
3676 */
3677 uint32_t fWaitFor = GuestSessionWaitForFlag_None;
3678 for (size_t i = 0; i < aWaitFor.size(); i++)
3679 fWaitFor |= aWaitFor[i];
3680
3681 return WaitFor(fWaitFor, aTimeoutMS, aReason);
3682#endif /* VBOX_WITH_GUEST_CONTROL */
3683}
3684
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette