VirtualBox

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

Last change on this file since 97338 was 97305, checked in by vboxsync, 2 years ago

Main/Guest Control: Renamed GuestSession::i_getPathStyle() -> i_getGuestPathStyle() + added GuestSession::i_getHostPathStyle(), to emphasized what is what.

Fixed source path style in GuestSession::fileCopyToGuest(), GuestSession::directoryCopyToGuest() and GuestSession::copyToGuest(). ​bugref:10286

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

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