VirtualBox

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

Last change on this file since 97476 was 97459, checked in by vboxsync, 2 years ago

Guest Control/Main: Corrected some vrcGuest nits. Cosmetical only.

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

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