VirtualBox

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

Last change on this file since 79161 was 79161, checked in by vboxsync, 6 years ago

Main/GuestSessionImpl.cpp: Added todo on directoryExists, Must always initalize rcGuest because experience indicates that the code handling it cannot always be depended upon to set it. bugref:9320

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

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