VirtualBox

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

Last change on this file since 105301 was 104635, checked in by vboxsync, 7 months ago

Main: Fixed warnings. ​bugref:3409

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

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