VirtualBox

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

Last change on this file since 98100 was 97980, checked in by vboxsync, 2 years ago

Guest Control/Main: Fixes for directoryCreateTemp() when aMode is being used. ticketref:21394

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

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