VirtualBox

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

Last change on this file since 103415 was 102833, checked in by vboxsync, 11 months ago

Guest Control: Added a flag for the get mount points HGCM API, so that we later can specify which mount points we want to get (or skip). bugref:10415

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 182.8 KB
Line 
1/* $Id: GuestSessionImpl.cpp 102833 2024-01-11 09:18:25Z 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 * @retval VERR_NOT_SUPPORTED if the installed Guest Additions do not support this feature.
3159 * @param vecMountPoints Where to return the mount points (guest-style paths).
3160 * @param pvrcGuest Guest VBox status code, when returning
3161 * VERR_GSTCTL_GUEST_ERROR. Any other return code indicates
3162 * some host side error.
3163 *
3164 * @note Takes the read lock.
3165 */
3166int GuestSession::i_getMountPoints(std::vector<com::Utf8Str> &vecMountPoints, int *pvrcGuest)
3167{
3168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3169
3170 if (!(mParent->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_MOUNT_POINTS_ENUM))
3171 return VERR_NOT_SUPPORTED;
3172
3173 GuestWaitEvent *pEvent = NULL;
3174 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
3175 if (RT_FAILURE(vrc))
3176 return vrc;
3177
3178 /* Prepare HGCM call. */
3179 VBOXHGCMSVCPARM paParms[4];
3180 int i = 0;
3181 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
3182 HGCMSvcSetU32(&paParms[i++], 0 /* fFlags, unused */);
3183
3184 alock.release(); /* Drop lock before sending. */
3185
3186 vrc = i_sendMessage(HOST_MSG_MOUNT_POINTS, i, paParms);
3187 if (RT_SUCCESS(vrc))
3188 {
3189 vrc = pEvent->Wait(30 * 1000);
3190 if (RT_SUCCESS(vrc))
3191 {
3192 vrc = pEvent->Payload().ToStringVector(vecMountPoints);
3193 }
3194 else
3195 {
3196 if (pEvent->HasGuestError() && pvrcGuest)
3197 *pvrcGuest = pEvent->GuestResult();
3198 }
3199 }
3200
3201 unregisterWaitEvent(pEvent);
3202
3203 LogFlowFuncLeaveRC(vrc);
3204 return vrc;
3205}
3206
3207/**
3208 * Returns the user's absolute home path, if any.
3209 *
3210 * @returns VBox status code.
3211 * @param strPath Where to store the user's home path.
3212 * @param pvrcGuest Guest VBox status code, when returning
3213 * VERR_GSTCTL_GUEST_ERROR. Any other return code indicates
3214 * some host side error.
3215 *
3216 * @note Takes the read lock.
3217 */
3218int GuestSession::i_pathUserHome(Utf8Str &strPath, int *pvrcGuest)
3219{
3220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3221
3222 /** @todo Cache the user's home path? */
3223
3224 GuestWaitEvent *pEvent = NULL;
3225 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
3226 if (RT_FAILURE(vrc))
3227 return vrc;
3228
3229 /* Prepare HGCM call. */
3230 VBOXHGCMSVCPARM paParms[2];
3231 int i = 0;
3232 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
3233
3234 alock.release(); /* Drop lock before sending. */
3235
3236 vrc = i_sendMessage(HOST_MSG_PATH_USER_HOME, i, paParms);
3237 if (RT_SUCCESS(vrc))
3238 {
3239 vrc = pEvent->Wait(30 * 1000);
3240 if (RT_SUCCESS(vrc))
3241 {
3242 strPath = pEvent->Payload().ToString();
3243 }
3244 else
3245 {
3246 if (pEvent->HasGuestError() && pvrcGuest)
3247 *pvrcGuest = pEvent->GuestResult();
3248 }
3249 }
3250
3251 unregisterWaitEvent(pEvent);
3252
3253 LogFlowFuncLeaveRC(vrc);
3254 return vrc;
3255}
3256
3257/**
3258 * Unregisters a process object from a guest session.
3259 *
3260 * @returns VBox status code. VERR_NOT_FOUND if the process is not registered (anymore).
3261 * @param pProcess Process object to unregister from session.
3262 *
3263 * @note Takes the write lock.
3264 */
3265int GuestSession::i_processUnregister(GuestProcess *pProcess)
3266{
3267 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
3268
3269 LogFlowThisFunc(("pProcess=%p\n", pProcess));
3270
3271 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3272
3273 const uint32_t idObject = pProcess->getObjectID();
3274
3275 LogFlowFunc(("Removing process (objectID=%RU32) ...\n", idObject));
3276
3277 int vrc = i_objectUnregister(idObject);
3278 if (RT_FAILURE(vrc))
3279 return vrc;
3280
3281 SessionProcesses::iterator itProcs = mData.mProcesses.find(idObject);
3282 AssertReturn(itProcs != mData.mProcesses.end(), VERR_NOT_FOUND);
3283
3284 /* Make sure to consume the pointer before the one of the iterator gets released. */
3285 ComObjPtr<GuestProcess> pProc = pProcess;
3286
3287 ULONG uPID;
3288 HRESULT hrc = pProc->COMGETTER(PID)(&uPID);
3289 ComAssertComRC(hrc);
3290
3291 LogFlowFunc(("Removing process ID=%RU32 (session %RU32, guest PID %RU32, now total %zu processes)\n",
3292 idObject, mData.mSession.mID, uPID, mData.mProcesses.size()));
3293
3294 vrc = pProcess->i_onUnregister();
3295 AssertRCReturn(vrc, vrc);
3296
3297 mData.mProcesses.erase(itProcs);
3298
3299 alock.release(); /* Release lock before firing off event. */
3300
3301 ::FireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProc, uPID, false /* Process unregistered */);
3302
3303 pProc.setNull();
3304
3305 LogFlowFuncLeaveRC(vrc);
3306 return vrc;
3307}
3308
3309/**
3310 * Creates but does *not* start the process yet.
3311 *
3312 * See GuestProcess::startProcess() or GuestProcess::startProcessAsync() for
3313 * starting the process.
3314 *
3315 * @returns IPRT status code.
3316 * @param procInfo Process startup info to use for starting the process.
3317 * @param pProcess Where to return the created guest process object on success.
3318 *
3319 * @note Takes the write lock.
3320 */
3321int GuestSession::i_processCreateEx(GuestProcessStartupInfo &procInfo, ComObjPtr<GuestProcess> &pProcess)
3322{
3323 LogFlowFunc(("mExe=%s, mFlags=%x, mTimeoutMS=%RU32\n",
3324 procInfo.mExecutable.c_str(), procInfo.mFlags, procInfo.mTimeoutMS));
3325#ifdef DEBUG
3326 if (procInfo.mArguments.size())
3327 {
3328 LogFlowFunc(("Arguments:"));
3329 ProcessArguments::const_iterator it = procInfo.mArguments.begin();
3330 while (it != procInfo.mArguments.end())
3331 {
3332 LogFlow((" %s", (*it).c_str()));
3333 ++it;
3334 }
3335 LogFlow(("\n"));
3336 }
3337#endif
3338
3339 /* Validate flags. */
3340 if (procInfo.mFlags)
3341 {
3342 if ( !(procInfo.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
3343 && !(procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
3344 && !(procInfo.mFlags & ProcessCreateFlag_Hidden)
3345 && !(procInfo.mFlags & ProcessCreateFlag_Profile)
3346 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
3347 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdErr))
3348 {
3349 return VERR_INVALID_PARAMETER;
3350 }
3351 }
3352
3353 if ( (procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
3354 && ( (procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
3355 || (procInfo.mFlags & ProcessCreateFlag_WaitForStdErr)
3356 )
3357 )
3358 {
3359 return VERR_INVALID_PARAMETER;
3360 }
3361
3362 if (procInfo.mPriority)
3363 {
3364 if (!(procInfo.mPriority & ProcessPriority_Default))
3365 return VERR_INVALID_PARAMETER;
3366 }
3367
3368 /* Adjust timeout.
3369 * If set to 0, we define an infinite timeout (unlimited process run time). */
3370 if (procInfo.mTimeoutMS == 0)
3371 procInfo.mTimeoutMS = UINT32_MAX;
3372
3373 /** @todo Implement process priority + affinity. */
3374
3375 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3376
3377 /* Create the process object. */
3378 HRESULT hrc = pProcess.createObject();
3379 if (FAILED(hrc))
3380 return VERR_COM_UNEXPECTED;
3381
3382 /* Register a new object ID. */
3383 uint32_t idObject;
3384 int vrc = i_objectRegister(pProcess, SESSIONOBJECTTYPE_PROCESS, &idObject);
3385 if (RT_FAILURE(vrc))
3386 {
3387 pProcess.setNull();
3388 return vrc;
3389 }
3390
3391 vrc = pProcess->init(mConsole, this /* Session */, idObject, procInfo, mData.mpBaseEnvironment);
3392 if (RT_FAILURE(vrc))
3393 return vrc;
3394
3395 /* Add the created process to our map. */
3396 try
3397 {
3398 mData.mProcesses[idObject] = pProcess;
3399
3400 LogFlowFunc(("Added new process (Session: %RU32) with process ID=%RU32 (now total %zu processes)\n",
3401 mData.mSession.mID, idObject, mData.mProcesses.size()));
3402
3403 alock.release(); /* Release lock before firing off event. */
3404
3405 ::FireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProcess, 0 /* PID */, true /* Process registered */);
3406 }
3407 catch (std::bad_alloc &)
3408 {
3409 vrc = VERR_NO_MEMORY;
3410 }
3411
3412 return vrc;
3413}
3414
3415/**
3416 * Checks if a process object exists and optionally returns its object.
3417 *
3418 * @returns \c true if process object exists, or \c false if not.
3419 * @param uProcessID ID of process object to check.
3420 * @param pProcess Where to return the found process object on success.
3421 *
3422 * @note No locking done!
3423 */
3424inline bool GuestSession::i_processExists(uint32_t uProcessID, ComObjPtr<GuestProcess> *pProcess)
3425{
3426 SessionProcesses::const_iterator it = mData.mProcesses.find(uProcessID);
3427 if (it != mData.mProcesses.end())
3428 {
3429 if (pProcess)
3430 *pProcess = it->second;
3431 return true;
3432 }
3433 return false;
3434}
3435
3436/**
3437 * Returns the process object from a guest PID.
3438 *
3439 * @returns VBox status code.
3440 * @param uPID Guest PID to get process object for.
3441 * @param pProcess Where to return the process object on success.
3442 *
3443 * @note No locking done!
3444 */
3445inline int GuestSession::i_processGetByPID(ULONG uPID, ComObjPtr<GuestProcess> *pProcess)
3446{
3447 AssertReturn(uPID, false);
3448 /* pProcess is optional. */
3449
3450 SessionProcesses::iterator itProcs = mData.mProcesses.begin();
3451 for (; itProcs != mData.mProcesses.end(); ++itProcs)
3452 {
3453 ComObjPtr<GuestProcess> pCurProc = itProcs->second;
3454 AutoCaller procCaller(pCurProc);
3455 if (!procCaller.isOk())
3456 return VERR_COM_INVALID_OBJECT_STATE;
3457
3458 ULONG uCurPID;
3459 HRESULT hrc = pCurProc->COMGETTER(PID)(&uCurPID);
3460 ComAssertComRC(hrc);
3461
3462 if (uCurPID == uPID)
3463 {
3464 if (pProcess)
3465 *pProcess = pCurProc;
3466 return VINF_SUCCESS;
3467 }
3468 }
3469
3470 return VERR_NOT_FOUND;
3471}
3472
3473/**
3474 * Sends a message to the HGCM host service.
3475 *
3476 * @returns VBox status code.
3477 * @retval VERR_VM_INVALID_VM_STATE if the VM is in a state where can't send message to the guest (anymore).
3478 * @param uMessage Message ID to send.
3479 * @param uParms Number of parameters in \a paParms to send.
3480 * @param paParms Array of HGCM parameters to send.
3481 * @param fDst Host message destination flags of type VBOX_GUESTCTRL_DST_XXX.
3482 */
3483int GuestSession::i_sendMessage(uint32_t uMessage, uint32_t uParms, PVBOXHGCMSVCPARM paParms,
3484 uint64_t fDst /*= VBOX_GUESTCTRL_DST_SESSION*/)
3485{
3486 LogFlowThisFuncEnter();
3487
3488#ifndef VBOX_GUESTCTRL_TEST_CASE
3489 VMMDev *pVMMDev = mConsole->i_getVMMDev();
3490 AssertPtrReturn(pVMMDev, VERR_VM_INVALID_VM_STATE);
3491
3492 /* Forward the information to the VMM device. */
3493 LogFlowThisFunc(("uMessage=%RU32 (%s), uParms=%RU32\n", uMessage, GstCtrlHostMsgtoStr((guestControl::eHostMsg)uMessage), uParms));
3494
3495 /* HACK ALERT! We extend the first parameter to 64-bit and use the
3496 two topmost bits for call destination information. */
3497 Assert(fDst == VBOX_GUESTCTRL_DST_SESSION || fDst == VBOX_GUESTCTRL_DST_ROOT_SVC || fDst == VBOX_GUESTCTRL_DST_BOTH);
3498 Assert(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT);
3499 paParms[0].type = VBOX_HGCM_SVC_PARM_64BIT;
3500 paParms[0].u.uint64 = (uint64_t)paParms[0].u.uint32 | fDst;
3501
3502 /* Make the call. */
3503 int vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uMessage, uParms, paParms);
3504 if (RT_FAILURE(vrc))
3505 {
3506 /** @todo What to do here? */
3507 }
3508#else
3509 /* Not needed within testcases. */
3510 int vrc = VINF_SUCCESS;
3511#endif
3512 LogFlowFuncLeaveRC(vrc);
3513 return vrc;
3514}
3515
3516/**
3517 * Sets the guest session's current status.
3518 *
3519 * @returns VBox status code.
3520 * @param sessionStatus Session status to set.
3521 * @param vrcSession Session result to set (for error handling).
3522 *
3523 * @note Takes the write lock.
3524 */
3525int GuestSession::i_setSessionStatus(GuestSessionStatus_T sessionStatus, int vrcSession)
3526{
3527 LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, vrcSession=%Rrc\n", mData.mStatus, sessionStatus, vrcSession));
3528
3529 if (sessionStatus == GuestSessionStatus_Error)
3530 {
3531 AssertMsg(RT_FAILURE(vrcSession), ("Guest vrcSession must be an error (%Rrc)\n", vrcSession));
3532 /* Do not allow overwriting an already set error. If this happens
3533 * this means we forgot some error checking/locking somewhere. */
3534 AssertMsg(RT_SUCCESS(mData.mVrc), ("Guest mVrc already set (to %Rrc)\n", mData.mVrc));
3535 }
3536 else
3537 AssertMsg(RT_SUCCESS(vrcSession), ("Guest vrcSession must not be an error (%Rrc)\n", vrcSession));
3538
3539 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3540
3541 int vrc = VINF_SUCCESS;
3542
3543 if (mData.mStatus != sessionStatus)
3544 {
3545 mData.mStatus = sessionStatus;
3546 mData.mVrc = vrcSession;
3547
3548 /* Make sure to notify all underlying objects first. */
3549 vrc = i_objectsNotifyAboutStatusChange(sessionStatus);
3550
3551 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
3552 HRESULT hrc = errorInfo.createObject();
3553 ComAssertComRC(hrc);
3554 int vrc2 = errorInfo->initEx(VBOX_E_IPRT_ERROR, vrcSession, COM_IIDOF(IGuestSession), getComponentName(),
3555 i_guestErrorToString(vrcSession));
3556 AssertRC(vrc2);
3557
3558 alock.release(); /* Release lock before firing off event. */
3559
3560 ::FireGuestSessionStateChangedEvent(mEventSource, this, mData.mSession.mID, sessionStatus, errorInfo);
3561 }
3562
3563 LogFlowFuncLeaveRC(vrc);
3564 return vrc;
3565}
3566
3567/** @todo Unused --remove? */
3568int GuestSession::i_signalWaiters(GuestSessionWaitResult_T enmWaitResult, int vrc /*= VINF_SUCCESS */)
3569{
3570 RT_NOREF(enmWaitResult, vrc);
3571
3572 /*LogFlowThisFunc(("enmWaitResult=%d, vrc=%Rrc, mWaitCount=%RU32, mWaitEvent=%p\n",
3573 enmWaitResult, vrc, mData.mWaitCount, mData.mWaitEvent));*/
3574
3575 /* Note: No write locking here -- already done in the caller. */
3576
3577 int vrc2 = VINF_SUCCESS;
3578 /*if (mData.mWaitEvent)
3579 vrc2 = mData.mWaitEvent->Signal(enmWaitResult, vrc);*/
3580 LogFlowFuncLeaveRC(vrc2);
3581 return vrc2;
3582}
3583
3584/**
3585 * Shuts down (and optionally powers off / reboots) the guest.
3586 * Needs supported Guest Additions installed.
3587 *
3588 * @returns VBox status code. VERR_NOT_SUPPORTED if not supported by Guest Additions.
3589 * @param fFlags Guest shutdown flags.
3590 * @param pvrcGuest Guest VBox status code, when returning
3591 * VERR_GSTCTL_GUEST_ERROR. Any other return code indicates
3592 * some host side error.
3593 *
3594 * @note Takes the read lock.
3595 */
3596int GuestSession::i_shutdown(uint32_t fFlags, int *pvrcGuest)
3597{
3598 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3599
3600 if (!(mParent->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_SHUTDOWN))
3601 return VERR_NOT_SUPPORTED;
3602
3603 LogRel(("Guest Control: Shutting down guest (flags = %#x) ...\n", fFlags));
3604
3605 GuestWaitEvent *pEvent = NULL;
3606 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
3607 if (RT_FAILURE(vrc))
3608 return vrc;
3609
3610 /* Prepare HGCM call. */
3611 VBOXHGCMSVCPARM paParms[2];
3612 int i = 0;
3613 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
3614 HGCMSvcSetU32(&paParms[i++], fFlags);
3615
3616 alock.release(); /* Drop lock before sending. */
3617
3618 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3619
3620 vrc = i_sendMessage(HOST_MSG_SHUTDOWN, i, paParms);
3621 if (RT_SUCCESS(vrc))
3622 {
3623 vrc = pEvent->Wait(30 * 1000);
3624 if (RT_FAILURE(vrc))
3625 {
3626 if (pEvent->HasGuestError())
3627 vrcGuest = pEvent->GuestResult();
3628 }
3629 }
3630
3631 if (RT_FAILURE(vrc))
3632 {
3633 LogRel(("Guest Control: Shutting down guest failed, vrc=%Rrc\n", vrc == VERR_GSTCTL_GUEST_ERROR ? vrcGuest : vrc));
3634 if (pEvent->HasGuestError() && pvrcGuest)
3635 *pvrcGuest = vrcGuest;
3636 }
3637
3638 unregisterWaitEvent(pEvent);
3639
3640 LogFlowFuncLeaveRC(vrc);
3641 return vrc;
3642}
3643
3644/**
3645 * Determines the protocol version (sets mData.mProtocolVersion).
3646 *
3647 * This is called from the init method prior to to establishing a guest session.
3648 *
3649 * @returns VBox status code.
3650 * @retval VERR_NOT_FOUND if the Guest Additions were not found (or were not reported) yet.
3651 */
3652int GuestSession::i_determineProtocolVersion(void)
3653{
3654 /*
3655 * We currently do this based on the reported Guest Additions version,
3656 * ASSUMING that VBoxService and VBoxDrv are at the same version.
3657 */
3658 ComObjPtr<Guest> pGuest = mParent;
3659 AssertReturn(!pGuest.isNull(), VERR_NOT_SUPPORTED);
3660 uint32_t uGaVersion = pGuest->i_getAdditionsVersion();
3661 if (!uGaVersion) /* If 0, there was no Guest Additions version detected (yet), or the VM is in reset state. */
3662 return VERR_NOT_FOUND;
3663
3664 /* Everyone supports version one, if they support anything at all. */
3665 mData.mProtocolVersion = 1;
3666
3667 /* Guest control 2.0 was introduced with 4.3.0. */
3668 if (uGaVersion >= VBOX_FULL_VERSION_MAKE(4,3,0))
3669 mData.mProtocolVersion = 2; /* Guest control 2.0. */
3670
3671 LogFlowThisFunc(("uGaVersion=%u.%u.%u => mProtocolVersion=%u\n",
3672 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
3673 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
3674
3675 /*
3676 * Inform the user about outdated Guest Additions (VM release log).
3677 */
3678 if (mData.mProtocolVersion < 2)
3679 LogRelMax(3, ("Warning: Guest Additions v%u.%u.%u only supports the older guest control protocol version %u.\n"
3680 " Please upgrade GAs to the current version to get full guest control capabilities.\n",
3681 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
3682 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
3683
3684 return VINF_SUCCESS;
3685}
3686
3687/**
3688 * Waits for guest session events.
3689 *
3690 * @returns VBox status code.
3691 * @retval VERR_GSTCTL_GUEST_ERROR on received guest error.
3692 * @retval VERR_TIMEOUT when a timeout has occurred.
3693 * @param fWaitFlags Wait flags to use.
3694 * @param uTimeoutMS Timeout (in ms) to wait.
3695 * @param waitResult Where to return the wait result on success.
3696 * @param pvrcGuest Where to return the guest error when
3697 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
3698 *
3699 * @note Takes the read lock.
3700 */
3701int GuestSession::i_waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, GuestSessionWaitResult_T &waitResult, int *pvrcGuest)
3702{
3703 LogFlowThisFuncEnter();
3704
3705 AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
3706
3707 /*LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p, pvrcGuest=%p\n",
3708 fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent, pvrcGuest));*/
3709
3710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3711
3712 /* Did some error occur before? Then skip waiting and return. */
3713 if (mData.mStatus == GuestSessionStatus_Error)
3714 {
3715 waitResult = GuestSessionWaitResult_Error;
3716 AssertMsg(RT_FAILURE(mData.mVrc), ("No error mVrc (%Rrc) set when guest session indicated an error\n", mData.mVrc));
3717 if (pvrcGuest)
3718 *pvrcGuest = mData.mVrc; /* Return last set error. */
3719 return VERR_GSTCTL_GUEST_ERROR;
3720 }
3721
3722 /* Guest Additions < 4.3 don't support session handling, skip. */
3723 if (mData.mProtocolVersion < 2)
3724 {
3725 waitResult = GuestSessionWaitResult_WaitFlagNotSupported;
3726
3727 LogFlowThisFunc(("Installed Guest Additions don't support waiting for dedicated sessions, skipping\n"));
3728 return VINF_SUCCESS;
3729 }
3730
3731 waitResult = GuestSessionWaitResult_None;
3732 if (fWaitFlags & GuestSessionWaitForFlag_Terminate)
3733 {
3734 switch (mData.mStatus)
3735 {
3736 case GuestSessionStatus_Terminated:
3737 case GuestSessionStatus_Down:
3738 waitResult = GuestSessionWaitResult_Terminate;
3739 break;
3740
3741 case GuestSessionStatus_TimedOutKilled:
3742 case GuestSessionStatus_TimedOutAbnormally:
3743 waitResult = GuestSessionWaitResult_Timeout;
3744 break;
3745
3746 case GuestSessionStatus_Error:
3747 /* Handled above. */
3748 break;
3749
3750 case GuestSessionStatus_Started:
3751 waitResult = GuestSessionWaitResult_Start;
3752 break;
3753
3754 case GuestSessionStatus_Undefined:
3755 case GuestSessionStatus_Starting:
3756 /* Do the waiting below. */
3757 break;
3758
3759 default:
3760 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
3761 return VERR_NOT_IMPLEMENTED;
3762 }
3763 }
3764 else if (fWaitFlags & GuestSessionWaitForFlag_Start)
3765 {
3766 switch (mData.mStatus)
3767 {
3768 case GuestSessionStatus_Started:
3769 case GuestSessionStatus_Terminating:
3770 case GuestSessionStatus_Terminated:
3771 case GuestSessionStatus_Down:
3772 waitResult = GuestSessionWaitResult_Start;
3773 break;
3774
3775 case GuestSessionStatus_Error:
3776 waitResult = GuestSessionWaitResult_Error;
3777 break;
3778
3779 case GuestSessionStatus_TimedOutKilled:
3780 case GuestSessionStatus_TimedOutAbnormally:
3781 waitResult = GuestSessionWaitResult_Timeout;
3782 break;
3783
3784 case GuestSessionStatus_Undefined:
3785 case GuestSessionStatus_Starting:
3786 /* Do the waiting below. */
3787 break;
3788
3789 default:
3790 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
3791 return VERR_NOT_IMPLEMENTED;
3792 }
3793 }
3794
3795 LogFlowThisFunc(("sessionStatus=%RU32, vrcSession=%Rrc, waitResult=%RU32\n", mData.mStatus, mData.mVrc, waitResult));
3796
3797 /* No waiting needed? Return immediately using the last set error. */
3798 if (waitResult != GuestSessionWaitResult_None)
3799 {
3800 if (pvrcGuest)
3801 *pvrcGuest = mData.mVrc; /* Return last set error (if any). */
3802 return RT_SUCCESS(mData.mVrc) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
3803 }
3804
3805 int vrc = VINF_SUCCESS;
3806
3807 uint64_t const tsStart = RTTimeMilliTS();
3808 uint64_t tsNow = tsStart;
3809
3810 while (tsNow - tsStart < uTimeoutMS)
3811 {
3812 GuestWaitEvent *pEvent = NULL;
3813 GuestEventTypes eventTypes;
3814 try
3815 {
3816 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
3817
3818 vrc = registerWaitEventEx(mData.mSession.mID, mData.mObjectID, eventTypes, &pEvent);
3819 }
3820 catch (std::bad_alloc &)
3821 {
3822 vrc = VERR_NO_MEMORY;
3823 }
3824
3825 if (RT_FAILURE(vrc))
3826 return vrc;
3827
3828 alock.release(); /* Release lock before waiting. */
3829
3830 GuestSessionStatus_T sessionStatus;
3831 vrc = i_waitForStatusChange(pEvent, fWaitFlags,
3832 uTimeoutMS - (tsNow - tsStart), &sessionStatus, pvrcGuest);
3833 if (RT_SUCCESS(vrc))
3834 {
3835 switch (sessionStatus)
3836 {
3837 case GuestSessionStatus_Started:
3838 waitResult = GuestSessionWaitResult_Start;
3839 break;
3840
3841 case GuestSessionStatus_Starting:
3842 RT_FALL_THROUGH();
3843 case GuestSessionStatus_Terminating:
3844 if (fWaitFlags & GuestSessionWaitForFlag_Status) /* Any status wanted? */
3845 waitResult = GuestSessionWaitResult_Status;
3846 /* else: Wait another round until we get the event(s) fWaitFlags defines. */
3847 break;
3848
3849 case GuestSessionStatus_Terminated:
3850 waitResult = GuestSessionWaitResult_Terminate;
3851 break;
3852
3853 case GuestSessionStatus_TimedOutKilled:
3854 RT_FALL_THROUGH();
3855 case GuestSessionStatus_TimedOutAbnormally:
3856 waitResult = GuestSessionWaitResult_Timeout;
3857 break;
3858
3859 case GuestSessionStatus_Down:
3860 waitResult = GuestSessionWaitResult_Terminate;
3861 break;
3862
3863 case GuestSessionStatus_Error:
3864 waitResult = GuestSessionWaitResult_Error;
3865 break;
3866
3867 default:
3868 waitResult = GuestSessionWaitResult_Status;
3869 break;
3870 }
3871 }
3872
3873 unregisterWaitEvent(pEvent);
3874
3875 /* Wait result not None, e.g. some result acquired or a wait error occurred? Bail out. */
3876 if ( waitResult != GuestSessionWaitResult_None
3877 || RT_FAILURE(vrc))
3878 break;
3879
3880 tsNow = RTTimeMilliTS();
3881
3882 alock.acquire(); /* Re-acquire lock before waiting for the next event. */
3883 }
3884
3885 if (tsNow - tsStart >= uTimeoutMS)
3886 {
3887 waitResult = GuestSessionWaitResult_None; /* Paranoia. */
3888 vrc = VERR_TIMEOUT;
3889 }
3890
3891 LogFlowFuncLeaveRC(vrc);
3892 return vrc;
3893}
3894
3895/**
3896 * Waits for guest session status changes.
3897 *
3898 * @returns VBox status code.
3899 * @retval VERR_GSTCTL_GUEST_ERROR on received guest error.
3900 * @retval VERR_WRONG_ORDER when an unexpected event type has been received.
3901 * @param pEvent Wait event to use for waiting.
3902 * @param fWaitFlags Wait flags to use.
3903 * @param uTimeoutMS Timeout (in ms) to wait.
3904 * @param pSessionStatus Where to return the guest session status.
3905 * @param pvrcGuest Where to return the guest error when
3906 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
3907 */
3908int GuestSession::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t fWaitFlags, uint32_t uTimeoutMS,
3909 GuestSessionStatus_T *pSessionStatus, int *pvrcGuest)
3910{
3911 RT_NOREF(fWaitFlags);
3912 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
3913
3914 VBoxEventType_T evtType;
3915 ComPtr<IEvent> pIEvent;
3916 int vrc = waitForEvent(pEvent, uTimeoutMS, &evtType, pIEvent.asOutParam());
3917 if (RT_SUCCESS(vrc))
3918 {
3919 if (evtType == VBoxEventType_OnGuestSessionStateChanged)
3920 {
3921 ComPtr<IGuestSessionStateChangedEvent> pChangedEvent = pIEvent;
3922 Assert(!pChangedEvent.isNull());
3923
3924 GuestSessionStatus_T sessionStatus;
3925 pChangedEvent->COMGETTER(Status)(&sessionStatus);
3926 if (pSessionStatus)
3927 *pSessionStatus = sessionStatus;
3928
3929 ComPtr<IVirtualBoxErrorInfo> errorInfo;
3930 HRESULT hrc = pChangedEvent->COMGETTER(Error)(errorInfo.asOutParam());
3931 ComAssertComRC(hrc);
3932
3933 LONG lGuestRc;
3934 hrc = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
3935 ComAssertComRC(hrc);
3936 if (RT_FAILURE((int)lGuestRc))
3937 vrc = VERR_GSTCTL_GUEST_ERROR;
3938 if (pvrcGuest)
3939 *pvrcGuest = (int)lGuestRc;
3940
3941 LogFlowThisFunc(("Status changed event for session ID=%RU32, new status is: %RU32 (%Rrc)\n",
3942 mData.mSession.mID, sessionStatus,
3943 RT_SUCCESS((int)lGuestRc) ? VINF_SUCCESS : (int)lGuestRc));
3944 }
3945 else /** @todo Re-visit this. Can this happen more frequently? */
3946 AssertMsgFailedReturn(("Got unexpected event type %#x\n", evtType), VERR_WRONG_ORDER);
3947 }
3948 /* waitForEvent may also return VERR_GSTCTL_GUEST_ERROR like we do above, so make pvrcGuest is set. */
3949 else if (pEvent->HasGuestError() && pvrcGuest)
3950 *pvrcGuest = pEvent->GuestResult();
3951 Assert(vrc != VERR_GSTCTL_GUEST_ERROR || !pvrcGuest || *pvrcGuest != (int)0xcccccccc);
3952
3953 LogFlowFuncLeaveRC(vrc);
3954 return vrc;
3955}
3956
3957// implementation of public methods
3958/////////////////////////////////////////////////////////////////////////////
3959
3960HRESULT GuestSession::close()
3961{
3962 AutoCaller autoCaller(this);
3963 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
3964
3965 LogFlowThisFuncEnter();
3966
3967 int vrc = VINF_SUCCESS; /* Shut up MSVC. */
3968
3969 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3970
3971 /* If the guest session is not in an usable state (anymore), do the cleanup stuff ourselves. */
3972 if (!i_isReady())
3973 {
3974 i_onRemove();
3975 return S_OK;
3976 }
3977
3978 alock.release(); /* Leave lock before calling i_closeSession() below. */
3979
3980 int vrcGuest = VINF_SUCCESS;
3981
3982 /* If the session is in a started state, tell the guest to close it.
3983 *
3984 * Note: Don't check if the session is ready via i_isStartedExternal() here;
3985 * the session (already) could be in a stopped / aborted state.
3986 */
3987 AutoCaller autoCallerParent(mParent);
3988 if ( SUCCEEDED(autoCallerParent.hrc())
3989 && i_isStarted())
3990 {
3991 uint32_t msTimeout = RT_MS_10SEC; /* 10s timeout by default */
3992 for (int i = 0; i < 3; i++)
3993 {
3994 if (i)
3995 {
3996 LogRel(("Guest Control: Closing session '%s' timed out (%RU32s timeout, attempt %d/10), retrying ...\n",
3997 mData.mSession.mName.c_str(), msTimeout / RT_MS_1SEC, i + 1));
3998 msTimeout += RT_MS_5SEC; /* Slightly increase the timeout. */
3999 }
4000
4001 /* Close session on guest. */
4002 vrc = i_closeSession(0 /* Flags */, msTimeout, &vrcGuest);
4003 if ( RT_SUCCESS(vrc)
4004 || vrc != VERR_TIMEOUT) /* If something else happened there is no point in retrying further. */
4005 break;
4006 }
4007 }
4008
4009 /* On failure don't return here, instead do all the cleanup
4010 * work first and then return an error. */
4011
4012 /* We have to make sure that our parent (IGuest) still is alive and in a working shapee.
4013 * If not, skip removing the session from it. */
4014 LogFlowThisFunc(("Removing session '%s' from parent ...", mData.mSession.mName.c_str()));
4015
4016 /* Remove ourselves from the session list. */
4017 int vrc2 = mParent->i_sessionRemove(mData.mSession.mID);
4018 if (vrc2 == VERR_NOT_FOUND) /* Not finding the session anymore isn't critical. */
4019 vrc2 = VINF_SUCCESS;
4020
4021 if (RT_SUCCESS(vrc))
4022 vrc = vrc2;
4023
4024 LogFlowThisFunc(("Returning vrc=%Rrc, vrcGuest=%Rrc\n", vrc, vrcGuest));
4025
4026 if (RT_FAILURE(vrc))
4027 {
4028 if (vrc == VERR_GSTCTL_GUEST_ERROR)
4029 {
4030 GuestErrorInfo ge(GuestErrorInfo::Type_Session, vrcGuest, mData.mSession.mName.c_str());
4031 return setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Closing guest session failed: %s"),
4032 GuestBase::getErrorAsString(ge).c_str());
4033 }
4034 return setError(VBOX_E_IPRT_ERROR, tr("Closing guest session \"%s\" failed with %Rrc"),
4035 mData.mSession.mName.c_str(), vrc);
4036 }
4037
4038 return S_OK;
4039}
4040
4041HRESULT GuestSession::fileCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
4042 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
4043{
4044 RT_NOREF(aSource, aDestination, aFlags, aProgress);
4045 ReturnComNotImplemented();
4046}
4047
4048HRESULT GuestSession::fileCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
4049 const std::vector<FileCopyFlag_T> &aFlags,
4050 ComPtr<IProgress> &aProgress)
4051{
4052 uint32_t fFlags = FileCopyFlag_None;
4053 if (aFlags.size())
4054 {
4055 for (size_t i = 0; i < aFlags.size(); i++)
4056 fFlags |= aFlags[i];
4057
4058 const uint32_t fValidFlags = FileCopyFlag_None | FileCopyFlag_NoReplace | FileCopyFlag_FollowLinks | FileCopyFlag_Update;
4059 if (fFlags & ~fValidFlags)
4060 return setError(E_INVALIDARG,tr("Unknown flags: flags value %#x, invalid: %#x"), fFlags, fFlags & ~fValidFlags);
4061 }
4062
4063 GuestSessionFsSourceSet SourceSet;
4064
4065 GuestSessionFsSourceSpec source;
4066 source.strSource = aSource;
4067 source.enmType = FsObjType_File;
4068 source.enmPathStyle = i_getGuestPathStyle();
4069 source.fDryRun = false; /** @todo Implement support for a dry run. */
4070 source.fDirCopyFlags = DirectoryCopyFlag_None;
4071 source.fFileCopyFlags = (FileCopyFlag_T)fFlags;
4072
4073 SourceSet.push_back(source);
4074
4075 return i_copyFromGuest(SourceSet, aDestination, aProgress);
4076}
4077
4078HRESULT GuestSession::fileCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
4079 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
4080{
4081 uint32_t fFlags = FileCopyFlag_None;
4082 if (aFlags.size())
4083 {
4084 for (size_t i = 0; i < aFlags.size(); i++)
4085 fFlags |= aFlags[i];
4086
4087 const uint32_t fValidFlags = FileCopyFlag_None | FileCopyFlag_NoReplace | FileCopyFlag_FollowLinks | FileCopyFlag_Update;
4088 if (fFlags & ~fValidFlags)
4089 return setError(E_INVALIDARG,tr("Unknown flags: flags value %#x, invalid: %#x"), fFlags, fFlags & ~fValidFlags);
4090 }
4091
4092 GuestSessionFsSourceSet SourceSet;
4093
4094 GuestSessionFsSourceSpec source;
4095 source.strSource = aSource;
4096 source.enmType = FsObjType_File;
4097 source.enmPathStyle = GuestSession::i_getHostPathStyle();
4098 source.fDryRun = false; /** @todo Implement support for a dry run. */
4099 source.fDirCopyFlags = DirectoryCopyFlag_None;
4100 source.fFileCopyFlags = (FileCopyFlag_T)fFlags;
4101
4102 SourceSet.push_back(source);
4103
4104 return i_copyToGuest(SourceSet, aDestination, aProgress);
4105}
4106
4107HRESULT GuestSession::copyFromGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
4108 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
4109 ComPtr<IProgress> &aProgress)
4110{
4111 const size_t cSources = aSources.size();
4112 if ( (aFilters.size() && aFilters.size() != cSources)
4113 || (aFlags.size() && aFlags.size() != cSources))
4114 {
4115 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
4116 }
4117
4118 GuestSessionFsSourceSet SourceSet;
4119
4120 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
4121 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
4122 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
4123
4124 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
4125 const bool fFollowSymlinks = true; /** @todo Ditto. */
4126
4127 while (itSource != aSources.end())
4128 {
4129 GuestFsObjData objData;
4130 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4131 int vrc = i_fsObjQueryInfo(*(itSource), fFollowSymlinks, objData, &vrcGuest);
4132 if ( RT_FAILURE(vrc)
4133 && !fContinueOnErrors)
4134 {
4135 if (GuestProcess::i_isGuestError(vrc))
4136 {
4137 GuestErrorInfo ge(GuestErrorInfo::Type_Process, vrcGuest, (*itSource).c_str());
4138 return setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Querying type for guest source failed: %s"),
4139 GuestBase::getErrorAsString(ge).c_str());
4140 }
4141 return setError(E_FAIL, tr("Querying type for guest source \"%s\" failed: %Rrc"), (*itSource).c_str(), vrc);
4142 }
4143
4144 Utf8Str strFlags;
4145 if (itFlags != aFlags.end())
4146 {
4147 strFlags = *itFlags;
4148 ++itFlags;
4149 }
4150
4151 Utf8Str strFilter;
4152 if (itFilter != aFilters.end())
4153 {
4154 strFilter = *itFilter;
4155 ++itFilter;
4156 }
4157
4158 GuestSessionFsSourceSpec source;
4159 source.strSource = *itSource;
4160 source.strFilter = strFilter;
4161 source.enmType = objData.mType;
4162 source.enmPathStyle = i_getGuestPathStyle();
4163 source.fDryRun = false; /** @todo Implement support for a dry run. */
4164
4165 /* Check both flag groups here, as copying a directory also could mean to explicitly
4166 * *not* replacing any existing files (or just copy files which are newer, for instance). */
4167 GuestSession::i_directoryCopyFlagFromStr(strFlags, false /* fStrict */, &source.fDirCopyFlags);
4168 GuestSession::i_fileCopyFlagFromStr(strFlags, false /* fStrict */, &source.fFileCopyFlags);
4169
4170 SourceSet.push_back(source);
4171
4172 ++itSource;
4173 }
4174
4175 return i_copyFromGuest(SourceSet, aDestination, aProgress);
4176}
4177
4178HRESULT GuestSession::copyToGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
4179 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
4180 ComPtr<IProgress> &aProgress)
4181{
4182 const size_t cSources = aSources.size();
4183 if ( (aFilters.size() && aFilters.size() != cSources)
4184 || (aFlags.size() && aFlags.size() != cSources))
4185 {
4186 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
4187 }
4188
4189 GuestSessionFsSourceSet SourceSet;
4190
4191 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
4192 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
4193 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
4194
4195 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
4196
4197 while (itSource != aSources.end())
4198 {
4199 RTFSOBJINFO objInfo;
4200 int vrc = RTPathQueryInfo((*itSource).c_str(), &objInfo, RTFSOBJATTRADD_NOTHING);
4201 if ( RT_FAILURE(vrc)
4202 && !fContinueOnErrors)
4203 {
4204 return setError(E_FAIL, tr("Unable to query type for source '%s' (%Rrc)"), (*itSource).c_str(), vrc);
4205 }
4206
4207 Utf8Str strFlags;
4208 if (itFlags != aFlags.end())
4209 {
4210 strFlags = *itFlags;
4211 ++itFlags;
4212 }
4213
4214 Utf8Str strFilter;
4215 if (itFilter != aFilters.end())
4216 {
4217 strFilter = *itFilter;
4218 ++itFilter;
4219 }
4220
4221 GuestSessionFsSourceSpec source;
4222 source.strSource = *itSource;
4223 source.strFilter = strFilter;
4224 source.enmType = GuestBase::fileModeToFsObjType(objInfo.Attr.fMode);
4225 source.enmPathStyle = GuestSession::i_getHostPathStyle();
4226 source.fDryRun = false; /** @todo Implement support for a dry run. */
4227
4228 GuestSession::i_directoryCopyFlagFromStr(strFlags, false /* fStrict */, &source.fDirCopyFlags);
4229 GuestSession::i_fileCopyFlagFromStr(strFlags, false /* fStrict */, &source.fFileCopyFlags);
4230
4231 SourceSet.push_back(source);
4232
4233 ++itSource;
4234 }
4235
4236 /* (Re-)Validate stuff. */
4237 if (RT_UNLIKELY(SourceSet.size() == 0)) /* At least one source must be present. */
4238 return setError(E_INVALIDARG, tr("No sources specified"));
4239 if (RT_UNLIKELY(SourceSet[0].strSource.isEmpty()))
4240 return setError(E_INVALIDARG, tr("First source entry is empty"));
4241 if (RT_UNLIKELY(aDestination.isEmpty()))
4242 return setError(E_INVALIDARG, tr("No destination specified"));
4243
4244 return i_copyToGuest(SourceSet, aDestination, aProgress);
4245}
4246
4247HRESULT GuestSession::directoryCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
4248 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
4249{
4250 RT_NOREF(aSource, aDestination, aFlags, aProgress);
4251 ReturnComNotImplemented();
4252}
4253
4254HRESULT GuestSession::directoryCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
4255 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
4256{
4257 uint32_t fFlags = DirectoryCopyFlag_None;
4258 if (aFlags.size())
4259 {
4260 for (size_t i = 0; i < aFlags.size(); i++)
4261 fFlags |= aFlags[i];
4262
4263 const uint32_t fValidFlags = DirectoryCopyFlag_None | DirectoryCopyFlag_CopyIntoExisting | DirectoryCopyFlag_Recursive
4264 | DirectoryCopyFlag_FollowLinks;
4265 if (fFlags & ~fValidFlags)
4266 return setError(E_INVALIDARG,tr("Unknown flags: flags value %#x, invalid: %#x"), fFlags, fFlags & ~fValidFlags);
4267 }
4268
4269 GuestSessionFsSourceSet SourceSet;
4270
4271 GuestSessionFsSourceSpec source;
4272 source.strSource = aSource;
4273 source.enmType = FsObjType_Directory;
4274 source.enmPathStyle = i_getGuestPathStyle();
4275 source.fDryRun = false; /** @todo Implement support for a dry run. */
4276 source.fDirCopyFlags = (DirectoryCopyFlag_T)fFlags;
4277 source.fFileCopyFlags = FileCopyFlag_None; /* Overwrite existing files. */
4278
4279 SourceSet.push_back(source);
4280
4281 return i_copyFromGuest(SourceSet, aDestination, aProgress);
4282}
4283
4284HRESULT GuestSession::directoryCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
4285 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
4286{
4287 uint32_t fFlags = DirectoryCopyFlag_None;
4288 if (aFlags.size())
4289 {
4290 for (size_t i = 0; i < aFlags.size(); i++)
4291 fFlags |= aFlags[i];
4292
4293 const uint32_t fValidFlags = DirectoryCopyFlag_None | DirectoryCopyFlag_CopyIntoExisting | DirectoryCopyFlag_Recursive
4294 | DirectoryCopyFlag_FollowLinks;
4295 if (fFlags & ~fValidFlags)
4296 return setError(E_INVALIDARG,tr("Unknown flags: flags value %#x, invalid: %#x"), fFlags, fFlags & ~fValidFlags);
4297 }
4298
4299 GuestSessionFsSourceSet SourceSet;
4300
4301 GuestSessionFsSourceSpec source;
4302 source.strSource = aSource;
4303 source.enmType = FsObjType_Directory;
4304 source.enmPathStyle = GuestSession::i_getHostPathStyle();
4305 source.fDryRun = false; /** @todo Implement support for a dry run. */
4306 source.fDirCopyFlags = (DirectoryCopyFlag_T)fFlags;
4307 source.fFileCopyFlags = FileCopyFlag_None; /* Overwrite existing files. */
4308
4309 SourceSet.push_back(source);
4310
4311 return i_copyToGuest(SourceSet, aDestination, aProgress);
4312}
4313
4314HRESULT GuestSession::directoryCreate(const com::Utf8Str &aPath, ULONG aMode,
4315 const std::vector<DirectoryCreateFlag_T> &aFlags)
4316{
4317 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
4318 return setError(E_INVALIDARG, tr("No directory to create specified"));
4319
4320 uint32_t fFlags = DirectoryCreateFlag_None;
4321 if (aFlags.size())
4322 {
4323 for (size_t i = 0; i < aFlags.size(); i++)
4324 fFlags |= aFlags[i];
4325
4326 if (fFlags & ~DirectoryCreateFlag_Parents)
4327 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), fFlags);
4328 }
4329
4330 HRESULT hrc = i_isStartedExternal();
4331 if (FAILED(hrc))
4332 return hrc;
4333
4334 LogFlowThisFuncEnter();
4335
4336 ComObjPtr <GuestDirectory> pDirectory;
4337 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4338 int vrc = i_directoryCreate(aPath, (uint32_t)aMode, fFlags, &vrcGuest);
4339 if (RT_FAILURE(vrc))
4340 {
4341 if (GuestProcess::i_isGuestError(vrc))
4342 {
4343 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, vrcGuest, aPath.c_str());
4344 return setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Guest directory creation failed: %s"),
4345 GuestBase::getErrorAsString(ge).c_str());
4346 }
4347 switch (vrc)
4348 {
4349 case VERR_INVALID_PARAMETER:
4350 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Guest directory creation failed: Invalid parameters given"));
4351 break;
4352
4353 case VERR_BROKEN_PIPE:
4354 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Guest directory creation failed: Unexpectedly aborted"));
4355 break;
4356
4357 default:
4358 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Guest directory creation failed: %Rrc"), vrc);
4359 break;
4360 }
4361 }
4362
4363 return hrc;
4364}
4365
4366HRESULT GuestSession::directoryCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath,
4367 BOOL aSecure, com::Utf8Str &aDirectory)
4368{
4369 if (RT_UNLIKELY((aTemplateName.c_str()) == NULL || *(aTemplateName.c_str()) == '\0'))
4370 return setError(E_INVALIDARG, tr("No template specified"));
4371 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
4372 return setError(E_INVALIDARG, tr("No directory name specified"));
4373 if (!aSecure) /* Ignore what mode is specified when a secure temp thing needs to be created. */
4374 if (RT_UNLIKELY(aMode & ~07777))
4375 return setError(E_INVALIDARG, tr("Mode invalid (must be specified in octal mode)"));
4376
4377 HRESULT hrc = i_isStartedExternal();
4378 if (FAILED(hrc))
4379 return hrc;
4380
4381 LogFlowThisFuncEnter();
4382
4383 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4384 int vrc = i_fsCreateTemp(aTemplateName, aPath, true /* Directory */, aDirectory, aMode, RT_BOOL(aSecure), &vrcGuest);
4385 if (!RT_SUCCESS(vrc))
4386 {
4387 switch (vrc)
4388 {
4389 case VERR_GSTCTL_GUEST_ERROR:
4390 {
4391 GuestErrorInfo ge(GuestErrorInfo::Type_Fs, vrcGuest, aPath.c_str());
4392 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Temporary guest directory creation failed: %s"),
4393 GuestBase::getErrorAsString(ge).c_str());
4394 break;
4395 }
4396 default:
4397 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Temporary guest directory creation \"%s\" with template \"%s\" failed: %Rrc"),
4398 aPath.c_str(), aTemplateName.c_str(), vrc);
4399 break;
4400 }
4401 }
4402
4403 return hrc;
4404}
4405
4406HRESULT GuestSession::directoryExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
4407{
4408 if (RT_UNLIKELY(aPath.isEmpty()))
4409 return setError(E_INVALIDARG, tr("Empty path"));
4410
4411 HRESULT hrc = i_isStartedExternal();
4412 if (FAILED(hrc))
4413 return hrc;
4414
4415 LogFlowThisFuncEnter();
4416
4417 GuestFsObjData objData;
4418 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4419
4420 int vrc = i_directoryQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &vrcGuest);
4421 if (RT_SUCCESS(vrc))
4422 *aExists = TRUE;
4423 else
4424 {
4425 switch (vrc)
4426 {
4427 case VERR_GSTCTL_GUEST_ERROR:
4428 {
4429 switch (vrcGuest)
4430 {
4431 case VERR_PATH_NOT_FOUND:
4432 *aExists = FALSE;
4433 break;
4434 default:
4435 {
4436 GuestErrorInfo ge(GuestErrorInfo::Type_Fs, vrcGuest, aPath.c_str());
4437 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Querying directory existence failed: %s"),
4438 GuestBase::getErrorAsString(ge).c_str());
4439 break;
4440 }
4441 }
4442 break;
4443 }
4444
4445 case VERR_NOT_A_DIRECTORY:
4446 {
4447 *aExists = FALSE;
4448 break;
4449 }
4450
4451 default:
4452 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying directory existence \"%s\" failed: %Rrc"),
4453 aPath.c_str(), vrc);
4454 break;
4455 }
4456 }
4457
4458 return hrc;
4459}
4460
4461HRESULT GuestSession::directoryOpen(const com::Utf8Str &aPath, const com::Utf8Str &aFilter,
4462 const std::vector<DirectoryOpenFlag_T> &aFlags, ComPtr<IGuestDirectory> &aDirectory)
4463{
4464 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
4465 return setError(E_INVALIDARG, tr("No directory to open specified"));
4466 if (RT_UNLIKELY((aFilter.c_str()) != NULL && *(aFilter.c_str()) != '\0'))
4467 return setError(E_INVALIDARG, tr("Directory filters are not implemented yet"));
4468
4469 uint32_t fFlags = DirectoryOpenFlag_None;
4470 if (aFlags.size())
4471 {
4472 for (size_t i = 0; i < aFlags.size(); i++)
4473 fFlags |= aFlags[i];
4474
4475 if (fFlags)
4476 return setError(E_INVALIDARG, tr("Open flags (%#x) not implemented yet"), fFlags);
4477 }
4478
4479 HRESULT hrc = i_isStartedExternal();
4480 if (FAILED(hrc))
4481 return hrc;
4482
4483 LogFlowThisFuncEnter();
4484
4485 GuestDirectoryOpenInfo openInfo;
4486 openInfo.mPath = aPath;
4487 openInfo.mFilter = aFilter;
4488 openInfo.mFlags = fFlags;
4489
4490 ComObjPtr<GuestDirectory> pDirectory;
4491 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4492 int vrc = i_directoryOpen(openInfo, pDirectory, &vrcGuest);
4493 if (RT_SUCCESS(vrc))
4494 {
4495 /* Return directory object to the caller. */
4496 hrc = pDirectory.queryInterfaceTo(aDirectory.asOutParam());
4497 }
4498 else
4499 {
4500 switch (vrc)
4501 {
4502 case VERR_INVALID_PARAMETER:
4503 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest directory \"%s\" failed; invalid parameters given"),
4504 aPath.c_str());
4505 break;
4506
4507 case VERR_GSTCTL_GUEST_ERROR:
4508 {
4509 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, vrcGuest, aPath.c_str());
4510 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Opening guest directory failed: %s"),
4511 GuestBase::getErrorAsString(ge).c_str());
4512 break;
4513 }
4514 default:
4515 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4516 break;
4517 }
4518 }
4519
4520 return hrc;
4521}
4522
4523HRESULT GuestSession::directoryRemove(const com::Utf8Str &aPath)
4524{
4525 if (RT_UNLIKELY(aPath.c_str() == NULL || *aPath.c_str() == '\0'))
4526 return setError(E_INVALIDARG, tr("No directory to remove specified"));
4527
4528 HRESULT hrc = i_isStartedExternal();
4529 if (FAILED(hrc))
4530 return hrc;
4531
4532 LogFlowThisFuncEnter();
4533
4534 /* No flags; only remove the directory when empty. */
4535 uint32_t fFlags = DIRREMOVEREC_FLAG_NONE;
4536
4537 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4538 int vrc = i_directoryRemove(aPath, fFlags, &vrcGuest);
4539 if (RT_FAILURE(vrc))
4540 {
4541 switch (vrc)
4542 {
4543 case VERR_NOT_SUPPORTED:
4544 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4545 tr("Handling removing guest directories not supported by installed Guest Additions"));
4546 break;
4547
4548 case VERR_GSTCTL_GUEST_ERROR:
4549 {
4550 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, vrcGuest, aPath.c_str());
4551 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Removing guest directory failed: %s"),
4552 GuestBase::getErrorAsString(ge).c_str());
4553 break;
4554 }
4555 default:
4556 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing guest directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4557 break;
4558 }
4559 }
4560
4561 return hrc;
4562}
4563
4564HRESULT GuestSession::directoryRemoveRecursive(const com::Utf8Str &aPath, const std::vector<DirectoryRemoveRecFlag_T> &aFlags,
4565 ComPtr<IProgress> &aProgress)
4566{
4567 if (RT_UNLIKELY(aPath.c_str() == NULL || *aPath.c_str() == '\0'))
4568 return setError(E_INVALIDARG, tr("No directory to remove recursively specified"));
4569
4570 /* By defautl remove recursively as the function name implies. */
4571 uint32_t fFlags = DIRREMOVEREC_FLAG_RECURSIVE;
4572 if (aFlags.size())
4573 {
4574 for (size_t i = 0; i < aFlags.size(); i++)
4575 {
4576 switch (aFlags[i])
4577 {
4578 case DirectoryRemoveRecFlag_None: /* Skip. */
4579 continue;
4580
4581 case DirectoryRemoveRecFlag_ContentAndDir:
4582 fFlags |= DIRREMOVEREC_FLAG_CONTENT_AND_DIR;
4583 break;
4584
4585 case DirectoryRemoveRecFlag_ContentOnly:
4586 fFlags |= DIRREMOVEREC_FLAG_CONTENT_ONLY;
4587 break;
4588
4589 default:
4590 return setError(E_INVALIDARG, tr("Invalid flags specified"));
4591 }
4592 }
4593 }
4594
4595 HRESULT hrc = i_isStartedExternal();
4596 if (FAILED(hrc))
4597 return hrc;
4598
4599 LogFlowThisFuncEnter();
4600
4601 ComObjPtr<Progress> pProgress;
4602 hrc = pProgress.createObject();
4603 if (SUCCEEDED(hrc))
4604 hrc = pProgress->init(static_cast<IGuestSession *>(this),
4605 Bstr(tr("Removing guest directory")).raw(),
4606 TRUE /*aCancelable*/);
4607 if (FAILED(hrc))
4608 return hrc;
4609
4610 /* Note: At the moment we don't supply progress information while
4611 * deleting a guest directory recursively. So just complete
4612 * the progress object right now. */
4613 /** @todo Implement progress reporting on guest directory deletion! */
4614 hrc = pProgress->i_notifyComplete(S_OK);
4615 if (FAILED(hrc))
4616 return hrc;
4617
4618 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4619 int vrc = i_directoryRemove(aPath, fFlags, &vrcGuest);
4620 if (RT_FAILURE(vrc))
4621 {
4622 switch (vrc)
4623 {
4624 case VERR_NOT_SUPPORTED:
4625 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4626 tr("Handling removing guest directories recursively not supported by installed Guest Additions"));
4627 break;
4628
4629 case VERR_GSTCTL_GUEST_ERROR:
4630 {
4631 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, vrcGuest, aPath.c_str());
4632 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Recursively removing guest directory failed: %s"),
4633 GuestBase::getErrorAsString(ge).c_str());
4634 break;
4635 }
4636 default:
4637 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Recursively removing guest directory \"%s\" failed: %Rrc"),
4638 aPath.c_str(), vrc);
4639 break;
4640 }
4641 }
4642 else
4643 {
4644 pProgress.queryInterfaceTo(aProgress.asOutParam());
4645 }
4646
4647 return hrc;
4648}
4649
4650HRESULT GuestSession::environmentScheduleSet(const com::Utf8Str &aName, const com::Utf8Str &aValue)
4651{
4652 LogFlowThisFuncEnter();
4653 int vrc;
4654 {
4655 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4656 vrc = mData.mEnvironmentChanges.setVariable(aName, aValue);
4657 }
4658 HRESULT hrc;
4659 if (RT_SUCCESS(vrc))
4660 hrc = S_OK;
4661 else if (vrc == VERR_ENV_INVALID_VAR_NAME)
4662 hrc = setError(E_INVALIDARG, tr("Invalid environment variable name '%s'"), aName.c_str());
4663 else
4664 hrc = setErrorVrc(vrc, tr("Failed to schedule setting environment variable '%s' to '%s'"), aName.c_str(), aValue.c_str());
4665
4666 LogFlowThisFuncLeave();
4667 return hrc;
4668}
4669
4670HRESULT GuestSession::environmentScheduleUnset(const com::Utf8Str &aName)
4671{
4672 LogFlowThisFuncEnter();
4673 int vrc;
4674 {
4675 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4676 vrc = mData.mEnvironmentChanges.unsetVariable(aName);
4677 }
4678 HRESULT hrc;
4679 if (RT_SUCCESS(vrc))
4680 hrc = S_OK;
4681 else if (vrc == VERR_ENV_INVALID_VAR_NAME)
4682 hrc = setError(E_INVALIDARG, tr("Invalid environment variable name '%s'"), aName.c_str());
4683 else
4684 hrc = setErrorVrc(vrc, tr("Failed to schedule unsetting environment variable '%s'"), aName.c_str());
4685
4686 LogFlowThisFuncLeave();
4687 return hrc;
4688}
4689
4690HRESULT GuestSession::environmentGetBaseVariable(const com::Utf8Str &aName, com::Utf8Str &aValue)
4691{
4692 LogFlowThisFuncEnter();
4693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4694
4695 HRESULT hrc;
4696 if (mData.mpBaseEnvironment)
4697 {
4698 int vrc = mData.mpBaseEnvironment->getVariable(aName, &aValue);
4699 if (RT_SUCCESS(vrc))
4700 hrc = S_OK;
4701 else if (vrc == VERR_ENV_INVALID_VAR_NAME)
4702 hrc = setError(E_INVALIDARG, tr("Invalid environment variable name '%s'"), aName.c_str());
4703 else
4704 hrc = setErrorVrc(vrc);
4705 }
4706 else if (mData.mProtocolVersion < 99999)
4707 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the Guest Additions"));
4708 else
4709 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
4710
4711 LogFlowThisFuncLeave();
4712 return hrc;
4713}
4714
4715HRESULT GuestSession::environmentDoesBaseVariableExist(const com::Utf8Str &aName, BOOL *aExists)
4716{
4717 LogFlowThisFuncEnter();
4718 *aExists = FALSE;
4719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4720
4721 HRESULT hrc;
4722 if (mData.mpBaseEnvironment)
4723 {
4724 hrc = S_OK;
4725 *aExists = mData.mpBaseEnvironment->doesVariableExist(aName);
4726 }
4727 else if (mData.mProtocolVersion < 99999)
4728 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the Guest Additions"));
4729 else
4730 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
4731
4732 LogFlowThisFuncLeave();
4733 return hrc;
4734}
4735
4736HRESULT GuestSession::fileCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath, BOOL aSecure,
4737 ComPtr<IGuestFile> &aFile)
4738{
4739 RT_NOREF(aTemplateName, aMode, aPath, aSecure, aFile);
4740 ReturnComNotImplemented();
4741}
4742
4743HRESULT GuestSession::fileExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
4744{
4745 /* By default we return non-existent. */
4746 *aExists = FALSE;
4747
4748 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
4749 return S_OK;
4750
4751 HRESULT hrc = i_isStartedExternal();
4752 if (FAILED(hrc))
4753 return hrc;
4754
4755 LogFlowThisFuncEnter();
4756
4757 GuestFsObjData objData;
4758 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4759 int vrc = i_fileQueryInfo(aPath, RT_BOOL(aFollowSymlinks), objData, &vrcGuest);
4760 if (RT_SUCCESS(vrc))
4761 {
4762 *aExists = TRUE;
4763 return S_OK;
4764 }
4765
4766 switch (vrc)
4767 {
4768 case VERR_GSTCTL_GUEST_ERROR:
4769 {
4770 switch (vrcGuest)
4771 {
4772 case VERR_PATH_NOT_FOUND:
4773 RT_FALL_THROUGH();
4774 case VERR_FILE_NOT_FOUND:
4775 break;
4776
4777 default:
4778 {
4779 GuestErrorInfo ge(GuestErrorInfo::Type_Fs, vrcGuest, aPath.c_str());
4780 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Querying guest file existence failed: %s"),
4781 GuestBase::getErrorAsString(ge).c_str());
4782 break;
4783 }
4784 }
4785
4786 break;
4787 }
4788
4789 case VERR_NOT_A_FILE:
4790 break;
4791
4792 default:
4793 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying guest file information for \"%s\" failed: %Rrc"),
4794 aPath.c_str(), vrc);
4795 break;
4796 }
4797
4798 return hrc;
4799}
4800
4801HRESULT GuestSession::fileOpen(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
4802 ULONG aCreationMode, ComPtr<IGuestFile> &aFile)
4803{
4804 LogFlowThisFuncEnter();
4805
4806 const std::vector<FileOpenExFlag_T> EmptyFlags;
4807 return fileOpenEx(aPath, aAccessMode, aOpenAction, FileSharingMode_All, aCreationMode, EmptyFlags, aFile);
4808}
4809
4810HRESULT GuestSession::fileOpenEx(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
4811 FileSharingMode_T aSharingMode, ULONG aCreationMode,
4812 const std::vector<FileOpenExFlag_T> &aFlags, ComPtr<IGuestFile> &aFile)
4813{
4814 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
4815 return setError(E_INVALIDARG, tr("No file to open specified"));
4816
4817 HRESULT hrc = i_isStartedExternal();
4818 if (FAILED(hrc))
4819 return hrc;
4820
4821 LogFlowThisFuncEnter();
4822
4823 /* Validate aAccessMode. */
4824 switch (aAccessMode)
4825 {
4826 case FileAccessMode_ReadOnly:
4827 RT_FALL_THRU();
4828 case FileAccessMode_WriteOnly:
4829 RT_FALL_THRU();
4830 case FileAccessMode_ReadWrite:
4831 break;
4832 case FileAccessMode_AppendOnly:
4833 RT_FALL_THRU();
4834 case FileAccessMode_AppendRead:
4835 return setError(E_NOTIMPL, tr("Append access modes are not yet implemented"));
4836 default:
4837 return setError(E_INVALIDARG, tr("Unknown FileAccessMode value %u (%#x)"), aAccessMode, aAccessMode);
4838 }
4839
4840 /* Validate aOpenAction to the old format. */
4841 switch (aOpenAction)
4842 {
4843 case FileOpenAction_OpenExisting:
4844 RT_FALL_THRU();
4845 case FileOpenAction_OpenOrCreate:
4846 RT_FALL_THRU();
4847 case FileOpenAction_CreateNew:
4848 RT_FALL_THRU();
4849 case FileOpenAction_CreateOrReplace:
4850 RT_FALL_THRU();
4851 case FileOpenAction_OpenExistingTruncated:
4852 RT_FALL_THRU();
4853 case FileOpenAction_AppendOrCreate:
4854 break;
4855 default:
4856 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
4857 }
4858
4859 /* Validate aSharingMode. */
4860 switch (aSharingMode)
4861 {
4862 case FileSharingMode_All:
4863 break;
4864 case FileSharingMode_Read:
4865 case FileSharingMode_Write:
4866 case FileSharingMode_ReadWrite:
4867 case FileSharingMode_Delete:
4868 case FileSharingMode_ReadDelete:
4869 case FileSharingMode_WriteDelete:
4870 return setError(E_NOTIMPL, tr("Only FileSharingMode_All is currently implemented"));
4871
4872 default:
4873 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
4874 }
4875
4876 /* Combine and validate flags. */
4877 uint32_t fOpenEx = 0;
4878 for (size_t i = 0; i < aFlags.size(); i++)
4879 fOpenEx |= aFlags[i];
4880 if (fOpenEx)
4881 return setError(E_INVALIDARG, tr("Unsupported FileOpenExFlag value(s) in aFlags (%#x)"), fOpenEx);
4882
4883 ComObjPtr <GuestFile> pFile;
4884 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4885 int vrc = i_fileOpenEx(aPath, aAccessMode, aOpenAction, aSharingMode, aCreationMode, aFlags, pFile, &vrcGuest);
4886 if (RT_SUCCESS(vrc))
4887 /* Return directory object to the caller. */
4888 hrc = pFile.queryInterfaceTo(aFile.asOutParam());
4889 else
4890 {
4891 switch (vrc)
4892 {
4893 case VERR_NOT_SUPPORTED:
4894 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4895 tr("Handling guest files not supported by installed Guest Additions"));
4896 break;
4897
4898 case VERR_GSTCTL_GUEST_ERROR:
4899 {
4900 GuestErrorInfo ge(GuestErrorInfo::Type_File, vrcGuest, aPath.c_str());
4901 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Opening guest file failed: %s"),
4902 GuestBase::getErrorAsString(ge).c_str());
4903 break;
4904 }
4905 default:
4906 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4907 break;
4908 }
4909 }
4910
4911 return hrc;
4912}
4913
4914HRESULT GuestSession::fileQuerySize(const com::Utf8Str &aPath, BOOL aFollowSymlinks, LONG64 *aSize)
4915{
4916 if (aPath.isEmpty())
4917 return setError(E_INVALIDARG, tr("No path specified"));
4918
4919 HRESULT hrc = i_isStartedExternal();
4920 if (FAILED(hrc))
4921 return hrc;
4922
4923 int64_t llSize;
4924 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4925 int vrc = i_fileQuerySize(aPath, aFollowSymlinks != FALSE, &llSize, &vrcGuest);
4926 if (RT_SUCCESS(vrc))
4927 *aSize = llSize;
4928 else
4929 {
4930 if (GuestProcess::i_isGuestError(vrc))
4931 {
4932 GuestErrorInfo ge(GuestErrorInfo::Type_Fs, vrcGuest, aPath.c_str());
4933 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Querying guest file size failed: %s"),
4934 GuestBase::getErrorAsString(ge).c_str());
4935 }
4936 else
4937 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying guest file size of \"%s\" failed: %Rrc"),
4938 vrc, aPath.c_str());
4939 }
4940
4941 return hrc;
4942}
4943
4944HRESULT GuestSession::fsQueryFreeSpace(const com::Utf8Str &aPath, LONG64 *aFreeSpace)
4945{
4946 ComPtr<IGuestFsInfo> pFsInfo;
4947 HRESULT hrc = fsQueryInfo(aPath, pFsInfo);
4948 if (SUCCEEDED(hrc))
4949 hrc = pFsInfo->COMGETTER(FreeSize)(aFreeSpace);
4950
4951 return hrc;
4952}
4953
4954HRESULT GuestSession::fsQueryInfo(const com::Utf8Str &aPath, ComPtr<IGuestFsInfo> &aInfo)
4955{
4956 if (aPath.isEmpty())
4957 return setError(E_INVALIDARG, tr("No path specified"));
4958
4959 HRESULT hrc = i_isStartedExternal();
4960 if (FAILED(hrc))
4961 return hrc;
4962
4963 GSTCTLFSINFO fsInfo;
4964 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4965 int vrc = i_fsQueryInfo(aPath, &fsInfo, &vrcGuest);
4966 if (RT_SUCCESS(vrc))
4967 {
4968 ComObjPtr<GuestFsInfo> ptrFsInfo;
4969 hrc = ptrFsInfo.createObject();
4970 if (SUCCEEDED(hrc))
4971 {
4972 vrc = ptrFsInfo->init(&fsInfo);
4973 if (RT_SUCCESS(vrc))
4974 hrc = ptrFsInfo.queryInterfaceTo(aInfo.asOutParam());
4975 else
4976 hrc = setErrorVrc(vrc);
4977 }
4978 }
4979 else
4980 {
4981 if (GuestProcess::i_isGuestError(vrc))
4982 {
4983 GuestErrorInfo ge(GuestErrorInfo::Type_Fs, vrcGuest, aPath.c_str());
4984 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Querying guest filesystem information failed: %s"),
4985 GuestBase::getErrorAsString(ge).c_str());
4986 }
4987 else
4988 hrc = setErrorVrc(vrc, tr("Querying guest filesystem information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4989 }
4990
4991 return hrc;
4992}
4993
4994HRESULT GuestSession::fsObjExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
4995{
4996 if (aPath.isEmpty())
4997 return setError(E_INVALIDARG, tr("No path specified"));
4998
4999 HRESULT hrc = i_isStartedExternal();
5000 if (FAILED(hrc))
5001 return hrc;
5002
5003 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
5004
5005 *aExists = false;
5006
5007 GuestFsObjData objData;
5008 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
5009 int vrc = i_fsObjQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &vrcGuest);
5010 if (RT_SUCCESS(vrc))
5011 *aExists = TRUE;
5012 else
5013 {
5014 if (GuestProcess::i_isGuestError(vrc))
5015 {
5016 if ( vrcGuest == VERR_NOT_A_FILE
5017 || vrcGuest == VERR_PATH_NOT_FOUND
5018 || vrcGuest == VERR_FILE_NOT_FOUND
5019 || vrcGuest == VERR_INVALID_NAME)
5020 hrc = S_OK; /* Ignore these vrc values. */
5021 else
5022 {
5023 GuestErrorInfo ge(GuestErrorInfo::Type_Fs, vrcGuest, aPath.c_str());
5024 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Querying guest file existence information failed: %s"),
5025 GuestBase::getErrorAsString(ge).c_str());
5026 }
5027 }
5028 else
5029 hrc = setErrorVrc(vrc, tr("Querying guest file existence information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
5030 }
5031
5032 return hrc;
5033}
5034
5035HRESULT GuestSession::fsObjQueryInfo(const com::Utf8Str &aPath, BOOL aFollowSymlinks, ComPtr<IGuestFsObjInfo> &aInfo)
5036{
5037 if (aPath.isEmpty())
5038 return setError(E_INVALIDARG, tr("No path specified"));
5039
5040 HRESULT hrc = i_isStartedExternal();
5041 if (FAILED(hrc))
5042 return hrc;
5043
5044 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
5045
5046 GuestFsObjData objData;
5047 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
5048 int vrc = i_fsObjQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &vrcGuest);
5049 if (RT_SUCCESS(vrc))
5050 {
5051 ComObjPtr<GuestFsObjInfo> ptrFsObjInfo;
5052 hrc = ptrFsObjInfo.createObject();
5053 if (SUCCEEDED(hrc))
5054 {
5055 vrc = ptrFsObjInfo->init(objData);
5056 if (RT_SUCCESS(vrc))
5057 hrc = ptrFsObjInfo.queryInterfaceTo(aInfo.asOutParam());
5058 else
5059 hrc = setErrorVrc(vrc);
5060 }
5061 }
5062 else
5063 {
5064 if (GuestProcess::i_isGuestError(vrc))
5065 {
5066 GuestErrorInfo ge(GuestErrorInfo::Type_Fs, vrcGuest, aPath.c_str());
5067 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Querying guest filesystem object information failed: %s"),
5068 GuestBase::getErrorAsString(ge).c_str());
5069 }
5070 else
5071 hrc = setErrorVrc(vrc, tr("Querying guest filesystem object information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
5072 }
5073
5074 return hrc;
5075}
5076
5077HRESULT GuestSession::fsObjRemove(const com::Utf8Str &aPath)
5078{
5079 if (RT_UNLIKELY(aPath.isEmpty()))
5080 return setError(E_INVALIDARG, tr("No path specified"));
5081
5082 HRESULT hrc = i_isStartedExternal();
5083 if (FAILED(hrc))
5084 return hrc;
5085
5086 LogFlowThisFunc(("aPath=%s\n", aPath.c_str()));
5087
5088 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
5089 int vrc = i_fileRemove(aPath, &vrcGuest);
5090 if (RT_FAILURE(vrc))
5091 {
5092 if (GuestProcess::i_isGuestError(vrc))
5093 {
5094 GuestErrorInfo ge(GuestErrorInfo::Type_File, vrcGuest, aPath.c_str());
5095 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Removing guest file failed: %s"),
5096 GuestBase::getErrorAsString(ge).c_str());
5097 }
5098 else
5099 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing guest file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
5100 }
5101
5102 return hrc;
5103}
5104
5105HRESULT GuestSession::fsObjRemoveArray(const std::vector<com::Utf8Str> &aPaths, ComPtr<IProgress> &aProgress)
5106{
5107 RT_NOREF(aPaths, aProgress);
5108 return E_NOTIMPL;
5109}
5110
5111HRESULT GuestSession::fsObjRename(const com::Utf8Str &aSource,
5112 const com::Utf8Str &aDestination,
5113 const std::vector<FsObjRenameFlag_T> &aFlags)
5114{
5115 if (RT_UNLIKELY(aSource.isEmpty()))
5116 return setError(E_INVALIDARG, tr("No source path specified"));
5117
5118 if (RT_UNLIKELY(aDestination.isEmpty()))
5119 return setError(E_INVALIDARG, tr("No destination path specified"));
5120
5121 HRESULT hrc = i_isStartedExternal();
5122 if (FAILED(hrc))
5123 return hrc;
5124
5125 /* Combine, validate and convert flags. */
5126 uint32_t fApiFlags = 0;
5127 for (size_t i = 0; i < aFlags.size(); i++)
5128 fApiFlags |= aFlags[i];
5129 if (fApiFlags & ~((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace))
5130 return setError(E_INVALIDARG, tr("Unknown rename flag: %#x"), fApiFlags);
5131
5132 LogFlowThisFunc(("aSource=%s, aDestination=%s\n", aSource.c_str(), aDestination.c_str()));
5133
5134 AssertCompile(FsObjRenameFlag_NoReplace == 0);
5135 AssertCompile(FsObjRenameFlag_Replace != 0);
5136 uint32_t fBackend;
5137 if ((fApiFlags & ((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace)) == FsObjRenameFlag_Replace)
5138 fBackend = PATHRENAME_FLAG_REPLACE;
5139 else
5140 fBackend = PATHRENAME_FLAG_NO_REPLACE;
5141
5142 /* Call worker to do the job. */
5143 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
5144 int vrc = i_pathRename(aSource, aDestination, fBackend, &vrcGuest);
5145 if (RT_FAILURE(vrc))
5146 {
5147 switch (vrc)
5148 {
5149 case VERR_NOT_SUPPORTED:
5150 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5151 tr("Handling renaming guest paths not supported by installed Guest Additions"));
5152 break;
5153
5154 case VERR_GSTCTL_GUEST_ERROR:
5155 {
5156 GuestErrorInfo ge(GuestErrorInfo::Type_Process, vrcGuest, aSource.c_str());
5157 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Renaming guest path failed: %s"),
5158 GuestBase::getErrorAsString(ge).c_str());
5159 break;
5160 }
5161 default:
5162 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming guest path \"%s\" failed: %Rrc"),
5163 aSource.c_str(), vrc);
5164 break;
5165 }
5166 }
5167
5168 return hrc;
5169}
5170
5171HRESULT GuestSession::fsObjMove(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
5172 const std::vector<FsObjMoveFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
5173{
5174 RT_NOREF(aSource, aDestination, aFlags, aProgress);
5175 ReturnComNotImplemented();
5176}
5177
5178HRESULT GuestSession::fsObjMoveArray(const std::vector<com::Utf8Str> &aSource,
5179 const com::Utf8Str &aDestination,
5180 const std::vector<FsObjMoveFlag_T> &aFlags,
5181 ComPtr<IProgress> &aProgress)
5182{
5183 RT_NOREF(aSource, aDestination, aFlags, aProgress);
5184 ReturnComNotImplemented();
5185}
5186
5187HRESULT GuestSession::fsObjCopyArray(const std::vector<com::Utf8Str> &aSource,
5188 const com::Utf8Str &aDestination,
5189 const std::vector<FileCopyFlag_T> &aFlags,
5190 ComPtr<IProgress> &aProgress)
5191{
5192 RT_NOREF(aSource, aDestination, aFlags, aProgress);
5193 ReturnComNotImplemented();
5194}
5195
5196HRESULT GuestSession::fsObjSetACL(const com::Utf8Str &aPath, BOOL aFollowSymlinks, const com::Utf8Str &aAcl, ULONG aMode)
5197{
5198 RT_NOREF(aPath, aFollowSymlinks, aAcl, aMode);
5199 ReturnComNotImplemented();
5200}
5201
5202
5203HRESULT GuestSession::processCreate(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
5204 const com::Utf8Str &aCwd,
5205 const std::vector<com::Utf8Str> &aEnvironment,
5206 const std::vector<ProcessCreateFlag_T> &aFlags,
5207 ULONG aTimeoutMS, ComPtr<IGuestProcess> &aGuestProcess)
5208{
5209 LogFlowThisFuncEnter();
5210
5211 std::vector<LONG> affinityIgnored;
5212 return processCreateEx(aExecutable, aArguments, aCwd, aEnvironment, aFlags, aTimeoutMS,
5213 ProcessPriority_Default, affinityIgnored, aGuestProcess);
5214}
5215
5216HRESULT GuestSession::processCreateEx(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
5217 const com::Utf8Str &aCwd,
5218 const std::vector<com::Utf8Str> &aEnvironment,
5219 const std::vector<ProcessCreateFlag_T> &aFlags, ULONG aTimeoutMS,
5220 ProcessPriority_T aPriority, const std::vector<LONG> &aAffinity,
5221 ComPtr<IGuestProcess> &aGuestProcess)
5222{
5223 HRESULT hrc = i_isStartedExternal();
5224 if (FAILED(hrc))
5225 return hrc;
5226
5227 /*
5228 * Must have an executable to execute. If none is given, we try use the
5229 * zero'th argument.
5230 */
5231 const char *pszExecutable = aExecutable.c_str();
5232 if (RT_UNLIKELY(pszExecutable == NULL || *pszExecutable == '\0'))
5233 {
5234 if (aArguments.size() > 0)
5235 pszExecutable = aArguments[0].c_str();
5236 if (pszExecutable == NULL || *pszExecutable == '\0')
5237 return setError(E_INVALIDARG, tr("No command to execute specified"));
5238 }
5239
5240 uint32_t const uProtocol = i_getProtocolVersion();
5241 uint64_t const fGuestControlFeatures0 = mParent->i_getGuestControlFeatures0();
5242
5243 /* If a current working directory (CWD) is set, make sure that the installed Guest Additions actually
5244 * support this before doing anything else. */
5245 if ( !aCwd.isEmpty()
5246 && ( uProtocol < 2
5247 || !(fGuestControlFeatures0 & VBOX_GUESTCTRL_GF_0_PROCESS_CWD)))
5248 return setError(VBOX_E_NOT_SUPPORTED,
5249 tr("Setting the current working directory is not supported by the installed Guest Addtions!"));
5250
5251 /* The rest of the input is being validated in i_processCreateEx(). */
5252
5253 LogFlowThisFuncEnter();
5254
5255 /*
5256 * Build the process startup info.
5257 */
5258 GuestProcessStartupInfo procInfo;
5259
5260 /* Executable and arguments. */
5261 procInfo.mExecutable = pszExecutable;
5262 if (aArguments.size())
5263 {
5264 for (size_t i = 0; i < aArguments.size(); i++)
5265 procInfo.mArguments.push_back(aArguments[i]);
5266 }
5267 else /* If no arguments were given, add the executable as argv[0] by default. */
5268 procInfo.mArguments.push_back(procInfo.mExecutable);
5269
5270 /* Optional working directory */
5271 procInfo.mCwd = aCwd;
5272
5273 /* Combine the environment changes associated with the ones passed in by
5274 the caller, giving priority to the latter. The changes are putenv style
5275 and will be applied to the standard environment for the guest user. */
5276 int vrc = procInfo.mEnvironmentChanges.copy(mData.mEnvironmentChanges);
5277 if (RT_SUCCESS(vrc))
5278 {
5279 size_t idxError = ~(size_t)0;
5280 vrc = procInfo.mEnvironmentChanges.applyPutEnvArray(aEnvironment, &idxError);
5281 if (RT_SUCCESS(vrc))
5282 {
5283 /* Convert the flag array into a mask. */
5284 if (aFlags.size())
5285 for (size_t i = 0; i < aFlags.size(); i++)
5286 procInfo.mFlags |= aFlags[i];
5287
5288 procInfo.mTimeoutMS = aTimeoutMS;
5289
5290 /** @todo use RTCPUSET instead of archaic 64-bit variables! */
5291 if (aAffinity.size())
5292 for (size_t i = 0; i < aAffinity.size(); i++)
5293 if (aAffinity[i])
5294 procInfo.mAffinity |= (uint64_t)1 << i;
5295
5296 procInfo.mPriority = aPriority;
5297
5298 /*
5299 * Create a guest process object.
5300 */
5301 ComObjPtr<GuestProcess> pProcess;
5302 vrc = i_processCreateEx(procInfo, pProcess);
5303 if (RT_SUCCESS(vrc))
5304 {
5305 ComPtr<IGuestProcess> pIProcess;
5306 hrc = pProcess.queryInterfaceTo(pIProcess.asOutParam());
5307 if (SUCCEEDED(hrc))
5308 {
5309 /*
5310 * Start the process.
5311 */
5312 vrc = pProcess->i_startProcessAsync();
5313 if (RT_SUCCESS(vrc))
5314 {
5315 aGuestProcess = pIProcess;
5316
5317 LogFlowFuncLeaveRC(vrc);
5318 return S_OK;
5319 }
5320
5321 hrc = setErrorVrc(vrc, tr("Failed to start guest process: %Rrc"), vrc);
5322 }
5323 }
5324 else if (vrc == VERR_GSTCTL_MAX_CID_OBJECTS_REACHED)
5325 hrc = setErrorVrc(vrc, tr("Maximum number of concurrent guest processes per session (%u) reached"),
5326 VBOX_GUESTCTRL_MAX_OBJECTS);
5327 else
5328 hrc = setErrorVrc(vrc, tr("Failed to create guest process object: %Rrc"), vrc);
5329 }
5330 else
5331 hrc = setErrorBoth(vrc == VERR_ENV_INVALID_VAR_NAME ? E_INVALIDARG : Global::vboxStatusCodeToCOM(vrc), vrc,
5332 tr("Failed to apply environment variable '%s', index %u (%Rrc)'"),
5333 aEnvironment[idxError].c_str(), idxError, vrc);
5334 }
5335 else
5336 hrc = setErrorVrc(vrc, tr("Failed to set up the environment: %Rrc"), vrc);
5337
5338 LogFlowFuncLeaveRC(vrc);
5339 return hrc;
5340}
5341
5342HRESULT GuestSession::processGet(ULONG aPid, ComPtr<IGuestProcess> &aGuestProcess)
5343
5344{
5345 if (aPid == 0)
5346 return setError(E_INVALIDARG, tr("No valid process ID (PID) specified"));
5347
5348 LogFlowThisFunc(("PID=%RU32\n", aPid));
5349
5350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5351
5352 HRESULT hrc = S_OK;
5353
5354 ComObjPtr<GuestProcess> pProcess;
5355 int vrc = i_processGetByPID(aPid, &pProcess);
5356 if (RT_FAILURE(vrc))
5357 hrc = setError(E_INVALIDARG, tr("No process with PID %RU32 found"), aPid);
5358
5359 /* This will set (*aProcess) to NULL if pProgress is NULL. */
5360 HRESULT hrc2 = pProcess.queryInterfaceTo(aGuestProcess.asOutParam());
5361 if (SUCCEEDED(hrc))
5362 hrc = hrc2;
5363
5364 LogFlowThisFunc(("aProcess=%p, hrc=%Rhrc\n", (IGuestProcess*)aGuestProcess, hrc));
5365 return hrc;
5366}
5367
5368HRESULT GuestSession::symlinkCreate(const com::Utf8Str &aSource, const com::Utf8Str &aTarget, SymlinkType_T aType)
5369{
5370 RT_NOREF(aSource, aTarget, aType);
5371 ReturnComNotImplemented();
5372}
5373
5374HRESULT GuestSession::symlinkExists(const com::Utf8Str &aSymlink, BOOL *aExists)
5375
5376{
5377 RT_NOREF(aSymlink, aExists);
5378 ReturnComNotImplemented();
5379}
5380
5381HRESULT GuestSession::symlinkRead(const com::Utf8Str &aSymlink, const std::vector<SymlinkReadFlag_T> &aFlags,
5382 com::Utf8Str &aTarget)
5383{
5384 RT_NOREF(aSymlink, aFlags, aTarget);
5385 ReturnComNotImplemented();
5386}
5387
5388/* Deprecated; use GuestSession::waitForArray() instead. */
5389HRESULT GuestSession::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
5390{
5391 /* Note: No call to i_isStartedExternal() needed here, as the session might not has been started (yet). */
5392
5393 LogFlowThisFuncEnter();
5394
5395 HRESULT hrc = S_OK;
5396
5397 /*
5398 * Note: Do not hold any locks here while waiting!
5399 */
5400 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS; GuestSessionWaitResult_T waitResult;
5401 int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &vrcGuest);
5402 if (RT_SUCCESS(vrc))
5403 *aReason = waitResult;
5404 else
5405 {
5406 switch (vrc)
5407 {
5408 case VERR_GSTCTL_GUEST_ERROR:
5409 {
5410 GuestErrorInfo ge(GuestErrorInfo::Type_Session, vrcGuest, mData.mSession.mName.c_str());
5411 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Waiting for guest process failed: %s"),
5412 GuestBase::getErrorAsString(ge).c_str());
5413 break;
5414 }
5415 case VERR_TIMEOUT:
5416 *aReason = GuestSessionWaitResult_Timeout;
5417 break;
5418
5419 default:
5420 {
5421 const char *pszSessionName = mData.mSession.mName.c_str();
5422 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5423 tr("Waiting for guest session \"%s\" failed: %Rrc"),
5424 pszSessionName ? pszSessionName : tr("Unnamed"), vrc);
5425 break;
5426 }
5427 }
5428 }
5429
5430 LogFlowFuncLeaveRC(vrc);
5431 return hrc;
5432}
5433
5434HRESULT GuestSession::waitForArray(const std::vector<GuestSessionWaitForFlag_T> &aWaitFor, ULONG aTimeoutMS,
5435 GuestSessionWaitResult_T *aReason)
5436{
5437 /* Note: No call to i_isStartedExternal() needed here, as the session might not has been started (yet). */
5438
5439 LogFlowThisFuncEnter();
5440
5441 /*
5442 * Note: Do not hold any locks here while waiting!
5443 */
5444 uint32_t fWaitFor = GuestSessionWaitForFlag_None;
5445 for (size_t i = 0; i < aWaitFor.size(); i++)
5446 fWaitFor |= aWaitFor[i];
5447
5448 return WaitFor(fWaitFor, aTimeoutMS, aReason);
5449}
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