VirtualBox

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

Last change on this file since 98264 was 98262, checked in by vboxsync, 2 years ago

Main: rc() -> hrc()/vrc(). bugref:10223

  • 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 98262 2023-01-24 01:42:14Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Guest session handling.
4 */
5
6/*
7 * Copyright (C) 2012-2023 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 , mVrc(VINF_SUCCESS) { }
77
78 virtual ~GuestSessionTaskInternal(void) { }
79
80 /** Returns the last set result code. */
81 int vrc(void) const { return mVrc; }
82 /** Returns whether the last set result code indicates success or not. */
83 bool isOk(void) const { return RT_SUCCESS(mVrc); }
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 VBox status code. */
92 int mVrc;
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.mVrc = 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->vrc()));
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.hrc()))
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.mVrc), ("Guest rc already set (to %Rrc)\n", mData.mVrc));
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.mVrc = 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.mVrc), ("No error rc (%Rrc) set when guest session indicated an error\n", mData.mVrc));
3165 if (prcGuest)
3166 *prcGuest = mData.mVrc; /* 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", mData.mStatus, mData.mVrc, waitResult));
3244
3245 /* No waiting needed? Return immediately using the last set error. */
3246 if (waitResult != GuestSessionWaitResult_None)
3247 {
3248 if (prcGuest)
3249 *prcGuest = mData.mVrc; /* Return last set error (if any). */
3250 return RT_SUCCESS(mData.mVrc) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
3251 }
3252
3253 int vrc = VINF_SUCCESS;
3254
3255 uint64_t const tsStart = RTTimeMilliTS();
3256 uint64_t tsNow = tsStart;
3257
3258 while (tsNow - tsStart < uTimeoutMS)
3259 {
3260 GuestWaitEvent *pEvent = NULL;
3261 GuestEventTypes eventTypes;
3262 try
3263 {
3264 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
3265
3266 vrc = registerWaitEventEx(mData.mSession.mID, mData.mObjectID, eventTypes, &pEvent);
3267 }
3268 catch (std::bad_alloc &)
3269 {
3270 vrc = VERR_NO_MEMORY;
3271 }
3272
3273 if (RT_FAILURE(vrc))
3274 return vrc;
3275
3276 alock.release(); /* Release lock before waiting. */
3277
3278 GuestSessionStatus_T sessionStatus;
3279 vrc = i_waitForStatusChange(pEvent, fWaitFlags,
3280 uTimeoutMS - (tsNow - tsStart), &sessionStatus, prcGuest);
3281 if (RT_SUCCESS(vrc))
3282 {
3283 switch (sessionStatus)
3284 {
3285 case GuestSessionStatus_Started:
3286 waitResult = GuestSessionWaitResult_Start;
3287 break;
3288
3289 case GuestSessionStatus_Starting:
3290 RT_FALL_THROUGH();
3291 case GuestSessionStatus_Terminating:
3292 if (fWaitFlags & GuestSessionWaitForFlag_Status) /* Any status wanted? */
3293 waitResult = GuestSessionWaitResult_Status;
3294 /* else: Wait another round until we get the event(s) fWaitFlags defines. */
3295 break;
3296
3297 case GuestSessionStatus_Terminated:
3298 waitResult = GuestSessionWaitResult_Terminate;
3299 break;
3300
3301 case GuestSessionStatus_TimedOutKilled:
3302 RT_FALL_THROUGH();
3303 case GuestSessionStatus_TimedOutAbnormally:
3304 waitResult = GuestSessionWaitResult_Timeout;
3305 break;
3306
3307 case GuestSessionStatus_Down:
3308 waitResult = GuestSessionWaitResult_Terminate;
3309 break;
3310
3311 case GuestSessionStatus_Error:
3312 waitResult = GuestSessionWaitResult_Error;
3313 break;
3314
3315 default:
3316 waitResult = GuestSessionWaitResult_Status;
3317 break;
3318 }
3319 }
3320
3321 unregisterWaitEvent(pEvent);
3322
3323 /* Wait result not None, e.g. some result acquired or a wait error occurred? Bail out. */
3324 if ( waitResult != GuestSessionWaitResult_None
3325 || RT_FAILURE(vrc))
3326 break;
3327
3328 tsNow = RTTimeMilliTS();
3329
3330 alock.acquire(); /* Re-acquire lock before waiting for the next event. */
3331 }
3332
3333 if (tsNow - tsStart >= uTimeoutMS)
3334 {
3335 waitResult = GuestSessionWaitResult_None; /* Paranoia. */
3336 vrc = VERR_TIMEOUT;
3337 }
3338
3339 LogFlowFuncLeaveRC(vrc);
3340 return vrc;
3341}
3342
3343/**
3344 * Waits for guest session status changes.
3345 *
3346 * @returns VBox status code.
3347 * @retval VERR_GSTCTL_GUEST_ERROR on received guest error.
3348 * @retval VERR_WRONG_ORDER when an unexpected event type has been received.
3349 * @param pEvent Wait event to use for waiting.
3350 * @param fWaitFlags Wait flags to use.
3351 * @param uTimeoutMS Timeout (in ms) to wait.
3352 * @param pSessionStatus Where to return the guest session status.
3353 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
3354 * was returned. Optional.
3355 */
3356int GuestSession::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t fWaitFlags, uint32_t uTimeoutMS,
3357 GuestSessionStatus_T *pSessionStatus, int *prcGuest)
3358{
3359 RT_NOREF(fWaitFlags);
3360 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
3361
3362 VBoxEventType_T evtType;
3363 ComPtr<IEvent> pIEvent;
3364 int vrc = waitForEvent(pEvent, uTimeoutMS, &evtType, pIEvent.asOutParam());
3365 if (RT_SUCCESS(vrc))
3366 {
3367 if (evtType == VBoxEventType_OnGuestSessionStateChanged)
3368 {
3369 ComPtr<IGuestSessionStateChangedEvent> pChangedEvent = pIEvent;
3370 Assert(!pChangedEvent.isNull());
3371
3372 GuestSessionStatus_T sessionStatus;
3373 pChangedEvent->COMGETTER(Status)(&sessionStatus);
3374 if (pSessionStatus)
3375 *pSessionStatus = sessionStatus;
3376
3377 ComPtr<IVirtualBoxErrorInfo> errorInfo;
3378 HRESULT hr = pChangedEvent->COMGETTER(Error)(errorInfo.asOutParam());
3379 ComAssertComRC(hr);
3380
3381 LONG lGuestRc;
3382 hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
3383 ComAssertComRC(hr);
3384 if (RT_FAILURE((int)lGuestRc))
3385 vrc = VERR_GSTCTL_GUEST_ERROR;
3386 if (prcGuest)
3387 *prcGuest = (int)lGuestRc;
3388
3389 LogFlowThisFunc(("Status changed event for session ID=%RU32, new status is: %RU32 (%Rrc)\n",
3390 mData.mSession.mID, sessionStatus,
3391 RT_SUCCESS((int)lGuestRc) ? VINF_SUCCESS : (int)lGuestRc));
3392 }
3393 else /** @todo Re-visit this. Can this happen more frequently? */
3394 AssertMsgFailedReturn(("Got unexpected event type %#x\n", evtType), VERR_WRONG_ORDER);
3395 }
3396 /* waitForEvent may also return VERR_GSTCTL_GUEST_ERROR like we do above, so make prcGuest is set. */
3397 else if (vrc == VERR_GSTCTL_GUEST_ERROR && prcGuest)
3398 *prcGuest = pEvent->GuestResult();
3399 Assert(vrc != VERR_GSTCTL_GUEST_ERROR || !prcGuest || *prcGuest != (int)0xcccccccc);
3400
3401 LogFlowFuncLeaveRC(vrc);
3402 return vrc;
3403}
3404
3405// implementation of public methods
3406/////////////////////////////////////////////////////////////////////////////
3407
3408HRESULT GuestSession::close()
3409{
3410 LogFlowThisFuncEnter();
3411
3412 /* Note: Don't check if the session is ready via i_isStartedExternal() here;
3413 * the session (already) could be in a stopped / aborted state. */
3414
3415 int vrc = VINF_SUCCESS; /* Shut up MSVC. */
3416 int rcGuest = VINF_SUCCESS;
3417
3418 uint32_t msTimeout = RT_MS_10SEC; /* 10s timeout by default */
3419 for (int i = 0; i < 3; i++)
3420 {
3421 if (i)
3422 {
3423 LogRel(("Guest Control: Closing session '%s' timed out (%RU32s timeout, attempt %d/10), retrying ...\n",
3424 mData.mSession.mName.c_str(), msTimeout / RT_MS_1SEC, i + 1));
3425 msTimeout += RT_MS_5SEC; /* Slightly increase the timeout. */
3426 }
3427
3428 /* Close session on guest. */
3429 vrc = i_closeSession(0 /* Flags */, msTimeout, &rcGuest);
3430 if ( RT_SUCCESS(vrc)
3431 || vrc != VERR_TIMEOUT) /* If something else happened there is no point in retrying further. */
3432 break;
3433 }
3434
3435 /* On failure don't return here, instead do all the cleanup
3436 * work first and then return an error. */
3437
3438 /* Destroy session + remove ourselves from the session list. */
3439 AssertPtr(mParent);
3440 int vrc2 = mParent->i_sessionDestroy(mData.mSession.mID);
3441 if (vrc2 == VERR_NOT_FOUND) /* Not finding the session anymore isn't critical. */
3442 vrc2 = VINF_SUCCESS;
3443
3444 if (RT_SUCCESS(vrc))
3445 vrc = vrc2;
3446
3447 LogFlowThisFunc(("Returning rc=%Rrc, rcGuest=%Rrc\n", vrc, rcGuest));
3448
3449 if (RT_FAILURE(vrc))
3450 {
3451 if (vrc == VERR_GSTCTL_GUEST_ERROR)
3452 {
3453 GuestErrorInfo ge(GuestErrorInfo::Type_Session, rcGuest, mData.mSession.mName.c_str());
3454 return setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Closing guest session failed: %s"),
3455 GuestBase::getErrorAsString(ge).c_str());
3456 }
3457 return setError(VBOX_E_IPRT_ERROR, tr("Closing guest session \"%s\" failed with %Rrc"),
3458 mData.mSession.mName.c_str(), vrc);
3459 }
3460
3461 return S_OK;
3462}
3463
3464HRESULT GuestSession::fileCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3465 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3466{
3467 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3468 ReturnComNotImplemented();
3469}
3470
3471HRESULT GuestSession::fileCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3472 const std::vector<FileCopyFlag_T> &aFlags,
3473 ComPtr<IProgress> &aProgress)
3474{
3475 uint32_t fFlags = FileCopyFlag_None;
3476 if (aFlags.size())
3477 {
3478 for (size_t i = 0; i < aFlags.size(); i++)
3479 fFlags |= aFlags[i];
3480
3481 const uint32_t fValidFlags = FileCopyFlag_None | FileCopyFlag_NoReplace | FileCopyFlag_FollowLinks | FileCopyFlag_Update;
3482 if (fFlags & ~fValidFlags)
3483 return setError(E_INVALIDARG,tr("Unknown flags: flags value %#x, invalid: %#x"), fFlags, fFlags & ~fValidFlags);
3484 }
3485
3486 GuestSessionFsSourceSet SourceSet;
3487
3488 GuestSessionFsSourceSpec source;
3489 source.strSource = aSource;
3490 source.enmType = FsObjType_File;
3491 source.enmPathStyle = i_getGuestPathStyle();
3492 source.fDryRun = false; /** @todo Implement support for a dry run. */
3493 source.fDirCopyFlags = DirectoryCopyFlag_None;
3494 source.fFileCopyFlags = (FileCopyFlag_T)fFlags;
3495
3496 SourceSet.push_back(source);
3497
3498 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3499}
3500
3501HRESULT GuestSession::fileCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3502 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3503{
3504 uint32_t fFlags = FileCopyFlag_None;
3505 if (aFlags.size())
3506 {
3507 for (size_t i = 0; i < aFlags.size(); i++)
3508 fFlags |= aFlags[i];
3509
3510 const uint32_t fValidFlags = FileCopyFlag_None | FileCopyFlag_NoReplace | FileCopyFlag_FollowLinks | FileCopyFlag_Update;
3511 if (fFlags & ~fValidFlags)
3512 return setError(E_INVALIDARG,tr("Unknown flags: flags value %#x, invalid: %#x"), fFlags, fFlags & ~fValidFlags);
3513 }
3514
3515 GuestSessionFsSourceSet SourceSet;
3516
3517 GuestSessionFsSourceSpec source;
3518 source.strSource = aSource;
3519 source.enmType = FsObjType_File;
3520 source.enmPathStyle = GuestSession::i_getHostPathStyle();
3521 source.fDryRun = false; /** @todo Implement support for a dry run. */
3522 source.fDirCopyFlags = DirectoryCopyFlag_None;
3523 source.fFileCopyFlags = (FileCopyFlag_T)fFlags;
3524
3525 SourceSet.push_back(source);
3526
3527 return i_copyToGuest(SourceSet, aDestination, aProgress);
3528}
3529
3530HRESULT GuestSession::copyFromGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
3531 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
3532 ComPtr<IProgress> &aProgress)
3533{
3534 const size_t cSources = aSources.size();
3535 if ( (aFilters.size() && aFilters.size() != cSources)
3536 || (aFlags.size() && aFlags.size() != cSources))
3537 {
3538 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
3539 }
3540
3541 GuestSessionFsSourceSet SourceSet;
3542
3543 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
3544 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
3545 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
3546
3547 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
3548 const bool fFollowSymlinks = true; /** @todo Ditto. */
3549
3550 while (itSource != aSources.end())
3551 {
3552 GuestFsObjData objData;
3553 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3554 int vrc = i_fsQueryInfo(*(itSource), fFollowSymlinks, objData, &rcGuest);
3555 if ( RT_FAILURE(vrc)
3556 && !fContinueOnErrors)
3557 {
3558 if (GuestProcess::i_isGuestError(vrc))
3559 {
3560 GuestErrorInfo ge(GuestErrorInfo::Type_Process, rcGuest, (*itSource).c_str());
3561 return setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying type for guest source failed: %s"),
3562 GuestBase::getErrorAsString(ge).c_str());
3563 }
3564 return setError(E_FAIL, tr("Querying type for guest source \"%s\" failed: %Rrc"), (*itSource).c_str(), vrc);
3565 }
3566
3567 Utf8Str strFlags;
3568 if (itFlags != aFlags.end())
3569 {
3570 strFlags = *itFlags;
3571 ++itFlags;
3572 }
3573
3574 Utf8Str strFilter;
3575 if (itFilter != aFilters.end())
3576 {
3577 strFilter = *itFilter;
3578 ++itFilter;
3579 }
3580
3581 GuestSessionFsSourceSpec source;
3582 source.strSource = *itSource;
3583 source.strFilter = strFilter;
3584 source.enmType = objData.mType;
3585 source.enmPathStyle = i_getGuestPathStyle();
3586 source.fDryRun = false; /** @todo Implement support for a dry run. */
3587
3588 /* Check both flag groups here, as copying a directory also could mean to explicitly
3589 * *not* replacing any existing files (or just copy files which are newer, for instance). */
3590 GuestSession::i_directoryCopyFlagFromStr(strFlags, false /* fStrict */, &source.fDirCopyFlags);
3591 GuestSession::i_fileCopyFlagFromStr(strFlags, false /* fStrict */, &source.fFileCopyFlags);
3592
3593 SourceSet.push_back(source);
3594
3595 ++itSource;
3596 }
3597
3598 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3599}
3600
3601HRESULT GuestSession::copyToGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
3602 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
3603 ComPtr<IProgress> &aProgress)
3604{
3605 const size_t cSources = aSources.size();
3606 if ( (aFilters.size() && aFilters.size() != cSources)
3607 || (aFlags.size() && aFlags.size() != cSources))
3608 {
3609 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
3610 }
3611
3612 GuestSessionFsSourceSet SourceSet;
3613
3614 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
3615 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
3616 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
3617
3618 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
3619
3620 while (itSource != aSources.end())
3621 {
3622 RTFSOBJINFO objInfo;
3623 int vrc = RTPathQueryInfo((*itSource).c_str(), &objInfo, RTFSOBJATTRADD_NOTHING);
3624 if ( RT_FAILURE(vrc)
3625 && !fContinueOnErrors)
3626 {
3627 return setError(E_FAIL, tr("Unable to query type for source '%s' (%Rrc)"), (*itSource).c_str(), vrc);
3628 }
3629
3630 Utf8Str strFlags;
3631 if (itFlags != aFlags.end())
3632 {
3633 strFlags = *itFlags;
3634 ++itFlags;
3635 }
3636
3637 Utf8Str strFilter;
3638 if (itFilter != aFilters.end())
3639 {
3640 strFilter = *itFilter;
3641 ++itFilter;
3642 }
3643
3644 GuestSessionFsSourceSpec source;
3645 source.strSource = *itSource;
3646 source.strFilter = strFilter;
3647 source.enmType = GuestBase::fileModeToFsObjType(objInfo.Attr.fMode);
3648 source.enmPathStyle = GuestSession::i_getHostPathStyle();
3649 source.fDryRun = false; /** @todo Implement support for a dry run. */
3650
3651 GuestSession::i_directoryCopyFlagFromStr(strFlags, false /* fStrict */, &source.fDirCopyFlags);
3652 GuestSession::i_fileCopyFlagFromStr(strFlags, false /* fStrict */, &source.fFileCopyFlags);
3653
3654 SourceSet.push_back(source);
3655
3656 ++itSource;
3657 }
3658
3659 /* (Re-)Validate stuff. */
3660 if (RT_UNLIKELY(SourceSet.size() == 0)) /* At least one source must be present. */
3661 return setError(E_INVALIDARG, tr("No sources specified"));
3662 if (RT_UNLIKELY(SourceSet[0].strSource.isEmpty()))
3663 return setError(E_INVALIDARG, tr("First source entry is empty"));
3664 if (RT_UNLIKELY(aDestination.isEmpty()))
3665 return setError(E_INVALIDARG, tr("No destination specified"));
3666
3667 return i_copyToGuest(SourceSet, aDestination, aProgress);
3668}
3669
3670HRESULT GuestSession::directoryCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3671 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3672{
3673 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3674 ReturnComNotImplemented();
3675}
3676
3677HRESULT GuestSession::directoryCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3678 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3679{
3680 uint32_t fFlags = DirectoryCopyFlag_None;
3681 if (aFlags.size())
3682 {
3683 for (size_t i = 0; i < aFlags.size(); i++)
3684 fFlags |= aFlags[i];
3685
3686 const uint32_t fValidFlags = DirectoryCopyFlag_None | DirectoryCopyFlag_CopyIntoExisting | DirectoryCopyFlag_Recursive
3687 | DirectoryCopyFlag_FollowLinks;
3688 if (fFlags & ~fValidFlags)
3689 return setError(E_INVALIDARG,tr("Unknown flags: flags value %#x, invalid: %#x"), fFlags, fFlags & ~fValidFlags);
3690 }
3691
3692 GuestSessionFsSourceSet SourceSet;
3693
3694 GuestSessionFsSourceSpec source;
3695 source.strSource = aSource;
3696 source.enmType = FsObjType_Directory;
3697 source.enmPathStyle = i_getGuestPathStyle();
3698 source.fDryRun = false; /** @todo Implement support for a dry run. */
3699 source.fDirCopyFlags = (DirectoryCopyFlag_T)fFlags;
3700 source.fFileCopyFlags = FileCopyFlag_None; /* Overwrite existing files. */
3701
3702 SourceSet.push_back(source);
3703
3704 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3705}
3706
3707HRESULT GuestSession::directoryCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3708 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3709{
3710 uint32_t fFlags = DirectoryCopyFlag_None;
3711 if (aFlags.size())
3712 {
3713 for (size_t i = 0; i < aFlags.size(); i++)
3714 fFlags |= aFlags[i];
3715
3716 const uint32_t fValidFlags = DirectoryCopyFlag_None | DirectoryCopyFlag_CopyIntoExisting | DirectoryCopyFlag_Recursive
3717 | DirectoryCopyFlag_FollowLinks;
3718 if (fFlags & ~fValidFlags)
3719 return setError(E_INVALIDARG,tr("Unknown flags: flags value %#x, invalid: %#x"), fFlags, fFlags & ~fValidFlags);
3720 }
3721
3722 GuestSessionFsSourceSet SourceSet;
3723
3724 GuestSessionFsSourceSpec source;
3725 source.strSource = aSource;
3726 source.enmType = FsObjType_Directory;
3727 source.enmPathStyle = GuestSession::i_getHostPathStyle();
3728 source.fDryRun = false; /** @todo Implement support for a dry run. */
3729 source.fDirCopyFlags = (DirectoryCopyFlag_T)fFlags;
3730 source.fFileCopyFlags = FileCopyFlag_None; /* Overwrite existing files. */
3731
3732 SourceSet.push_back(source);
3733
3734 return i_copyToGuest(SourceSet, aDestination, aProgress);
3735}
3736
3737HRESULT GuestSession::directoryCreate(const com::Utf8Str &aPath, ULONG aMode,
3738 const std::vector<DirectoryCreateFlag_T> &aFlags)
3739{
3740 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3741 return setError(E_INVALIDARG, tr("No directory to create specified"));
3742
3743 uint32_t fFlags = DirectoryCreateFlag_None;
3744 if (aFlags.size())
3745 {
3746 for (size_t i = 0; i < aFlags.size(); i++)
3747 fFlags |= aFlags[i];
3748
3749 if (fFlags & ~DirectoryCreateFlag_Parents)
3750 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), fFlags);
3751 }
3752
3753 HRESULT hrc = i_isStartedExternal();
3754 if (FAILED(hrc))
3755 return hrc;
3756
3757 LogFlowThisFuncEnter();
3758
3759 ComObjPtr <GuestDirectory> pDirectory; int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3760 int vrc = i_directoryCreate(aPath, (uint32_t)aMode, fFlags, &rcGuest);
3761 if (RT_FAILURE(vrc))
3762 {
3763 if (GuestProcess::i_isGuestError(vrc))
3764 {
3765 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, rcGuest, aPath.c_str());
3766 return setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Guest directory creation failed: %s"),
3767 GuestBase::getErrorAsString(ge).c_str());
3768 }
3769 switch (vrc)
3770 {
3771 case VERR_INVALID_PARAMETER:
3772 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Guest directory creation failed: Invalid parameters given"));
3773 break;
3774
3775 case VERR_BROKEN_PIPE:
3776 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Guest directory creation failed: Unexpectedly aborted"));
3777 break;
3778
3779 default:
3780 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Guest directory creation failed: %Rrc"), vrc);
3781 break;
3782 }
3783 }
3784
3785 return hrc;
3786}
3787
3788HRESULT GuestSession::directoryCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath,
3789 BOOL aSecure, com::Utf8Str &aDirectory)
3790{
3791 if (RT_UNLIKELY((aTemplateName.c_str()) == NULL || *(aTemplateName.c_str()) == '\0'))
3792 return setError(E_INVALIDARG, tr("No template specified"));
3793 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3794 return setError(E_INVALIDARG, tr("No directory name specified"));
3795 if (!aSecure) /* Ignore what mode is specified when a secure temp thing needs to be created. */
3796 if (RT_UNLIKELY(aMode & ~07777))
3797 return setError(E_INVALIDARG, tr("Mode invalid (must be specified in octal mode)"));
3798
3799 HRESULT hrc = i_isStartedExternal();
3800 if (FAILED(hrc))
3801 return hrc;
3802
3803 LogFlowThisFuncEnter();
3804
3805 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3806 int vrc = i_fsCreateTemp(aTemplateName, aPath, true /* Directory */, aDirectory, aMode, RT_BOOL(aSecure), &rcGuest);
3807 if (!RT_SUCCESS(vrc))
3808 {
3809 switch (vrc)
3810 {
3811 case VERR_GSTCTL_GUEST_ERROR:
3812 {
3813 GuestErrorInfo ge(GuestErrorInfo::Type_ToolMkTemp, rcGuest, aPath.c_str());
3814 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Temporary guest directory creation failed: %s"),
3815 GuestBase::getErrorAsString(ge).c_str());
3816 break;
3817 }
3818 default:
3819 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Temporary guest directory creation \"%s\" with template \"%s\" failed: %Rrc"),
3820 aPath.c_str(), aTemplateName.c_str(), vrc);
3821 break;
3822 }
3823 }
3824
3825 return hrc;
3826}
3827
3828HRESULT GuestSession::directoryExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3829{
3830 if (RT_UNLIKELY(aPath.isEmpty()))
3831 return setError(E_INVALIDARG, tr("Empty path"));
3832
3833 HRESULT hrc = i_isStartedExternal();
3834 if (FAILED(hrc))
3835 return hrc;
3836
3837 LogFlowThisFuncEnter();
3838
3839 GuestFsObjData objData;
3840 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3841
3842 int vrc = i_directoryQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3843 if (RT_SUCCESS(vrc))
3844 *aExists = TRUE;
3845 else
3846 {
3847 switch (vrc)
3848 {
3849 case VERR_GSTCTL_GUEST_ERROR:
3850 {
3851 switch (rcGuest)
3852 {
3853 case VERR_PATH_NOT_FOUND:
3854 *aExists = FALSE;
3855 break;
3856 default:
3857 {
3858 GuestErrorInfo ge(GuestErrorInfo::Type_ToolStat, rcGuest, aPath.c_str());
3859 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying directory existence failed: %s"),
3860 GuestBase::getErrorAsString(ge).c_str());
3861 break;
3862 }
3863 }
3864 break;
3865 }
3866
3867 case VERR_NOT_A_DIRECTORY:
3868 {
3869 *aExists = FALSE;
3870 break;
3871 }
3872
3873 default:
3874 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying directory existence \"%s\" failed: %Rrc"),
3875 aPath.c_str(), vrc);
3876 break;
3877 }
3878 }
3879
3880 return hrc;
3881}
3882
3883HRESULT GuestSession::directoryOpen(const com::Utf8Str &aPath, const com::Utf8Str &aFilter,
3884 const std::vector<DirectoryOpenFlag_T> &aFlags, ComPtr<IGuestDirectory> &aDirectory)
3885{
3886 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3887 return setError(E_INVALIDARG, tr("No directory to open specified"));
3888 if (RT_UNLIKELY((aFilter.c_str()) != NULL && *(aFilter.c_str()) != '\0'))
3889 return setError(E_INVALIDARG, tr("Directory filters are not implemented yet"));
3890
3891 uint32_t fFlags = DirectoryOpenFlag_None;
3892 if (aFlags.size())
3893 {
3894 for (size_t i = 0; i < aFlags.size(); i++)
3895 fFlags |= aFlags[i];
3896
3897 if (fFlags)
3898 return setError(E_INVALIDARG, tr("Open flags (%#x) not implemented yet"), fFlags);
3899 }
3900
3901 HRESULT hrc = i_isStartedExternal();
3902 if (FAILED(hrc))
3903 return hrc;
3904
3905 LogFlowThisFuncEnter();
3906
3907 GuestDirectoryOpenInfo openInfo;
3908 openInfo.mPath = aPath;
3909 openInfo.mFilter = aFilter;
3910 openInfo.mFlags = fFlags;
3911
3912 ComObjPtr<GuestDirectory> pDirectory; int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3913 int vrc = i_directoryOpen(openInfo, pDirectory, &rcGuest);
3914 if (RT_SUCCESS(vrc))
3915 {
3916 /* Return directory object to the caller. */
3917 hrc = pDirectory.queryInterfaceTo(aDirectory.asOutParam());
3918 }
3919 else
3920 {
3921 switch (vrc)
3922 {
3923 case VERR_INVALID_PARAMETER:
3924 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest directory \"%s\" failed; invalid parameters given"),
3925 aPath.c_str());
3926 break;
3927
3928 case VERR_GSTCTL_GUEST_ERROR:
3929 {
3930 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, rcGuest, aPath.c_str());
3931 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Opening guest directory failed: %s"),
3932 GuestBase::getErrorAsString(ge).c_str());
3933 break;
3934 }
3935 default:
3936 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3937 break;
3938 }
3939 }
3940
3941 return hrc;
3942}
3943
3944HRESULT GuestSession::directoryRemove(const com::Utf8Str &aPath)
3945{
3946 if (RT_UNLIKELY(aPath.c_str() == NULL || *aPath.c_str() == '\0'))
3947 return setError(E_INVALIDARG, tr("No directory to remove specified"));
3948
3949 HRESULT hrc = i_isStartedExternal();
3950 if (FAILED(hrc))
3951 return hrc;
3952
3953 LogFlowThisFuncEnter();
3954
3955 /* No flags; only remove the directory when empty. */
3956 uint32_t fFlags = DIRREMOVEREC_FLAG_NONE;
3957
3958 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3959 int vrc = i_directoryRemove(aPath, fFlags, &rcGuest);
3960 if (RT_FAILURE(vrc))
3961 {
3962 switch (vrc)
3963 {
3964 case VERR_NOT_SUPPORTED:
3965 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3966 tr("Handling removing guest directories not supported by installed Guest Additions"));
3967 break;
3968
3969 case VERR_GSTCTL_GUEST_ERROR:
3970 {
3971 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, rcGuest, aPath.c_str());
3972 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Removing guest directory failed: %s"),
3973 GuestBase::getErrorAsString(ge).c_str());
3974 break;
3975 }
3976 default:
3977 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing guest directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3978 break;
3979 }
3980 }
3981
3982 return hrc;
3983}
3984
3985HRESULT GuestSession::directoryRemoveRecursive(const com::Utf8Str &aPath, const std::vector<DirectoryRemoveRecFlag_T> &aFlags,
3986 ComPtr<IProgress> &aProgress)
3987{
3988 if (RT_UNLIKELY(aPath.c_str() == NULL || *aPath.c_str() == '\0'))
3989 return setError(E_INVALIDARG, tr("No directory to remove recursively specified"));
3990
3991 /* By defautl remove recursively as the function name implies. */
3992 uint32_t fFlags = DIRREMOVEREC_FLAG_RECURSIVE;
3993 if (aFlags.size())
3994 {
3995 for (size_t i = 0; i < aFlags.size(); i++)
3996 {
3997 switch (aFlags[i])
3998 {
3999 case DirectoryRemoveRecFlag_None: /* Skip. */
4000 continue;
4001
4002 case DirectoryRemoveRecFlag_ContentAndDir:
4003 fFlags |= DIRREMOVEREC_FLAG_CONTENT_AND_DIR;
4004 break;
4005
4006 case DirectoryRemoveRecFlag_ContentOnly:
4007 fFlags |= DIRREMOVEREC_FLAG_CONTENT_ONLY;
4008 break;
4009
4010 default:
4011 return setError(E_INVALIDARG, tr("Invalid flags specified"));
4012 }
4013 }
4014 }
4015
4016 HRESULT hrc = i_isStartedExternal();
4017 if (FAILED(hrc))
4018 return hrc;
4019
4020 LogFlowThisFuncEnter();
4021
4022 ComObjPtr<Progress> pProgress;
4023 hrc = pProgress.createObject();
4024 if (SUCCEEDED(hrc))
4025 hrc = pProgress->init(static_cast<IGuestSession *>(this),
4026 Bstr(tr("Removing guest directory")).raw(),
4027 TRUE /*aCancelable*/);
4028 if (FAILED(hrc))
4029 return hrc;
4030
4031 /* Note: At the moment we don't supply progress information while
4032 * deleting a guest directory recursively. So just complete
4033 * the progress object right now. */
4034 /** @todo Implement progress reporting on guest directory deletion! */
4035 hrc = pProgress->i_notifyComplete(S_OK);
4036 if (FAILED(hrc))
4037 return hrc;
4038
4039 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4040 int vrc = i_directoryRemove(aPath, fFlags, &rcGuest);
4041 if (RT_FAILURE(vrc))
4042 {
4043 switch (vrc)
4044 {
4045 case VERR_NOT_SUPPORTED:
4046 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4047 tr("Handling removing guest directories recursively not supported by installed Guest Additions"));
4048 break;
4049
4050 case VERR_GSTCTL_GUEST_ERROR:
4051 {
4052 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, rcGuest, aPath.c_str());
4053 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Recursively removing guest directory failed: %s"),
4054 GuestBase::getErrorAsString(ge).c_str());
4055 break;
4056 }
4057 default:
4058 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Recursively removing guest directory \"%s\" failed: %Rrc"),
4059 aPath.c_str(), vrc);
4060 break;
4061 }
4062 }
4063 else
4064 {
4065 pProgress.queryInterfaceTo(aProgress.asOutParam());
4066 }
4067
4068 return hrc;
4069}
4070
4071HRESULT GuestSession::environmentScheduleSet(const com::Utf8Str &aName, const com::Utf8Str &aValue)
4072{
4073 LogFlowThisFuncEnter();
4074 int vrc;
4075 {
4076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4077 vrc = mData.mEnvironmentChanges.setVariable(aName, aValue);
4078 }
4079 HRESULT hrc;
4080 if (RT_SUCCESS(vrc))
4081 hrc = S_OK;
4082 else if (vrc == VERR_ENV_INVALID_VAR_NAME)
4083 hrc = setError(E_INVALIDARG, tr("Invalid environment variable name '%s'"), aName.c_str());
4084 else
4085 hrc = setErrorVrc(vrc, tr("Failed to schedule setting environment variable '%s' to '%s'"), aName.c_str(), aValue.c_str());
4086
4087 LogFlowThisFuncLeave();
4088 return hrc;
4089}
4090
4091HRESULT GuestSession::environmentScheduleUnset(const com::Utf8Str &aName)
4092{
4093 LogFlowThisFuncEnter();
4094 int vrc;
4095 {
4096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4097 vrc = mData.mEnvironmentChanges.unsetVariable(aName);
4098 }
4099 HRESULT hrc;
4100 if (RT_SUCCESS(vrc))
4101 hrc = S_OK;
4102 else if (vrc == VERR_ENV_INVALID_VAR_NAME)
4103 hrc = setError(E_INVALIDARG, tr("Invalid environment variable name '%s'"), aName.c_str());
4104 else
4105 hrc = setErrorVrc(vrc, tr("Failed to schedule unsetting environment variable '%s'"), aName.c_str());
4106
4107 LogFlowThisFuncLeave();
4108 return hrc;
4109}
4110
4111HRESULT GuestSession::environmentGetBaseVariable(const com::Utf8Str &aName, com::Utf8Str &aValue)
4112{
4113 LogFlowThisFuncEnter();
4114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4115
4116 HRESULT hrc;
4117 if (mData.mpBaseEnvironment)
4118 {
4119 int vrc = mData.mpBaseEnvironment->getVariable(aName, &aValue);
4120 if (RT_SUCCESS(vrc))
4121 hrc = S_OK;
4122 else if (vrc == VERR_ENV_INVALID_VAR_NAME)
4123 hrc = setError(E_INVALIDARG, tr("Invalid environment variable name '%s'"), aName.c_str());
4124 else
4125 hrc = setErrorVrc(vrc);
4126 }
4127 else if (mData.mProtocolVersion < 99999)
4128 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the Guest Additions"));
4129 else
4130 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
4131
4132 LogFlowThisFuncLeave();
4133 return hrc;
4134}
4135
4136HRESULT GuestSession::environmentDoesBaseVariableExist(const com::Utf8Str &aName, BOOL *aExists)
4137{
4138 LogFlowThisFuncEnter();
4139 *aExists = FALSE;
4140 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4141
4142 HRESULT hrc;
4143 if (mData.mpBaseEnvironment)
4144 {
4145 hrc = S_OK;
4146 *aExists = mData.mpBaseEnvironment->doesVariableExist(aName);
4147 }
4148 else if (mData.mProtocolVersion < 99999)
4149 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the Guest Additions"));
4150 else
4151 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
4152
4153 LogFlowThisFuncLeave();
4154 return hrc;
4155}
4156
4157HRESULT GuestSession::fileCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath, BOOL aSecure,
4158 ComPtr<IGuestFile> &aFile)
4159{
4160 RT_NOREF(aTemplateName, aMode, aPath, aSecure, aFile);
4161 ReturnComNotImplemented();
4162}
4163
4164HRESULT GuestSession::fileExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
4165{
4166 /* By default we return non-existent. */
4167 *aExists = FALSE;
4168
4169 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
4170 return S_OK;
4171
4172 HRESULT hrc = i_isStartedExternal();
4173 if (FAILED(hrc))
4174 return hrc;
4175
4176 LogFlowThisFuncEnter();
4177
4178 GuestFsObjData objData; int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4179 int vrc = i_fileQueryInfo(aPath, RT_BOOL(aFollowSymlinks), objData, &rcGuest);
4180 if (RT_SUCCESS(vrc))
4181 {
4182 *aExists = TRUE;
4183 return S_OK;
4184 }
4185
4186 switch (vrc)
4187 {
4188 case VERR_GSTCTL_GUEST_ERROR:
4189 {
4190 switch (rcGuest)
4191 {
4192 case VERR_PATH_NOT_FOUND:
4193 RT_FALL_THROUGH();
4194 case VERR_FILE_NOT_FOUND:
4195 break;
4196
4197 default:
4198 {
4199 GuestErrorInfo ge(GuestErrorInfo::Type_ToolStat, rcGuest, aPath.c_str());
4200 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying guest file existence failed: %s"),
4201 GuestBase::getErrorAsString(ge).c_str());
4202 break;
4203 }
4204 }
4205
4206 break;
4207 }
4208
4209 case VERR_NOT_A_FILE:
4210 break;
4211
4212 default:
4213 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying guest file information for \"%s\" failed: %Rrc"),
4214 aPath.c_str(), vrc);
4215 break;
4216 }
4217
4218 return hrc;
4219}
4220
4221HRESULT GuestSession::fileOpen(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
4222 ULONG aCreationMode, ComPtr<IGuestFile> &aFile)
4223{
4224 LogFlowThisFuncEnter();
4225
4226 const std::vector<FileOpenExFlag_T> EmptyFlags;
4227 return fileOpenEx(aPath, aAccessMode, aOpenAction, FileSharingMode_All, aCreationMode, EmptyFlags, aFile);
4228}
4229
4230HRESULT GuestSession::fileOpenEx(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
4231 FileSharingMode_T aSharingMode, ULONG aCreationMode,
4232 const std::vector<FileOpenExFlag_T> &aFlags, ComPtr<IGuestFile> &aFile)
4233{
4234 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
4235 return setError(E_INVALIDARG, tr("No file to open specified"));
4236
4237 HRESULT hrc = i_isStartedExternal();
4238 if (FAILED(hrc))
4239 return hrc;
4240
4241 LogFlowThisFuncEnter();
4242
4243 /* Validate aAccessMode. */
4244 switch (aAccessMode)
4245 {
4246 case FileAccessMode_ReadOnly:
4247 RT_FALL_THRU();
4248 case FileAccessMode_WriteOnly:
4249 RT_FALL_THRU();
4250 case FileAccessMode_ReadWrite:
4251 break;
4252 case FileAccessMode_AppendOnly:
4253 RT_FALL_THRU();
4254 case FileAccessMode_AppendRead:
4255 return setError(E_NOTIMPL, tr("Append access modes are not yet implemented"));
4256 default:
4257 return setError(E_INVALIDARG, tr("Unknown FileAccessMode value %u (%#x)"), aAccessMode, aAccessMode);
4258 }
4259
4260 /* Validate aOpenAction to the old format. */
4261 switch (aOpenAction)
4262 {
4263 case FileOpenAction_OpenExisting:
4264 RT_FALL_THRU();
4265 case FileOpenAction_OpenOrCreate:
4266 RT_FALL_THRU();
4267 case FileOpenAction_CreateNew:
4268 RT_FALL_THRU();
4269 case FileOpenAction_CreateOrReplace:
4270 RT_FALL_THRU();
4271 case FileOpenAction_OpenExistingTruncated:
4272 RT_FALL_THRU();
4273 case FileOpenAction_AppendOrCreate:
4274 break;
4275 default:
4276 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
4277 }
4278
4279 /* Validate aSharingMode. */
4280 switch (aSharingMode)
4281 {
4282 case FileSharingMode_All:
4283 break;
4284 case FileSharingMode_Read:
4285 case FileSharingMode_Write:
4286 case FileSharingMode_ReadWrite:
4287 case FileSharingMode_Delete:
4288 case FileSharingMode_ReadDelete:
4289 case FileSharingMode_WriteDelete:
4290 return setError(E_NOTIMPL, tr("Only FileSharingMode_All is currently implemented"));
4291
4292 default:
4293 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
4294 }
4295
4296 /* Combine and validate flags. */
4297 uint32_t fOpenEx = 0;
4298 for (size_t i = 0; i < aFlags.size(); i++)
4299 fOpenEx |= aFlags[i];
4300 if (fOpenEx)
4301 return setError(E_INVALIDARG, tr("Unsupported FileOpenExFlag value(s) in aFlags (%#x)"), fOpenEx);
4302
4303 ComObjPtr <GuestFile> pFile;
4304 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4305 int vrc = i_fileOpenEx(aPath, aAccessMode, aOpenAction, aSharingMode, aCreationMode, aFlags, pFile, &rcGuest);
4306 if (RT_SUCCESS(vrc))
4307 /* Return directory object to the caller. */
4308 hrc = pFile.queryInterfaceTo(aFile.asOutParam());
4309 else
4310 {
4311 switch (vrc)
4312 {
4313 case VERR_NOT_SUPPORTED:
4314 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4315 tr("Handling guest files not supported by installed Guest Additions"));
4316 break;
4317
4318 case VERR_GSTCTL_GUEST_ERROR:
4319 {
4320 GuestErrorInfo ge(GuestErrorInfo::Type_File, rcGuest, aPath.c_str());
4321 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Opening guest file failed: %s"),
4322 GuestBase::getErrorAsString(ge).c_str());
4323 break;
4324 }
4325 default:
4326 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4327 break;
4328 }
4329 }
4330
4331 return hrc;
4332}
4333
4334HRESULT GuestSession::fileQuerySize(const com::Utf8Str &aPath, BOOL aFollowSymlinks, LONG64 *aSize)
4335{
4336 if (aPath.isEmpty())
4337 return setError(E_INVALIDARG, tr("No path specified"));
4338
4339 HRESULT hrc = i_isStartedExternal();
4340 if (FAILED(hrc))
4341 return hrc;
4342
4343 int64_t llSize; int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4344 int vrc = i_fileQuerySize(aPath, aFollowSymlinks != FALSE, &llSize, &rcGuest);
4345 if (RT_SUCCESS(vrc))
4346 {
4347 *aSize = llSize;
4348 }
4349 else
4350 {
4351 if (GuestProcess::i_isGuestError(vrc))
4352 {
4353 GuestErrorInfo ge(GuestErrorInfo::Type_ToolStat, rcGuest, aPath.c_str());
4354 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying guest file size failed: %s"),
4355 GuestBase::getErrorAsString(ge).c_str());
4356 }
4357 else
4358 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying guest file size of \"%s\" failed: %Rrc"),
4359 vrc, aPath.c_str());
4360 }
4361
4362 return hrc;
4363}
4364
4365HRESULT GuestSession::fsQueryFreeSpace(const com::Utf8Str &aPath, LONG64 *aFreeSpace)
4366{
4367 RT_NOREF(aPath, aFreeSpace);
4368
4369 return E_NOTIMPL;
4370}
4371
4372HRESULT GuestSession::fsQueryInfo(const com::Utf8Str &aPath, ComPtr<IGuestFsInfo> &aInfo)
4373{
4374 RT_NOREF(aPath, aInfo);
4375
4376 return E_NOTIMPL;
4377}
4378
4379HRESULT GuestSession::fsObjExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
4380{
4381 if (aPath.isEmpty())
4382 return setError(E_INVALIDARG, tr("No path specified"));
4383
4384 HRESULT hrc = i_isStartedExternal();
4385 if (FAILED(hrc))
4386 return hrc;
4387
4388 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
4389
4390 *aExists = false;
4391
4392 GuestFsObjData objData;
4393 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4394 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
4395 if (RT_SUCCESS(vrc))
4396 {
4397 *aExists = TRUE;
4398 }
4399 else
4400 {
4401 if (GuestProcess::i_isGuestError(vrc))
4402 {
4403 if ( rcGuest == VERR_NOT_A_FILE
4404 || rcGuest == VERR_PATH_NOT_FOUND
4405 || rcGuest == VERR_FILE_NOT_FOUND
4406 || rcGuest == VERR_INVALID_NAME)
4407 {
4408 hrc = S_OK; /* Ignore these vrc values. */
4409 }
4410 else
4411 {
4412 GuestErrorInfo ge(GuestErrorInfo::Type_ToolStat, rcGuest, aPath.c_str());
4413 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying guest file existence information failed: %s"),
4414 GuestBase::getErrorAsString(ge).c_str());
4415 }
4416 }
4417 else
4418 hrc = setErrorVrc(vrc, tr("Querying guest file existence information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4419 }
4420
4421 return hrc;
4422}
4423
4424HRESULT GuestSession::fsObjQueryInfo(const com::Utf8Str &aPath, BOOL aFollowSymlinks, ComPtr<IGuestFsObjInfo> &aInfo)
4425{
4426 if (aPath.isEmpty())
4427 return setError(E_INVALIDARG, tr("No path specified"));
4428
4429 HRESULT hrc = i_isStartedExternal();
4430 if (FAILED(hrc))
4431 return hrc;
4432
4433 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
4434
4435 GuestFsObjData Info; int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4436 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, Info, &rcGuest);
4437 if (RT_SUCCESS(vrc))
4438 {
4439 ComObjPtr<GuestFsObjInfo> ptrFsObjInfo;
4440 hrc = ptrFsObjInfo.createObject();
4441 if (SUCCEEDED(hrc))
4442 {
4443 vrc = ptrFsObjInfo->init(Info);
4444 if (RT_SUCCESS(vrc))
4445 hrc = ptrFsObjInfo.queryInterfaceTo(aInfo.asOutParam());
4446 else
4447 hrc = setErrorVrc(vrc);
4448 }
4449 }
4450 else
4451 {
4452 if (GuestProcess::i_isGuestError(vrc))
4453 {
4454 GuestErrorInfo ge(GuestErrorInfo::Type_ToolStat, rcGuest, aPath.c_str());
4455 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying guest file information failed: %s"),
4456 GuestBase::getErrorAsString(ge).c_str());
4457 }
4458 else
4459 hrc = setErrorVrc(vrc, tr("Querying guest file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4460 }
4461
4462 return hrc;
4463}
4464
4465HRESULT GuestSession::fsObjRemove(const com::Utf8Str &aPath)
4466{
4467 if (RT_UNLIKELY(aPath.isEmpty()))
4468 return setError(E_INVALIDARG, tr("No path specified"));
4469
4470 HRESULT hrc = i_isStartedExternal();
4471 if (FAILED(hrc))
4472 return hrc;
4473
4474 LogFlowThisFunc(("aPath=%s\n", aPath.c_str()));
4475
4476 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4477 int vrc = i_fileRemove(aPath, &rcGuest);
4478 if (RT_FAILURE(vrc))
4479 {
4480 if (GuestProcess::i_isGuestError(vrc))
4481 {
4482 GuestErrorInfo ge(GuestErrorInfo::Type_ToolRm, rcGuest, aPath.c_str());
4483 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Removing guest file failed: %s"),
4484 GuestBase::getErrorAsString(ge).c_str());
4485 }
4486 else
4487 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing guest file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4488 }
4489
4490 return hrc;
4491}
4492
4493HRESULT GuestSession::fsObjRemoveArray(const std::vector<com::Utf8Str> &aPaths, ComPtr<IProgress> &aProgress)
4494{
4495 RT_NOREF(aPaths, aProgress);
4496 return E_NOTIMPL;
4497}
4498
4499HRESULT GuestSession::fsObjRename(const com::Utf8Str &aSource,
4500 const com::Utf8Str &aDestination,
4501 const std::vector<FsObjRenameFlag_T> &aFlags)
4502{
4503 if (RT_UNLIKELY(aSource.isEmpty()))
4504 return setError(E_INVALIDARG, tr("No source path specified"));
4505
4506 if (RT_UNLIKELY(aDestination.isEmpty()))
4507 return setError(E_INVALIDARG, tr("No destination path specified"));
4508
4509 HRESULT hrc = i_isStartedExternal();
4510 if (FAILED(hrc))
4511 return hrc;
4512
4513 /* Combine, validate and convert flags. */
4514 uint32_t fApiFlags = 0;
4515 for (size_t i = 0; i < aFlags.size(); i++)
4516 fApiFlags |= aFlags[i];
4517 if (fApiFlags & ~((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace))
4518 return setError(E_INVALIDARG, tr("Unknown rename flag: %#x"), fApiFlags);
4519
4520 LogFlowThisFunc(("aSource=%s, aDestination=%s\n", aSource.c_str(), aDestination.c_str()));
4521
4522 AssertCompile(FsObjRenameFlag_NoReplace == 0);
4523 AssertCompile(FsObjRenameFlag_Replace != 0);
4524 uint32_t fBackend;
4525 if ((fApiFlags & ((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace)) == FsObjRenameFlag_Replace)
4526 fBackend = PATHRENAME_FLAG_REPLACE;
4527 else
4528 fBackend = PATHRENAME_FLAG_NO_REPLACE;
4529
4530 /* Call worker to do the job. */
4531 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4532 int vrc = i_pathRename(aSource, aDestination, fBackend, &rcGuest);
4533 if (RT_FAILURE(vrc))
4534 {
4535 switch (vrc)
4536 {
4537 case VERR_NOT_SUPPORTED:
4538 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4539 tr("Handling renaming guest paths not supported by installed Guest Additions"));
4540 break;
4541
4542 case VERR_GSTCTL_GUEST_ERROR:
4543 {
4544 GuestErrorInfo ge(GuestErrorInfo::Type_Process, rcGuest, aSource.c_str());
4545 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Renaming guest path failed: %s"),
4546 GuestBase::getErrorAsString(ge).c_str());
4547 break;
4548 }
4549 default:
4550 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming guest path \"%s\" failed: %Rrc"),
4551 aSource.c_str(), vrc);
4552 break;
4553 }
4554 }
4555
4556 return hrc;
4557}
4558
4559HRESULT GuestSession::fsObjMove(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
4560 const std::vector<FsObjMoveFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
4561{
4562 RT_NOREF(aSource, aDestination, aFlags, aProgress);
4563 ReturnComNotImplemented();
4564}
4565
4566HRESULT GuestSession::fsObjMoveArray(const std::vector<com::Utf8Str> &aSource,
4567 const com::Utf8Str &aDestination,
4568 const std::vector<FsObjMoveFlag_T> &aFlags,
4569 ComPtr<IProgress> &aProgress)
4570{
4571 RT_NOREF(aSource, aDestination, aFlags, aProgress);
4572 ReturnComNotImplemented();
4573}
4574
4575HRESULT GuestSession::fsObjCopyArray(const std::vector<com::Utf8Str> &aSource,
4576 const com::Utf8Str &aDestination,
4577 const std::vector<FileCopyFlag_T> &aFlags,
4578 ComPtr<IProgress> &aProgress)
4579{
4580 RT_NOREF(aSource, aDestination, aFlags, aProgress);
4581 ReturnComNotImplemented();
4582}
4583
4584HRESULT GuestSession::fsObjSetACL(const com::Utf8Str &aPath, BOOL aFollowSymlinks, const com::Utf8Str &aAcl, ULONG aMode)
4585{
4586 RT_NOREF(aPath, aFollowSymlinks, aAcl, aMode);
4587 ReturnComNotImplemented();
4588}
4589
4590
4591HRESULT GuestSession::processCreate(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
4592 const std::vector<com::Utf8Str> &aEnvironment,
4593 const std::vector<ProcessCreateFlag_T> &aFlags,
4594 ULONG aTimeoutMS, ComPtr<IGuestProcess> &aGuestProcess)
4595{
4596 LogFlowThisFuncEnter();
4597
4598 std::vector<LONG> affinityIgnored;
4599 return processCreateEx(aExecutable, aArguments, aEnvironment, aFlags, aTimeoutMS, ProcessPriority_Default,
4600 affinityIgnored, aGuestProcess);
4601}
4602
4603HRESULT GuestSession::processCreateEx(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
4604 const std::vector<com::Utf8Str> &aEnvironment,
4605 const std::vector<ProcessCreateFlag_T> &aFlags, ULONG aTimeoutMS,
4606 ProcessPriority_T aPriority, const std::vector<LONG> &aAffinity,
4607 ComPtr<IGuestProcess> &aGuestProcess)
4608{
4609 HRESULT hr = i_isStartedExternal();
4610 if (FAILED(hr))
4611 return hr;
4612
4613 /*
4614 * Must have an executable to execute. If none is given, we try use the
4615 * zero'th argument.
4616 */
4617 const char *pszExecutable = aExecutable.c_str();
4618 if (RT_UNLIKELY(pszExecutable == NULL || *pszExecutable == '\0'))
4619 {
4620 if (aArguments.size() > 0)
4621 pszExecutable = aArguments[0].c_str();
4622 if (pszExecutable == NULL || *pszExecutable == '\0')
4623 return setError(E_INVALIDARG, tr("No command to execute specified"));
4624 }
4625
4626 /* The rest of the input is being validated in i_processCreateEx(). */
4627
4628 LogFlowThisFuncEnter();
4629
4630 /*
4631 * Build the process startup info.
4632 */
4633 GuestProcessStartupInfo procInfo;
4634
4635 /* Executable and arguments. */
4636 procInfo.mExecutable = pszExecutable;
4637 if (aArguments.size())
4638 {
4639 for (size_t i = 0; i < aArguments.size(); i++)
4640 procInfo.mArguments.push_back(aArguments[i]);
4641 }
4642 else /* If no arguments were given, add the executable as argv[0] by default. */
4643 procInfo.mArguments.push_back(procInfo.mExecutable);
4644
4645 /* Combine the environment changes associated with the ones passed in by
4646 the caller, giving priority to the latter. The changes are putenv style
4647 and will be applied to the standard environment for the guest user. */
4648 int vrc = procInfo.mEnvironmentChanges.copy(mData.mEnvironmentChanges);
4649 if (RT_SUCCESS(vrc))
4650 {
4651 size_t idxError = ~(size_t)0;
4652 vrc = procInfo.mEnvironmentChanges.applyPutEnvArray(aEnvironment, &idxError);
4653 if (RT_SUCCESS(vrc))
4654 {
4655 /* Convert the flag array into a mask. */
4656 if (aFlags.size())
4657 for (size_t i = 0; i < aFlags.size(); i++)
4658 procInfo.mFlags |= aFlags[i];
4659
4660 procInfo.mTimeoutMS = aTimeoutMS;
4661
4662 /** @todo use RTCPUSET instead of archaic 64-bit variables! */
4663 if (aAffinity.size())
4664 for (size_t i = 0; i < aAffinity.size(); i++)
4665 if (aAffinity[i])
4666 procInfo.mAffinity |= (uint64_t)1 << i;
4667
4668 procInfo.mPriority = aPriority;
4669
4670 /*
4671 * Create a guest process object.
4672 */
4673 ComObjPtr<GuestProcess> pProcess;
4674 vrc = i_processCreateEx(procInfo, pProcess);
4675 if (RT_SUCCESS(vrc))
4676 {
4677 ComPtr<IGuestProcess> pIProcess;
4678 hr = pProcess.queryInterfaceTo(pIProcess.asOutParam());
4679 if (SUCCEEDED(hr))
4680 {
4681 /*
4682 * Start the process.
4683 */
4684 vrc = pProcess->i_startProcessAsync();
4685 if (RT_SUCCESS(vrc))
4686 {
4687 aGuestProcess = pIProcess;
4688
4689 LogFlowFuncLeaveRC(vrc);
4690 return S_OK;
4691 }
4692
4693 hr = setErrorVrc(vrc, tr("Failed to start guest process: %Rrc"), vrc);
4694 }
4695 }
4696 else if (vrc == VERR_GSTCTL_MAX_CID_OBJECTS_REACHED)
4697 hr = setErrorVrc(vrc, tr("Maximum number of concurrent guest processes per session (%u) reached"),
4698 VBOX_GUESTCTRL_MAX_OBJECTS);
4699 else
4700 hr = setErrorVrc(vrc, tr("Failed to create guest process object: %Rrc"), vrc);
4701 }
4702 else
4703 hr = setErrorBoth(vrc == VERR_ENV_INVALID_VAR_NAME ? E_INVALIDARG : Global::vboxStatusCodeToCOM(vrc), vrc,
4704 tr("Failed to apply environment variable '%s', index %u (%Rrc)'"),
4705 aEnvironment[idxError].c_str(), idxError, vrc);
4706 }
4707 else
4708 hr = setErrorVrc(vrc, tr("Failed to set up the environment: %Rrc"), vrc);
4709
4710 LogFlowFuncLeaveRC(vrc);
4711 return hr;
4712}
4713
4714HRESULT GuestSession::processGet(ULONG aPid, ComPtr<IGuestProcess> &aGuestProcess)
4715
4716{
4717 if (aPid == 0)
4718 return setError(E_INVALIDARG, tr("No valid process ID (PID) specified"));
4719
4720 LogFlowThisFunc(("PID=%RU32\n", aPid));
4721
4722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4723
4724 HRESULT hr = S_OK;
4725
4726 ComObjPtr<GuestProcess> pProcess;
4727 int rc = i_processGetByPID(aPid, &pProcess);
4728 if (RT_FAILURE(rc))
4729 hr = setError(E_INVALIDARG, tr("No process with PID %RU32 found"), aPid);
4730
4731 /* This will set (*aProcess) to NULL if pProgress is NULL. */
4732 HRESULT hr2 = pProcess.queryInterfaceTo(aGuestProcess.asOutParam());
4733 if (SUCCEEDED(hr))
4734 hr = hr2;
4735
4736 LogFlowThisFunc(("aProcess=%p, hr=%Rhrc\n", (IGuestProcess*)aGuestProcess, hr));
4737 return hr;
4738}
4739
4740HRESULT GuestSession::symlinkCreate(const com::Utf8Str &aSource, const com::Utf8Str &aTarget, SymlinkType_T aType)
4741{
4742 RT_NOREF(aSource, aTarget, aType);
4743 ReturnComNotImplemented();
4744}
4745
4746HRESULT GuestSession::symlinkExists(const com::Utf8Str &aSymlink, BOOL *aExists)
4747
4748{
4749 RT_NOREF(aSymlink, aExists);
4750 ReturnComNotImplemented();
4751}
4752
4753HRESULT GuestSession::symlinkRead(const com::Utf8Str &aSymlink, const std::vector<SymlinkReadFlag_T> &aFlags,
4754 com::Utf8Str &aTarget)
4755{
4756 RT_NOREF(aSymlink, aFlags, aTarget);
4757 ReturnComNotImplemented();
4758}
4759
4760HRESULT GuestSession::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
4761{
4762 /* Note: No call to i_isStartedExternal() needed here, as the session might not has been started (yet). */
4763
4764 LogFlowThisFuncEnter();
4765
4766 HRESULT hrc = S_OK;
4767
4768 /*
4769 * Note: Do not hold any locks here while waiting!
4770 */
4771 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS; GuestSessionWaitResult_T waitResult;
4772 int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &rcGuest);
4773 if (RT_SUCCESS(vrc))
4774 *aReason = waitResult;
4775 else
4776 {
4777 switch (vrc)
4778 {
4779 case VERR_GSTCTL_GUEST_ERROR:
4780 {
4781 GuestErrorInfo ge(GuestErrorInfo::Type_Session, rcGuest, mData.mSession.mName.c_str());
4782 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Waiting for guest process failed: %s"),
4783 GuestBase::getErrorAsString(ge).c_str());
4784 break;
4785 }
4786 case VERR_TIMEOUT:
4787 *aReason = GuestSessionWaitResult_Timeout;
4788 break;
4789
4790 default:
4791 {
4792 const char *pszSessionName = mData.mSession.mName.c_str();
4793 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4794 tr("Waiting for guest session \"%s\" failed: %Rrc"),
4795 pszSessionName ? pszSessionName : tr("Unnamed"), vrc);
4796 break;
4797 }
4798 }
4799 }
4800
4801 LogFlowFuncLeaveRC(vrc);
4802 return hrc;
4803}
4804
4805HRESULT GuestSession::waitForArray(const std::vector<GuestSessionWaitForFlag_T> &aWaitFor, ULONG aTimeoutMS,
4806 GuestSessionWaitResult_T *aReason)
4807{
4808 /* Note: No call to i_isStartedExternal() needed here, as the session might not has been started (yet). */
4809
4810 LogFlowThisFuncEnter();
4811
4812 /*
4813 * Note: Do not hold any locks here while waiting!
4814 */
4815 uint32_t fWaitFor = GuestSessionWaitForFlag_None;
4816 for (size_t i = 0; i < aWaitFor.size(); i++)
4817 fWaitFor |= aWaitFor[i];
4818
4819 return WaitFor(fWaitFor, aTimeoutMS, aReason);
4820}
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