VirtualBox

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

Last change on this file since 45780 was 45780, checked in by vboxsync, 12 years ago

Main/GuestCtrl: Use active listeners instead of passive ones because of performance reasons (untested).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 90.9 KB
Line 
1
2/* $Id: GuestSessionImpl.cpp 45780 2013-04-26 15:19:33Z vboxsync $ */
3/** @file
4 * VirtualBox Main - Guest session handling.
5 */
6
7/*
8 * Copyright (C) 2012-2013 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19
20/*******************************************************************************
21* Header Files *
22*******************************************************************************/
23#include "GuestImpl.h"
24#include "GuestErrorInfoImpl.h"
25#include "GuestSessionImpl.h"
26#include "GuestCtrlImplPrivate.h"
27
28#include "Global.h"
29#include "AutoCaller.h"
30#include "ProgressImpl.h"
31#include "VBoxEvents.h"
32#include "VMMDev.h"
33
34#include <memory> /* For auto_ptr. */
35
36#include <iprt/cpp/utils.h> /* For unconst(). */
37#include <iprt/env.h>
38#include <iprt/file.h> /* For CopyTo/From. */
39
40#include <VBox/com/array.h>
41#include <VBox/com/listeners.h>
42#include <VBox/version.h>
43
44#ifdef LOG_GROUP
45 #undef LOG_GROUP
46#endif
47#define LOG_GROUP LOG_GROUP_GUEST_CONTROL
48#include <VBox/log.h>
49
50
51/**
52 * Base class representing an internal
53 * asynchronous session task.
54 */
55class GuestSessionTaskInternal
56{
57public:
58
59 GuestSessionTaskInternal(GuestSession *pSession)
60 : mSession(pSession),
61 mRC(VINF_SUCCESS) { }
62
63 virtual ~GuestSessionTaskInternal(void) { }
64
65 int rc(void) const { return mRC; }
66 bool isOk(void) const { return RT_SUCCESS(mRC); }
67 const ComObjPtr<GuestSession> &Session(void) const { return mSession; }
68
69protected:
70
71 const ComObjPtr<GuestSession> mSession;
72 int mRC;
73};
74
75/**
76 * Class for asynchronously opening a guest session.
77 */
78class GuestSessionTaskInternalOpen : public GuestSessionTaskInternal
79{
80public:
81
82 GuestSessionTaskInternalOpen(GuestSession *pSession)
83 : GuestSessionTaskInternal(pSession) { }
84};
85
86/**
87 * Internal listener class to serve events in an
88 * active manner, e.g. without polling delays.
89 */
90class GuestSessionListener
91{
92public:
93
94 GuestSessionListener(void)
95 {
96 }
97
98 HRESULT init(GuestSession *pSession)
99 {
100 mSession = pSession;
101 return S_OK;
102 }
103
104 void uninit(void)
105 {
106 mSession.setNull();
107 }
108
109 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
110 {
111 switch (aType)
112 {
113 case VBoxEventType_OnGuestSessionStateChanged:
114 {
115 Assert(!mSession.isNull());
116 int rc2 = mSession->signalWaitEvents(aType, aEvent);
117#ifdef DEBUG_andy
118 LogFlowFunc(("Signalling events of type=%ld, session=%p resulted in rc=%Rrc\n",
119 aType, mSession, rc2));
120#endif
121 break;
122 }
123
124 default:
125 AssertMsgFailed(("Unhandled event %ld\n", aType));
126 break;
127 }
128
129 return S_OK;
130 }
131
132private:
133
134 ComObjPtr<GuestSession> mSession;
135};
136typedef ListenerImpl<GuestSessionListener, GuestSession*> GuestSessionListenerImpl;
137
138VBOX_LISTENER_DECLARE(GuestProcessListenerImpl)
139
140// constructor / destructor
141/////////////////////////////////////////////////////////////////////////////
142
143DEFINE_EMPTY_CTOR_DTOR(GuestSession)
144
145HRESULT GuestSession::FinalConstruct(void)
146{
147 LogFlowThisFunc(("\n"));
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 mParent = pGuest;
186
187 /* Copy over startup info. */
188 /** @todo Use an overloaded copy operator. Later. */
189 mData.mSession.mID = ssInfo.mID;
190 mData.mSession.mIsInternal = ssInfo.mIsInternal;
191 mData.mSession.mName = ssInfo.mName;
192 mData.mSession.mOpenFlags = ssInfo.mOpenFlags;
193 mData.mSession.mOpenTimeoutMS = ssInfo.mOpenTimeoutMS;
194
195 /** @todo Use an overloaded copy operator. Later. */
196 mData.mCredentials.mUser = guestCreds.mUser;
197 mData.mCredentials.mPassword = guestCreds.mPassword;
198 mData.mCredentials.mDomain = guestCreds.mDomain;
199
200 mData.mRC = VINF_SUCCESS;
201 mData.mStatus = GuestSessionStatus_Undefined;
202 mData.mNumObjects = 0;
203
204 HRESULT hr;
205
206 int rc = queryInfo();
207 if (RT_SUCCESS(rc))
208 {
209 unconst(mEventSource).createObject();
210 Assert(!mEventSource.isNull());
211 HRESULT hr = mEventSource->init(static_cast<IGuestSession*>(this));
212 if (FAILED(hr))
213 rc = VERR_COM_UNEXPECTED;
214 }
215
216 if (RT_SUCCESS(rc))
217 {
218 try
219 {
220 GuestSessionListener *pListener = new GuestSessionListener();
221 ComObjPtr<GuestSessionListenerImpl> thisListener;
222 hr = thisListener.createObject();
223 if (SUCCEEDED(hr))
224 hr = thisListener->init(pListener, this);
225
226 if (SUCCEEDED(hr))
227 {
228 mListener = thisListener;
229
230 com::SafeArray <VBoxEventType_T> eventTypes;
231 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
232 hr = mEventSource->RegisterListener(mListener,
233 ComSafeArrayAsInParam(eventTypes),
234 TRUE /* Active listener */);
235 if (SUCCEEDED(hr))
236 {
237 rc = RTCritSectInit(&mWaitEventCritSect);
238 AssertRC(rc);
239 }
240 else
241 rc = VERR_COM_UNEXPECTED;
242 }
243 else
244 rc = VERR_COM_UNEXPECTED;
245 }
246 catch(std::bad_alloc &)
247 {
248 rc = VERR_NO_MEMORY;
249 }
250 }
251
252 if (RT_SUCCESS(rc))
253 {
254 /* Confirm a successful initialization when it's the case. */
255 autoInitSpan.setSucceeded();
256 }
257 else
258 autoInitSpan.setFailed();
259
260 LogFlowFuncLeaveRC(rc);
261 return rc;
262#endif /* VBOX_WITH_GUEST_CONTROL */
263}
264
265/**
266 * Uninitializes the instance.
267 * Called from FinalRelease().
268 */
269void GuestSession::uninit(void)
270{
271 LogFlowThisFuncEnter();
272
273 /* Enclose the state transition Ready->InUninit->NotReady. */
274 AutoUninitSpan autoUninitSpan(this);
275 if (autoUninitSpan.uninitDone())
276 return;
277
278 int rc = VINF_SUCCESS;
279
280#ifdef VBOX_WITH_GUEST_CONTROL
281 LogFlowThisFunc(("Closing directories (%RU64 total)\n",
282 mData.mDirectories.size()));
283 for (SessionDirectories::iterator itDirs = mData.mDirectories.begin();
284 itDirs != mData.mDirectories.end(); ++itDirs)
285 {
286 (*itDirs)->uninit();
287 }
288 mData.mDirectories.clear();
289
290 LogFlowThisFunc(("Closing files (%RU64 total)\n",
291 mData.mFiles.size()));
292 for (SessionFiles::iterator itFiles = mData.mFiles.begin();
293 itFiles != mData.mFiles.end(); ++itFiles)
294 {
295 itFiles->second->uninit();
296 }
297 mData.mFiles.clear();
298
299 LogFlowThisFunc(("Closing processes (%RU64 total)\n",
300 mData.mProcesses.size()));
301 for (SessionProcesses::iterator itProcs = mData.mProcesses.begin();
302 itProcs != mData.mProcesses.end(); ++itProcs)
303 {
304 itProcs->second->uninit();
305 }
306 mData.mProcesses.clear();
307
308 LogFlowThisFunc(("mNumObjects=%RU32\n", mData.mNumObjects));
309
310 unconst(mEventSource).setNull();
311 unregisterEventListener();
312
313#endif /* VBOX_WITH_GUEST_CONTROL */
314 LogFlowFuncLeaveRC(rc);
315}
316
317// implementation of public getters/setters for attributes
318/////////////////////////////////////////////////////////////////////////////
319
320STDMETHODIMP GuestSession::COMGETTER(User)(BSTR *aUser)
321{
322#ifndef VBOX_WITH_GUEST_CONTROL
323 ReturnComNotImplemented();
324#else
325 LogFlowThisFuncEnter();
326
327 CheckComArgOutPointerValid(aUser);
328
329 AutoCaller autoCaller(this);
330 if (FAILED(autoCaller.rc())) return autoCaller.rc();
331
332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
333
334 mData.mCredentials.mUser.cloneTo(aUser);
335
336 LogFlowFuncLeaveRC(S_OK);
337 return S_OK;
338#endif /* VBOX_WITH_GUEST_CONTROL */
339}
340
341STDMETHODIMP GuestSession::COMGETTER(Domain)(BSTR *aDomain)
342{
343#ifndef VBOX_WITH_GUEST_CONTROL
344 ReturnComNotImplemented();
345#else
346 LogFlowThisFuncEnter();
347
348 CheckComArgOutPointerValid(aDomain);
349
350 AutoCaller autoCaller(this);
351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
352
353 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
354
355 mData.mCredentials.mDomain.cloneTo(aDomain);
356
357 LogFlowFuncLeaveRC(S_OK);
358 return S_OK;
359#endif /* VBOX_WITH_GUEST_CONTROL */
360}
361
362STDMETHODIMP GuestSession::COMGETTER(Name)(BSTR *aName)
363{
364#ifndef VBOX_WITH_GUEST_CONTROL
365 ReturnComNotImplemented();
366#else
367 LogFlowThisFuncEnter();
368
369 CheckComArgOutPointerValid(aName);
370
371 AutoCaller autoCaller(this);
372 if (FAILED(autoCaller.rc())) return autoCaller.rc();
373
374 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
375
376 mData.mSession.mName.cloneTo(aName);
377
378 LogFlowFuncLeaveRC(S_OK);
379 return S_OK;
380#endif /* VBOX_WITH_GUEST_CONTROL */
381}
382
383STDMETHODIMP GuestSession::COMGETTER(Id)(ULONG *aId)
384{
385#ifndef VBOX_WITH_GUEST_CONTROL
386 ReturnComNotImplemented();
387#else
388 LogFlowThisFuncEnter();
389
390 CheckComArgOutPointerValid(aId);
391
392 AutoCaller autoCaller(this);
393 if (FAILED(autoCaller.rc())) return autoCaller.rc();
394
395 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
396
397 *aId = mData.mSession.mID;
398
399 LogFlowFuncLeaveRC(S_OK);
400 return S_OK;
401#endif /* VBOX_WITH_GUEST_CONTROL */
402}
403
404STDMETHODIMP GuestSession::COMGETTER(Status)(GuestSessionStatus_T *aStatus)
405{
406#ifndef VBOX_WITH_GUEST_CONTROL
407 ReturnComNotImplemented();
408#else
409 LogFlowThisFuncEnter();
410
411 CheckComArgOutPointerValid(aStatus);
412
413 AutoCaller autoCaller(this);
414 if (FAILED(autoCaller.rc())) return autoCaller.rc();
415
416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
417
418 *aStatus = mData.mStatus;
419
420 LogFlowFuncLeaveRC(S_OK);
421 return S_OK;
422#endif /* VBOX_WITH_GUEST_CONTROL */
423}
424
425STDMETHODIMP GuestSession::COMGETTER(Timeout)(ULONG *aTimeout)
426{
427#ifndef VBOX_WITH_GUEST_CONTROL
428 ReturnComNotImplemented();
429#else
430 LogFlowThisFuncEnter();
431
432 CheckComArgOutPointerValid(aTimeout);
433
434 AutoCaller autoCaller(this);
435 if (FAILED(autoCaller.rc())) return autoCaller.rc();
436
437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
438
439 *aTimeout = mData.mTimeout;
440
441 LogFlowFuncLeaveRC(S_OK);
442 return S_OK;
443#endif /* VBOX_WITH_GUEST_CONTROL */
444}
445
446STDMETHODIMP GuestSession::COMSETTER(Timeout)(ULONG aTimeout)
447{
448#ifndef VBOX_WITH_GUEST_CONTROL
449 ReturnComNotImplemented();
450#else
451 LogFlowThisFuncEnter();
452
453 AutoCaller autoCaller(this);
454 if (FAILED(autoCaller.rc())) return autoCaller.rc();
455
456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
457
458 mData.mTimeout = aTimeout;
459
460 LogFlowFuncLeaveRC(S_OK);
461 return S_OK;
462#endif /* VBOX_WITH_GUEST_CONTROL */
463}
464
465STDMETHODIMP GuestSession::COMGETTER(Environment)(ComSafeArrayOut(BSTR, aEnvironment))
466{
467#ifndef VBOX_WITH_GUEST_CONTROL
468 ReturnComNotImplemented();
469#else
470 LogFlowThisFuncEnter();
471
472 CheckComArgOutSafeArrayPointerValid(aEnvironment);
473
474 AutoCaller autoCaller(this);
475 if (FAILED(autoCaller.rc())) return autoCaller.rc();
476
477 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
478
479 size_t cEnvVars = mData.mEnvironment.Size();
480 LogFlowThisFunc(("[%s]: cEnvVars=%RU32\n",
481 mData.mSession.mName.c_str(), cEnvVars));
482 com::SafeArray<BSTR> environment(cEnvVars);
483
484 for (size_t i = 0; i < cEnvVars; i++)
485 {
486 Bstr strEnv(mData.mEnvironment.Get(i));
487 strEnv.cloneTo(&environment[i]);
488 }
489 environment.detachTo(ComSafeArrayOutArg(aEnvironment));
490
491 LogFlowFuncLeaveRC(S_OK);
492 return S_OK;
493#endif /* VBOX_WITH_GUEST_CONTROL */
494}
495
496STDMETHODIMP GuestSession::COMSETTER(Environment)(ComSafeArrayIn(IN_BSTR, aValues))
497{
498#ifndef VBOX_WITH_GUEST_CONTROL
499 ReturnComNotImplemented();
500#else
501 LogFlowThisFuncEnter();
502
503 AutoCaller autoCaller(this);
504 if (FAILED(autoCaller.rc())) return autoCaller.rc();
505
506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
507
508 com::SafeArray<IN_BSTR> environment(ComSafeArrayInArg(aValues));
509
510 int rc = VINF_SUCCESS;
511 for (size_t i = 0; i < environment.size() && RT_SUCCESS(rc); i++)
512 {
513 Utf8Str strEnv(environment[i]);
514 if (!strEnv.isEmpty()) /* Silently skip empty entries. */
515 rc = mData.mEnvironment.Set(strEnv);
516 }
517
518 HRESULT hr = RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
519 LogFlowFuncLeaveRC(hr);
520 return hr;
521#endif /* VBOX_WITH_GUEST_CONTROL */
522}
523
524STDMETHODIMP GuestSession::COMGETTER(Processes)(ComSafeArrayOut(IGuestProcess *, aProcesses))
525{
526#ifndef VBOX_WITH_GUEST_CONTROL
527 ReturnComNotImplemented();
528#else
529 LogFlowThisFuncEnter();
530
531 CheckComArgOutSafeArrayPointerValid(aProcesses);
532
533 AutoCaller autoCaller(this);
534 if (FAILED(autoCaller.rc())) return autoCaller.rc();
535
536 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
537
538 SafeIfaceArray<IGuestProcess> collection(mData.mProcesses);
539 collection.detachTo(ComSafeArrayOutArg(aProcesses));
540
541 LogFlowFuncLeaveRC(S_OK);
542 return S_OK;
543#endif /* VBOX_WITH_GUEST_CONTROL */
544}
545
546STDMETHODIMP GuestSession::COMGETTER(Directories)(ComSafeArrayOut(IGuestDirectory *, aDirectories))
547{
548#ifndef VBOX_WITH_GUEST_CONTROL
549 ReturnComNotImplemented();
550#else
551 LogFlowThisFuncEnter();
552
553 CheckComArgOutSafeArrayPointerValid(aDirectories);
554
555 AutoCaller autoCaller(this);
556 if (FAILED(autoCaller.rc())) return autoCaller.rc();
557
558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
559
560 SafeIfaceArray<IGuestDirectory> collection(mData.mDirectories);
561 collection.detachTo(ComSafeArrayOutArg(aDirectories));
562
563 LogFlowFuncLeaveRC(S_OK);
564 return S_OK;
565#endif /* VBOX_WITH_GUEST_CONTROL */
566}
567
568STDMETHODIMP GuestSession::COMGETTER(Files)(ComSafeArrayOut(IGuestFile *, aFiles))
569{
570#ifndef VBOX_WITH_GUEST_CONTROL
571 ReturnComNotImplemented();
572#else
573 LogFlowThisFuncEnter();
574
575 CheckComArgOutSafeArrayPointerValid(aFiles);
576
577 AutoCaller autoCaller(this);
578 if (FAILED(autoCaller.rc())) return autoCaller.rc();
579
580 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
581
582 SafeIfaceArray<IGuestFile> collection(mData.mFiles);
583 collection.detachTo(ComSafeArrayOutArg(aFiles));
584
585 LogFlowFuncLeaveRC(S_OK);
586 return S_OK;
587#endif /* VBOX_WITH_GUEST_CONTROL */
588}
589
590STDMETHODIMP GuestSession::COMGETTER(EventSource)(IEventSource ** aEventSource)
591{
592#ifndef VBOX_WITH_GUEST_CONTROL
593 ReturnComNotImplemented();
594#else
595 LogFlowThisFuncEnter();
596
597 CheckComArgOutPointerValid(aEventSource);
598
599 AutoCaller autoCaller(this);
600 if (FAILED(autoCaller.rc())) return autoCaller.rc();
601
602 // no need to lock - lifetime constant
603 mEventSource.queryInterfaceTo(aEventSource);
604
605 LogFlowFuncLeaveRC(S_OK);
606 return S_OK;
607#endif /* VBOX_WITH_GUEST_CONTROL */
608}
609
610// private methods
611///////////////////////////////////////////////////////////////////////////////
612
613int GuestSession::closeSession(uint32_t uFlags, uint32_t uTimeoutMS, int *pGuestRc)
614{
615 LogFlowThisFunc(("uFlags=%x, uTimeoutMS=%RU32\n", uFlags, uTimeoutMS));
616
617 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
618
619 /* Legacy Guest Additions don't support opening dedicated
620 guest sessions. */
621 if (mData.mProtocolVersion < 2)
622 {
623 LogFlowThisFunc(("Installed Guest Additions don't support closing dedicated sessions, skipping\n"));
624 return VINF_SUCCESS;
625 }
626
627 /** @todo uFlags validation. */
628
629 if (mData.mStatus != GuestSessionStatus_Started)
630 return VINF_SUCCESS;
631
632 int vrc;
633
634 GuestWaitEvent *pEvent = NULL;
635 std::list < VBoxEventType_T > eventTypes;
636 try
637 {
638 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
639
640 vrc = registerEvent(mData.mSession.mID, 0 /* Object ID */,
641 eventTypes, &pEvent);
642 }
643 catch (std::bad_alloc)
644 {
645 vrc = VERR_NO_MEMORY;
646 }
647
648 if (RT_FAILURE(vrc))
649 return vrc;
650
651 LogFlowThisFunc(("Sending closing request to guest session ID=%RU32, uFlags=%x\n",
652 mData.mSession.mID, uFlags));
653
654 VBOXHGCMSVCPARM paParms[4];
655 int i = 0;
656 paParms[i++].setUInt32(pEvent->ContextID());
657 paParms[i++].setUInt32(uFlags);
658
659 vrc = sendCommand(HOST_SESSION_CLOSE, i, paParms);
660 if (RT_SUCCESS(vrc))
661 {
662 alock.release(); /* Drop the write lock before waiting. */
663
664 vrc = waitForStatusChange(pEvent, GuestSessionWaitForFlag_Terminate, uTimeoutMS,
665 NULL /* Session status */, pGuestRc);
666 }
667
668 unregisterEvent(pEvent);
669
670 LogFlowFuncLeaveRC(vrc);
671 return vrc;
672}
673
674int GuestSession::directoryRemoveFromList(GuestDirectory *pDirectory)
675{
676 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
677
678 for (SessionDirectories::iterator itDirs = mData.mDirectories.begin();
679 itDirs != mData.mDirectories.end(); ++itDirs)
680 {
681 if (pDirectory == (*itDirs))
682 {
683 Bstr strName;
684 HRESULT hr = (*itDirs)->COMGETTER(DirectoryName)(strName.asOutParam());
685 ComAssertComRC(hr);
686
687 Assert(mData.mDirectories.size());
688 LogFlowFunc(("Removing directory \"%s\" (Session: %RU32) (now total %ld directories)\n",
689 Utf8Str(strName).c_str(), mData.mSession.mID, mData.mDirectories.size() - 1));
690
691 mData.mDirectories.erase(itDirs);
692 return VINF_SUCCESS;
693 }
694 }
695
696 return VERR_NOT_FOUND;
697}
698
699int GuestSession::directoryCreateInternal(const Utf8Str &strPath, uint32_t uMode, uint32_t uFlags, int *pGuestRc)
700{
701 /* pGuestRc is optional. */
702
703 LogFlowThisFunc(("strPath=%s, uMode=%x, uFlags=%x\n",
704 strPath.c_str(), uMode, uFlags));
705
706 GuestProcessStartupInfo procInfo;
707 procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_MKDIR);
708 procInfo.mFlags = ProcessCreateFlag_Hidden;
709
710 int vrc = VINF_SUCCESS;
711
712 /* Construct arguments. */
713 if (uFlags & DirectoryCreateFlag_Parents)
714 procInfo.mArguments.push_back(Utf8Str("--parents")); /* We also want to create the parent directories. */
715 if (uMode)
716 {
717 procInfo.mArguments.push_back(Utf8Str("--mode")); /* Set the creation mode. */
718
719 char szMode[16];
720 if (RTStrPrintf(szMode, sizeof(szMode), "%o", uMode))
721 {
722 procInfo.mArguments.push_back(Utf8Str(szMode));
723 }
724 else
725 vrc = VERR_BUFFER_OVERFLOW;
726 }
727 procInfo.mArguments.push_back(strPath); /* The directory we want to create. */
728
729 if (RT_SUCCESS(vrc))
730 {
731 int guestRc;
732 GuestProcessTool procTool;
733 vrc = procTool.Init(this, procInfo, false /* Async */, &guestRc);
734 if (RT_SUCCESS(vrc))
735 {
736 if (RT_SUCCESS(guestRc))
737 vrc = procTool.Wait(GUESTPROCESSTOOL_FLAG_NONE, &guestRc);
738 }
739
740 if (RT_SUCCESS(vrc))
741 {
742 if (RT_SUCCESS(guestRc))
743 guestRc = procTool.TerminatedOk(NULL /* Exit code */);
744 }
745
746 if ( vrc == VERR_GSTCTL_GUEST_ERROR
747 && pGuestRc)
748 {
749 *pGuestRc = guestRc;
750 }
751 }
752
753 LogFlowFuncLeaveRC(vrc);
754 return vrc;
755}
756
757int GuestSession::directoryQueryInfoInternal(const Utf8Str &strPath, GuestFsObjData &objData, int *pGuestRc)
758{
759 LogFlowThisFunc(("strPath=%s\n", strPath.c_str()));
760
761 int vrc = fsQueryInfoInternal(strPath, objData, pGuestRc);
762 if (RT_SUCCESS(vrc))
763 {
764 vrc = objData.mType == FsObjType_Directory
765 ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
766 }
767
768 LogFlowFuncLeaveRC(vrc);
769 return vrc;
770}
771
772int GuestSession::objectCreateTempInternal(const Utf8Str &strTemplate, const Utf8Str &strPath,
773 bool fDirectory, const Utf8Str &strName, int *pGuestRc)
774{
775 GuestProcessStartupInfo procInfo;
776 procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_MKTEMP);
777 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
778 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
779 if (fDirectory)
780 procInfo.mArguments.push_back(Utf8Str("-d"));
781 if (strPath.length()) /* Otherwise use /tmp or equivalent. */
782 {
783 procInfo.mArguments.push_back(Utf8Str("-t"));
784 procInfo.mArguments.push_back(strPath);
785 }
786 procInfo.mArguments.push_back(strTemplate);
787
788 GuestProcessTool procTool; int guestRc;
789 int vrc = procTool.Init(this, procInfo, false /* Async */, &guestRc);
790 if (RT_SUCCESS(vrc))
791 {
792 if (RT_SUCCESS(guestRc))
793 vrc = procTool.Wait(GUESTPROCESSTOOL_FLAG_NONE, &guestRc);
794 }
795
796 if (RT_SUCCESS(vrc))
797 {
798 if (RT_SUCCESS(guestRc))
799 guestRc = procTool.TerminatedOk(NULL /* Exit code */);
800 }
801
802 if ( vrc == VERR_GSTCTL_GUEST_ERROR
803 && pGuestRc)
804 {
805 *pGuestRc = guestRc;
806 }
807
808 LogFlowFuncLeaveRC(vrc);
809 return vrc;
810}
811
812int GuestSession::directoryOpenInternal(const Utf8Str &strPath, const Utf8Str &strFilter,
813 uint32_t uFlags, ComObjPtr<GuestDirectory> &pDirectory)
814{
815 LogFlowThisFunc(("strPath=%s, strPath=%s, uFlags=%x\n",
816 strPath.c_str(), strFilter.c_str(), uFlags));
817
818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
819
820 /* Create the directory object. */
821 HRESULT hr = pDirectory.createObject();
822 if (FAILED(hr))
823 return VERR_COM_UNEXPECTED;
824
825 int vrc = pDirectory->init(this /* Parent */,
826 strPath, strFilter, uFlags);
827 if (RT_FAILURE(vrc))
828 return vrc;
829
830 /* Add the created directory to our vector. */
831 mData.mDirectories.push_back(pDirectory);
832
833 LogFlowFunc(("Added new directory \"%s\" (Session: %RU32)\n",
834 strPath.c_str(), mData.mSession.mID));
835
836 LogFlowFuncLeaveRC(vrc);
837 return vrc;
838}
839
840int GuestSession::dispatchToFile(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
841{
842 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
843
844 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
845 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
846
847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
848
849 uint32_t uFileID = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCtxCb->uContextID);
850#ifdef DEBUG
851 LogFlowFunc(("uFileID=%RU32 (%RU32 total)\n",
852 uFileID, mData.mFiles.size()));
853#endif
854 int rc;
855 SessionFiles::const_iterator itFile
856 = mData.mFiles.find(uFileID);
857 if (itFile != mData.mFiles.end())
858 {
859 ComObjPtr<GuestFile> pFile(itFile->second);
860 Assert(!pFile.isNull());
861
862 alock.release();
863
864 rc = pFile->callbackDispatcher(pCtxCb, pSvcCb);
865 }
866 else
867 rc = VERR_NOT_FOUND;
868
869 LogFlowFuncLeaveRC(rc);
870 return rc;
871}
872
873int GuestSession::dispatchToProcess(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
874{
875 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
876
877 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
878 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
879
880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
881
882 uint32_t uProcessID = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCtxCb->uContextID);
883#ifdef DEBUG
884 LogFlowFunc(("uProcessID=%RU32 (%RU32 total)\n",
885 uProcessID, mData.mProcesses.size()));
886#endif
887 int rc;
888 SessionProcesses::const_iterator itProc
889 = mData.mProcesses.find(uProcessID);
890 if (itProc != mData.mProcesses.end())
891 {
892 ComObjPtr<GuestProcess> pProcess(itProc->second);
893 Assert(!pProcess.isNull());
894
895 /* Set protocol version so that pSvcCb can
896 * be interpreted right. */
897 pCtxCb->uProtocol = mData.mProtocolVersion;
898
899 alock.release();
900 rc = pProcess->callbackDispatcher(pCtxCb, pSvcCb);
901 }
902 else
903 rc = VERR_NOT_FOUND;
904
905 LogFlowFuncLeaveRC(rc);
906 return rc;
907}
908
909int GuestSession::dispatchToThis(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
910{
911 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
912 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
913
914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
915
916#ifdef DEBUG
917 LogFlowThisFunc(("sessionID=%RU32, CID=%RU32, uFunction=%RU32, pSvcCb=%p\n",
918 mData.mSession.mID, pCbCtx->uContextID, pCbCtx->uFunction, pSvcCb));
919#endif
920
921 int rc = VINF_SUCCESS;
922 switch (pCbCtx->uFunction)
923 {
924 case GUEST_DISCONNECTED:
925 /** @todo Handle closing all guest objects. */
926 break;
927
928 case GUEST_SESSION_NOTIFY:
929 {
930 rc = onSessionStatusChange(pCbCtx, pSvcCb);
931 break;
932 }
933
934 default:
935 /* Silently skip unknown callbacks. */
936 rc = VERR_NOT_SUPPORTED;
937 break;
938 }
939
940 LogFlowFuncLeaveRC(rc);
941 return rc;
942}
943
944inline bool GuestSession::fileExists(uint32_t uFileID, ComObjPtr<GuestFile> *pFile)
945{
946 SessionFiles::const_iterator it = mData.mFiles.find(uFileID);
947 if (it != mData.mFiles.end())
948 {
949 if (pFile)
950 *pFile = it->second;
951 return true;
952 }
953 return false;
954}
955
956int GuestSession::fileRemoveFromList(GuestFile *pFile)
957{
958 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
959
960 for (SessionFiles::iterator itFiles = mData.mFiles.begin();
961 itFiles != mData.mFiles.end(); ++itFiles)
962 {
963 if (pFile == itFiles->second)
964 {
965 GuestFile *pThis = itFiles->second;
966 AssertPtr(pThis);
967
968 Bstr strName;
969 HRESULT hr = pThis->COMGETTER(FileName)(strName.asOutParam());
970 ComAssertComRC(hr);
971
972 Assert(mData.mNumObjects);
973 LogFlowThisFunc(("Removing guest file \"%s\" (Session: %RU32) (now total %ld files, %ld objects)\n",
974 Utf8Str(strName).c_str(), mData.mSession.mID, mData.mFiles.size() - 1, mData.mNumObjects - 1));
975
976 mData.mFiles.erase(itFiles);
977 mData.mNumObjects--;
978
979 fireGuestFileRegisteredEvent(mEventSource, this, pFile,
980 false /* Unregistered */);
981 return VINF_SUCCESS;
982 }
983 }
984
985 return VERR_NOT_FOUND;
986}
987
988int GuestSession::fileRemoveInternal(const Utf8Str &strPath, int *pGuestRc)
989{
990 GuestProcessStartupInfo procInfo;
991 GuestProcessStream streamOut;
992
993 procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_RM);
994 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
995 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
996 procInfo.mArguments.push_back(strPath); /* The file we want to remove. */
997
998 GuestProcessTool procTool; int guestRc;
999 int vrc = procTool.Init(this, procInfo, false /* Async */, &guestRc);
1000 if (RT_SUCCESS(vrc))
1001 vrc = procTool.Wait(GUESTPROCESSTOOL_FLAG_NONE, &guestRc);
1002
1003 if (RT_SUCCESS(vrc))
1004 {
1005 if (RT_SUCCESS(guestRc))
1006 guestRc = procTool.TerminatedOk(NULL /* Exit code */);
1007 }
1008
1009 if ( vrc == VERR_GSTCTL_GUEST_ERROR
1010 && pGuestRc)
1011 {
1012 *pGuestRc = guestRc;
1013 }
1014
1015 LogFlowFuncLeaveRC(vrc);
1016 return vrc;
1017}
1018
1019int GuestSession::fileOpenInternal(const GuestFileOpenInfo &openInfo, ComObjPtr<GuestFile> &pFile, int *pGuestRc)
1020{
1021 LogFlowThisFunc(("strPath=%s, strOpenMode=%s, strDisposition=%s, uCreationMode=%x, iOffset=%RI64\n",
1022 openInfo.mFileName.c_str(), openInfo.mOpenMode.c_str(), openInfo.mDisposition.c_str(),
1023 openInfo.mCreationMode, openInfo.mInitialOffset));
1024
1025 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1026
1027 int rc = VERR_MAX_PROCS_REACHED;
1028 if (mData.mNumObjects >= VBOX_GUESTCTRL_MAX_OBJECTS)
1029 return rc;
1030
1031 /* Create a new (host-based) file ID and assign it. */
1032 uint32_t uNewFileID = 0;
1033 ULONG uTries = 0;
1034
1035 for (;;)
1036 {
1037 /* Is the file ID already used? */
1038 if (!fileExists(uNewFileID, NULL /* pProgress */))
1039 {
1040 /* Callback with context ID was not found. This means
1041 * we can use this context ID for our new callback we want
1042 * to add below. */
1043 rc = VINF_SUCCESS;
1044 break;
1045 }
1046 uNewFileID++;
1047 if (uNewFileID == VBOX_GUESTCTRL_MAX_OBJECTS)
1048 uNewFileID = 0;
1049
1050 if (++uTries == UINT32_MAX)
1051 break; /* Don't try too hard. */
1052 }
1053
1054 if (RT_FAILURE(rc))
1055 return rc;
1056
1057 /* Create the directory object. */
1058 HRESULT hr = pFile.createObject();
1059 if (FAILED(hr))
1060 return VERR_COM_UNEXPECTED;
1061
1062 Console *pConsole = mParent->getConsole();
1063 AssertPtr(pConsole);
1064
1065 rc = pFile->init(pConsole, this /* GuestSession */,
1066 uNewFileID, openInfo);
1067 if (RT_FAILURE(rc))
1068 return rc;
1069
1070 int guestRc;
1071 rc = pFile->openFile(&guestRc);
1072 if (RT_SUCCESS(rc))
1073 {
1074 /* Add the created file to our vector. */
1075 mData.mFiles[uNewFileID] = pFile;
1076 mData.mNumObjects++;
1077 Assert(mData.mNumObjects <= VBOX_GUESTCTRL_MAX_OBJECTS);
1078
1079 LogFlowFunc(("Added new guest file \"%s\" (Session: %RU32) (now total %ld files, %ld objects)\n",
1080 openInfo.mFileName.c_str(), mData.mSession.mID, mData.mFiles.size(), mData.mNumObjects));
1081
1082 fireGuestFileRegisteredEvent(mEventSource, this, pFile,
1083 true /* Registered */);
1084 }
1085
1086 if (pGuestRc)
1087 *pGuestRc = guestRc;
1088
1089 LogFlowFuncLeaveRC(rc);
1090 return rc;
1091}
1092
1093int GuestSession::fileQueryInfoInternal(const Utf8Str &strPath, GuestFsObjData &objData, int *pGuestRc)
1094{
1095 LogFlowThisFunc(("strPath=%s\n", strPath.c_str()));
1096
1097 int vrc = fsQueryInfoInternal(strPath, objData, pGuestRc);
1098 if (RT_SUCCESS(vrc))
1099 {
1100 vrc = objData.mType == FsObjType_File
1101 ? VINF_SUCCESS : VERR_NOT_A_FILE;
1102 }
1103
1104 LogFlowFuncLeaveRC(vrc);
1105 return vrc;
1106}
1107
1108int GuestSession::fileQuerySizeInternal(const Utf8Str &strPath, int64_t *pllSize, int *pGuestRc)
1109{
1110 AssertPtrReturn(pllSize, VERR_INVALID_POINTER);
1111
1112 GuestFsObjData objData;
1113 int vrc = fileQueryInfoInternal(strPath, objData, pGuestRc);
1114 if (RT_SUCCESS(vrc))
1115 *pllSize = objData.mObjectSize;
1116
1117 return vrc;
1118}
1119
1120int GuestSession::fsQueryInfoInternal(const Utf8Str &strPath, GuestFsObjData &objData, int *pGuestRc)
1121{
1122 LogFlowThisFunc(("strPath=%s\n", strPath.c_str()));
1123
1124 /** @todo Merge this with IGuestFile::queryInfo(). */
1125 GuestProcessStartupInfo procInfo;
1126 procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_STAT);
1127 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
1128
1129 /* Construct arguments. */
1130 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
1131 procInfo.mArguments.push_back(strPath);
1132
1133 GuestProcessTool procTool; int guestRc;
1134 int vrc = procTool.Init(this, procInfo, false /* Async */, &guestRc);
1135 if (RT_SUCCESS(vrc))
1136 vrc = procTool.Wait(GUESTPROCESSTOOL_FLAG_NONE, &guestRc);
1137 if (RT_SUCCESS(vrc))
1138 {
1139 guestRc = procTool.TerminatedOk(NULL /* Exit code */);
1140 if (RT_SUCCESS(guestRc))
1141 {
1142 GuestProcessStreamBlock curBlock;
1143 vrc = procTool.GetCurrentBlock(OUTPUT_HANDLE_ID_STDOUT, curBlock);
1144 /** @todo Check for more / validate blocks! */
1145 if (RT_SUCCESS(vrc))
1146 vrc = objData.FromStat(curBlock);
1147 }
1148 }
1149
1150 if ( vrc == VERR_GSTCTL_GUEST_ERROR
1151 && pGuestRc)
1152 {
1153 *pGuestRc = guestRc;
1154 }
1155
1156 LogFlowFuncLeaveRC(vrc);
1157 return vrc;
1158}
1159
1160const GuestCredentials& GuestSession::getCredentials(void)
1161{
1162 return mData.mCredentials;
1163}
1164
1165const GuestEnvironment& GuestSession::getEnvironment(void)
1166{
1167 return mData.mEnvironment;
1168}
1169
1170Utf8Str GuestSession::getName(void)
1171{
1172 return mData.mSession.mName;
1173}
1174
1175/* static */
1176Utf8Str GuestSession::guestErrorToString(int guestRc)
1177{
1178 Utf8Str strError;
1179
1180 /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
1181 switch (guestRc)
1182 {
1183 case VERR_INVALID_VM_HANDLE:
1184 strError += Utf8StrFmt(tr("VMM device is not available (is the VM running?)"));
1185 break;
1186
1187 case VERR_HGCM_SERVICE_NOT_FOUND:
1188 strError += Utf8StrFmt(tr("The guest execution service is not available"));
1189 break;
1190
1191 case VERR_AUTHENTICATION_FAILURE:
1192 strError += Utf8StrFmt(tr("The specified user was not able to logon on guest"));
1193 break;
1194
1195 case VERR_TIMEOUT:
1196 strError += Utf8StrFmt(tr("The guest did not respond within time"));
1197 break;
1198
1199 case VERR_CANCELLED:
1200 strError += Utf8StrFmt(tr("The session operation was canceled"));
1201 break;
1202
1203 case VERR_PERMISSION_DENIED:
1204 strError += Utf8StrFmt(tr("Invalid user/password credentials"));
1205 break;
1206
1207 case VERR_MAX_PROCS_REACHED:
1208 strError += Utf8StrFmt(tr("Maximum number of concurrent guest processes has been reached"));
1209 break;
1210
1211 case VERR_NOT_EQUAL: /** @todo Imprecise to the user; can mean anything and all. */
1212 strError += Utf8StrFmt(tr("Unable to retrieve requested information"));
1213 break;
1214
1215 case VERR_NOT_FOUND:
1216 strError += Utf8StrFmt(tr("The guest execution service is not ready (yet)"));
1217 break;
1218
1219 default:
1220 strError += Utf8StrFmt("%Rrc", guestRc);
1221 break;
1222 }
1223
1224 return strError;
1225}
1226
1227/**
1228 * Checks if this session is ready state where it can handle
1229 * all session-bound actions (like guest processes, guest files).
1230 * Only used by official API methods. Will set an external
1231 * error when not ready.
1232 */
1233HRESULT GuestSession::isReadyExternal(void)
1234{
1235 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1236
1237 /** @todo Be a bit more informative. */
1238 if (mData.mStatus != GuestSessionStatus_Started)
1239 return setError(E_UNEXPECTED, tr("Session is not in started state"));
1240
1241 return S_OK;
1242}
1243
1244/** No locking! */
1245int GuestSession::onSessionStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
1246{
1247 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
1248 /* pCallback is optional. */
1249 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
1250
1251 if (pSvcCbData->mParms < 3)
1252 return VERR_INVALID_PARAMETER;
1253
1254 CALLBACKDATA_SESSION_NOTIFY dataCb;
1255 /* pSvcCb->mpaParms[0] always contains the context ID. */
1256 pSvcCbData->mpaParms[1].getUInt32(&dataCb.uType);
1257 pSvcCbData->mpaParms[2].getUInt32(&dataCb.uResult);
1258
1259 LogFlowThisFunc(("ID=%RU32, uType=%RU32, guestRc=%Rrc\n",
1260 mData.mSession.mID, dataCb.uType, dataCb.uResult));
1261
1262 int vrc = VINF_SUCCESS;
1263
1264 GuestSessionStatus_T sessionStatus = GuestSessionStatus_Undefined;
1265
1266 int guestRc = dataCb.uResult; /** @todo uint32_t vs. int. */
1267 switch (dataCb.uType)
1268 {
1269 case GUEST_SESSION_NOTIFYTYPE_ERROR:
1270 sessionStatus = GuestSessionStatus_Error;
1271 break;
1272
1273 case GUEST_SESSION_NOTIFYTYPE_STARTED:
1274 sessionStatus = GuestSessionStatus_Started;
1275 break;
1276
1277 case GUEST_SESSION_NOTIFYTYPE_TEN:
1278 case GUEST_SESSION_NOTIFYTYPE_TES:
1279 case GUEST_SESSION_NOTIFYTYPE_TEA:
1280 sessionStatus = GuestSessionStatus_Terminated;
1281 break;
1282
1283 case GUEST_SESSION_NOTIFYTYPE_TOK:
1284 sessionStatus = GuestSessionStatus_TimedOutKilled;
1285 break;
1286
1287 case GUEST_SESSION_NOTIFYTYPE_TOA:
1288 sessionStatus = GuestSessionStatus_TimedOutAbnormally;
1289 break;
1290
1291 case GUEST_SESSION_NOTIFYTYPE_DWN:
1292 sessionStatus = GuestSessionStatus_Down;
1293 break;
1294
1295 case GUEST_SESSION_NOTIFYTYPE_UNDEFINED:
1296 default:
1297 vrc = VERR_NOT_SUPPORTED;
1298 break;
1299 }
1300
1301 if (RT_SUCCESS(vrc))
1302 {
1303 if (RT_FAILURE(guestRc))
1304 sessionStatus = GuestSessionStatus_Error;
1305 }
1306
1307 /* Set the session status. */
1308 if (sessionStatus != GuestSessionStatus_Undefined)
1309 {
1310 int rc2 = setSessionStatus(sessionStatus, guestRc);
1311 if (RT_SUCCESS(vrc))
1312 vrc = rc2;
1313 }
1314
1315 LogFlowThisFunc(("ID=%RU32, guestRc=%Rrc\n", mData.mSession.mID, guestRc));
1316
1317 LogFlowFuncLeaveRC(vrc);
1318 return vrc;
1319}
1320
1321int GuestSession::startSessionIntenal(int *pGuestRc)
1322{
1323 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1324
1325 LogFlowThisFunc(("mID=%RU32, mName=%s, uProtocolVersion=%RU32, openFlags=%x, openTimeoutMS=%RU32\n",
1326 mData.mSession.mID, mData.mSession.mName.c_str(), mData.mProtocolVersion,
1327 mData.mSession.mOpenFlags, mData.mSession.mOpenTimeoutMS));
1328
1329 /* Legacy Guest Additions don't support opening dedicated
1330 guest sessions. Simply return success here. */
1331 if (mData.mProtocolVersion < 2)
1332 {
1333 mData.mStatus = GuestSessionStatus_Started;
1334
1335 LogFlowThisFunc(("Installed Guest Additions don't support opening dedicated sessions, skipping\n"));
1336 return VINF_SUCCESS;
1337 }
1338
1339 if (mData.mStatus != GuestSessionStatus_Undefined)
1340 return VINF_SUCCESS;
1341
1342 /** @todo mData.mSession.uFlags validation. */
1343
1344 /* Set current session status. */
1345 mData.mStatus = GuestSessionStatus_Starting;
1346
1347 int vrc;
1348
1349 GuestWaitEvent *pEvent = NULL;
1350 std::list < VBoxEventType_T > eventTypes;
1351 try
1352 {
1353 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
1354
1355 vrc = registerEvent(mData.mSession.mID, 0 /* Object ID */,
1356 eventTypes, &pEvent);
1357 }
1358 catch (std::bad_alloc)
1359 {
1360 vrc = VERR_NO_MEMORY;
1361 }
1362
1363 if (RT_FAILURE(vrc))
1364 return vrc;
1365
1366 VBOXHGCMSVCPARM paParms[8];
1367
1368 int i = 0;
1369 paParms[i++].setUInt32(pEvent->ContextID());
1370 paParms[i++].setUInt32(mData.mProtocolVersion);
1371 paParms[i++].setPointer((void*)mData.mCredentials.mUser.c_str(),
1372 (ULONG)mData.mCredentials.mUser.length() + 1);
1373 paParms[i++].setPointer((void*)mData.mCredentials.mPassword.c_str(),
1374 (ULONG)mData.mCredentials.mPassword.length() + 1);
1375 paParms[i++].setPointer((void*)mData.mCredentials.mDomain.c_str(),
1376 (ULONG)mData.mCredentials.mDomain.length() + 1);
1377 paParms[i++].setUInt32(mData.mSession.mOpenFlags);
1378
1379 vrc = sendCommand(HOST_SESSION_CREATE, i, paParms);
1380 if (RT_SUCCESS(vrc))
1381 {
1382 alock.release(); /* Drop write lock before waiting. */
1383
1384 vrc = waitForStatusChange(pEvent, GuestSessionWaitForFlag_Start,
1385 30 * 1000 /* 30s timeout */,
1386 NULL /* Session status */, pGuestRc);
1387 }
1388
1389 unregisterEvent(pEvent);
1390
1391 LogFlowFuncLeaveRC(vrc);
1392 return vrc;
1393}
1394
1395int GuestSession::startSessionAsync(void)
1396{
1397 LogFlowThisFuncEnter();
1398
1399 int vrc;
1400
1401 try
1402 {
1403 /* Asynchronously open the session on the guest by kicking off a
1404 * worker thread. */
1405 std::auto_ptr<GuestSessionTaskInternalOpen> pTask(new GuestSessionTaskInternalOpen(this));
1406 AssertReturn(pTask->isOk(), pTask->rc());
1407
1408 vrc = RTThreadCreate(NULL, GuestSession::startSessionThread,
1409 (void *)pTask.get(), 0,
1410 RTTHREADTYPE_MAIN_WORKER, 0,
1411 "gctlSesStart");
1412 if (RT_SUCCESS(vrc))
1413 {
1414 /* pTask is now owned by openSessionThread(), so release it. */
1415 pTask.release();
1416 }
1417 }
1418 catch(std::bad_alloc &)
1419 {
1420 vrc = VERR_NO_MEMORY;
1421 }
1422
1423 LogFlowFuncLeaveRC(vrc);
1424 return vrc;
1425}
1426
1427/* static */
1428DECLCALLBACK(int) GuestSession::startSessionThread(RTTHREAD Thread, void *pvUser)
1429{
1430 LogFlowFunc(("pvUser=%p\n", pvUser));
1431
1432 std::auto_ptr<GuestSessionTaskInternalOpen> pTask(static_cast<GuestSessionTaskInternalOpen*>(pvUser));
1433 AssertPtr(pTask.get());
1434
1435 const ComObjPtr<GuestSession> pSession(pTask->Session());
1436 Assert(!pSession.isNull());
1437
1438 AutoCaller autoCaller(pSession);
1439 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1440
1441 int vrc = pSession->startSessionIntenal(NULL /* Guest rc, ignored */);
1442 /* Nothing to do here anymore. */
1443
1444 LogFlowFuncLeaveRC(vrc);
1445 return vrc;
1446}
1447
1448int GuestSession::processRemoveFromList(GuestProcess *pProcess)
1449{
1450 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
1451
1452 LogFlowThisFunc(("pProcess=%p\n", pProcess));
1453
1454 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1455
1456 int rc = VERR_NOT_FOUND;
1457
1458 ULONG uPID;
1459 HRESULT hr = pProcess->COMGETTER(PID)(&uPID);
1460 ComAssertComRC(hr);
1461
1462 LogFlowFunc(("Closing process (PID=%RU32) ...\n", uPID));
1463
1464 SessionProcesses::iterator itProcs = mData.mProcesses.begin();
1465 while (itProcs != mData.mProcesses.end())
1466 {
1467 if (pProcess == itProcs->second)
1468 {
1469 GuestProcess *pCurProc = itProcs->second;
1470 AssertPtr(pCurProc);
1471
1472 hr = pCurProc->COMGETTER(PID)(&uPID);
1473 ComAssertComRC(hr);
1474
1475 Assert(mData.mNumObjects);
1476 LogFlowFunc(("Removing process ID=%RU32 (Session: %RU32), guest PID=%RU32 (now total %ld processes, %ld objects)\n",
1477 pCurProc->getObjectID(), mData.mSession.mID, uPID, mData.mProcesses.size() - 1, mData.mNumObjects - 1));
1478
1479 mData.mProcesses.erase(itProcs);
1480 mData.mNumObjects--;
1481
1482 fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, NULL /* Process */,
1483 uPID, false /* Process unregistered */);
1484 rc = VINF_SUCCESS;
1485 break;
1486 }
1487
1488 itProcs++;
1489 }
1490
1491 LogFlowFuncLeaveRC(rc);
1492 return rc;
1493}
1494
1495/**
1496 * Creates but does *not* start the process yet. See GuestProcess::startProcess() or
1497 * GuestProcess::startProcessAsync() for that.
1498 *
1499 * @return IPRT status code.
1500 * @param procInfo
1501 * @param pProcess
1502 */
1503int GuestSession::processCreateExInteral(GuestProcessStartupInfo &procInfo, ComObjPtr<GuestProcess> &pProcess)
1504{
1505 LogFlowFunc(("mCmd=%s, mFlags=%x, mTimeoutMS=%RU32\n",
1506 procInfo.mCommand.c_str(), procInfo.mFlags, procInfo.mTimeoutMS));
1507#ifdef DEBUG
1508 if (procInfo.mArguments.size())
1509 {
1510 LogFlowFunc(("Arguments:"));
1511 ProcessArguments::const_iterator it = procInfo.mArguments.begin();
1512 while (it != procInfo.mArguments.end())
1513 {
1514 LogFlow((" %s", (*it).c_str()));
1515 it++;
1516 }
1517 LogFlow(("\n"));
1518 }
1519#endif
1520
1521 /* Validate flags. */
1522 if (procInfo.mFlags)
1523 {
1524 if ( !(procInfo.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
1525 && !(procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
1526 && !(procInfo.mFlags & ProcessCreateFlag_Hidden)
1527 && !(procInfo.mFlags & ProcessCreateFlag_NoProfile)
1528 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
1529 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdErr))
1530 {
1531 return VERR_INVALID_PARAMETER;
1532 }
1533 }
1534
1535 if ( (procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
1536 && ( (procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
1537 || (procInfo.mFlags & ProcessCreateFlag_WaitForStdErr)
1538 )
1539 )
1540 {
1541 return VERR_INVALID_PARAMETER;
1542 }
1543
1544 /* Adjust timeout. If set to 0, we define
1545 * an infinite timeout. */
1546 if (procInfo.mTimeoutMS == 0)
1547 procInfo.mTimeoutMS = UINT32_MAX;
1548
1549 /** @tood Implement process priority + affinity. */
1550
1551 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1552
1553 int rc = VERR_MAX_PROCS_REACHED;
1554 if (mData.mNumObjects >= VBOX_GUESTCTRL_MAX_OBJECTS)
1555 return rc;
1556
1557 /* Create a new (host-based) process ID and assign it. */
1558 uint32_t uNewProcessID = 0;
1559 ULONG uTries = 0;
1560
1561 for (;;)
1562 {
1563 /* Is the context ID already used? */
1564 if (!processExists(uNewProcessID, NULL /* pProgress */))
1565 {
1566 /* Callback with context ID was not found. This means
1567 * we can use this context ID for our new callback we want
1568 * to add below. */
1569 rc = VINF_SUCCESS;
1570 break;
1571 }
1572 uNewProcessID++;
1573 if (uNewProcessID == VBOX_GUESTCTRL_MAX_OBJECTS)
1574 uNewProcessID = 0;
1575
1576 if (++uTries == VBOX_GUESTCTRL_MAX_OBJECTS)
1577 break; /* Don't try too hard. */
1578 }
1579
1580 if (RT_FAILURE(rc))
1581 return rc;
1582
1583 /* Create the process object. */
1584 HRESULT hr = pProcess.createObject();
1585 if (FAILED(hr))
1586 return VERR_COM_UNEXPECTED;
1587
1588 rc = pProcess->init(mParent->getConsole() /* Console */, this /* Session */,
1589 uNewProcessID, procInfo);
1590 if (RT_FAILURE(rc))
1591 return rc;
1592
1593 /* Add the created process to our map. */
1594 mData.mProcesses[uNewProcessID] = pProcess;
1595 mData.mNumObjects++;
1596 Assert(mData.mNumObjects <= VBOX_GUESTCTRL_MAX_OBJECTS);
1597
1598 fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProcess,
1599 0 /* PID */, true /* Process registered */);
1600
1601 LogFlowFunc(("Added new process (Session: %RU32) with process ID=%RU32 (now total %ld processes, %ld objects)\n",
1602 mData.mSession.mID, uNewProcessID, mData.mProcesses.size(), mData.mNumObjects));
1603
1604 return rc;
1605}
1606
1607inline bool GuestSession::processExists(uint32_t uProcessID, ComObjPtr<GuestProcess> *pProcess)
1608{
1609 SessionProcesses::const_iterator it = mData.mProcesses.find(uProcessID);
1610 if (it != mData.mProcesses.end())
1611 {
1612 if (pProcess)
1613 *pProcess = it->second;
1614 return true;
1615 }
1616 return false;
1617}
1618
1619inline int GuestSession::processGetByPID(ULONG uPID, ComObjPtr<GuestProcess> *pProcess)
1620{
1621 AssertReturn(uPID, false);
1622 /* pProcess is optional. */
1623
1624 SessionProcesses::iterator itProcs = mData.mProcesses.begin();
1625 for (; itProcs != mData.mProcesses.end(); itProcs++)
1626 {
1627 ComObjPtr<GuestProcess> pCurProc = itProcs->second;
1628 AutoCaller procCaller(pCurProc);
1629 if (procCaller.rc())
1630 return VERR_COM_INVALID_OBJECT_STATE;
1631
1632 ULONG uCurPID;
1633 HRESULT hr = pCurProc->COMGETTER(PID)(&uCurPID);
1634 ComAssertComRC(hr);
1635
1636 if (uCurPID == uPID)
1637 {
1638 if (pProcess)
1639 *pProcess = pCurProc;
1640 return VINF_SUCCESS;
1641 }
1642 }
1643
1644 return VERR_NOT_FOUND;
1645}
1646
1647int GuestSession::sendCommand(uint32_t uFunction,
1648 uint32_t uParms, PVBOXHGCMSVCPARM paParms)
1649{
1650 LogFlowThisFuncEnter();
1651
1652#ifndef VBOX_GUESTCTRL_TEST_CASE
1653 ComObjPtr<Console> pConsole = mParent->getConsole();
1654 Assert(!pConsole.isNull());
1655
1656 /* Forward the information to the VMM device. */
1657 VMMDev *pVMMDev = pConsole->getVMMDev();
1658 AssertPtr(pVMMDev);
1659
1660 LogFlowThisFunc(("uFunction=%RU32, uParms=%RU32\n", uFunction, uParms));
1661 int vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uFunction, uParms, paParms);
1662 if (RT_FAILURE(vrc))
1663 {
1664 /** @todo What to do here? */
1665 }
1666#else
1667 /* Not needed within testcases. */
1668 int vrc = VINF_SUCCESS;
1669#endif
1670 LogFlowFuncLeaveRC(vrc);
1671 return vrc;
1672}
1673
1674/* static */
1675HRESULT GuestSession::setErrorExternal(VirtualBoxBase *pInterface, int guestRc)
1676{
1677 AssertPtr(pInterface);
1678 AssertMsg(RT_FAILURE(guestRc), ("Guest rc does not indicate a failure when setting error\n"));
1679
1680 return pInterface->setError(VBOX_E_IPRT_ERROR, GuestSession::guestErrorToString(guestRc).c_str());
1681}
1682
1683/* Does not do locking; caller is responsible for that! */
1684int GuestSession::setSessionStatus(GuestSessionStatus_T sessionStatus, int sessionRc)
1685{
1686 LogFlowThisFunc(("oldStatus=%ld, newStatus=%ld, sessionRc=%Rrc\n",
1687 mData.mStatus, sessionStatus, sessionRc));
1688
1689 if (sessionStatus == GuestSessionStatus_Error)
1690 {
1691 AssertMsg(RT_FAILURE(sessionRc), ("Guest rc must be an error (%Rrc)\n", sessionRc));
1692 /* Do not allow overwriting an already set error. If this happens
1693 * this means we forgot some error checking/locking somewhere. */
1694 AssertMsg(RT_SUCCESS(mData.mRC), ("Guest rc already set (to %Rrc)\n", mData.mRC));
1695 }
1696 else
1697 AssertMsg(RT_SUCCESS(sessionRc), ("Guest rc must not be an error (%Rrc)\n", sessionRc));
1698
1699 if (mData.mStatus != sessionStatus)
1700 {
1701 mData.mStatus = sessionStatus;
1702 mData.mRC = sessionRc;
1703
1704 ComObjPtr<GuestErrorInfo> errorInfo;
1705 HRESULT hr = errorInfo.createObject();
1706 ComAssertComRC(hr);
1707 int rc2 = errorInfo->init(sessionRc, guestErrorToString(sessionRc));
1708 AssertRC(rc2);
1709
1710 fireGuestSessionStateChangedEvent(mEventSource, this,
1711 mData.mSession.mID, sessionStatus, errorInfo);
1712 }
1713
1714 return VINF_SUCCESS;
1715}
1716
1717int GuestSession::signalWaiters(GuestSessionWaitResult_T enmWaitResult, int rc /*= VINF_SUCCESS */)
1718{
1719 /*LogFlowThisFunc(("enmWaitResult=%d, rc=%Rrc, mWaitCount=%RU32, mWaitEvent=%p\n",
1720 enmWaitResult, rc, mData.mWaitCount, mData.mWaitEvent));*/
1721
1722 /* Note: No write locking here -- already done in the caller. */
1723
1724 int vrc = VINF_SUCCESS;
1725 /*if (mData.mWaitEvent)
1726 vrc = mData.mWaitEvent->Signal(enmWaitResult, rc);*/
1727 LogFlowFuncLeaveRC(vrc);
1728 return vrc;
1729}
1730
1731int GuestSession::startTaskAsync(const Utf8Str &strTaskDesc,
1732 GuestSessionTask *pTask, ComObjPtr<Progress> &pProgress)
1733{
1734 LogFlowThisFunc(("strTaskDesc=%s, pTask=%p\n", strTaskDesc.c_str(), pTask));
1735
1736 AssertPtrReturn(pTask, VERR_INVALID_POINTER);
1737
1738 /* Create the progress object. */
1739 HRESULT hr = pProgress.createObject();
1740 if (FAILED(hr))
1741 return VERR_COM_UNEXPECTED;
1742
1743 hr = pProgress->init(static_cast<IGuestSession*>(this),
1744 Bstr(strTaskDesc).raw(),
1745 TRUE /* aCancelable */);
1746 if (FAILED(hr))
1747 return VERR_COM_UNEXPECTED;
1748
1749 /* Initialize our worker task. */
1750 std::auto_ptr<GuestSessionTask> task(pTask);
1751
1752 int rc = task->RunAsync(strTaskDesc, pProgress);
1753 if (RT_FAILURE(rc))
1754 return rc;
1755
1756 /* Don't destruct on success. */
1757 task.release();
1758
1759 LogFlowFuncLeaveRC(rc);
1760 return rc;
1761}
1762
1763/**
1764 * Queries/collects information prior to establishing a guest session.
1765 * This is necessary to know which guest control protocol version to use,
1766 * among other things (later).
1767 *
1768 * @return IPRT status code.
1769 */
1770int GuestSession::queryInfo(void)
1771{
1772#ifndef DEBUG_andy
1773 /* Since the new functions are not fully implemented yet, force Main
1774 to use protocol ver 1 so far. */
1775 mData.mProtocolVersion = 1;
1776#else
1777 #if 1
1778 /* For debugging only: Hardcode version. */
1779 mData.mProtocolVersion = 2;
1780 #else
1781 /*
1782 * Try querying the guest control protocol version running on the guest.
1783 * This is done using the Guest Additions version
1784 */
1785 ComObjPtr<Guest> pGuest = mParent;
1786 Assert(!pGuest.isNull());
1787
1788 uint32_t uVerAdditions = pGuest->getAdditionsVersion();
1789 mData.mProtocolVersion = ( VBOX_FULL_VERSION_GET_MAJOR(uVerAdditions) >= 4
1790 && VBOX_FULL_VERSION_GET_MINOR(uVerAdditions) >= 3) /** @todo What's about v5.0 ? */
1791 ? 2 /* Guest control 2.0. */
1792 : 1; /* Legacy guest control (VBox < 4.3). */
1793 /* Build revision is ignored. */
1794
1795 /* Tell the user but don't bitch too often. */
1796 static short s_gctrlLegacyWarning = 0;
1797 if (s_gctrlLegacyWarning++ < 3) /** @todo Find a bit nicer text. */
1798 LogRel((tr("Warning: Guest Additions are older (%ld.%ld) than host capabilities for guest control, please upgrade them. Using protocol version %ld now\n"),
1799 VBOX_FULL_VERSION_GET_MAJOR(uVerAdditions), VBOX_FULL_VERSION_GET_MINOR(uVerAdditions), mData.mProtocolVersion));
1800 #endif
1801#endif
1802 return VINF_SUCCESS;
1803}
1804
1805int GuestSession::waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, GuestSessionWaitResult_T &waitResult, int *pGuestRc)
1806{
1807 LogFlowThisFuncEnter();
1808
1809 AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
1810
1811 /*LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p, pGuestRc=%p\n",
1812 fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent, pGuestRc));*/
1813
1814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1815
1816 /* Did some error occur before? Then skip waiting and return. */
1817 if (mData.mStatus == GuestSessionStatus_Error)
1818 {
1819 waitResult = GuestSessionWaitResult_Error;
1820 AssertMsg(RT_FAILURE(mData.mRC), ("No error rc (%Rrc) set when guest session indicated an error\n", mData.mRC));
1821 if (pGuestRc)
1822 *pGuestRc = mData.mRC; /* Return last set error. */
1823 return VERR_GSTCTL_GUEST_ERROR;
1824 }
1825
1826 waitResult = GuestSessionWaitResult_None;
1827 if (fWaitFlags & GuestSessionWaitForFlag_Terminate)
1828 {
1829 switch (mData.mStatus)
1830 {
1831 case GuestSessionStatus_Terminated:
1832 case GuestSessionStatus_Down:
1833 waitResult = GuestSessionWaitResult_Terminate;
1834 break;
1835
1836 case GuestSessionStatus_TimedOutKilled:
1837 case GuestSessionStatus_TimedOutAbnormally:
1838 waitResult = GuestSessionWaitResult_Timeout;
1839 break;
1840
1841 case GuestSessionStatus_Error:
1842 /* Handled above. */
1843 break;
1844
1845 case GuestSessionStatus_Started:
1846 waitResult = GuestSessionWaitResult_Start;
1847 break;
1848
1849 case GuestSessionStatus_Undefined:
1850 case GuestSessionStatus_Starting:
1851 /* Do the waiting below. */
1852 break;
1853
1854 default:
1855 AssertMsgFailed(("Unhandled session status %ld\n", mData.mStatus));
1856 return VERR_NOT_IMPLEMENTED;
1857 }
1858 }
1859 else if (fWaitFlags & GuestSessionWaitForFlag_Start)
1860 {
1861 switch (mData.mStatus)
1862 {
1863 case GuestSessionStatus_Started:
1864 case GuestSessionStatus_Terminating:
1865 case GuestSessionStatus_Terminated:
1866 case GuestSessionStatus_Down:
1867 waitResult = GuestSessionWaitResult_Start;
1868 break;
1869
1870 case GuestSessionStatus_Error:
1871 waitResult = GuestSessionWaitResult_Error;
1872 break;
1873
1874 case GuestSessionStatus_TimedOutKilled:
1875 case GuestSessionStatus_TimedOutAbnormally:
1876 waitResult = GuestSessionWaitResult_Timeout;
1877 break;
1878
1879 case GuestSessionStatus_Undefined:
1880 case GuestSessionStatus_Starting:
1881 /* Do the waiting below. */
1882 break;
1883
1884 default:
1885 AssertMsgFailed(("Unhandled session status %ld\n", mData.mStatus));
1886 return VERR_NOT_IMPLEMENTED;
1887 }
1888 }
1889
1890 LogFlowThisFunc(("sessionStatus=%ld, sessionRc=%Rrc, waitResult=%ld\n",
1891 mData.mStatus, mData.mRC, waitResult));
1892
1893 /* No waiting needed? Return immediately using the last set error. */
1894 if (waitResult != GuestSessionWaitResult_None)
1895 {
1896 if (pGuestRc)
1897 *pGuestRc = mData.mRC; /* Return last set error (if any). */
1898 return RT_SUCCESS(mData.mRC) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
1899 }
1900
1901 alock.release(); /* Release lock before waiting. */
1902
1903 int vrc;
1904
1905 GuestWaitEvent *pEvent = NULL;
1906 std::list < VBoxEventType_T > eventTypes;
1907 try
1908 {
1909 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
1910
1911 vrc = registerEvent(mData.mSession.mID, 0 /* Object ID */,
1912 eventTypes, &pEvent);
1913 }
1914 catch (std::bad_alloc)
1915 {
1916 vrc = VERR_NO_MEMORY;
1917 }
1918
1919 if (RT_FAILURE(vrc))
1920 return vrc;
1921
1922 GuestSessionStatus_T sessionStatus;
1923 vrc = waitForStatusChange(pEvent, fWaitFlags,
1924 uTimeoutMS, &sessionStatus, pGuestRc);
1925 if (RT_SUCCESS(vrc))
1926 {
1927 switch (sessionStatus)
1928 {
1929 case GuestSessionStatus_Started:
1930 waitResult = GuestSessionWaitResult_Start;
1931 break;
1932
1933 case GuestSessionStatus_Terminated:
1934 waitResult = GuestSessionWaitResult_Terminate;
1935 break;
1936
1937 case GuestSessionStatus_TimedOutKilled:
1938 case GuestSessionStatus_TimedOutAbnormally:
1939 waitResult = GuestSessionWaitResult_Timeout;
1940 break;
1941
1942 case GuestSessionStatus_Down:
1943 waitResult = GuestSessionWaitResult_Terminate;
1944 break;
1945
1946 case GuestSessionStatus_Error:
1947 waitResult = GuestSessionWaitResult_Error;
1948 break;
1949
1950 default:
1951 waitResult = GuestSessionWaitResult_Status;
1952 break;
1953 }
1954 }
1955
1956 unregisterEvent(pEvent);
1957
1958 LogFlowFuncLeaveRC(vrc);
1959 return vrc;
1960}
1961
1962int GuestSession::waitForStatusChange(GuestWaitEvent *pEvent, uint32_t fWaitFlags, uint32_t uTimeoutMS,
1963 GuestSessionStatus_T *pSessionStatus, int *pGuestRc)
1964{
1965 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1966
1967 VBoxEventType_T evtType;
1968 ComPtr<IEvent> pIEvent;
1969 int vrc = waitForEvent(pEvent, uTimeoutMS,
1970 &evtType, pIEvent.asOutParam());
1971 if (RT_SUCCESS(vrc))
1972 {
1973 Assert(evtType == VBoxEventType_OnGuestSessionStateChanged);
1974
1975 ComPtr<IGuestSessionStateChangedEvent> pChangedEvent = pIEvent;
1976 Assert(!pChangedEvent.isNull());
1977
1978 GuestSessionStatus_T sessionStatus;
1979 pChangedEvent->COMGETTER(Status)(&sessionStatus);
1980 if (pSessionStatus)
1981 *pSessionStatus = sessionStatus;
1982
1983 ComPtr<IGuestErrorInfo> errorInfo;
1984 HRESULT hr = pChangedEvent->COMGETTER(Error)(errorInfo.asOutParam());
1985 ComAssertComRC(hr);
1986
1987 LONG lGuestRc;
1988 hr = errorInfo->COMGETTER(Result)(&lGuestRc);
1989 ComAssertComRC(hr);
1990 if (RT_FAILURE((int)lGuestRc))
1991 vrc = VERR_GSTCTL_GUEST_ERROR;
1992 if (pGuestRc)
1993 *pGuestRc = (int)lGuestRc;
1994
1995 LogFlowThisFunc(("Status changed event for session ID=%RU32: %ld (%Rrc)\n",
1996 mData.mSession.mID, sessionStatus,
1997 RT_SUCCESS((int)lGuestRc) ? VINF_SUCCESS : (int)lGuestRc));
1998 }
1999
2000 LogFlowFuncLeaveRC(vrc);
2001 return vrc;
2002}
2003
2004// implementation of public methods
2005/////////////////////////////////////////////////////////////////////////////
2006
2007STDMETHODIMP GuestSession::Close(void)
2008{
2009#ifndef VBOX_WITH_GUEST_CONTROL
2010 ReturnComNotImplemented();
2011#else
2012 LogFlowThisFuncEnter();
2013
2014 AutoCaller autoCaller(this);
2015 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2016
2017 /* Close session on guest. */
2018 int guestRc;
2019 int rc = closeSession(0 /* Flags */, 30 * 1000 /* Timeout */,
2020 &guestRc);
2021 /* On failure don't return here, instead do all the cleanup
2022 * work first and then return an error. */
2023
2024 /* Remove ourselves from the session list. */
2025 int rc2 = mParent->sessionRemove(this);
2026 if (RT_SUCCESS(rc))
2027 rc = rc2;
2028
2029 /*
2030 * Release autocaller before calling uninit.
2031 */
2032 autoCaller.release();
2033
2034 uninit();
2035
2036 LogFlowFuncLeaveRC(rc);
2037 if (RT_FAILURE(rc))
2038 {
2039 if (rc == VERR_GSTCTL_GUEST_ERROR)
2040 return GuestSession::setErrorExternal(this, guestRc);
2041
2042 return setError(VBOX_E_IPRT_ERROR,
2043 tr("Closing guest session failed with %Rrc"), rc);
2044 }
2045
2046 return S_OK;
2047#endif /* VBOX_WITH_GUEST_CONTROL */
2048}
2049
2050STDMETHODIMP GuestSession::CopyFrom(IN_BSTR aSource, IN_BSTR aDest, ComSafeArrayIn(CopyFileFlag_T, aFlags), IProgress **aProgress)
2051{
2052#ifndef VBOX_WITH_GUEST_CONTROL
2053 ReturnComNotImplemented();
2054#else
2055 CheckComArgStrNotEmptyOrNull(aSource);
2056 CheckComArgStrNotEmptyOrNull(aDest);
2057 CheckComArgOutPointerValid(aProgress);
2058
2059 LogFlowThisFuncEnter();
2060
2061 if (RT_UNLIKELY((aSource) == NULL || *(aSource) == '\0'))
2062 return setError(E_INVALIDARG, tr("No source specified"));
2063 if (RT_UNLIKELY((aDest) == NULL || *(aDest) == '\0'))
2064 return setError(E_INVALIDARG, tr("No destination specified"));
2065
2066 AutoCaller autoCaller(this);
2067 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2068
2069 uint32_t fFlags = CopyFileFlag_None;
2070 if (aFlags)
2071 {
2072 com::SafeArray<CopyFileFlag_T> flags(ComSafeArrayInArg(aFlags));
2073 for (size_t i = 0; i < flags.size(); i++)
2074 fFlags |= flags[i];
2075 }
2076
2077 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2078
2079 HRESULT hr = S_OK;
2080
2081 try
2082 {
2083 ComObjPtr<Progress> pProgress;
2084 SessionTaskCopyFrom *pTask = new SessionTaskCopyFrom(this /* GuestSession */,
2085 Utf8Str(aSource), Utf8Str(aDest), fFlags);
2086 int rc = startTaskAsync(Utf8StrFmt(tr("Copying \"%ls\" from guest to \"%ls\" on the host"), aSource, aDest),
2087 pTask, pProgress);
2088 if (RT_SUCCESS(rc))
2089 {
2090 /* Return progress to the caller. */
2091 hr = pProgress.queryInterfaceTo(aProgress);
2092 }
2093 else
2094 hr = setError(VBOX_E_IPRT_ERROR,
2095 tr("Starting task for copying file \"%ls\" from guest to \"%ls\" on the host failed: %Rrc"), rc);
2096 }
2097 catch(std::bad_alloc &)
2098 {
2099 hr = E_OUTOFMEMORY;
2100 }
2101
2102 return hr;
2103#endif /* VBOX_WITH_GUEST_CONTROL */
2104}
2105
2106STDMETHODIMP GuestSession::CopyTo(IN_BSTR aSource, IN_BSTR aDest, ComSafeArrayIn(CopyFileFlag_T, aFlags), IProgress **aProgress)
2107{
2108#ifndef VBOX_WITH_GUEST_CONTROL
2109 ReturnComNotImplemented();
2110#else
2111 CheckComArgStrNotEmptyOrNull(aSource);
2112 CheckComArgStrNotEmptyOrNull(aDest);
2113 CheckComArgOutPointerValid(aProgress);
2114
2115 LogFlowThisFuncEnter();
2116
2117 if (RT_UNLIKELY((aSource) == NULL || *(aSource) == '\0'))
2118 return setError(E_INVALIDARG, tr("No source specified"));
2119 if (RT_UNLIKELY((aDest) == NULL || *(aDest) == '\0'))
2120 return setError(E_INVALIDARG, tr("No destination specified"));
2121
2122 AutoCaller autoCaller(this);
2123 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2124
2125 uint32_t fFlags = CopyFileFlag_None;
2126 if (aFlags)
2127 {
2128 com::SafeArray<CopyFileFlag_T> flags(ComSafeArrayInArg(aFlags));
2129 for (size_t i = 0; i < flags.size(); i++)
2130 fFlags |= flags[i];
2131 }
2132
2133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2134
2135 HRESULT hr = S_OK;
2136
2137 try
2138 {
2139 ComObjPtr<Progress> pProgress;
2140 SessionTaskCopyTo *pTask = new SessionTaskCopyTo(this /* GuestSession */,
2141 Utf8Str(aSource), Utf8Str(aDest), fFlags);
2142 AssertPtrReturn(pTask, E_OUTOFMEMORY);
2143 int rc = startTaskAsync(Utf8StrFmt(tr("Copying \"%ls\" from host to \"%ls\" on the guest"), aSource, aDest),
2144 pTask, pProgress);
2145 if (RT_SUCCESS(rc))
2146 {
2147 /* Return progress to the caller. */
2148 hr = pProgress.queryInterfaceTo(aProgress);
2149 }
2150 else
2151 hr = setError(VBOX_E_IPRT_ERROR,
2152 tr("Starting task for copying file \"%ls\" from host to \"%ls\" on the guest failed: %Rrc"), rc);
2153 }
2154 catch(std::bad_alloc &)
2155 {
2156 hr = E_OUTOFMEMORY;
2157 }
2158
2159 return hr;
2160#endif /* VBOX_WITH_GUEST_CONTROL */
2161}
2162
2163STDMETHODIMP GuestSession::DirectoryCreate(IN_BSTR aPath, ULONG aMode,
2164 ComSafeArrayIn(DirectoryCreateFlag_T, aFlags))
2165{
2166#ifndef VBOX_WITH_GUEST_CONTROL
2167 ReturnComNotImplemented();
2168#else
2169 LogFlowThisFuncEnter();
2170
2171 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2172 return setError(E_INVALIDARG, tr("No directory to create specified"));
2173
2174 AutoCaller autoCaller(this);
2175 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2176
2177 uint32_t fFlags = DirectoryCreateFlag_None;
2178 if (aFlags)
2179 {
2180 com::SafeArray<DirectoryCreateFlag_T> flags(ComSafeArrayInArg(aFlags));
2181 for (size_t i = 0; i < flags.size(); i++)
2182 fFlags |= flags[i];
2183
2184 if (fFlags)
2185 {
2186 if (!(fFlags & DirectoryCreateFlag_Parents))
2187 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), fFlags);
2188 }
2189 }
2190
2191 HRESULT hr = S_OK;
2192
2193 ComObjPtr <GuestDirectory> pDirectory; int guestRc;
2194 int rc = directoryCreateInternal(Utf8Str(aPath), (uint32_t)aMode, fFlags, &guestRc);
2195 if (RT_FAILURE(rc))
2196 {
2197 switch (rc)
2198 {
2199 case VERR_GSTCTL_GUEST_ERROR:
2200 hr = GuestProcess::setErrorExternal(this, guestRc);
2201 break;
2202
2203 case VERR_INVALID_PARAMETER:
2204 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Invalid parameters given"));
2205 break;
2206
2207 case VERR_BROKEN_PIPE:
2208 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Unexpectedly aborted"));
2209 break;
2210
2211 case VERR_CANT_CREATE:
2212 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Could not create directory"));
2213 break;
2214
2215 default:
2216 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: %Rrc"), rc);
2217 break;
2218 }
2219 }
2220
2221 return hr;
2222#endif /* VBOX_WITH_GUEST_CONTROL */
2223}
2224
2225STDMETHODIMP GuestSession::DirectoryCreateTemp(IN_BSTR aTemplate, ULONG aMode, IN_BSTR aPath, BOOL aSecure, BSTR *aDirectory)
2226{
2227#ifndef VBOX_WITH_GUEST_CONTROL
2228 ReturnComNotImplemented();
2229#else
2230 LogFlowThisFuncEnter();
2231
2232 if (RT_UNLIKELY((aTemplate) == NULL || *(aTemplate) == '\0'))
2233 return setError(E_INVALIDARG, tr("No template specified"));
2234 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2235 return setError(E_INVALIDARG, tr("No directory name specified"));
2236 CheckComArgOutPointerValid(aDirectory);
2237
2238 AutoCaller autoCaller(this);
2239 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2240
2241 HRESULT hr = S_OK;
2242
2243 Utf8Str strName; int guestRc;
2244 int rc = objectCreateTempInternal(Utf8Str(aTemplate),
2245 Utf8Str(aPath),
2246 true /* Directory */, strName, &guestRc);
2247 if (RT_SUCCESS(rc))
2248 {
2249 strName.cloneTo(aDirectory);
2250 }
2251 else
2252 {
2253 switch (rc)
2254 {
2255 case VERR_GSTCTL_GUEST_ERROR:
2256 hr = GuestProcess::setErrorExternal(this, guestRc);
2257 break;
2258
2259 default:
2260 hr = setError(VBOX_E_IPRT_ERROR, tr("Temporary directory creation \"%s\" with template \"%s\" failed: %Rrc"),
2261 Utf8Str(aPath).c_str(), Utf8Str(aTemplate).c_str(), rc);
2262 break;
2263 }
2264 }
2265
2266 return hr;
2267#endif /* VBOX_WITH_GUEST_CONTROL */
2268}
2269
2270STDMETHODIMP GuestSession::DirectoryExists(IN_BSTR aPath, BOOL *aExists)
2271{
2272#ifndef VBOX_WITH_GUEST_CONTROL
2273 ReturnComNotImplemented();
2274#else
2275 LogFlowThisFuncEnter();
2276
2277 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2278 return setError(E_INVALIDARG, tr("No directory to check existence for specified"));
2279 CheckComArgOutPointerValid(aExists);
2280
2281 AutoCaller autoCaller(this);
2282 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2283
2284 HRESULT hr = S_OK;
2285
2286 GuestFsObjData objData; int guestRc;
2287 int rc = directoryQueryInfoInternal(Utf8Str(aPath), objData, &guestRc);
2288 if (RT_SUCCESS(rc))
2289 {
2290 *aExists = objData.mType == FsObjType_Directory;
2291 }
2292 else
2293 {
2294 switch (rc)
2295 {
2296 case VERR_GSTCTL_GUEST_ERROR:
2297 hr = GuestProcess::setErrorExternal(this, guestRc);
2298 break;
2299
2300 default:
2301 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying directory existence \"%s\" failed: %Rrc"),
2302 Utf8Str(aPath).c_str(), rc);
2303 break;
2304 }
2305 }
2306
2307 return hr;
2308#endif /* VBOX_WITH_GUEST_CONTROL */
2309}
2310
2311STDMETHODIMP GuestSession::DirectoryOpen(IN_BSTR aPath, IN_BSTR aFilter, ComSafeArrayIn(DirectoryOpenFlag_T, aFlags), IGuestDirectory **aDirectory)
2312{
2313#ifndef VBOX_WITH_GUEST_CONTROL
2314 ReturnComNotImplemented();
2315#else
2316 LogFlowThisFuncEnter();
2317
2318 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2319 return setError(E_INVALIDARG, tr("No directory to open specified"));
2320 if (RT_UNLIKELY((aFilter) != NULL && *(aFilter) != '\0'))
2321 return setError(E_INVALIDARG, tr("Directory filters are not implemented yet"));
2322 CheckComArgOutPointerValid(aDirectory);
2323
2324 AutoCaller autoCaller(this);
2325 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2326
2327 uint32_t fFlags = DirectoryOpenFlag_None;
2328 if (aFlags)
2329 {
2330 com::SafeArray<DirectoryOpenFlag_T> flags(ComSafeArrayInArg(aFlags));
2331 for (size_t i = 0; i < flags.size(); i++)
2332 fFlags |= flags[i];
2333
2334 if (fFlags)
2335 return setError(E_INVALIDARG, tr("Open flags (%#x) not implemented yet"), fFlags);
2336 }
2337
2338 HRESULT hr = S_OK;
2339
2340 ComObjPtr <GuestDirectory> pDirectory;
2341 int rc = directoryOpenInternal(Utf8Str(aPath), Utf8Str(aFilter), fFlags, pDirectory);
2342 if (RT_SUCCESS(rc))
2343 {
2344 /* Return directory object to the caller. */
2345 hr = pDirectory.queryInterfaceTo(aDirectory);
2346 }
2347 else
2348 {
2349 switch (rc)
2350 {
2351 case VERR_INVALID_PARAMETER:
2352 hr = setError(VBOX_E_IPRT_ERROR, tr("Opening directory \"%s\" failed; invalid parameters given",
2353 Utf8Str(aPath).c_str()));
2354 break;
2355
2356 default:
2357 hr = setError(VBOX_E_IPRT_ERROR, tr("Opening directory \"%s\" failed: %Rrc"),
2358 Utf8Str(aPath).c_str(),rc);
2359 break;
2360 }
2361 }
2362
2363 return hr;
2364#endif /* VBOX_WITH_GUEST_CONTROL */
2365}
2366
2367STDMETHODIMP GuestSession::DirectoryQueryInfo(IN_BSTR aPath, IGuestFsObjInfo **aInfo)
2368{
2369#ifndef VBOX_WITH_GUEST_CONTROL
2370 ReturnComNotImplemented();
2371#else
2372 LogFlowThisFuncEnter();
2373
2374 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2375 return setError(E_INVALIDARG, tr("No directory to query information for specified"));
2376 CheckComArgOutPointerValid(aInfo);
2377
2378 AutoCaller autoCaller(this);
2379 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2380
2381 HRESULT hr = S_OK;
2382
2383 GuestFsObjData objData; int guestRc;
2384 int vrc = directoryQueryInfoInternal(Utf8Str(aPath), objData, &guestRc);
2385 if (RT_SUCCESS(vrc))
2386 {
2387 if (objData.mType == FsObjType_Directory)
2388 {
2389 ComObjPtr<GuestFsObjInfo> pFsObjInfo;
2390 hr = pFsObjInfo.createObject();
2391 if (FAILED(hr)) return hr;
2392
2393 vrc = pFsObjInfo->init(objData);
2394 if (RT_SUCCESS(vrc))
2395 {
2396 hr = pFsObjInfo.queryInterfaceTo(aInfo);
2397 if (FAILED(hr)) return hr;
2398 }
2399 }
2400 }
2401
2402 if (RT_FAILURE(vrc))
2403 {
2404 switch (vrc)
2405 {
2406 case VERR_GSTCTL_GUEST_ERROR:
2407 hr = GuestProcess::setErrorExternal(this, guestRc);
2408 break;
2409
2410 case VERR_NOT_A_DIRECTORY:
2411 hr = setError(VBOX_E_IPRT_ERROR, tr("Element \"%s\" exists but is not a directory",
2412 Utf8Str(aPath).c_str()));
2413 break;
2414
2415 default:
2416 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying directory information for \"%s\" failed: %Rrc"),
2417 Utf8Str(aPath).c_str(), vrc);
2418 break;
2419 }
2420 }
2421
2422 return hr;
2423#endif /* VBOX_WITH_GUEST_CONTROL */
2424}
2425
2426STDMETHODIMP GuestSession::DirectoryRemove(IN_BSTR aPath)
2427{
2428#ifndef VBOX_WITH_GUEST_CONTROL
2429 ReturnComNotImplemented();
2430#else
2431 LogFlowThisFuncEnter();
2432
2433 AutoCaller autoCaller(this);
2434 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2435
2436 ReturnComNotImplemented();
2437#endif /* VBOX_WITH_GUEST_CONTROL */
2438}
2439
2440STDMETHODIMP GuestSession::DirectoryRemoveRecursive(IN_BSTR aPath, ComSafeArrayIn(DirectoryRemoveRecFlag_T, aFlags), IProgress **aProgress)
2441{
2442#ifndef VBOX_WITH_GUEST_CONTROL
2443 ReturnComNotImplemented();
2444#else
2445 LogFlowThisFuncEnter();
2446
2447 AutoCaller autoCaller(this);
2448 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2449
2450 ReturnComNotImplemented();
2451#endif /* VBOX_WITH_GUEST_CONTROL */
2452}
2453
2454STDMETHODIMP GuestSession::DirectoryRename(IN_BSTR aSource, IN_BSTR aDest, ComSafeArrayIn(PathRenameFlag_T, aFlags))
2455{
2456#ifndef VBOX_WITH_GUEST_CONTROL
2457 ReturnComNotImplemented();
2458#else
2459 LogFlowThisFuncEnter();
2460
2461 AutoCaller autoCaller(this);
2462 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2463
2464 ReturnComNotImplemented();
2465#endif /* VBOX_WITH_GUEST_CONTROL */
2466}
2467
2468STDMETHODIMP GuestSession::DirectorySetACL(IN_BSTR aPath, IN_BSTR aACL)
2469{
2470#ifndef VBOX_WITH_GUEST_CONTROL
2471 ReturnComNotImplemented();
2472#else
2473 LogFlowThisFuncEnter();
2474
2475 AutoCaller autoCaller(this);
2476 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2477
2478 ReturnComNotImplemented();
2479#endif /* VBOX_WITH_GUEST_CONTROL */
2480}
2481
2482STDMETHODIMP GuestSession::EnvironmentClear(void)
2483{
2484#ifndef VBOX_WITH_GUEST_CONTROL
2485 ReturnComNotImplemented();
2486#else
2487 LogFlowThisFuncEnter();
2488
2489 AutoCaller autoCaller(this);
2490 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2491
2492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2493
2494 mData.mEnvironment.Clear();
2495
2496 LogFlowFuncLeaveRC(S_OK);
2497 return S_OK;
2498#endif /* VBOX_WITH_GUEST_CONTROL */
2499}
2500
2501STDMETHODIMP GuestSession::EnvironmentGet(IN_BSTR aName, BSTR *aValue)
2502{
2503#ifndef VBOX_WITH_GUEST_CONTROL
2504 ReturnComNotImplemented();
2505#else
2506 LogFlowThisFuncEnter();
2507
2508 if (RT_UNLIKELY((aName) == NULL || *(aName) == '\0'))
2509 return setError(E_INVALIDARG, tr("No value name specified"));
2510
2511 CheckComArgOutPointerValid(aValue);
2512
2513 AutoCaller autoCaller(this);
2514 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2515
2516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2517
2518 Bstr strValue(mData.mEnvironment.Get(Utf8Str(aName)));
2519 strValue.cloneTo(aValue);
2520
2521 LogFlowFuncLeaveRC(S_OK);
2522 return S_OK;
2523#endif /* VBOX_WITH_GUEST_CONTROL */
2524}
2525
2526STDMETHODIMP GuestSession::EnvironmentSet(IN_BSTR aName, IN_BSTR aValue)
2527{
2528#ifndef VBOX_WITH_GUEST_CONTROL
2529 ReturnComNotImplemented();
2530#else
2531 LogFlowThisFuncEnter();
2532
2533 if (RT_UNLIKELY((aName) == NULL || *(aName) == '\0'))
2534 return setError(E_INVALIDARG, tr("No value name specified"));
2535
2536 AutoCaller autoCaller(this);
2537 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2538
2539 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2540
2541 int rc = mData.mEnvironment.Set(Utf8Str(aName), Utf8Str(aValue));
2542
2543 HRESULT hr = RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
2544 LogFlowFuncLeaveRC(hr);
2545 return hr;
2546#endif /* VBOX_WITH_GUEST_CONTROL */
2547}
2548
2549STDMETHODIMP GuestSession::EnvironmentUnset(IN_BSTR aName)
2550{
2551#ifndef VBOX_WITH_GUEST_CONTROL
2552 ReturnComNotImplemented();
2553#else
2554 LogFlowThisFuncEnter();
2555
2556 AutoCaller autoCaller(this);
2557 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2558
2559 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2560
2561 mData.mEnvironment.Unset(Utf8Str(aName));
2562
2563 LogFlowFuncLeaveRC(S_OK);
2564 return S_OK;
2565#endif /* VBOX_WITH_GUEST_CONTROL */
2566}
2567
2568STDMETHODIMP GuestSession::FileCreateTemp(IN_BSTR aTemplate, ULONG aMode, IN_BSTR aPath, BOOL aSecure, IGuestFile **aFile)
2569{
2570#ifndef VBOX_WITH_GUEST_CONTROL
2571 ReturnComNotImplemented();
2572#else
2573 LogFlowThisFuncEnter();
2574
2575 AutoCaller autoCaller(this);
2576 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2577
2578 ReturnComNotImplemented();
2579#endif /* VBOX_WITH_GUEST_CONTROL */
2580}
2581
2582STDMETHODIMP GuestSession::FileExists(IN_BSTR aPath, BOOL *aExists)
2583{
2584#ifndef VBOX_WITH_GUEST_CONTROL
2585 ReturnComNotImplemented();
2586#else
2587 LogFlowThisFuncEnter();
2588
2589 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2590 return setError(E_INVALIDARG, tr("No file to check existence for specified"));
2591 CheckComArgOutPointerValid(aExists);
2592
2593 AutoCaller autoCaller(this);
2594 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2595
2596 GuestFsObjData objData; int guestRc;
2597 int vrc = fileQueryInfoInternal(Utf8Str(aPath), objData, &guestRc);
2598 if (RT_SUCCESS(vrc))
2599 {
2600 *aExists = TRUE;
2601 return S_OK;
2602 }
2603
2604 HRESULT hr = S_OK;
2605
2606 switch (vrc)
2607 {
2608 case VERR_GSTCTL_GUEST_ERROR:
2609 hr = GuestProcess::setErrorExternal(this, guestRc);
2610 break;
2611
2612 case VERR_NOT_A_FILE:
2613 *aExists = FALSE;
2614 break;
2615
2616 default:
2617 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying file information for \"%s\" failed: %Rrc"),
2618 Utf8Str(aPath).c_str(), vrc);
2619 break;
2620 }
2621
2622 return hr;
2623#endif /* VBOX_WITH_GUEST_CONTROL */
2624}
2625
2626STDMETHODIMP GuestSession::FileRemove(IN_BSTR aPath)
2627{
2628#ifndef VBOX_WITH_GUEST_CONTROL
2629 ReturnComNotImplemented();
2630#else
2631 LogFlowThisFuncEnter();
2632
2633 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2634 return setError(E_INVALIDARG, tr("No file to remove specified"));
2635
2636 AutoCaller autoCaller(this);
2637 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2638
2639 HRESULT hr = S_OK;
2640
2641 int guestRc;
2642 int vrc = fileRemoveInternal(Utf8Str(aPath), &guestRc);
2643 if (RT_FAILURE(vrc))
2644 {
2645 switch (vrc)
2646 {
2647 case VERR_GSTCTL_GUEST_ERROR:
2648 hr = GuestProcess::setErrorExternal(this, guestRc);
2649 break;
2650
2651 default:
2652 hr = setError(VBOX_E_IPRT_ERROR, tr("Removing file \"%s\" failed: %Rrc"),
2653 Utf8Str(aPath).c_str(), vrc);
2654 break;
2655 }
2656 }
2657
2658 return hr;
2659#endif /* VBOX_WITH_GUEST_CONTROL */
2660}
2661
2662STDMETHODIMP GuestSession::FileOpen(IN_BSTR aPath, IN_BSTR aOpenMode, IN_BSTR aDisposition, ULONG aCreationMode, LONG64 aOffset, IGuestFile **aFile)
2663{
2664#ifndef VBOX_WITH_GUEST_CONTROL
2665 ReturnComNotImplemented();
2666#else
2667 LogFlowThisFuncEnter();
2668
2669 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2670 return setError(E_INVALIDARG, tr("No file to open specified"));
2671 if (RT_UNLIKELY((aOpenMode) == NULL || *(aOpenMode) == '\0'))
2672 return setError(E_INVALIDARG, tr("No open mode specified"));
2673 if (RT_UNLIKELY((aDisposition) == NULL || *(aDisposition) == '\0'))
2674 return setError(E_INVALIDARG, tr("No disposition mode specified"));
2675
2676 CheckComArgOutPointerValid(aFile);
2677
2678 AutoCaller autoCaller(this);
2679 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2680
2681 HRESULT hr = isReadyExternal();
2682 if (FAILED(hr))
2683 return hr;
2684
2685 /** @todo Validate open mode. */
2686 /** @todo Validate disposition mode. */
2687
2688 /** @todo Validate creation mode. */
2689 uint32_t uCreationMode = 0;
2690
2691 GuestFileOpenInfo openInfo;
2692 openInfo.mFileName = Utf8Str(aPath);
2693 openInfo.mOpenMode = Utf8Str(aOpenMode);
2694 openInfo.mDisposition = Utf8Str(aDisposition);
2695 openInfo.mCreationMode = aCreationMode;
2696 openInfo.mInitialOffset = aOffset;
2697
2698 ComObjPtr <GuestFile> pFile; int guestRc;
2699 int vrc = fileOpenInternal(openInfo, pFile, &guestRc);
2700 if (RT_SUCCESS(vrc))
2701 {
2702 /* Return directory object to the caller. */
2703 hr = pFile.queryInterfaceTo(aFile);
2704 }
2705 else
2706 {
2707 switch (vrc)
2708 {
2709 case VERR_GSTCTL_GUEST_ERROR:
2710 hr = GuestFile::setErrorExternal(this, guestRc);
2711 break;
2712
2713 default:
2714 hr = setError(VBOX_E_IPRT_ERROR, tr("Opening guest file \"%s\" failed: %Rrc"),
2715 Utf8Str(aPath).c_str(), vrc);
2716 break;
2717 }
2718 }
2719
2720 return hr;
2721#endif /* VBOX_WITH_GUEST_CONTROL */
2722}
2723
2724STDMETHODIMP GuestSession::FileQueryInfo(IN_BSTR aPath, IGuestFsObjInfo **aInfo)
2725{
2726#ifndef VBOX_WITH_GUEST_CONTROL
2727 ReturnComNotImplemented();
2728#else
2729 LogFlowThisFuncEnter();
2730
2731 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2732 return setError(E_INVALIDARG, tr("No file to query information for specified"));
2733 CheckComArgOutPointerValid(aInfo);
2734
2735 AutoCaller autoCaller(this);
2736 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2737
2738 HRESULT hr = S_OK;
2739
2740 GuestFsObjData objData; int guestRc;
2741 int vrc = fileQueryInfoInternal(Utf8Str(aPath), objData, &guestRc);
2742 if (RT_SUCCESS(vrc))
2743 {
2744 ComObjPtr<GuestFsObjInfo> pFsObjInfo;
2745 hr = pFsObjInfo.createObject();
2746 if (FAILED(hr)) return hr;
2747
2748 vrc = pFsObjInfo->init(objData);
2749 if (RT_SUCCESS(vrc))
2750 {
2751 hr = pFsObjInfo.queryInterfaceTo(aInfo);
2752 if (FAILED(hr)) return hr;
2753 }
2754 }
2755
2756 if (RT_FAILURE(vrc))
2757 {
2758 switch (vrc)
2759 {
2760 case VERR_GSTCTL_GUEST_ERROR:
2761 hr = GuestProcess::setErrorExternal(this, guestRc);
2762 break;
2763
2764 case VERR_NOT_A_FILE:
2765 hr = setError(VBOX_E_IPRT_ERROR, tr("Element exists but is not a file"));
2766 break;
2767
2768 default:
2769 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying file information failed: %Rrc"), vrc);
2770 break;
2771 }
2772 }
2773
2774 return hr;
2775#endif /* VBOX_WITH_GUEST_CONTROL */
2776}
2777
2778STDMETHODIMP GuestSession::FileQuerySize(IN_BSTR aPath, LONG64 *aSize)
2779{
2780#ifndef VBOX_WITH_GUEST_CONTROL
2781 ReturnComNotImplemented();
2782#else
2783 LogFlowThisFuncEnter();
2784
2785 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2786 return setError(E_INVALIDARG, tr("No file to query size for specified"));
2787 CheckComArgOutPointerValid(aSize);
2788
2789 AutoCaller autoCaller(this);
2790 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2791
2792 HRESULT hr = S_OK;
2793
2794 int64_t llSize; int guestRc;
2795 int vrc = fileQuerySizeInternal(Utf8Str(aPath), &llSize, &guestRc);
2796 if (RT_SUCCESS(vrc))
2797 {
2798 *aSize = llSize;
2799 }
2800 else
2801 {
2802 switch (vrc)
2803 {
2804 case VERR_GSTCTL_GUEST_ERROR:
2805 hr = GuestProcess::setErrorExternal(this, guestRc);
2806 break;
2807
2808 default:
2809 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying file size failed: %Rrc"), vrc);
2810 break;
2811 }
2812 }
2813
2814 return hr;
2815#endif /* VBOX_WITH_GUEST_CONTROL */
2816}
2817
2818STDMETHODIMP GuestSession::FileRename(IN_BSTR aSource, IN_BSTR aDest, ComSafeArrayIn(PathRenameFlag_T, aFlags))
2819{
2820#ifndef VBOX_WITH_GUEST_CONTROL
2821 ReturnComNotImplemented();
2822#else
2823 LogFlowThisFuncEnter();
2824
2825 AutoCaller autoCaller(this);
2826 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2827
2828 ReturnComNotImplemented();
2829#endif /* VBOX_WITH_GUEST_CONTROL */
2830}
2831
2832STDMETHODIMP GuestSession::FileSetACL(IN_BSTR aPath, IN_BSTR aACL)
2833{
2834#ifndef VBOX_WITH_GUEST_CONTROL
2835 ReturnComNotImplemented();
2836#else
2837 LogFlowThisFuncEnter();
2838
2839 AutoCaller autoCaller(this);
2840 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2841
2842 ReturnComNotImplemented();
2843#endif /* VBOX_WITH_GUEST_CONTROL */
2844}
2845
2846STDMETHODIMP GuestSession::ProcessCreate(IN_BSTR aCommand, ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
2847 ComSafeArrayIn(ProcessCreateFlag_T, aFlags), ULONG aTimeoutMS, IGuestProcess **aProcess)
2848{
2849#ifndef VBOX_WITH_GUEST_CONTROL
2850 ReturnComNotImplemented();
2851#else
2852 LogFlowThisFuncEnter();
2853
2854 com::SafeArray<LONG> affinity;
2855
2856 return ProcessCreateEx(aCommand, ComSafeArrayInArg(aArguments), ComSafeArrayInArg(aEnvironment),
2857 ComSafeArrayInArg(aFlags), aTimeoutMS, ProcessPriority_Default, ComSafeArrayAsInParam(affinity), aProcess);
2858#endif /* VBOX_WITH_GUEST_CONTROL */
2859}
2860
2861STDMETHODIMP GuestSession::ProcessCreateEx(IN_BSTR aCommand, ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
2862 ComSafeArrayIn(ProcessCreateFlag_T, aFlags), ULONG aTimeoutMS,
2863 ProcessPriority_T aPriority, ComSafeArrayIn(LONG, aAffinity),
2864 IGuestProcess **aProcess)
2865{
2866#ifndef VBOX_WITH_GUEST_CONTROL
2867 ReturnComNotImplemented();
2868#else
2869 LogFlowThisFuncEnter();
2870
2871 if (RT_UNLIKELY((aCommand) == NULL || *(aCommand) == '\0'))
2872 return setError(E_INVALIDARG, tr("No command to execute specified"));
2873 CheckComArgOutPointerValid(aProcess);
2874
2875 AutoCaller autoCaller(this);
2876 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2877
2878 HRESULT hr = isReadyExternal();
2879 if (FAILED(hr))
2880 return hr;
2881
2882 GuestProcessStartupInfo procInfo;
2883 procInfo.mCommand = Utf8Str(aCommand);
2884
2885 if (aArguments)
2886 {
2887 com::SafeArray<IN_BSTR> arguments(ComSafeArrayInArg(aArguments));
2888 for (size_t i = 0; i < arguments.size(); i++)
2889 procInfo.mArguments.push_back(Utf8Str(arguments[i]));
2890 }
2891
2892 int rc = VINF_SUCCESS;
2893
2894 /*
2895 * Create the process environment:
2896 * - Apply the session environment in a first step, and
2897 * - Apply environment variables specified by this call to
2898 * have the chance of overwriting/deleting session entries.
2899 */
2900 procInfo.mEnvironment = mData.mEnvironment; /* Apply original session environment. */
2901
2902 if (aEnvironment)
2903 {
2904 com::SafeArray<IN_BSTR> environment(ComSafeArrayInArg(aEnvironment));
2905 for (size_t i = 0; i < environment.size() && RT_SUCCESS(rc); i++)
2906 rc = procInfo.mEnvironment.Set(Utf8Str(environment[i]));
2907 }
2908
2909 if (RT_SUCCESS(rc))
2910 {
2911 if (aFlags)
2912 {
2913 com::SafeArray<ProcessCreateFlag_T> flags(ComSafeArrayInArg(aFlags));
2914 for (size_t i = 0; i < flags.size(); i++)
2915 procInfo.mFlags |= flags[i];
2916 }
2917
2918 procInfo.mTimeoutMS = aTimeoutMS;
2919
2920 if (aAffinity)
2921 {
2922 com::SafeArray<LONG> affinity(ComSafeArrayInArg(aAffinity));
2923 for (size_t i = 0; i < affinity.size(); i++)
2924 {
2925 if (affinity[i])
2926 procInfo.mAffinity |= (uint64_t)1 << i;
2927 }
2928 }
2929
2930 procInfo.mPriority = aPriority;
2931
2932 ComObjPtr<GuestProcess> pProcess;
2933 rc = processCreateExInteral(procInfo, pProcess);
2934 if (RT_SUCCESS(rc))
2935 {
2936 /* Return guest session to the caller. */
2937 HRESULT hr2 = pProcess.queryInterfaceTo(aProcess);
2938 if (FAILED(hr2))
2939 rc = VERR_COM_OBJECT_NOT_FOUND;
2940
2941 if (RT_SUCCESS(rc))
2942 rc = pProcess->startProcessAsync();
2943 }
2944 }
2945
2946 if (RT_FAILURE(rc))
2947 {
2948 switch (rc)
2949 {
2950 case VERR_MAX_PROCS_REACHED:
2951 hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of concurrent guest processes per session (%ld) reached"),
2952 VBOX_GUESTCTRL_MAX_OBJECTS);
2953 break;
2954
2955 /** @todo Add more errors here. */
2956
2957 default:
2958 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not create guest process, rc=%Rrc"), rc);
2959 break;
2960 }
2961 }
2962
2963 LogFlowFuncLeaveRC(rc);
2964 return hr;
2965#endif /* VBOX_WITH_GUEST_CONTROL */
2966}
2967
2968STDMETHODIMP GuestSession::ProcessGet(ULONG aPID, IGuestProcess **aProcess)
2969{
2970#ifndef VBOX_WITH_GUEST_CONTROL
2971 ReturnComNotImplemented();
2972#else
2973 LogFlowThisFunc(("aPID=%RU32\n", aPID));
2974
2975 CheckComArgOutPointerValid(aProcess);
2976 if (aPID == 0)
2977 return setError(E_INVALIDARG, tr("No valid process ID (PID) specified"));
2978
2979 AutoCaller autoCaller(this);
2980 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2981
2982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2983
2984 HRESULT hr = S_OK;
2985
2986 ComObjPtr<GuestProcess> pProcess;
2987 int rc = processGetByPID(aPID, &pProcess);
2988 if (RT_FAILURE(rc))
2989 hr = setError(E_INVALIDARG, tr("No process with PID %RU32 found"), aPID);
2990
2991 /* This will set (*aProcess) to NULL if pProgress is NULL. */
2992 HRESULT hr2 = pProcess.queryInterfaceTo(aProcess);
2993 if (SUCCEEDED(hr))
2994 hr = hr2;
2995
2996 LogFlowThisFunc(("aProcess=%p, hr=%Rhrc\n", *aProcess, hr));
2997 return hr;
2998#endif /* VBOX_WITH_GUEST_CONTROL */
2999}
3000
3001STDMETHODIMP GuestSession::SymlinkCreate(IN_BSTR aSource, IN_BSTR aTarget, SymlinkType_T aType)
3002{
3003#ifndef VBOX_WITH_GUEST_CONTROL
3004 ReturnComNotImplemented();
3005#else
3006 LogFlowThisFuncEnter();
3007
3008 AutoCaller autoCaller(this);
3009 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3010
3011 ReturnComNotImplemented();
3012#endif /* VBOX_WITH_GUEST_CONTROL */
3013}
3014
3015STDMETHODIMP GuestSession::SymlinkExists(IN_BSTR aSymlink, BOOL *aExists)
3016{
3017#ifndef VBOX_WITH_GUEST_CONTROL
3018 ReturnComNotImplemented();
3019#else
3020 LogFlowThisFuncEnter();
3021
3022 AutoCaller autoCaller(this);
3023 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3024
3025 ReturnComNotImplemented();
3026#endif /* VBOX_WITH_GUEST_CONTROL */
3027}
3028
3029STDMETHODIMP GuestSession::SymlinkRead(IN_BSTR aSymlink, ComSafeArrayIn(SymlinkReadFlag_T, aFlags), BSTR *aTarget)
3030{
3031#ifndef VBOX_WITH_GUEST_CONTROL
3032 ReturnComNotImplemented();
3033#else
3034 LogFlowThisFuncEnter();
3035
3036 AutoCaller autoCaller(this);
3037 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3038
3039 ReturnComNotImplemented();
3040#endif /* VBOX_WITH_GUEST_CONTROL */
3041}
3042
3043STDMETHODIMP GuestSession::SymlinkRemoveDirectory(IN_BSTR aPath)
3044{
3045#ifndef VBOX_WITH_GUEST_CONTROL
3046 ReturnComNotImplemented();
3047#else
3048 LogFlowThisFuncEnter();
3049
3050 AutoCaller autoCaller(this);
3051 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3052
3053 ReturnComNotImplemented();
3054#endif /* VBOX_WITH_GUEST_CONTROL */
3055}
3056
3057STDMETHODIMP GuestSession::SymlinkRemoveFile(IN_BSTR aFile)
3058{
3059#ifndef VBOX_WITH_GUEST_CONTROL
3060 ReturnComNotImplemented();
3061#else
3062 LogFlowThisFuncEnter();
3063
3064 AutoCaller autoCaller(this);
3065 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3066
3067 ReturnComNotImplemented();
3068#endif /* VBOX_WITH_GUEST_CONTROL */
3069}
3070
3071STDMETHODIMP GuestSession::WaitFor(ULONG aWaitFlags, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
3072{
3073#ifndef VBOX_WITH_GUEST_CONTROL
3074 ReturnComNotImplemented();
3075#else
3076 LogFlowThisFuncEnter();
3077
3078 CheckComArgOutPointerValid(aReason);
3079
3080 AutoCaller autoCaller(this);
3081 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3082
3083 /*
3084 * Note: Do not hold any locks here while waiting!
3085 */
3086 HRESULT hr = S_OK;
3087
3088 int guestRc; GuestSessionWaitResult_T waitResult;
3089 int vrc = waitFor(aWaitFlags, aTimeoutMS, waitResult, &guestRc);
3090 if (RT_SUCCESS(vrc))
3091 {
3092 *aReason = waitResult;
3093 }
3094 else
3095 {
3096 switch (vrc)
3097 {
3098 case VERR_GSTCTL_GUEST_ERROR:
3099 hr = GuestSession::setErrorExternal(this, guestRc);
3100 break;
3101
3102 case VERR_TIMEOUT:
3103 *aReason = GuestSessionWaitResult_Timeout;
3104 break;
3105
3106 default:
3107 {
3108 const char *pszSessionName = mData.mSession.mName.c_str();
3109 hr = setError(VBOX_E_IPRT_ERROR,
3110 tr("Waiting for guest session \"%s\" failed: %Rrc"),
3111 pszSessionName ? pszSessionName : tr("Unnamed"), vrc);
3112 break;
3113 }
3114 }
3115 }
3116
3117 LogFlowFuncLeaveRC(vrc);
3118 return hr;
3119#endif /* VBOX_WITH_GUEST_CONTROL */
3120}
3121
3122STDMETHODIMP GuestSession::WaitForArray(ComSafeArrayIn(GuestSessionWaitForFlag_T, aFlags), ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
3123{
3124#ifndef VBOX_WITH_GUEST_CONTROL
3125 ReturnComNotImplemented();
3126#else
3127 LogFlowThisFuncEnter();
3128
3129 CheckComArgOutPointerValid(aReason);
3130
3131 AutoCaller autoCaller(this);
3132 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3133
3134 /*
3135 * Note: Do not hold any locks here while waiting!
3136 */
3137 uint32_t fWaitFor = GuestSessionWaitForFlag_None;
3138 com::SafeArray<GuestSessionWaitForFlag_T> flags(ComSafeArrayInArg(aFlags));
3139 for (size_t i = 0; i < flags.size(); i++)
3140 fWaitFor |= flags[i];
3141
3142 return WaitFor(fWaitFor, aTimeoutMS, aReason);
3143#endif /* VBOX_WITH_GUEST_CONTROL */
3144}
3145
Note: See TracBrowser for help on using the repository browser.

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