VirtualBox

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

Last change on this file was 104178, checked in by vboxsync, 5 weeks ago

Guest Control:

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

© 2023 Oracle
ContactPrivacy policyTerms of Use