VirtualBox

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

Last change on this file since 104003 was 104003, checked in by vboxsync, 2 months ago

Guest Control/Main: Added a new define GSTCTL_DEFAULT_TIMEOUT_MS and replaced most of the hardcoded timeout values with it.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 183.0 KB
Line 
1/* $Id: GuestSessionImpl.cpp 104003 2024-03-22 16:13:12Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Guest session handling.
4 */
5
6/*
7 * Copyright (C) 2012-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_MAIN_GUESTSESSION
33#include "LoggingNew.h"
34
35#include "GuestImpl.h"
36#ifndef VBOX_WITH_GUEST_CONTROL
37# error "VBOX_WITH_GUEST_CONTROL must defined in this file"
38#endif
39#include "GuestSessionImpl.h"
40#include "GuestSessionImplTasks.h"
41#include "GuestFsInfoImpl.h"
42#include "GuestCtrlImplPrivate.h"
43#include "VirtualBoxErrorInfoImpl.h"
44
45#include "Global.h"
46#include "AutoCaller.h"
47#include "ProgressImpl.h"
48#include "VBoxEvents.h"
49#include "VMMDev.h"
50#include "ThreadTask.h"
51
52#include <memory> /* For auto_ptr. */
53
54#include <iprt/cpp/utils.h> /* For unconst(). */
55#include <iprt/ctype.h>
56#include <iprt/env.h>
57#include <iprt/file.h> /* For CopyTo/From. */
58#include <iprt/path.h>
59#include <iprt/rand.h>
60
61#include <VBox/com/array.h>
62#include <VBox/com/listeners.h>
63#include <VBox/version.h>
64
65
66/**
67 * Base class representing an internal
68 * asynchronous session task.
69 */
70class GuestSessionTaskInternal : public ThreadTask
71{
72public:
73
74 GuestSessionTaskInternal(GuestSession *pSession)
75 : ThreadTask("GenericGuestSessionTaskInternal")
76 , mSession(pSession)
77 , mVrc(VINF_SUCCESS) { }
78
79 virtual ~GuestSessionTaskInternal(void) { }
80
81 /** Returns the last set result code. */
82 int vrc(void) const { return mVrc; }
83 /** Returns whether the last set result code indicates success or not. */
84 bool isOk(void) const { return RT_SUCCESS(mVrc); }
85 /** Returns the task's guest session object. */
86 const ComObjPtr<GuestSession> &Session(void) const { return mSession; }
87
88protected:
89
90 /** Guest session the task belongs to. */
91 const ComObjPtr<GuestSession> mSession;
92 /** The last set VBox status code. */
93 int mVrc;
94};
95
96/**
97 * Class for asynchronously starting a guest session.
98 */
99class GuestSessionTaskInternalStart : public GuestSessionTaskInternal
100{
101public:
102
103 GuestSessionTaskInternalStart(GuestSession *pSession)
104 : GuestSessionTaskInternal(pSession)
105 {
106 m_strTaskName = "gctlSesStart";
107 }
108
109 void handler()
110 {
111 /* Ignore return code */
112 GuestSession::i_startSessionThreadTask(this);
113 }
114};
115
116/**
117 * Internal listener class to serve events in an
118 * active manner, e.g. without polling delays.
119 */
120class GuestSessionListener
121{
122public:
123
124 GuestSessionListener(void)
125 {
126 }
127
128 virtual ~GuestSessionListener(void)
129 {
130 }
131
132 HRESULT init(GuestSession *pSession)
133 {
134 AssertPtrReturn(pSession, E_POINTER);
135 mSession = pSession;
136 return S_OK;
137 }
138
139 void uninit(void)
140 {
141 mSession = NULL;
142 }
143
144 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
145 {
146 switch (aType)
147 {
148 case VBoxEventType_OnGuestSessionStateChanged:
149 {
150 AssertPtrReturn(mSession, E_POINTER);
151 int vrc2 = mSession->signalWaitEvent(aType, aEvent);
152 RT_NOREF(vrc2);
153#ifdef DEBUG_andy
154 LogFlowFunc(("Signalling events of type=%RU32, session=%p resulted in vrc2=%Rrc\n", aType, mSession, vrc2));
155#endif
156 break;
157 }
158
159 default:
160 AssertMsgFailed(("Unhandled event %RU32\n", aType));
161 break;
162 }
163
164 return S_OK;
165 }
166
167private:
168
169 GuestSession *mSession;
170};
171typedef ListenerImpl<GuestSessionListener, GuestSession*> GuestSessionListenerImpl;
172
173VBOX_LISTENER_DECLARE(GuestSessionListenerImpl)
174
175// constructor / destructor
176/////////////////////////////////////////////////////////////////////////////
177
178DEFINE_EMPTY_CTOR_DTOR(GuestSession)
179
180HRESULT GuestSession::FinalConstruct(void)
181{
182 LogFlowThisFuncEnter();
183 return BaseFinalConstruct();
184}
185
186void GuestSession::FinalRelease(void)
187{
188 LogFlowThisFuncEnter();
189 uninit();
190 BaseFinalRelease();
191 LogFlowThisFuncLeave();
192}
193
194// public initializer/uninitializer for internal purposes only
195/////////////////////////////////////////////////////////////////////////////
196
197/**
198 * Initializes a guest session but does *not* open in on the guest side
199 * yet. This needs to be done via the openSession() / openSessionAsync calls.
200 *
201 * @returns VBox status code.
202 * @retval VERR_NOT_FOUND if the Guest Additions were not found (or were not reported yet).
203 * @param pGuest Guest object the guest session belongs to.
204 * @param ssInfo Guest session startup info to use.
205 * @param guestCreds Guest credentials to use for starting a guest session
206 * with a specific guest account.
207 */
208int GuestSession::init(Guest *pGuest, const GuestSessionStartupInfo &ssInfo,
209 const GuestCredentials &guestCreds)
210{
211 LogFlowThisFunc(("pGuest=%p, ssInfo=%p, guestCreds=%p\n",
212 pGuest, &ssInfo, &guestCreds));
213
214 /* Enclose the state transition NotReady->InInit->Ready. */
215 AutoInitSpan autoInitSpan(this);
216 AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
217
218 AssertPtrReturn(pGuest, VERR_INVALID_POINTER);
219
220 /*
221 * Initialize our data members from the input.
222 */
223 mParent = pGuest;
224 mConsole = pGuest->i_getConsole();
225
226 /* Copy over startup info. */
227 /** @todo Use an overloaded copy operator. Later. */
228 mData.mSession.mID = ssInfo.mID;
229 mData.mSession.mIsInternal = ssInfo.mIsInternal;
230 mData.mSession.mName = ssInfo.mName;
231 mData.mSession.mOpenFlags = ssInfo.mOpenFlags;
232 mData.mSession.mOpenTimeoutMS = ssInfo.mOpenTimeoutMS;
233
234 /* Copy over session credentials. */
235 /** @todo Use an overloaded copy operator. Later. */
236 mData.mCredentials.mUser = guestCreds.mUser;
237 mData.mCredentials.mPassword = guestCreds.mPassword;
238 mData.mCredentials.mDomain = guestCreds.mDomain;
239
240 /* Initialize the remainder of the data. */
241 mData.mVrc = VINF_SUCCESS;
242 mData.mStatus = GuestSessionStatus_Undefined;
243 mData.mpBaseEnvironment = NULL;
244
245 /*
246 * Register an object for the session itself to clearly
247 * distinguish callbacks which are for this session directly, or for
248 * objects (like files, directories, ...) which are bound to this session.
249 */
250 int vrc = i_objectRegister(NULL /* pObject */, SESSIONOBJECTTYPE_SESSION, &mData.mObjectID);
251 if (RT_SUCCESS(vrc))
252 {
253 vrc = mData.mEnvironmentChanges.initChangeRecord(pGuest->i_isGuestInWindowsNtFamily()
254 ? RTENV_CREATE_F_ALLOW_EQUAL_FIRST_IN_VAR : 0);
255 if (RT_SUCCESS(vrc))
256 {
257 vrc = RTCritSectInit(&mWaitEventCritSect);
258 AssertRC(vrc);
259 }
260 }
261
262 if (RT_SUCCESS(vrc))
263 vrc = i_determineProtocolVersion();
264
265 if (RT_SUCCESS(vrc))
266 {
267 /*
268 * <Replace this if you figure out what the code is doing.>
269 */
270 HRESULT hrc = unconst(mEventSource).createObject();
271 if (SUCCEEDED(hrc))
272 hrc = mEventSource->init();
273 if (SUCCEEDED(hrc))
274 {
275 try
276 {
277 GuestSessionListener *pListener = new GuestSessionListener();
278 ComObjPtr<GuestSessionListenerImpl> thisListener;
279 hrc = thisListener.createObject();
280 if (SUCCEEDED(hrc))
281 hrc = thisListener->init(pListener, this); /* thisListener takes ownership of pListener. */
282 if (SUCCEEDED(hrc))
283 {
284 com::SafeArray <VBoxEventType_T> eventTypes;
285 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
286 hrc = mEventSource->RegisterListener(thisListener,
287 ComSafeArrayAsInParam(eventTypes),
288 TRUE /* Active listener */);
289 if (SUCCEEDED(hrc))
290 {
291 mLocalListener = thisListener;
292
293 /*
294 * Mark this object as operational and return success.
295 */
296 autoInitSpan.setSucceeded();
297 LogFlowThisFunc(("mName=%s mID=%RU32 mIsInternal=%RTbool vrc=VINF_SUCCESS\n",
298 mData.mSession.mName.c_str(), mData.mSession.mID, mData.mSession.mIsInternal));
299 return VINF_SUCCESS;
300 }
301 }
302 }
303 catch (std::bad_alloc &)
304 {
305 hrc = E_OUTOFMEMORY;
306 }
307 }
308 vrc = Global::vboxStatusCodeFromCOM(hrc);
309 }
310
311 autoInitSpan.setFailed();
312 LogThisFunc(("Failed! mName=%s mID=%RU32 mIsInternal=%RTbool => vrc=%Rrc\n",
313 mData.mSession.mName.c_str(), mData.mSession.mID, mData.mSession.mIsInternal, vrc));
314 return vrc;
315}
316
317/**
318 * Uninitializes the instance.
319 * Called from FinalRelease().
320 */
321void GuestSession::uninit(void)
322{
323 LogFlowThisFuncEnter();
324
325 /* Enclose the state transition Ready->InUninit->NotReady. */
326 AutoUninitSpan autoUninitSpan(this);
327 if (autoUninitSpan.uninitDone())
328 return;
329
330 /* Call i_onRemove to take care of the object cleanups. */
331 i_onRemove();
332
333 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
334
335 /* Unregister the session's object ID. */
336 i_objectUnregister(mData.mObjectID);
337
338 Assert(mData.mObjects.size () == 0);
339 mData.mObjects.clear();
340
341 mData.mEnvironmentChanges.reset();
342
343 if (mData.mpBaseEnvironment)
344 {
345 mData.mpBaseEnvironment->releaseConst();
346 mData.mpBaseEnvironment = NULL;
347 }
348
349 /* Unitialize our local listener. */
350 mLocalListener.setNull();
351
352 baseUninit();
353
354 LogFlowFuncLeave();
355}
356
357// implementation of public getters/setters for attributes
358/////////////////////////////////////////////////////////////////////////////
359
360HRESULT GuestSession::getUser(com::Utf8Str &aUser)
361{
362 LogFlowThisFuncEnter();
363
364 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
365
366 aUser = mData.mCredentials.mUser;
367
368 LogFlowThisFuncLeave();
369 return S_OK;
370}
371
372HRESULT GuestSession::getDomain(com::Utf8Str &aDomain)
373{
374 LogFlowThisFuncEnter();
375
376 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
377
378 aDomain = mData.mCredentials.mDomain;
379
380 LogFlowThisFuncLeave();
381 return S_OK;
382}
383
384HRESULT GuestSession::getName(com::Utf8Str &aName)
385{
386 LogFlowThisFuncEnter();
387
388 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
389
390 aName = mData.mSession.mName;
391
392 LogFlowThisFuncLeave();
393 return S_OK;
394}
395
396HRESULT GuestSession::getId(ULONG *aId)
397{
398 LogFlowThisFuncEnter();
399
400 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
401
402 *aId = mData.mSession.mID;
403
404 LogFlowThisFuncLeave();
405 return S_OK;
406}
407
408HRESULT GuestSession::getStatus(GuestSessionStatus_T *aStatus)
409{
410 LogFlowThisFuncEnter();
411
412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
413
414 *aStatus = mData.mStatus;
415
416 LogFlowThisFuncLeave();
417 return S_OK;
418}
419
420HRESULT GuestSession::getTimeout(ULONG *aTimeout)
421{
422 LogFlowThisFuncEnter();
423
424 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
425
426 *aTimeout = mData.mTimeout;
427
428 LogFlowThisFuncLeave();
429 return S_OK;
430}
431
432HRESULT GuestSession::setTimeout(ULONG aTimeout)
433{
434 LogFlowThisFuncEnter();
435
436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
437
438 mData.mTimeout = aTimeout;
439
440 LogFlowThisFuncLeave();
441 return S_OK;
442}
443
444HRESULT GuestSession::getProtocolVersion(ULONG *aProtocolVersion)
445{
446 LogFlowThisFuncEnter();
447
448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
449
450 *aProtocolVersion = mData.mProtocolVersion;
451
452 LogFlowThisFuncLeave();
453 return S_OK;
454}
455
456HRESULT GuestSession::getEnvironmentChanges(std::vector<com::Utf8Str> &aEnvironmentChanges)
457{
458 LogFlowThisFuncEnter();
459
460 int vrc;
461 {
462 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
463 vrc = mData.mEnvironmentChanges.queryPutEnvArray(&aEnvironmentChanges);
464 }
465
466 LogFlowFuncLeaveRC(vrc);
467 return Global::vboxStatusCodeToCOM(vrc);
468}
469
470HRESULT GuestSession::setEnvironmentChanges(const std::vector<com::Utf8Str> &aEnvironmentChanges)
471{
472 LogFlowThisFuncEnter();
473
474 int vrc;
475 size_t idxError = ~(size_t)0;
476 {
477 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
478 mData.mEnvironmentChanges.reset();
479 vrc = mData.mEnvironmentChanges.applyPutEnvArray(aEnvironmentChanges, &idxError);
480 }
481
482 LogFlowFuncLeaveRC(vrc);
483 if (RT_SUCCESS(vrc))
484 return S_OK;
485 if (vrc == VERR_ENV_INVALID_VAR_NAME)
486 return setError(E_INVALIDARG, tr("Invalid environment variable name '%s', index %zu"),
487 aEnvironmentChanges[idxError].c_str(), idxError);
488 return setErrorBoth(Global::vboxStatusCodeToCOM(vrc), vrc, tr("Failed to apply '%s', index %zu (%Rrc)"),
489 aEnvironmentChanges[idxError].c_str(), idxError, vrc);
490}
491
492HRESULT GuestSession::getEnvironmentBase(std::vector<com::Utf8Str> &aEnvironmentBase)
493{
494 LogFlowThisFuncEnter();
495
496 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
497 HRESULT hrc;
498 if (mData.mpBaseEnvironment)
499 {
500 int vrc = mData.mpBaseEnvironment->queryPutEnvArray(&aEnvironmentBase);
501 hrc = Global::vboxStatusCodeToCOM(vrc);
502 }
503 else if (mData.mProtocolVersion < 99999)
504 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the Guest Additions"));
505 else
506 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
507
508 LogFlowFuncLeave();
509 return hrc;
510}
511
512HRESULT GuestSession::getProcesses(std::vector<ComPtr<IGuestProcess> > &aProcesses)
513{
514 LogFlowThisFuncEnter();
515
516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
517
518 aProcesses.resize(mData.mProcesses.size());
519 size_t i = 0;
520 for (SessionProcesses::iterator it = mData.mProcesses.begin();
521 it != mData.mProcesses.end();
522 ++it, ++i)
523 {
524 it->second.queryInterfaceTo(aProcesses[i].asOutParam());
525 }
526
527 LogFlowFunc(("mProcesses=%zu\n", aProcesses.size()));
528 return S_OK;
529}
530
531HRESULT GuestSession::getPathStyle(PathStyle_T *aPathStyle)
532{
533 *aPathStyle = i_getGuestPathStyle();
534 return S_OK;
535}
536
537HRESULT GuestSession::getCurrentDirectory(com::Utf8Str &aCurrentDirectory)
538{
539 RT_NOREF(aCurrentDirectory);
540 ReturnComNotImplemented();
541}
542
543HRESULT GuestSession::setCurrentDirectory(const com::Utf8Str &aCurrentDirectory)
544{
545 RT_NOREF(aCurrentDirectory);
546 ReturnComNotImplemented();
547}
548
549HRESULT GuestSession::getUserHome(com::Utf8Str &aUserHome)
550{
551 HRESULT hrc = i_isStartedExternal();
552 if (FAILED(hrc))
553 return hrc;
554
555 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
556 int vrc = i_pathUserHome(aUserHome, &vrcGuest);
557 if (RT_FAILURE(vrc))
558 {
559 switch (vrc)
560 {
561 case VERR_GSTCTL_GUEST_ERROR:
562 {
563 switch (vrcGuest)
564 {
565 case VERR_NOT_SUPPORTED:
566 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest,
567 tr("Getting the user's home path is not supported by installed Guest Additions"));
568 break;
569
570 default:
571 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest,
572 tr("Getting the user's home path failed on the guest: %Rrc"), vrcGuest);
573 break;
574 }
575 break;
576 }
577
578 default:
579 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Getting the user's home path failed: %Rrc"), vrc);
580 break;
581 }
582 }
583
584 return hrc;
585}
586
587HRESULT GuestSession::getUserDocuments(com::Utf8Str &aUserDocuments)
588{
589 HRESULT hrc = i_isStartedExternal();
590 if (FAILED(hrc))
591 return hrc;
592
593 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
594 int vrc = i_pathUserDocuments(aUserDocuments, &vrcGuest);
595 if (RT_FAILURE(vrc))
596 {
597 switch (vrc)
598 {
599 case VERR_GSTCTL_GUEST_ERROR:
600 {
601 switch (vrcGuest)
602 {
603 case VERR_NOT_SUPPORTED:
604 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest,
605 tr("Getting the user's documents path is not supported by installed Guest Additions"));
606 break;
607
608 default:
609 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest,
610 tr("Getting the user's documents path failed on the guest: %Rrc"), vrcGuest);
611 break;
612 }
613 break;
614 }
615
616 default:
617 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Getting the user's documents path failed: %Rrc"), vrc);
618 break;
619 }
620 }
621
622 return hrc;
623}
624
625HRESULT GuestSession::getMountPoints(std::vector<com::Utf8Str> &aMountPoints)
626{
627 HRESULT hrc = i_isStartedExternal();
628 if (FAILED(hrc))
629 return hrc;
630
631 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
632 int vrc = i_getMountPoints(aMountPoints, &vrcGuest);
633 if (RT_FAILURE(vrc))
634 {
635 switch (vrc)
636 {
637 case VERR_GSTCTL_GUEST_ERROR:
638 {
639 switch (vrcGuest)
640 {
641 case VERR_NOT_SUPPORTED:
642 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest,
643 tr("Getting the mount points is not supported by installed Guest Additions"));
644 break;
645
646 default:
647 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest,
648 tr("Getting the mount points failed on the guest: %Rrc"), vrcGuest);
649 break;
650 }
651 break;
652 }
653
654 default:
655 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Getting the mount points failed: %Rrc"), vrc);
656 break;
657 }
658 }
659
660 return hrc;
661}
662
663HRESULT GuestSession::getDirectories(std::vector<ComPtr<IGuestDirectory> > &aDirectories)
664{
665 LogFlowThisFuncEnter();
666
667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
668
669 aDirectories.resize(mData.mDirectories.size());
670 size_t i = 0;
671 for (SessionDirectories::iterator it = mData.mDirectories.begin(); it != mData.mDirectories.end(); ++it, ++i)
672 {
673 it->second.queryInterfaceTo(aDirectories[i].asOutParam());
674 }
675
676 LogFlowFunc(("mDirectories=%zu\n", aDirectories.size()));
677 return S_OK;
678}
679
680HRESULT GuestSession::getFiles(std::vector<ComPtr<IGuestFile> > &aFiles)
681{
682 LogFlowThisFuncEnter();
683
684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
685
686 aFiles.resize(mData.mFiles.size());
687 size_t i = 0;
688 for(SessionFiles::iterator it = mData.mFiles.begin(); it != mData.mFiles.end(); ++it, ++i)
689 it->second.queryInterfaceTo(aFiles[i].asOutParam());
690
691 LogFlowFunc(("mDirectories=%zu\n", aFiles.size()));
692
693 return S_OK;
694}
695
696HRESULT GuestSession::getEventSource(ComPtr<IEventSource> &aEventSource)
697{
698 LogFlowThisFuncEnter();
699
700 // no need to lock - lifetime constant
701 mEventSource.queryInterfaceTo(aEventSource.asOutParam());
702
703 LogFlowThisFuncLeave();
704 return S_OK;
705}
706
707// private methods
708///////////////////////////////////////////////////////////////////////////////
709
710/**
711 * Closes a guest session on the guest.
712 *
713 * @returns VBox status code.
714 * @param uFlags Guest session close flags.
715 * @param uTimeoutMS Timeout (in ms) to wait.
716 * @param pvrcGuest Where to return the guest error when
717 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
718 *
719 * @note Takes the read lock.
720 */
721int GuestSession::i_closeSession(uint32_t uFlags, uint32_t uTimeoutMS, int *pvrcGuest)
722{
723 AssertPtrReturn(pvrcGuest, VERR_INVALID_POINTER);
724
725 LogFlowThisFunc(("uFlags=%x, uTimeoutMS=%RU32\n", uFlags, uTimeoutMS));
726
727 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
728
729 /* Guest Additions < 4.3 don't support closing dedicated
730 guest sessions, skip. */
731 if (mData.mProtocolVersion < 2)
732 {
733 LogFlowThisFunc(("Installed Guest Additions don't support closing dedicated sessions, skipping\n"));
734 return VINF_SUCCESS;
735 }
736
737 /** @todo uFlags validation. */
738
739 if (mData.mStatus != GuestSessionStatus_Started)
740 {
741 LogFlowThisFunc(("Session ID=%RU32 not started (anymore), status now is: %RU32\n",
742 mData.mSession.mID, mData.mStatus));
743 return VINF_SUCCESS;
744 }
745
746 int vrc;
747
748 GuestWaitEvent *pEvent = NULL;
749 GuestEventTypes eventTypes;
750 try
751 {
752 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
753
754 vrc = registerWaitEventEx(mData.mSession.mID, mData.mObjectID, eventTypes, &pEvent);
755 }
756 catch (std::bad_alloc &)
757 {
758 vrc = VERR_NO_MEMORY;
759 }
760
761 if (RT_FAILURE(vrc))
762 return vrc;
763
764 LogFlowThisFunc(("Sending closing request to guest session ID=%RU32, uFlags=%x\n",
765 mData.mSession.mID, uFlags));
766
767 alock.release();
768
769 VBOXHGCMSVCPARM paParms[4];
770 int i = 0;
771 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
772 HGCMSvcSetU32(&paParms[i++], uFlags);
773
774 vrc = i_sendMessage(HOST_MSG_SESSION_CLOSE, i, paParms, VBOX_GUESTCTRL_DST_BOTH);
775 if (RT_SUCCESS(vrc))
776 vrc = i_waitForStatusChange(pEvent, GuestSessionWaitForFlag_Terminate, uTimeoutMS,
777 NULL /* Session status */, pvrcGuest);
778
779 unregisterWaitEvent(pEvent);
780
781 LogFlowFuncLeaveRC(vrc);
782 return vrc;
783}
784
785/**
786 * Internal worker function for public APIs that handle copying elements from
787 * guest to the host.
788 *
789 * @return HRESULT
790 * @param SourceSet Source set specifying what to copy.
791 * @param strDestination Destination path on the host. Host path style.
792 * @param pProgress Progress object returned to the caller.
793 */
794HRESULT GuestSession::i_copyFromGuest(const GuestSessionFsSourceSet &SourceSet,
795 const com::Utf8Str &strDestination, ComPtr<IProgress> &pProgress)
796{
797 HRESULT hrc = i_isStartedExternal();
798 if (FAILED(hrc))
799 return hrc;
800
801 LogFlowThisFuncEnter();
802
803 /* Validate stuff. */
804 if (RT_UNLIKELY(SourceSet.size() == 0 || *(SourceSet[0].strSource.c_str()) == '\0')) /* At least one source must be present. */
805 return setError(E_INVALIDARG, tr("No source(s) specified"));
806 if (RT_UNLIKELY((strDestination.c_str()) == NULL || *(strDestination.c_str()) == '\0'))
807 return setError(E_INVALIDARG, tr("No destination specified"));
808
809 GuestSessionFsSourceSet::const_iterator itSrc = SourceSet.begin();
810 while (itSrc != SourceSet.end())
811 {
812 LogRel2(("Guest Control: Copying '%s' from guest to '%s' on the host (type: %s, filter: %s)\n",
813 itSrc->strSource.c_str(), strDestination.c_str(), GuestBase::fsObjTypeToStr(itSrc->enmType), itSrc->strFilter.c_str()));
814 ++itSrc;
815 }
816
817 /* Create a task and return the progress obejct for it. */
818 GuestSessionTaskCopyFrom *pTask = NULL;
819 try
820 {
821 pTask = new GuestSessionTaskCopyFrom(this /* GuestSession */, SourceSet, strDestination);
822 }
823 catch (std::bad_alloc &)
824 {
825 return setError(E_OUTOFMEMORY, tr("Failed to create GuestSessionTaskCopyFrom object"));
826 }
827
828 try
829 {
830 hrc = pTask->Init(Utf8StrFmt(tr("Copying to \"%s\" on the host"), strDestination.c_str()));
831 }
832 catch (std::bad_alloc &)
833 {
834 hrc = E_OUTOFMEMORY;
835 }
836 if (SUCCEEDED(hrc))
837 {
838 ComObjPtr<Progress> ptrProgressObj = pTask->GetProgressObject();
839
840 /* Kick off the worker thread. Note! Consumes pTask. */
841 hrc = pTask->createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
842 pTask = NULL;
843 if (SUCCEEDED(hrc))
844 hrc = ptrProgressObj.queryInterfaceTo(pProgress.asOutParam());
845 else
846 hrc = setError(hrc, tr("Starting thread for copying from guest to the host failed"));
847 }
848 else
849 {
850 hrc = setError(hrc, tr("Initializing GuestSessionTaskCopyFrom object failed"));
851 delete pTask;
852 }
853
854 LogFlowFunc(("Returning %Rhrc\n", hrc));
855 return hrc;
856}
857
858/**
859 * Internal worker function for public APIs that handle copying elements from
860 * host to the guest.
861 *
862 * @return HRESULT
863 * @param SourceSet Source set specifying what to copy.
864 * @param strDestination Destination path on the guest. Guest path style.
865 * @param pProgress Progress object returned to the caller.
866 */
867HRESULT GuestSession::i_copyToGuest(const GuestSessionFsSourceSet &SourceSet,
868 const com::Utf8Str &strDestination, ComPtr<IProgress> &pProgress)
869{
870 HRESULT hrc = i_isStartedExternal();
871 if (FAILED(hrc))
872 return hrc;
873
874 LogFlowThisFuncEnter();
875
876 GuestSessionFsSourceSet::const_iterator itSrc = SourceSet.begin();
877 while (itSrc != SourceSet.end())
878 {
879 LogRel2(("Guest Control: Copying '%s' from host to '%s' on the guest (type: %s, filter: %s)\n",
880 itSrc->strSource.c_str(), strDestination.c_str(), GuestBase::fsObjTypeToStr(itSrc->enmType), itSrc->strFilter.c_str()));
881 ++itSrc;
882 }
883
884 /* Create a task and return the progress object for it. */
885 GuestSessionTaskCopyTo *pTask = NULL;
886 try
887 {
888 pTask = new GuestSessionTaskCopyTo(this /* GuestSession */, SourceSet, strDestination);
889 }
890 catch (std::bad_alloc &)
891 {
892 return setError(E_OUTOFMEMORY, tr("Failed to create GuestSessionTaskCopyTo object"));
893 }
894
895 try
896 {
897 hrc = pTask->Init(Utf8StrFmt(tr("Copying to \"%s\" on the guest"), strDestination.c_str()));
898 }
899 catch (std::bad_alloc &)
900 {
901 hrc = E_OUTOFMEMORY;
902 }
903 if (SUCCEEDED(hrc))
904 {
905 ComObjPtr<Progress> ptrProgressObj = pTask->GetProgressObject();
906
907 /* Kick off the worker thread. Note! Consumes pTask. */
908 hrc = pTask->createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
909 pTask = NULL;
910 if (SUCCEEDED(hrc))
911 hrc = ptrProgressObj.queryInterfaceTo(pProgress.asOutParam());
912 else
913 hrc = setError(hrc, tr("Starting thread for copying from host to the guest failed"));
914 }
915 else
916 {
917 hrc = setError(hrc, tr("Initializing GuestSessionTaskCopyTo object failed"));
918 delete pTask;
919 }
920
921 LogFlowFunc(("Returning %Rhrc\n", hrc));
922 return hrc;
923}
924
925/**
926 * Validates and extracts directory copy flags from a comma-separated string.
927 *
928 * @return COM status, error set on failure
929 * @param strFlags String to extract flags from.
930 * @param fStrict Whether to set an error when an unknown / invalid flag is detected.
931 * @param pfFlags Where to store the extracted (and validated) flags.
932 */
933HRESULT GuestSession::i_directoryCopyFlagFromStr(const com::Utf8Str &strFlags, bool fStrict, DirectoryCopyFlag_T *pfFlags)
934{
935 unsigned fFlags = DirectoryCopyFlag_None;
936
937 /* Validate and set flags. */
938 if (strFlags.isNotEmpty())
939 {
940 const char *pszNext = strFlags.c_str();
941 for (;;)
942 {
943 /* Find the next keyword, ignoring all whitespace. */
944 pszNext = RTStrStripL(pszNext);
945
946 const char * const pszComma = strchr(pszNext, ',');
947 size_t cchKeyword = pszComma ? pszComma - pszNext : strlen(pszNext);
948 while (cchKeyword > 0 && RT_C_IS_SPACE(pszNext[cchKeyword - 1]))
949 cchKeyword--;
950
951 if (cchKeyword > 0)
952 {
953 /* Convert keyword to flag. */
954#define MATCH_KEYWORD(a_szKeyword) ( cchKeyword == sizeof(a_szKeyword) - 1U \
955 && memcmp(pszNext, a_szKeyword, sizeof(a_szKeyword) - 1U) == 0)
956 if (MATCH_KEYWORD("CopyIntoExisting"))
957 fFlags |= (unsigned)DirectoryCopyFlag_CopyIntoExisting;
958 else if (MATCH_KEYWORD("Recursive"))
959 fFlags |= (unsigned)DirectoryCopyFlag_Recursive;
960 else if (MATCH_KEYWORD("FollowLinks"))
961 fFlags |= (unsigned)DirectoryCopyFlag_FollowLinks;
962 else if (fStrict)
963 return setError(E_INVALIDARG, tr("Invalid directory copy flag: %.*s"), (int)cchKeyword, pszNext);
964#undef MATCH_KEYWORD
965 }
966 if (!pszComma)
967 break;
968 pszNext = pszComma + 1;
969 }
970 }
971
972 if (pfFlags)
973 *pfFlags = (DirectoryCopyFlag_T)fFlags;
974 return S_OK;
975}
976
977#ifdef VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT
978/**
979 * Creates a directory on the guest.
980 *
981 * @returns VBox status code.
982 * @param strPath Path on guest to directory to create.
983 * @param uMode Creation mode to use (octal, 0777 max).
984 * @param uFlags Directory creation flags to use.
985 * @param pvrcGuest Where to return the guest error when
986 * VERR_GSTCTL_GUEST_ERROR was returned.
987 */
988int GuestSession::i_directoryCreateViaToolbox(const Utf8Str &strPath, uint32_t uMode, uint32_t uFlags, int *pvrcGuest)
989{
990 int vrc = VINF_SUCCESS;
991
992 GuestProcessStartupInfo procInfo;
993 procInfo.mFlags = ProcessCreateFlag_Hidden;
994 procInfo.mExecutable = VBOXSERVICE_TOOL_MKDIR;
995
996 try
997 {
998 procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */
999
1000 /* Construct arguments. */
1001 if (uFlags)
1002 {
1003 if (uFlags & DirectoryCreateFlag_Parents)
1004 procInfo.mArguments.push_back(Utf8Str("--parents")); /* We also want to create the parent directories. */
1005 else
1006 vrc = VERR_INVALID_PARAMETER;
1007 }
1008
1009 if ( RT_SUCCESS(vrc)
1010 && uMode)
1011 {
1012 procInfo.mArguments.push_back(Utf8Str("--mode")); /* Set the creation mode. */
1013
1014 char szMode[16];
1015 if (RTStrPrintf(szMode, sizeof(szMode), "%o", uMode))
1016 procInfo.mArguments.push_back(Utf8Str(szMode));
1017 else
1018 vrc = VERR_BUFFER_OVERFLOW;
1019 }
1020
1021 procInfo.mArguments.push_back("--"); /* '--version' is a valid directory name. */
1022 procInfo.mArguments.push_back(strPath); /* The directory we want to create. */
1023 }
1024 catch (std::bad_alloc &)
1025 {
1026 vrc = VERR_NO_MEMORY;
1027 }
1028
1029 if (RT_SUCCESS(vrc))
1030 vrc = GuestProcessToolbox::runTool(this, procInfo, pvrcGuest);
1031
1032 return vrc;
1033}
1034#endif /* VBOX_WITH_GSTCTL_TOOLBOX_SUPPORT */
1035
1036/**
1037 * Creates a directory on the guest.
1038 *
1039 * @returns VBox status code.
1040 * @param strPath Path on guest to directory to create.
1041 * @param uMode Creation mode to use (octal, 0777 max).
1042 * @param uFlags Directory creation flags to use.
1043 * @param pvrcGuest Where to return the guest error when
1044 * VERR_GSTCTL_GUEST_ERROR was returned.
1045 */
1046int GuestSession::i_directoryCreate(const Utf8Str &strPath, uint32_t uMode, uint32_t uFlags, int *pvrcGuest)
1047{
1048 AssertPtrReturn(pvrcGuest, VERR_INVALID_POINTER);
1049 LogFlowThisFunc(("strPath=%s, uMode=%x, uFlags=%x\n", strPath.c_str(), uMode, uFlags));
1050 *pvrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1051
1052 int vrc = VINF_SUCCESS;
1053
1054#ifdef VBOX_WITH_GSTCTL_TOOLBOX_AS_CMDS
1055 if (mParent->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_TOOLBOX_AS_CMDS)
1056 {
1057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1058
1059 GuestWaitEvent *pEvent = NULL;
1060 vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
1061 if (RT_FAILURE(vrc))
1062 return vrc;
1063
1064 uint32_t fFlags = GSTCTL_CREATEDIRECTORY_F_NONE;
1065 if (uFlags & DirectoryCreateFlag_Parents)
1066 fFlags |= GSTCTL_CREATEDIRECTORY_F_PARENTS;
1067 Assert(!(fFlags & ~GSTCTL_CREATEDIRECTORY_F_VALID_MASK));
1068
1069 /* Prepare HGCM call. */
1070 VBOXHGCMSVCPARM paParms[4];
1071 int i = 0;
1072 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
1073 HGCMSvcSetPv (&paParms[i++], (void*)strPath.c_str(), (ULONG)strPath.length() + 1);
1074 HGCMSvcSetU32(&paParms[i++], uMode);
1075 HGCMSvcSetU32(&paParms[i++], fFlags);
1076 alock.release(); /* Drop lock before sending. */
1077
1078 vrc = i_sendMessage(HOST_MSG_DIR_CREATE, i, paParms);
1079 if (RT_SUCCESS(vrc))
1080 {
1081 vrc = pEvent->Wait(GSTCTL_DEFAULT_TIMEOUT_MS);
1082 if (RT_SUCCESS(vrc))
1083 {
1084 // Nothing to do here.
1085 }
1086 else if (pEvent->HasGuestError() && 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(GSTCTL_DEFAULT_TIMEOUT_MS);
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(GSTCTL_DEFAULT_TIMEOUT_MS);
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(GSTCTL_DEFAULT_TIMEOUT_MS);
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_open(GSTCTL_DEFAULT_TIMEOUT_MS, &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(GSTCTL_DEFAULT_TIMEOUT_MS);
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(GSTCTL_DEFAULT_TIMEOUT_MS);
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 GSTCTL_DEFAULT_TIMEOUT_MS, NULL /* Session status */, pvrcGuest);
2799 }
2800 else
2801 {
2802 /*
2803 * Unable to start guest session - update its current state.
2804 * Since there is no (official API) way to recover a failed guest session
2805 * this also marks the end state. Internally just calling this
2806 * same function again will work though.
2807 */
2808 i_setSessionStatus(GuestSessionStatus_Error, vrc); /* ignore return code */
2809 }
2810
2811 unregisterWaitEvent(pEvent);
2812
2813 LogFlowFuncLeaveRC(vrc);
2814 return vrc;
2815}
2816
2817/**
2818 * Starts the guest session asynchronously in a separate worker thread.
2819 *
2820 * @returns IPRT status code.
2821 */
2822int GuestSession::i_startSessionAsync(void)
2823{
2824 LogFlowThisFuncEnter();
2825
2826 /* Create task: */
2827 GuestSessionTaskInternalStart *pTask = NULL;
2828 try
2829 {
2830 pTask = new GuestSessionTaskInternalStart(this);
2831 }
2832 catch (std::bad_alloc &)
2833 {
2834 return VERR_NO_MEMORY;
2835 }
2836 if (pTask->isOk())
2837 {
2838 /* Kick off the thread: */
2839 HRESULT hrc = pTask->createThread();
2840 pTask = NULL; /* Not valid anymore, not even on failure! */
2841 if (SUCCEEDED(hrc))
2842 {
2843 LogFlowFuncLeaveRC(VINF_SUCCESS);
2844 return VINF_SUCCESS;
2845 }
2846 LogFlow(("GuestSession: Failed to create thread for GuestSessionTaskInternalOpen task.\n"));
2847 }
2848 else
2849 LogFlow(("GuestSession: GuestSessionTaskInternalStart creation failed: %Rhrc.\n", pTask->vrc()));
2850 LogFlowFuncLeaveRC(VERR_GENERAL_FAILURE);
2851 return VERR_GENERAL_FAILURE;
2852}
2853
2854/**
2855 * Static function to start a guest session asynchronously.
2856 *
2857 * @returns IPRT status code.
2858 * @param pTask Task object to use for starting the guest session.
2859 */
2860/* static */
2861int GuestSession::i_startSessionThreadTask(GuestSessionTaskInternalStart *pTask)
2862{
2863 LogFlowFunc(("pTask=%p\n", pTask));
2864 AssertPtr(pTask);
2865
2866 const ComObjPtr<GuestSession> pSession(pTask->Session());
2867 Assert(!pSession.isNull());
2868
2869 AutoCaller autoCaller(pSession);
2870 if (FAILED(autoCaller.hrc()))
2871 return VERR_COM_INVALID_OBJECT_STATE;
2872
2873 int vrc = pSession->i_startSession(NULL /*pvrcGuest*/);
2874 /* Nothing to do here anymore. */
2875
2876 LogFlowFuncLeaveRC(vrc);
2877 return vrc;
2878}
2879
2880/**
2881 * Registers an object with the session, i.e. allocates an object ID.
2882 *
2883 * @return VBox status code.
2884 * @retval VERR_GSTCTL_MAX_OBJECTS_REACHED if the maximum of concurrent objects
2885 * is reached.
2886 * @param pObject Guest object to register (weak pointer). Optional.
2887 * @param enmType Session object type to register.
2888 * @param pidObject Where to return the object ID on success. Optional.
2889 */
2890int GuestSession::i_objectRegister(GuestObject *pObject, SESSIONOBJECTTYPE enmType, uint32_t *pidObject)
2891{
2892 /* pObject can be NULL. */
2893 /* pidObject is optional. */
2894
2895 /*
2896 * Pick a random bit as starting point. If it's in use, search forward
2897 * for a free one, wrapping around. We've reserved both the zero'th and
2898 * max-1 IDs (see Data constructor).
2899 */
2900 uint32_t idObject = RTRandU32Ex(1, VBOX_GUESTCTRL_MAX_OBJECTS - 2);
2901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2902 if (!ASMBitTestAndSet(&mData.bmObjectIds[0], idObject))
2903 { /* likely */ }
2904 else if (mData.mObjects.size() < VBOX_GUESTCTRL_MAX_OBJECTS - 2 /* First and last are not used */)
2905 {
2906 /* Forward search. */
2907 int iHit = ASMBitNextClear(&mData.bmObjectIds[0], VBOX_GUESTCTRL_MAX_OBJECTS, idObject);
2908 if (iHit < 0)
2909 iHit = ASMBitFirstClear(&mData.bmObjectIds[0], VBOX_GUESTCTRL_MAX_OBJECTS);
2910 AssertLogRelMsgReturn(iHit >= 0, ("object count: %#zu\n", mData.mObjects.size()), VERR_GSTCTL_MAX_CID_OBJECTS_REACHED);
2911 idObject = iHit;
2912 AssertLogRelMsgReturn(!ASMBitTestAndSet(&mData.bmObjectIds[0], idObject), ("idObject=%#x\n", idObject), VERR_INTERNAL_ERROR_2);
2913 }
2914 else
2915 {
2916 LogFunc(("Maximum number of objects reached (enmType=%RU32, %zu objects)\n", enmType, mData.mObjects.size()));
2917 return VERR_GSTCTL_MAX_CID_OBJECTS_REACHED;
2918 }
2919
2920 Log2Func(("enmType=%RU32 -> idObject=%RU32 (%zu objects)\n", enmType, idObject, mData.mObjects.size()));
2921
2922 try
2923 {
2924 mData.mObjects[idObject].pObject = pObject; /* Can be NULL. */
2925 mData.mObjects[idObject].enmType = enmType;
2926 mData.mObjects[idObject].msBirth = RTTimeMilliTS();
2927 }
2928 catch (std::bad_alloc &)
2929 {
2930 ASMBitClear(&mData.bmObjectIds[0], idObject);
2931 return VERR_NO_MEMORY;
2932 }
2933
2934 if (pidObject)
2935 *pidObject = idObject;
2936
2937 return VINF_SUCCESS;
2938}
2939
2940/**
2941 * Unregisters an object from the session objects list.
2942 *
2943 * @retval VINF_SUCCESS on success.
2944 * @retval VERR_NOT_FOUND if the object ID was not found.
2945 * @param idObject Object ID to unregister.
2946 *
2947 * @note Takes the write lock.
2948 */
2949int GuestSession::i_objectUnregister(uint32_t idObject)
2950{
2951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2952
2953 int vrc = VINF_SUCCESS;
2954 AssertMsgStmt(ASMBitTestAndClear(&mData.bmObjectIds, idObject), ("idObject=%#x\n", idObject), vrc = VERR_NOT_FOUND);
2955
2956 SessionObjects::iterator ItObj = mData.mObjects.find(idObject);
2957 AssertMsgReturn(ItObj != mData.mObjects.end(), ("idObject=%#x\n", idObject), VERR_NOT_FOUND);
2958 mData.mObjects.erase(ItObj);
2959
2960 return vrc;
2961}
2962
2963/**
2964 * Unregisters all objects from the session list.
2965 *
2966 * @returns VBox status code.
2967 *
2968 * @note Takes the write lock.
2969 */
2970int GuestSession::i_objectsUnregister(void)
2971{
2972 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2973
2974 LogFlowThisFunc(("Unregistering directories (%zu total)\n", mData.mDirectories.size()));
2975
2976 SessionDirectories::iterator itDirs;
2977 while ((itDirs = mData.mDirectories.begin()) != mData.mDirectories.end())
2978 {
2979 alock.release();
2980 i_directoryUnregister(itDirs->second);
2981 alock.acquire();
2982 }
2983
2984 Assert(mData.mDirectories.size() == 0);
2985 mData.mDirectories.clear();
2986
2987 LogFlowThisFunc(("Unregistering files (%zu total)\n", mData.mFiles.size()));
2988
2989 SessionFiles::iterator itFiles;
2990 while ((itFiles = mData.mFiles.begin()) != mData.mFiles.end())
2991 {
2992 alock.release();
2993 i_fileUnregister(itFiles->second);
2994 alock.acquire();
2995 }
2996
2997 Assert(mData.mFiles.size() == 0);
2998 mData.mFiles.clear();
2999
3000 LogFlowThisFunc(("Unregistering processes (%zu total)\n", mData.mProcesses.size()));
3001
3002 SessionProcesses::iterator itProcs;
3003 while ((itProcs = mData.mProcesses.begin()) != mData.mProcesses.end())
3004 {
3005 alock.release();
3006 i_processUnregister(itProcs->second);
3007 alock.acquire();
3008 }
3009
3010 Assert(mData.mProcesses.size() == 0);
3011 mData.mProcesses.clear();
3012
3013 return VINF_SUCCESS;
3014}
3015
3016/**
3017 * Notifies all registered objects about a guest session status change.
3018 *
3019 * @returns VBox status code.
3020 * @param enmSessionStatus Session status to notify objects about.
3021 */
3022int GuestSession::i_objectsNotifyAboutStatusChange(GuestSessionStatus_T enmSessionStatus)
3023{
3024 LogFlowThisFunc(("enmSessionStatus=%RU32\n", enmSessionStatus));
3025
3026 int vrc = VINF_SUCCESS;
3027
3028 SessionObjects::iterator itObjs = mData.mObjects.begin();
3029 while (itObjs != mData.mObjects.end())
3030 {
3031 GuestObject *pObj = itObjs->second.pObject;
3032 if (pObj) /* pObject can be NULL (weak pointer). */
3033 {
3034 int vrc2 = pObj->i_onSessionStatusChange(enmSessionStatus);
3035 if (RT_SUCCESS(vrc))
3036 vrc = vrc2;
3037
3038 /* If the session got terminated, make sure to cancel all wait events for
3039 * the current object. */
3040 if (i_isTerminated())
3041 pObj->cancelWaitEvents();
3042 }
3043
3044 ++itObjs;
3045 }
3046
3047 LogFlowFuncLeaveRC(vrc);
3048 return vrc;
3049}
3050
3051/**
3052 * Renames a path on the guest.
3053 *
3054 * @returns VBox status code.
3055 * @returns VERR_GSTCTL_GUEST_ERROR on received guest error.
3056 * @param strSource Source path on guest to rename.
3057 * @param strDest Destination path on guest to rename \a strSource to.
3058 * @param uFlags Renaming flags.
3059 * @param pvrcGuest Where to return the guest error when
3060 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
3061 * @note Takes the read lock.
3062 */
3063int GuestSession::i_pathRename(const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags, int *pvrcGuest)
3064{
3065 AssertReturn(!(uFlags & ~PATHRENAME_FLAG_VALID_MASK), VERR_INVALID_PARAMETER);
3066
3067 LogFlowThisFunc(("strSource=%s, strDest=%s, uFlags=0x%x\n",
3068 strSource.c_str(), strDest.c_str(), uFlags));
3069
3070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3071
3072 GuestWaitEvent *pEvent = NULL;
3073 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
3074 if (RT_FAILURE(vrc))
3075 return vrc;
3076
3077 /* Prepare HGCM call. */
3078 VBOXHGCMSVCPARM paParms[8];
3079 int i = 0;
3080 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
3081 HGCMSvcSetPv(&paParms[i++], (void*)strSource.c_str(),
3082 (ULONG)strSource.length() + 1);
3083 HGCMSvcSetPv(&paParms[i++], (void*)strDest.c_str(),
3084 (ULONG)strDest.length() + 1);
3085 HGCMSvcSetU32(&paParms[i++], uFlags);
3086
3087 alock.release(); /* Drop lock before sending. */
3088
3089 vrc = i_sendMessage(HOST_MSG_PATH_RENAME, i, paParms);
3090 if (RT_SUCCESS(vrc))
3091 {
3092 vrc = pEvent->Wait(GSTCTL_DEFAULT_TIMEOUT_MS);
3093 if (pEvent->HasGuestError() && pvrcGuest)
3094 *pvrcGuest = pEvent->GuestResult();
3095 }
3096
3097 unregisterWaitEvent(pEvent);
3098
3099 LogFlowFuncLeaveRC(vrc);
3100 return vrc;
3101}
3102
3103/**
3104 * Returns the user's absolute documents path, if any.
3105 *
3106 * @returns VBox status code.
3107 * @param strPath Where to store the user's document path.
3108 * @param pvrcGuest Guest VBox status code, when returning
3109 * VERR_GSTCTL_GUEST_ERROR. Any other return code indicates
3110 * some host side error.
3111 *
3112 * @note Takes the read lock.
3113 */
3114int GuestSession::i_pathUserDocuments(Utf8Str &strPath, int *pvrcGuest)
3115{
3116 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3117
3118 /** @todo Cache the user's document path? */
3119
3120 GuestWaitEvent *pEvent = NULL;
3121 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
3122 if (RT_FAILURE(vrc))
3123 return vrc;
3124
3125 /* Prepare HGCM call. */
3126 VBOXHGCMSVCPARM paParms[2];
3127 int i = 0;
3128 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
3129
3130 alock.release(); /* Drop lock before sending. */
3131
3132 vrc = i_sendMessage(HOST_MSG_PATH_USER_DOCUMENTS, i, paParms);
3133 if (RT_SUCCESS(vrc))
3134 {
3135 vrc = pEvent->Wait(GSTCTL_DEFAULT_TIMEOUT_MS);
3136 if (RT_SUCCESS(vrc))
3137 {
3138 strPath = pEvent->Payload().ToString();
3139 }
3140 else
3141 {
3142 if (pEvent->HasGuestError() && pvrcGuest)
3143 *pvrcGuest = pEvent->GuestResult();
3144 }
3145 }
3146
3147 unregisterWaitEvent(pEvent);
3148
3149 LogFlowFuncLeaveRC(vrc);
3150 return vrc;
3151}
3152
3153/**
3154 * Returns the currently accessible mount points of the guest.
3155 *
3156 * @returns VBox status code.
3157 * @retval VERR_NOT_SUPPORTED if the installed Guest Additions do not support this feature.
3158 * @param vecMountPoints Where to return the mount points (guest-style paths).
3159 * @param pvrcGuest Guest VBox status code, when returning
3160 * VERR_GSTCTL_GUEST_ERROR. Any other return code indicates
3161 * some host side error.
3162 *
3163 * @note Takes the read lock.
3164 */
3165int GuestSession::i_getMountPoints(std::vector<com::Utf8Str> &vecMountPoints, int *pvrcGuest)
3166{
3167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3168
3169 if (!(mParent->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_MOUNT_POINTS_ENUM))
3170 return VERR_NOT_SUPPORTED;
3171
3172 GuestWaitEvent *pEvent = NULL;
3173 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
3174 if (RT_FAILURE(vrc))
3175 return vrc;
3176
3177 /* Prepare HGCM call. */
3178 VBOXHGCMSVCPARM paParms[4];
3179 int i = 0;
3180 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
3181 HGCMSvcSetU32(&paParms[i++], 0 /* fFlags, unused */);
3182
3183 alock.release(); /* Drop lock before sending. */
3184
3185 vrc = i_sendMessage(HOST_MSG_MOUNT_POINTS, i, paParms);
3186 if (RT_SUCCESS(vrc))
3187 {
3188 vrc = pEvent->Wait(GSTCTL_DEFAULT_TIMEOUT_MS);
3189 if (RT_SUCCESS(vrc))
3190 {
3191 vrc = pEvent->Payload().ToStringVector(vecMountPoints);
3192 }
3193 else
3194 {
3195 if (pEvent->HasGuestError() && pvrcGuest)
3196 *pvrcGuest = pEvent->GuestResult();
3197 }
3198 }
3199
3200 unregisterWaitEvent(pEvent);
3201
3202 LogFlowFuncLeaveRC(vrc);
3203 return vrc;
3204}
3205
3206/**
3207 * Returns the user's absolute home path, if any.
3208 *
3209 * @returns VBox status code.
3210 * @param strPath Where to store the user's home path.
3211 * @param pvrcGuest Guest VBox status code, when returning
3212 * VERR_GSTCTL_GUEST_ERROR. Any other return code indicates
3213 * some host side error.
3214 *
3215 * @note Takes the read lock.
3216 */
3217int GuestSession::i_pathUserHome(Utf8Str &strPath, int *pvrcGuest)
3218{
3219 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3220
3221 /** @todo Cache the user's home path? */
3222
3223 GuestWaitEvent *pEvent = NULL;
3224 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
3225 if (RT_FAILURE(vrc))
3226 return vrc;
3227
3228 /* Prepare HGCM call. */
3229 VBOXHGCMSVCPARM paParms[2];
3230 int i = 0;
3231 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
3232
3233 alock.release(); /* Drop lock before sending. */
3234
3235 vrc = i_sendMessage(HOST_MSG_PATH_USER_HOME, i, paParms);
3236 if (RT_SUCCESS(vrc))
3237 {
3238 vrc = pEvent->Wait(GSTCTL_DEFAULT_TIMEOUT_MS);
3239 if (RT_SUCCESS(vrc))
3240 {
3241 strPath = pEvent->Payload().ToString();
3242 }
3243 else
3244 {
3245 if (pEvent->HasGuestError() && pvrcGuest)
3246 *pvrcGuest = pEvent->GuestResult();
3247 }
3248 }
3249
3250 unregisterWaitEvent(pEvent);
3251
3252 LogFlowFuncLeaveRC(vrc);
3253 return vrc;
3254}
3255
3256/**
3257 * Unregisters a process object from a guest session.
3258 *
3259 * @returns VBox status code. VERR_NOT_FOUND if the process is not registered (anymore).
3260 * @param pProcess Process object to unregister from session.
3261 *
3262 * @note Takes the write lock.
3263 */
3264int GuestSession::i_processUnregister(GuestProcess *pProcess)
3265{
3266 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
3267
3268 LogFlowThisFunc(("pProcess=%p\n", pProcess));
3269
3270 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3271
3272 const uint32_t idObject = pProcess->getObjectID();
3273
3274 LogFlowFunc(("Removing process (objectID=%RU32) ...\n", idObject));
3275
3276 int vrc = i_objectUnregister(idObject);
3277 if (RT_FAILURE(vrc))
3278 return vrc;
3279
3280 SessionProcesses::iterator itProcs = mData.mProcesses.find(idObject);
3281 AssertReturn(itProcs != mData.mProcesses.end(), VERR_NOT_FOUND);
3282
3283 /* Make sure to consume the pointer before the one of the iterator gets released. */
3284 ComObjPtr<GuestProcess> pProc = pProcess;
3285
3286 ULONG uPID;
3287 HRESULT hrc = pProc->COMGETTER(PID)(&uPID);
3288 ComAssertComRC(hrc);
3289
3290 LogFlowFunc(("Removing process ID=%RU32 (session %RU32, guest PID %RU32, now total %zu processes)\n",
3291 idObject, mData.mSession.mID, uPID, mData.mProcesses.size()));
3292
3293 vrc = pProcess->i_onUnregister();
3294 AssertRCReturn(vrc, vrc);
3295
3296 mData.mProcesses.erase(itProcs);
3297
3298 alock.release(); /* Release lock before firing off event. */
3299
3300 ::FireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProc, uPID, false /* Process unregistered */);
3301
3302 pProc.setNull();
3303
3304 LogFlowFuncLeaveRC(vrc);
3305 return vrc;
3306}
3307
3308/**
3309 * Creates but does *not* start the process yet.
3310 *
3311 * See GuestProcess::startProcess() or GuestProcess::startProcessAsync() for
3312 * starting the process.
3313 *
3314 * @returns IPRT status code.
3315 * @param procInfo Process startup info to use for starting the process.
3316 * @param pProcess Where to return the created guest process object on success.
3317 *
3318 * @note Takes the write lock.
3319 */
3320int GuestSession::i_processCreateEx(GuestProcessStartupInfo &procInfo, ComObjPtr<GuestProcess> &pProcess)
3321{
3322 LogFlowFunc(("mExe=%s, mFlags=%x, mTimeoutMS=%RU32\n",
3323 procInfo.mExecutable.c_str(), procInfo.mFlags, procInfo.mTimeoutMS));
3324#ifdef DEBUG
3325 if (procInfo.mArguments.size())
3326 {
3327 LogFlowFunc(("Arguments:"));
3328 ProcessArguments::const_iterator it = procInfo.mArguments.begin();
3329 while (it != procInfo.mArguments.end())
3330 {
3331 LogFlow((" %s", (*it).c_str()));
3332 ++it;
3333 }
3334 LogFlow(("\n"));
3335 }
3336#endif
3337
3338 /* Validate flags. */
3339 if (procInfo.mFlags)
3340 {
3341 if ( !(procInfo.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
3342 && !(procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
3343 && !(procInfo.mFlags & ProcessCreateFlag_Hidden)
3344 && !(procInfo.mFlags & ProcessCreateFlag_Profile)
3345 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
3346 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdErr))
3347 {
3348 return VERR_INVALID_PARAMETER;
3349 }
3350 }
3351
3352 if ( (procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
3353 && ( (procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
3354 || (procInfo.mFlags & ProcessCreateFlag_WaitForStdErr)
3355 )
3356 )
3357 {
3358 return VERR_INVALID_PARAMETER;
3359 }
3360
3361 if (procInfo.mPriority)
3362 {
3363 if (!(procInfo.mPriority & ProcessPriority_Default))
3364 return VERR_INVALID_PARAMETER;
3365 }
3366
3367 /* Adjust timeout.
3368 * If set to 0, we define an infinite timeout (unlimited process run time). */
3369 if (procInfo.mTimeoutMS == 0)
3370 procInfo.mTimeoutMS = UINT32_MAX;
3371
3372 /** @todo Implement process priority + affinity. */
3373
3374 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3375
3376 /* Create the process object. */
3377 HRESULT hrc = pProcess.createObject();
3378 if (FAILED(hrc))
3379 return VERR_COM_UNEXPECTED;
3380
3381 /* Register a new object ID. */
3382 uint32_t idObject;
3383 int vrc = i_objectRegister(pProcess, SESSIONOBJECTTYPE_PROCESS, &idObject);
3384 if (RT_FAILURE(vrc))
3385 {
3386 pProcess.setNull();
3387 return vrc;
3388 }
3389
3390 vrc = pProcess->init(mConsole, this /* Session */, idObject, procInfo, mData.mpBaseEnvironment);
3391 if (RT_FAILURE(vrc))
3392 return vrc;
3393
3394 /* Add the created process to our map. */
3395 try
3396 {
3397 mData.mProcesses[idObject] = pProcess;
3398
3399 LogFlowFunc(("Added new process (Session: %RU32) with process ID=%RU32 (now total %zu processes)\n",
3400 mData.mSession.mID, idObject, mData.mProcesses.size()));
3401
3402 alock.release(); /* Release lock before firing off event. */
3403
3404 ::FireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProcess, 0 /* PID */, true /* Process registered */);
3405 }
3406 catch (std::bad_alloc &)
3407 {
3408 vrc = VERR_NO_MEMORY;
3409 }
3410
3411 return vrc;
3412}
3413
3414/**
3415 * Checks if a process object exists and optionally returns its object.
3416 *
3417 * @returns \c true if process object exists, or \c false if not.
3418 * @param uProcessID ID of process object to check.
3419 * @param pProcess Where to return the found process object on success.
3420 *
3421 * @note No locking done!
3422 */
3423inline bool GuestSession::i_processExists(uint32_t uProcessID, ComObjPtr<GuestProcess> *pProcess)
3424{
3425 SessionProcesses::const_iterator it = mData.mProcesses.find(uProcessID);
3426 if (it != mData.mProcesses.end())
3427 {
3428 if (pProcess)
3429 *pProcess = it->second;
3430 return true;
3431 }
3432 return false;
3433}
3434
3435/**
3436 * Returns the process object from a guest PID.
3437 *
3438 * @returns VBox status code.
3439 * @param uPID Guest PID to get process object for.
3440 * @param pProcess Where to return the process object on success.
3441 *
3442 * @note No locking done!
3443 */
3444inline int GuestSession::i_processGetByPID(ULONG uPID, ComObjPtr<GuestProcess> *pProcess)
3445{
3446 AssertReturn(uPID, false);
3447 /* pProcess is optional. */
3448
3449 SessionProcesses::iterator itProcs = mData.mProcesses.begin();
3450 for (; itProcs != mData.mProcesses.end(); ++itProcs)
3451 {
3452 ComObjPtr<GuestProcess> pCurProc = itProcs->second;
3453 AutoCaller procCaller(pCurProc);
3454 if (!procCaller.isOk())
3455 return VERR_COM_INVALID_OBJECT_STATE;
3456
3457 ULONG uCurPID;
3458 HRESULT hrc = pCurProc->COMGETTER(PID)(&uCurPID);
3459 ComAssertComRC(hrc);
3460
3461 if (uCurPID == uPID)
3462 {
3463 if (pProcess)
3464 *pProcess = pCurProc;
3465 return VINF_SUCCESS;
3466 }
3467 }
3468
3469 return VERR_NOT_FOUND;
3470}
3471
3472/**
3473 * Sends a message to the HGCM host service.
3474 *
3475 * @returns VBox status code.
3476 * @retval VERR_VM_INVALID_VM_STATE if the VM is in a state where can't send message to the guest (anymore).
3477 * @param uMessage Message ID to send.
3478 * @param uParms Number of parameters in \a paParms to send.
3479 * @param paParms Array of HGCM parameters to send.
3480 * @param fDst Host message destination flags of type VBOX_GUESTCTRL_DST_XXX.
3481 */
3482int GuestSession::i_sendMessage(uint32_t uMessage, uint32_t uParms, PVBOXHGCMSVCPARM paParms,
3483 uint64_t fDst /*= VBOX_GUESTCTRL_DST_SESSION*/)
3484{
3485 LogFlowThisFuncEnter();
3486
3487#ifndef VBOX_GUESTCTRL_TEST_CASE
3488 VMMDev *pVMMDev = mConsole->i_getVMMDev();
3489 AssertPtrReturn(pVMMDev, VERR_VM_INVALID_VM_STATE);
3490
3491 /* Forward the information to the VMM device. */
3492 LogFlowThisFunc(("uMessage=%RU32 (%s), uParms=%RU32\n", uMessage, GstCtrlHostMsgtoStr((guestControl::eHostMsg)uMessage), uParms));
3493
3494 /* HACK ALERT! We extend the first parameter to 64-bit and use the
3495 two topmost bits for call destination information. */
3496 Assert(fDst == VBOX_GUESTCTRL_DST_SESSION || fDst == VBOX_GUESTCTRL_DST_ROOT_SVC || fDst == VBOX_GUESTCTRL_DST_BOTH);
3497 Assert(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT);
3498 paParms[0].type = VBOX_HGCM_SVC_PARM_64BIT;
3499 paParms[0].u.uint64 = (uint64_t)paParms[0].u.uint32 | fDst;
3500
3501 /* Make the call. */
3502 int vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uMessage, uParms, paParms);
3503 if (RT_FAILURE(vrc))
3504 {
3505 /** @todo What to do here? */
3506 }
3507#else
3508 /* Not needed within testcases. */
3509 int vrc = VINF_SUCCESS;
3510#endif
3511 LogFlowFuncLeaveRC(vrc);
3512 return vrc;
3513}
3514
3515/**
3516 * Sets the guest session's current status.
3517 *
3518 * @returns VBox status code.
3519 * @param sessionStatus Session status to set.
3520 * @param vrcSession Session result to set (for error handling).
3521 *
3522 * @note Takes the write lock.
3523 */
3524int GuestSession::i_setSessionStatus(GuestSessionStatus_T sessionStatus, int vrcSession)
3525{
3526 LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, vrcSession=%Rrc\n", mData.mStatus, sessionStatus, vrcSession));
3527
3528 if (sessionStatus == GuestSessionStatus_Error)
3529 {
3530 AssertMsg(RT_FAILURE(vrcSession), ("Guest vrcSession must be an error (%Rrc)\n", vrcSession));
3531 /* Do not allow overwriting an already set error. If this happens
3532 * this means we forgot some error checking/locking somewhere. */
3533 AssertMsg(RT_SUCCESS(mData.mVrc), ("Guest mVrc already set (to %Rrc)\n", mData.mVrc));
3534 }
3535 else
3536 AssertMsg(RT_SUCCESS(vrcSession), ("Guest vrcSession must not be an error (%Rrc)\n", vrcSession));
3537
3538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3539
3540 int vrc = VINF_SUCCESS;
3541
3542 if (mData.mStatus != sessionStatus)
3543 {
3544 mData.mStatus = sessionStatus;
3545 mData.mVrc = vrcSession;
3546
3547 /* Make sure to notify all underlying objects first. */
3548 vrc = i_objectsNotifyAboutStatusChange(sessionStatus);
3549
3550 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
3551 HRESULT hrc = errorInfo.createObject();
3552 ComAssertComRC(hrc);
3553 int vrc2 = errorInfo->initEx(VBOX_E_IPRT_ERROR, vrcSession, COM_IIDOF(IGuestSession), getComponentName(),
3554 i_guestErrorToString(vrcSession));
3555 AssertRC(vrc2);
3556
3557 alock.release(); /* Release lock before firing off event. */
3558
3559 ::FireGuestSessionStateChangedEvent(mEventSource, this, mData.mSession.mID, sessionStatus, errorInfo);
3560 }
3561
3562 LogFlowFuncLeaveRC(vrc);
3563 return vrc;
3564}
3565
3566/** @todo Unused --remove? */
3567int GuestSession::i_signalWaiters(GuestSessionWaitResult_T enmWaitResult, int vrc /*= VINF_SUCCESS */)
3568{
3569 RT_NOREF(enmWaitResult, vrc);
3570
3571 /*LogFlowThisFunc(("enmWaitResult=%d, vrc=%Rrc, mWaitCount=%RU32, mWaitEvent=%p\n",
3572 enmWaitResult, vrc, mData.mWaitCount, mData.mWaitEvent));*/
3573
3574 /* Note: No write locking here -- already done in the caller. */
3575
3576 int vrc2 = VINF_SUCCESS;
3577 /*if (mData.mWaitEvent)
3578 vrc2 = mData.mWaitEvent->Signal(enmWaitResult, vrc);*/
3579 LogFlowFuncLeaveRC(vrc2);
3580 return vrc2;
3581}
3582
3583/**
3584 * Shuts down (and optionally powers off / reboots) the guest.
3585 * Needs supported Guest Additions installed.
3586 *
3587 * @returns VBox status code. VERR_NOT_SUPPORTED if not supported by Guest Additions.
3588 * @param fFlags Guest shutdown flags.
3589 * @param pvrcGuest Guest VBox status code, when returning
3590 * VERR_GSTCTL_GUEST_ERROR. Any other return code indicates
3591 * some host side error.
3592 *
3593 * @note Takes the read lock.
3594 */
3595int GuestSession::i_shutdown(uint32_t fFlags, int *pvrcGuest)
3596{
3597 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3598
3599 if (!(mParent->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_SHUTDOWN))
3600 return VERR_NOT_SUPPORTED;
3601
3602 LogRel(("Guest Control: Shutting down guest (flags = %#x) ...\n", fFlags));
3603
3604 GuestWaitEvent *pEvent = NULL;
3605 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
3606 if (RT_FAILURE(vrc))
3607 return vrc;
3608
3609 /* Prepare HGCM call. */
3610 VBOXHGCMSVCPARM paParms[2];
3611 int i = 0;
3612 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
3613 HGCMSvcSetU32(&paParms[i++], fFlags);
3614
3615 alock.release(); /* Drop lock before sending. */
3616
3617 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3618
3619 vrc = i_sendMessage(HOST_MSG_SHUTDOWN, i, paParms);
3620 if (RT_SUCCESS(vrc))
3621 {
3622 vrc = pEvent->Wait(GSTCTL_DEFAULT_TIMEOUT_MS);
3623 if (RT_FAILURE(vrc))
3624 {
3625 if (pEvent->HasGuestError())
3626 vrcGuest = pEvent->GuestResult();
3627 }
3628 }
3629
3630 if (RT_FAILURE(vrc))
3631 {
3632 LogRel(("Guest Control: Shutting down guest failed, vrc=%Rrc\n", vrc == VERR_GSTCTL_GUEST_ERROR ? vrcGuest : vrc));
3633 if (pEvent->HasGuestError() && pvrcGuest)
3634 *pvrcGuest = vrcGuest;
3635 }
3636
3637 unregisterWaitEvent(pEvent);
3638
3639 LogFlowFuncLeaveRC(vrc);
3640 return vrc;
3641}
3642
3643/**
3644 * Determines the protocol version (sets mData.mProtocolVersion).
3645 *
3646 * This is called from the init method prior to to establishing a guest session.
3647 *
3648 * @returns VBox status code.
3649 * @retval VERR_NOT_FOUND if the Guest Additions were not found (or were not reported) yet.
3650 */
3651int GuestSession::i_determineProtocolVersion(void)
3652{
3653 /*
3654 * We currently do this based on the reported Guest Additions version,
3655 * ASSUMING that VBoxService and VBoxDrv are at the same version.
3656 */
3657 ComObjPtr<Guest> pGuest = mParent;
3658 AssertReturn(!pGuest.isNull(), VERR_NOT_SUPPORTED);
3659 uint32_t uGaVersion = pGuest->i_getAdditionsVersion();
3660 if (!uGaVersion) /* If 0, there was no Guest Additions version detected (yet), or the VM is in reset state. */
3661 return VERR_NOT_FOUND;
3662
3663 /* Everyone supports version one, if they support anything at all. */
3664 mData.mProtocolVersion = 1;
3665
3666 /* Guest control 2.0 was introduced with 4.3.0. */
3667 if (uGaVersion >= VBOX_FULL_VERSION_MAKE(4,3,0))
3668 mData.mProtocolVersion = 2; /* Guest control 2.0. */
3669
3670 LogFlowThisFunc(("uGaVersion=%u.%u.%u => mProtocolVersion=%u\n",
3671 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
3672 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
3673
3674 /*
3675 * Inform the user about outdated Guest Additions (VM release log).
3676 */
3677 if (mData.mProtocolVersion < 2)
3678 LogRelMax(3, ("Warning: Guest Additions v%u.%u.%u only supports the older guest control protocol version %u.\n"
3679 " Please upgrade GAs to the current version to get full guest control capabilities.\n",
3680 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
3681 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
3682
3683 return VINF_SUCCESS;
3684}
3685
3686/**
3687 * Waits for guest session events.
3688 *
3689 * @returns VBox status code.
3690 * @retval VERR_GSTCTL_GUEST_ERROR on received guest error.
3691 * @retval VERR_TIMEOUT when a timeout has occurred.
3692 * @param fWaitFlags Wait flags to use.
3693 * @param uTimeoutMS Timeout (in ms) to wait.
3694 * @param waitResult Where to return the wait result on success.
3695 * @param pvrcGuest Where to return the guest error when
3696 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
3697 *
3698 * @note Takes the read lock.
3699 */
3700int GuestSession::i_waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, GuestSessionWaitResult_T &waitResult, int *pvrcGuest)
3701{
3702 LogFlowThisFuncEnter();
3703
3704 AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
3705
3706 /*LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p, pvrcGuest=%p\n",
3707 fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent, pvrcGuest));*/
3708
3709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3710
3711 /* Did some error occur before? Then skip waiting and return. */
3712 if (mData.mStatus == GuestSessionStatus_Error)
3713 {
3714 waitResult = GuestSessionWaitResult_Error;
3715 AssertMsg(RT_FAILURE(mData.mVrc), ("No error mVrc (%Rrc) set when guest session indicated an error\n", mData.mVrc));
3716 if (pvrcGuest)
3717 *pvrcGuest = mData.mVrc; /* Return last set error. */
3718 return VERR_GSTCTL_GUEST_ERROR;
3719 }
3720
3721 /* Guest Additions < 4.3 don't support session handling, skip. */
3722 if (mData.mProtocolVersion < 2)
3723 {
3724 waitResult = GuestSessionWaitResult_WaitFlagNotSupported;
3725
3726 LogFlowThisFunc(("Installed Guest Additions don't support waiting for dedicated sessions, skipping\n"));
3727 return VINF_SUCCESS;
3728 }
3729
3730 waitResult = GuestSessionWaitResult_None;
3731 if (fWaitFlags & GuestSessionWaitForFlag_Terminate)
3732 {
3733 switch (mData.mStatus)
3734 {
3735 case GuestSessionStatus_Terminated:
3736 case GuestSessionStatus_Down:
3737 waitResult = GuestSessionWaitResult_Terminate;
3738 break;
3739
3740 case GuestSessionStatus_TimedOutKilled:
3741 case GuestSessionStatus_TimedOutAbnormally:
3742 waitResult = GuestSessionWaitResult_Timeout;
3743 break;
3744
3745 case GuestSessionStatus_Error:
3746 /* Handled above. */
3747 break;
3748
3749 case GuestSessionStatus_Started:
3750 waitResult = GuestSessionWaitResult_Start;
3751 break;
3752
3753 case GuestSessionStatus_Undefined:
3754 case GuestSessionStatus_Starting:
3755 /* Do the waiting below. */
3756 break;
3757
3758 default:
3759 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
3760 return VERR_NOT_IMPLEMENTED;
3761 }
3762 }
3763 else if (fWaitFlags & GuestSessionWaitForFlag_Start)
3764 {
3765 switch (mData.mStatus)
3766 {
3767 case GuestSessionStatus_Started:
3768 case GuestSessionStatus_Terminating:
3769 case GuestSessionStatus_Terminated:
3770 case GuestSessionStatus_Down:
3771 waitResult = GuestSessionWaitResult_Start;
3772 break;
3773
3774 case GuestSessionStatus_Error:
3775 waitResult = GuestSessionWaitResult_Error;
3776 break;
3777
3778 case GuestSessionStatus_TimedOutKilled:
3779 case GuestSessionStatus_TimedOutAbnormally:
3780 waitResult = GuestSessionWaitResult_Timeout;
3781 break;
3782
3783 case GuestSessionStatus_Undefined:
3784 case GuestSessionStatus_Starting:
3785 /* Do the waiting below. */
3786 break;
3787
3788 default:
3789 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
3790 return VERR_NOT_IMPLEMENTED;
3791 }
3792 }
3793
3794 LogFlowThisFunc(("sessionStatus=%RU32, vrcSession=%Rrc, waitResult=%RU32\n", mData.mStatus, mData.mVrc, waitResult));
3795
3796 /* No waiting needed? Return immediately using the last set error. */
3797 if (waitResult != GuestSessionWaitResult_None)
3798 {
3799 if (pvrcGuest)
3800 *pvrcGuest = mData.mVrc; /* Return last set error (if any). */
3801 return RT_SUCCESS(mData.mVrc) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
3802 }
3803
3804 int vrc = VINF_SUCCESS;
3805
3806 uint64_t const tsStart = RTTimeMilliTS();
3807 uint64_t tsNow = tsStart;
3808
3809 while (tsNow - tsStart < uTimeoutMS)
3810 {
3811 GuestWaitEvent *pEvent = NULL;
3812 GuestEventTypes eventTypes;
3813 try
3814 {
3815 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
3816
3817 vrc = registerWaitEventEx(mData.mSession.mID, mData.mObjectID, eventTypes, &pEvent);
3818 }
3819 catch (std::bad_alloc &)
3820 {
3821 vrc = VERR_NO_MEMORY;
3822 }
3823
3824 if (RT_FAILURE(vrc))
3825 return vrc;
3826
3827 alock.release(); /* Release lock before waiting. */
3828
3829 GuestSessionStatus_T sessionStatus;
3830 vrc = i_waitForStatusChange(pEvent, fWaitFlags,
3831 uTimeoutMS - (tsNow - tsStart), &sessionStatus, pvrcGuest);
3832 if (RT_SUCCESS(vrc))
3833 {
3834 switch (sessionStatus)
3835 {
3836 case GuestSessionStatus_Started:
3837 waitResult = GuestSessionWaitResult_Start;
3838 break;
3839
3840 case GuestSessionStatus_Starting:
3841 RT_FALL_THROUGH();
3842 case GuestSessionStatus_Terminating:
3843 if (fWaitFlags & GuestSessionWaitForFlag_Status) /* Any status wanted? */
3844 waitResult = GuestSessionWaitResult_Status;
3845 /* else: Wait another round until we get the event(s) fWaitFlags defines. */
3846 break;
3847
3848 case GuestSessionStatus_Terminated:
3849 waitResult = GuestSessionWaitResult_Terminate;
3850 break;
3851
3852 case GuestSessionStatus_TimedOutKilled:
3853 RT_FALL_THROUGH();
3854 case GuestSessionStatus_TimedOutAbnormally:
3855 waitResult = GuestSessionWaitResult_Timeout;
3856 break;
3857
3858 case GuestSessionStatus_Down:
3859 waitResult = GuestSessionWaitResult_Terminate;
3860 break;
3861
3862 case GuestSessionStatus_Error:
3863 waitResult = GuestSessionWaitResult_Error;
3864 break;
3865
3866 default:
3867 waitResult = GuestSessionWaitResult_Status;
3868 break;
3869 }
3870 }
3871
3872 unregisterWaitEvent(pEvent);
3873
3874 /* Wait result not None, e.g. some result acquired or a wait error occurred? Bail out. */
3875 if ( waitResult != GuestSessionWaitResult_None
3876 || RT_FAILURE(vrc))
3877 break;
3878
3879 tsNow = RTTimeMilliTS();
3880
3881 alock.acquire(); /* Re-acquire lock before waiting for the next event. */
3882 }
3883
3884 if (tsNow - tsStart >= uTimeoutMS)
3885 {
3886 waitResult = GuestSessionWaitResult_None; /* Paranoia. */
3887 vrc = VERR_TIMEOUT;
3888 }
3889
3890 LogFlowFuncLeaveRC(vrc);
3891 return vrc;
3892}
3893
3894/**
3895 * Waits for guest session status changes.
3896 *
3897 * @returns VBox status code.
3898 * @retval VERR_GSTCTL_GUEST_ERROR on received guest error.
3899 * @retval VERR_WRONG_ORDER when an unexpected event type has been received.
3900 * @param pEvent Wait event to use for waiting.
3901 * @param fWaitFlags Wait flags to use.
3902 * @param uTimeoutMS Timeout (in ms) to wait.
3903 * @param pSessionStatus Where to return the guest session status.
3904 * @param pvrcGuest Where to return the guest error when
3905 * VERR_GSTCTL_GUEST_ERROR was returned. Optional.
3906 */
3907int GuestSession::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t fWaitFlags, uint32_t uTimeoutMS,
3908 GuestSessionStatus_T *pSessionStatus, int *pvrcGuest)
3909{
3910 RT_NOREF(fWaitFlags);
3911 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
3912
3913 VBoxEventType_T evtType;
3914 ComPtr<IEvent> pIEvent;
3915 int vrc = waitForEvent(pEvent, uTimeoutMS, &evtType, pIEvent.asOutParam());
3916 if (RT_SUCCESS(vrc))
3917 {
3918 if (evtType == VBoxEventType_OnGuestSessionStateChanged)
3919 {
3920 ComPtr<IGuestSessionStateChangedEvent> pChangedEvent = pIEvent;
3921 Assert(!pChangedEvent.isNull());
3922
3923 GuestSessionStatus_T sessionStatus;
3924 pChangedEvent->COMGETTER(Status)(&sessionStatus);
3925 if (pSessionStatus)
3926 *pSessionStatus = sessionStatus;
3927
3928 ComPtr<IVirtualBoxErrorInfo> errorInfo;
3929 HRESULT hrc = pChangedEvent->COMGETTER(Error)(errorInfo.asOutParam());
3930 ComAssertComRC(hrc);
3931
3932 LONG lGuestRc;
3933 hrc = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
3934 ComAssertComRC(hrc);
3935 if (RT_FAILURE((int)lGuestRc))
3936 vrc = VERR_GSTCTL_GUEST_ERROR;
3937 if (pvrcGuest)
3938 *pvrcGuest = (int)lGuestRc;
3939
3940 LogFlowThisFunc(("Status changed event for session ID=%RU32, new status is: %RU32 (%Rrc)\n",
3941 mData.mSession.mID, sessionStatus,
3942 RT_SUCCESS((int)lGuestRc) ? VINF_SUCCESS : (int)lGuestRc));
3943 }
3944 else /** @todo Re-visit this. Can this happen more frequently? */
3945 AssertMsgFailedReturn(("Got unexpected event type %#x\n", evtType), VERR_WRONG_ORDER);
3946 }
3947 /* waitForEvent may also return VERR_GSTCTL_GUEST_ERROR like we do above, so make pvrcGuest is set. */
3948 else if (pEvent->HasGuestError() && pvrcGuest)
3949 *pvrcGuest = pEvent->GuestResult();
3950 Assert(vrc != VERR_GSTCTL_GUEST_ERROR || !pvrcGuest || *pvrcGuest != (int)0xcccccccc);
3951
3952 LogFlowFuncLeaveRC(vrc);
3953 return vrc;
3954}
3955
3956// implementation of public methods
3957/////////////////////////////////////////////////////////////////////////////
3958
3959HRESULT GuestSession::close()
3960{
3961 AutoCaller autoCaller(this);
3962 if (FAILED(autoCaller.hrc())) return autoCaller.hrc();
3963
3964 LogFlowThisFuncEnter();
3965
3966 int vrc = VINF_SUCCESS; /* Shut up MSVC. */
3967
3968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3969
3970 /* If the guest session is not in an usable state (anymore), do the cleanup stuff ourselves. */
3971 if (!i_isReady())
3972 {
3973 i_onRemove();
3974 return S_OK;
3975 }
3976
3977 alock.release(); /* Leave lock before calling i_closeSession() below. */
3978
3979 int vrcGuest = VINF_SUCCESS;
3980
3981 /* If the session is in a started state, tell the guest to close it.
3982 *
3983 * Note: Don't check if the session is ready via i_isStartedExternal() here;
3984 * the session (already) could be in a stopped / aborted state.
3985 */
3986 AutoCaller autoCallerParent(mParent);
3987 if ( SUCCEEDED(autoCallerParent.hrc())
3988 && i_isStarted())
3989 {
3990 uint32_t msTimeout = RT_MS_10SEC; /* 10s timeout by default */
3991 for (int i = 0; i < 3; i++)
3992 {
3993 if (i)
3994 {
3995 LogRel(("Guest Control: Closing session '%s' timed out (%RU32s timeout, attempt %d/10), retrying ...\n",
3996 mData.mSession.mName.c_str(), msTimeout / RT_MS_1SEC, i + 1));
3997 msTimeout += RT_MS_5SEC; /* Slightly increase the timeout. */
3998 }
3999
4000 /* Close session on guest. */
4001 vrc = i_closeSession(0 /* Flags */, msTimeout, &vrcGuest);
4002 if ( RT_SUCCESS(vrc)
4003 || vrc != VERR_TIMEOUT) /* If something else happened there is no point in retrying further. */
4004 break;
4005 }
4006 }
4007
4008 /* On failure don't return here, instead do all the cleanup
4009 * work first and then return an error. */
4010
4011 /* We have to make sure that our parent (IGuest) still is alive and in a working shapee.
4012 * If not, skip removing the session from it. */
4013 LogFlowThisFunc(("Removing session '%s' from parent ...", mData.mSession.mName.c_str()));
4014
4015 /* Remove ourselves from the session list. */
4016 int vrc2 = mParent->i_sessionRemove(mData.mSession.mID);
4017 if (vrc2 == VERR_NOT_FOUND) /* Not finding the session anymore isn't critical. */
4018 vrc2 = VINF_SUCCESS;
4019
4020 if (RT_SUCCESS(vrc))
4021 vrc = vrc2;
4022
4023 LogFlowThisFunc(("Returning vrc=%Rrc, vrcGuest=%Rrc\n", vrc, vrcGuest));
4024
4025 if (RT_FAILURE(vrc))
4026 {
4027 if (vrc == VERR_GSTCTL_GUEST_ERROR)
4028 {
4029 GuestErrorInfo ge(GuestErrorInfo::Type_Session, vrcGuest, mData.mSession.mName.c_str());
4030 return setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Closing guest session failed: %s"),
4031 GuestBase::getErrorAsString(ge).c_str());
4032 }
4033 return setError(VBOX_E_IPRT_ERROR, tr("Closing guest session \"%s\" failed with %Rrc"),
4034 mData.mSession.mName.c_str(), vrc);
4035 }
4036
4037 return S_OK;
4038}
4039
4040HRESULT GuestSession::fileCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
4041 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
4042{
4043 RT_NOREF(aSource, aDestination, aFlags, aProgress);
4044 ReturnComNotImplemented();
4045}
4046
4047HRESULT GuestSession::fileCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
4048 const std::vector<FileCopyFlag_T> &aFlags,
4049 ComPtr<IProgress> &aProgress)
4050{
4051 uint32_t fFlags = FileCopyFlag_None;
4052 if (aFlags.size())
4053 {
4054 for (size_t i = 0; i < aFlags.size(); i++)
4055 fFlags |= aFlags[i];
4056
4057 const uint32_t fValidFlags = FileCopyFlag_None | FileCopyFlag_NoReplace | FileCopyFlag_FollowLinks | FileCopyFlag_Update;
4058 if (fFlags & ~fValidFlags)
4059 return setError(E_INVALIDARG,tr("Unknown flags: flags value %#x, invalid: %#x"), fFlags, fFlags & ~fValidFlags);
4060 }
4061
4062 GuestSessionFsSourceSet SourceSet;
4063
4064 GuestSessionFsSourceSpec source;
4065 source.strSource = aSource;
4066 source.enmType = FsObjType_File;
4067 source.enmPathStyle = i_getGuestPathStyle();
4068 source.fDryRun = false; /** @todo Implement support for a dry run. */
4069 source.fDirCopyFlags = DirectoryCopyFlag_None;
4070 source.fFileCopyFlags = (FileCopyFlag_T)fFlags;
4071
4072 SourceSet.push_back(source);
4073
4074 return i_copyFromGuest(SourceSet, aDestination, aProgress);
4075}
4076
4077HRESULT GuestSession::fileCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
4078 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
4079{
4080 uint32_t fFlags = FileCopyFlag_None;
4081 if (aFlags.size())
4082 {
4083 for (size_t i = 0; i < aFlags.size(); i++)
4084 fFlags |= aFlags[i];
4085
4086 const uint32_t fValidFlags = FileCopyFlag_None | FileCopyFlag_NoReplace | FileCopyFlag_FollowLinks | FileCopyFlag_Update;
4087 if (fFlags & ~fValidFlags)
4088 return setError(E_INVALIDARG,tr("Unknown flags: flags value %#x, invalid: %#x"), fFlags, fFlags & ~fValidFlags);
4089 }
4090
4091 GuestSessionFsSourceSet SourceSet;
4092
4093 GuestSessionFsSourceSpec source;
4094 source.strSource = aSource;
4095 source.enmType = FsObjType_File;
4096 source.enmPathStyle = GuestSession::i_getHostPathStyle();
4097 source.fDryRun = false; /** @todo Implement support for a dry run. */
4098 source.fDirCopyFlags = DirectoryCopyFlag_None;
4099 source.fFileCopyFlags = (FileCopyFlag_T)fFlags;
4100
4101 SourceSet.push_back(source);
4102
4103 return i_copyToGuest(SourceSet, aDestination, aProgress);
4104}
4105
4106HRESULT GuestSession::copyFromGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
4107 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
4108 ComPtr<IProgress> &aProgress)
4109{
4110 const size_t cSources = aSources.size();
4111 if ( (aFilters.size() && aFilters.size() != cSources)
4112 || (aFlags.size() && aFlags.size() != cSources))
4113 {
4114 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
4115 }
4116
4117 GuestSessionFsSourceSet SourceSet;
4118
4119 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
4120 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
4121 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
4122
4123 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
4124 const bool fFollowSymlinks = true; /** @todo Ditto. */
4125
4126 while (itSource != aSources.end())
4127 {
4128 GuestFsObjData objData;
4129 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4130 int vrc = i_fsObjQueryInfo(*(itSource), fFollowSymlinks, objData, &vrcGuest);
4131 if ( RT_FAILURE(vrc)
4132 && !fContinueOnErrors)
4133 {
4134 if (GuestProcess::i_isGuestError(vrc))
4135 {
4136 GuestErrorInfo ge(GuestErrorInfo::Type_Process, vrcGuest, (*itSource).c_str());
4137 return setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Querying type for guest source failed: %s"),
4138 GuestBase::getErrorAsString(ge).c_str());
4139 }
4140 return setError(E_FAIL, tr("Querying type for guest source \"%s\" failed: %Rrc"), (*itSource).c_str(), vrc);
4141 }
4142
4143 Utf8Str strFlags;
4144 if (itFlags != aFlags.end())
4145 {
4146 strFlags = *itFlags;
4147 ++itFlags;
4148 }
4149
4150 Utf8Str strFilter;
4151 if (itFilter != aFilters.end())
4152 {
4153 strFilter = *itFilter;
4154 ++itFilter;
4155 }
4156
4157 GuestSessionFsSourceSpec source;
4158 source.strSource = *itSource;
4159 source.strFilter = strFilter;
4160 source.enmType = objData.mType;
4161 source.enmPathStyle = i_getGuestPathStyle();
4162 source.fDryRun = false; /** @todo Implement support for a dry run. */
4163
4164 /* Check both flag groups here, as copying a directory also could mean to explicitly
4165 * *not* replacing any existing files (or just copy files which are newer, for instance). */
4166 GuestSession::i_directoryCopyFlagFromStr(strFlags, false /* fStrict */, &source.fDirCopyFlags);
4167 GuestSession::i_fileCopyFlagFromStr(strFlags, false /* fStrict */, &source.fFileCopyFlags);
4168
4169 SourceSet.push_back(source);
4170
4171 ++itSource;
4172 }
4173
4174 return i_copyFromGuest(SourceSet, aDestination, aProgress);
4175}
4176
4177HRESULT GuestSession::copyToGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
4178 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
4179 ComPtr<IProgress> &aProgress)
4180{
4181 const size_t cSources = aSources.size();
4182 if ( (aFilters.size() && aFilters.size() != cSources)
4183 || (aFlags.size() && aFlags.size() != cSources))
4184 {
4185 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
4186 }
4187
4188 GuestSessionFsSourceSet SourceSet;
4189
4190 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
4191 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
4192 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
4193
4194 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
4195
4196 while (itSource != aSources.end())
4197 {
4198 RTFSOBJINFO objInfo;
4199 int vrc = RTPathQueryInfo((*itSource).c_str(), &objInfo, RTFSOBJATTRADD_NOTHING);
4200 if ( RT_FAILURE(vrc)
4201 && !fContinueOnErrors)
4202 {
4203 return setError(E_FAIL, tr("Unable to query type for source '%s' (%Rrc)"), (*itSource).c_str(), vrc);
4204 }
4205
4206 Utf8Str strFlags;
4207 if (itFlags != aFlags.end())
4208 {
4209 strFlags = *itFlags;
4210 ++itFlags;
4211 }
4212
4213 Utf8Str strFilter;
4214 if (itFilter != aFilters.end())
4215 {
4216 strFilter = *itFilter;
4217 ++itFilter;
4218 }
4219
4220 GuestSessionFsSourceSpec source;
4221 source.strSource = *itSource;
4222 source.strFilter = strFilter;
4223 source.enmType = GuestBase::fileModeToFsObjType(objInfo.Attr.fMode);
4224 source.enmPathStyle = GuestSession::i_getHostPathStyle();
4225 source.fDryRun = false; /** @todo Implement support for a dry run. */
4226
4227 GuestSession::i_directoryCopyFlagFromStr(strFlags, false /* fStrict */, &source.fDirCopyFlags);
4228 GuestSession::i_fileCopyFlagFromStr(strFlags, false /* fStrict */, &source.fFileCopyFlags);
4229
4230 SourceSet.push_back(source);
4231
4232 ++itSource;
4233 }
4234
4235 /* (Re-)Validate stuff. */
4236 if (RT_UNLIKELY(SourceSet.size() == 0)) /* At least one source must be present. */
4237 return setError(E_INVALIDARG, tr("No sources specified"));
4238 if (RT_UNLIKELY(SourceSet[0].strSource.isEmpty()))
4239 return setError(E_INVALIDARG, tr("First source entry is empty"));
4240 if (RT_UNLIKELY(aDestination.isEmpty()))
4241 return setError(E_INVALIDARG, tr("No destination specified"));
4242
4243 return i_copyToGuest(SourceSet, aDestination, aProgress);
4244}
4245
4246HRESULT GuestSession::directoryCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
4247 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
4248{
4249 RT_NOREF(aSource, aDestination, aFlags, aProgress);
4250 ReturnComNotImplemented();
4251}
4252
4253HRESULT GuestSession::directoryCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
4254 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
4255{
4256 uint32_t fFlags = DirectoryCopyFlag_None;
4257 if (aFlags.size())
4258 {
4259 for (size_t i = 0; i < aFlags.size(); i++)
4260 fFlags |= aFlags[i];
4261
4262 const uint32_t fValidFlags = DirectoryCopyFlag_None | DirectoryCopyFlag_CopyIntoExisting | DirectoryCopyFlag_Recursive
4263 | DirectoryCopyFlag_FollowLinks;
4264 if (fFlags & ~fValidFlags)
4265 return setError(E_INVALIDARG,tr("Unknown flags: flags value %#x, invalid: %#x"), fFlags, fFlags & ~fValidFlags);
4266 }
4267
4268 GuestSessionFsSourceSet SourceSet;
4269
4270 GuestSessionFsSourceSpec source;
4271 source.strSource = aSource;
4272 source.enmType = FsObjType_Directory;
4273 source.enmPathStyle = i_getGuestPathStyle();
4274 source.fDryRun = false; /** @todo Implement support for a dry run. */
4275 source.fDirCopyFlags = (DirectoryCopyFlag_T)fFlags;
4276 source.fFileCopyFlags = FileCopyFlag_None; /* Overwrite existing files. */
4277
4278 SourceSet.push_back(source);
4279
4280 return i_copyFromGuest(SourceSet, aDestination, aProgress);
4281}
4282
4283HRESULT GuestSession::directoryCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
4284 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
4285{
4286 uint32_t fFlags = DirectoryCopyFlag_None;
4287 if (aFlags.size())
4288 {
4289 for (size_t i = 0; i < aFlags.size(); i++)
4290 fFlags |= aFlags[i];
4291
4292 const uint32_t fValidFlags = DirectoryCopyFlag_None | DirectoryCopyFlag_CopyIntoExisting | DirectoryCopyFlag_Recursive
4293 | DirectoryCopyFlag_FollowLinks;
4294 if (fFlags & ~fValidFlags)
4295 return setError(E_INVALIDARG,tr("Unknown flags: flags value %#x, invalid: %#x"), fFlags, fFlags & ~fValidFlags);
4296 }
4297
4298 GuestSessionFsSourceSet SourceSet;
4299
4300 GuestSessionFsSourceSpec source;
4301 source.strSource = aSource;
4302 source.enmType = FsObjType_Directory;
4303 source.enmPathStyle = GuestSession::i_getHostPathStyle();
4304 source.fDryRun = false; /** @todo Implement support for a dry run. */
4305 source.fDirCopyFlags = (DirectoryCopyFlag_T)fFlags;
4306 source.fFileCopyFlags = FileCopyFlag_None; /* Overwrite existing files. */
4307
4308 SourceSet.push_back(source);
4309
4310 return i_copyToGuest(SourceSet, aDestination, aProgress);
4311}
4312
4313HRESULT GuestSession::directoryCreate(const com::Utf8Str &aPath, ULONG aMode,
4314 const std::vector<DirectoryCreateFlag_T> &aFlags)
4315{
4316 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
4317 return setError(E_INVALIDARG, tr("No directory to create specified"));
4318
4319 uint32_t fFlags = DirectoryCreateFlag_None;
4320 if (aFlags.size())
4321 {
4322 for (size_t i = 0; i < aFlags.size(); i++)
4323 fFlags |= aFlags[i];
4324
4325 if (fFlags & ~DirectoryCreateFlag_Parents)
4326 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), fFlags);
4327 }
4328
4329 HRESULT hrc = i_isStartedExternal();
4330 if (FAILED(hrc))
4331 return hrc;
4332
4333 LogFlowThisFuncEnter();
4334
4335 ComObjPtr <GuestDirectory> pDirectory;
4336 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4337 int vrc = i_directoryCreate(aPath, (uint32_t)aMode, fFlags, &vrcGuest);
4338 if (RT_FAILURE(vrc))
4339 {
4340 if (GuestProcess::i_isGuestError(vrc))
4341 {
4342 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, vrcGuest, aPath.c_str());
4343 return setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Guest directory creation failed: %s"),
4344 GuestBase::getErrorAsString(ge).c_str());
4345 }
4346 switch (vrc)
4347 {
4348 case VERR_INVALID_PARAMETER:
4349 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Guest directory creation failed: Invalid parameters given"));
4350 break;
4351
4352 case VERR_BROKEN_PIPE:
4353 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Guest directory creation failed: Unexpectedly aborted"));
4354 break;
4355
4356 default:
4357 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Guest directory creation failed: %Rrc"), vrc);
4358 break;
4359 }
4360 }
4361
4362 return hrc;
4363}
4364
4365HRESULT GuestSession::directoryCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath,
4366 BOOL aSecure, com::Utf8Str &aDirectory)
4367{
4368 if (RT_UNLIKELY((aTemplateName.c_str()) == NULL || *(aTemplateName.c_str()) == '\0'))
4369 return setError(E_INVALIDARG, tr("No template specified"));
4370 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
4371 return setError(E_INVALIDARG, tr("No directory name specified"));
4372 if (!aSecure) /* Ignore what mode is specified when a secure temp thing needs to be created. */
4373 if (RT_UNLIKELY(aMode & ~07777))
4374 return setError(E_INVALIDARG, tr("Mode invalid (must be specified in octal mode)"));
4375
4376 HRESULT hrc = i_isStartedExternal();
4377 if (FAILED(hrc))
4378 return hrc;
4379
4380 LogFlowThisFuncEnter();
4381
4382 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4383 int vrc = i_fsCreateTemp(aTemplateName, aPath, true /* Directory */, aDirectory, aMode, RT_BOOL(aSecure), &vrcGuest);
4384 if (!RT_SUCCESS(vrc))
4385 {
4386 switch (vrc)
4387 {
4388 case VERR_GSTCTL_GUEST_ERROR:
4389 {
4390 GuestErrorInfo ge(GuestErrorInfo::Type_Fs, vrcGuest, aPath.c_str());
4391 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Temporary guest directory creation failed: %s"),
4392 GuestBase::getErrorAsString(ge).c_str());
4393 break;
4394 }
4395 default:
4396 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Temporary guest directory creation \"%s\" with template \"%s\" failed: %Rrc"),
4397 aPath.c_str(), aTemplateName.c_str(), vrc);
4398 break;
4399 }
4400 }
4401
4402 return hrc;
4403}
4404
4405HRESULT GuestSession::directoryExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
4406{
4407 if (RT_UNLIKELY(aPath.isEmpty()))
4408 return setError(E_INVALIDARG, tr("Empty path"));
4409
4410 HRESULT hrc = i_isStartedExternal();
4411 if (FAILED(hrc))
4412 return hrc;
4413
4414 LogFlowThisFuncEnter();
4415
4416 GuestFsObjData objData;
4417 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4418
4419 int vrc = i_directoryQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &vrcGuest);
4420 if (RT_SUCCESS(vrc))
4421 *aExists = TRUE;
4422 else
4423 {
4424 switch (vrc)
4425 {
4426 case VERR_GSTCTL_GUEST_ERROR:
4427 {
4428 switch (vrcGuest)
4429 {
4430 case VERR_PATH_NOT_FOUND:
4431 *aExists = FALSE;
4432 break;
4433 default:
4434 {
4435 GuestErrorInfo ge(GuestErrorInfo::Type_Fs, vrcGuest, aPath.c_str());
4436 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Querying directory existence failed: %s"),
4437 GuestBase::getErrorAsString(ge).c_str());
4438 break;
4439 }
4440 }
4441 break;
4442 }
4443
4444 case VERR_NOT_A_DIRECTORY:
4445 {
4446 *aExists = FALSE;
4447 break;
4448 }
4449
4450 default:
4451 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying directory existence \"%s\" failed: %Rrc"),
4452 aPath.c_str(), vrc);
4453 break;
4454 }
4455 }
4456
4457 return hrc;
4458}
4459
4460HRESULT GuestSession::directoryOpen(const com::Utf8Str &aPath, const com::Utf8Str &aFilter,
4461 const std::vector<DirectoryOpenFlag_T> &aFlags, ComPtr<IGuestDirectory> &aDirectory)
4462{
4463 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
4464 return setError(E_INVALIDARG, tr("No directory to open specified"));
4465 if (RT_UNLIKELY((aFilter.c_str()) != NULL && *(aFilter.c_str()) != '\0'))
4466 return setError(E_INVALIDARG, tr("Directory filters are not implemented yet"));
4467
4468 uint32_t fFlags = DirectoryOpenFlag_None;
4469 if (aFlags.size())
4470 {
4471 for (size_t i = 0; i < aFlags.size(); i++)
4472 fFlags |= aFlags[i];
4473
4474 if (fFlags)
4475 return setError(E_INVALIDARG, tr("Open flags (%#x) not implemented yet"), fFlags);
4476 }
4477
4478 HRESULT hrc = i_isStartedExternal();
4479 if (FAILED(hrc))
4480 return hrc;
4481
4482 LogFlowThisFuncEnter();
4483
4484 GuestDirectoryOpenInfo openInfo;
4485 openInfo.mPath = aPath;
4486 openInfo.mFilter = aFilter;
4487 openInfo.mFlags = fFlags;
4488
4489 ComObjPtr<GuestDirectory> pDirectory;
4490 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4491 int vrc = i_directoryOpen(openInfo, pDirectory, &vrcGuest);
4492 if (RT_SUCCESS(vrc))
4493 {
4494 /* Return directory object to the caller. */
4495 hrc = pDirectory.queryInterfaceTo(aDirectory.asOutParam());
4496 }
4497 else
4498 {
4499 switch (vrc)
4500 {
4501 case VERR_INVALID_PARAMETER:
4502 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest directory \"%s\" failed; invalid parameters given"),
4503 aPath.c_str());
4504 break;
4505
4506 case VERR_GSTCTL_GUEST_ERROR:
4507 {
4508 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, vrcGuest, aPath.c_str());
4509 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Opening guest directory failed: %s"),
4510 GuestBase::getErrorAsString(ge).c_str());
4511 break;
4512 }
4513 default:
4514 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4515 break;
4516 }
4517 }
4518
4519 return hrc;
4520}
4521
4522HRESULT GuestSession::directoryRemove(const com::Utf8Str &aPath)
4523{
4524 if (RT_UNLIKELY(aPath.c_str() == NULL || *aPath.c_str() == '\0'))
4525 return setError(E_INVALIDARG, tr("No directory to remove specified"));
4526
4527 HRESULT hrc = i_isStartedExternal();
4528 if (FAILED(hrc))
4529 return hrc;
4530
4531 LogFlowThisFuncEnter();
4532
4533 /* No flags; only remove the directory when empty. */
4534 uint32_t fFlags = DIRREMOVEREC_FLAG_NONE;
4535
4536 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4537 int vrc = i_directoryRemove(aPath, fFlags, &vrcGuest);
4538 if (RT_FAILURE(vrc))
4539 {
4540 switch (vrc)
4541 {
4542 case VERR_NOT_SUPPORTED:
4543 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4544 tr("Handling removing guest directories not supported by installed Guest Additions"));
4545 break;
4546
4547 case VERR_GSTCTL_GUEST_ERROR:
4548 {
4549 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, vrcGuest, aPath.c_str());
4550 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Removing guest directory failed: %s"),
4551 GuestBase::getErrorAsString(ge).c_str());
4552 break;
4553 }
4554 default:
4555 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing guest directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4556 break;
4557 }
4558 }
4559
4560 return hrc;
4561}
4562
4563HRESULT GuestSession::directoryRemoveRecursive(const com::Utf8Str &aPath, const std::vector<DirectoryRemoveRecFlag_T> &aFlags,
4564 ComPtr<IProgress> &aProgress)
4565{
4566 if (RT_UNLIKELY(aPath.c_str() == NULL || *aPath.c_str() == '\0'))
4567 return setError(E_INVALIDARG, tr("No directory to remove recursively specified"));
4568
4569 /* By defautl remove recursively as the function name implies. */
4570 uint32_t fFlags = DIRREMOVEREC_FLAG_RECURSIVE;
4571 if (aFlags.size())
4572 {
4573 for (size_t i = 0; i < aFlags.size(); i++)
4574 {
4575 switch (aFlags[i])
4576 {
4577 case DirectoryRemoveRecFlag_None: /* Skip. */
4578 continue;
4579
4580 case DirectoryRemoveRecFlag_ContentAndDir:
4581 fFlags |= DIRREMOVEREC_FLAG_CONTENT_AND_DIR;
4582 break;
4583
4584 case DirectoryRemoveRecFlag_ContentOnly:
4585 fFlags |= DIRREMOVEREC_FLAG_CONTENT_ONLY;
4586 break;
4587
4588 default:
4589 return setError(E_INVALIDARG, tr("Invalid flags specified"));
4590 }
4591 }
4592 }
4593
4594 HRESULT hrc = i_isStartedExternal();
4595 if (FAILED(hrc))
4596 return hrc;
4597
4598 LogFlowThisFuncEnter();
4599
4600 ComObjPtr<Progress> pProgress;
4601 hrc = pProgress.createObject();
4602 if (SUCCEEDED(hrc))
4603 hrc = pProgress->init(static_cast<IGuestSession *>(this),
4604 Bstr(tr("Removing guest directory")).raw(),
4605 TRUE /*aCancelable*/);
4606 if (FAILED(hrc))
4607 return hrc;
4608
4609 /* Note: At the moment we don't supply progress information while
4610 * deleting a guest directory recursively. So just complete
4611 * the progress object right now. */
4612 /** @todo Implement progress reporting on guest directory deletion! */
4613 hrc = pProgress->i_notifyComplete(S_OK);
4614 if (FAILED(hrc))
4615 return hrc;
4616
4617 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4618 int vrc = i_directoryRemove(aPath, fFlags, &vrcGuest);
4619 if (RT_FAILURE(vrc))
4620 {
4621 switch (vrc)
4622 {
4623 case VERR_NOT_SUPPORTED:
4624 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4625 tr("Handling removing guest directories recursively not supported by installed Guest Additions"));
4626 break;
4627
4628 case VERR_GSTCTL_GUEST_ERROR:
4629 {
4630 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, vrcGuest, aPath.c_str());
4631 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Recursively removing guest directory failed: %s"),
4632 GuestBase::getErrorAsString(ge).c_str());
4633 break;
4634 }
4635 default:
4636 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Recursively removing guest directory \"%s\" failed: %Rrc"),
4637 aPath.c_str(), vrc);
4638 break;
4639 }
4640 }
4641 else
4642 {
4643 pProgress.queryInterfaceTo(aProgress.asOutParam());
4644 }
4645
4646 return hrc;
4647}
4648
4649HRESULT GuestSession::environmentScheduleSet(const com::Utf8Str &aName, const com::Utf8Str &aValue)
4650{
4651 LogFlowThisFuncEnter();
4652 int vrc;
4653 {
4654 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4655 vrc = mData.mEnvironmentChanges.setVariable(aName, aValue);
4656 }
4657 HRESULT hrc;
4658 if (RT_SUCCESS(vrc))
4659 hrc = S_OK;
4660 else if (vrc == VERR_ENV_INVALID_VAR_NAME)
4661 hrc = setError(E_INVALIDARG, tr("Invalid environment variable name '%s'"), aName.c_str());
4662 else
4663 hrc = setErrorVrc(vrc, tr("Failed to schedule setting environment variable '%s' to '%s'"), aName.c_str(), aValue.c_str());
4664
4665 LogFlowThisFuncLeave();
4666 return hrc;
4667}
4668
4669HRESULT GuestSession::environmentScheduleUnset(const com::Utf8Str &aName)
4670{
4671 LogFlowThisFuncEnter();
4672 int vrc;
4673 {
4674 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4675 vrc = mData.mEnvironmentChanges.unsetVariable(aName);
4676 }
4677 HRESULT hrc;
4678 if (RT_SUCCESS(vrc))
4679 hrc = S_OK;
4680 else if (vrc == VERR_ENV_INVALID_VAR_NAME)
4681 hrc = setError(E_INVALIDARG, tr("Invalid environment variable name '%s'"), aName.c_str());
4682 else
4683 hrc = setErrorVrc(vrc, tr("Failed to schedule unsetting environment variable '%s'"), aName.c_str());
4684
4685 LogFlowThisFuncLeave();
4686 return hrc;
4687}
4688
4689HRESULT GuestSession::environmentGetBaseVariable(const com::Utf8Str &aName, com::Utf8Str &aValue)
4690{
4691 LogFlowThisFuncEnter();
4692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4693
4694 HRESULT hrc;
4695 if (mData.mpBaseEnvironment)
4696 {
4697 int vrc = mData.mpBaseEnvironment->getVariable(aName, &aValue);
4698 if (RT_SUCCESS(vrc))
4699 hrc = S_OK;
4700 else if (vrc == VERR_ENV_INVALID_VAR_NAME)
4701 hrc = setError(E_INVALIDARG, tr("Invalid environment variable name '%s'"), aName.c_str());
4702 else
4703 hrc = setErrorVrc(vrc);
4704 }
4705 else if (mData.mProtocolVersion < 99999)
4706 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the Guest Additions"));
4707 else
4708 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
4709
4710 LogFlowThisFuncLeave();
4711 return hrc;
4712}
4713
4714HRESULT GuestSession::environmentDoesBaseVariableExist(const com::Utf8Str &aName, BOOL *aExists)
4715{
4716 LogFlowThisFuncEnter();
4717 *aExists = FALSE;
4718 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4719
4720 HRESULT hrc;
4721 if (mData.mpBaseEnvironment)
4722 {
4723 hrc = S_OK;
4724 *aExists = mData.mpBaseEnvironment->doesVariableExist(aName);
4725 }
4726 else if (mData.mProtocolVersion < 99999)
4727 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the Guest Additions"));
4728 else
4729 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
4730
4731 LogFlowThisFuncLeave();
4732 return hrc;
4733}
4734
4735HRESULT GuestSession::fileCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath, BOOL aSecure,
4736 ComPtr<IGuestFile> &aFile)
4737{
4738 RT_NOREF(aTemplateName, aMode, aPath, aSecure, aFile);
4739 ReturnComNotImplemented();
4740}
4741
4742HRESULT GuestSession::fileExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
4743{
4744 /* By default we return non-existent. */
4745 *aExists = FALSE;
4746
4747 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
4748 return S_OK;
4749
4750 HRESULT hrc = i_isStartedExternal();
4751 if (FAILED(hrc))
4752 return hrc;
4753
4754 LogFlowThisFuncEnter();
4755
4756 GuestFsObjData objData;
4757 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4758 int vrc = i_fileQueryInfo(aPath, RT_BOOL(aFollowSymlinks), objData, &vrcGuest);
4759 if (RT_SUCCESS(vrc))
4760 {
4761 *aExists = TRUE;
4762 return S_OK;
4763 }
4764
4765 switch (vrc)
4766 {
4767 case VERR_GSTCTL_GUEST_ERROR:
4768 {
4769 switch (vrcGuest)
4770 {
4771 case VERR_PATH_NOT_FOUND:
4772 RT_FALL_THROUGH();
4773 case VERR_FILE_NOT_FOUND:
4774 break;
4775
4776 default:
4777 {
4778 GuestErrorInfo ge(GuestErrorInfo::Type_Fs, vrcGuest, aPath.c_str());
4779 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Querying guest file existence failed: %s"),
4780 GuestBase::getErrorAsString(ge).c_str());
4781 break;
4782 }
4783 }
4784
4785 break;
4786 }
4787
4788 case VERR_NOT_A_FILE:
4789 break;
4790
4791 default:
4792 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying guest file information for \"%s\" failed: %Rrc"),
4793 aPath.c_str(), vrc);
4794 break;
4795 }
4796
4797 return hrc;
4798}
4799
4800HRESULT GuestSession::fileOpen(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
4801 ULONG aCreationMode, ComPtr<IGuestFile> &aFile)
4802{
4803 LogFlowThisFuncEnter();
4804
4805 const std::vector<FileOpenExFlag_T> EmptyFlags;
4806 return fileOpenEx(aPath, aAccessMode, aOpenAction, FileSharingMode_All, aCreationMode, EmptyFlags, aFile);
4807}
4808
4809HRESULT GuestSession::fileOpenEx(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
4810 FileSharingMode_T aSharingMode, ULONG aCreationMode,
4811 const std::vector<FileOpenExFlag_T> &aFlags, ComPtr<IGuestFile> &aFile)
4812{
4813 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
4814 return setError(E_INVALIDARG, tr("No file to open specified"));
4815
4816 HRESULT hrc = i_isStartedExternal();
4817 if (FAILED(hrc))
4818 return hrc;
4819
4820 LogFlowThisFuncEnter();
4821
4822 /* Validate aAccessMode. */
4823 switch (aAccessMode)
4824 {
4825 case FileAccessMode_ReadOnly:
4826 RT_FALL_THRU();
4827 case FileAccessMode_WriteOnly:
4828 RT_FALL_THRU();
4829 case FileAccessMode_ReadWrite:
4830 break;
4831 case FileAccessMode_AppendOnly:
4832 RT_FALL_THRU();
4833 case FileAccessMode_AppendRead:
4834 return setError(E_NOTIMPL, tr("Append access modes are not yet implemented"));
4835 default:
4836 return setError(E_INVALIDARG, tr("Unknown FileAccessMode value %u (%#x)"), aAccessMode, aAccessMode);
4837 }
4838
4839 /* Validate aOpenAction to the old format. */
4840 switch (aOpenAction)
4841 {
4842 case FileOpenAction_OpenExisting:
4843 RT_FALL_THRU();
4844 case FileOpenAction_OpenOrCreate:
4845 RT_FALL_THRU();
4846 case FileOpenAction_CreateNew:
4847 RT_FALL_THRU();
4848 case FileOpenAction_CreateOrReplace:
4849 RT_FALL_THRU();
4850 case FileOpenAction_OpenExistingTruncated:
4851 RT_FALL_THRU();
4852 case FileOpenAction_AppendOrCreate:
4853 break;
4854 default:
4855 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
4856 }
4857
4858 /* Validate aSharingMode. */
4859 switch (aSharingMode)
4860 {
4861 case FileSharingMode_All:
4862 break;
4863 case FileSharingMode_Read:
4864 case FileSharingMode_Write:
4865 case FileSharingMode_ReadWrite:
4866 case FileSharingMode_Delete:
4867 case FileSharingMode_ReadDelete:
4868 case FileSharingMode_WriteDelete:
4869 return setError(E_NOTIMPL, tr("Only FileSharingMode_All is currently implemented"));
4870
4871 default:
4872 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
4873 }
4874
4875 /* Combine and validate flags. */
4876 uint32_t fOpenEx = 0;
4877 for (size_t i = 0; i < aFlags.size(); i++)
4878 fOpenEx |= aFlags[i];
4879 if (fOpenEx)
4880 return setError(E_INVALIDARG, tr("Unsupported FileOpenExFlag value(s) in aFlags (%#x)"), fOpenEx);
4881
4882 ComObjPtr <GuestFile> pFile;
4883 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4884 int vrc = i_fileOpenEx(aPath, aAccessMode, aOpenAction, aSharingMode, aCreationMode, aFlags, pFile, &vrcGuest);
4885 if (RT_SUCCESS(vrc))
4886 /* Return directory object to the caller. */
4887 hrc = pFile.queryInterfaceTo(aFile.asOutParam());
4888 else
4889 {
4890 switch (vrc)
4891 {
4892 case VERR_NOT_SUPPORTED:
4893 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4894 tr("Handling guest files not supported by installed Guest Additions"));
4895 break;
4896
4897 case VERR_GSTCTL_GUEST_ERROR:
4898 {
4899 GuestErrorInfo ge(GuestErrorInfo::Type_File, vrcGuest, aPath.c_str());
4900 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Opening guest file failed: %s"),
4901 GuestBase::getErrorAsString(ge).c_str());
4902 break;
4903 }
4904 default:
4905 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4906 break;
4907 }
4908 }
4909
4910 return hrc;
4911}
4912
4913HRESULT GuestSession::fileQuerySize(const com::Utf8Str &aPath, BOOL aFollowSymlinks, LONG64 *aSize)
4914{
4915 if (aPath.isEmpty())
4916 return setError(E_INVALIDARG, tr("No path specified"));
4917
4918 HRESULT hrc = i_isStartedExternal();
4919 if (FAILED(hrc))
4920 return hrc;
4921
4922 int64_t llSize;
4923 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4924 int vrc = i_fileQuerySize(aPath, aFollowSymlinks != FALSE, &llSize, &vrcGuest);
4925 if (RT_SUCCESS(vrc))
4926 *aSize = llSize;
4927 else
4928 {
4929 if (GuestProcess::i_isGuestError(vrc))
4930 {
4931 GuestErrorInfo ge(GuestErrorInfo::Type_Fs, vrcGuest, aPath.c_str());
4932 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Querying guest file size failed: %s"),
4933 GuestBase::getErrorAsString(ge).c_str());
4934 }
4935 else
4936 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying guest file size of \"%s\" failed: %Rrc"),
4937 vrc, aPath.c_str());
4938 }
4939
4940 return hrc;
4941}
4942
4943HRESULT GuestSession::fsQueryFreeSpace(const com::Utf8Str &aPath, LONG64 *aFreeSpace)
4944{
4945 ComPtr<IGuestFsInfo> pFsInfo;
4946 HRESULT hrc = fsQueryInfo(aPath, pFsInfo);
4947 if (SUCCEEDED(hrc))
4948 hrc = pFsInfo->COMGETTER(FreeSize)(aFreeSpace);
4949
4950 return hrc;
4951}
4952
4953HRESULT GuestSession::fsQueryInfo(const com::Utf8Str &aPath, ComPtr<IGuestFsInfo> &aInfo)
4954{
4955 if (aPath.isEmpty())
4956 return setError(E_INVALIDARG, tr("No path specified"));
4957
4958 HRESULT hrc = i_isStartedExternal();
4959 if (FAILED(hrc))
4960 return hrc;
4961
4962 GSTCTLFSINFO fsInfo;
4963 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4964 int vrc = i_fsQueryInfo(aPath, &fsInfo, &vrcGuest);
4965 if (RT_SUCCESS(vrc))
4966 {
4967 ComObjPtr<GuestFsInfo> ptrFsInfo;
4968 hrc = ptrFsInfo.createObject();
4969 if (SUCCEEDED(hrc))
4970 {
4971 vrc = ptrFsInfo->init(&fsInfo);
4972 if (RT_SUCCESS(vrc))
4973 hrc = ptrFsInfo.queryInterfaceTo(aInfo.asOutParam());
4974 else
4975 hrc = setErrorVrc(vrc);
4976 }
4977 }
4978 else
4979 {
4980 if (GuestProcess::i_isGuestError(vrc))
4981 {
4982 GuestErrorInfo ge(GuestErrorInfo::Type_Fs, vrcGuest, aPath.c_str());
4983 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Querying guest filesystem information failed: %s"),
4984 GuestBase::getErrorAsString(ge).c_str());
4985 }
4986 else
4987 hrc = setErrorVrc(vrc, tr("Querying guest filesystem information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4988 }
4989
4990 return hrc;
4991}
4992
4993HRESULT GuestSession::fsObjExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
4994{
4995 if (aPath.isEmpty())
4996 return setError(E_INVALIDARG, tr("No path specified"));
4997
4998 HRESULT hrc = i_isStartedExternal();
4999 if (FAILED(hrc))
5000 return hrc;
5001
5002 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
5003
5004 *aExists = false;
5005
5006 GuestFsObjData objData;
5007 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
5008 int vrc = i_fsObjQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &vrcGuest);
5009 if (RT_SUCCESS(vrc))
5010 *aExists = TRUE;
5011 else
5012 {
5013 if (GuestProcess::i_isGuestError(vrc))
5014 {
5015 if ( vrcGuest == VERR_NOT_A_FILE
5016 || vrcGuest == VERR_PATH_NOT_FOUND
5017 || vrcGuest == VERR_FILE_NOT_FOUND
5018 || vrcGuest == VERR_INVALID_NAME)
5019 hrc = S_OK; /* Ignore these vrc values. */
5020 else
5021 {
5022 GuestErrorInfo ge(GuestErrorInfo::Type_Fs, vrcGuest, aPath.c_str());
5023 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Querying guest file existence information failed: %s"),
5024 GuestBase::getErrorAsString(ge).c_str());
5025 }
5026 }
5027 else
5028 hrc = setErrorVrc(vrc, tr("Querying guest file existence information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
5029 }
5030
5031 return hrc;
5032}
5033
5034HRESULT GuestSession::fsObjQueryInfo(const com::Utf8Str &aPath, BOOL aFollowSymlinks, ComPtr<IGuestFsObjInfo> &aInfo)
5035{
5036 if (aPath.isEmpty())
5037 return setError(E_INVALIDARG, tr("No path specified"));
5038
5039 HRESULT hrc = i_isStartedExternal();
5040 if (FAILED(hrc))
5041 return hrc;
5042
5043 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
5044
5045 GuestFsObjData objData;
5046 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
5047 int vrc = i_fsObjQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &vrcGuest);
5048 if (RT_SUCCESS(vrc))
5049 {
5050 ComObjPtr<GuestFsObjInfo> ptrFsObjInfo;
5051 hrc = ptrFsObjInfo.createObject();
5052 if (SUCCEEDED(hrc))
5053 {
5054 vrc = ptrFsObjInfo->init(objData);
5055 if (RT_SUCCESS(vrc))
5056 hrc = ptrFsObjInfo.queryInterfaceTo(aInfo.asOutParam());
5057 else
5058 hrc = setErrorVrc(vrc);
5059 }
5060 }
5061 else
5062 {
5063 if (GuestProcess::i_isGuestError(vrc))
5064 {
5065 GuestErrorInfo ge(GuestErrorInfo::Type_Fs, vrcGuest, aPath.c_str());
5066 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Querying guest filesystem object information failed: %s"),
5067 GuestBase::getErrorAsString(ge).c_str());
5068 }
5069 else
5070 hrc = setErrorVrc(vrc, tr("Querying guest filesystem object information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
5071 }
5072
5073 return hrc;
5074}
5075
5076HRESULT GuestSession::fsObjRemove(const com::Utf8Str &aPath)
5077{
5078 if (RT_UNLIKELY(aPath.isEmpty()))
5079 return setError(E_INVALIDARG, tr("No path specified"));
5080
5081 HRESULT hrc = i_isStartedExternal();
5082 if (FAILED(hrc))
5083 return hrc;
5084
5085 LogFlowThisFunc(("aPath=%s\n", aPath.c_str()));
5086
5087 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
5088 int vrc = i_fileRemove(aPath, &vrcGuest);
5089 if (RT_FAILURE(vrc))
5090 {
5091 if (GuestProcess::i_isGuestError(vrc))
5092 {
5093 GuestErrorInfo ge(GuestErrorInfo::Type_File, vrcGuest, aPath.c_str());
5094 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Removing guest file failed: %s"),
5095 GuestBase::getErrorAsString(ge).c_str());
5096 }
5097 else
5098 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing guest file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
5099 }
5100
5101 return hrc;
5102}
5103
5104HRESULT GuestSession::fsObjRemoveArray(const std::vector<com::Utf8Str> &aPaths, ComPtr<IProgress> &aProgress)
5105{
5106 RT_NOREF(aPaths, aProgress);
5107 return E_NOTIMPL;
5108}
5109
5110HRESULT GuestSession::fsObjRename(const com::Utf8Str &aSource,
5111 const com::Utf8Str &aDestination,
5112 const std::vector<FsObjRenameFlag_T> &aFlags)
5113{
5114 if (RT_UNLIKELY(aSource.isEmpty()))
5115 return setError(E_INVALIDARG, tr("No source path specified"));
5116
5117 if (RT_UNLIKELY(aDestination.isEmpty()))
5118 return setError(E_INVALIDARG, tr("No destination path specified"));
5119
5120 HRESULT hrc = i_isStartedExternal();
5121 if (FAILED(hrc))
5122 return hrc;
5123
5124 /* Combine, validate and convert flags. */
5125 uint32_t fApiFlags = 0;
5126 for (size_t i = 0; i < aFlags.size(); i++)
5127 fApiFlags |= aFlags[i];
5128 if (fApiFlags & ~((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace))
5129 return setError(E_INVALIDARG, tr("Unknown rename flag: %#x"), fApiFlags);
5130
5131 LogFlowThisFunc(("aSource=%s, aDestination=%s\n", aSource.c_str(), aDestination.c_str()));
5132
5133 AssertCompile(FsObjRenameFlag_NoReplace == 0);
5134 AssertCompile(FsObjRenameFlag_Replace != 0);
5135 uint32_t fBackend;
5136 if ((fApiFlags & ((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace)) == FsObjRenameFlag_Replace)
5137 fBackend = PATHRENAME_FLAG_REPLACE;
5138 else
5139 fBackend = PATHRENAME_FLAG_NO_REPLACE;
5140
5141 /* Call worker to do the job. */
5142 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
5143 int vrc = i_pathRename(aSource, aDestination, fBackend, &vrcGuest);
5144 if (RT_FAILURE(vrc))
5145 {
5146 switch (vrc)
5147 {
5148 case VERR_NOT_SUPPORTED:
5149 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5150 tr("Handling renaming guest paths not supported by installed Guest Additions"));
5151 break;
5152
5153 case VERR_GSTCTL_GUEST_ERROR:
5154 {
5155 GuestErrorInfo ge(GuestErrorInfo::Type_Process, vrcGuest, aSource.c_str());
5156 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Renaming guest path failed: %s"),
5157 GuestBase::getErrorAsString(ge).c_str());
5158 break;
5159 }
5160 default:
5161 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming guest path \"%s\" failed: %Rrc"),
5162 aSource.c_str(), vrc);
5163 break;
5164 }
5165 }
5166
5167 return hrc;
5168}
5169
5170HRESULT GuestSession::fsObjMove(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
5171 const std::vector<FsObjMoveFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
5172{
5173 RT_NOREF(aSource, aDestination, aFlags, aProgress);
5174 ReturnComNotImplemented();
5175}
5176
5177HRESULT GuestSession::fsObjMoveArray(const std::vector<com::Utf8Str> &aSource,
5178 const com::Utf8Str &aDestination,
5179 const std::vector<FsObjMoveFlag_T> &aFlags,
5180 ComPtr<IProgress> &aProgress)
5181{
5182 RT_NOREF(aSource, aDestination, aFlags, aProgress);
5183 ReturnComNotImplemented();
5184}
5185
5186HRESULT GuestSession::fsObjCopyArray(const std::vector<com::Utf8Str> &aSource,
5187 const com::Utf8Str &aDestination,
5188 const std::vector<FileCopyFlag_T> &aFlags,
5189 ComPtr<IProgress> &aProgress)
5190{
5191 RT_NOREF(aSource, aDestination, aFlags, aProgress);
5192 ReturnComNotImplemented();
5193}
5194
5195HRESULT GuestSession::fsObjSetACL(const com::Utf8Str &aPath, BOOL aFollowSymlinks, const com::Utf8Str &aAcl, ULONG aMode)
5196{
5197 RT_NOREF(aPath, aFollowSymlinks, aAcl, aMode);
5198 ReturnComNotImplemented();
5199}
5200
5201
5202HRESULT GuestSession::processCreate(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
5203 const com::Utf8Str &aCwd,
5204 const std::vector<com::Utf8Str> &aEnvironment,
5205 const std::vector<ProcessCreateFlag_T> &aFlags,
5206 ULONG aTimeoutMS, ComPtr<IGuestProcess> &aGuestProcess)
5207{
5208 LogFlowThisFuncEnter();
5209
5210 std::vector<LONG> affinityIgnored;
5211 return processCreateEx(aExecutable, aArguments, aCwd, aEnvironment, aFlags, aTimeoutMS,
5212 ProcessPriority_Default, affinityIgnored, aGuestProcess);
5213}
5214
5215HRESULT GuestSession::processCreateEx(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
5216 const com::Utf8Str &aCwd,
5217 const std::vector<com::Utf8Str> &aEnvironment,
5218 const std::vector<ProcessCreateFlag_T> &aFlags, ULONG aTimeoutMS,
5219 ProcessPriority_T aPriority, const std::vector<LONG> &aAffinity,
5220 ComPtr<IGuestProcess> &aGuestProcess)
5221{
5222 HRESULT hrc = i_isStartedExternal();
5223 if (FAILED(hrc))
5224 return hrc;
5225
5226 /*
5227 * Must have an executable to execute. If none is given, we try use the
5228 * zero'th argument.
5229 */
5230 const char *pszExecutable = aExecutable.c_str();
5231 if (RT_UNLIKELY(pszExecutable == NULL || *pszExecutable == '\0'))
5232 {
5233 if (aArguments.size() > 0)
5234 pszExecutable = aArguments[0].c_str();
5235 if (pszExecutable == NULL || *pszExecutable == '\0')
5236 return setError(E_INVALIDARG, tr("No command to execute specified"));
5237 }
5238
5239 uint32_t const uProtocol = i_getProtocolVersion();
5240 uint64_t const fGuestControlFeatures0 = mParent->i_getGuestControlFeatures0();
5241
5242 /* If a current working directory (CWD) is set, make sure that the installed Guest Additions actually
5243 * support this before doing anything else. */
5244 if ( !aCwd.isEmpty()
5245 && ( uProtocol < 2
5246 || !(fGuestControlFeatures0 & VBOX_GUESTCTRL_GF_0_PROCESS_CWD)))
5247 return setError(VBOX_E_NOT_SUPPORTED,
5248 tr("Setting the current working directory is not supported by the installed Guest Addtions!"));
5249
5250 /* The rest of the input is being validated in i_processCreateEx(). */
5251
5252 LogFlowThisFuncEnter();
5253
5254 /*
5255 * Build the process startup info.
5256 */
5257 GuestProcessStartupInfo procInfo;
5258
5259 /* Executable and arguments. */
5260 procInfo.mExecutable = pszExecutable;
5261 if (aArguments.size())
5262 {
5263 for (size_t i = 0; i < aArguments.size(); i++)
5264 procInfo.mArguments.push_back(aArguments[i]);
5265 }
5266 else /* If no arguments were given, add the executable as argv[0] by default. */
5267 procInfo.mArguments.push_back(procInfo.mExecutable);
5268
5269 /* Optional working directory */
5270 procInfo.mCwd = aCwd;
5271
5272 /* Combine the environment changes associated with the ones passed in by
5273 the caller, giving priority to the latter. The changes are putenv style
5274 and will be applied to the standard environment for the guest user. */
5275 int vrc = procInfo.mEnvironmentChanges.copy(mData.mEnvironmentChanges);
5276 if (RT_SUCCESS(vrc))
5277 {
5278 size_t idxError = ~(size_t)0;
5279 vrc = procInfo.mEnvironmentChanges.applyPutEnvArray(aEnvironment, &idxError);
5280 if (RT_SUCCESS(vrc))
5281 {
5282 /* Convert the flag array into a mask. */
5283 if (aFlags.size())
5284 for (size_t i = 0; i < aFlags.size(); i++)
5285 procInfo.mFlags |= aFlags[i];
5286
5287 procInfo.mTimeoutMS = aTimeoutMS;
5288
5289 /** @todo use RTCPUSET instead of archaic 64-bit variables! */
5290 if (aAffinity.size())
5291 for (size_t i = 0; i < aAffinity.size(); i++)
5292 if (aAffinity[i])
5293 procInfo.mAffinity |= (uint64_t)1 << i;
5294
5295 procInfo.mPriority = aPriority;
5296
5297 /*
5298 * Create a guest process object.
5299 */
5300 ComObjPtr<GuestProcess> pProcess;
5301 vrc = i_processCreateEx(procInfo, pProcess);
5302 if (RT_SUCCESS(vrc))
5303 {
5304 ComPtr<IGuestProcess> pIProcess;
5305 hrc = pProcess.queryInterfaceTo(pIProcess.asOutParam());
5306 if (SUCCEEDED(hrc))
5307 {
5308 /*
5309 * Start the process.
5310 */
5311 vrc = pProcess->i_startProcessAsync();
5312 if (RT_SUCCESS(vrc))
5313 {
5314 aGuestProcess = pIProcess;
5315
5316 LogFlowFuncLeaveRC(vrc);
5317 return S_OK;
5318 }
5319
5320 hrc = setErrorVrc(vrc, tr("Failed to start guest process: %Rrc"), vrc);
5321 }
5322 }
5323 else if (vrc == VERR_GSTCTL_MAX_CID_OBJECTS_REACHED)
5324 hrc = setErrorVrc(vrc, tr("Maximum number of concurrent guest processes per session (%u) reached"),
5325 VBOX_GUESTCTRL_MAX_OBJECTS);
5326 else
5327 hrc = setErrorVrc(vrc, tr("Failed to create guest process object: %Rrc"), vrc);
5328 }
5329 else
5330 hrc = setErrorBoth(vrc == VERR_ENV_INVALID_VAR_NAME ? E_INVALIDARG : Global::vboxStatusCodeToCOM(vrc), vrc,
5331 tr("Failed to apply environment variable '%s', index %u (%Rrc)'"),
5332 aEnvironment[idxError].c_str(), idxError, vrc);
5333 }
5334 else
5335 hrc = setErrorVrc(vrc, tr("Failed to set up the environment: %Rrc"), vrc);
5336
5337 LogFlowFuncLeaveRC(vrc);
5338 return hrc;
5339}
5340
5341HRESULT GuestSession::processGet(ULONG aPid, ComPtr<IGuestProcess> &aGuestProcess)
5342
5343{
5344 if (aPid == 0)
5345 return setError(E_INVALIDARG, tr("No valid process ID (PID) specified"));
5346
5347 LogFlowThisFunc(("PID=%RU32\n", aPid));
5348
5349 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5350
5351 HRESULT hrc = S_OK;
5352
5353 ComObjPtr<GuestProcess> pProcess;
5354 int vrc = i_processGetByPID(aPid, &pProcess);
5355 if (RT_FAILURE(vrc))
5356 hrc = setError(E_INVALIDARG, tr("No process with PID %RU32 found"), aPid);
5357
5358 /* This will set (*aProcess) to NULL if pProgress is NULL. */
5359 HRESULT hrc2 = pProcess.queryInterfaceTo(aGuestProcess.asOutParam());
5360 if (SUCCEEDED(hrc))
5361 hrc = hrc2;
5362
5363 LogFlowThisFunc(("aProcess=%p, hrc=%Rhrc\n", (IGuestProcess*)aGuestProcess, hrc));
5364 return hrc;
5365}
5366
5367HRESULT GuestSession::symlinkCreate(const com::Utf8Str &aSource, const com::Utf8Str &aTarget, SymlinkType_T aType)
5368{
5369 RT_NOREF(aSource, aTarget, aType);
5370 ReturnComNotImplemented();
5371}
5372
5373HRESULT GuestSession::symlinkExists(const com::Utf8Str &aSymlink, BOOL *aExists)
5374
5375{
5376 RT_NOREF(aSymlink, aExists);
5377 ReturnComNotImplemented();
5378}
5379
5380HRESULT GuestSession::symlinkRead(const com::Utf8Str &aSymlink, const std::vector<SymlinkReadFlag_T> &aFlags,
5381 com::Utf8Str &aTarget)
5382{
5383 RT_NOREF(aSymlink, aFlags, aTarget);
5384 ReturnComNotImplemented();
5385}
5386
5387/* Deprecated; use GuestSession::waitForArray() instead. */
5388HRESULT GuestSession::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
5389{
5390 /* Note: No call to i_isStartedExternal() needed here, as the session might not has been started (yet). */
5391
5392 LogFlowThisFuncEnter();
5393
5394 HRESULT hrc = S_OK;
5395
5396 /*
5397 * Note: Do not hold any locks here while waiting!
5398 */
5399 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS; GuestSessionWaitResult_T waitResult;
5400 int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &vrcGuest);
5401 if (RT_SUCCESS(vrc))
5402 *aReason = waitResult;
5403 else
5404 {
5405 switch (vrc)
5406 {
5407 case VERR_GSTCTL_GUEST_ERROR:
5408 {
5409 GuestErrorInfo ge(GuestErrorInfo::Type_Session, vrcGuest, mData.mSession.mName.c_str());
5410 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrcGuest, tr("Waiting for guest process failed: %s"),
5411 GuestBase::getErrorAsString(ge).c_str());
5412 break;
5413 }
5414 case VERR_TIMEOUT:
5415 *aReason = GuestSessionWaitResult_Timeout;
5416 break;
5417
5418 default:
5419 {
5420 const char *pszSessionName = mData.mSession.mName.c_str();
5421 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5422 tr("Waiting for guest session \"%s\" failed: %Rrc"),
5423 pszSessionName ? pszSessionName : tr("Unnamed"), vrc);
5424 break;
5425 }
5426 }
5427 }
5428
5429 LogFlowFuncLeaveRC(vrc);
5430 return hrc;
5431}
5432
5433HRESULT GuestSession::waitForArray(const std::vector<GuestSessionWaitForFlag_T> &aWaitFor, ULONG aTimeoutMS,
5434 GuestSessionWaitResult_T *aReason)
5435{
5436 /* Note: No call to i_isStartedExternal() needed here, as the session might not has been started (yet). */
5437
5438 LogFlowThisFuncEnter();
5439
5440 /*
5441 * Note: Do not hold any locks here while waiting!
5442 */
5443 uint32_t fWaitFor = GuestSessionWaitForFlag_None;
5444 for (size_t i = 0; i < aWaitFor.size(); i++)
5445 fWaitFor |= aWaitFor[i];
5446
5447 return WaitFor(fWaitFor, aTimeoutMS, aReason);
5448}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use