VirtualBox

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

Last change on this file since 102654 was 102654, checked in by vboxsync, 14 months ago

Guest Control: Implemented IGuestSession::getMountPoints. bugref:10415

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