VirtualBox

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

Last change on this file since 80873 was 80873, checked in by vboxsync, 5 years ago

Main/GuestSessionImpl.cpp: Keep returning E_INVALIDARG for VERR_ENV_INVALID_VAR_NAME. No need for autocaller in API methods as the wrapper already worked them. More error details.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 139.5 KB
Line 
1/* $Id: GuestSessionImpl.cpp 80873 2019-09-17 23:55:26Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Guest session handling.
4 */
5
6/*
7 * Copyright (C) 2012-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_MAIN_GUESTSESSION
23#include "LoggingNew.h"
24
25#include "GuestImpl.h"
26#ifndef VBOX_WITH_GUEST_CONTROL
27# error "VBOX_WITH_GUEST_CONTROL must defined in this file"
28#endif
29#include "GuestSessionImpl.h"
30#include "GuestSessionImplTasks.h"
31#include "GuestCtrlImplPrivate.h"
32#include "VirtualBoxErrorInfoImpl.h"
33
34#include "Global.h"
35#include "AutoCaller.h"
36#include "ProgressImpl.h"
37#include "VBoxEvents.h"
38#include "VMMDev.h"
39#include "ThreadTask.h"
40
41#include <memory> /* For auto_ptr. */
42
43#include <iprt/cpp/utils.h> /* For unconst(). */
44#include <iprt/ctype.h>
45#include <iprt/env.h>
46#include <iprt/file.h> /* For CopyTo/From. */
47#include <iprt/path.h>
48#include <iprt/rand.h>
49
50#include <VBox/com/array.h>
51#include <VBox/com/listeners.h>
52#include <VBox/version.h>
53
54
55/**
56 * Base class representing an internal
57 * asynchronous session task.
58 */
59class GuestSessionTaskInternal : public ThreadTask
60{
61public:
62
63 GuestSessionTaskInternal(GuestSession *pSession)
64 : ThreadTask("GenericGuestSessionTaskInternal")
65 , mSession(pSession)
66 , mRC(VINF_SUCCESS) { }
67
68 virtual ~GuestSessionTaskInternal(void) { }
69
70 int rc(void) const { return mRC; }
71 bool isOk(void) const { return RT_SUCCESS(mRC); }
72 const ComObjPtr<GuestSession> &Session(void) const { return mSession; }
73
74protected:
75
76 const ComObjPtr<GuestSession> mSession;
77 int mRC;
78};
79
80/**
81 * Class for asynchronously starting a guest session.
82 */
83class GuestSessionTaskInternalStart : public GuestSessionTaskInternal
84{
85public:
86
87 GuestSessionTaskInternalStart(GuestSession *pSession)
88 : GuestSessionTaskInternal(pSession)
89 {
90 m_strTaskName = "gctlSesStart";
91 }
92
93 void handler()
94 {
95 /* Ignore rc */ GuestSession::i_startSessionThreadTask(this);
96 }
97};
98
99/**
100 * Internal listener class to serve events in an
101 * active manner, e.g. without polling delays.
102 */
103class GuestSessionListener
104{
105public:
106
107 GuestSessionListener(void)
108 {
109 }
110
111 virtual ~GuestSessionListener(void)
112 {
113 }
114
115 HRESULT init(GuestSession *pSession)
116 {
117 AssertPtrReturn(pSession, E_POINTER);
118 mSession = pSession;
119 return S_OK;
120 }
121
122 void uninit(void)
123 {
124 mSession = NULL;
125 }
126
127 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
128 {
129 switch (aType)
130 {
131 case VBoxEventType_OnGuestSessionStateChanged:
132 {
133 AssertPtrReturn(mSession, E_POINTER);
134 int rc2 = mSession->signalWaitEvent(aType, aEvent);
135 RT_NOREF(rc2);
136#ifdef DEBUG_andy
137 LogFlowFunc(("Signalling events of type=%RU32, session=%p resulted in rc=%Rrc\n",
138 aType, mSession, rc2));
139#endif
140 break;
141 }
142
143 default:
144 AssertMsgFailed(("Unhandled event %RU32\n", aType));
145 break;
146 }
147
148 return S_OK;
149 }
150
151private:
152
153 GuestSession *mSession;
154};
155typedef ListenerImpl<GuestSessionListener, GuestSession*> GuestSessionListenerImpl;
156
157VBOX_LISTENER_DECLARE(GuestSessionListenerImpl)
158
159// constructor / destructor
160/////////////////////////////////////////////////////////////////////////////
161
162DEFINE_EMPTY_CTOR_DTOR(GuestSession)
163
164HRESULT GuestSession::FinalConstruct(void)
165{
166 LogFlowThisFuncEnter();
167 return BaseFinalConstruct();
168}
169
170void GuestSession::FinalRelease(void)
171{
172 LogFlowThisFuncEnter();
173 uninit();
174 BaseFinalRelease();
175 LogFlowThisFuncLeave();
176}
177
178// public initializer/uninitializer for internal purposes only
179/////////////////////////////////////////////////////////////////////////////
180
181/**
182 * Initializes a guest session but does *not* open in on the guest side
183 * yet. This needs to be done via the openSession() / openSessionAsync calls.
184 *
185 * @return IPRT status code.
186 ** @todo Docs!
187 */
188int GuestSession::init(Guest *pGuest, const GuestSessionStartupInfo &ssInfo,
189 const GuestCredentials &guestCreds)
190{
191 LogFlowThisFunc(("pGuest=%p, ssInfo=%p, guestCreds=%p\n",
192 pGuest, &ssInfo, &guestCreds));
193
194 /* Enclose the state transition NotReady->InInit->Ready. */
195 AutoInitSpan autoInitSpan(this);
196 AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
197
198 AssertPtrReturn(pGuest, VERR_INVALID_POINTER);
199
200 /*
201 * Initialize our data members from the input.
202 */
203 mParent = pGuest;
204
205 /* Copy over startup info. */
206 /** @todo Use an overloaded copy operator. Later. */
207 mData.mSession.mID = ssInfo.mID;
208 mData.mSession.mIsInternal = ssInfo.mIsInternal;
209 mData.mSession.mName = ssInfo.mName;
210 mData.mSession.mOpenFlags = ssInfo.mOpenFlags;
211 mData.mSession.mOpenTimeoutMS = ssInfo.mOpenTimeoutMS;
212
213 /* Copy over session credentials. */
214 /** @todo Use an overloaded copy operator. Later. */
215 mData.mCredentials.mUser = guestCreds.mUser;
216 mData.mCredentials.mPassword = guestCreds.mPassword;
217 mData.mCredentials.mDomain = guestCreds.mDomain;
218
219 /* Initialize the remainder of the data. */
220 mData.mRC = VINF_SUCCESS;
221 mData.mStatus = GuestSessionStatus_Undefined;
222 mData.mpBaseEnvironment = NULL;
223
224 /*
225 * Register an object for the session itself to clearly
226 * distinguish callbacks which are for this session directly, or for
227 * objects (like files, directories, ...) which are bound to this session.
228 */
229 int rc = i_objectRegister(NULL /* pObject */, SESSIONOBJECTTYPE_SESSION, &mData.mObjectID);
230 if (RT_SUCCESS(rc))
231 {
232 rc = mData.mEnvironmentChanges.initChangeRecord(pGuest->i_isGuestInWindowsNtFamily()
233 ? RTENV_CREATE_F_ALLOW_EQUAL_FIRST_IN_VAR : 0);
234 if (RT_SUCCESS(rc))
235 {
236 rc = RTCritSectInit(&mWaitEventCritSect);
237 AssertRC(rc);
238 }
239 }
240
241 if (RT_SUCCESS(rc))
242 rc = i_determineProtocolVersion();
243
244 if (RT_SUCCESS(rc))
245 {
246 /*
247 * <Replace this if you figure out what the code is doing.>
248 */
249 HRESULT hr = unconst(mEventSource).createObject();
250 if (SUCCEEDED(hr))
251 hr = mEventSource->init();
252 if (SUCCEEDED(hr))
253 {
254 try
255 {
256 GuestSessionListener *pListener = new GuestSessionListener();
257 ComObjPtr<GuestSessionListenerImpl> thisListener;
258 hr = thisListener.createObject();
259 if (SUCCEEDED(hr))
260 hr = thisListener->init(pListener, this); /* thisListener takes ownership of pListener. */
261 if (SUCCEEDED(hr))
262 {
263 com::SafeArray <VBoxEventType_T> eventTypes;
264 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
265 hr = mEventSource->RegisterListener(thisListener,
266 ComSafeArrayAsInParam(eventTypes),
267 TRUE /* Active listener */);
268 if (SUCCEEDED(hr))
269 {
270 mLocalListener = thisListener;
271
272 /*
273 * Mark this object as operational and return success.
274 */
275 autoInitSpan.setSucceeded();
276 LogFlowThisFunc(("mName=%s mID=%RU32 mIsInternal=%RTbool rc=VINF_SUCCESS\n",
277 mData.mSession.mName.c_str(), mData.mSession.mID, mData.mSession.mIsInternal));
278 return VINF_SUCCESS;
279 }
280 }
281 }
282 catch (std::bad_alloc &)
283 {
284 hr = E_OUTOFMEMORY;
285 }
286 }
287 rc = Global::vboxStatusCodeFromCOM(hr);
288 }
289
290 autoInitSpan.setFailed();
291 LogThisFunc(("Failed! mName=%s mID=%RU32 mIsInternal=%RTbool => rc=%Rrc\n",
292 mData.mSession.mName.c_str(), mData.mSession.mID, mData.mSession.mIsInternal, rc));
293 return rc;
294}
295
296/**
297 * Uninitializes the instance.
298 * Called from FinalRelease().
299 */
300void GuestSession::uninit(void)
301{
302 /* Enclose the state transition Ready->InUninit->NotReady. */
303 AutoUninitSpan autoUninitSpan(this);
304 if (autoUninitSpan.uninitDone())
305 return;
306
307 LogFlowThisFuncEnter();
308
309 /* Call i_onRemove to take care of the object cleanups. */
310 i_onRemove();
311
312 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
313
314 /* Unregister the session's object ID. */
315 i_objectUnregister(mData.mObjectID);
316
317 Assert(mData.mObjects.size () == 0);
318 mData.mObjects.clear();
319
320 mData.mEnvironmentChanges.reset();
321
322 if (mData.mpBaseEnvironment)
323 {
324 mData.mpBaseEnvironment->releaseConst();
325 mData.mpBaseEnvironment = NULL;
326 }
327
328 /* Unitialize our local listener. */
329 mLocalListener.setNull();
330
331 baseUninit();
332
333 LogFlowFuncLeave();
334}
335
336// implementation of public getters/setters for attributes
337/////////////////////////////////////////////////////////////////////////////
338
339HRESULT GuestSession::getUser(com::Utf8Str &aUser)
340{
341 LogFlowThisFuncEnter();
342
343 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
344
345 aUser = mData.mCredentials.mUser;
346
347 LogFlowThisFuncLeave();
348 return S_OK;
349}
350
351HRESULT GuestSession::getDomain(com::Utf8Str &aDomain)
352{
353 LogFlowThisFuncEnter();
354
355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
356
357 aDomain = mData.mCredentials.mDomain;
358
359 LogFlowThisFuncLeave();
360 return S_OK;
361}
362
363HRESULT GuestSession::getName(com::Utf8Str &aName)
364{
365 LogFlowThisFuncEnter();
366
367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
368
369 aName = mData.mSession.mName;
370
371 LogFlowThisFuncLeave();
372 return S_OK;
373}
374
375HRESULT GuestSession::getId(ULONG *aId)
376{
377 LogFlowThisFuncEnter();
378
379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
380
381 *aId = mData.mSession.mID;
382
383 LogFlowThisFuncLeave();
384 return S_OK;
385}
386
387HRESULT GuestSession::getStatus(GuestSessionStatus_T *aStatus)
388{
389 LogFlowThisFuncEnter();
390
391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
392
393 *aStatus = mData.mStatus;
394
395 LogFlowThisFuncLeave();
396 return S_OK;
397}
398
399HRESULT GuestSession::getTimeout(ULONG *aTimeout)
400{
401 LogFlowThisFuncEnter();
402
403 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
404
405 *aTimeout = mData.mTimeout;
406
407 LogFlowThisFuncLeave();
408 return S_OK;
409}
410
411HRESULT GuestSession::setTimeout(ULONG aTimeout)
412{
413 LogFlowThisFuncEnter();
414
415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
416
417 mData.mTimeout = aTimeout;
418
419 LogFlowThisFuncLeave();
420 return S_OK;
421}
422
423HRESULT GuestSession::getProtocolVersion(ULONG *aProtocolVersion)
424{
425 LogFlowThisFuncEnter();
426
427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
428
429 *aProtocolVersion = mData.mProtocolVersion;
430
431 LogFlowThisFuncLeave();
432 return S_OK;
433}
434
435HRESULT GuestSession::getEnvironmentChanges(std::vector<com::Utf8Str> &aEnvironmentChanges)
436{
437 LogFlowThisFuncEnter();
438
439 int vrc;
440 {
441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
442 vrc = mData.mEnvironmentChanges.queryPutEnvArray(&aEnvironmentChanges);
443 }
444
445 LogFlowFuncLeaveRC(vrc);
446 return Global::vboxStatusCodeToCOM(vrc);
447}
448
449HRESULT GuestSession::setEnvironmentChanges(const std::vector<com::Utf8Str> &aEnvironmentChanges)
450{
451 LogFlowThisFuncEnter();
452
453 int vrc;
454 size_t idxError = ~(size_t)0;
455 {
456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
457 mData.mEnvironmentChanges.reset();
458 vrc = mData.mEnvironmentChanges.applyPutEnvArray(aEnvironmentChanges, &idxError);
459 }
460
461 LogFlowFuncLeaveRC(vrc);
462 if (RT_SUCCESS(vrc))
463 return S_OK;
464 if (vrc == VERR_ENV_INVALID_VAR_NAME)
465 return setError(E_INVALIDARG, tr("Invalid environment variable name '%s', index %zu"),
466 aEnvironmentChanges[idxError].c_str(), idxError);
467 return setErrorBoth(Global::vboxStatusCodeToCOM(vrc), vrc, tr("Failed to apply '%s', index %zu (%Rrc)"),
468 aEnvironmentChanges[idxError].c_str(), idxError, vrc);
469}
470
471HRESULT GuestSession::getEnvironmentBase(std::vector<com::Utf8Str> &aEnvironmentBase)
472{
473 LogFlowThisFuncEnter();
474
475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
476 HRESULT hrc;
477 if (mData.mpBaseEnvironment)
478 {
479 int vrc = mData.mpBaseEnvironment->queryPutEnvArray(&aEnvironmentBase);
480 hrc = Global::vboxStatusCodeToCOM(vrc);
481 }
482 else if (mData.mProtocolVersion < 99999)
483 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
484 else
485 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
486
487 LogFlowFuncLeave();
488 return hrc;
489}
490
491HRESULT GuestSession::getProcesses(std::vector<ComPtr<IGuestProcess> > &aProcesses)
492{
493 LogFlowThisFuncEnter();
494
495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
496
497 aProcesses.resize(mData.mProcesses.size());
498 size_t i = 0;
499 for (SessionProcesses::iterator it = mData.mProcesses.begin();
500 it != mData.mProcesses.end();
501 ++it, ++i)
502 {
503 it->second.queryInterfaceTo(aProcesses[i].asOutParam());
504 }
505
506 LogFlowFunc(("mProcesses=%zu\n", aProcesses.size()));
507 return S_OK;
508}
509
510HRESULT GuestSession::getPathStyle(PathStyle_T *aPathStyle)
511{
512 *aPathStyle = i_getPathStyle();
513 return S_OK;
514}
515
516HRESULT GuestSession::getCurrentDirectory(com::Utf8Str &aCurrentDirectory)
517{
518 RT_NOREF(aCurrentDirectory);
519 ReturnComNotImplemented();
520}
521
522HRESULT GuestSession::setCurrentDirectory(const com::Utf8Str &aCurrentDirectory)
523{
524 RT_NOREF(aCurrentDirectory);
525 ReturnComNotImplemented();
526}
527
528HRESULT GuestSession::getUserHome(com::Utf8Str &aUserHome)
529{
530 HRESULT hr = i_isStartedExternal();
531 if (FAILED(hr))
532 return hr;
533
534 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
535 int vrc = i_pathUserHome(aUserHome, &rcGuest);
536 if (RT_FAILURE(vrc))
537 {
538 switch (vrc)
539 {
540 case VERR_GSTCTL_GUEST_ERROR:
541 {
542 switch (rcGuest)
543 {
544 case VERR_NOT_SUPPORTED:
545 hr = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest,
546 tr("Getting the user's home path is not supported by installed Guest Additions"));
547 break;
548
549 default:
550 hr = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest,
551 tr("Getting the user's home path failed on the guest: %Rrc"), rcGuest);
552 break;
553 }
554 break;
555 }
556
557 default:
558 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Getting the user's home path failed: %Rrc"), vrc);
559 break;
560 }
561 }
562
563 return hr;
564}
565
566HRESULT GuestSession::getUserDocuments(com::Utf8Str &aUserDocuments)
567{
568 HRESULT hr = i_isStartedExternal();
569 if (FAILED(hr))
570 return hr;
571
572 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
573 int vrc = i_pathUserDocuments(aUserDocuments, &rcGuest);
574 if (RT_FAILURE(vrc))
575 {
576 switch (vrc)
577 {
578 case VERR_GSTCTL_GUEST_ERROR:
579 {
580 switch (rcGuest)
581 {
582 case VERR_NOT_SUPPORTED:
583 hr = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest,
584 tr("Getting the user's documents path is not supported by installed Guest Additions"));
585 break;
586
587 default:
588 hr = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest,
589 tr("Getting the user's documents path failed on the guest: %Rrc"), rcGuest);
590 break;
591 }
592 break;
593 }
594
595 default:
596 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Getting the user's documents path failed: %Rrc"), vrc);
597 break;
598 }
599 }
600
601 return hr;
602}
603
604HRESULT GuestSession::getDirectories(std::vector<ComPtr<IGuestDirectory> > &aDirectories)
605{
606 LogFlowThisFuncEnter();
607
608 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
609
610 aDirectories.resize(mData.mDirectories.size());
611 size_t i = 0;
612 for (SessionDirectories::iterator it = mData.mDirectories.begin(); it != mData.mDirectories.end(); ++it, ++i)
613 {
614 it->second.queryInterfaceTo(aDirectories[i].asOutParam());
615 }
616
617 LogFlowFunc(("mDirectories=%zu\n", aDirectories.size()));
618 return S_OK;
619}
620
621HRESULT GuestSession::getFiles(std::vector<ComPtr<IGuestFile> > &aFiles)
622{
623 LogFlowThisFuncEnter();
624
625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
626
627 aFiles.resize(mData.mFiles.size());
628 size_t i = 0;
629 for(SessionFiles::iterator it = mData.mFiles.begin(); it != mData.mFiles.end(); ++it, ++i)
630 it->second.queryInterfaceTo(aFiles[i].asOutParam());
631
632 LogFlowFunc(("mDirectories=%zu\n", aFiles.size()));
633
634 return S_OK;
635}
636
637HRESULT GuestSession::getEventSource(ComPtr<IEventSource> &aEventSource)
638{
639 LogFlowThisFuncEnter();
640
641 // no need to lock - lifetime constant
642 mEventSource.queryInterfaceTo(aEventSource.asOutParam());
643
644 LogFlowThisFuncLeave();
645 return S_OK;
646}
647
648// private methods
649///////////////////////////////////////////////////////////////////////////////
650
651int GuestSession::i_closeSession(uint32_t uFlags, uint32_t uTimeoutMS, int *prcGuest)
652{
653 AssertPtrReturn(prcGuest, VERR_INVALID_POINTER);
654
655 LogFlowThisFunc(("uFlags=%x, uTimeoutMS=%RU32\n", uFlags, uTimeoutMS));
656
657 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
658
659 /* Guest Additions < 4.3 don't support closing dedicated
660 guest sessions, skip. */
661 if (mData.mProtocolVersion < 2)
662 {
663 LogFlowThisFunc(("Installed Guest Additions don't support closing dedicated sessions, skipping\n"));
664 return VINF_SUCCESS;
665 }
666
667 /** @todo uFlags validation. */
668
669 if (mData.mStatus != GuestSessionStatus_Started)
670 {
671 LogFlowThisFunc(("Session ID=%RU32 not started (anymore), status now is: %RU32\n",
672 mData.mSession.mID, mData.mStatus));
673 return VINF_SUCCESS;
674 }
675
676 int vrc;
677
678 GuestWaitEvent *pEvent = NULL;
679 GuestEventTypes eventTypes;
680 try
681 {
682 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
683
684 vrc = registerWaitEventEx(mData.mSession.mID, mData.mObjectID, eventTypes, &pEvent);
685 }
686 catch (std::bad_alloc &)
687 {
688 vrc = VERR_NO_MEMORY;
689 }
690
691 if (RT_FAILURE(vrc))
692 return vrc;
693
694 LogFlowThisFunc(("Sending closing request to guest session ID=%RU32, uFlags=%x\n",
695 mData.mSession.mID, uFlags));
696
697 VBOXHGCMSVCPARM paParms[4];
698 int i = 0;
699 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
700 HGCMSvcSetU32(&paParms[i++], uFlags);
701
702 alock.release(); /* Drop the write lock before waiting. */
703
704 vrc = i_sendMessage(HOST_MSG_SESSION_CLOSE, i, paParms, VBOX_GUESTCTRL_DST_BOTH);
705 if (RT_SUCCESS(vrc))
706 vrc = i_waitForStatusChange(pEvent, GuestSessionWaitForFlag_Terminate, uTimeoutMS,
707 NULL /* Session status */, prcGuest);
708
709 unregisterWaitEvent(pEvent);
710
711 LogFlowFuncLeaveRC(vrc);
712 return vrc;
713}
714
715/**
716 * Internal worker function for public APIs that handle copying elements from
717 * guest to the host.
718 *
719 * @return HRESULT
720 * @param SourceSet Source set specifying what to copy.
721 * @param strDestination Destination path on the host. Host path style.
722 * @param pProgress Progress object returned to the caller.
723 */
724HRESULT GuestSession::i_copyFromGuest(const GuestSessionFsSourceSet &SourceSet,
725 const com::Utf8Str &strDestination, ComPtr<IProgress> &pProgress)
726{
727 HRESULT hrc = i_isStartedExternal();
728 if (FAILED(hrc))
729 return hrc;
730
731 LogFlowThisFuncEnter();
732
733 /* Validate stuff. */
734 if (RT_UNLIKELY(SourceSet.size() == 0 || *(SourceSet[0].strSource.c_str()) == '\0')) /* At least one source must be present. */
735 return setError(E_INVALIDARG, tr("No source(s) specified"));
736 if (RT_UNLIKELY((strDestination.c_str()) == NULL || *(strDestination.c_str()) == '\0'))
737 return setError(E_INVALIDARG, tr("No destination specified"));
738
739 /* Create a task and return the progress obejct for it. */
740 GuestSessionTaskCopyFrom *pTask = NULL;
741 try
742 {
743 pTask = new GuestSessionTaskCopyFrom(this /* GuestSession */, SourceSet, strDestination);
744 }
745 catch (std::bad_alloc &)
746 {
747 return setError(E_OUTOFMEMORY, tr("Failed to create GuestSessionTaskCopyFrom object"));
748 }
749
750 try
751 {
752 hrc = pTask->Init(Utf8StrFmt(tr("Copying to \"%s\" on the host"), strDestination.c_str()));
753 }
754 catch (std::bad_alloc &)
755 {
756 hrc = E_OUTOFMEMORY;
757 }
758 if (SUCCEEDED(hrc))
759 {
760 ComObjPtr<Progress> ptrProgressObj = pTask->GetProgressObject();
761
762 /* Kick off the worker thread. Note! Consumes pTask. */
763 hrc = pTask->createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
764 pTask = NULL;
765 if (SUCCEEDED(hrc))
766 hrc = ptrProgressObj.queryInterfaceTo(pProgress.asOutParam());
767 else
768 hrc = setError(hrc, tr("Starting thread for copying from guest to the host failed"));
769 }
770 else
771 {
772 hrc = setError(hrc, tr("Initializing GuestSessionTaskCopyFrom object failed"));
773 delete pTask;
774 }
775
776 LogFlowFunc(("Returning %Rhrc\n", hrc));
777 return hrc;
778}
779
780/**
781 * Internal worker function for public APIs that handle copying elements from
782 * host to the guest.
783 *
784 * @return HRESULT
785 * @param SourceSet Source set specifying what to copy.
786 * @param strDestination Destination path on the guest. Guest path style.
787 * @param pProgress Progress object returned to the caller.
788 */
789HRESULT GuestSession::i_copyToGuest(const GuestSessionFsSourceSet &SourceSet,
790 const com::Utf8Str &strDestination, ComPtr<IProgress> &pProgress)
791{
792 HRESULT hrc = i_isStartedExternal();
793 if (FAILED(hrc))
794 return hrc;
795
796 LogFlowThisFuncEnter();
797
798 /* Validate stuff. */
799/** @todo r=bird: these validations are better left to the caller. The first one in particular as there is only one
800 * of the four callers which supplies a user specified source set, making an assertion more appropriate and efficient
801 * here. */
802 if (RT_UNLIKELY(SourceSet.size() == 0)) /* At least one source must be present. */
803 return setError(E_INVALIDARG, tr("No sources specified"));
804 if (RT_UNLIKELY(SourceSet[0].strSource.isEmpty()))
805 return setError(E_INVALIDARG, tr("First source entry is empty"));
806 if (RT_UNLIKELY(strDestination.isEmpty()))
807 return setError(E_INVALIDARG, tr("No destination specified"));
808
809 /* Create a task and return the progress obejct for it. */
810 GuestSessionTaskCopyTo *pTask = NULL;
811 try
812 {
813 pTask = new GuestSessionTaskCopyTo(this /* GuestSession */, SourceSet, strDestination);
814 }
815 catch (std::bad_alloc &)
816 {
817 return setError(E_OUTOFMEMORY, tr("Failed to create GuestSessionTaskCopyTo object"));
818 }
819
820 try
821 {
822 hrc = pTask->Init(Utf8StrFmt(tr("Copying to \"%s\" on the guest"), strDestination.c_str()));
823 }
824 catch (std::bad_alloc &)
825 {
826 hrc = E_OUTOFMEMORY;
827 }
828 if (SUCCEEDED(hrc))
829 {
830 ComObjPtr<Progress> ptrProgressObj = pTask->GetProgressObject();
831
832 /* Kick off the worker thread. Note! Consumes pTask. */
833 hrc = pTask->createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
834 pTask = NULL;
835 if (SUCCEEDED(hrc))
836 hrc = ptrProgressObj.queryInterfaceTo(pProgress.asOutParam());
837 else
838 hrc = setError(hrc, tr("Starting thread for copying from host to the guest failed"));
839 }
840 else
841 {
842 hrc = setError(hrc, tr("Initializing GuestSessionTaskCopyTo object failed"));
843 delete pTask;
844 }
845
846 LogFlowFunc(("Returning %Rhrc\n", hrc));
847 return hrc;
848}
849
850/**
851 * Validates and extracts directory copy flags from a comma-separated string.
852 *
853 * @return COM status, error set on failure
854 * @param strFlags String to extract flags from.
855 * @param pfFlags Where to store the extracted (and validated) flags.
856 */
857HRESULT GuestSession::i_directoryCopyFlagFromStr(const com::Utf8Str &strFlags, DirectoryCopyFlag_T *pfFlags)
858{
859 unsigned fFlags = DirectoryCopyFlag_None;
860
861 /* Validate and set flags. */
862 if (strFlags.isNotEmpty())
863 {
864 const char *pszNext = strFlags.c_str();
865 for (;;)
866 {
867 /* Find the next keyword, ignoring all whitespace. */
868 pszNext = RTStrStripL(pszNext);
869
870 const char * const pszComma = strchr(pszNext, ',');
871 size_t cchKeyword = pszComma ? pszComma - pszNext : strlen(pszNext);
872 while (cchKeyword > 0 && RT_C_IS_SPACE(pszNext[cchKeyword - 1]))
873 cchKeyword--;
874
875 if (cchKeyword > 0)
876 {
877 /* Convert keyword to flag. */
878#define MATCH_KEYWORD(a_szKeyword) ( cchKeyword == sizeof(a_szKeyword) - 1U \
879 && memcmp(pszNext, a_szKeyword, sizeof(a_szKeyword) - 1U) == 0)
880 if (MATCH_KEYWORD("CopyIntoExisting"))
881 fFlags |= (unsigned)DirectoryCopyFlag_CopyIntoExisting;
882 else
883 return setError(E_INVALIDARG, tr("Invalid directory copy flag: %.*s"), (int)cchKeyword, pszNext);
884#undef MATCH_KEYWORD
885 }
886 if (!pszComma)
887 break;
888 pszNext = pszComma + 1;
889 }
890 }
891
892 if (pfFlags)
893 *pfFlags = (DirectoryCopyFlag_T)fFlags;
894 return S_OK;
895}
896
897int GuestSession::i_directoryCreate(const Utf8Str &strPath, uint32_t uMode,
898 uint32_t uFlags, int *prcGuest)
899{
900 AssertPtrReturn(prcGuest, VERR_INVALID_POINTER);
901
902 LogFlowThisFunc(("strPath=%s, uMode=%x, uFlags=%x\n", strPath.c_str(), uMode, uFlags));
903
904 int vrc = VINF_SUCCESS;
905
906 GuestProcessStartupInfo procInfo;
907 procInfo.mFlags = ProcessCreateFlag_Hidden;
908 procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_MKDIR);
909
910 try
911 {
912 procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */
913
914 /* Construct arguments. */
915 if (uFlags)
916 {
917 if (uFlags & DirectoryCreateFlag_Parents)
918 procInfo.mArguments.push_back(Utf8Str("--parents")); /* We also want to create the parent directories. */
919 else
920 vrc = VERR_INVALID_PARAMETER;
921 }
922
923 if ( RT_SUCCESS(vrc)
924 && uMode)
925 {
926 procInfo.mArguments.push_back(Utf8Str("--mode")); /* Set the creation mode. */
927
928 char szMode[16];
929 if (RTStrPrintf(szMode, sizeof(szMode), "%o", uMode))
930 {
931 procInfo.mArguments.push_back(Utf8Str(szMode));
932 }
933 else
934 vrc = VERR_BUFFER_OVERFLOW;
935 }
936
937 procInfo.mArguments.push_back("--"); /* '--version' is a valid directory name. */
938 procInfo.mArguments.push_back(strPath); /* The directory we want to create. */
939 }
940 catch (std::bad_alloc &)
941 {
942 vrc = VERR_NO_MEMORY;
943 }
944
945 if (RT_SUCCESS(vrc))
946 vrc = GuestProcessTool::run(this, procInfo, prcGuest);
947
948 LogFlowFuncLeaveRC(vrc);
949 return vrc;
950}
951
952inline bool GuestSession::i_directoryExists(uint32_t uDirID, ComObjPtr<GuestDirectory> *pDir)
953{
954 SessionDirectories::const_iterator it = mData.mDirectories.find(uDirID);
955 if (it != mData.mDirectories.end())
956 {
957 if (pDir)
958 *pDir = it->second;
959 return true;
960 }
961 return false;
962}
963
964int GuestSession::i_directoryQueryInfo(const Utf8Str &strPath, bool fFollowSymlinks,
965 GuestFsObjData &objData, int *prcGuest)
966{
967 AssertPtrReturn(prcGuest, VERR_INVALID_POINTER);
968
969 LogFlowThisFunc(("strPath=%s, fFollowSymlinks=%RTbool\n", strPath.c_str(), fFollowSymlinks));
970
971 int vrc = i_fsQueryInfo(strPath, fFollowSymlinks, objData, prcGuest);
972 if (RT_SUCCESS(vrc))
973 {
974 vrc = objData.mType == FsObjType_Directory
975 ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
976 }
977
978 LogFlowFuncLeaveRC(vrc);
979 return vrc;
980}
981
982/**
983 * Unregisters a directory object from a session.
984 *
985 * @return VBox status code. VERR_NOT_FOUND if the directory is not registered (anymore).
986 * @param pDirectory Directory object to unregister from session.
987 */
988int GuestSession::i_directoryUnregister(GuestDirectory *pDirectory)
989{
990 AssertPtrReturn(pDirectory, VERR_INVALID_POINTER);
991
992 LogFlowThisFunc(("pDirectory=%p\n", pDirectory));
993
994 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
995
996 const uint32_t idObject = pDirectory->getObjectID();
997
998 LogFlowFunc(("Removing directory (objectID=%RU32) ...\n", idObject));
999
1000 int rc = i_objectUnregister(idObject);
1001 if (RT_FAILURE(rc))
1002 return rc;
1003
1004 SessionDirectories::iterator itDirs = mData.mDirectories.find(idObject);
1005 AssertReturn(itDirs != mData.mDirectories.end(), VERR_NOT_FOUND);
1006
1007 /* Make sure to consume the pointer before the one of the iterator gets released. */
1008 ComObjPtr<GuestDirectory> pDirConsumed = pDirectory;
1009
1010 LogFlowFunc(("Removing directory ID=%RU32 (session %RU32, now total %zu directories)\n",
1011 idObject, mData.mSession.mID, mData.mDirectories.size()));
1012
1013 rc = pDirConsumed->i_onUnregister();
1014 AssertRCReturn(rc, rc);
1015
1016 mData.mDirectories.erase(itDirs);
1017
1018 alock.release(); /* Release lock before firing off event. */
1019
1020// fireGuestDirectoryRegisteredEvent(mEventSource, this /* Session */, pDirConsumed, false /* Process unregistered */);
1021
1022 pDirConsumed.setNull();
1023
1024 LogFlowFuncLeaveRC(rc);
1025 return rc;
1026}
1027
1028int GuestSession::i_directoryRemove(const Utf8Str &strPath, uint32_t fFlags, int *prcGuest)
1029{
1030 AssertReturn(!(fFlags & ~DIRREMOVEREC_FLAG_VALID_MASK), VERR_INVALID_PARAMETER);
1031 AssertPtrReturn(prcGuest, VERR_INVALID_POINTER);
1032
1033 LogFlowThisFunc(("strPath=%s, uFlags=0x%x\n", strPath.c_str(), fFlags));
1034
1035 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1036
1037 GuestWaitEvent *pEvent = NULL;
1038 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
1039 if (RT_FAILURE(vrc))
1040 return vrc;
1041
1042 /* Prepare HGCM call. */
1043 VBOXHGCMSVCPARM paParms[8];
1044 int i = 0;
1045 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
1046 HGCMSvcSetPv(&paParms[i++], (void*)strPath.c_str(),
1047 (ULONG)strPath.length() + 1);
1048 HGCMSvcSetU32(&paParms[i++], fFlags);
1049
1050 alock.release(); /* Drop write lock before sending. */
1051
1052 vrc = i_sendMessage(HOST_MSG_DIR_REMOVE, i, paParms);
1053 if (RT_SUCCESS(vrc))
1054 {
1055 vrc = pEvent->Wait(30 * 1000);
1056 if ( vrc == VERR_GSTCTL_GUEST_ERROR
1057 && prcGuest)
1058 *prcGuest = pEvent->GuestResult();
1059 }
1060
1061 unregisterWaitEvent(pEvent);
1062
1063 LogFlowFuncLeaveRC(vrc);
1064 return vrc;
1065}
1066
1067int GuestSession::i_fsCreateTemp(const Utf8Str &strTemplate, const Utf8Str &strPath, bool fDirectory, Utf8Str &strName,
1068 int *prcGuest)
1069{
1070 AssertPtrReturn(prcGuest, VERR_INVALID_POINTER);
1071
1072 LogFlowThisFunc(("strTemplate=%s, strPath=%s, fDirectory=%RTbool\n",
1073 strTemplate.c_str(), strPath.c_str(), fDirectory));
1074
1075 GuestProcessStartupInfo procInfo;
1076 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
1077 try
1078 {
1079 procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_MKTEMP);
1080 procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */
1081 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
1082 if (fDirectory)
1083 procInfo.mArguments.push_back(Utf8Str("-d"));
1084 if (strPath.length()) /* Otherwise use /tmp or equivalent. */
1085 {
1086 procInfo.mArguments.push_back(Utf8Str("-t"));
1087 procInfo.mArguments.push_back(strPath);
1088 }
1089 procInfo.mArguments.push_back("--"); /* strTemplate could be '--help'. */
1090 procInfo.mArguments.push_back(strTemplate);
1091 }
1092 catch (std::bad_alloc &)
1093 {
1094 Log(("Out of memory!\n"));
1095 return VERR_NO_MEMORY;
1096 }
1097
1098 /** @todo Use an internal HGCM command for this operation, since
1099 * we now can run in a user-dedicated session. */
1100 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1101 GuestCtrlStreamObjects stdOut;
1102 int vrc = GuestProcessTool::runEx(this, procInfo, &stdOut, 1 /* cStrmOutObjects */, &vrcGuest);
1103 if (!GuestProcess::i_isGuestError(vrc))
1104 {
1105 GuestFsObjData objData;
1106 if (!stdOut.empty())
1107 {
1108 vrc = objData.FromMkTemp(stdOut.at(0));
1109 if (RT_FAILURE(vrc))
1110 {
1111 vrcGuest = vrc;
1112 if (prcGuest)
1113 *prcGuest = vrc;
1114 vrc = VERR_GSTCTL_GUEST_ERROR;
1115 }
1116 }
1117 else
1118 vrc = VERR_BROKEN_PIPE;
1119
1120 if (RT_SUCCESS(vrc))
1121 strName = objData.mName;
1122 }
1123 else if (prcGuest)
1124 *prcGuest = vrcGuest;
1125
1126 LogFlowThisFunc(("Returning vrc=%Rrc, vrcGuest=%Rrc\n", vrc, vrcGuest));
1127 return vrc;
1128}
1129
1130int GuestSession::i_directoryOpen(const GuestDirectoryOpenInfo &openInfo,
1131 ComObjPtr<GuestDirectory> &pDirectory, int *prcGuest)
1132{
1133 AssertPtrReturn(prcGuest, VERR_INVALID_POINTER);
1134
1135 LogFlowThisFunc(("strPath=%s, strPath=%s, uFlags=%x\n",
1136 openInfo.mPath.c_str(), openInfo.mFilter.c_str(), openInfo.mFlags));
1137
1138 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1139
1140 /* Create the directory object. */
1141 HRESULT hr = pDirectory.createObject();
1142 if (FAILED(hr))
1143 return Global::vboxStatusCodeFromCOM(hr);
1144
1145 /* Register a new object ID. */
1146 uint32_t idObject;
1147 int vrc = i_objectRegister(pDirectory, SESSIONOBJECTTYPE_DIRECTORY, &idObject);
1148 if (RT_FAILURE(vrc))
1149 {
1150 pDirectory.setNull();
1151 return vrc;
1152 }
1153
1154 Console *pConsole = mParent->i_getConsole();
1155 AssertPtr(pConsole);
1156
1157 vrc = pDirectory->init(pConsole, this /* Parent */, idObject, openInfo);
1158 if (RT_FAILURE(vrc))
1159 return vrc;
1160
1161 /*
1162 * Since this is a synchronous guest call we have to
1163 * register the file object first, releasing the session's
1164 * lock and then proceed with the actual opening command
1165 * -- otherwise the file's opening callback would hang
1166 * because the session's lock still is in place.
1167 */
1168 try
1169 {
1170 /* Add the created directory to our map. */
1171 mData.mDirectories[idObject] = pDirectory;
1172
1173 LogFlowFunc(("Added new guest directory \"%s\" (Session: %RU32) (now total %zu directories)\n",
1174 openInfo.mPath.c_str(), mData.mSession.mID, mData.mDirectories.size()));
1175
1176 alock.release(); /* Release lock before firing off event. */
1177
1178 /** @todo Fire off a VBoxEventType_OnGuestDirectoryRegistered event? */
1179 }
1180 catch (std::bad_alloc &)
1181 {
1182 vrc = VERR_NO_MEMORY;
1183 }
1184
1185 if (RT_SUCCESS(vrc))
1186 {
1187 /* Nothing further to do here yet. */
1188 if (prcGuest)
1189 *prcGuest = VINF_SUCCESS;
1190 }
1191
1192 LogFlowFuncLeaveRC(vrc);
1193 return vrc;
1194}
1195
1196/**
1197 * Dispatches a host callback to its corresponding object.
1198 *
1199 * @return VBox status code. VERR_NOT_FOUND if no corresponding object was found.
1200 * @param pCtxCb Host callback context.
1201 * @param pSvcCb Service callback data.
1202 */
1203int GuestSession::i_dispatchToObject(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
1204{
1205 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
1206
1207 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
1208 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
1209
1210 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1211
1212 /*
1213 * Find the object.
1214 */
1215 int rc = VERR_NOT_FOUND;
1216 const uint32_t idObject = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCtxCb->uContextID);
1217 SessionObjects::const_iterator itObjs = mData.mObjects.find(idObject);
1218 if (itObjs != mData.mObjects.end())
1219 {
1220 /* Set protocol version so that pSvcCb can be interpreted right. */
1221 pCtxCb->uProtocol = mData.mProtocolVersion;
1222
1223 switch (itObjs->second.enmType)
1224 {
1225 case SESSIONOBJECTTYPE_ANONYMOUS:
1226 rc = VERR_NOT_SUPPORTED;
1227 break;
1228
1229 case SESSIONOBJECTTYPE_SESSION:
1230 {
1231 alock.release();
1232
1233 rc = i_dispatchToThis(pCtxCb, pSvcCb);
1234 break;
1235 }
1236 case SESSIONOBJECTTYPE_DIRECTORY:
1237 {
1238 SessionDirectories::const_iterator itDir = mData.mDirectories.find(idObject);
1239 if (itDir != mData.mDirectories.end())
1240 {
1241 ComObjPtr<GuestDirectory> pDirectory(itDir->second);
1242 Assert(!pDirectory.isNull());
1243
1244 alock.release();
1245
1246 rc = pDirectory->i_callbackDispatcher(pCtxCb, pSvcCb);
1247 }
1248 break;
1249 }
1250 case SESSIONOBJECTTYPE_FILE:
1251 {
1252 SessionFiles::const_iterator itFile = mData.mFiles.find(idObject);
1253 if (itFile != mData.mFiles.end())
1254 {
1255 ComObjPtr<GuestFile> pFile(itFile->second);
1256 Assert(!pFile.isNull());
1257
1258 alock.release();
1259
1260 rc = pFile->i_callbackDispatcher(pCtxCb, pSvcCb);
1261 }
1262 break;
1263 }
1264 case SESSIONOBJECTTYPE_PROCESS:
1265 {
1266 SessionProcesses::const_iterator itProc = mData.mProcesses.find(idObject);
1267 if (itProc != mData.mProcesses.end())
1268 {
1269 ComObjPtr<GuestProcess> pProcess(itProc->second);
1270 Assert(!pProcess.isNull());
1271
1272 alock.release();
1273
1274 rc = pProcess->i_callbackDispatcher(pCtxCb, pSvcCb);
1275 }
1276 break;
1277 }
1278 default:
1279 AssertMsgFailed(("%d\n", itObjs->second.enmType));
1280 rc = VERR_INTERNAL_ERROR_4;
1281 break;
1282 }
1283 }
1284
1285 LogFlowFuncLeaveRC(rc);
1286 return rc;
1287}
1288
1289int GuestSession::i_dispatchToThis(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
1290{
1291 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
1292 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
1293
1294 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1295
1296 LogFlowThisFunc(("sessionID=%RU32, CID=%RU32, uMessage=%RU32, pSvcCb=%p\n",
1297 mData.mSession.mID, pCbCtx->uContextID, pCbCtx->uMessage, pSvcCb));
1298 int rc;
1299 switch (pCbCtx->uMessage)
1300 {
1301 case GUEST_MSG_DISCONNECTED:
1302 /** @todo Handle closing all guest objects. */
1303 rc = VERR_INTERNAL_ERROR;
1304 break;
1305
1306 case GUEST_MSG_SESSION_NOTIFY: /* Guest Additions >= 4.3.0. */
1307 {
1308 rc = i_onSessionStatusChange(pCbCtx, pSvcCb);
1309 break;
1310 }
1311
1312 default:
1313 rc = dispatchGeneric(pCbCtx, pSvcCb);
1314 break;
1315 }
1316
1317 LogFlowFuncLeaveRC(rc);
1318 return rc;
1319}
1320
1321/**
1322 * Validates and extracts file copy flags from a comma-separated string.
1323 *
1324 * @return COM status, error set on failure
1325 * @param strFlags String to extract flags from.
1326 * @param pfFlags Where to store the extracted (and validated) flags.
1327 */
1328HRESULT GuestSession::i_fileCopyFlagFromStr(const com::Utf8Str &strFlags, FileCopyFlag_T *pfFlags)
1329{
1330 unsigned fFlags = (unsigned)FileCopyFlag_None;
1331
1332 /* Validate and set flags. */
1333 if (strFlags.isNotEmpty())
1334 {
1335 const char *pszNext = strFlags.c_str();
1336 for (;;)
1337 {
1338 /* Find the next keyword, ignoring all whitespace. */
1339 pszNext = RTStrStripL(pszNext);
1340
1341 const char * const pszComma = strchr(pszNext, ',');
1342 size_t cchKeyword = pszComma ? pszComma - pszNext : strlen(pszNext);
1343 while (cchKeyword > 0 && RT_C_IS_SPACE(pszNext[cchKeyword - 1]))
1344 cchKeyword--;
1345
1346 if (cchKeyword > 0)
1347 {
1348 /* Convert keyword to flag. */
1349#define MATCH_KEYWORD(a_szKeyword) ( cchKeyword == sizeof(a_szKeyword) - 1U \
1350 && memcmp(pszNext, a_szKeyword, sizeof(a_szKeyword) - 1U) == 0)
1351 if (MATCH_KEYWORD("NoReplace"))
1352 fFlags |= (unsigned)FileCopyFlag_NoReplace;
1353 else if (MATCH_KEYWORD("FollowLinks"))
1354 fFlags |= (unsigned)FileCopyFlag_FollowLinks;
1355 else if (MATCH_KEYWORD("Update"))
1356 fFlags |= (unsigned)FileCopyFlag_Update;
1357 else
1358 return setError(E_INVALIDARG, tr("Invalid file copy flag: %.*s"), (int)cchKeyword, pszNext);
1359#undef MATCH_KEYWORD
1360 }
1361 if (!pszComma)
1362 break;
1363 pszNext = pszComma + 1;
1364 }
1365 }
1366
1367 if (pfFlags)
1368 *pfFlags = (FileCopyFlag_T)fFlags;
1369 return S_OK;
1370}
1371
1372inline bool GuestSession::i_fileExists(uint32_t uFileID, ComObjPtr<GuestFile> *pFile)
1373{
1374 SessionFiles::const_iterator it = mData.mFiles.find(uFileID);
1375 if (it != mData.mFiles.end())
1376 {
1377 if (pFile)
1378 *pFile = it->second;
1379 return true;
1380 }
1381 return false;
1382}
1383
1384/**
1385 * Unregisters a file object from a session.
1386 *
1387 * @return VBox status code. VERR_NOT_FOUND if the file is not registered (anymore).
1388 * @param pFile File object to unregister from session.
1389 */
1390int GuestSession::i_fileUnregister(GuestFile *pFile)
1391{
1392 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1393
1394 LogFlowThisFunc(("pFile=%p\n", pFile));
1395
1396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1397
1398 const uint32_t idObject = pFile->getObjectID();
1399
1400 LogFlowFunc(("Removing file (objectID=%RU32) ...\n", idObject));
1401
1402 int rc = i_objectUnregister(idObject);
1403 if (RT_FAILURE(rc))
1404 return rc;
1405
1406 SessionFiles::iterator itFiles = mData.mFiles.find(idObject);
1407 AssertReturn(itFiles != mData.mFiles.end(), VERR_NOT_FOUND);
1408
1409 /* Make sure to consume the pointer before the one of the iterator gets released. */
1410 ComObjPtr<GuestFile> pFileConsumed = pFile;
1411
1412 LogFlowFunc(("Removing file ID=%RU32 (session %RU32, now total %zu files)\n",
1413 pFileConsumed->getObjectID(), mData.mSession.mID, mData.mFiles.size()));
1414
1415 rc = pFileConsumed->i_onUnregister();
1416 AssertRCReturn(rc, rc);
1417
1418 mData.mFiles.erase(itFiles);
1419
1420 alock.release(); /* Release lock before firing off event. */
1421
1422 fireGuestFileRegisteredEvent(mEventSource, this, pFileConsumed, false /* Unregistered */);
1423
1424 pFileConsumed.setNull();
1425
1426 LogFlowFuncLeaveRC(rc);
1427 return rc;
1428}
1429
1430int GuestSession::i_fileRemove(const Utf8Str &strPath, int *prcGuest)
1431{
1432 LogFlowThisFunc(("strPath=%s\n", strPath.c_str()));
1433
1434 int vrc = VINF_SUCCESS;
1435
1436 GuestProcessStartupInfo procInfo;
1437 GuestProcessStream streamOut;
1438
1439 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
1440 procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_RM);
1441
1442 try
1443 {
1444 procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */
1445 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
1446 procInfo.mArguments.push_back("--"); /* strPath could be '--help', which is a valid filename. */
1447 procInfo.mArguments.push_back(strPath); /* The file we want to remove. */
1448 }
1449 catch (std::bad_alloc &)
1450 {
1451 vrc = VERR_NO_MEMORY;
1452 }
1453
1454 if (RT_SUCCESS(vrc))
1455 vrc = GuestProcessTool::run(this, procInfo, prcGuest);
1456
1457 LogFlowFuncLeaveRC(vrc);
1458 return vrc;
1459}
1460
1461int GuestSession::i_fileOpenEx(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
1462 FileSharingMode_T aSharingMode, ULONG aCreationMode, const std::vector<FileOpenExFlag_T> &aFlags,
1463 ComObjPtr<GuestFile> &pFile, int *prcGuest)
1464{
1465 GuestFileOpenInfo openInfo;
1466 RT_ZERO(openInfo);
1467
1468 openInfo.mFilename = aPath;
1469 openInfo.mCreationMode = aCreationMode;
1470 openInfo.mAccessMode = aAccessMode;
1471 openInfo.mOpenAction = aOpenAction;
1472 openInfo.mSharingMode = aSharingMode;
1473
1474 /* Combine and validate flags. */
1475 uint32_t fOpenEx = 0;
1476 for (size_t i = 0; i < aFlags.size(); i++)
1477 fOpenEx = aFlags[i];
1478 if (fOpenEx)
1479 return VERR_INVALID_PARAMETER; /* FileOpenExFlag not implemented yet. */
1480 openInfo.mfOpenEx = fOpenEx;
1481
1482 return i_fileOpen(openInfo, pFile, prcGuest);
1483}
1484
1485int GuestSession::i_fileOpen(const GuestFileOpenInfo &openInfo, ComObjPtr<GuestFile> &pFile, int *prcGuest)
1486{
1487 LogFlowThisFunc(("strFile=%s, enmAccessMode=0x%x, enmOpenAction=0x%x, uCreationMode=%RU32, mfOpenEx=%RU32\n",
1488 openInfo.mFilename.c_str(), openInfo.mAccessMode, openInfo.mOpenAction, openInfo.mCreationMode,
1489 openInfo.mfOpenEx));
1490
1491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1492
1493 /* Guest Additions < 4.3 don't support handling guest files, skip. */
1494 if (mData.mProtocolVersion < 2)
1495 {
1496 if (prcGuest)
1497 *prcGuest = VERR_NOT_SUPPORTED;
1498 return VERR_GSTCTL_GUEST_ERROR;
1499 }
1500
1501 /* Create the directory object. */
1502 HRESULT hr = pFile.createObject();
1503 if (FAILED(hr))
1504 return VERR_COM_UNEXPECTED;
1505
1506 /* Register a new object ID. */
1507 uint32_t idObject;
1508 int rc = i_objectRegister(pFile, SESSIONOBJECTTYPE_FILE, &idObject);
1509 if (RT_FAILURE(rc))
1510 {
1511 pFile.setNull();
1512 return rc;
1513 }
1514
1515 Console *pConsole = mParent->i_getConsole();
1516 AssertPtr(pConsole);
1517
1518 rc = pFile->init(pConsole, this /* GuestSession */, idObject, openInfo);
1519 if (RT_FAILURE(rc))
1520 return rc;
1521
1522 /*
1523 * Since this is a synchronous guest call we have to
1524 * register the file object first, releasing the session's
1525 * lock and then proceed with the actual opening command
1526 * -- otherwise the file's opening callback would hang
1527 * because the session's lock still is in place.
1528 */
1529 try
1530 {
1531 /* Add the created file to our vector. */
1532 mData.mFiles[idObject] = pFile;
1533
1534 LogFlowFunc(("Added new guest file \"%s\" (Session: %RU32) (now total %zu files)\n",
1535 openInfo.mFilename.c_str(), mData.mSession.mID, mData.mFiles.size()));
1536
1537 alock.release(); /* Release lock before firing off event. */
1538
1539 fireGuestFileRegisteredEvent(mEventSource, this, pFile, true /* Registered */);
1540 }
1541 catch (std::bad_alloc &)
1542 {
1543 rc = VERR_NO_MEMORY;
1544 }
1545
1546 if (RT_SUCCESS(rc))
1547 {
1548 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1549 rc = pFile->i_openFile(30 * 1000 /* 30s timeout */, &rcGuest);
1550 if ( rc == VERR_GSTCTL_GUEST_ERROR
1551 && prcGuest)
1552 {
1553 *prcGuest = rcGuest;
1554 }
1555 }
1556
1557 LogFlowFuncLeaveRC(rc);
1558 return rc;
1559}
1560
1561/**
1562 * Queries information from a file on the guest.
1563 *
1564 * @returns IPRT status code. VERR_NOT_A_FILE if the queried file system object on the guest is not a file,
1565 * or VERR_GSTCTL_GUEST_ERROR if prcGuest contains more error information from the guest.
1566 * @param strPath Absolute path of file to query information for.
1567 * @param fFollowSymlinks Whether or not to follow symbolic links on the guest.
1568 * @param objData Where to store the acquired information.
1569 * @param prcGuest Where to store the guest rc. Optional.
1570 */
1571int GuestSession::i_fileQueryInfo(const Utf8Str &strPath, bool fFollowSymlinks, GuestFsObjData &objData, int *prcGuest)
1572{
1573 LogFlowThisFunc(("strPath=%s fFollowSymlinks=%RTbool\n", strPath.c_str(), fFollowSymlinks));
1574
1575 int vrc = i_fsQueryInfo(strPath, fFollowSymlinks, objData, prcGuest);
1576 if (RT_SUCCESS(vrc))
1577 {
1578 vrc = objData.mType == FsObjType_File
1579 ? VINF_SUCCESS : VERR_NOT_A_FILE;
1580 }
1581
1582 LogFlowFuncLeaveRC(vrc);
1583 return vrc;
1584}
1585
1586int GuestSession::i_fileQuerySize(const Utf8Str &strPath, bool fFollowSymlinks, int64_t *pllSize, int *prcGuest)
1587{
1588 AssertPtrReturn(pllSize, VERR_INVALID_POINTER);
1589
1590 GuestFsObjData objData;
1591 int vrc = i_fileQueryInfo(strPath, fFollowSymlinks, objData, prcGuest);
1592 if (RT_SUCCESS(vrc))
1593 *pllSize = objData.mObjectSize;
1594
1595 return vrc;
1596}
1597
1598/**
1599 * Queries information of a file system object (file, directory, ...).
1600 *
1601 * @return IPRT status code.
1602 * @param strPath Path to file system object to query information for.
1603 * @param fFollowSymlinks Whether to follow symbolic links or not.
1604 * @param objData Where to return the file system object data, if found.
1605 * @param prcGuest Guest rc, when returning VERR_GSTCTL_GUEST_ERROR.
1606 * Any other return code indicates some host side error.
1607 */
1608int GuestSession::i_fsQueryInfo(const Utf8Str &strPath, bool fFollowSymlinks, GuestFsObjData &objData, int *prcGuest)
1609{
1610 LogFlowThisFunc(("strPath=%s\n", strPath.c_str()));
1611
1612 /** @todo Merge this with IGuestFile::queryInfo(). */
1613 GuestProcessStartupInfo procInfo;
1614 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
1615 try
1616 {
1617 procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_STAT);
1618 procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */
1619 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
1620 if (fFollowSymlinks)
1621 procInfo.mArguments.push_back(Utf8Str("-L"));
1622 procInfo.mArguments.push_back("--"); /* strPath could be '--help', which is a valid filename. */
1623 procInfo.mArguments.push_back(strPath);
1624 }
1625 catch (std::bad_alloc &)
1626 {
1627 Log(("Out of memory!\n"));
1628 return VERR_NO_MEMORY;
1629 }
1630
1631 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1632 GuestCtrlStreamObjects stdOut;
1633 int vrc = GuestProcessTool::runEx(this, procInfo,
1634 &stdOut, 1 /* cStrmOutObjects */,
1635 &vrcGuest);
1636 if (!GuestProcess::i_isGuestError(vrc))
1637 {
1638 if (!stdOut.empty())
1639 {
1640 vrc = objData.FromStat(stdOut.at(0));
1641 if (RT_FAILURE(vrc))
1642 {
1643 vrcGuest = vrc;
1644 if (prcGuest)
1645 *prcGuest = vrc;
1646 vrc = VERR_GSTCTL_GUEST_ERROR;
1647 }
1648 }
1649 else
1650 vrc = VERR_BROKEN_PIPE;
1651 }
1652 else if (prcGuest)
1653 *prcGuest = vrcGuest;
1654
1655 LogFlowThisFunc(("Returning vrc=%Rrc, vrcGuest=%Rrc\n", vrc, vrcGuest));
1656 return vrc;
1657}
1658
1659const GuestCredentials& GuestSession::i_getCredentials(void)
1660{
1661 return mData.mCredentials;
1662}
1663
1664Utf8Str GuestSession::i_getName(void)
1665{
1666 return mData.mSession.mName;
1667}
1668
1669/* static */
1670Utf8Str GuestSession::i_guestErrorToString(int rcGuest)
1671{
1672 Utf8Str strError;
1673
1674 /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
1675 switch (rcGuest)
1676 {
1677 case VERR_INVALID_VM_HANDLE:
1678 strError += Utf8StrFmt(tr("VMM device is not available (is the VM running?)"));
1679 break;
1680
1681 case VERR_HGCM_SERVICE_NOT_FOUND:
1682 strError += Utf8StrFmt(tr("The guest execution service is not available"));
1683 break;
1684
1685 case VERR_ACCOUNT_RESTRICTED:
1686 strError += Utf8StrFmt(tr("The specified user account on the guest is restricted and can't be used to logon"));
1687 break;
1688
1689 case VERR_AUTHENTICATION_FAILURE:
1690 strError += Utf8StrFmt(tr("The specified user was not able to logon on guest"));
1691 break;
1692
1693 case VERR_TIMEOUT:
1694 strError += Utf8StrFmt(tr("The guest did not respond within time"));
1695 break;
1696
1697 case VERR_CANCELLED:
1698 strError += Utf8StrFmt(tr("The session operation was canceled"));
1699 break;
1700
1701 case VERR_GSTCTL_MAX_CID_OBJECTS_REACHED:
1702 strError += Utf8StrFmt(tr("Maximum number of concurrent guest processes has been reached"));
1703 break;
1704
1705 case VERR_NOT_FOUND:
1706 strError += Utf8StrFmt(tr("The guest execution service is not ready (yet)"));
1707 break;
1708
1709 default:
1710 strError += Utf8StrFmt("%Rrc", rcGuest);
1711 break;
1712 }
1713
1714 return strError;
1715}
1716
1717/**
1718 * Returns whether the session is in a started state or not.
1719 *
1720 * @returns \c true if in a started state, or \c false if not.
1721 */
1722bool GuestSession::i_isStarted(void) const
1723{
1724 return (mData.mStatus == GuestSessionStatus_Started);
1725}
1726
1727/**
1728 * Checks if this session is ready state where it can handle
1729 * all session-bound actions (like guest processes, guest files).
1730 * Only used by official API methods. Will set an external
1731 * error when not ready.
1732 */
1733HRESULT GuestSession::i_isStartedExternal(void)
1734{
1735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1736
1737 /** @todo Be a bit more informative. */
1738 if (!i_isStarted())
1739 return setError(E_UNEXPECTED, tr("Session is not in started state"));
1740
1741 return S_OK;
1742}
1743
1744/**
1745 * Returns whether a session status implies a terminated state or not.
1746 *
1747 * @returns \c true if it's a terminated state, or \c false if not.
1748 */
1749/* static */
1750bool GuestSession::i_isTerminated(GuestSessionStatus_T enmStatus)
1751{
1752 switch (enmStatus)
1753 {
1754 case GuestSessionStatus_Terminated:
1755 RT_FALL_THROUGH();
1756 case GuestSessionStatus_TimedOutKilled:
1757 RT_FALL_THROUGH();
1758 case GuestSessionStatus_TimedOutAbnormally:
1759 RT_FALL_THROUGH();
1760 case GuestSessionStatus_Down:
1761 RT_FALL_THROUGH();
1762 case GuestSessionStatus_Error:
1763 return true;
1764
1765 default:
1766 break;
1767 }
1768
1769 return false;
1770}
1771
1772/**
1773 * Returns whether the session is in a terminated state or not.
1774 *
1775 * @returns \c true if in a terminated state, or \c false if not.
1776 */
1777bool GuestSession::i_isTerminated(void) const
1778{
1779 return GuestSession::i_isTerminated(mData.mStatus);
1780}
1781
1782/**
1783 * Called by IGuest right before this session gets removed from
1784 * the public session list.
1785 */
1786int GuestSession::i_onRemove(void)
1787{
1788 LogFlowThisFuncEnter();
1789
1790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1791
1792 int vrc = i_objectsUnregister();
1793
1794 /*
1795 * Note: The event source stuff holds references to this object,
1796 * so make sure that this is cleaned up *before* calling uninit.
1797 */
1798 if (!mEventSource.isNull())
1799 {
1800 mEventSource->UnregisterListener(mLocalListener);
1801
1802 mLocalListener.setNull();
1803 unconst(mEventSource).setNull();
1804 }
1805
1806 LogFlowFuncLeaveRC(vrc);
1807 return vrc;
1808}
1809
1810/** No locking! */
1811int GuestSession::i_onSessionStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
1812{
1813 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
1814 /* pCallback is optional. */
1815 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
1816
1817 if (pSvcCbData->mParms < 3)
1818 return VERR_INVALID_PARAMETER;
1819
1820 CALLBACKDATA_SESSION_NOTIFY dataCb;
1821 /* pSvcCb->mpaParms[0] always contains the context ID. */
1822 int vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[1], &dataCb.uType);
1823 AssertRCReturn(vrc, vrc);
1824 vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[2], &dataCb.uResult);
1825 AssertRCReturn(vrc, vrc);
1826
1827 LogFlowThisFunc(("ID=%RU32, uType=%RU32, rcGuest=%Rrc\n",
1828 mData.mSession.mID, dataCb.uType, dataCb.uResult));
1829
1830 GuestSessionStatus_T sessionStatus = GuestSessionStatus_Undefined;
1831
1832 int rcGuest = dataCb.uResult; /** @todo uint32_t vs. int. */
1833 switch (dataCb.uType)
1834 {
1835 case GUEST_SESSION_NOTIFYTYPE_ERROR:
1836 sessionStatus = GuestSessionStatus_Error;
1837 break;
1838
1839 case GUEST_SESSION_NOTIFYTYPE_STARTED:
1840 sessionStatus = GuestSessionStatus_Started;
1841#if 0 /** @todo If we get some environment stuff along with this kind notification: */
1842 const char *pszzEnvBlock = ...;
1843 uint32_t cbEnvBlock = ...;
1844 if (!mData.mpBaseEnvironment)
1845 {
1846 GuestEnvironment *pBaseEnv;
1847 try { pBaseEnv = new GuestEnvironment(); } catch (std::bad_alloc &) { pBaseEnv = NULL; }
1848 if (pBaseEnv)
1849 {
1850 int vrc = pBaseEnv->initNormal(Guest.i_isGuestInWindowsNtFamily() ? RTENV_CREATE_F_ALLOW_EQUAL_FIRST_IN_VAR : 0);
1851 if (RT_SUCCESS(vrc))
1852 vrc = pBaseEnv->copyUtf8Block(pszzEnvBlock, cbEnvBlock);
1853 if (RT_SUCCESS(vrc))
1854 mData.mpBaseEnvironment = pBaseEnv;
1855 else
1856 pBaseEnv->release();
1857 }
1858 }
1859#endif
1860 break;
1861
1862 case GUEST_SESSION_NOTIFYTYPE_TEN:
1863 case GUEST_SESSION_NOTIFYTYPE_TES:
1864 case GUEST_SESSION_NOTIFYTYPE_TEA:
1865 sessionStatus = GuestSessionStatus_Terminated;
1866 break;
1867
1868 case GUEST_SESSION_NOTIFYTYPE_TOK:
1869 sessionStatus = GuestSessionStatus_TimedOutKilled;
1870 break;
1871
1872 case GUEST_SESSION_NOTIFYTYPE_TOA:
1873 sessionStatus = GuestSessionStatus_TimedOutAbnormally;
1874 break;
1875
1876 case GUEST_SESSION_NOTIFYTYPE_DWN:
1877 sessionStatus = GuestSessionStatus_Down;
1878 break;
1879
1880 case GUEST_SESSION_NOTIFYTYPE_UNDEFINED:
1881 default:
1882 vrc = VERR_NOT_SUPPORTED;
1883 break;
1884 }
1885
1886 if (RT_SUCCESS(vrc))
1887 {
1888 if (RT_FAILURE(rcGuest))
1889 sessionStatus = GuestSessionStatus_Error;
1890 }
1891
1892 /* Set the session status. */
1893 if (RT_SUCCESS(vrc))
1894 vrc = i_setSessionStatus(sessionStatus, rcGuest);
1895
1896 LogFlowThisFunc(("ID=%RU32, rcGuest=%Rrc\n", mData.mSession.mID, rcGuest));
1897
1898 LogFlowFuncLeaveRC(vrc);
1899 return vrc;
1900}
1901
1902PathStyle_T GuestSession::i_getPathStyle(void)
1903{
1904 PathStyle_T enmPathStyle;
1905
1906 VBOXOSTYPE enmOsType = mParent->i_getGuestOSType();
1907 if (enmOsType < VBOXOSTYPE_DOS)
1908 {
1909 LogFlowFunc(("returns PathStyle_Unknown\n"));
1910 enmPathStyle = PathStyle_Unknown;
1911 }
1912 else if (enmOsType < VBOXOSTYPE_Linux)
1913 {
1914 LogFlowFunc(("returns PathStyle_DOS\n"));
1915 enmPathStyle = PathStyle_DOS;
1916 }
1917 else
1918 {
1919 LogFlowFunc(("returns PathStyle_UNIX\n"));
1920 enmPathStyle = PathStyle_UNIX;
1921 }
1922
1923 return enmPathStyle;
1924}
1925
1926int GuestSession::i_startSession(int *prcGuest)
1927{
1928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1929
1930 LogFlowThisFunc(("mID=%RU32, mName=%s, uProtocolVersion=%RU32, openFlags=%x, openTimeoutMS=%RU32\n",
1931 mData.mSession.mID, mData.mSession.mName.c_str(), mData.mProtocolVersion,
1932 mData.mSession.mOpenFlags, mData.mSession.mOpenTimeoutMS));
1933
1934 /* Guest Additions < 4.3 don't support opening dedicated
1935 guest sessions. Simply return success here. */
1936 if (mData.mProtocolVersion < 2)
1937 {
1938 mData.mStatus = GuestSessionStatus_Started;
1939
1940 LogFlowThisFunc(("Installed Guest Additions don't support opening dedicated sessions, skipping\n"));
1941 return VINF_SUCCESS;
1942 }
1943
1944 if (mData.mStatus != GuestSessionStatus_Undefined)
1945 return VINF_SUCCESS;
1946
1947 /** @todo mData.mSession.uFlags validation. */
1948
1949 /* Set current session status. */
1950 mData.mStatus = GuestSessionStatus_Starting;
1951 mData.mRC = VINF_SUCCESS; /* Clear previous error, if any. */
1952
1953 int vrc;
1954
1955 GuestWaitEvent *pEvent = NULL;
1956 GuestEventTypes eventTypes;
1957 try
1958 {
1959 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
1960
1961 vrc = registerWaitEventEx(mData.mSession.mID, mData.mObjectID, eventTypes, &pEvent);
1962 }
1963 catch (std::bad_alloc &)
1964 {
1965 vrc = VERR_NO_MEMORY;
1966 }
1967
1968 if (RT_FAILURE(vrc))
1969 return vrc;
1970
1971 VBOXHGCMSVCPARM paParms[8];
1972
1973 int i = 0;
1974 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
1975 HGCMSvcSetU32(&paParms[i++], mData.mProtocolVersion);
1976 HGCMSvcSetPv(&paParms[i++], (void*)mData.mCredentials.mUser.c_str(),
1977 (ULONG)mData.mCredentials.mUser.length() + 1);
1978 HGCMSvcSetPv(&paParms[i++], (void*)mData.mCredentials.mPassword.c_str(),
1979 (ULONG)mData.mCredentials.mPassword.length() + 1);
1980 HGCMSvcSetPv(&paParms[i++], (void*)mData.mCredentials.mDomain.c_str(),
1981 (ULONG)mData.mCredentials.mDomain.length() + 1);
1982 HGCMSvcSetU32(&paParms[i++], mData.mSession.mOpenFlags);
1983
1984 alock.release(); /* Drop write lock before sending. */
1985
1986 vrc = i_sendMessage(HOST_MSG_SESSION_CREATE, i, paParms, VBOX_GUESTCTRL_DST_ROOT_SVC);
1987 if (RT_SUCCESS(vrc))
1988 {
1989 vrc = i_waitForStatusChange(pEvent, GuestSessionWaitForFlag_Start,
1990 30 * 1000 /* 30s timeout */,
1991 NULL /* Session status */, prcGuest);
1992 }
1993 else
1994 {
1995 /*
1996 * Unable to start guest session - update its current state.
1997 * Since there is no (official API) way to recover a failed guest session
1998 * this also marks the end state. Internally just calling this
1999 * same function again will work though.
2000 */
2001 mData.mStatus = GuestSessionStatus_Error;
2002 mData.mRC = vrc;
2003 }
2004
2005 unregisterWaitEvent(pEvent);
2006
2007 LogFlowFuncLeaveRC(vrc);
2008 return vrc;
2009}
2010
2011/**
2012 * Starts the guest session asynchronously in a separate thread.
2013 *
2014 * @returns IPRT status code.
2015 */
2016int GuestSession::i_startSessionAsync(void)
2017{
2018 LogFlowThisFuncEnter();
2019
2020 /* Create task: */
2021 GuestSessionTaskInternalStart *pTask = NULL;
2022 try
2023 {
2024 pTask = new GuestSessionTaskInternalStart(this);
2025 }
2026 catch (std::bad_alloc &)
2027 {
2028 return VERR_NO_MEMORY;
2029 }
2030 if (pTask->isOk())
2031 {
2032 /* Kick off the thread: */
2033 HRESULT hrc = pTask->createThread();
2034 pTask = NULL; /* Not valid anymore, not even on failure! */
2035 if (SUCCEEDED(hrc))
2036 {
2037 LogFlowFuncLeaveRC(VINF_SUCCESS);
2038 return VINF_SUCCESS;
2039 }
2040 LogFlow(("GuestSession: Failed to create thread for GuestSessionTaskInternalOpen task.\n"));
2041 }
2042 else
2043 LogFlow(("GuestSession: GuestSessionTaskInternalStart creation failed: %Rhrc.\n", pTask->rc()));
2044 LogFlowFuncLeaveRC(VERR_GENERAL_FAILURE);
2045 return VERR_GENERAL_FAILURE;
2046}
2047
2048/**
2049 * Static function to start a guest session asynchronously.
2050 *
2051 * @returns IPRT status code.
2052 * @param pTask Task object to use for starting the guest session.
2053 */
2054/* static */
2055int GuestSession::i_startSessionThreadTask(GuestSessionTaskInternalStart *pTask)
2056{
2057 LogFlowFunc(("pTask=%p\n", pTask));
2058 AssertPtr(pTask);
2059
2060 const ComObjPtr<GuestSession> pSession(pTask->Session());
2061 Assert(!pSession.isNull());
2062
2063 AutoCaller autoCaller(pSession);
2064 if (FAILED(autoCaller.rc()))
2065 return VERR_COM_INVALID_OBJECT_STATE;
2066
2067 int vrc = pSession->i_startSession(NULL /* Guest rc, ignored */);
2068 /* Nothing to do here anymore. */
2069
2070 LogFlowFuncLeaveRC(vrc);
2071 return vrc;
2072}
2073
2074/**
2075 * Registers an object with the session, i.e. allocates an object ID.
2076 *
2077 * @return VBox status code.
2078 * @retval VERR_GSTCTL_MAX_OBJECTS_REACHED if the maximum of concurrent objects
2079 * is reached.
2080 * @param pObject Guest object to register (weak pointer). Optional.
2081 * @param enmType Session object type to register.
2082 * @param pidObject Where to return the object ID on success. Optional.
2083 */
2084int GuestSession::i_objectRegister(GuestObject *pObject, SESSIONOBJECTTYPE enmType, uint32_t *pidObject)
2085{
2086 /* pObject can be NULL. */
2087 /* pidObject is optional. */
2088
2089 /*
2090 * Pick a random bit as starting point. If it's in use, search forward
2091 * for a free one, wrapping around. We've reserved both the zero'th and
2092 * max-1 IDs (see Data constructor).
2093 */
2094 uint32_t idObject = RTRandU32Ex(1, VBOX_GUESTCTRL_MAX_OBJECTS - 2);
2095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2096 if (!ASMBitTestAndSet(&mData.bmObjectIds[0], idObject))
2097 { /* likely */ }
2098 else if (mData.mObjects.size() < VBOX_GUESTCTRL_MAX_OBJECTS - 2 /* First and last are not used */)
2099 {
2100 /* Forward search. */
2101 int iHit = ASMBitNextClear(&mData.bmObjectIds[0], VBOX_GUESTCTRL_MAX_OBJECTS, idObject);
2102 if (iHit < 0)
2103 iHit = ASMBitFirstClear(&mData.bmObjectIds[0], VBOX_GUESTCTRL_MAX_OBJECTS);
2104 AssertLogRelMsgReturn(iHit >= 0, ("object count: %#zu\n", mData.mObjects.size()), VERR_GSTCTL_MAX_CID_OBJECTS_REACHED);
2105 idObject = iHit;
2106 AssertLogRelMsgReturn(!ASMBitTestAndSet(&mData.bmObjectIds[0], idObject), ("idObject=%#x\n", idObject), VERR_INTERNAL_ERROR_2);
2107 }
2108 else
2109 {
2110 LogFunc(("Maximum number of objects reached (enmType=%RU32, %zu objects)\n", enmType, mData.mObjects.size()));
2111 return VERR_GSTCTL_MAX_CID_OBJECTS_REACHED;
2112 }
2113
2114 Log2Func(("enmType=%RU32 -> idObject=%RU32 (%zu objects)\n", enmType, idObject, mData.mObjects.size()));
2115
2116 try
2117 {
2118 mData.mObjects[idObject].pObject = pObject; /* Can be NULL. */
2119 mData.mObjects[idObject].enmType = enmType;
2120 mData.mObjects[idObject].msBirth = RTTimeMilliTS();
2121 }
2122 catch (std::bad_alloc &)
2123 {
2124 ASMBitClear(&mData.bmObjectIds[0], idObject);
2125 return VERR_NO_MEMORY;
2126 }
2127
2128 if (pidObject)
2129 *pidObject = idObject;
2130
2131 return VINF_SUCCESS;
2132}
2133
2134/**
2135 * Unregisters an object from the session objects list.
2136 *
2137 * @retval VINF_SUCCESS on success.
2138 * @retval VERR_NOT_FOUND if the object ID was not found.
2139 * @param idObject Object ID to unregister.
2140 */
2141int GuestSession::i_objectUnregister(uint32_t idObject)
2142{
2143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2144
2145 int rc = VINF_SUCCESS;
2146 AssertMsgStmt(ASMBitTestAndClear(&mData.bmObjectIds, idObject), ("idObject=%#x\n", idObject), rc = VERR_NOT_FOUND);
2147
2148 SessionObjects::iterator ItObj = mData.mObjects.find(idObject);
2149 AssertMsgReturn(ItObj != mData.mObjects.end(), ("idObject=%#x\n", idObject), VERR_NOT_FOUND);
2150 mData.mObjects.erase(ItObj);
2151
2152 return rc;
2153}
2154
2155/**
2156 * Unregisters all objects from the session list.
2157 *
2158 * @returns VBox status code.
2159 */
2160int GuestSession::i_objectsUnregister(void)
2161{
2162 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2163
2164 LogFlowThisFunc(("Unregistering directories (%zu total)\n", mData.mDirectories.size()));
2165
2166 SessionDirectories::iterator itDirs;
2167 while ((itDirs = mData.mDirectories.begin()) != mData.mDirectories.end())
2168 {
2169 alock.release();
2170 i_directoryUnregister(itDirs->second);
2171 alock.acquire();
2172 }
2173
2174 Assert(mData.mDirectories.size() == 0);
2175 mData.mDirectories.clear();
2176
2177 LogFlowThisFunc(("Unregistering files (%zu total)\n", mData.mFiles.size()));
2178
2179 SessionFiles::iterator itFiles;
2180 while ((itFiles = mData.mFiles.begin()) != mData.mFiles.end())
2181 {
2182 alock.release();
2183 i_fileUnregister(itFiles->second);
2184 alock.acquire();
2185 }
2186
2187 Assert(mData.mFiles.size() == 0);
2188 mData.mFiles.clear();
2189
2190 LogFlowThisFunc(("Unregistering processes (%zu total)\n", mData.mProcesses.size()));
2191
2192 SessionProcesses::iterator itProcs;
2193 while ((itProcs = mData.mProcesses.begin()) != mData.mProcesses.end())
2194 {
2195 alock.release();
2196 i_processUnregister(itProcs->second);
2197 alock.acquire();
2198 }
2199
2200 Assert(mData.mProcesses.size() == 0);
2201 mData.mProcesses.clear();
2202
2203 return VINF_SUCCESS;
2204}
2205
2206/**
2207 * Notifies all registered objects about a session status change.
2208 *
2209 * @returns VBox status code.
2210 * @param enmSessionStatus Session status to notify objects about.
2211 */
2212int GuestSession::i_objectsNotifyAboutStatusChange(GuestSessionStatus_T enmSessionStatus)
2213{
2214 LogFlowThisFunc(("enmSessionStatus=%RU32\n", enmSessionStatus));
2215
2216 int vrc = VINF_SUCCESS;
2217
2218 SessionObjects::iterator itObjs = mData.mObjects.begin();
2219 while (itObjs != mData.mObjects.end())
2220 {
2221 GuestObject *pObj = itObjs->second.pObject;
2222 if (pObj) /* pObject can be NULL (weak pointer). */
2223 {
2224 int vrc2 = pObj->i_onSessionStatusChange(enmSessionStatus);
2225 if (RT_SUCCESS(vrc))
2226 vrc = vrc2;
2227
2228 /* If the session got terminated, make sure to cancel all wait events for
2229 * the current object. */
2230 if (i_isTerminated())
2231 pObj->cancelWaitEvents();
2232 }
2233
2234 ++itObjs;
2235 }
2236
2237 LogFlowFuncLeaveRC(vrc);
2238 return vrc;
2239}
2240
2241int GuestSession::i_pathRename(const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags, int *prcGuest)
2242{
2243 AssertReturn(!(uFlags & ~PATHRENAME_FLAG_VALID_MASK), VERR_INVALID_PARAMETER);
2244
2245 LogFlowThisFunc(("strSource=%s, strDest=%s, uFlags=0x%x\n",
2246 strSource.c_str(), strDest.c_str(), uFlags));
2247
2248 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2249
2250 GuestWaitEvent *pEvent = NULL;
2251 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2252 if (RT_FAILURE(vrc))
2253 return vrc;
2254
2255 /* Prepare HGCM call. */
2256 VBOXHGCMSVCPARM paParms[8];
2257 int i = 0;
2258 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2259 HGCMSvcSetPv(&paParms[i++], (void*)strSource.c_str(),
2260 (ULONG)strSource.length() + 1);
2261 HGCMSvcSetPv(&paParms[i++], (void*)strDest.c_str(),
2262 (ULONG)strDest.length() + 1);
2263 HGCMSvcSetU32(&paParms[i++], uFlags);
2264
2265 alock.release(); /* Drop write lock before sending. */
2266
2267 vrc = i_sendMessage(HOST_MSG_PATH_RENAME, i, paParms);
2268 if (RT_SUCCESS(vrc))
2269 {
2270 vrc = pEvent->Wait(30 * 1000);
2271 if ( vrc == VERR_GSTCTL_GUEST_ERROR
2272 && prcGuest)
2273 *prcGuest = pEvent->GuestResult();
2274 }
2275
2276 unregisterWaitEvent(pEvent);
2277
2278 LogFlowFuncLeaveRC(vrc);
2279 return vrc;
2280}
2281
2282/**
2283 * Returns the user's absolute documents path, if any.
2284 *
2285 * @return VBox status code.
2286 * @param strPath Where to store the user's document path.
2287 * @param prcGuest Guest rc, when returning VERR_GSTCTL_GUEST_ERROR.
2288 * Any other return code indicates some host side error.
2289 */
2290int GuestSession::i_pathUserDocuments(Utf8Str &strPath, int *prcGuest)
2291{
2292 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2293
2294 /** @todo Cache the user's document path? */
2295
2296 GuestWaitEvent *pEvent = NULL;
2297 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2298 if (RT_FAILURE(vrc))
2299 return vrc;
2300
2301 /* Prepare HGCM call. */
2302 VBOXHGCMSVCPARM paParms[2];
2303 int i = 0;
2304 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2305
2306 alock.release(); /* Drop write lock before sending. */
2307
2308 vrc = i_sendMessage(HOST_MSG_PATH_USER_DOCUMENTS, i, paParms);
2309 if (RT_SUCCESS(vrc))
2310 {
2311 vrc = pEvent->Wait(30 * 1000);
2312 if (RT_SUCCESS(vrc))
2313 {
2314 strPath = pEvent->Payload().ToString();
2315 }
2316 else
2317 {
2318 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2319 {
2320 if (prcGuest)
2321 *prcGuest = pEvent->GuestResult();
2322 }
2323 }
2324 }
2325
2326 unregisterWaitEvent(pEvent);
2327
2328 LogFlowFuncLeaveRC(vrc);
2329 return vrc;
2330}
2331
2332/**
2333 * Returns the user's absolute home path, if any.
2334 *
2335 * @return VBox status code.
2336 * @param strPath Where to store the user's home path.
2337 * @param prcGuest Guest rc, when returning VERR_GSTCTL_GUEST_ERROR.
2338 * Any other return code indicates some host side error.
2339 */
2340int GuestSession::i_pathUserHome(Utf8Str &strPath, int *prcGuest)
2341{
2342 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2343
2344 /** @todo Cache the user's home path? */
2345
2346 GuestWaitEvent *pEvent = NULL;
2347 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2348 if (RT_FAILURE(vrc))
2349 return vrc;
2350
2351 /* Prepare HGCM call. */
2352 VBOXHGCMSVCPARM paParms[2];
2353 int i = 0;
2354 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2355
2356 alock.release(); /* Drop write lock before sending. */
2357
2358 vrc = i_sendMessage(HOST_MSG_PATH_USER_HOME, i, paParms);
2359 if (RT_SUCCESS(vrc))
2360 {
2361 vrc = pEvent->Wait(30 * 1000);
2362 if (RT_SUCCESS(vrc))
2363 {
2364 strPath = pEvent->Payload().ToString();
2365 }
2366 else
2367 {
2368 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2369 {
2370 if (prcGuest)
2371 *prcGuest = pEvent->GuestResult();
2372 }
2373 }
2374 }
2375
2376 unregisterWaitEvent(pEvent);
2377
2378 LogFlowFuncLeaveRC(vrc);
2379 return vrc;
2380}
2381
2382/**
2383 * Unregisters a process object from a session.
2384 *
2385 * @return VBox status code. VERR_NOT_FOUND if the process is not registered (anymore).
2386 * @param pProcess Process object to unregister from session.
2387 */
2388int GuestSession::i_processUnregister(GuestProcess *pProcess)
2389{
2390 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
2391
2392 LogFlowThisFunc(("pProcess=%p\n", pProcess));
2393
2394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2395
2396 const uint32_t idObject = pProcess->getObjectID();
2397
2398 LogFlowFunc(("Removing process (objectID=%RU32) ...\n", idObject));
2399
2400 int rc = i_objectUnregister(idObject);
2401 if (RT_FAILURE(rc))
2402 return rc;
2403
2404 SessionProcesses::iterator itProcs = mData.mProcesses.find(idObject);
2405 AssertReturn(itProcs != mData.mProcesses.end(), VERR_NOT_FOUND);
2406
2407 /* Make sure to consume the pointer before the one of the iterator gets released. */
2408 ComObjPtr<GuestProcess> pProc = pProcess;
2409
2410 ULONG uPID;
2411 HRESULT hr = pProc->COMGETTER(PID)(&uPID);
2412 ComAssertComRC(hr);
2413
2414 LogFlowFunc(("Removing process ID=%RU32 (session %RU32, guest PID %RU32, now total %zu processes)\n",
2415 idObject, mData.mSession.mID, uPID, mData.mProcesses.size()));
2416
2417 rc = pProcess->i_onUnregister();
2418 AssertRCReturn(rc, rc);
2419
2420 mData.mProcesses.erase(itProcs);
2421
2422 alock.release(); /* Release lock before firing off event. */
2423
2424 fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProc, uPID, false /* Process unregistered */);
2425
2426 pProc.setNull();
2427
2428 LogFlowFuncLeaveRC(rc);
2429 return rc;
2430}
2431
2432/**
2433 * Creates but does *not* start the process yet.
2434 *
2435 * See GuestProcess::startProcess() or GuestProcess::startProcessAsync() for
2436 * starting the process.
2437 *
2438 * @return IPRT status code.
2439 * @param procInfo
2440 * @param pProcess
2441 */
2442int GuestSession::i_processCreateEx(GuestProcessStartupInfo &procInfo, ComObjPtr<GuestProcess> &pProcess)
2443{
2444 LogFlowFunc(("mExe=%s, mFlags=%x, mTimeoutMS=%RU32\n",
2445 procInfo.mExecutable.c_str(), procInfo.mFlags, procInfo.mTimeoutMS));
2446#ifdef DEBUG
2447 if (procInfo.mArguments.size())
2448 {
2449 LogFlowFunc(("Arguments:"));
2450 ProcessArguments::const_iterator it = procInfo.mArguments.begin();
2451 while (it != procInfo.mArguments.end())
2452 {
2453 LogFlow((" %s", (*it).c_str()));
2454 ++it;
2455 }
2456 LogFlow(("\n"));
2457 }
2458#endif
2459
2460 /* Validate flags. */
2461 if (procInfo.mFlags)
2462 {
2463 if ( !(procInfo.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
2464 && !(procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
2465 && !(procInfo.mFlags & ProcessCreateFlag_Hidden)
2466 && !(procInfo.mFlags & ProcessCreateFlag_Profile)
2467 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
2468 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdErr))
2469 {
2470 return VERR_INVALID_PARAMETER;
2471 }
2472 }
2473
2474 if ( (procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
2475 && ( (procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
2476 || (procInfo.mFlags & ProcessCreateFlag_WaitForStdErr)
2477 )
2478 )
2479 {
2480 return VERR_INVALID_PARAMETER;
2481 }
2482
2483 if (procInfo.mPriority)
2484 {
2485 if (!(procInfo.mPriority & ProcessPriority_Default))
2486 return VERR_INVALID_PARAMETER;
2487 }
2488
2489 /* Adjust timeout.
2490 * If set to 0, we define an infinite timeout (unlimited process run time). */
2491 if (procInfo.mTimeoutMS == 0)
2492 procInfo.mTimeoutMS = UINT32_MAX;
2493
2494 /** @todo Implement process priority + affinity. */
2495
2496 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2497
2498 /* Create the process object. */
2499 HRESULT hr = pProcess.createObject();
2500 if (FAILED(hr))
2501 return VERR_COM_UNEXPECTED;
2502
2503 /* Register a new object ID. */
2504 uint32_t idObject;
2505 int rc = i_objectRegister(pProcess, SESSIONOBJECTTYPE_PROCESS, &idObject);
2506 if (RT_FAILURE(rc))
2507 {
2508 pProcess.setNull();
2509 return rc;
2510 }
2511
2512 rc = pProcess->init(mParent->i_getConsole() /* Console */, this /* Session */, idObject,
2513 procInfo, mData.mpBaseEnvironment);
2514 if (RT_FAILURE(rc))
2515 return rc;
2516
2517 /* Add the created process to our map. */
2518 try
2519 {
2520 mData.mProcesses[idObject] = pProcess;
2521
2522 LogFlowFunc(("Added new process (Session: %RU32) with process ID=%RU32 (now total %zu processes)\n",
2523 mData.mSession.mID, idObject, mData.mProcesses.size()));
2524
2525 alock.release(); /* Release lock before firing off event. */
2526
2527 fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProcess, 0 /* PID */, true /* Process registered */);
2528 }
2529 catch (std::bad_alloc &)
2530 {
2531 rc = VERR_NO_MEMORY;
2532 }
2533
2534 return rc;
2535}
2536
2537inline bool GuestSession::i_processExists(uint32_t uProcessID, ComObjPtr<GuestProcess> *pProcess)
2538{
2539 SessionProcesses::const_iterator it = mData.mProcesses.find(uProcessID);
2540 if (it != mData.mProcesses.end())
2541 {
2542 if (pProcess)
2543 *pProcess = it->second;
2544 return true;
2545 }
2546 return false;
2547}
2548
2549inline int GuestSession::i_processGetByPID(ULONG uPID, ComObjPtr<GuestProcess> *pProcess)
2550{
2551 AssertReturn(uPID, false);
2552 /* pProcess is optional. */
2553
2554 SessionProcesses::iterator itProcs = mData.mProcesses.begin();
2555 for (; itProcs != mData.mProcesses.end(); ++itProcs)
2556 {
2557 ComObjPtr<GuestProcess> pCurProc = itProcs->second;
2558 AutoCaller procCaller(pCurProc);
2559 if (procCaller.rc())
2560 return VERR_COM_INVALID_OBJECT_STATE;
2561
2562 ULONG uCurPID;
2563 HRESULT hr = pCurProc->COMGETTER(PID)(&uCurPID);
2564 ComAssertComRC(hr);
2565
2566 if (uCurPID == uPID)
2567 {
2568 if (pProcess)
2569 *pProcess = pCurProc;
2570 return VINF_SUCCESS;
2571 }
2572 }
2573
2574 return VERR_NOT_FOUND;
2575}
2576
2577int GuestSession::i_sendMessage(uint32_t uMessage, uint32_t uParms, PVBOXHGCMSVCPARM paParms,
2578 uint64_t fDst /*= VBOX_GUESTCTRL_DST_SESSION*/)
2579{
2580 LogFlowThisFuncEnter();
2581
2582#ifndef VBOX_GUESTCTRL_TEST_CASE
2583 ComObjPtr<Console> pConsole = mParent->i_getConsole();
2584 Assert(!pConsole.isNull());
2585
2586 /* Forward the information to the VMM device. */
2587 VMMDev *pVMMDev = pConsole->i_getVMMDev();
2588 AssertPtr(pVMMDev);
2589
2590 LogFlowThisFunc(("uMessage=%RU32 (%s), uParms=%RU32\n", uMessage, GstCtrlHostMsgtoStr((guestControl::eHostMsg)uMessage), uParms));
2591
2592 /* HACK ALERT! We extend the first parameter to 64-bit and use the
2593 two topmost bits for call destination information. */
2594 Assert(fDst == VBOX_GUESTCTRL_DST_SESSION || fDst == VBOX_GUESTCTRL_DST_ROOT_SVC || fDst == VBOX_GUESTCTRL_DST_BOTH);
2595 Assert(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT);
2596 paParms[0].type = VBOX_HGCM_SVC_PARM_64BIT;
2597 paParms[0].u.uint64 = (uint64_t)paParms[0].u.uint32 | fDst;
2598
2599 /* Make the call. */
2600 int vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uMessage, uParms, paParms);
2601 if (RT_FAILURE(vrc))
2602 {
2603 /** @todo What to do here? */
2604 }
2605#else
2606 /* Not needed within testcases. */
2607 int vrc = VINF_SUCCESS;
2608#endif
2609 LogFlowFuncLeaveRC(vrc);
2610 return vrc;
2611}
2612
2613/* static */
2614HRESULT GuestSession::i_setErrorExternal(VirtualBoxBase *pInterface, int rcGuest)
2615{
2616 AssertPtr(pInterface);
2617 AssertMsg(RT_FAILURE(rcGuest), ("Guest rc does not indicate a failure when setting error\n"));
2618
2619 return pInterface->setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, GuestSession::i_guestErrorToString(rcGuest).c_str());
2620}
2621
2622/* Does not do locking; caller is responsible for that! */
2623int GuestSession::i_setSessionStatus(GuestSessionStatus_T sessionStatus, int sessionRc)
2624{
2625 LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, sessionRc=%Rrc\n",
2626 mData.mStatus, sessionStatus, sessionRc));
2627
2628 if (sessionStatus == GuestSessionStatus_Error)
2629 {
2630 AssertMsg(RT_FAILURE(sessionRc), ("Guest rc must be an error (%Rrc)\n", sessionRc));
2631 /* Do not allow overwriting an already set error. If this happens
2632 * this means we forgot some error checking/locking somewhere. */
2633 AssertMsg(RT_SUCCESS(mData.mRC), ("Guest rc already set (to %Rrc)\n", mData.mRC));
2634 }
2635 else
2636 AssertMsg(RT_SUCCESS(sessionRc), ("Guest rc must not be an error (%Rrc)\n", sessionRc));
2637
2638 int vrc = VINF_SUCCESS;
2639
2640 if (mData.mStatus != sessionStatus)
2641 {
2642 mData.mStatus = sessionStatus;
2643 mData.mRC = sessionRc;
2644
2645 /* Make sure to notify all underlying objects first. */
2646 vrc = i_objectsNotifyAboutStatusChange(sessionStatus);
2647
2648 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
2649 HRESULT hr = errorInfo.createObject();
2650 ComAssertComRC(hr);
2651 int rc2 = errorInfo->initEx(VBOX_E_IPRT_ERROR, sessionRc,
2652 COM_IIDOF(IGuestSession), getComponentName(),
2653 i_guestErrorToString(sessionRc));
2654 AssertRC(rc2);
2655
2656 fireGuestSessionStateChangedEvent(mEventSource, this,
2657 mData.mSession.mID, sessionStatus, errorInfo);
2658 }
2659
2660 LogFlowFuncLeaveRC(vrc);
2661 return vrc;
2662}
2663
2664int GuestSession::i_signalWaiters(GuestSessionWaitResult_T enmWaitResult, int rc /*= VINF_SUCCESS */)
2665{
2666 RT_NOREF(enmWaitResult, rc);
2667
2668 /*LogFlowThisFunc(("enmWaitResult=%d, rc=%Rrc, mWaitCount=%RU32, mWaitEvent=%p\n",
2669 enmWaitResult, rc, mData.mWaitCount, mData.mWaitEvent));*/
2670
2671 /* Note: No write locking here -- already done in the caller. */
2672
2673 int vrc = VINF_SUCCESS;
2674 /*if (mData.mWaitEvent)
2675 vrc = mData.mWaitEvent->Signal(enmWaitResult, rc);*/
2676 LogFlowFuncLeaveRC(vrc);
2677 return vrc;
2678}
2679
2680/**
2681 * Determines the protocol version (sets mData.mProtocolVersion).
2682 *
2683 * This is called from the init method prior to to establishing a guest
2684 * session.
2685 *
2686 * @return IPRT status code.
2687 */
2688int GuestSession::i_determineProtocolVersion(void)
2689{
2690 /*
2691 * We currently do this based on the reported guest additions version,
2692 * ASSUMING that VBoxService and VBoxDrv are at the same version.
2693 */
2694 ComObjPtr<Guest> pGuest = mParent;
2695 AssertReturn(!pGuest.isNull(), VERR_NOT_SUPPORTED);
2696 uint32_t uGaVersion = pGuest->i_getAdditionsVersion();
2697
2698 /* Everyone supports version one, if they support anything at all. */
2699 mData.mProtocolVersion = 1;
2700
2701 /* Guest control 2.0 was introduced with 4.3.0. */
2702 if (uGaVersion >= VBOX_FULL_VERSION_MAKE(4,3,0))
2703 mData.mProtocolVersion = 2; /* Guest control 2.0. */
2704
2705 LogFlowThisFunc(("uGaVersion=%u.%u.%u => mProtocolVersion=%u\n",
2706 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
2707 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
2708
2709 /*
2710 * Inform the user about outdated guest additions (VM release log).
2711 */
2712 if (mData.mProtocolVersion < 2)
2713 LogRelMax(3, (tr("Warning: Guest Additions v%u.%u.%u only supports the older guest control protocol version %u.\n"
2714 " Please upgrade GAs to the current version to get full guest control capabilities.\n"),
2715 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
2716 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
2717
2718 return VINF_SUCCESS;
2719}
2720
2721int GuestSession::i_waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, GuestSessionWaitResult_T &waitResult, int *prcGuest)
2722{
2723 LogFlowThisFuncEnter();
2724
2725 AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
2726
2727 /*LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p, prcGuest=%p\n",
2728 fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent, prcGuest));*/
2729
2730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2731
2732 /* Did some error occur before? Then skip waiting and return. */
2733 if (mData.mStatus == GuestSessionStatus_Error)
2734 {
2735 waitResult = GuestSessionWaitResult_Error;
2736 AssertMsg(RT_FAILURE(mData.mRC), ("No error rc (%Rrc) set when guest session indicated an error\n", mData.mRC));
2737 if (prcGuest)
2738 *prcGuest = mData.mRC; /* Return last set error. */
2739 return VERR_GSTCTL_GUEST_ERROR;
2740 }
2741
2742 /* Guest Additions < 4.3 don't support session handling, skip. */
2743 if (mData.mProtocolVersion < 2)
2744 {
2745 waitResult = GuestSessionWaitResult_WaitFlagNotSupported;
2746
2747 LogFlowThisFunc(("Installed Guest Additions don't support waiting for dedicated sessions, skipping\n"));
2748 return VINF_SUCCESS;
2749 }
2750
2751 waitResult = GuestSessionWaitResult_None;
2752 if (fWaitFlags & GuestSessionWaitForFlag_Terminate)
2753 {
2754 switch (mData.mStatus)
2755 {
2756 case GuestSessionStatus_Terminated:
2757 case GuestSessionStatus_Down:
2758 waitResult = GuestSessionWaitResult_Terminate;
2759 break;
2760
2761 case GuestSessionStatus_TimedOutKilled:
2762 case GuestSessionStatus_TimedOutAbnormally:
2763 waitResult = GuestSessionWaitResult_Timeout;
2764 break;
2765
2766 case GuestSessionStatus_Error:
2767 /* Handled above. */
2768 break;
2769
2770 case GuestSessionStatus_Started:
2771 waitResult = GuestSessionWaitResult_Start;
2772 break;
2773
2774 case GuestSessionStatus_Undefined:
2775 case GuestSessionStatus_Starting:
2776 /* Do the waiting below. */
2777 break;
2778
2779 default:
2780 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
2781 return VERR_NOT_IMPLEMENTED;
2782 }
2783 }
2784 else if (fWaitFlags & GuestSessionWaitForFlag_Start)
2785 {
2786 switch (mData.mStatus)
2787 {
2788 case GuestSessionStatus_Started:
2789 case GuestSessionStatus_Terminating:
2790 case GuestSessionStatus_Terminated:
2791 case GuestSessionStatus_Down:
2792 waitResult = GuestSessionWaitResult_Start;
2793 break;
2794
2795 case GuestSessionStatus_Error:
2796 waitResult = GuestSessionWaitResult_Error;
2797 break;
2798
2799 case GuestSessionStatus_TimedOutKilled:
2800 case GuestSessionStatus_TimedOutAbnormally:
2801 waitResult = GuestSessionWaitResult_Timeout;
2802 break;
2803
2804 case GuestSessionStatus_Undefined:
2805 case GuestSessionStatus_Starting:
2806 /* Do the waiting below. */
2807 break;
2808
2809 default:
2810 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
2811 return VERR_NOT_IMPLEMENTED;
2812 }
2813 }
2814
2815 LogFlowThisFunc(("sessionStatus=%RU32, sessionRc=%Rrc, waitResult=%RU32\n",
2816 mData.mStatus, mData.mRC, waitResult));
2817
2818 /* No waiting needed? Return immediately using the last set error. */
2819 if (waitResult != GuestSessionWaitResult_None)
2820 {
2821 if (prcGuest)
2822 *prcGuest = mData.mRC; /* Return last set error (if any). */
2823 return RT_SUCCESS(mData.mRC) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
2824 }
2825
2826 int vrc;
2827
2828 GuestWaitEvent *pEvent = NULL;
2829 GuestEventTypes eventTypes;
2830 try
2831 {
2832 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
2833
2834 vrc = registerWaitEventEx(mData.mSession.mID, mData.mObjectID, eventTypes, &pEvent);
2835 }
2836 catch (std::bad_alloc &)
2837 {
2838 vrc = VERR_NO_MEMORY;
2839 }
2840
2841 if (RT_FAILURE(vrc))
2842 return vrc;
2843
2844 alock.release(); /* Release lock before waiting. */
2845
2846 GuestSessionStatus_T sessionStatus;
2847 vrc = i_waitForStatusChange(pEvent, fWaitFlags,
2848 uTimeoutMS, &sessionStatus, prcGuest);
2849 if (RT_SUCCESS(vrc))
2850 {
2851 switch (sessionStatus)
2852 {
2853 case GuestSessionStatus_Started:
2854 waitResult = GuestSessionWaitResult_Start;
2855 break;
2856
2857 case GuestSessionStatus_Terminated:
2858 waitResult = GuestSessionWaitResult_Terminate;
2859 break;
2860
2861 case GuestSessionStatus_TimedOutKilled:
2862 case GuestSessionStatus_TimedOutAbnormally:
2863 waitResult = GuestSessionWaitResult_Timeout;
2864 break;
2865
2866 case GuestSessionStatus_Down:
2867 waitResult = GuestSessionWaitResult_Terminate;
2868 break;
2869
2870 case GuestSessionStatus_Error:
2871 waitResult = GuestSessionWaitResult_Error;
2872 break;
2873
2874 default:
2875 waitResult = GuestSessionWaitResult_Status;
2876 break;
2877 }
2878 }
2879
2880 unregisterWaitEvent(pEvent);
2881
2882 LogFlowFuncLeaveRC(vrc);
2883 return vrc;
2884}
2885
2886/**
2887 * Undocumented, you guess what it does.
2888 *
2889 * @note Similar code in GuestFile::i_waitForStatusChange() and
2890 * GuestProcess::i_waitForStatusChange().
2891 */
2892int GuestSession::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t fWaitFlags, uint32_t uTimeoutMS,
2893 GuestSessionStatus_T *pSessionStatus, int *prcGuest)
2894{
2895 RT_NOREF(fWaitFlags);
2896 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
2897
2898 VBoxEventType_T evtType;
2899 ComPtr<IEvent> pIEvent;
2900 int vrc = waitForEvent(pEvent, uTimeoutMS,
2901 &evtType, pIEvent.asOutParam());
2902 if (RT_SUCCESS(vrc))
2903 {
2904 Assert(evtType == VBoxEventType_OnGuestSessionStateChanged);
2905
2906 ComPtr<IGuestSessionStateChangedEvent> pChangedEvent = pIEvent;
2907 Assert(!pChangedEvent.isNull());
2908
2909 GuestSessionStatus_T sessionStatus;
2910 pChangedEvent->COMGETTER(Status)(&sessionStatus);
2911 if (pSessionStatus)
2912 *pSessionStatus = sessionStatus;
2913
2914 ComPtr<IVirtualBoxErrorInfo> errorInfo;
2915 HRESULT hr = pChangedEvent->COMGETTER(Error)(errorInfo.asOutParam());
2916 ComAssertComRC(hr);
2917
2918 LONG lGuestRc;
2919 hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
2920 ComAssertComRC(hr);
2921 if (RT_FAILURE((int)lGuestRc))
2922 vrc = VERR_GSTCTL_GUEST_ERROR;
2923 if (prcGuest)
2924 *prcGuest = (int)lGuestRc;
2925
2926 LogFlowThisFunc(("Status changed event for session ID=%RU32, new status is: %RU32 (%Rrc)\n",
2927 mData.mSession.mID, sessionStatus,
2928 RT_SUCCESS((int)lGuestRc) ? VINF_SUCCESS : (int)lGuestRc));
2929 }
2930 /* waitForEvent may also return VERR_GSTCTL_GUEST_ERROR like we do above, so make prcGuest is set. */
2931 else if (vrc == VERR_GSTCTL_GUEST_ERROR && prcGuest)
2932 *prcGuest = pEvent->GuestResult();
2933 Assert(vrc != VERR_GSTCTL_GUEST_ERROR || !prcGuest || *prcGuest != (int)0xcccccccc);
2934
2935 LogFlowFuncLeaveRC(vrc);
2936 return vrc;
2937}
2938
2939// implementation of public methods
2940/////////////////////////////////////////////////////////////////////////////
2941
2942HRESULT GuestSession::close()
2943{
2944 AutoCaller autoCaller(this);
2945 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2946
2947 LogFlowThisFuncEnter();
2948
2949 /* Note: Don't check if the session is ready via i_isReadyExternal() here;
2950 * the session (already) could be in a stopped / aborted state. */
2951
2952 /* Close session on guest. */
2953 int rcGuest = VINF_SUCCESS;
2954 int vrc = i_closeSession(0 /* Flags */, 30 * 1000 /* Timeout */, &rcGuest);
2955 /* On failure don't return here, instead do all the cleanup
2956 * work first and then return an error. */
2957
2958 /* Remove ourselves from the session list. */
2959 AssertPtr(mParent);
2960 int vrc2 = mParent->i_sessionRemove(mData.mSession.mID);
2961 if (vrc2 == VERR_NOT_FOUND) /* Not finding the session anymore isn't critical. */
2962 vrc2 = VINF_SUCCESS;
2963
2964 if (RT_SUCCESS(vrc))
2965 vrc = vrc2;
2966
2967 LogFlowThisFunc(("Returning rc=%Rrc, rcGuest=%Rrc\n", vrc, rcGuest));
2968
2969 if (RT_FAILURE(vrc))
2970 {
2971 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2972 return GuestSession::i_setErrorExternal(this, rcGuest);
2973 return setError(VBOX_E_IPRT_ERROR, tr("Closing guest session failed with %Rrc"), vrc);
2974 }
2975
2976 return S_OK;
2977}
2978
2979HRESULT GuestSession::fileCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2980 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
2981{
2982 RT_NOREF(aSource, aDestination, aFlags, aProgress);
2983 ReturnComNotImplemented();
2984}
2985
2986HRESULT GuestSession::fileCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2987 const std::vector<FileCopyFlag_T> &aFlags,
2988 ComPtr<IProgress> &aProgress)
2989{
2990 AutoCaller autoCaller(this);
2991 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2992
2993 uint32_t fFlags = FileCopyFlag_None;
2994 if (aFlags.size())
2995 {
2996 for (size_t i = 0; i < aFlags.size(); i++)
2997 fFlags |= aFlags[i];
2998 /** @todo r=bird: Please reject unknown flags. */
2999 }
3000
3001 GuestSessionFsSourceSet SourceSet;
3002
3003 GuestSessionFsSourceSpec source;
3004 source.strSource = aSource;
3005 source.enmType = FsObjType_File;
3006 source.enmPathStyle = i_getPathStyle();
3007 source.fDryRun = false; /** @todo Implement support for a dry run. */
3008 source.Type.File.fCopyFlags = (FileCopyFlag_T)fFlags;
3009
3010 SourceSet.push_back(source);
3011
3012 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3013}
3014
3015HRESULT GuestSession::fileCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3016 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3017{
3018 AutoCaller autoCaller(this);
3019 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3020
3021 uint32_t fFlags = FileCopyFlag_None;
3022 if (aFlags.size())
3023 {
3024 for (size_t i = 0; i < aFlags.size(); i++)
3025 fFlags |= aFlags[i];
3026 /** @todo r=bird: Please reject unknown flags. */
3027 }
3028
3029 GuestSessionFsSourceSet SourceSet;
3030
3031 /** @todo r=bird: The GuestSessionFsSourceSpec constructor does not zero the
3032 * members you aren't setting here and there are no hints about "input"
3033 * vs "task" members, so you have me worrying about using random stack by
3034 * accident somewhere... For instance Type.File.phFile sure sounds like
3035 * an input field and thus a disaster waiting to happen. */
3036 GuestSessionFsSourceSpec source;
3037 source.strSource = aSource;
3038 source.enmType = FsObjType_File;
3039 source.enmPathStyle = i_getPathStyle();
3040 source.fDryRun = false; /** @todo Implement support for a dry run. */
3041 source.Type.File.fCopyFlags = (FileCopyFlag_T)fFlags;
3042
3043 SourceSet.push_back(source);
3044
3045 return i_copyToGuest(SourceSet, aDestination, aProgress);
3046}
3047
3048HRESULT GuestSession::copyFromGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
3049 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
3050 ComPtr<IProgress> &aProgress)
3051{
3052 AutoCaller autoCaller(this);
3053 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3054
3055 const size_t cSources = aSources.size();
3056 if ( (aFilters.size() && aFilters.size() != cSources)
3057 || (aFlags.size() && aFlags.size() != cSources))
3058 {
3059 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
3060 }
3061
3062 GuestSessionFsSourceSet SourceSet;
3063
3064 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
3065 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
3066 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
3067
3068 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
3069 const bool fFollowSymlinks = true; /** @todo Ditto. */
3070
3071 while (itSource != aSources.end())
3072 {
3073 GuestFsObjData objData;
3074 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3075 int vrc = i_fsQueryInfo(*(itSource), fFollowSymlinks, objData, &rcGuest);
3076 if ( RT_FAILURE(vrc)
3077 && !fContinueOnErrors)
3078 {
3079 if (GuestProcess::i_isGuestError(vrc))
3080 return setError(E_FAIL, tr("Unable to query type for source '%s': %s"), (*itSource).c_str(),
3081 GuestProcess::i_guestErrorToString(rcGuest).c_str());
3082 else
3083 return setError(E_FAIL, tr("Unable to query type for source '%s' (%Rrc)"), (*itSource).c_str(), vrc);
3084 }
3085
3086 Utf8Str strFlags;
3087 if (itFlags != aFlags.end())
3088 {
3089 strFlags = *itFlags;
3090 ++itFlags;
3091 }
3092
3093 Utf8Str strFilter;
3094 if (itFilter != aFilters.end())
3095 {
3096 strFilter = *itFilter;
3097 ++itFilter;
3098 }
3099
3100 GuestSessionFsSourceSpec source;
3101 source.strSource = *itSource;
3102 source.strFilter = strFilter;
3103 source.enmType = objData.mType;
3104 source.enmPathStyle = i_getPathStyle();
3105 source.fDryRun = false; /** @todo Implement support for a dry run. */
3106
3107 HRESULT hrc;
3108 if (source.enmType == FsObjType_Directory)
3109 {
3110 hrc = GuestSession::i_directoryCopyFlagFromStr(strFlags, &source.Type.Dir.fCopyFlags);
3111 source.Type.Dir.fRecursive = true; /* Implicit. */
3112 }
3113 else if (source.enmType == FsObjType_File)
3114 hrc = GuestSession::i_fileCopyFlagFromStr(strFlags, &source.Type.File.fCopyFlags);
3115 else
3116 return setError(E_INVALIDARG, tr("Source type %d invalid / not supported"), source.enmType);
3117 if (FAILED(hrc))
3118 return hrc;
3119
3120 SourceSet.push_back(source);
3121
3122 ++itSource;
3123 }
3124
3125 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3126}
3127
3128HRESULT GuestSession::copyToGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
3129 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
3130 ComPtr<IProgress> &aProgress)
3131{
3132 AutoCaller autoCaller(this);
3133 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3134
3135 const size_t cSources = aSources.size();
3136 if ( (aFilters.size() && aFilters.size() != cSources)
3137 || (aFlags.size() && aFlags.size() != cSources))
3138 {
3139 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
3140 }
3141
3142 GuestSessionFsSourceSet SourceSet;
3143
3144 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
3145 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
3146 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
3147
3148 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
3149
3150 while (itSource != aSources.end())
3151 {
3152 RTFSOBJINFO objInfo;
3153 int vrc = RTPathQueryInfo((*itSource).c_str(), &objInfo, RTFSOBJATTRADD_NOTHING);
3154 if ( RT_FAILURE(vrc)
3155 && !fContinueOnErrors)
3156 {
3157 return setError(E_FAIL, tr("Unable to query type for source '%s' (%Rrc)"), (*itSource).c_str(), vrc);
3158 }
3159
3160 Utf8Str strFlags;
3161 if (itFlags != aFlags.end())
3162 {
3163 strFlags = *itFlags;
3164 ++itFlags;
3165 }
3166
3167 Utf8Str strFilter;
3168 if (itFilter != aFilters.end())
3169 {
3170 strFilter = *itFilter;
3171 ++itFilter;
3172 }
3173
3174 GuestSessionFsSourceSpec source;
3175 source.strSource = *itSource;
3176 source.strFilter = strFilter;
3177 source.enmType = GuestBase::fileModeToFsObjType(objInfo.Attr.fMode);
3178 source.enmPathStyle = i_getPathStyle();
3179 source.fDryRun = false; /** @todo Implement support for a dry run. */
3180
3181 HRESULT hrc;
3182 if (source.enmType == FsObjType_Directory)
3183 {
3184 hrc = GuestSession::i_directoryCopyFlagFromStr(strFlags, &source.Type.Dir.fCopyFlags);
3185 source.Type.Dir.fFollowSymlinks = true; /** @todo Add a flag for that in DirectoryCopyFlag_T. Later. */
3186 source.Type.Dir.fRecursive = true; /* Implicit. */
3187 }
3188 else if (source.enmType == FsObjType_File)
3189 hrc = GuestSession::i_fileCopyFlagFromStr(strFlags, &source.Type.File.fCopyFlags);
3190 else
3191 return setError(E_INVALIDARG, tr("Source type %d invalid / not supported"), source.enmType);
3192 if (FAILED(hrc))
3193 return hrc;
3194
3195 SourceSet.push_back(source);
3196
3197 ++itSource;
3198 }
3199
3200 return i_copyToGuest(SourceSet, aDestination, aProgress);
3201}
3202
3203HRESULT GuestSession::directoryCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3204 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3205{
3206 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3207 ReturnComNotImplemented();
3208}
3209
3210HRESULT GuestSession::directoryCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3211 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3212{
3213 AutoCaller autoCaller(this);
3214 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3215
3216 uint32_t fFlags = DirectoryCopyFlag_None;
3217 if (aFlags.size())
3218 {
3219 for (size_t i = 0; i < aFlags.size(); i++)
3220 fFlags |= aFlags[i];
3221 /** @todo r=bird: Please reject unknown flags. */
3222 }
3223
3224 GuestSessionFsSourceSet SourceSet;
3225
3226 GuestSessionFsSourceSpec source;
3227 source.strSource = aSource;
3228 source.enmType = FsObjType_Directory;
3229 source.enmPathStyle = i_getPathStyle();
3230 source.fDryRun = false; /** @todo Implement support for a dry run. */
3231 source.Type.Dir.fCopyFlags = (DirectoryCopyFlag_T)fFlags;
3232 source.Type.Dir.fRecursive = true; /* Implicit. */
3233
3234 SourceSet.push_back(source);
3235
3236 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3237}
3238
3239HRESULT GuestSession::directoryCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3240 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3241{
3242 AutoCaller autoCaller(this);
3243 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3244
3245 uint32_t fFlags = DirectoryCopyFlag_None;
3246 if (aFlags.size())
3247 {
3248 for (size_t i = 0; i < aFlags.size(); i++)
3249 fFlags |= aFlags[i];
3250 /** @todo r=bird: Please reject unknown flags. */
3251 }
3252
3253 GuestSessionFsSourceSet SourceSet;
3254
3255 GuestSessionFsSourceSpec source;
3256 source.strSource = aSource;
3257 source.enmType = FsObjType_Directory;
3258 source.enmPathStyle = i_getPathStyle();
3259 source.fDryRun = false; /** @todo Implement support for a dry run. */
3260 source.Type.Dir.fCopyFlags = (DirectoryCopyFlag_T)fFlags;
3261 source.Type.Dir.fFollowSymlinks = true; /** @todo Add a flag for that in DirectoryCopyFlag_T. Later. */
3262 source.Type.Dir.fRecursive = true; /* Implicit. */
3263
3264 SourceSet.push_back(source);
3265
3266 return i_copyToGuest(SourceSet, aDestination, aProgress);
3267}
3268
3269HRESULT GuestSession::directoryCreate(const com::Utf8Str &aPath, ULONG aMode,
3270 const std::vector<DirectoryCreateFlag_T> &aFlags)
3271{
3272 AutoCaller autoCaller(this);
3273 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3274
3275 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3276 return setError(E_INVALIDARG, tr("No directory to create specified"));
3277
3278 uint32_t fFlags = DirectoryCreateFlag_None;
3279 if (aFlags.size())
3280 {
3281 for (size_t i = 0; i < aFlags.size(); i++)
3282 fFlags |= aFlags[i];
3283
3284 /** @todo r=bird: This should be: if (fFlags & ~DirectoryCreateFlag_Parents) */
3285 if (fFlags)
3286 if (!(fFlags & DirectoryCreateFlag_Parents))
3287 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), fFlags);
3288 }
3289
3290 HRESULT hrc = i_isStartedExternal();
3291 if (FAILED(hrc))
3292 return hrc;
3293
3294 LogFlowThisFuncEnter();
3295
3296 ComObjPtr <GuestDirectory> pDirectory; int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3297 int vrc = i_directoryCreate(aPath, (uint32_t)aMode, fFlags, &rcGuest);
3298 if (RT_FAILURE(vrc))
3299 {
3300 if (GuestProcess::i_isGuestError(vrc))
3301 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest,
3302 tr("Directory creation failed: %s"), GuestDirectory::i_guestErrorToString(rcGuest).c_str());
3303 else
3304 {
3305 switch (vrc)
3306 {
3307 case VERR_INVALID_PARAMETER:
3308 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: Invalid parameters given"));
3309 break;
3310
3311 case VERR_BROKEN_PIPE:
3312 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: Unexpectedly aborted"));
3313 break;
3314
3315 default:
3316 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: %Rrc"), vrc);
3317 break;
3318 }
3319 }
3320 }
3321
3322 return hrc;
3323}
3324
3325HRESULT GuestSession::directoryCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath,
3326 BOOL aSecure, com::Utf8Str &aDirectory)
3327{
3328 RT_NOREF(aMode, aSecure); /** @todo r=bird: WTF? */
3329
3330 AutoCaller autoCaller(this);
3331 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3332
3333 if (RT_UNLIKELY((aTemplateName.c_str()) == NULL || *(aTemplateName.c_str()) == '\0'))
3334 return setError(E_INVALIDARG, tr("No template specified"));
3335 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3336 return setError(E_INVALIDARG, tr("No directory name specified"));
3337
3338 HRESULT hrc = i_isStartedExternal();
3339 if (FAILED(hrc))
3340 return hrc;
3341
3342 LogFlowThisFuncEnter();
3343
3344 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3345 int vrc = i_fsCreateTemp(aTemplateName, aPath, true /* Directory */, aDirectory, &rcGuest);
3346 if (!RT_SUCCESS(vrc))
3347 {
3348 switch (vrc)
3349 {
3350 case VERR_GSTCTL_GUEST_ERROR:
3351 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3352 break;
3353
3354 default:
3355 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Temporary directory creation \"%s\" with template \"%s\" failed: %Rrc"),
3356 aPath.c_str(), aTemplateName.c_str(), vrc);
3357 break;
3358 }
3359 }
3360
3361 return hrc;
3362}
3363
3364HRESULT GuestSession::directoryExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3365{
3366 AutoCaller autoCaller(this); /** @todo r=bird: GuestSessionWrap.cpp does already, doesn't it? */
3367 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3368
3369 if (RT_UNLIKELY(aPath.isEmpty()))
3370 return setError(E_INVALIDARG, tr("Empty path"));
3371
3372 HRESULT hrc = i_isStartedExternal();
3373 if (FAILED(hrc))
3374 return hrc;
3375
3376 LogFlowThisFuncEnter();
3377
3378 GuestFsObjData objData;
3379 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3380 /** @todo r=bird: Please look at i_directoryQueryInfo() and explain why there
3381 * is an extra FsObjType_Directory check here...
3382 *
3383 * Looks a lot like you wanted to replicate the RTDirExists behavior, but when
3384 * refactoring in i_directoryQueryInfo you lost overview here. One problem
3385 * could be that the documention is VirtualBox.xidl does not mention what
3386 * happens when the path leads to a file system object that isn't a
3387 * directory.
3388 *
3389 * Fix the documention and behaviour so it works like RTDirExists and
3390 * RTFileExists. */
3391 int vrc = i_directoryQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3392 if (RT_SUCCESS(vrc))
3393 *aExists = objData.mType == FsObjType_Directory;
3394 else
3395 {
3396 switch (vrc)
3397 {
3398 case VERR_GSTCTL_GUEST_ERROR:
3399 {
3400 switch (rcGuest)
3401 {
3402 case VERR_PATH_NOT_FOUND:
3403 *aExists = FALSE;
3404 break;
3405 default:
3406 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying directory existence \"%s\" failed: %s"),
3407 aPath.c_str(), GuestProcess::i_guestErrorToString(rcGuest).c_str());
3408 break;
3409 }
3410 break;
3411 }
3412
3413 default:
3414 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying directory existence \"%s\" failed: %Rrc"),
3415 aPath.c_str(), vrc);
3416 break;
3417 }
3418 }
3419
3420 return hrc;
3421}
3422
3423HRESULT GuestSession::directoryOpen(const com::Utf8Str &aPath, const com::Utf8Str &aFilter,
3424 const std::vector<DirectoryOpenFlag_T> &aFlags, ComPtr<IGuestDirectory> &aDirectory)
3425{
3426 AutoCaller autoCaller(this);
3427 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3428
3429 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3430 return setError(E_INVALIDARG, tr("No directory to open specified"));
3431 if (RT_UNLIKELY((aFilter.c_str()) != NULL && *(aFilter.c_str()) != '\0'))
3432 return setError(E_INVALIDARG, tr("Directory filters are not implemented yet"));
3433
3434 uint32_t fFlags = DirectoryOpenFlag_None;
3435 if (aFlags.size())
3436 {
3437 for (size_t i = 0; i < aFlags.size(); i++)
3438 fFlags |= aFlags[i];
3439
3440 if (fFlags)
3441 return setError(E_INVALIDARG, tr("Open flags (%#x) not implemented yet"), fFlags);
3442 }
3443
3444 HRESULT hrc = i_isStartedExternal();
3445 if (FAILED(hrc))
3446 return hrc;
3447
3448 LogFlowThisFuncEnter();
3449
3450 GuestDirectoryOpenInfo openInfo;
3451 openInfo.mPath = aPath;
3452 openInfo.mFilter = aFilter;
3453 openInfo.mFlags = fFlags;
3454
3455 ComObjPtr<GuestDirectory> pDirectory; int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3456 int vrc = i_directoryOpen(openInfo, pDirectory, &rcGuest);
3457 if (RT_SUCCESS(vrc))
3458 {
3459 /* Return directory object to the caller. */
3460 hrc = pDirectory.queryInterfaceTo(aDirectory.asOutParam());
3461 }
3462 else
3463 {
3464 switch (vrc)
3465 {
3466 case VERR_INVALID_PARAMETER:
3467 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening directory \"%s\" failed; invalid parameters given"),
3468 aPath.c_str());
3469 break;
3470
3471 case VERR_GSTCTL_GUEST_ERROR:
3472 hrc = GuestDirectory::i_setErrorExternal(this, rcGuest);
3473 break;
3474
3475 default:
3476 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3477 break;
3478 }
3479 }
3480
3481 return hrc;
3482}
3483
3484HRESULT GuestSession::directoryRemove(const com::Utf8Str &aPath)
3485{
3486 AutoCaller autoCaller(this);
3487 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3488
3489 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3490 return setError(E_INVALIDARG, tr("No directory to remove specified"));
3491
3492 HRESULT hrc = i_isStartedExternal();
3493 if (FAILED(hrc))
3494 return hrc;
3495
3496 LogFlowThisFuncEnter();
3497
3498 /* No flags; only remove the directory when empty. */
3499 uint32_t fFlags = DIRREMOVEREC_FLAG_NONE;
3500
3501 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3502 int vrc = i_directoryRemove(aPath, fFlags, &rcGuest);
3503 if (RT_FAILURE(vrc))
3504 {
3505 switch (vrc)
3506 {
3507 case VERR_NOT_SUPPORTED:
3508 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3509 tr("Handling removing guest directories not supported by installed Guest Additions"));
3510 break;
3511
3512 case VERR_GSTCTL_GUEST_ERROR:
3513 hrc = GuestDirectory::i_setErrorExternal(this, rcGuest);
3514 break;
3515
3516 default:
3517 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing guest directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3518 break;
3519 }
3520 }
3521
3522 return hrc;
3523}
3524
3525HRESULT GuestSession::directoryRemoveRecursive(const com::Utf8Str &aPath, const std::vector<DirectoryRemoveRecFlag_T> &aFlags,
3526 ComPtr<IProgress> &aProgress)
3527{
3528 AutoCaller autoCaller(this);
3529 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3530
3531 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3532 return setError(E_INVALIDARG, tr("No directory to remove recursively specified"));
3533
3534 /* By default only delete empty directory structures, e.g. the operation will abort if there are
3535 * directories which are not empty. */
3536 uint32_t fFlags = DIRREMOVEREC_FLAG_RECURSIVE;
3537 if (aFlags.size())
3538 {
3539 for (size_t i = 0; i < aFlags.size(); i++)
3540 {
3541 switch (aFlags[i])
3542 {
3543 case DirectoryRemoveRecFlag_None: /* Skip. */
3544 continue;
3545
3546 case DirectoryRemoveRecFlag_ContentAndDir:
3547 fFlags |= DIRREMOVEREC_FLAG_CONTENT_AND_DIR;
3548 break;
3549
3550 case DirectoryRemoveRecFlag_ContentOnly:
3551 fFlags |= DIRREMOVEREC_FLAG_CONTENT_ONLY;
3552 break;
3553
3554 default:
3555 return setError(E_INVALIDARG, tr("Invalid flags specified"));
3556 }
3557 }
3558 }
3559
3560 HRESULT hrc = i_isStartedExternal();
3561 if (FAILED(hrc))
3562 return hrc;
3563
3564 LogFlowThisFuncEnter();
3565
3566 ComObjPtr<Progress> pProgress;
3567 hrc = pProgress.createObject();
3568 if (SUCCEEDED(hrc))
3569 hrc = pProgress->init(static_cast<IGuestSession *>(this),
3570 Bstr(tr("Removing guest directory")).raw(),
3571 TRUE /*aCancelable*/);
3572 if (FAILED(hrc))
3573 return hrc;
3574
3575 /* Note: At the moment we don't supply progress information while
3576 * deleting a guest directory recursively. So just complete
3577 * the progress object right now. */
3578 /** @todo Implement progress reporting on guest directory deletion! */
3579 hrc = pProgress->i_notifyComplete(S_OK);
3580 if (FAILED(hrc))
3581 return hrc;
3582
3583 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3584 int vrc = i_directoryRemove(aPath, fFlags, &rcGuest);
3585 if (RT_FAILURE(vrc))
3586 {
3587 switch (vrc)
3588 {
3589 case VERR_NOT_SUPPORTED:
3590 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3591 tr("Handling removing guest directories recursively not supported by installed Guest Additions"));
3592 break;
3593
3594 case VERR_GSTCTL_GUEST_ERROR:
3595 hrc = GuestFile::i_setErrorExternal(this, rcGuest);
3596 break;
3597
3598 default:
3599 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Recursively removing guest directory \"%s\" failed: %Rrc"),
3600 aPath.c_str(), vrc);
3601 break;
3602 }
3603 }
3604 else
3605 {
3606 pProgress.queryInterfaceTo(aProgress.asOutParam());
3607 }
3608
3609 return hrc;
3610}
3611
3612HRESULT GuestSession::environmentScheduleSet(const com::Utf8Str &aName, const com::Utf8Str &aValue)
3613{
3614 LogFlowThisFuncEnter();
3615 int vrc;
3616 {
3617 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3618 vrc = mData.mEnvironmentChanges.setVariable(aName, aValue);
3619 }
3620 HRESULT hrc;
3621 if (RT_SUCCESS(vrc))
3622 hrc = S_OK;
3623 else if (vrc == VERR_ENV_INVALID_VAR_NAME)
3624 hrc = setError(E_INVALIDARG, tr("Invalid environment variable name '%s'"), aName.c_str());
3625 else
3626 hrc = setErrorVrc(vrc, tr("Failed to schedule setting environment variable '%s' to '%s'"), aName.c_str(), aValue.c_str());
3627
3628 LogFlowThisFuncLeave();
3629 return hrc;
3630}
3631
3632HRESULT GuestSession::environmentScheduleUnset(const com::Utf8Str &aName)
3633{
3634 LogFlowThisFuncEnter();
3635 int vrc;
3636 {
3637 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3638 vrc = mData.mEnvironmentChanges.unsetVariable(aName);
3639 }
3640 HRESULT hrc;
3641 if (RT_SUCCESS(vrc))
3642 hrc = S_OK;
3643 else if (vrc == VERR_ENV_INVALID_VAR_NAME)
3644 hrc = setError(E_INVALIDARG, tr("Invalid environment variable name '%s'"), aName.c_str());
3645 else
3646 hrc = setErrorVrc(vrc, tr("Failed to schedule unsetting environment variable '%s'"), aName.c_str());
3647
3648 LogFlowThisFuncLeave();
3649 return hrc;
3650}
3651
3652HRESULT GuestSession::environmentGetBaseVariable(const com::Utf8Str &aName, com::Utf8Str &aValue)
3653{
3654 LogFlowThisFuncEnter();
3655 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3656
3657 HRESULT hrc;
3658 if (mData.mpBaseEnvironment)
3659 {
3660 int vrc = mData.mpBaseEnvironment->getVariable(aName, &aValue);
3661 if (RT_SUCCESS(vrc))
3662 hrc = S_OK;
3663 else if (vrc == VERR_ENV_INVALID_VAR_NAME)
3664 hrc = setError(E_INVALIDARG, tr("Invalid environment variable name '%s'"), aName.c_str());
3665 else
3666 hrc = setErrorVrc(vrc);
3667 }
3668 else if (mData.mProtocolVersion < 99999)
3669 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
3670 else
3671 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
3672
3673 LogFlowThisFuncLeave();
3674 return hrc;
3675}
3676
3677HRESULT GuestSession::environmentDoesBaseVariableExist(const com::Utf8Str &aName, BOOL *aExists)
3678{
3679 LogFlowThisFuncEnter();
3680 *aExists = FALSE;
3681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3682
3683 HRESULT hrc;
3684 if (mData.mpBaseEnvironment)
3685 {
3686 hrc = S_OK;
3687 *aExists = mData.mpBaseEnvironment->doesVariableExist(aName);
3688 }
3689 else if (mData.mProtocolVersion < 99999)
3690 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
3691 else
3692 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
3693
3694 LogFlowThisFuncLeave();
3695 return hrc;
3696}
3697
3698HRESULT GuestSession::fileCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath, BOOL aSecure,
3699 ComPtr<IGuestFile> &aFile)
3700{
3701 RT_NOREF(aTemplateName, aMode, aPath, aSecure, aFile);
3702 ReturnComNotImplemented();
3703}
3704
3705HRESULT GuestSession::fileExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3706{
3707 AutoCaller autoCaller(this);
3708 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3709
3710 /* By default we return non-existent. */
3711 *aExists = FALSE;
3712
3713 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3714 return S_OK;
3715
3716 HRESULT hrc = i_isStartedExternal();
3717 if (FAILED(hrc))
3718 return hrc;
3719
3720 LogFlowThisFuncEnter();
3721
3722 GuestFsObjData objData; int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3723 int vrc = i_fileQueryInfo(aPath, RT_BOOL(aFollowSymlinks), objData, &rcGuest);
3724 if (RT_SUCCESS(vrc))
3725 {
3726 *aExists = TRUE;
3727 return S_OK;
3728 }
3729
3730 switch (vrc)
3731 {
3732 case VERR_GSTCTL_GUEST_ERROR:
3733 {
3734 switch (rcGuest)
3735 {
3736 case VERR_PATH_NOT_FOUND:
3737 RT_FALL_THROUGH();
3738 case VERR_FILE_NOT_FOUND:
3739 break;
3740
3741 default:
3742 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3743 break;
3744 }
3745
3746 break;
3747 }
3748
3749 case VERR_NOT_A_FILE:
3750 break;
3751
3752 default:
3753 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying file information for \"%s\" failed: %Rrc"),
3754 aPath.c_str(), vrc);
3755 break;
3756 }
3757
3758 return hrc;
3759}
3760
3761HRESULT GuestSession::fileOpen(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3762 ULONG aCreationMode, ComPtr<IGuestFile> &aFile)
3763{
3764 LogFlowThisFuncEnter();
3765
3766 const std::vector<FileOpenExFlag_T> EmptyFlags;
3767 return fileOpenEx(aPath, aAccessMode, aOpenAction, FileSharingMode_All, aCreationMode, EmptyFlags, aFile);
3768}
3769
3770HRESULT GuestSession::fileOpenEx(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3771 FileSharingMode_T aSharingMode, ULONG aCreationMode,
3772 const std::vector<FileOpenExFlag_T> &aFlags, ComPtr<IGuestFile> &aFile)
3773{
3774 AutoCaller autoCaller(this);
3775 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3776
3777 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3778 return setError(E_INVALIDARG, tr("No file to open specified"));
3779
3780 HRESULT hrc = i_isStartedExternal();
3781 if (FAILED(hrc))
3782 return hrc;
3783
3784 LogFlowThisFuncEnter();
3785
3786 GuestFileOpenInfo openInfo;
3787 openInfo.mFilename = aPath;
3788 openInfo.mCreationMode = aCreationMode;
3789
3790 /* Validate aAccessMode. */
3791 switch (aAccessMode)
3792 {
3793 case FileAccessMode_ReadOnly:
3794 RT_FALL_THRU();
3795 case FileAccessMode_WriteOnly:
3796 RT_FALL_THRU();
3797 case FileAccessMode_ReadWrite:
3798 openInfo.mAccessMode = aAccessMode;
3799 break;
3800 case FileAccessMode_AppendOnly:
3801 RT_FALL_THRU();
3802 case FileAccessMode_AppendRead:
3803 return setError(E_NOTIMPL, tr("Append access modes are not yet implemented"));
3804 default:
3805 return setError(E_INVALIDARG, tr("Unknown FileAccessMode value %u (%#x)"), aAccessMode, aAccessMode);
3806 }
3807
3808 /* Validate aOpenAction to the old format. */
3809 switch (aOpenAction)
3810 {
3811 case FileOpenAction_OpenExisting:
3812 RT_FALL_THRU();
3813 case FileOpenAction_OpenOrCreate:
3814 RT_FALL_THRU();
3815 case FileOpenAction_CreateNew:
3816 RT_FALL_THRU();
3817 case FileOpenAction_CreateOrReplace:
3818 RT_FALL_THRU();
3819 case FileOpenAction_OpenExistingTruncated:
3820 RT_FALL_THRU();
3821 case FileOpenAction_AppendOrCreate:
3822 openInfo.mOpenAction = aOpenAction;
3823 break;
3824 default:
3825 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3826 }
3827
3828 /* Validate aSharingMode. */
3829 switch (aSharingMode)
3830 {
3831 case FileSharingMode_All:
3832 openInfo.mSharingMode = aSharingMode;
3833 break;
3834 case FileSharingMode_Read:
3835 case FileSharingMode_Write:
3836 case FileSharingMode_ReadWrite:
3837 case FileSharingMode_Delete:
3838 case FileSharingMode_ReadDelete:
3839 case FileSharingMode_WriteDelete:
3840 return setError(E_NOTIMPL, tr("Only FileSharingMode_All is currently implemented"));
3841
3842 default:
3843 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3844 }
3845
3846 /* Combine and validate flags. */
3847 uint32_t fOpenEx = 0;
3848 for (size_t i = 0; i < aFlags.size(); i++)
3849 fOpenEx = aFlags[i];
3850 if (fOpenEx)
3851 return setError(E_INVALIDARG, tr("Unsupported FileOpenExFlag value(s) in aFlags (%#x)"), fOpenEx);
3852 openInfo.mfOpenEx = fOpenEx;
3853
3854 ComObjPtr <GuestFile> pFile;
3855 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3856 int vrc = i_fileOpenEx(aPath, aAccessMode, aOpenAction, aSharingMode, aCreationMode, aFlags, pFile, &rcGuest);
3857 if (RT_SUCCESS(vrc))
3858 /* Return directory object to the caller. */
3859 hrc = pFile.queryInterfaceTo(aFile.asOutParam());
3860 else
3861 {
3862 switch (vrc)
3863 {
3864 case VERR_NOT_SUPPORTED:
3865 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3866 tr("Handling guest files not supported by installed Guest Additions"));
3867 break;
3868
3869 case VERR_GSTCTL_GUEST_ERROR:
3870 hrc = GuestFile::i_setErrorExternal(this, rcGuest);
3871 break;
3872
3873 default:
3874 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3875 break;
3876 }
3877 }
3878
3879 return hrc;
3880}
3881
3882HRESULT GuestSession::fileQuerySize(const com::Utf8Str &aPath, BOOL aFollowSymlinks, LONG64 *aSize)
3883{
3884 AutoCaller autoCaller(this);
3885 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3886
3887 if (aPath.isEmpty())
3888 return setError(E_INVALIDARG, tr("No path specified"));
3889
3890 HRESULT hrc = i_isStartedExternal();
3891 if (FAILED(hrc))
3892 return hrc;
3893
3894 int64_t llSize; int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3895 int vrc = i_fileQuerySize(aPath, aFollowSymlinks != FALSE, &llSize, &rcGuest);
3896 if (RT_SUCCESS(vrc))
3897 {
3898 *aSize = llSize;
3899 }
3900 else
3901 {
3902 if (GuestProcess::i_isGuestError(vrc))
3903 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3904 else
3905 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying file size failed: %Rrc"), vrc);
3906 }
3907
3908 return hrc;
3909}
3910
3911HRESULT GuestSession::fsObjExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3912{
3913 AutoCaller autoCaller(this);
3914 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3915
3916 if (aPath.isEmpty())
3917 return setError(E_INVALIDARG, tr("No path specified"));
3918
3919 HRESULT hrc = i_isStartedExternal();
3920 if (FAILED(hrc))
3921 return hrc;
3922
3923 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
3924
3925 *aExists = false;
3926
3927 GuestFsObjData objData;
3928 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3929 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3930 if (RT_SUCCESS(vrc))
3931 {
3932 *aExists = TRUE;
3933 }
3934 else
3935 {
3936 if (GuestProcess::i_isGuestError(vrc))
3937 {
3938 if ( rcGuest == VERR_NOT_A_FILE
3939 || rcGuest == VERR_PATH_NOT_FOUND
3940 || rcGuest == VERR_FILE_NOT_FOUND
3941 || rcGuest == VERR_INVALID_NAME)
3942 {
3943 hrc = S_OK; /* Ignore these vrc values. */
3944 }
3945 else
3946 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3947 }
3948 else
3949 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3950 }
3951
3952 return hrc;
3953}
3954
3955HRESULT GuestSession::fsObjQueryInfo(const com::Utf8Str &aPath, BOOL aFollowSymlinks, ComPtr<IGuestFsObjInfo> &aInfo)
3956{
3957 AutoCaller autoCaller(this);
3958 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3959
3960 if (aPath.isEmpty())
3961 return setError(E_INVALIDARG, tr("No path specified"));
3962
3963 HRESULT hrc = i_isStartedExternal();
3964 if (FAILED(hrc))
3965 return hrc;
3966
3967 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
3968
3969 GuestFsObjData Info; int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3970 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, Info, &rcGuest);
3971 if (RT_SUCCESS(vrc))
3972 {
3973 ComObjPtr<GuestFsObjInfo> ptrFsObjInfo;
3974 hrc = ptrFsObjInfo.createObject();
3975 if (SUCCEEDED(hrc))
3976 {
3977 vrc = ptrFsObjInfo->init(Info);
3978 if (RT_SUCCESS(vrc))
3979 hrc = ptrFsObjInfo.queryInterfaceTo(aInfo.asOutParam());
3980 else
3981 hrc = setErrorVrc(vrc);
3982 }
3983 }
3984 else
3985 {
3986 if (GuestProcess::i_isGuestError(vrc))
3987 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3988 else
3989 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3990 }
3991
3992 return hrc;
3993}
3994
3995HRESULT GuestSession::fsObjRemove(const com::Utf8Str &aPath)
3996{
3997 AutoCaller autoCaller(this);
3998 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3999
4000 if (RT_UNLIKELY(aPath.isEmpty()))
4001 return setError(E_INVALIDARG, tr("No path specified"));
4002
4003 HRESULT hrc = i_isStartedExternal();
4004 if (FAILED(hrc))
4005 return hrc;
4006
4007 LogFlowThisFunc(("aPath=%s\n", aPath.c_str()));
4008
4009 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4010 int vrc = i_fileRemove(aPath, &rcGuest);
4011 if (RT_FAILURE(vrc))
4012 {
4013 if (GuestProcess::i_isGuestError(vrc))
4014 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
4015 else
4016 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4017 }
4018
4019 return hrc;
4020}
4021
4022HRESULT GuestSession::fsObjRemoveArray(const std::vector<com::Utf8Str> &aPaths, ComPtr<IProgress> &aProgress)
4023{
4024 AutoCaller autoCaller(this);
4025 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4026
4027 RT_NOREF(aPaths, aProgress);
4028
4029 return E_NOTIMPL;
4030}
4031
4032HRESULT GuestSession::fsObjRename(const com::Utf8Str &aSource,
4033 const com::Utf8Str &aDestination,
4034 const std::vector<FsObjRenameFlag_T> &aFlags)
4035{
4036 AutoCaller autoCaller(this);
4037 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4038
4039 if (RT_UNLIKELY(aSource.isEmpty()))
4040 return setError(E_INVALIDARG, tr("No source path specified"));
4041
4042 if (RT_UNLIKELY(aDestination.isEmpty()))
4043 return setError(E_INVALIDARG, tr("No destination path specified"));
4044
4045 HRESULT hrc = i_isStartedExternal();
4046 if (FAILED(hrc))
4047 return hrc;
4048
4049 /* Combine, validate and convert flags. */
4050 uint32_t fApiFlags = 0;
4051 for (size_t i = 0; i < aFlags.size(); i++)
4052 fApiFlags |= aFlags[i];
4053 if (fApiFlags & ~((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace))
4054 return setError(E_INVALIDARG, tr("Unknown rename flag: %#x"), fApiFlags);
4055
4056 LogFlowThisFunc(("aSource=%s, aDestination=%s\n", aSource.c_str(), aDestination.c_str()));
4057
4058 AssertCompile(FsObjRenameFlag_NoReplace == 0);
4059 AssertCompile(FsObjRenameFlag_Replace != 0);
4060 uint32_t fBackend;
4061 if ((fApiFlags & ((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace)) == FsObjRenameFlag_Replace)
4062 fBackend = PATHRENAME_FLAG_REPLACE;
4063 else
4064 fBackend = PATHRENAME_FLAG_NO_REPLACE;
4065
4066 /* Call worker to do the job. */
4067 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4068 int vrc = i_pathRename(aSource, aDestination, fBackend, &rcGuest);
4069 if (RT_FAILURE(vrc))
4070 {
4071 switch (vrc)
4072 {
4073 case VERR_NOT_SUPPORTED:
4074 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4075 tr("Handling renaming guest directories not supported by installed Guest Additions"));
4076 break;
4077
4078 case VERR_GSTCTL_GUEST_ERROR:
4079 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Renaming guest directory failed: %Rrc"), rcGuest);
4080 break;
4081
4082 default:
4083 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming guest directory \"%s\" failed: %Rrc"),
4084 aSource.c_str(), vrc);
4085 break;
4086 }
4087 }
4088
4089 return hrc;
4090}
4091
4092HRESULT GuestSession::fsObjMove(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
4093 const std::vector<FsObjMoveFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
4094{
4095 RT_NOREF(aSource, aDestination, aFlags, aProgress);
4096 ReturnComNotImplemented();
4097}
4098
4099HRESULT GuestSession::fsObjMoveArray(const std::vector<com::Utf8Str> &aSource,
4100 const com::Utf8Str &aDestination,
4101 const std::vector<FsObjMoveFlag_T> &aFlags,
4102 ComPtr<IProgress> &aProgress)
4103{
4104 RT_NOREF(aSource, aDestination, aFlags, aProgress);
4105 ReturnComNotImplemented();
4106}
4107
4108HRESULT GuestSession::fsObjCopyArray(const std::vector<com::Utf8Str> &aSource,
4109 const com::Utf8Str &aDestination,
4110 const std::vector<FileCopyFlag_T> &aFlags,
4111 ComPtr<IProgress> &aProgress)
4112{
4113 RT_NOREF(aSource, aDestination, aFlags, aProgress);
4114 ReturnComNotImplemented();
4115}
4116
4117HRESULT GuestSession::fsObjSetACL(const com::Utf8Str &aPath, BOOL aFollowSymlinks, const com::Utf8Str &aAcl, ULONG aMode)
4118{
4119 RT_NOREF(aPath, aFollowSymlinks, aAcl, aMode);
4120 ReturnComNotImplemented();
4121}
4122
4123
4124HRESULT GuestSession::processCreate(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
4125 const std::vector<com::Utf8Str> &aEnvironment,
4126 const std::vector<ProcessCreateFlag_T> &aFlags,
4127 ULONG aTimeoutMS, ComPtr<IGuestProcess> &aGuestProcess)
4128{
4129 LogFlowThisFuncEnter();
4130
4131 std::vector<LONG> affinityIgnored;
4132 return processCreateEx(aExecutable, aArguments, aEnvironment, aFlags, aTimeoutMS, ProcessPriority_Default,
4133 affinityIgnored, aGuestProcess);
4134}
4135
4136HRESULT GuestSession::processCreateEx(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
4137 const std::vector<com::Utf8Str> &aEnvironment,
4138 const std::vector<ProcessCreateFlag_T> &aFlags, ULONG aTimeoutMS,
4139 ProcessPriority_T aPriority, const std::vector<LONG> &aAffinity,
4140 ComPtr<IGuestProcess> &aGuestProcess)
4141{
4142 AutoCaller autoCaller(this);
4143 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4144
4145 HRESULT hr = i_isStartedExternal();
4146 if (FAILED(hr))
4147 return hr;
4148
4149 /*
4150 * Must have an executable to execute. If none is given, we try use the
4151 * zero'th argument.
4152 */
4153 const char *pszExecutable = aExecutable.c_str();
4154 if (RT_UNLIKELY(pszExecutable == NULL || *pszExecutable == '\0'))
4155 {
4156 if (aArguments.size() > 0)
4157 pszExecutable = aArguments[0].c_str();
4158 if (pszExecutable == NULL || *pszExecutable == '\0')
4159 return setError(E_INVALIDARG, tr("No command to execute specified"));
4160 }
4161
4162 /* The rest of the input is being validated in i_processCreateEx(). */
4163
4164 LogFlowThisFuncEnter();
4165
4166 /*
4167 * Build the process startup info.
4168 */
4169 GuestProcessStartupInfo procInfo;
4170
4171 /* Executable and arguments. */
4172 procInfo.mExecutable = pszExecutable;
4173 if (aArguments.size())
4174 for (size_t i = 0; i < aArguments.size(); i++)
4175 procInfo.mArguments.push_back(aArguments[i]);
4176
4177 /* Combine the environment changes associated with the ones passed in by
4178 the caller, giving priority to the latter. The changes are putenv style
4179 and will be applied to the standard environment for the guest user. */
4180 int vrc = procInfo.mEnvironmentChanges.copy(mData.mEnvironmentChanges);
4181 if (RT_SUCCESS(vrc))
4182 {
4183 size_t idxError = ~(size_t)0;
4184 vrc = procInfo.mEnvironmentChanges.applyPutEnvArray(aEnvironment, &idxError);
4185 if (RT_SUCCESS(vrc))
4186 {
4187 /* Convert the flag array into a mask. */
4188 if (aFlags.size())
4189 for (size_t i = 0; i < aFlags.size(); i++)
4190 procInfo.mFlags |= aFlags[i];
4191
4192 procInfo.mTimeoutMS = aTimeoutMS;
4193
4194 /** @todo use RTCPUSET instead of archaic 64-bit variables! */
4195 if (aAffinity.size())
4196 for (size_t i = 0; i < aAffinity.size(); i++)
4197 if (aAffinity[i])
4198 procInfo.mAffinity |= (uint64_t)1 << i;
4199
4200 procInfo.mPriority = aPriority;
4201
4202 /*
4203 * Create a guest process object.
4204 */
4205 ComObjPtr<GuestProcess> pProcess;
4206 vrc = i_processCreateEx(procInfo, pProcess);
4207 if (RT_SUCCESS(vrc))
4208 {
4209 ComPtr<IGuestProcess> pIProcess;
4210 hr = pProcess.queryInterfaceTo(pIProcess.asOutParam());
4211 if (SUCCEEDED(hr))
4212 {
4213 /*
4214 * Start the process.
4215 */
4216 vrc = pProcess->i_startProcessAsync();
4217 if (RT_SUCCESS(vrc))
4218 {
4219 aGuestProcess = pIProcess;
4220
4221 LogFlowFuncLeaveRC(vrc);
4222 return S_OK;
4223 }
4224
4225 hr = setErrorVrc(vrc, tr("Failed to start guest process: %Rrc"), vrc);
4226 }
4227 }
4228 else if (vrc == VERR_GSTCTL_MAX_CID_OBJECTS_REACHED)
4229 hr = setErrorVrc(vrc, tr("Maximum number of concurrent guest processes per session (%u) reached"),
4230 VBOX_GUESTCTRL_MAX_OBJECTS);
4231 else
4232 hr = setErrorVrc(vrc, tr("Failed to create guest process object: %Rrc"), vrc);
4233 }
4234 else
4235 hr = setErrorBoth(vrc == VERR_ENV_INVALID_VAR_NAME ? E_INVALIDARG : Global::vboxStatusCodeToCOM(vrc), vrc,
4236 tr("Failed to apply environment variable '%s', index %u (%Rrc)'"),
4237 aEnvironment[idxError].c_str(), idxError, vrc);
4238 }
4239 else
4240 hr = setErrorVrc(vrc, tr("Failed to set up the environment: %Rrc"), vrc);
4241
4242 LogFlowFuncLeaveRC(vrc);
4243 return hr;
4244}
4245
4246HRESULT GuestSession::processGet(ULONG aPid, ComPtr<IGuestProcess> &aGuestProcess)
4247
4248{
4249 AutoCaller autoCaller(this);
4250 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4251
4252 if (aPid == 0)
4253 return setError(E_INVALIDARG, tr("No valid process ID (PID) specified"));
4254
4255 LogFlowThisFunc(("PID=%RU32\n", aPid));
4256
4257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4258
4259 HRESULT hr = S_OK;
4260
4261 ComObjPtr<GuestProcess> pProcess;
4262 int rc = i_processGetByPID(aPid, &pProcess);
4263 if (RT_FAILURE(rc))
4264 hr = setError(E_INVALIDARG, tr("No process with PID %RU32 found"), aPid);
4265
4266 /* This will set (*aProcess) to NULL if pProgress is NULL. */
4267 HRESULT hr2 = pProcess.queryInterfaceTo(aGuestProcess.asOutParam());
4268 if (SUCCEEDED(hr))
4269 hr = hr2;
4270
4271 LogFlowThisFunc(("aProcess=%p, hr=%Rhrc\n", (IGuestProcess*)aGuestProcess, hr));
4272 return hr;
4273}
4274
4275HRESULT GuestSession::symlinkCreate(const com::Utf8Str &aSource, const com::Utf8Str &aTarget, SymlinkType_T aType)
4276{
4277 RT_NOREF(aSource, aTarget, aType);
4278 ReturnComNotImplemented();
4279}
4280
4281HRESULT GuestSession::symlinkExists(const com::Utf8Str &aSymlink, BOOL *aExists)
4282
4283{
4284 RT_NOREF(aSymlink, aExists);
4285 ReturnComNotImplemented();
4286}
4287
4288HRESULT GuestSession::symlinkRead(const com::Utf8Str &aSymlink, const std::vector<SymlinkReadFlag_T> &aFlags,
4289 com::Utf8Str &aTarget)
4290{
4291 RT_NOREF(aSymlink, aFlags, aTarget);
4292 ReturnComNotImplemented();
4293}
4294
4295HRESULT GuestSession::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
4296{
4297 AutoCaller autoCaller(this);
4298 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4299
4300 /* Note: No call to i_isReadyExternal() needed here, as the session might not has been started (yet). */
4301
4302 LogFlowThisFuncEnter();
4303
4304 HRESULT hrc = S_OK;
4305
4306 /*
4307 * Note: Do not hold any locks here while waiting!
4308 */
4309 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS; GuestSessionWaitResult_T waitResult;
4310 int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &rcGuest);
4311 if (RT_SUCCESS(vrc))
4312 *aReason = waitResult;
4313 else
4314 {
4315 switch (vrc)
4316 {
4317 case VERR_GSTCTL_GUEST_ERROR:
4318 hrc = GuestSession::i_setErrorExternal(this, rcGuest);
4319 break;
4320
4321 case VERR_TIMEOUT:
4322 *aReason = GuestSessionWaitResult_Timeout;
4323 break;
4324
4325 default:
4326 {
4327 const char *pszSessionName = mData.mSession.mName.c_str();
4328 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4329 tr("Waiting for guest session \"%s\" failed: %Rrc"),
4330 pszSessionName ? pszSessionName : tr("Unnamed"), vrc);
4331 break;
4332 }
4333 }
4334 }
4335
4336 LogFlowFuncLeaveRC(vrc);
4337 return hrc;
4338}
4339
4340HRESULT GuestSession::waitForArray(const std::vector<GuestSessionWaitForFlag_T> &aWaitFor, ULONG aTimeoutMS,
4341 GuestSessionWaitResult_T *aReason)
4342{
4343 AutoCaller autoCaller(this);
4344 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4345
4346 /* Note: No call to i_isReadyExternal() needed here, as the session might not has been started (yet). */
4347
4348 LogFlowThisFuncEnter();
4349
4350 /*
4351 * Note: Do not hold any locks here while waiting!
4352 */
4353 uint32_t fWaitFor = GuestSessionWaitForFlag_None;
4354 for (size_t i = 0; i < aWaitFor.size(); i++)
4355 fWaitFor |= aWaitFor[i];
4356
4357 return WaitFor(fWaitFor, aTimeoutMS, aReason);
4358}
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