VirtualBox

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

Last change on this file since 96391 was 95503, checked in by vboxsync, 3 years ago

Guest Control/Main: Explicitly handle GuestSessionStatus_Starting and GuestSessionStatus_Terminating statuses in i_waitFor() by waiting for the next upcoming event if this is not what the caller explicitly wanted to wait for.

For example, this will help waiting for a guest session to get fully started (status GuestSessionStatus_Started), where a GuestSessionStatus_Starting event will come first and therefore needs to be ignored. bugref:10157

[Build fix]

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