VirtualBox

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

Last change on this file since 77582 was 77582, checked in by vboxsync, 6 years ago

Guest Control/Main: Renamed IGuestSession::i_isReadyExternal() -> IGuestSession::i_isStartedExternal() to match terminology, and added i_isStarted() + i_isTerminated().

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