VirtualBox

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

Last change on this file since 102614 was 102614, checked in by vboxsync, 12 months ago

Guest Control: Keep a COM object reference to the guest and console objects, so that we can easier access those when needed. Also added a IGuestSession::i_isReady() function that checks the current VM state. This should help wrt closing a guest sessions when the VM already is in a transient state, for instance while a guest session is left open and a VM snapshot is being taken. Should fix the guest session re-connection issues after a VM shutdown + saved state has been taken with FE/Qt's file manager.

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

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