VirtualBox

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

Last change on this file since 97436 was 97412, checked in by vboxsync, 2 years ago

Guest Control/Main: Temporary verbose logging build for the Testboxes. Losing patience with the non-working environment tweaks, sigh. ​bugref:10286

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 158.1 KB
Line 
1/* $Id: GuestSessionImpl.cpp 97412 2022-11-06 09:30:21Z 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 LogRel(("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 LogRel(("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 = vrc;
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 int vrc = VINF_SUCCESS;
1589
1590 GuestProcessStartupInfo procInfo;
1591 GuestProcessStream streamOut;
1592
1593 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
1594 procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_RM);
1595
1596 try
1597 {
1598 procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */
1599 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
1600 procInfo.mArguments.push_back("--"); /* strPath could be '--help', which is a valid filename. */
1601 procInfo.mArguments.push_back(strPath); /* The file we want to remove. */
1602 }
1603 catch (std::bad_alloc &)
1604 {
1605 vrc = VERR_NO_MEMORY;
1606 }
1607
1608 if (RT_SUCCESS(vrc))
1609 vrc = GuestProcessTool::run(this, procInfo, prcGuest);
1610
1611 LogFlowFuncLeaveRC(vrc);
1612 return vrc;
1613}
1614
1615/**
1616 * Opens a file on the guest.
1617 *
1618 * @returns VBox status code.
1619 * @returns VERR_GSTCTL_GUEST_ERROR on received guest error.
1620 * @param aPath File path on guest to open.
1621 * @param aAccessMode Access mode to use.
1622 * @param aOpenAction Open action to use.
1623 * @param aSharingMode Sharing mode to use.
1624 * @param aCreationMode Creation mode to use.
1625 * @param aFlags Open flags to use.
1626 * @param pFile Where to return the file object on success.
1627 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
1628 * was returned. Optional.
1629 *
1630 * @note Takes the write lock.
1631 */
1632int GuestSession::i_fileOpenEx(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
1633 FileSharingMode_T aSharingMode, ULONG aCreationMode, const std::vector<FileOpenExFlag_T> &aFlags,
1634 ComObjPtr<GuestFile> &pFile, int *prcGuest)
1635{
1636 GuestFileOpenInfo openInfo;
1637 openInfo.mFilename = aPath;
1638 openInfo.mCreationMode = aCreationMode;
1639 openInfo.mAccessMode = aAccessMode;
1640 openInfo.mOpenAction = aOpenAction;
1641 openInfo.mSharingMode = aSharingMode;
1642
1643 /* Combine and validate flags. */
1644 for (size_t i = 0; i < aFlags.size(); i++)
1645 openInfo.mfOpenEx |= aFlags[i];
1646 /* Validation is done in i_fileOpen(). */
1647
1648 return i_fileOpen(openInfo, pFile, prcGuest);
1649}
1650
1651/**
1652 * Opens a file on the guest.
1653 *
1654 * @returns VBox status code.
1655 * @returns VERR_GSTCTL_GUEST_ERROR on received guest error.
1656 * @param openInfo Open information to use for opening the file.
1657 * @param pFile Where to return the file object on success.
1658 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
1659 * was returned. Optional.
1660 *
1661 * @note Takes the write lock.
1662 */
1663int GuestSession::i_fileOpen(const GuestFileOpenInfo &openInfo, ComObjPtr<GuestFile> &pFile, int *prcGuest)
1664{
1665 LogFlowThisFunc(("strFile=%s, enmAccessMode=0x%x, enmOpenAction=0x%x, uCreationMode=%RU32, mfOpenEx=%RU32\n",
1666 openInfo.mFilename.c_str(), openInfo.mAccessMode, openInfo.mOpenAction, openInfo.mCreationMode,
1667 openInfo.mfOpenEx));
1668
1669 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1670
1671 /* Guest Additions < 4.3 don't support handling guest files, skip. */
1672 if (mData.mProtocolVersion < 2)
1673 {
1674 if (prcGuest)
1675 *prcGuest = VERR_NOT_SUPPORTED;
1676 return VERR_GSTCTL_GUEST_ERROR;
1677 }
1678
1679 if (!openInfo.IsValid())
1680 return VERR_INVALID_PARAMETER;
1681
1682 /* Create the directory object. */
1683 HRESULT hr = pFile.createObject();
1684 if (FAILED(hr))
1685 return VERR_COM_UNEXPECTED;
1686
1687 /* Register a new object ID. */
1688 uint32_t idObject;
1689 int rc = i_objectRegister(pFile, SESSIONOBJECTTYPE_FILE, &idObject);
1690 if (RT_FAILURE(rc))
1691 {
1692 pFile.setNull();
1693 return rc;
1694 }
1695
1696 Console *pConsole = mParent->i_getConsole();
1697 AssertPtr(pConsole);
1698
1699 rc = pFile->init(pConsole, this /* GuestSession */, idObject, openInfo);
1700 if (RT_FAILURE(rc))
1701 return rc;
1702
1703 /*
1704 * Since this is a synchronous guest call we have to
1705 * register the file object first, releasing the session's
1706 * lock and then proceed with the actual opening command
1707 * -- otherwise the file's opening callback would hang
1708 * because the session's lock still is in place.
1709 */
1710 try
1711 {
1712 /* Add the created file to our vector. */
1713 mData.mFiles[idObject] = pFile;
1714
1715 LogFlowFunc(("Added new guest file \"%s\" (Session: %RU32) (now total %zu files)\n",
1716 openInfo.mFilename.c_str(), mData.mSession.mID, mData.mFiles.size()));
1717
1718 alock.release(); /* Release lock before firing off event. */
1719
1720 ::FireGuestFileRegisteredEvent(mEventSource, this, pFile, true /* Registered */);
1721 }
1722 catch (std::bad_alloc &)
1723 {
1724 rc = VERR_NO_MEMORY;
1725 }
1726
1727 if (RT_SUCCESS(rc))
1728 {
1729 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1730 rc = pFile->i_openFile(30 * 1000 /* 30s timeout */, &rcGuest);
1731 if ( rc == VERR_GSTCTL_GUEST_ERROR
1732 && prcGuest)
1733 {
1734 *prcGuest = rcGuest;
1735 }
1736 }
1737
1738 LogFlowFuncLeaveRC(rc);
1739 return rc;
1740}
1741
1742/**
1743 * Queries information from a file on the guest.
1744 *
1745 * @returns IPRT status code. VERR_NOT_A_FILE if the queried file system object on the guest is not a file,
1746 * or VERR_GSTCTL_GUEST_ERROR if prcGuest contains more error information from the guest.
1747 * @param strPath Absolute path of file to query information for.
1748 * @param fFollowSymlinks Whether or not to follow symbolic links on the guest.
1749 * @param objData Where to store the acquired information.
1750 * @param prcGuest Where to store the guest rc. Optional.
1751 */
1752int GuestSession::i_fileQueryInfo(const Utf8Str &strPath, bool fFollowSymlinks, GuestFsObjData &objData, int *prcGuest)
1753{
1754 LogFlowThisFunc(("strPath=%s fFollowSymlinks=%RTbool\n", strPath.c_str(), fFollowSymlinks));
1755
1756 int vrc = i_fsQueryInfo(strPath, fFollowSymlinks, objData, prcGuest);
1757 if (RT_SUCCESS(vrc))
1758 {
1759 vrc = objData.mType == FsObjType_File
1760 ? VINF_SUCCESS : VERR_NOT_A_FILE;
1761 }
1762
1763 LogFlowFuncLeaveRC(vrc);
1764 return vrc;
1765}
1766
1767/**
1768 * Queries the size of a file on the guest.
1769 *
1770 * @returns VBox status code.
1771 * @returns VERR_GSTCTL_GUEST_ERROR on received guest error.
1772 * @retval VERR_
1773 * @param strPath Path of file on guest to query size for.
1774 * @param fFollowSymlinks \c true when wanting to follow symbolic links, \c false if not.
1775 * @param pllSize Where to return the queried file size on success.
1776 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
1777 * was returned. Optional.
1778 */
1779int GuestSession::i_fileQuerySize(const Utf8Str &strPath, bool fFollowSymlinks, int64_t *pllSize, int *prcGuest)
1780{
1781 AssertPtrReturn(pllSize, VERR_INVALID_POINTER);
1782
1783 GuestFsObjData objData;
1784 int vrc = i_fileQueryInfo(strPath, fFollowSymlinks, objData, prcGuest);
1785 if (RT_SUCCESS(vrc))
1786 *pllSize = objData.mObjectSize;
1787
1788 return vrc;
1789}
1790
1791/**
1792 * Queries information of a file system object (file, directory, ...).
1793 *
1794 * @return IPRT status code.
1795 * @param strPath Path to file system object to query information for.
1796 * @param fFollowSymlinks Whether to follow symbolic links or not.
1797 * @param objData Where to return the file system object data, if found.
1798 * @param prcGuest Guest rc, when returning VERR_GSTCTL_GUEST_ERROR.
1799 * Any other return code indicates some host side error.
1800 */
1801int GuestSession::i_fsQueryInfo(const Utf8Str &strPath, bool fFollowSymlinks, GuestFsObjData &objData, int *prcGuest)
1802{
1803 LogFlowThisFunc(("strPath=%s\n", strPath.c_str()));
1804
1805 /** @todo Merge this with IGuestFile::queryInfo(). */
1806 GuestProcessStartupInfo procInfo;
1807 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
1808 try
1809 {
1810 procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_STAT);
1811 procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */
1812 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
1813 if (fFollowSymlinks)
1814 procInfo.mArguments.push_back(Utf8Str("-L"));
1815 procInfo.mArguments.push_back("--"); /* strPath could be '--help', which is a valid filename. */
1816 procInfo.mArguments.push_back(strPath);
1817 }
1818 catch (std::bad_alloc &)
1819 {
1820 Log(("Out of memory!\n"));
1821 return VERR_NO_MEMORY;
1822 }
1823
1824 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1825 GuestCtrlStreamObjects stdOut;
1826 int vrc = GuestProcessTool::runEx(this, procInfo,
1827 &stdOut, 1 /* cStrmOutObjects */,
1828 &vrcGuest);
1829 if (!GuestProcess::i_isGuestError(vrc))
1830 {
1831 if (!stdOut.empty())
1832 {
1833 vrc = objData.FromStat(stdOut.at(0));
1834 if (RT_FAILURE(vrc))
1835 {
1836 vrcGuest = vrc;
1837 if (prcGuest)
1838 *prcGuest = vrc;
1839 vrc = VERR_GSTCTL_GUEST_ERROR;
1840 }
1841 }
1842 else
1843 vrc = VERR_BROKEN_PIPE;
1844 }
1845 else if (prcGuest)
1846 *prcGuest = vrcGuest;
1847
1848 LogFlowThisFunc(("Returning vrc=%Rrc, vrcGuest=%Rrc\n", vrc, vrcGuest));
1849 return vrc;
1850}
1851
1852/**
1853 * Returns the guest credentials of a guest session.
1854 *
1855 * @returns Guest credentials.
1856 */
1857const GuestCredentials& GuestSession::i_getCredentials(void)
1858{
1859 return mData.mCredentials;
1860}
1861
1862/**
1863 * Returns the guest session (friendly) name.
1864 *
1865 * @returns Guest session name.
1866 */
1867Utf8Str GuestSession::i_getName(void)
1868{
1869 return mData.mSession.mName;
1870}
1871
1872/**
1873 * Returns a stringified error description for a given guest result code.
1874 *
1875 * @returns Stringified error description.
1876 */
1877/* static */
1878Utf8Str GuestSession::i_guestErrorToString(int rcGuest)
1879{
1880 Utf8Str strError;
1881
1882 /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
1883 switch (rcGuest)
1884 {
1885 case VERR_INVALID_VM_HANDLE:
1886 strError.printf(tr("VMM device is not available (is the VM running?)"));
1887 break;
1888
1889 case VERR_HGCM_SERVICE_NOT_FOUND:
1890 strError.printf(tr("The guest execution service is not available"));
1891 break;
1892
1893 case VERR_ACCOUNT_RESTRICTED:
1894 strError.printf(tr("The specified user account on the guest is restricted and can't be used to logon"));
1895 break;
1896
1897 case VERR_AUTHENTICATION_FAILURE:
1898 strError.printf(tr("The specified user was not able to logon on guest"));
1899 break;
1900
1901 case VERR_TIMEOUT:
1902 strError.printf(tr("The guest did not respond within time"));
1903 break;
1904
1905 case VERR_CANCELLED:
1906 strError.printf(tr("The session operation was canceled"));
1907 break;
1908
1909 case VERR_GSTCTL_MAX_CID_OBJECTS_REACHED:
1910 strError.printf(tr("Maximum number of concurrent guest processes has been reached"));
1911 break;
1912
1913 case VERR_NOT_FOUND:
1914 strError.printf(tr("The guest execution service is not ready (yet)"));
1915 break;
1916
1917 default:
1918 strError.printf("%Rrc", rcGuest);
1919 break;
1920 }
1921
1922 return strError;
1923}
1924
1925/**
1926 * Returns whether the session is in a started state or not.
1927 *
1928 * @returns \c true if in a started state, or \c false if not.
1929 */
1930bool GuestSession::i_isStarted(void) const
1931{
1932 return (mData.mStatus == GuestSessionStatus_Started);
1933}
1934
1935/**
1936 * Checks if this session is ready state where it can handle
1937 * all session-bound actions (like guest processes, guest files).
1938 * Only used by official API methods. Will set an external
1939 * error when not ready.
1940 */
1941HRESULT GuestSession::i_isStartedExternal(void)
1942{
1943 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1944
1945 /** @todo Be a bit more informative. */
1946 if (!i_isStarted())
1947 return setError(E_UNEXPECTED, tr("Session is not in started state"));
1948
1949 return S_OK;
1950}
1951
1952/**
1953 * Returns whether a guest session status implies a terminated state or not.
1954 *
1955 * @returns \c true if it's a terminated state, or \c false if not.
1956 */
1957/* static */
1958bool GuestSession::i_isTerminated(GuestSessionStatus_T enmStatus)
1959{
1960 switch (enmStatus)
1961 {
1962 case GuestSessionStatus_Terminated:
1963 RT_FALL_THROUGH();
1964 case GuestSessionStatus_TimedOutKilled:
1965 RT_FALL_THROUGH();
1966 case GuestSessionStatus_TimedOutAbnormally:
1967 RT_FALL_THROUGH();
1968 case GuestSessionStatus_Down:
1969 RT_FALL_THROUGH();
1970 case GuestSessionStatus_Error:
1971 return true;
1972
1973 default:
1974 break;
1975 }
1976
1977 return false;
1978}
1979
1980/**
1981 * Returns whether the session is in a terminated state or not.
1982 *
1983 * @returns \c true if in a terminated state, or \c false if not.
1984 */
1985bool GuestSession::i_isTerminated(void) const
1986{
1987 return GuestSession::i_isTerminated(mData.mStatus);
1988}
1989
1990/**
1991 * Called by IGuest right before this session gets removed from
1992 * the public session list.
1993 *
1994 * @note Takes the write lock.
1995 */
1996int GuestSession::i_onRemove(void)
1997{
1998 LogFlowThisFuncEnter();
1999
2000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2001
2002 int vrc = i_objectsUnregister();
2003
2004 /*
2005 * Note: The event source stuff holds references to this object,
2006 * so make sure that this is cleaned up *before* calling uninit.
2007 */
2008 if (!mEventSource.isNull())
2009 {
2010 mEventSource->UnregisterListener(mLocalListener);
2011
2012 mLocalListener.setNull();
2013 unconst(mEventSource).setNull();
2014 }
2015
2016 LogFlowFuncLeaveRC(vrc);
2017 return vrc;
2018}
2019
2020/**
2021 * Handles guest session status changes from the guest.
2022 *
2023 * @returns VBox status code.
2024 * @param pCbCtx Host callback context from HGCM service.
2025 * @param pSvcCbData HGCM service callback data.
2026 *
2027 * @note Takes the read lock (for session ID lookup).
2028 */
2029int GuestSession::i_onSessionStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
2030{
2031 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
2032 /* pCallback is optional. */
2033 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
2034
2035 if (pSvcCbData->mParms < 3)
2036 return VERR_INVALID_PARAMETER;
2037
2038 CALLBACKDATA_SESSION_NOTIFY dataCb;
2039 /* pSvcCb->mpaParms[0] always contains the context ID. */
2040 int vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[1], &dataCb.uType);
2041 AssertRCReturn(vrc, vrc);
2042 vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[2], &dataCb.uResult);
2043 AssertRCReturn(vrc, vrc);
2044
2045 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2046
2047 LogFlowThisFunc(("ID=%RU32, uType=%RU32, rcGuest=%Rrc\n",
2048 mData.mSession.mID, dataCb.uType, dataCb.uResult));
2049
2050 GuestSessionStatus_T sessionStatus = GuestSessionStatus_Undefined;
2051
2052 int rcGuest = dataCb.uResult; /** @todo uint32_t vs. int. */
2053 switch (dataCb.uType)
2054 {
2055 case GUEST_SESSION_NOTIFYTYPE_ERROR:
2056 sessionStatus = GuestSessionStatus_Error;
2057 LogRel(("Guest Control: Error starting Session '%s' (%Rrc) \n", mData.mSession.mName.c_str(), rcGuest));
2058 break;
2059
2060 case GUEST_SESSION_NOTIFYTYPE_STARTED:
2061 sessionStatus = GuestSessionStatus_Started;
2062#if 0 /** @todo If we get some environment stuff along with this kind notification: */
2063 const char *pszzEnvBlock = ...;
2064 uint32_t cbEnvBlock = ...;
2065 if (!mData.mpBaseEnvironment)
2066 {
2067 GuestEnvironment *pBaseEnv;
2068 try { pBaseEnv = new GuestEnvironment(); } catch (std::bad_alloc &) { pBaseEnv = NULL; }
2069 if (pBaseEnv)
2070 {
2071 int vrc = pBaseEnv->initNormal(Guest.i_isGuestInWindowsNtFamily() ? RTENV_CREATE_F_ALLOW_EQUAL_FIRST_IN_VAR : 0);
2072 if (RT_SUCCESS(vrc))
2073 vrc = pBaseEnv->copyUtf8Block(pszzEnvBlock, cbEnvBlock);
2074 if (RT_SUCCESS(vrc))
2075 mData.mpBaseEnvironment = pBaseEnv;
2076 else
2077 pBaseEnv->release();
2078 }
2079 }
2080#endif
2081 LogRel(("Guest Control: Session '%s' was successfully started\n", mData.mSession.mName.c_str()));
2082 break;
2083
2084 case GUEST_SESSION_NOTIFYTYPE_TEN:
2085 LogRel(("Guest Control: Session '%s' was terminated normally with exit code %#x\n",
2086 mData.mSession.mName.c_str(), dataCb.uResult));
2087 sessionStatus = GuestSessionStatus_Terminated;
2088 break;
2089
2090 case GUEST_SESSION_NOTIFYTYPE_TEA:
2091 LogRel(("Guest Control: Session '%s' was terminated abnormally\n", mData.mSession.mName.c_str()));
2092 sessionStatus = GuestSessionStatus_Terminated;
2093 /* dataCb.uResult is undefined. */
2094 break;
2095
2096 case GUEST_SESSION_NOTIFYTYPE_TES:
2097 LogRel(("Guest Control: Session '%s' was terminated via signal %#x\n", mData.mSession.mName.c_str(), dataCb.uResult));
2098 sessionStatus = GuestSessionStatus_Terminated;
2099 break;
2100
2101 case GUEST_SESSION_NOTIFYTYPE_TOK:
2102 sessionStatus = GuestSessionStatus_TimedOutKilled;
2103 LogRel(("Guest Control: Session '%s' timed out and was killed\n", mData.mSession.mName.c_str()));
2104 break;
2105
2106 case GUEST_SESSION_NOTIFYTYPE_TOA:
2107 sessionStatus = GuestSessionStatus_TimedOutAbnormally;
2108 LogRel(("Guest Control: Session '%s' timed out and was not killed successfully\n", mData.mSession.mName.c_str()));
2109 break;
2110
2111 case GUEST_SESSION_NOTIFYTYPE_DWN:
2112 sessionStatus = GuestSessionStatus_Down;
2113 LogRel(("Guest Control: Session '%s' got killed as guest service/OS is down\n", mData.mSession.mName.c_str()));
2114 break;
2115
2116 case GUEST_SESSION_NOTIFYTYPE_UNDEFINED:
2117 default:
2118 vrc = VERR_NOT_SUPPORTED;
2119 break;
2120 }
2121
2122 /* Leave the lock, as i_setSessionStatus() below will require a write lock for actually
2123 * committing the session state. */
2124 alock.release();
2125
2126 if (RT_SUCCESS(vrc))
2127 {
2128 if (RT_FAILURE(rcGuest))
2129 sessionStatus = GuestSessionStatus_Error;
2130 }
2131
2132 /* Set the session status. */
2133 if (RT_SUCCESS(vrc))
2134 vrc = i_setSessionStatus(sessionStatus, rcGuest);
2135
2136 LogFlowThisFunc(("ID=%RU32, rcGuest=%Rrc\n", mData.mSession.mID, rcGuest));
2137
2138 LogFlowFuncLeaveRC(vrc);
2139 return vrc;
2140}
2141
2142/**
2143 * Returns the path separation style used on the guest.
2144 *
2145 * @returns Separation style used on the guest.
2146 */
2147PathStyle_T GuestSession::i_getGuestPathStyle(void)
2148{
2149 PathStyle_T enmPathStyle;
2150
2151 VBOXOSTYPE enmOsType = mParent->i_getGuestOSType();
2152 if (enmOsType < VBOXOSTYPE_DOS)
2153 {
2154 LogFlowFunc(("returns PathStyle_Unknown\n"));
2155 enmPathStyle = PathStyle_Unknown;
2156 }
2157 else if (enmOsType < VBOXOSTYPE_Linux)
2158 {
2159 LogFlowFunc(("returns PathStyle_DOS\n"));
2160 enmPathStyle = PathStyle_DOS;
2161 }
2162 else
2163 {
2164 LogFlowFunc(("returns PathStyle_UNIX\n"));
2165 enmPathStyle = PathStyle_UNIX;
2166 }
2167
2168 return enmPathStyle;
2169}
2170
2171/**
2172 * Returns the path separation style used on the host.
2173 *
2174 * @returns Separation style used on the host.
2175 */
2176/* static */
2177PathStyle_T GuestSession::i_getHostPathStyle(void)
2178{
2179#if RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS
2180 return PathStyle_DOS;
2181#else
2182 return PathStyle_UNIX;
2183#endif
2184}
2185
2186/**
2187 * Starts the guest session on the guest.
2188 *
2189 * @returns VBox status code.
2190 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
2191 * was returned. Optional.
2192 *
2193 * @note Takes the read and write locks.
2194 */
2195int GuestSession::i_startSession(int *prcGuest)
2196{
2197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2198
2199 LogFlowThisFunc(("mID=%RU32, mName=%s, uProtocolVersion=%RU32, openFlags=%x, openTimeoutMS=%RU32\n",
2200 mData.mSession.mID, mData.mSession.mName.c_str(), mData.mProtocolVersion,
2201 mData.mSession.mOpenFlags, mData.mSession.mOpenTimeoutMS));
2202
2203 /* Guest Additions < 4.3 don't support opening dedicated
2204 guest sessions. Simply return success here. */
2205 if (mData.mProtocolVersion < 2)
2206 {
2207 alock.release(); /* Release lock before changing status. */
2208
2209 /* ignore rc */ i_setSessionStatus(GuestSessionStatus_Started, VINF_SUCCESS);
2210 LogFlowThisFunc(("Installed Guest Additions don't support opening dedicated sessions, skipping\n"));
2211 return VINF_SUCCESS;
2212 }
2213
2214 if (mData.mStatus != GuestSessionStatus_Undefined)
2215 return VINF_SUCCESS;
2216
2217 /** @todo mData.mSession.uFlags validation. */
2218
2219 alock.release(); /* Release lock before changing status. */
2220
2221 /* Set current session status. */
2222 int vrc = i_setSessionStatus(GuestSessionStatus_Starting, VINF_SUCCESS);
2223 if (RT_FAILURE(vrc))
2224 return vrc;
2225
2226 GuestWaitEvent *pEvent = NULL;
2227 GuestEventTypes eventTypes;
2228 try
2229 {
2230 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
2231
2232 vrc = registerWaitEventEx(mData.mSession.mID, mData.mObjectID, eventTypes, &pEvent);
2233 }
2234 catch (std::bad_alloc &)
2235 {
2236 vrc = VERR_NO_MEMORY;
2237 }
2238
2239 if (RT_FAILURE(vrc))
2240 return vrc;
2241
2242 alock.acquire(); /* Re-acquire lock before accessing session attributes below. */
2243
2244 VBOXHGCMSVCPARM paParms[8];
2245
2246 int i = 0;
2247 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2248 HGCMSvcSetU32(&paParms[i++], mData.mProtocolVersion);
2249 HGCMSvcSetPv(&paParms[i++], (void*)mData.mCredentials.mUser.c_str(),
2250 (ULONG)mData.mCredentials.mUser.length() + 1);
2251 HGCMSvcSetPv(&paParms[i++], (void*)mData.mCredentials.mPassword.c_str(),
2252 (ULONG)mData.mCredentials.mPassword.length() + 1);
2253 HGCMSvcSetPv(&paParms[i++], (void*)mData.mCredentials.mDomain.c_str(),
2254 (ULONG)mData.mCredentials.mDomain.length() + 1);
2255 HGCMSvcSetU32(&paParms[i++], mData.mSession.mOpenFlags);
2256
2257 alock.release(); /* Drop lock before sending. */
2258
2259 vrc = i_sendMessage(HOST_MSG_SESSION_CREATE, i, paParms, VBOX_GUESTCTRL_DST_ROOT_SVC);
2260 if (RT_SUCCESS(vrc))
2261 {
2262 vrc = i_waitForStatusChange(pEvent, GuestSessionWaitForFlag_Start,
2263 30 * 1000 /* 30s timeout */,
2264 NULL /* Session status */, prcGuest);
2265 }
2266 else
2267 {
2268 /*
2269 * Unable to start guest session - update its current state.
2270 * Since there is no (official API) way to recover a failed guest session
2271 * this also marks the end state. Internally just calling this
2272 * same function again will work though.
2273 */
2274 /* ignore rc */ i_setSessionStatus(GuestSessionStatus_Error, vrc);
2275 }
2276
2277 unregisterWaitEvent(pEvent);
2278
2279 LogFlowFuncLeaveRC(vrc);
2280 return vrc;
2281}
2282
2283/**
2284 * Starts the guest session asynchronously in a separate worker thread.
2285 *
2286 * @returns IPRT status code.
2287 */
2288int GuestSession::i_startSessionAsync(void)
2289{
2290 LogFlowThisFuncEnter();
2291
2292 /* Create task: */
2293 GuestSessionTaskInternalStart *pTask = NULL;
2294 try
2295 {
2296 pTask = new GuestSessionTaskInternalStart(this);
2297 }
2298 catch (std::bad_alloc &)
2299 {
2300 return VERR_NO_MEMORY;
2301 }
2302 if (pTask->isOk())
2303 {
2304 /* Kick off the thread: */
2305 HRESULT hrc = pTask->createThread();
2306 pTask = NULL; /* Not valid anymore, not even on failure! */
2307 if (SUCCEEDED(hrc))
2308 {
2309 LogFlowFuncLeaveRC(VINF_SUCCESS);
2310 return VINF_SUCCESS;
2311 }
2312 LogFlow(("GuestSession: Failed to create thread for GuestSessionTaskInternalOpen task.\n"));
2313 }
2314 else
2315 LogFlow(("GuestSession: GuestSessionTaskInternalStart creation failed: %Rhrc.\n", pTask->rc()));
2316 LogFlowFuncLeaveRC(VERR_GENERAL_FAILURE);
2317 return VERR_GENERAL_FAILURE;
2318}
2319
2320/**
2321 * Static function to start a guest session asynchronously.
2322 *
2323 * @returns IPRT status code.
2324 * @param pTask Task object to use for starting the guest session.
2325 */
2326/* static */
2327int GuestSession::i_startSessionThreadTask(GuestSessionTaskInternalStart *pTask)
2328{
2329 LogFlowFunc(("pTask=%p\n", pTask));
2330 AssertPtr(pTask);
2331
2332 const ComObjPtr<GuestSession> pSession(pTask->Session());
2333 Assert(!pSession.isNull());
2334
2335 AutoCaller autoCaller(pSession);
2336 if (FAILED(autoCaller.rc()))
2337 return VERR_COM_INVALID_OBJECT_STATE;
2338
2339 int vrc = pSession->i_startSession(NULL /* Guest rc, ignored */);
2340 /* Nothing to do here anymore. */
2341
2342 LogFlowFuncLeaveRC(vrc);
2343 return vrc;
2344}
2345
2346/**
2347 * Registers an object with the session, i.e. allocates an object ID.
2348 *
2349 * @return VBox status code.
2350 * @retval VERR_GSTCTL_MAX_OBJECTS_REACHED if the maximum of concurrent objects
2351 * is reached.
2352 * @param pObject Guest object to register (weak pointer). Optional.
2353 * @param enmType Session object type to register.
2354 * @param pidObject Where to return the object ID on success. Optional.
2355 */
2356int GuestSession::i_objectRegister(GuestObject *pObject, SESSIONOBJECTTYPE enmType, uint32_t *pidObject)
2357{
2358 /* pObject can be NULL. */
2359 /* pidObject is optional. */
2360
2361 /*
2362 * Pick a random bit as starting point. If it's in use, search forward
2363 * for a free one, wrapping around. We've reserved both the zero'th and
2364 * max-1 IDs (see Data constructor).
2365 */
2366 uint32_t idObject = RTRandU32Ex(1, VBOX_GUESTCTRL_MAX_OBJECTS - 2);
2367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2368 if (!ASMBitTestAndSet(&mData.bmObjectIds[0], idObject))
2369 { /* likely */ }
2370 else if (mData.mObjects.size() < VBOX_GUESTCTRL_MAX_OBJECTS - 2 /* First and last are not used */)
2371 {
2372 /* Forward search. */
2373 int iHit = ASMBitNextClear(&mData.bmObjectIds[0], VBOX_GUESTCTRL_MAX_OBJECTS, idObject);
2374 if (iHit < 0)
2375 iHit = ASMBitFirstClear(&mData.bmObjectIds[0], VBOX_GUESTCTRL_MAX_OBJECTS);
2376 AssertLogRelMsgReturn(iHit >= 0, ("object count: %#zu\n", mData.mObjects.size()), VERR_GSTCTL_MAX_CID_OBJECTS_REACHED);
2377 idObject = iHit;
2378 AssertLogRelMsgReturn(!ASMBitTestAndSet(&mData.bmObjectIds[0], idObject), ("idObject=%#x\n", idObject), VERR_INTERNAL_ERROR_2);
2379 }
2380 else
2381 {
2382 LogFunc(("Maximum number of objects reached (enmType=%RU32, %zu objects)\n", enmType, mData.mObjects.size()));
2383 return VERR_GSTCTL_MAX_CID_OBJECTS_REACHED;
2384 }
2385
2386 Log2Func(("enmType=%RU32 -> idObject=%RU32 (%zu objects)\n", enmType, idObject, mData.mObjects.size()));
2387
2388 try
2389 {
2390 mData.mObjects[idObject].pObject = pObject; /* Can be NULL. */
2391 mData.mObjects[idObject].enmType = enmType;
2392 mData.mObjects[idObject].msBirth = RTTimeMilliTS();
2393 }
2394 catch (std::bad_alloc &)
2395 {
2396 ASMBitClear(&mData.bmObjectIds[0], idObject);
2397 return VERR_NO_MEMORY;
2398 }
2399
2400 if (pidObject)
2401 *pidObject = idObject;
2402
2403 return VINF_SUCCESS;
2404}
2405
2406/**
2407 * Unregisters an object from the session objects list.
2408 *
2409 * @retval VINF_SUCCESS on success.
2410 * @retval VERR_NOT_FOUND if the object ID was not found.
2411 * @param idObject Object ID to unregister.
2412 *
2413 * @note Takes the write lock.
2414 */
2415int GuestSession::i_objectUnregister(uint32_t idObject)
2416{
2417 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2418
2419 int rc = VINF_SUCCESS;
2420 AssertMsgStmt(ASMBitTestAndClear(&mData.bmObjectIds, idObject), ("idObject=%#x\n", idObject), rc = VERR_NOT_FOUND);
2421
2422 SessionObjects::iterator ItObj = mData.mObjects.find(idObject);
2423 AssertMsgReturn(ItObj != mData.mObjects.end(), ("idObject=%#x\n", idObject), VERR_NOT_FOUND);
2424 mData.mObjects.erase(ItObj);
2425
2426 return rc;
2427}
2428
2429/**
2430 * Unregisters all objects from the session list.
2431 *
2432 * @returns VBox status code.
2433 *
2434 * @note Takes the write lock.
2435 */
2436int GuestSession::i_objectsUnregister(void)
2437{
2438 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2439
2440 LogFlowThisFunc(("Unregistering directories (%zu total)\n", mData.mDirectories.size()));
2441
2442 SessionDirectories::iterator itDirs;
2443 while ((itDirs = mData.mDirectories.begin()) != mData.mDirectories.end())
2444 {
2445 alock.release();
2446 i_directoryUnregister(itDirs->second);
2447 alock.acquire();
2448 }
2449
2450 Assert(mData.mDirectories.size() == 0);
2451 mData.mDirectories.clear();
2452
2453 LogFlowThisFunc(("Unregistering files (%zu total)\n", mData.mFiles.size()));
2454
2455 SessionFiles::iterator itFiles;
2456 while ((itFiles = mData.mFiles.begin()) != mData.mFiles.end())
2457 {
2458 alock.release();
2459 i_fileUnregister(itFiles->second);
2460 alock.acquire();
2461 }
2462
2463 Assert(mData.mFiles.size() == 0);
2464 mData.mFiles.clear();
2465
2466 LogFlowThisFunc(("Unregistering processes (%zu total)\n", mData.mProcesses.size()));
2467
2468 SessionProcesses::iterator itProcs;
2469 while ((itProcs = mData.mProcesses.begin()) != mData.mProcesses.end())
2470 {
2471 alock.release();
2472 i_processUnregister(itProcs->second);
2473 alock.acquire();
2474 }
2475
2476 Assert(mData.mProcesses.size() == 0);
2477 mData.mProcesses.clear();
2478
2479 return VINF_SUCCESS;
2480}
2481
2482/**
2483 * Notifies all registered objects about a guest session status change.
2484 *
2485 * @returns VBox status code.
2486 * @param enmSessionStatus Session status to notify objects about.
2487 */
2488int GuestSession::i_objectsNotifyAboutStatusChange(GuestSessionStatus_T enmSessionStatus)
2489{
2490 LogFlowThisFunc(("enmSessionStatus=%RU32\n", enmSessionStatus));
2491
2492 int vrc = VINF_SUCCESS;
2493
2494 SessionObjects::iterator itObjs = mData.mObjects.begin();
2495 while (itObjs != mData.mObjects.end())
2496 {
2497 GuestObject *pObj = itObjs->second.pObject;
2498 if (pObj) /* pObject can be NULL (weak pointer). */
2499 {
2500 int vrc2 = pObj->i_onSessionStatusChange(enmSessionStatus);
2501 if (RT_SUCCESS(vrc))
2502 vrc = vrc2;
2503
2504 /* If the session got terminated, make sure to cancel all wait events for
2505 * the current object. */
2506 if (i_isTerminated())
2507 pObj->cancelWaitEvents();
2508 }
2509
2510 ++itObjs;
2511 }
2512
2513 LogFlowFuncLeaveRC(vrc);
2514 return vrc;
2515}
2516
2517/**
2518 * Renames a path on the guest.
2519 *
2520 * @returns VBox status code.
2521 * @returns VERR_GSTCTL_GUEST_ERROR on received guest error.
2522 * @param strSource Source path on guest to rename.
2523 * @param strDest Destination path on guest to rename \a strSource to.
2524 * @param uFlags Renaming flags.
2525 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
2526 * was returned. Optional.
2527 * @note Takes the read lock.
2528 */
2529int GuestSession::i_pathRename(const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags, int *prcGuest)
2530{
2531 AssertReturn(!(uFlags & ~PATHRENAME_FLAG_VALID_MASK), VERR_INVALID_PARAMETER);
2532
2533 LogFlowThisFunc(("strSource=%s, strDest=%s, uFlags=0x%x\n",
2534 strSource.c_str(), strDest.c_str(), uFlags));
2535
2536 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2537
2538 GuestWaitEvent *pEvent = NULL;
2539 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2540 if (RT_FAILURE(vrc))
2541 return vrc;
2542
2543 /* Prepare HGCM call. */
2544 VBOXHGCMSVCPARM paParms[8];
2545 int i = 0;
2546 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2547 HGCMSvcSetPv(&paParms[i++], (void*)strSource.c_str(),
2548 (ULONG)strSource.length() + 1);
2549 HGCMSvcSetPv(&paParms[i++], (void*)strDest.c_str(),
2550 (ULONG)strDest.length() + 1);
2551 HGCMSvcSetU32(&paParms[i++], uFlags);
2552
2553 alock.release(); /* Drop lock before sending. */
2554
2555 vrc = i_sendMessage(HOST_MSG_PATH_RENAME, i, paParms);
2556 if (RT_SUCCESS(vrc))
2557 {
2558 vrc = pEvent->Wait(30 * 1000);
2559 if ( vrc == VERR_GSTCTL_GUEST_ERROR
2560 && prcGuest)
2561 *prcGuest = pEvent->GuestResult();
2562 }
2563
2564 unregisterWaitEvent(pEvent);
2565
2566 LogFlowFuncLeaveRC(vrc);
2567 return vrc;
2568}
2569
2570/**
2571 * Returns the user's absolute documents path, if any.
2572 *
2573 * @returns VBox status code.
2574 * @param strPath Where to store the user's document path.
2575 * @param prcGuest Guest rc, when returning VERR_GSTCTL_GUEST_ERROR.
2576 * Any other return code indicates some host side error.
2577 *
2578 * @note Takes the read lock.
2579 */
2580int GuestSession::i_pathUserDocuments(Utf8Str &strPath, int *prcGuest)
2581{
2582 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2583
2584 /** @todo Cache the user's document path? */
2585
2586 GuestWaitEvent *pEvent = NULL;
2587 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2588 if (RT_FAILURE(vrc))
2589 return vrc;
2590
2591 /* Prepare HGCM call. */
2592 VBOXHGCMSVCPARM paParms[2];
2593 int i = 0;
2594 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2595
2596 alock.release(); /* Drop lock before sending. */
2597
2598 vrc = i_sendMessage(HOST_MSG_PATH_USER_DOCUMENTS, i, paParms);
2599 if (RT_SUCCESS(vrc))
2600 {
2601 vrc = pEvent->Wait(30 * 1000);
2602 if (RT_SUCCESS(vrc))
2603 {
2604 strPath = pEvent->Payload().ToString();
2605 }
2606 else
2607 {
2608 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2609 {
2610 if (prcGuest)
2611 *prcGuest = pEvent->GuestResult();
2612 }
2613 }
2614 }
2615
2616 unregisterWaitEvent(pEvent);
2617
2618 LogFlowFuncLeaveRC(vrc);
2619 return vrc;
2620}
2621
2622/**
2623 * Returns the user's absolute home path, if any.
2624 *
2625 * @returns VBox status code.
2626 * @param strPath Where to store the user's home path.
2627 * @param prcGuest Guest rc, when returning VERR_GSTCTL_GUEST_ERROR.
2628 * Any other return code indicates some host side error.
2629 *
2630 * @note Takes the read lock.
2631 */
2632int GuestSession::i_pathUserHome(Utf8Str &strPath, int *prcGuest)
2633{
2634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2635
2636 /** @todo Cache the user's home path? */
2637
2638 GuestWaitEvent *pEvent = NULL;
2639 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2640 if (RT_FAILURE(vrc))
2641 return vrc;
2642
2643 /* Prepare HGCM call. */
2644 VBOXHGCMSVCPARM paParms[2];
2645 int i = 0;
2646 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2647
2648 alock.release(); /* Drop lock before sending. */
2649
2650 vrc = i_sendMessage(HOST_MSG_PATH_USER_HOME, i, paParms);
2651 if (RT_SUCCESS(vrc))
2652 {
2653 vrc = pEvent->Wait(30 * 1000);
2654 if (RT_SUCCESS(vrc))
2655 {
2656 strPath = pEvent->Payload().ToString();
2657 }
2658 else
2659 {
2660 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2661 {
2662 if (prcGuest)
2663 *prcGuest = pEvent->GuestResult();
2664 }
2665 }
2666 }
2667
2668 unregisterWaitEvent(pEvent);
2669
2670 LogFlowFuncLeaveRC(vrc);
2671 return vrc;
2672}
2673
2674/**
2675 * Unregisters a process object from a guest session.
2676 *
2677 * @returns VBox status code. VERR_NOT_FOUND if the process is not registered (anymore).
2678 * @param pProcess Process object to unregister from session.
2679 *
2680 * @note Takes the write lock.
2681 */
2682int GuestSession::i_processUnregister(GuestProcess *pProcess)
2683{
2684 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
2685
2686 LogFlowThisFunc(("pProcess=%p\n", pProcess));
2687
2688 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2689
2690 const uint32_t idObject = pProcess->getObjectID();
2691
2692 LogFlowFunc(("Removing process (objectID=%RU32) ...\n", idObject));
2693
2694 int rc = i_objectUnregister(idObject);
2695 if (RT_FAILURE(rc))
2696 return rc;
2697
2698 SessionProcesses::iterator itProcs = mData.mProcesses.find(idObject);
2699 AssertReturn(itProcs != mData.mProcesses.end(), VERR_NOT_FOUND);
2700
2701 /* Make sure to consume the pointer before the one of the iterator gets released. */
2702 ComObjPtr<GuestProcess> pProc = pProcess;
2703
2704 ULONG uPID;
2705 HRESULT hr = pProc->COMGETTER(PID)(&uPID);
2706 ComAssertComRC(hr);
2707
2708 LogFlowFunc(("Removing process ID=%RU32 (session %RU32, guest PID %RU32, now total %zu processes)\n",
2709 idObject, mData.mSession.mID, uPID, mData.mProcesses.size()));
2710
2711 rc = pProcess->i_onUnregister();
2712 AssertRCReturn(rc, rc);
2713
2714 mData.mProcesses.erase(itProcs);
2715
2716 alock.release(); /* Release lock before firing off event. */
2717
2718 ::FireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProc, uPID, false /* Process unregistered */);
2719
2720 pProc.setNull();
2721
2722 LogFlowFuncLeaveRC(rc);
2723 return rc;
2724}
2725
2726/**
2727 * Creates but does *not* start the process yet.
2728 *
2729 * See GuestProcess::startProcess() or GuestProcess::startProcessAsync() for
2730 * starting the process.
2731 *
2732 * @returns IPRT status code.
2733 * @param procInfo Process startup info to use for starting the process.
2734 * @param pProcess Where to return the created guest process object on success.
2735 *
2736 * @note Takes the write lock.
2737 */
2738int GuestSession::i_processCreateEx(GuestProcessStartupInfo &procInfo, ComObjPtr<GuestProcess> &pProcess)
2739{
2740 LogFlowFunc(("mExe=%s, mFlags=%x, mTimeoutMS=%RU32\n",
2741 procInfo.mExecutable.c_str(), procInfo.mFlags, procInfo.mTimeoutMS));
2742#ifdef DEBUG
2743 if (procInfo.mArguments.size())
2744 {
2745 LogFlowFunc(("Arguments:"));
2746 ProcessArguments::const_iterator it = procInfo.mArguments.begin();
2747 while (it != procInfo.mArguments.end())
2748 {
2749 LogFlow((" %s", (*it).c_str()));
2750 ++it;
2751 }
2752 LogFlow(("\n"));
2753 }
2754#endif
2755
2756 /* Validate flags. */
2757 if (procInfo.mFlags)
2758 {
2759 if ( !(procInfo.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
2760 && !(procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
2761 && !(procInfo.mFlags & ProcessCreateFlag_Hidden)
2762 && !(procInfo.mFlags & ProcessCreateFlag_Profile)
2763 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
2764 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdErr))
2765 {
2766 return VERR_INVALID_PARAMETER;
2767 }
2768 }
2769
2770 if ( (procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
2771 && ( (procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
2772 || (procInfo.mFlags & ProcessCreateFlag_WaitForStdErr)
2773 )
2774 )
2775 {
2776 return VERR_INVALID_PARAMETER;
2777 }
2778
2779 if (procInfo.mPriority)
2780 {
2781 if (!(procInfo.mPriority & ProcessPriority_Default))
2782 return VERR_INVALID_PARAMETER;
2783 }
2784
2785 /* Adjust timeout.
2786 * If set to 0, we define an infinite timeout (unlimited process run time). */
2787 if (procInfo.mTimeoutMS == 0)
2788 procInfo.mTimeoutMS = UINT32_MAX;
2789
2790 /** @todo Implement process priority + affinity. */
2791
2792 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2793
2794 /* Create the process object. */
2795 HRESULT hr = pProcess.createObject();
2796 if (FAILED(hr))
2797 return VERR_COM_UNEXPECTED;
2798
2799 /* Register a new object ID. */
2800 uint32_t idObject;
2801 int rc = i_objectRegister(pProcess, SESSIONOBJECTTYPE_PROCESS, &idObject);
2802 if (RT_FAILURE(rc))
2803 {
2804 pProcess.setNull();
2805 return rc;
2806 }
2807
2808 rc = pProcess->init(mParent->i_getConsole() /* Console */, this /* Session */, idObject,
2809 procInfo, mData.mpBaseEnvironment);
2810 if (RT_FAILURE(rc))
2811 return rc;
2812
2813 /* Add the created process to our map. */
2814 try
2815 {
2816 mData.mProcesses[idObject] = pProcess;
2817
2818 LogFlowFunc(("Added new process (Session: %RU32) with process ID=%RU32 (now total %zu processes)\n",
2819 mData.mSession.mID, idObject, mData.mProcesses.size()));
2820
2821 alock.release(); /* Release lock before firing off event. */
2822
2823 ::FireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProcess, 0 /* PID */, true /* Process registered */);
2824 }
2825 catch (std::bad_alloc &)
2826 {
2827 rc = VERR_NO_MEMORY;
2828 }
2829
2830 return rc;
2831}
2832
2833/**
2834 * Checks if a process object exists and optionally returns its object.
2835 *
2836 * @returns \c true if process object exists, or \c false if not.
2837 * @param uProcessID ID of process object to check.
2838 * @param pProcess Where to return the found process object on success.
2839 *
2840 * @note No locking done!
2841 */
2842inline bool GuestSession::i_processExists(uint32_t uProcessID, ComObjPtr<GuestProcess> *pProcess)
2843{
2844 SessionProcesses::const_iterator it = mData.mProcesses.find(uProcessID);
2845 if (it != mData.mProcesses.end())
2846 {
2847 if (pProcess)
2848 *pProcess = it->second;
2849 return true;
2850 }
2851 return false;
2852}
2853
2854/**
2855 * Returns the process object from a guest PID.
2856 *
2857 * @returns VBox status code.
2858 * @param uPID Guest PID to get process object for.
2859 * @param pProcess Where to return the process object on success.
2860 *
2861 * @note No locking done!
2862 */
2863inline int GuestSession::i_processGetByPID(ULONG uPID, ComObjPtr<GuestProcess> *pProcess)
2864{
2865 AssertReturn(uPID, false);
2866 /* pProcess is optional. */
2867
2868 SessionProcesses::iterator itProcs = mData.mProcesses.begin();
2869 for (; itProcs != mData.mProcesses.end(); ++itProcs)
2870 {
2871 ComObjPtr<GuestProcess> pCurProc = itProcs->second;
2872 AutoCaller procCaller(pCurProc);
2873 if (!procCaller.isOk())
2874 return VERR_COM_INVALID_OBJECT_STATE;
2875
2876 ULONG uCurPID;
2877 HRESULT hr = pCurProc->COMGETTER(PID)(&uCurPID);
2878 ComAssertComRC(hr);
2879
2880 if (uCurPID == uPID)
2881 {
2882 if (pProcess)
2883 *pProcess = pCurProc;
2884 return VINF_SUCCESS;
2885 }
2886 }
2887
2888 return VERR_NOT_FOUND;
2889}
2890
2891/**
2892 * Sends a message to the HGCM host service.
2893 *
2894 * @returns VBox status code.
2895 * @param uMessage Message ID to send.
2896 * @param uParms Number of parameters in \a paParms to send.
2897 * @param paParms Array of HGCM parameters to send.
2898 * @param fDst Host message destination flags of type VBOX_GUESTCTRL_DST_XXX.
2899 */
2900int GuestSession::i_sendMessage(uint32_t uMessage, uint32_t uParms, PVBOXHGCMSVCPARM paParms,
2901 uint64_t fDst /*= VBOX_GUESTCTRL_DST_SESSION*/)
2902{
2903 LogFlowThisFuncEnter();
2904
2905#ifndef VBOX_GUESTCTRL_TEST_CASE
2906 ComObjPtr<Console> pConsole = mParent->i_getConsole();
2907 Assert(!pConsole.isNull());
2908
2909 /* Forward the information to the VMM device. */
2910 VMMDev *pVMMDev = pConsole->i_getVMMDev();
2911 AssertPtr(pVMMDev);
2912
2913 LogFlowThisFunc(("uMessage=%RU32 (%s), uParms=%RU32\n", uMessage, GstCtrlHostMsgtoStr((guestControl::eHostMsg)uMessage), uParms));
2914
2915 /* HACK ALERT! We extend the first parameter to 64-bit and use the
2916 two topmost bits for call destination information. */
2917 Assert(fDst == VBOX_GUESTCTRL_DST_SESSION || fDst == VBOX_GUESTCTRL_DST_ROOT_SVC || fDst == VBOX_GUESTCTRL_DST_BOTH);
2918 Assert(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT);
2919 paParms[0].type = VBOX_HGCM_SVC_PARM_64BIT;
2920 paParms[0].u.uint64 = (uint64_t)paParms[0].u.uint32 | fDst;
2921
2922 /* Make the call. */
2923 int vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uMessage, uParms, paParms);
2924 if (RT_FAILURE(vrc))
2925 {
2926 /** @todo What to do here? */
2927 }
2928#else
2929 /* Not needed within testcases. */
2930 int vrc = VINF_SUCCESS;
2931#endif
2932 LogFlowFuncLeaveRC(vrc);
2933 return vrc;
2934}
2935
2936/**
2937 * Sets the guest session's current status.
2938 *
2939 * @returns VBox status code.
2940 * @param sessionStatus Session status to set.
2941 * @param sessionRc Session result to set (for error handling).
2942 *
2943 * @note Takes the write lock.
2944 */
2945int GuestSession::i_setSessionStatus(GuestSessionStatus_T sessionStatus, int sessionRc)
2946{
2947 LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, sessionRc=%Rrc\n",
2948 mData.mStatus, sessionStatus, sessionRc));
2949
2950 if (sessionStatus == GuestSessionStatus_Error)
2951 {
2952 AssertMsg(RT_FAILURE(sessionRc), ("Guest rc must be an error (%Rrc)\n", sessionRc));
2953 /* Do not allow overwriting an already set error. If this happens
2954 * this means we forgot some error checking/locking somewhere. */
2955 AssertMsg(RT_SUCCESS(mData.mRC), ("Guest rc already set (to %Rrc)\n", mData.mRC));
2956 }
2957 else
2958 AssertMsg(RT_SUCCESS(sessionRc), ("Guest rc must not be an error (%Rrc)\n", sessionRc));
2959
2960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2961
2962 int vrc = VINF_SUCCESS;
2963
2964 if (mData.mStatus != sessionStatus)
2965 {
2966 mData.mStatus = sessionStatus;
2967 mData.mRC = sessionRc;
2968
2969 /* Make sure to notify all underlying objects first. */
2970 vrc = i_objectsNotifyAboutStatusChange(sessionStatus);
2971
2972 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
2973 HRESULT hr = errorInfo.createObject();
2974 ComAssertComRC(hr);
2975 int rc2 = errorInfo->initEx(VBOX_E_IPRT_ERROR, sessionRc,
2976 COM_IIDOF(IGuestSession), getComponentName(),
2977 i_guestErrorToString(sessionRc));
2978 AssertRC(rc2);
2979
2980 alock.release(); /* Release lock before firing off event. */
2981
2982 ::FireGuestSessionStateChangedEvent(mEventSource, this, mData.mSession.mID, sessionStatus, errorInfo);
2983 }
2984
2985 LogFlowFuncLeaveRC(vrc);
2986 return vrc;
2987}
2988
2989/** @todo Unused --remove? */
2990int GuestSession::i_signalWaiters(GuestSessionWaitResult_T enmWaitResult, int rc /*= VINF_SUCCESS */)
2991{
2992 RT_NOREF(enmWaitResult, rc);
2993
2994 /*LogFlowThisFunc(("enmWaitResult=%d, rc=%Rrc, mWaitCount=%RU32, mWaitEvent=%p\n",
2995 enmWaitResult, rc, mData.mWaitCount, mData.mWaitEvent));*/
2996
2997 /* Note: No write locking here -- already done in the caller. */
2998
2999 int vrc = VINF_SUCCESS;
3000 /*if (mData.mWaitEvent)
3001 vrc = mData.mWaitEvent->Signal(enmWaitResult, rc);*/
3002 LogFlowFuncLeaveRC(vrc);
3003 return vrc;
3004}
3005
3006/**
3007 * Shuts down (and optionally powers off / reboots) the guest.
3008 * Needs supported Guest Additions installed.
3009 *
3010 * @returns VBox status code. VERR_NOT_SUPPORTED if not supported by Guest Additions.
3011 * @param fFlags Guest shutdown flags.
3012 * @param prcGuest Guest rc, when returning VERR_GSTCTL_GUEST_ERROR.
3013 * Any other return code indicates some host side error.
3014 *
3015 * @note Takes the read lock.
3016 */
3017int GuestSession::i_shutdown(uint32_t fFlags, int *prcGuest)
3018{
3019 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3020
3021 AssertPtrReturn(mParent, VERR_INVALID_POINTER);
3022 if (!(mParent->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_SHUTDOWN))
3023 return VERR_NOT_SUPPORTED;
3024
3025 LogRel(("Guest Control: Shutting down guest (flags = %#x) ...\n", fFlags));
3026
3027 GuestWaitEvent *pEvent = NULL;
3028 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
3029 if (RT_FAILURE(vrc))
3030 return vrc;
3031
3032 /* Prepare HGCM call. */
3033 VBOXHGCMSVCPARM paParms[2];
3034 int i = 0;
3035 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
3036 HGCMSvcSetU32(&paParms[i++], fFlags);
3037
3038 alock.release(); /* Drop lock before sending. */
3039
3040 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3041
3042 vrc = i_sendMessage(HOST_MSG_SHUTDOWN, i, paParms);
3043 if (RT_SUCCESS(vrc))
3044 {
3045 vrc = pEvent->Wait(30 * 1000);
3046 if (RT_FAILURE(vrc))
3047 {
3048 if (vrc == VERR_GSTCTL_GUEST_ERROR)
3049 rcGuest = pEvent->GuestResult();
3050 }
3051 }
3052
3053 if (RT_FAILURE(vrc))
3054 {
3055 LogRel(("Guest Control: Shutting down guest failed, rc=%Rrc\n",
3056 vrc == VERR_GSTCTL_GUEST_ERROR ? rcGuest : vrc));
3057
3058 if ( vrc == VERR_GSTCTL_GUEST_ERROR
3059 && prcGuest)
3060 *prcGuest = rcGuest;
3061 }
3062
3063 unregisterWaitEvent(pEvent);
3064
3065 LogFlowFuncLeaveRC(vrc);
3066 return vrc;
3067}
3068
3069/**
3070 * Determines the protocol version (sets mData.mProtocolVersion).
3071 *
3072 * This is called from the init method prior to to establishing a guest
3073 * session.
3074 *
3075 * @returns VBox status code.
3076 */
3077int GuestSession::i_determineProtocolVersion(void)
3078{
3079 /*
3080 * We currently do this based on the reported Guest Additions version,
3081 * ASSUMING that VBoxService and VBoxDrv are at the same version.
3082 */
3083 ComObjPtr<Guest> pGuest = mParent;
3084 AssertReturn(!pGuest.isNull(), VERR_NOT_SUPPORTED);
3085 uint32_t uGaVersion = pGuest->i_getAdditionsVersion();
3086
3087 /* Everyone supports version one, if they support anything at all. */
3088 mData.mProtocolVersion = 1;
3089
3090 /* Guest control 2.0 was introduced with 4.3.0. */
3091 if (uGaVersion >= VBOX_FULL_VERSION_MAKE(4,3,0))
3092 mData.mProtocolVersion = 2; /* Guest control 2.0. */
3093
3094 LogFlowThisFunc(("uGaVersion=%u.%u.%u => mProtocolVersion=%u\n",
3095 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
3096 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
3097
3098 /*
3099 * Inform the user about outdated Guest Additions (VM release log).
3100 */
3101 if (mData.mProtocolVersion < 2)
3102 LogRelMax(3, ("Warning: Guest Additions v%u.%u.%u only supports the older guest control protocol version %u.\n"
3103 " Please upgrade GAs to the current version to get full guest control capabilities.\n",
3104 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
3105 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
3106
3107 return VINF_SUCCESS;
3108}
3109
3110/**
3111 * Waits for guest session events.
3112 *
3113 * @returns VBox status code.
3114 * @retval VERR_GSTCTL_GUEST_ERROR on received guest error.
3115 * @retval VERR_TIMEOUT when a timeout has occurred.
3116 * @param fWaitFlags Wait flags to use.
3117 * @param uTimeoutMS Timeout (in ms) to wait.
3118 * @param waitResult Where to return the wait result on success.
3119 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
3120 * was returned. Optional.
3121 *
3122 * @note Takes the read lock.
3123 */
3124int GuestSession::i_waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, GuestSessionWaitResult_T &waitResult, int *prcGuest)
3125{
3126 LogFlowThisFuncEnter();
3127
3128 AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
3129
3130 /*LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p, prcGuest=%p\n",
3131 fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent, prcGuest));*/
3132
3133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3134
3135 /* Did some error occur before? Then skip waiting and return. */
3136 if (mData.mStatus == GuestSessionStatus_Error)
3137 {
3138 waitResult = GuestSessionWaitResult_Error;
3139 AssertMsg(RT_FAILURE(mData.mRC), ("No error rc (%Rrc) set when guest session indicated an error\n", mData.mRC));
3140 if (prcGuest)
3141 *prcGuest = mData.mRC; /* Return last set error. */
3142 return VERR_GSTCTL_GUEST_ERROR;
3143 }
3144
3145 /* Guest Additions < 4.3 don't support session handling, skip. */
3146 if (mData.mProtocolVersion < 2)
3147 {
3148 waitResult = GuestSessionWaitResult_WaitFlagNotSupported;
3149
3150 LogFlowThisFunc(("Installed Guest Additions don't support waiting for dedicated sessions, skipping\n"));
3151 return VINF_SUCCESS;
3152 }
3153
3154 waitResult = GuestSessionWaitResult_None;
3155 if (fWaitFlags & GuestSessionWaitForFlag_Terminate)
3156 {
3157 switch (mData.mStatus)
3158 {
3159 case GuestSessionStatus_Terminated:
3160 case GuestSessionStatus_Down:
3161 waitResult = GuestSessionWaitResult_Terminate;
3162 break;
3163
3164 case GuestSessionStatus_TimedOutKilled:
3165 case GuestSessionStatus_TimedOutAbnormally:
3166 waitResult = GuestSessionWaitResult_Timeout;
3167 break;
3168
3169 case GuestSessionStatus_Error:
3170 /* Handled above. */
3171 break;
3172
3173 case GuestSessionStatus_Started:
3174 waitResult = GuestSessionWaitResult_Start;
3175 break;
3176
3177 case GuestSessionStatus_Undefined:
3178 case GuestSessionStatus_Starting:
3179 /* Do the waiting below. */
3180 break;
3181
3182 default:
3183 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
3184 return VERR_NOT_IMPLEMENTED;
3185 }
3186 }
3187 else if (fWaitFlags & GuestSessionWaitForFlag_Start)
3188 {
3189 switch (mData.mStatus)
3190 {
3191 case GuestSessionStatus_Started:
3192 case GuestSessionStatus_Terminating:
3193 case GuestSessionStatus_Terminated:
3194 case GuestSessionStatus_Down:
3195 waitResult = GuestSessionWaitResult_Start;
3196 break;
3197
3198 case GuestSessionStatus_Error:
3199 waitResult = GuestSessionWaitResult_Error;
3200 break;
3201
3202 case GuestSessionStatus_TimedOutKilled:
3203 case GuestSessionStatus_TimedOutAbnormally:
3204 waitResult = GuestSessionWaitResult_Timeout;
3205 break;
3206
3207 case GuestSessionStatus_Undefined:
3208 case GuestSessionStatus_Starting:
3209 /* Do the waiting below. */
3210 break;
3211
3212 default:
3213 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
3214 return VERR_NOT_IMPLEMENTED;
3215 }
3216 }
3217
3218 LogFlowThisFunc(("sessionStatus=%RU32, sessionRc=%Rrc, waitResult=%RU32\n",
3219 mData.mStatus, mData.mRC, waitResult));
3220
3221 /* No waiting needed? Return immediately using the last set error. */
3222 if (waitResult != GuestSessionWaitResult_None)
3223 {
3224 if (prcGuest)
3225 *prcGuest = mData.mRC; /* Return last set error (if any). */
3226 return RT_SUCCESS(mData.mRC) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
3227 }
3228
3229 int vrc = VINF_SUCCESS;
3230
3231 uint64_t const tsStart = RTTimeMilliTS();
3232 uint64_t tsNow = tsStart;
3233
3234 while (tsNow - tsStart < uTimeoutMS)
3235 {
3236 GuestWaitEvent *pEvent = NULL;
3237 GuestEventTypes eventTypes;
3238 try
3239 {
3240 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
3241
3242 vrc = registerWaitEventEx(mData.mSession.mID, mData.mObjectID, eventTypes, &pEvent);
3243 }
3244 catch (std::bad_alloc &)
3245 {
3246 vrc = VERR_NO_MEMORY;
3247 }
3248
3249 if (RT_FAILURE(vrc))
3250 return vrc;
3251
3252 alock.release(); /* Release lock before waiting. */
3253
3254 GuestSessionStatus_T sessionStatus;
3255 vrc = i_waitForStatusChange(pEvent, fWaitFlags,
3256 uTimeoutMS - (tsNow - tsStart), &sessionStatus, prcGuest);
3257 if (RT_SUCCESS(vrc))
3258 {
3259 switch (sessionStatus)
3260 {
3261 case GuestSessionStatus_Started:
3262 waitResult = GuestSessionWaitResult_Start;
3263 break;
3264
3265 case GuestSessionStatus_Starting:
3266 RT_FALL_THROUGH();
3267 case GuestSessionStatus_Terminating:
3268 if (fWaitFlags & GuestSessionWaitForFlag_Status) /* Any status wanted? */
3269 waitResult = GuestSessionWaitResult_Status;
3270 /* else: Wait another round until we get the event(s) fWaitFlags defines. */
3271 break;
3272
3273 case GuestSessionStatus_Terminated:
3274 waitResult = GuestSessionWaitResult_Terminate;
3275 break;
3276
3277 case GuestSessionStatus_TimedOutKilled:
3278 RT_FALL_THROUGH();
3279 case GuestSessionStatus_TimedOutAbnormally:
3280 waitResult = GuestSessionWaitResult_Timeout;
3281 break;
3282
3283 case GuestSessionStatus_Down:
3284 waitResult = GuestSessionWaitResult_Terminate;
3285 break;
3286
3287 case GuestSessionStatus_Error:
3288 waitResult = GuestSessionWaitResult_Error;
3289 break;
3290
3291 default:
3292 waitResult = GuestSessionWaitResult_Status;
3293 break;
3294 }
3295 }
3296
3297 unregisterWaitEvent(pEvent);
3298
3299 /* Wait result not None, e.g. some result acquired or a wait error occurred? Bail out. */
3300 if ( waitResult != GuestSessionWaitResult_None
3301 || RT_FAILURE(vrc))
3302 break;
3303
3304 tsNow = RTTimeMilliTS();
3305
3306 alock.acquire(); /* Re-acquire lock before waiting for the next event. */
3307 }
3308
3309 if (tsNow - tsStart >= uTimeoutMS)
3310 {
3311 waitResult = GuestSessionWaitResult_None; /* Paranoia. */
3312 vrc = VERR_TIMEOUT;
3313 }
3314
3315 LogFlowFuncLeaveRC(vrc);
3316 return vrc;
3317}
3318
3319/**
3320 * Waits for guest session status changes.
3321 *
3322 * @returns VBox status code.
3323 * @retval VERR_GSTCTL_GUEST_ERROR on received guest error.
3324 * @retval VERR_WRONG_ORDER when an unexpected event type has been received.
3325 * @param pEvent Wait event to use for waiting.
3326 * @param fWaitFlags Wait flags to use.
3327 * @param uTimeoutMS Timeout (in ms) to wait.
3328 * @param pSessionStatus Where to return the guest session status.
3329 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
3330 * was returned. Optional.
3331 */
3332int GuestSession::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t fWaitFlags, uint32_t uTimeoutMS,
3333 GuestSessionStatus_T *pSessionStatus, int *prcGuest)
3334{
3335 RT_NOREF(fWaitFlags);
3336 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
3337
3338 VBoxEventType_T evtType;
3339 ComPtr<IEvent> pIEvent;
3340 int vrc = waitForEvent(pEvent, uTimeoutMS, &evtType, pIEvent.asOutParam());
3341 if (RT_SUCCESS(vrc))
3342 {
3343 if (evtType == VBoxEventType_OnGuestSessionStateChanged)
3344 {
3345 ComPtr<IGuestSessionStateChangedEvent> pChangedEvent = pIEvent;
3346 Assert(!pChangedEvent.isNull());
3347
3348 GuestSessionStatus_T sessionStatus;
3349 pChangedEvent->COMGETTER(Status)(&sessionStatus);
3350 if (pSessionStatus)
3351 *pSessionStatus = sessionStatus;
3352
3353 ComPtr<IVirtualBoxErrorInfo> errorInfo;
3354 HRESULT hr = pChangedEvent->COMGETTER(Error)(errorInfo.asOutParam());
3355 ComAssertComRC(hr);
3356
3357 LONG lGuestRc;
3358 hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
3359 ComAssertComRC(hr);
3360 if (RT_FAILURE((int)lGuestRc))
3361 vrc = VERR_GSTCTL_GUEST_ERROR;
3362 if (prcGuest)
3363 *prcGuest = (int)lGuestRc;
3364
3365 LogFlowThisFunc(("Status changed event for session ID=%RU32, new status is: %RU32 (%Rrc)\n",
3366 mData.mSession.mID, sessionStatus,
3367 RT_SUCCESS((int)lGuestRc) ? VINF_SUCCESS : (int)lGuestRc));
3368 }
3369 else /** @todo Re-visit this. Can this happen more frequently? */
3370 AssertMsgFailedReturn(("Got unexpected event type %#x\n", evtType), VERR_WRONG_ORDER);
3371 }
3372 /* waitForEvent may also return VERR_GSTCTL_GUEST_ERROR like we do above, so make prcGuest is set. */
3373 else if (vrc == VERR_GSTCTL_GUEST_ERROR && prcGuest)
3374 *prcGuest = pEvent->GuestResult();
3375 Assert(vrc != VERR_GSTCTL_GUEST_ERROR || !prcGuest || *prcGuest != (int)0xcccccccc);
3376
3377 LogFlowFuncLeaveRC(vrc);
3378 return vrc;
3379}
3380
3381// implementation of public methods
3382/////////////////////////////////////////////////////////////////////////////
3383
3384HRESULT GuestSession::close()
3385{
3386 LogFlowThisFuncEnter();
3387
3388 /* Note: Don't check if the session is ready via i_isStartedExternal() here;
3389 * the session (already) could be in a stopped / aborted state. */
3390
3391 int vrc = VINF_SUCCESS; /* Shut up MSVC. */
3392 int rcGuest = VINF_SUCCESS;
3393
3394 uint32_t msTimeout = RT_MS_10SEC; /* 10s timeout by default */
3395 for (int i = 0; i < 3; i++)
3396 {
3397 if (i)
3398 {
3399 LogRel(("Guest Control: Closing session '%s' timed out (%RU32s timeout, attempt %d/10), retrying ...\n",
3400 mData.mSession.mName.c_str(), msTimeout / RT_MS_1SEC, i + 1));
3401 msTimeout += RT_MS_5SEC; /* Slightly increase the timeout. */
3402 }
3403
3404 /* Close session on guest. */
3405 vrc = i_closeSession(0 /* Flags */, msTimeout, &rcGuest);
3406 if ( RT_SUCCESS(vrc)
3407 || vrc != VERR_TIMEOUT) /* If something else happened there is no point in retrying further. */
3408 break;
3409 }
3410
3411 /* On failure don't return here, instead do all the cleanup
3412 * work first and then return an error. */
3413
3414 /* Destroy session + remove ourselves from the session list. */
3415 AssertPtr(mParent);
3416 int vrc2 = mParent->i_sessionDestroy(mData.mSession.mID);
3417 if (vrc2 == VERR_NOT_FOUND) /* Not finding the session anymore isn't critical. */
3418 vrc2 = VINF_SUCCESS;
3419
3420 if (RT_SUCCESS(vrc))
3421 vrc = vrc2;
3422
3423 LogFlowThisFunc(("Returning rc=%Rrc, rcGuest=%Rrc\n", vrc, rcGuest));
3424
3425 if (RT_FAILURE(vrc))
3426 {
3427 if (vrc == VERR_GSTCTL_GUEST_ERROR)
3428 {
3429 GuestErrorInfo ge(GuestErrorInfo::Type_Session, rcGuest, mData.mSession.mName.c_str());
3430 return setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Closing guest session failed: %s"),
3431 GuestBase::getErrorAsString(ge).c_str());
3432 }
3433 return setError(VBOX_E_IPRT_ERROR, tr("Closing guest session \"%s\" failed with %Rrc"),
3434 mData.mSession.mName.c_str(), vrc);
3435 }
3436
3437 return S_OK;
3438}
3439
3440HRESULT GuestSession::fileCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3441 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3442{
3443 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3444 ReturnComNotImplemented();
3445}
3446
3447HRESULT GuestSession::fileCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3448 const std::vector<FileCopyFlag_T> &aFlags,
3449 ComPtr<IProgress> &aProgress)
3450{
3451 uint32_t fFlags = FileCopyFlag_None;
3452 if (aFlags.size())
3453 {
3454 for (size_t i = 0; i < aFlags.size(); i++)
3455 fFlags |= aFlags[i];
3456
3457 const uint32_t fValidFlags = FileCopyFlag_None | FileCopyFlag_NoReplace | FileCopyFlag_FollowLinks | FileCopyFlag_Update;
3458 if (fFlags & ~fValidFlags)
3459 return setError(E_INVALIDARG,tr("Unknown flags: flags value %#x, invalid: %#x"), fFlags, fFlags & ~fValidFlags);
3460 }
3461
3462 GuestSessionFsSourceSet SourceSet;
3463
3464 GuestSessionFsSourceSpec source;
3465 source.strSource = aSource;
3466 source.enmType = FsObjType_File;
3467 source.enmPathStyle = i_getGuestPathStyle();
3468 source.fDryRun = false; /** @todo Implement support for a dry run. */
3469 source.fDirCopyFlags = DirectoryCopyFlag_None;
3470 source.fFileCopyFlags = (FileCopyFlag_T)fFlags;
3471
3472 SourceSet.push_back(source);
3473
3474 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3475}
3476
3477HRESULT GuestSession::fileCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3478 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3479{
3480 uint32_t fFlags = FileCopyFlag_None;
3481 if (aFlags.size())
3482 {
3483 for (size_t i = 0; i < aFlags.size(); i++)
3484 fFlags |= aFlags[i];
3485
3486 const uint32_t fValidFlags = FileCopyFlag_None | FileCopyFlag_NoReplace | FileCopyFlag_FollowLinks | FileCopyFlag_Update;
3487 if (fFlags & ~fValidFlags)
3488 return setError(E_INVALIDARG,tr("Unknown flags: flags value %#x, invalid: %#x"), fFlags, fFlags & ~fValidFlags);
3489 }
3490
3491 GuestSessionFsSourceSet SourceSet;
3492
3493 GuestSessionFsSourceSpec source;
3494 source.strSource = aSource;
3495 source.enmType = FsObjType_File;
3496 source.enmPathStyle = GuestSession::i_getHostPathStyle();
3497 source.fDryRun = false; /** @todo Implement support for a dry run. */
3498 source.fDirCopyFlags = DirectoryCopyFlag_None;
3499 source.fFileCopyFlags = (FileCopyFlag_T)fFlags;
3500
3501 SourceSet.push_back(source);
3502
3503 return i_copyToGuest(SourceSet, aDestination, aProgress);
3504}
3505
3506HRESULT GuestSession::copyFromGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
3507 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
3508 ComPtr<IProgress> &aProgress)
3509{
3510 const size_t cSources = aSources.size();
3511 if ( (aFilters.size() && aFilters.size() != cSources)
3512 || (aFlags.size() && aFlags.size() != cSources))
3513 {
3514 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
3515 }
3516
3517 GuestSessionFsSourceSet SourceSet;
3518
3519 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
3520 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
3521 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
3522
3523 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
3524 const bool fFollowSymlinks = true; /** @todo Ditto. */
3525
3526 while (itSource != aSources.end())
3527 {
3528 GuestFsObjData objData;
3529 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3530 int vrc = i_fsQueryInfo(*(itSource), fFollowSymlinks, objData, &rcGuest);
3531 if ( RT_FAILURE(vrc)
3532 && !fContinueOnErrors)
3533 {
3534 if (GuestProcess::i_isGuestError(vrc))
3535 {
3536 GuestErrorInfo ge(GuestErrorInfo::Type_Process, rcGuest, (*itSource).c_str());
3537 return setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying type for guest source failed: %s"),
3538 GuestBase::getErrorAsString(ge).c_str());
3539 }
3540 return setError(E_FAIL, tr("Querying type for guest source \"%s\" failed: %Rrc"), (*itSource).c_str(), vrc);
3541 }
3542
3543 Utf8Str strFlags;
3544 if (itFlags != aFlags.end())
3545 {
3546 strFlags = *itFlags;
3547 ++itFlags;
3548 }
3549
3550 Utf8Str strFilter;
3551 if (itFilter != aFilters.end())
3552 {
3553 strFilter = *itFilter;
3554 ++itFilter;
3555 }
3556
3557 GuestSessionFsSourceSpec source;
3558 source.strSource = *itSource;
3559 source.strFilter = strFilter;
3560 source.enmType = objData.mType;
3561 source.enmPathStyle = i_getGuestPathStyle();
3562 source.fDryRun = false; /** @todo Implement support for a dry run. */
3563
3564 /* Check both flag groups here, as copying a directory also could mean to explicitly
3565 * *not* replacing any existing files (or just copy files which are newer, for instance). */
3566 GuestSession::i_directoryCopyFlagFromStr(strFlags, false /* fStrict */, &source.fDirCopyFlags);
3567 GuestSession::i_fileCopyFlagFromStr(strFlags, false /* fStrict */, &source.fFileCopyFlags);
3568
3569 SourceSet.push_back(source);
3570
3571 ++itSource;
3572 }
3573
3574 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3575}
3576
3577HRESULT GuestSession::copyToGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
3578 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
3579 ComPtr<IProgress> &aProgress)
3580{
3581 const size_t cSources = aSources.size();
3582 if ( (aFilters.size() && aFilters.size() != cSources)
3583 || (aFlags.size() && aFlags.size() != cSources))
3584 {
3585 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
3586 }
3587
3588 GuestSessionFsSourceSet SourceSet;
3589
3590 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
3591 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
3592 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
3593
3594 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
3595
3596 while (itSource != aSources.end())
3597 {
3598 RTFSOBJINFO objInfo;
3599 int vrc = RTPathQueryInfo((*itSource).c_str(), &objInfo, RTFSOBJATTRADD_NOTHING);
3600 if ( RT_FAILURE(vrc)
3601 && !fContinueOnErrors)
3602 {
3603 return setError(E_FAIL, tr("Unable to query type for source '%s' (%Rrc)"), (*itSource).c_str(), vrc);
3604 }
3605
3606 Utf8Str strFlags;
3607 if (itFlags != aFlags.end())
3608 {
3609 strFlags = *itFlags;
3610 ++itFlags;
3611 }
3612
3613 Utf8Str strFilter;
3614 if (itFilter != aFilters.end())
3615 {
3616 strFilter = *itFilter;
3617 ++itFilter;
3618 }
3619
3620 GuestSessionFsSourceSpec source;
3621 source.strSource = *itSource;
3622 source.strFilter = strFilter;
3623 source.enmType = GuestBase::fileModeToFsObjType(objInfo.Attr.fMode);
3624 source.enmPathStyle = GuestSession::i_getHostPathStyle();
3625 source.fDryRun = false; /** @todo Implement support for a dry run. */
3626
3627 GuestSession::i_directoryCopyFlagFromStr(strFlags, false /* fStrict */, &source.fDirCopyFlags);
3628 GuestSession::i_fileCopyFlagFromStr(strFlags, false /* fStrict */, &source.fFileCopyFlags);
3629
3630 SourceSet.push_back(source);
3631
3632 ++itSource;
3633 }
3634
3635 /* (Re-)Validate stuff. */
3636 if (RT_UNLIKELY(SourceSet.size() == 0)) /* At least one source must be present. */
3637 return setError(E_INVALIDARG, tr("No sources specified"));
3638 if (RT_UNLIKELY(SourceSet[0].strSource.isEmpty()))
3639 return setError(E_INVALIDARG, tr("First source entry is empty"));
3640 if (RT_UNLIKELY(aDestination.isEmpty()))
3641 return setError(E_INVALIDARG, tr("No destination specified"));
3642
3643 return i_copyToGuest(SourceSet, aDestination, aProgress);
3644}
3645
3646HRESULT GuestSession::directoryCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3647 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3648{
3649 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3650 ReturnComNotImplemented();
3651}
3652
3653HRESULT GuestSession::directoryCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3654 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3655{
3656 uint32_t fFlags = DirectoryCopyFlag_None;
3657 if (aFlags.size())
3658 {
3659 for (size_t i = 0; i < aFlags.size(); i++)
3660 fFlags |= aFlags[i];
3661
3662 const uint32_t fValidFlags = DirectoryCopyFlag_None | DirectoryCopyFlag_CopyIntoExisting | DirectoryCopyFlag_Recursive
3663 | DirectoryCopyFlag_FollowLinks;
3664 if (fFlags & ~fValidFlags)
3665 return setError(E_INVALIDARG,tr("Unknown flags: flags value %#x, invalid: %#x"), fFlags, fFlags & ~fValidFlags);
3666 }
3667
3668 GuestSessionFsSourceSet SourceSet;
3669
3670 GuestSessionFsSourceSpec source;
3671 source.strSource = aSource;
3672 source.enmType = FsObjType_Directory;
3673 source.enmPathStyle = i_getGuestPathStyle();
3674 source.fDryRun = false; /** @todo Implement support for a dry run. */
3675 source.fDirCopyFlags = (DirectoryCopyFlag_T)fFlags;
3676 source.fFileCopyFlags = FileCopyFlag_None; /* Overwrite existing files. */
3677
3678 SourceSet.push_back(source);
3679
3680 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3681}
3682
3683HRESULT GuestSession::directoryCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3684 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3685{
3686 uint32_t fFlags = DirectoryCopyFlag_None;
3687 if (aFlags.size())
3688 {
3689 for (size_t i = 0; i < aFlags.size(); i++)
3690 fFlags |= aFlags[i];
3691
3692 const uint32_t fValidFlags = DirectoryCopyFlag_None | DirectoryCopyFlag_CopyIntoExisting | DirectoryCopyFlag_Recursive
3693 | DirectoryCopyFlag_FollowLinks;
3694 if (fFlags & ~fValidFlags)
3695 return setError(E_INVALIDARG,tr("Unknown flags: flags value %#x, invalid: %#x"), fFlags, fFlags & ~fValidFlags);
3696 }
3697
3698 GuestSessionFsSourceSet SourceSet;
3699
3700 GuestSessionFsSourceSpec source;
3701 source.strSource = aSource;
3702 source.enmType = FsObjType_Directory;
3703 source.enmPathStyle = GuestSession::i_getHostPathStyle();
3704 source.fDryRun = false; /** @todo Implement support for a dry run. */
3705 source.fDirCopyFlags = (DirectoryCopyFlag_T)fFlags;
3706 source.fFileCopyFlags = FileCopyFlag_None; /* Overwrite existing files. */
3707
3708 SourceSet.push_back(source);
3709
3710 return i_copyToGuest(SourceSet, aDestination, aProgress);
3711}
3712
3713HRESULT GuestSession::directoryCreate(const com::Utf8Str &aPath, ULONG aMode,
3714 const std::vector<DirectoryCreateFlag_T> &aFlags)
3715{
3716 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3717 return setError(E_INVALIDARG, tr("No directory to create specified"));
3718
3719 uint32_t fFlags = DirectoryCreateFlag_None;
3720 if (aFlags.size())
3721 {
3722 for (size_t i = 0; i < aFlags.size(); i++)
3723 fFlags |= aFlags[i];
3724
3725 if (fFlags & ~DirectoryCreateFlag_Parents)
3726 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), fFlags);
3727 }
3728
3729 HRESULT hrc = i_isStartedExternal();
3730 if (FAILED(hrc))
3731 return hrc;
3732
3733 LogFlowThisFuncEnter();
3734
3735 ComObjPtr <GuestDirectory> pDirectory; int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3736 int vrc = i_directoryCreate(aPath, (uint32_t)aMode, fFlags, &rcGuest);
3737 if (RT_FAILURE(vrc))
3738 {
3739 if (GuestProcess::i_isGuestError(vrc))
3740 {
3741 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, rcGuest, aPath.c_str());
3742 return setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Guest directory creation failed: %s"),
3743 GuestBase::getErrorAsString(ge).c_str());
3744 }
3745 switch (vrc)
3746 {
3747 case VERR_INVALID_PARAMETER:
3748 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Guest directory creation failed: Invalid parameters given"));
3749 break;
3750
3751 case VERR_BROKEN_PIPE:
3752 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Guest directory creation failed: Unexpectedly aborted"));
3753 break;
3754
3755 default:
3756 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Guest directory creation failed: %Rrc"), vrc);
3757 break;
3758 }
3759 }
3760
3761 return hrc;
3762}
3763
3764HRESULT GuestSession::directoryCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath,
3765 BOOL aSecure, com::Utf8Str &aDirectory)
3766{
3767 if (RT_UNLIKELY((aTemplateName.c_str()) == NULL || *(aTemplateName.c_str()) == '\0'))
3768 return setError(E_INVALIDARG, tr("No template specified"));
3769 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3770 return setError(E_INVALIDARG, tr("No directory name specified"));
3771 if (!aSecure) /* Ignore what mode is specified when a secure temp thing needs to be created. */
3772 if (RT_UNLIKELY(!(aMode & ~07777)))
3773 return setError(E_INVALIDARG, tr("Mode invalid (must be specified in octal mode)"));
3774
3775 HRESULT hrc = i_isStartedExternal();
3776 if (FAILED(hrc))
3777 return hrc;
3778
3779 LogFlowThisFuncEnter();
3780
3781 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3782 int vrc = i_fsCreateTemp(aTemplateName, aPath, true /* Directory */, aDirectory, aMode, RT_BOOL(aSecure), &rcGuest);
3783 if (!RT_SUCCESS(vrc))
3784 {
3785 switch (vrc)
3786 {
3787 case VERR_GSTCTL_GUEST_ERROR:
3788 {
3789 GuestErrorInfo ge(GuestErrorInfo::Type_ToolMkTemp, rcGuest, aPath.c_str());
3790 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Temporary guest directory creation failed: %s"),
3791 GuestBase::getErrorAsString(ge).c_str());
3792 break;
3793 }
3794 default:
3795 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Temporary guest directory creation \"%s\" with template \"%s\" failed: %Rrc"),
3796 aPath.c_str(), aTemplateName.c_str(), vrc);
3797 break;
3798 }
3799 }
3800
3801 return hrc;
3802}
3803
3804HRESULT GuestSession::directoryExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3805{
3806 if (RT_UNLIKELY(aPath.isEmpty()))
3807 return setError(E_INVALIDARG, tr("Empty path"));
3808
3809 HRESULT hrc = i_isStartedExternal();
3810 if (FAILED(hrc))
3811 return hrc;
3812
3813 LogFlowThisFuncEnter();
3814
3815 GuestFsObjData objData;
3816 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3817
3818 int vrc = i_directoryQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3819 if (RT_SUCCESS(vrc))
3820 *aExists = TRUE;
3821 else
3822 {
3823 switch (vrc)
3824 {
3825 case VERR_GSTCTL_GUEST_ERROR:
3826 {
3827 switch (rcGuest)
3828 {
3829 case VERR_PATH_NOT_FOUND:
3830 *aExists = FALSE;
3831 break;
3832 default:
3833 {
3834 GuestErrorInfo ge(GuestErrorInfo::Type_ToolStat, rcGuest, aPath.c_str());
3835 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying directory existence failed: %s"),
3836 GuestBase::getErrorAsString(ge).c_str());
3837 break;
3838 }
3839 }
3840 break;
3841 }
3842
3843 case VERR_NOT_A_DIRECTORY:
3844 {
3845 *aExists = FALSE;
3846 break;
3847 }
3848
3849 default:
3850 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying directory existence \"%s\" failed: %Rrc"),
3851 aPath.c_str(), vrc);
3852 break;
3853 }
3854 }
3855
3856 return hrc;
3857}
3858
3859HRESULT GuestSession::directoryOpen(const com::Utf8Str &aPath, const com::Utf8Str &aFilter,
3860 const std::vector<DirectoryOpenFlag_T> &aFlags, ComPtr<IGuestDirectory> &aDirectory)
3861{
3862 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3863 return setError(E_INVALIDARG, tr("No directory to open specified"));
3864 if (RT_UNLIKELY((aFilter.c_str()) != NULL && *(aFilter.c_str()) != '\0'))
3865 return setError(E_INVALIDARG, tr("Directory filters are not implemented yet"));
3866
3867 uint32_t fFlags = DirectoryOpenFlag_None;
3868 if (aFlags.size())
3869 {
3870 for (size_t i = 0; i < aFlags.size(); i++)
3871 fFlags |= aFlags[i];
3872
3873 if (fFlags)
3874 return setError(E_INVALIDARG, tr("Open flags (%#x) not implemented yet"), fFlags);
3875 }
3876
3877 HRESULT hrc = i_isStartedExternal();
3878 if (FAILED(hrc))
3879 return hrc;
3880
3881 LogFlowThisFuncEnter();
3882
3883 GuestDirectoryOpenInfo openInfo;
3884 openInfo.mPath = aPath;
3885 openInfo.mFilter = aFilter;
3886 openInfo.mFlags = fFlags;
3887
3888 ComObjPtr<GuestDirectory> pDirectory; int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3889 int vrc = i_directoryOpen(openInfo, pDirectory, &rcGuest);
3890 if (RT_SUCCESS(vrc))
3891 {
3892 /* Return directory object to the caller. */
3893 hrc = pDirectory.queryInterfaceTo(aDirectory.asOutParam());
3894 }
3895 else
3896 {
3897 switch (vrc)
3898 {
3899 case VERR_INVALID_PARAMETER:
3900 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest directory \"%s\" failed; invalid parameters given"),
3901 aPath.c_str());
3902 break;
3903
3904 case VERR_GSTCTL_GUEST_ERROR:
3905 {
3906 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, rcGuest, aPath.c_str());
3907 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Opening guest directory failed: %s"),
3908 GuestBase::getErrorAsString(ge).c_str());
3909 break;
3910 }
3911 default:
3912 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3913 break;
3914 }
3915 }
3916
3917 return hrc;
3918}
3919
3920HRESULT GuestSession::directoryRemove(const com::Utf8Str &aPath)
3921{
3922 if (RT_UNLIKELY(aPath.c_str() == NULL || *aPath.c_str() == '\0'))
3923 return setError(E_INVALIDARG, tr("No directory to remove specified"));
3924
3925 HRESULT hrc = i_isStartedExternal();
3926 if (FAILED(hrc))
3927 return hrc;
3928
3929 LogFlowThisFuncEnter();
3930
3931 /* No flags; only remove the directory when empty. */
3932 uint32_t fFlags = DIRREMOVEREC_FLAG_NONE;
3933
3934 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3935 int vrc = i_directoryRemove(aPath, fFlags, &rcGuest);
3936 if (RT_FAILURE(vrc))
3937 {
3938 switch (vrc)
3939 {
3940 case VERR_NOT_SUPPORTED:
3941 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3942 tr("Handling removing guest directories not supported by installed Guest Additions"));
3943 break;
3944
3945 case VERR_GSTCTL_GUEST_ERROR:
3946 {
3947 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, rcGuest, aPath.c_str());
3948 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Removing guest directory failed: %s"),
3949 GuestBase::getErrorAsString(ge).c_str());
3950 break;
3951 }
3952 default:
3953 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing guest directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3954 break;
3955 }
3956 }
3957
3958 return hrc;
3959}
3960
3961HRESULT GuestSession::directoryRemoveRecursive(const com::Utf8Str &aPath, const std::vector<DirectoryRemoveRecFlag_T> &aFlags,
3962 ComPtr<IProgress> &aProgress)
3963{
3964 if (RT_UNLIKELY(aPath.c_str() == NULL || *aPath.c_str() == '\0'))
3965 return setError(E_INVALIDARG, tr("No directory to remove recursively specified"));
3966
3967 /* By defautl remove recursively as the function name implies. */
3968 uint32_t fFlags = DIRREMOVEREC_FLAG_RECURSIVE;
3969 if (aFlags.size())
3970 {
3971 for (size_t i = 0; i < aFlags.size(); i++)
3972 {
3973 switch (aFlags[i])
3974 {
3975 case DirectoryRemoveRecFlag_None: /* Skip. */
3976 continue;
3977
3978 case DirectoryRemoveRecFlag_ContentAndDir:
3979 fFlags |= DIRREMOVEREC_FLAG_CONTENT_AND_DIR;
3980 break;
3981
3982 case DirectoryRemoveRecFlag_ContentOnly:
3983 fFlags |= DIRREMOVEREC_FLAG_CONTENT_ONLY;
3984 break;
3985
3986 default:
3987 return setError(E_INVALIDARG, tr("Invalid flags specified"));
3988 }
3989 }
3990 }
3991
3992 HRESULT hrc = i_isStartedExternal();
3993 if (FAILED(hrc))
3994 return hrc;
3995
3996 LogFlowThisFuncEnter();
3997
3998 ComObjPtr<Progress> pProgress;
3999 hrc = pProgress.createObject();
4000 if (SUCCEEDED(hrc))
4001 hrc = pProgress->init(static_cast<IGuestSession *>(this),
4002 Bstr(tr("Removing guest directory")).raw(),
4003 TRUE /*aCancelable*/);
4004 if (FAILED(hrc))
4005 return hrc;
4006
4007 /* Note: At the moment we don't supply progress information while
4008 * deleting a guest directory recursively. So just complete
4009 * the progress object right now. */
4010 /** @todo Implement progress reporting on guest directory deletion! */
4011 hrc = pProgress->i_notifyComplete(S_OK);
4012 if (FAILED(hrc))
4013 return hrc;
4014
4015 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4016 int vrc = i_directoryRemove(aPath, fFlags, &rcGuest);
4017 if (RT_FAILURE(vrc))
4018 {
4019 switch (vrc)
4020 {
4021 case VERR_NOT_SUPPORTED:
4022 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4023 tr("Handling removing guest directories recursively not supported by installed Guest Additions"));
4024 break;
4025
4026 case VERR_GSTCTL_GUEST_ERROR:
4027 {
4028 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, rcGuest, aPath.c_str());
4029 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Recursively removing guest directory failed: %s"),
4030 GuestBase::getErrorAsString(ge).c_str());
4031 break;
4032 }
4033 default:
4034 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Recursively removing guest directory \"%s\" failed: %Rrc"),
4035 aPath.c_str(), vrc);
4036 break;
4037 }
4038 }
4039 else
4040 {
4041 pProgress.queryInterfaceTo(aProgress.asOutParam());
4042 }
4043
4044 return hrc;
4045}
4046
4047HRESULT GuestSession::environmentScheduleSet(const com::Utf8Str &aName, const com::Utf8Str &aValue)
4048{
4049 LogFlowThisFuncEnter();
4050 int vrc;
4051 {
4052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4053 vrc = mData.mEnvironmentChanges.setVariable(aName, aValue);
4054 }
4055 HRESULT hrc;
4056 if (RT_SUCCESS(vrc))
4057 hrc = S_OK;
4058 else if (vrc == VERR_ENV_INVALID_VAR_NAME)
4059 hrc = setError(E_INVALIDARG, tr("Invalid environment variable name '%s'"), aName.c_str());
4060 else
4061 hrc = setErrorVrc(vrc, tr("Failed to schedule setting environment variable '%s' to '%s'"), aName.c_str(), aValue.c_str());
4062
4063 LogFlowThisFuncLeave();
4064 return hrc;
4065}
4066
4067HRESULT GuestSession::environmentScheduleUnset(const com::Utf8Str &aName)
4068{
4069 LogFlowThisFuncEnter();
4070 int vrc;
4071 {
4072 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4073 vrc = mData.mEnvironmentChanges.unsetVariable(aName);
4074 }
4075 HRESULT hrc;
4076 if (RT_SUCCESS(vrc))
4077 hrc = S_OK;
4078 else if (vrc == VERR_ENV_INVALID_VAR_NAME)
4079 hrc = setError(E_INVALIDARG, tr("Invalid environment variable name '%s'"), aName.c_str());
4080 else
4081 hrc = setErrorVrc(vrc, tr("Failed to schedule unsetting environment variable '%s'"), aName.c_str());
4082
4083 LogFlowThisFuncLeave();
4084 return hrc;
4085}
4086
4087HRESULT GuestSession::environmentGetBaseVariable(const com::Utf8Str &aName, com::Utf8Str &aValue)
4088{
4089 LogFlowThisFuncEnter();
4090 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4091
4092 HRESULT hrc;
4093 if (mData.mpBaseEnvironment)
4094 {
4095 int vrc = mData.mpBaseEnvironment->getVariable(aName, &aValue);
4096 if (RT_SUCCESS(vrc))
4097 hrc = S_OK;
4098 else if (vrc == VERR_ENV_INVALID_VAR_NAME)
4099 hrc = setError(E_INVALIDARG, tr("Invalid environment variable name '%s'"), aName.c_str());
4100 else
4101 hrc = setErrorVrc(vrc);
4102 }
4103 else if (mData.mProtocolVersion < 99999)
4104 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the Guest Additions"));
4105 else
4106 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
4107
4108 LogFlowThisFuncLeave();
4109 return hrc;
4110}
4111
4112HRESULT GuestSession::environmentDoesBaseVariableExist(const com::Utf8Str &aName, BOOL *aExists)
4113{
4114 LogFlowThisFuncEnter();
4115 *aExists = FALSE;
4116 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4117
4118 HRESULT hrc;
4119 if (mData.mpBaseEnvironment)
4120 {
4121 hrc = S_OK;
4122 *aExists = mData.mpBaseEnvironment->doesVariableExist(aName);
4123 }
4124 else if (mData.mProtocolVersion < 99999)
4125 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the Guest Additions"));
4126 else
4127 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
4128
4129 LogFlowThisFuncLeave();
4130 return hrc;
4131}
4132
4133HRESULT GuestSession::fileCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath, BOOL aSecure,
4134 ComPtr<IGuestFile> &aFile)
4135{
4136 RT_NOREF(aTemplateName, aMode, aPath, aSecure, aFile);
4137 ReturnComNotImplemented();
4138}
4139
4140HRESULT GuestSession::fileExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
4141{
4142 /* By default we return non-existent. */
4143 *aExists = FALSE;
4144
4145 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
4146 return S_OK;
4147
4148 HRESULT hrc = i_isStartedExternal();
4149 if (FAILED(hrc))
4150 return hrc;
4151
4152 LogFlowThisFuncEnter();
4153
4154 GuestFsObjData objData; int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4155 int vrc = i_fileQueryInfo(aPath, RT_BOOL(aFollowSymlinks), objData, &rcGuest);
4156 if (RT_SUCCESS(vrc))
4157 {
4158 *aExists = TRUE;
4159 return S_OK;
4160 }
4161
4162 switch (vrc)
4163 {
4164 case VERR_GSTCTL_GUEST_ERROR:
4165 {
4166 switch (rcGuest)
4167 {
4168 case VERR_PATH_NOT_FOUND:
4169 RT_FALL_THROUGH();
4170 case VERR_FILE_NOT_FOUND:
4171 break;
4172
4173 default:
4174 {
4175 GuestErrorInfo ge(GuestErrorInfo::Type_ToolStat, rcGuest, aPath.c_str());
4176 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying guest file existence failed: %s"),
4177 GuestBase::getErrorAsString(ge).c_str());
4178 break;
4179 }
4180 }
4181
4182 break;
4183 }
4184
4185 case VERR_NOT_A_FILE:
4186 break;
4187
4188 default:
4189 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying guest file information for \"%s\" failed: %Rrc"),
4190 aPath.c_str(), vrc);
4191 break;
4192 }
4193
4194 return hrc;
4195}
4196
4197HRESULT GuestSession::fileOpen(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
4198 ULONG aCreationMode, ComPtr<IGuestFile> &aFile)
4199{
4200 LogFlowThisFuncEnter();
4201
4202 const std::vector<FileOpenExFlag_T> EmptyFlags;
4203 return fileOpenEx(aPath, aAccessMode, aOpenAction, FileSharingMode_All, aCreationMode, EmptyFlags, aFile);
4204}
4205
4206HRESULT GuestSession::fileOpenEx(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
4207 FileSharingMode_T aSharingMode, ULONG aCreationMode,
4208 const std::vector<FileOpenExFlag_T> &aFlags, ComPtr<IGuestFile> &aFile)
4209{
4210 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
4211 return setError(E_INVALIDARG, tr("No file to open specified"));
4212
4213 HRESULT hrc = i_isStartedExternal();
4214 if (FAILED(hrc))
4215 return hrc;
4216
4217 LogFlowThisFuncEnter();
4218
4219 /* Validate aAccessMode. */
4220 switch (aAccessMode)
4221 {
4222 case FileAccessMode_ReadOnly:
4223 RT_FALL_THRU();
4224 case FileAccessMode_WriteOnly:
4225 RT_FALL_THRU();
4226 case FileAccessMode_ReadWrite:
4227 break;
4228 case FileAccessMode_AppendOnly:
4229 RT_FALL_THRU();
4230 case FileAccessMode_AppendRead:
4231 return setError(E_NOTIMPL, tr("Append access modes are not yet implemented"));
4232 default:
4233 return setError(E_INVALIDARG, tr("Unknown FileAccessMode value %u (%#x)"), aAccessMode, aAccessMode);
4234 }
4235
4236 /* Validate aOpenAction to the old format. */
4237 switch (aOpenAction)
4238 {
4239 case FileOpenAction_OpenExisting:
4240 RT_FALL_THRU();
4241 case FileOpenAction_OpenOrCreate:
4242 RT_FALL_THRU();
4243 case FileOpenAction_CreateNew:
4244 RT_FALL_THRU();
4245 case FileOpenAction_CreateOrReplace:
4246 RT_FALL_THRU();
4247 case FileOpenAction_OpenExistingTruncated:
4248 RT_FALL_THRU();
4249 case FileOpenAction_AppendOrCreate:
4250 break;
4251 default:
4252 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
4253 }
4254
4255 /* Validate aSharingMode. */
4256 switch (aSharingMode)
4257 {
4258 case FileSharingMode_All:
4259 break;
4260 case FileSharingMode_Read:
4261 case FileSharingMode_Write:
4262 case FileSharingMode_ReadWrite:
4263 case FileSharingMode_Delete:
4264 case FileSharingMode_ReadDelete:
4265 case FileSharingMode_WriteDelete:
4266 return setError(E_NOTIMPL, tr("Only FileSharingMode_All is currently implemented"));
4267
4268 default:
4269 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
4270 }
4271
4272 /* Combine and validate flags. */
4273 uint32_t fOpenEx = 0;
4274 for (size_t i = 0; i < aFlags.size(); i++)
4275 fOpenEx |= aFlags[i];
4276 if (fOpenEx)
4277 return setError(E_INVALIDARG, tr("Unsupported FileOpenExFlag value(s) in aFlags (%#x)"), fOpenEx);
4278
4279 ComObjPtr <GuestFile> pFile;
4280 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4281 int vrc = i_fileOpenEx(aPath, aAccessMode, aOpenAction, aSharingMode, aCreationMode, aFlags, pFile, &rcGuest);
4282 if (RT_SUCCESS(vrc))
4283 /* Return directory object to the caller. */
4284 hrc = pFile.queryInterfaceTo(aFile.asOutParam());
4285 else
4286 {
4287 switch (vrc)
4288 {
4289 case VERR_NOT_SUPPORTED:
4290 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4291 tr("Handling guest files not supported by installed Guest Additions"));
4292 break;
4293
4294 case VERR_GSTCTL_GUEST_ERROR:
4295 {
4296 GuestErrorInfo ge(GuestErrorInfo::Type_File, rcGuest, aPath.c_str());
4297 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Opening guest file failed: %s"),
4298 GuestBase::getErrorAsString(ge).c_str());
4299 break;
4300 }
4301 default:
4302 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4303 break;
4304 }
4305 }
4306
4307 return hrc;
4308}
4309
4310HRESULT GuestSession::fileQuerySize(const com::Utf8Str &aPath, BOOL aFollowSymlinks, LONG64 *aSize)
4311{
4312 if (aPath.isEmpty())
4313 return setError(E_INVALIDARG, tr("No path specified"));
4314
4315 HRESULT hrc = i_isStartedExternal();
4316 if (FAILED(hrc))
4317 return hrc;
4318
4319 int64_t llSize; int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4320 int vrc = i_fileQuerySize(aPath, aFollowSymlinks != FALSE, &llSize, &rcGuest);
4321 if (RT_SUCCESS(vrc))
4322 {
4323 *aSize = llSize;
4324 }
4325 else
4326 {
4327 if (GuestProcess::i_isGuestError(vrc))
4328 {
4329 GuestErrorInfo ge(GuestErrorInfo::Type_ToolStat, rcGuest, aPath.c_str());
4330 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying guest file size failed: %s"),
4331 GuestBase::getErrorAsString(ge).c_str());
4332 }
4333 else
4334 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying guest file size of \"%s\" failed: %Rrc"),
4335 vrc, aPath.c_str());
4336 }
4337
4338 return hrc;
4339}
4340
4341HRESULT GuestSession::fsQueryFreeSpace(const com::Utf8Str &aPath, LONG64 *aFreeSpace)
4342{
4343 RT_NOREF(aPath, aFreeSpace);
4344
4345 return E_NOTIMPL;
4346}
4347
4348HRESULT GuestSession::fsQueryInfo(const com::Utf8Str &aPath, ComPtr<IGuestFsInfo> &aInfo)
4349{
4350 RT_NOREF(aPath, aInfo);
4351
4352 return E_NOTIMPL;
4353}
4354
4355HRESULT GuestSession::fsObjExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
4356{
4357 if (aPath.isEmpty())
4358 return setError(E_INVALIDARG, tr("No path specified"));
4359
4360 HRESULT hrc = i_isStartedExternal();
4361 if (FAILED(hrc))
4362 return hrc;
4363
4364 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
4365
4366 *aExists = false;
4367
4368 GuestFsObjData objData;
4369 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4370 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
4371 if (RT_SUCCESS(vrc))
4372 {
4373 *aExists = TRUE;
4374 }
4375 else
4376 {
4377 if (GuestProcess::i_isGuestError(vrc))
4378 {
4379 if ( rcGuest == VERR_NOT_A_FILE
4380 || rcGuest == VERR_PATH_NOT_FOUND
4381 || rcGuest == VERR_FILE_NOT_FOUND
4382 || rcGuest == VERR_INVALID_NAME)
4383 {
4384 hrc = S_OK; /* Ignore these vrc values. */
4385 }
4386 else
4387 {
4388 GuestErrorInfo ge(GuestErrorInfo::Type_ToolStat, rcGuest, aPath.c_str());
4389 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying guest file existence information failed: %s"),
4390 GuestBase::getErrorAsString(ge).c_str());
4391 }
4392 }
4393 else
4394 hrc = setErrorVrc(vrc, tr("Querying guest file existence information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4395 }
4396
4397 return hrc;
4398}
4399
4400HRESULT GuestSession::fsObjQueryInfo(const com::Utf8Str &aPath, BOOL aFollowSymlinks, ComPtr<IGuestFsObjInfo> &aInfo)
4401{
4402 if (aPath.isEmpty())
4403 return setError(E_INVALIDARG, tr("No path specified"));
4404
4405 HRESULT hrc = i_isStartedExternal();
4406 if (FAILED(hrc))
4407 return hrc;
4408
4409 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
4410
4411 GuestFsObjData Info; int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4412 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, Info, &rcGuest);
4413 if (RT_SUCCESS(vrc))
4414 {
4415 ComObjPtr<GuestFsObjInfo> ptrFsObjInfo;
4416 hrc = ptrFsObjInfo.createObject();
4417 if (SUCCEEDED(hrc))
4418 {
4419 vrc = ptrFsObjInfo->init(Info);
4420 if (RT_SUCCESS(vrc))
4421 hrc = ptrFsObjInfo.queryInterfaceTo(aInfo.asOutParam());
4422 else
4423 hrc = setErrorVrc(vrc);
4424 }
4425 }
4426 else
4427 {
4428 if (GuestProcess::i_isGuestError(vrc))
4429 {
4430 GuestErrorInfo ge(GuestErrorInfo::Type_ToolStat, rcGuest, aPath.c_str());
4431 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying guest file information failed: %s"),
4432 GuestBase::getErrorAsString(ge).c_str());
4433 }
4434 else
4435 hrc = setErrorVrc(vrc, tr("Querying guest file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4436 }
4437
4438 return hrc;
4439}
4440
4441HRESULT GuestSession::fsObjRemove(const com::Utf8Str &aPath)
4442{
4443 if (RT_UNLIKELY(aPath.isEmpty()))
4444 return setError(E_INVALIDARG, tr("No path specified"));
4445
4446 HRESULT hrc = i_isStartedExternal();
4447 if (FAILED(hrc))
4448 return hrc;
4449
4450 LogFlowThisFunc(("aPath=%s\n", aPath.c_str()));
4451
4452 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4453 int vrc = i_fileRemove(aPath, &rcGuest);
4454 if (RT_FAILURE(vrc))
4455 {
4456 if (GuestProcess::i_isGuestError(vrc))
4457 {
4458 GuestErrorInfo ge(GuestErrorInfo::Type_ToolRm, rcGuest, aPath.c_str());
4459 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Removing guest file failed: %s"),
4460 GuestBase::getErrorAsString(ge).c_str());
4461 }
4462 else
4463 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing guest file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4464 }
4465
4466 return hrc;
4467}
4468
4469HRESULT GuestSession::fsObjRemoveArray(const std::vector<com::Utf8Str> &aPaths, ComPtr<IProgress> &aProgress)
4470{
4471 RT_NOREF(aPaths, aProgress);
4472 return E_NOTIMPL;
4473}
4474
4475HRESULT GuestSession::fsObjRename(const com::Utf8Str &aSource,
4476 const com::Utf8Str &aDestination,
4477 const std::vector<FsObjRenameFlag_T> &aFlags)
4478{
4479 if (RT_UNLIKELY(aSource.isEmpty()))
4480 return setError(E_INVALIDARG, tr("No source path specified"));
4481
4482 if (RT_UNLIKELY(aDestination.isEmpty()))
4483 return setError(E_INVALIDARG, tr("No destination path specified"));
4484
4485 HRESULT hrc = i_isStartedExternal();
4486 if (FAILED(hrc))
4487 return hrc;
4488
4489 /* Combine, validate and convert flags. */
4490 uint32_t fApiFlags = 0;
4491 for (size_t i = 0; i < aFlags.size(); i++)
4492 fApiFlags |= aFlags[i];
4493 if (fApiFlags & ~((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace))
4494 return setError(E_INVALIDARG, tr("Unknown rename flag: %#x"), fApiFlags);
4495
4496 LogFlowThisFunc(("aSource=%s, aDestination=%s\n", aSource.c_str(), aDestination.c_str()));
4497
4498 AssertCompile(FsObjRenameFlag_NoReplace == 0);
4499 AssertCompile(FsObjRenameFlag_Replace != 0);
4500 uint32_t fBackend;
4501 if ((fApiFlags & ((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace)) == FsObjRenameFlag_Replace)
4502 fBackend = PATHRENAME_FLAG_REPLACE;
4503 else
4504 fBackend = PATHRENAME_FLAG_NO_REPLACE;
4505
4506 /* Call worker to do the job. */
4507 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4508 int vrc = i_pathRename(aSource, aDestination, fBackend, &rcGuest);
4509 if (RT_FAILURE(vrc))
4510 {
4511 switch (vrc)
4512 {
4513 case VERR_NOT_SUPPORTED:
4514 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4515 tr("Handling renaming guest paths not supported by installed Guest Additions"));
4516 break;
4517
4518 case VERR_GSTCTL_GUEST_ERROR:
4519 {
4520 GuestErrorInfo ge(GuestErrorInfo::Type_Process, rcGuest, aSource.c_str());
4521 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Renaming guest path failed: %s"),
4522 GuestBase::getErrorAsString(ge).c_str());
4523 break;
4524 }
4525 default:
4526 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming guest path \"%s\" failed: %Rrc"),
4527 aSource.c_str(), vrc);
4528 break;
4529 }
4530 }
4531
4532 return hrc;
4533}
4534
4535HRESULT GuestSession::fsObjMove(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
4536 const std::vector<FsObjMoveFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
4537{
4538 RT_NOREF(aSource, aDestination, aFlags, aProgress);
4539 ReturnComNotImplemented();
4540}
4541
4542HRESULT GuestSession::fsObjMoveArray(const std::vector<com::Utf8Str> &aSource,
4543 const com::Utf8Str &aDestination,
4544 const std::vector<FsObjMoveFlag_T> &aFlags,
4545 ComPtr<IProgress> &aProgress)
4546{
4547 RT_NOREF(aSource, aDestination, aFlags, aProgress);
4548 ReturnComNotImplemented();
4549}
4550
4551HRESULT GuestSession::fsObjCopyArray(const std::vector<com::Utf8Str> &aSource,
4552 const com::Utf8Str &aDestination,
4553 const std::vector<FileCopyFlag_T> &aFlags,
4554 ComPtr<IProgress> &aProgress)
4555{
4556 RT_NOREF(aSource, aDestination, aFlags, aProgress);
4557 ReturnComNotImplemented();
4558}
4559
4560HRESULT GuestSession::fsObjSetACL(const com::Utf8Str &aPath, BOOL aFollowSymlinks, const com::Utf8Str &aAcl, ULONG aMode)
4561{
4562 RT_NOREF(aPath, aFollowSymlinks, aAcl, aMode);
4563 ReturnComNotImplemented();
4564}
4565
4566
4567HRESULT GuestSession::processCreate(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
4568 const std::vector<com::Utf8Str> &aEnvironment,
4569 const std::vector<ProcessCreateFlag_T> &aFlags,
4570 ULONG aTimeoutMS, ComPtr<IGuestProcess> &aGuestProcess)
4571{
4572 LogFlowThisFuncEnter();
4573
4574 std::vector<LONG> affinityIgnored;
4575 return processCreateEx(aExecutable, aArguments, aEnvironment, aFlags, aTimeoutMS, ProcessPriority_Default,
4576 affinityIgnored, aGuestProcess);
4577}
4578
4579HRESULT GuestSession::processCreateEx(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
4580 const std::vector<com::Utf8Str> &aEnvironment,
4581 const std::vector<ProcessCreateFlag_T> &aFlags, ULONG aTimeoutMS,
4582 ProcessPriority_T aPriority, const std::vector<LONG> &aAffinity,
4583 ComPtr<IGuestProcess> &aGuestProcess)
4584{
4585 HRESULT hr = i_isStartedExternal();
4586 if (FAILED(hr))
4587 return hr;
4588
4589 /*
4590 * Must have an executable to execute. If none is given, we try use the
4591 * zero'th argument.
4592 */
4593 const char *pszExecutable = aExecutable.c_str();
4594 if (RT_UNLIKELY(pszExecutable == NULL || *pszExecutable == '\0'))
4595 {
4596 if (aArguments.size() > 0)
4597 pszExecutable = aArguments[0].c_str();
4598 if (pszExecutable == NULL || *pszExecutable == '\0')
4599 return setError(E_INVALIDARG, tr("No command to execute specified"));
4600 }
4601
4602 /* The rest of the input is being validated in i_processCreateEx(). */
4603
4604 LogFlowThisFuncEnter();
4605
4606 /*
4607 * Build the process startup info.
4608 */
4609 GuestProcessStartupInfo procInfo;
4610
4611 /* Executable and arguments. */
4612 procInfo.mExecutable = pszExecutable;
4613 if (aArguments.size())
4614 {
4615 for (size_t i = 0; i < aArguments.size(); i++)
4616 procInfo.mArguments.push_back(aArguments[i]);
4617 }
4618 else /* If no arguments were given, add the executable as argv[0] by default. */
4619 procInfo.mArguments.push_back(procInfo.mExecutable);
4620
4621 /* Combine the environment changes associated with the ones passed in by
4622 the caller, giving priority to the latter. The changes are putenv style
4623 and will be applied to the standard environment for the guest user. */
4624 int vrc = procInfo.mEnvironmentChanges.copy(mData.mEnvironmentChanges);
4625 if (RT_SUCCESS(vrc))
4626 {
4627 size_t idxError = ~(size_t)0;
4628 vrc = procInfo.mEnvironmentChanges.applyPutEnvArray(aEnvironment, &idxError);
4629 if (RT_SUCCESS(vrc))
4630 {
4631 /* Convert the flag array into a mask. */
4632 if (aFlags.size())
4633 for (size_t i = 0; i < aFlags.size(); i++)
4634 procInfo.mFlags |= aFlags[i];
4635
4636 procInfo.mTimeoutMS = aTimeoutMS;
4637
4638 /** @todo use RTCPUSET instead of archaic 64-bit variables! */
4639 if (aAffinity.size())
4640 for (size_t i = 0; i < aAffinity.size(); i++)
4641 if (aAffinity[i])
4642 procInfo.mAffinity |= (uint64_t)1 << i;
4643
4644 procInfo.mPriority = aPriority;
4645
4646 /*
4647 * Create a guest process object.
4648 */
4649 ComObjPtr<GuestProcess> pProcess;
4650 vrc = i_processCreateEx(procInfo, pProcess);
4651 if (RT_SUCCESS(vrc))
4652 {
4653 ComPtr<IGuestProcess> pIProcess;
4654 hr = pProcess.queryInterfaceTo(pIProcess.asOutParam());
4655 if (SUCCEEDED(hr))
4656 {
4657 /*
4658 * Start the process.
4659 */
4660 vrc = pProcess->i_startProcessAsync();
4661 if (RT_SUCCESS(vrc))
4662 {
4663 aGuestProcess = pIProcess;
4664
4665 LogFlowFuncLeaveRC(vrc);
4666 return S_OK;
4667 }
4668
4669 hr = setErrorVrc(vrc, tr("Failed to start guest process: %Rrc"), vrc);
4670 }
4671 }
4672 else if (vrc == VERR_GSTCTL_MAX_CID_OBJECTS_REACHED)
4673 hr = setErrorVrc(vrc, tr("Maximum number of concurrent guest processes per session (%u) reached"),
4674 VBOX_GUESTCTRL_MAX_OBJECTS);
4675 else
4676 hr = setErrorVrc(vrc, tr("Failed to create guest process object: %Rrc"), vrc);
4677 }
4678 else
4679 hr = setErrorBoth(vrc == VERR_ENV_INVALID_VAR_NAME ? E_INVALIDARG : Global::vboxStatusCodeToCOM(vrc), vrc,
4680 tr("Failed to apply environment variable '%s', index %u (%Rrc)'"),
4681 aEnvironment[idxError].c_str(), idxError, vrc);
4682 }
4683 else
4684 hr = setErrorVrc(vrc, tr("Failed to set up the environment: %Rrc"), vrc);
4685
4686 LogFlowFuncLeaveRC(vrc);
4687 return hr;
4688}
4689
4690HRESULT GuestSession::processGet(ULONG aPid, ComPtr<IGuestProcess> &aGuestProcess)
4691
4692{
4693 if (aPid == 0)
4694 return setError(E_INVALIDARG, tr("No valid process ID (PID) specified"));
4695
4696 LogFlowThisFunc(("PID=%RU32\n", aPid));
4697
4698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4699
4700 HRESULT hr = S_OK;
4701
4702 ComObjPtr<GuestProcess> pProcess;
4703 int rc = i_processGetByPID(aPid, &pProcess);
4704 if (RT_FAILURE(rc))
4705 hr = setError(E_INVALIDARG, tr("No process with PID %RU32 found"), aPid);
4706
4707 /* This will set (*aProcess) to NULL if pProgress is NULL. */
4708 HRESULT hr2 = pProcess.queryInterfaceTo(aGuestProcess.asOutParam());
4709 if (SUCCEEDED(hr))
4710 hr = hr2;
4711
4712 LogFlowThisFunc(("aProcess=%p, hr=%Rhrc\n", (IGuestProcess*)aGuestProcess, hr));
4713 return hr;
4714}
4715
4716HRESULT GuestSession::symlinkCreate(const com::Utf8Str &aSource, const com::Utf8Str &aTarget, SymlinkType_T aType)
4717{
4718 RT_NOREF(aSource, aTarget, aType);
4719 ReturnComNotImplemented();
4720}
4721
4722HRESULT GuestSession::symlinkExists(const com::Utf8Str &aSymlink, BOOL *aExists)
4723
4724{
4725 RT_NOREF(aSymlink, aExists);
4726 ReturnComNotImplemented();
4727}
4728
4729HRESULT GuestSession::symlinkRead(const com::Utf8Str &aSymlink, const std::vector<SymlinkReadFlag_T> &aFlags,
4730 com::Utf8Str &aTarget)
4731{
4732 RT_NOREF(aSymlink, aFlags, aTarget);
4733 ReturnComNotImplemented();
4734}
4735
4736HRESULT GuestSession::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
4737{
4738 /* Note: No call to i_isStartedExternal() needed here, as the session might not has been started (yet). */
4739
4740 LogFlowThisFuncEnter();
4741
4742 HRESULT hrc = S_OK;
4743
4744 /*
4745 * Note: Do not hold any locks here while waiting!
4746 */
4747 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS; GuestSessionWaitResult_T waitResult;
4748 int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &rcGuest);
4749 if (RT_SUCCESS(vrc))
4750 *aReason = waitResult;
4751 else
4752 {
4753 switch (vrc)
4754 {
4755 case VERR_GSTCTL_GUEST_ERROR:
4756 {
4757 GuestErrorInfo ge(GuestErrorInfo::Type_Session, rcGuest, mData.mSession.mName.c_str());
4758 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Waiting for guest process failed: %s"),
4759 GuestBase::getErrorAsString(ge).c_str());
4760 break;
4761 }
4762 case VERR_TIMEOUT:
4763 *aReason = GuestSessionWaitResult_Timeout;
4764 break;
4765
4766 default:
4767 {
4768 const char *pszSessionName = mData.mSession.mName.c_str();
4769 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4770 tr("Waiting for guest session \"%s\" failed: %Rrc"),
4771 pszSessionName ? pszSessionName : tr("Unnamed"), vrc);
4772 break;
4773 }
4774 }
4775 }
4776
4777 LogFlowFuncLeaveRC(vrc);
4778 return hrc;
4779}
4780
4781HRESULT GuestSession::waitForArray(const std::vector<GuestSessionWaitForFlag_T> &aWaitFor, ULONG aTimeoutMS,
4782 GuestSessionWaitResult_T *aReason)
4783{
4784 /* Note: No call to i_isStartedExternal() needed here, as the session might not has been started (yet). */
4785
4786 LogFlowThisFuncEnter();
4787
4788 /*
4789 * Note: Do not hold any locks here while waiting!
4790 */
4791 uint32_t fWaitFor = GuestSessionWaitForFlag_None;
4792 for (size_t i = 0; i < aWaitFor.size(); i++)
4793 fWaitFor |= aWaitFor[i];
4794
4795 return WaitFor(fWaitFor, aTimeoutMS, aReason);
4796}
Note: See TracBrowser for help on using the repository browser.

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