VirtualBox

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

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

Guest Control: Relaxed checks in IGuestSession::close() a bit, to make testcases pass again. Addendum to r160769.

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

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