VirtualBox

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

Last change on this file since 98273 was 98272, checked in by vboxsync, 23 months ago

Main/Guest*: rc -> hrc/vrc. bugref:10223

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