VirtualBox

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

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

Main/GuestCtrl: Fixed three i_waitForStatusChange() methods that would return VERR_GSTCTL_GUEST_ERROR without setting prcGuest, making the caller use uninitialized stack as status code for the operation. bugref:9320

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

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