VirtualBox

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

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

Guest Control/Main: Implemented virtual guest object methods for session status changes to allow guest objects set their internal state accordingly. The guest session's object map now also keeps a (weak) pointer to the guest objects for handling the callbacks.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 137.3 KB
Line 
1/* $Id: GuestSessionImpl.cpp 77587 2019-03-06 16:40:18Z 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 uFlags, int *prcGuest)
1026{
1027 AssertReturn(!(uFlags & ~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(), uFlags));
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++], uFlags);
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
2886int GuestSession::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t fWaitFlags, uint32_t uTimeoutMS,
2887 GuestSessionStatus_T *pSessionStatus, int *prcGuest)
2888{
2889 RT_NOREF(fWaitFlags);
2890 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
2891
2892 VBoxEventType_T evtType;
2893 ComPtr<IEvent> pIEvent;
2894 int vrc = waitForEvent(pEvent, uTimeoutMS,
2895 &evtType, pIEvent.asOutParam());
2896 if (RT_SUCCESS(vrc))
2897 {
2898 Assert(evtType == VBoxEventType_OnGuestSessionStateChanged);
2899
2900 ComPtr<IGuestSessionStateChangedEvent> pChangedEvent = pIEvent;
2901 Assert(!pChangedEvent.isNull());
2902
2903 GuestSessionStatus_T sessionStatus;
2904 pChangedEvent->COMGETTER(Status)(&sessionStatus);
2905 if (pSessionStatus)
2906 *pSessionStatus = sessionStatus;
2907
2908 ComPtr<IVirtualBoxErrorInfo> errorInfo;
2909 HRESULT hr = pChangedEvent->COMGETTER(Error)(errorInfo.asOutParam());
2910 ComAssertComRC(hr);
2911
2912 LONG lGuestRc;
2913 hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
2914 ComAssertComRC(hr);
2915 if (RT_FAILURE((int)lGuestRc))
2916 vrc = VERR_GSTCTL_GUEST_ERROR;
2917 if (prcGuest)
2918 *prcGuest = (int)lGuestRc;
2919
2920 LogFlowThisFunc(("Status changed event for session ID=%RU32, new status is: %RU32 (%Rrc)\n",
2921 mData.mSession.mID, sessionStatus,
2922 RT_SUCCESS((int)lGuestRc) ? VINF_SUCCESS : (int)lGuestRc));
2923 }
2924
2925 LogFlowFuncLeaveRC(vrc);
2926 return vrc;
2927}
2928
2929// implementation of public methods
2930/////////////////////////////////////////////////////////////////////////////
2931
2932HRESULT GuestSession::close()
2933{
2934 AutoCaller autoCaller(this);
2935 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2936
2937 LogFlowThisFuncEnter();
2938
2939 /* Note: Don't check if the session is ready via i_isReadyExternal() here;
2940 * the session (already) could be in a stopped / aborted state. */
2941
2942 /* Close session on guest. */
2943 int rcGuest = VINF_SUCCESS;
2944 int vrc = i_closeSession(0 /* Flags */, 30 * 1000 /* Timeout */, &rcGuest);
2945 /* On failure don't return here, instead do all the cleanup
2946 * work first and then return an error. */
2947
2948 /* Remove ourselves from the session list. */
2949 AssertPtr(mParent);
2950 int vrc2 = mParent->i_sessionRemove(mData.mSession.mID);
2951 if (vrc2 == VERR_NOT_FOUND) /* Not finding the session anymore isn't critical. */
2952 vrc2 = VINF_SUCCESS;
2953
2954 if (RT_SUCCESS(vrc))
2955 vrc = vrc2;
2956
2957 LogFlowThisFunc(("Returning rc=%Rrc, rcGuest=%Rrc\n", vrc, rcGuest));
2958
2959 if (RT_FAILURE(vrc))
2960 {
2961 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2962 return GuestSession::i_setErrorExternal(this, rcGuest);
2963 return setError(VBOX_E_IPRT_ERROR, tr("Closing guest session failed with %Rrc"), vrc);
2964 }
2965
2966 return S_OK;
2967}
2968
2969HRESULT GuestSession::fileCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2970 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
2971{
2972 RT_NOREF(aSource, aDestination, aFlags, aProgress);
2973 ReturnComNotImplemented();
2974}
2975
2976HRESULT GuestSession::fileCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2977 const std::vector<FileCopyFlag_T> &aFlags,
2978 ComPtr<IProgress> &aProgress)
2979{
2980 AutoCaller autoCaller(this);
2981 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2982
2983 uint32_t fFlags = FileCopyFlag_None;
2984 if (aFlags.size())
2985 {
2986 for (size_t i = 0; i < aFlags.size(); i++)
2987 fFlags |= aFlags[i];
2988 }
2989
2990 GuestSessionFsSourceSet SourceSet;
2991
2992 GuestSessionFsSourceSpec source;
2993 source.strSource = aSource;
2994 source.enmType = FsObjType_File;
2995 source.enmPathStyle = i_getPathStyle();
2996 source.fDryRun = false; /** @todo Implement support for a dry run. */
2997 source.Type.File.fCopyFlags = (FileCopyFlag_T)fFlags;
2998
2999 SourceSet.push_back(source);
3000
3001 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3002}
3003
3004HRESULT GuestSession::fileCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3005 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3006{
3007 AutoCaller autoCaller(this);
3008 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3009
3010 uint32_t fFlags = FileCopyFlag_None;
3011 if (aFlags.size())
3012 {
3013 for (size_t i = 0; i < aFlags.size(); i++)
3014 fFlags |= aFlags[i];
3015 }
3016
3017 GuestSessionFsSourceSet SourceSet;
3018
3019 GuestSessionFsSourceSpec source;
3020 source.strSource = aSource;
3021 source.enmType = FsObjType_File;
3022 source.enmPathStyle = i_getPathStyle();
3023 source.fDryRun = false; /** @todo Implement support for a dry run. */
3024 source.Type.File.fCopyFlags = (FileCopyFlag_T)fFlags;
3025
3026 SourceSet.push_back(source);
3027
3028 return i_copyToGuest(SourceSet, aDestination, aProgress);
3029}
3030
3031HRESULT GuestSession::copyFromGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
3032 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
3033 ComPtr<IProgress> &aProgress)
3034{
3035 AutoCaller autoCaller(this);
3036 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3037
3038 const size_t cSources = aSources.size();
3039 if ( (aFilters.size() && aFilters.size() != cSources)
3040 || (aFlags.size() && aFlags.size() != cSources))
3041 {
3042 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
3043 }
3044
3045 GuestSessionFsSourceSet SourceSet;
3046
3047 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
3048 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
3049 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
3050
3051 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
3052 const bool fFollowSymlinks = true; /** @todo Ditto. */
3053
3054 while (itSource != aSources.end())
3055 {
3056 GuestFsObjData objData;
3057 int rcGuest;
3058 int vrc = i_fsQueryInfo(*(itSource), fFollowSymlinks, objData, &rcGuest);
3059 if ( RT_FAILURE(vrc)
3060 && !fContinueOnErrors)
3061 {
3062 if (GuestProcess::i_isGuestError(vrc))
3063 return setError(E_FAIL, tr("Unable to query type for source '%s': %s"), (*itSource).c_str(),
3064 GuestProcess::i_guestErrorToString(rcGuest).c_str());
3065 else
3066 return setError(E_FAIL, tr("Unable to query type for source '%s' (%Rrc)"), (*itSource).c_str(), vrc);
3067 }
3068
3069 Utf8Str strFlags;
3070 if (itFlags != aFlags.end())
3071 {
3072 strFlags = *itFlags;
3073 ++itFlags;
3074 }
3075
3076 Utf8Str strFilter;
3077 if (itFilter != aFilters.end())
3078 {
3079 strFilter = *itFilter;
3080 ++itFilter;
3081 }
3082
3083 GuestSessionFsSourceSpec source;
3084 source.strSource = *itSource;
3085 source.strFilter = strFilter;
3086 source.enmType = objData.mType;
3087 source.enmPathStyle = i_getPathStyle();
3088 source.fDryRun = false; /** @todo Implement support for a dry run. */
3089
3090 HRESULT hrc;
3091 if (source.enmType == FsObjType_Directory)
3092 {
3093 hrc = GuestSession::i_directoryCopyFlagFromStr(strFlags, &source.Type.Dir.fCopyFlags);
3094 source.Type.Dir.fRecursive = true; /* Implicit. */
3095 }
3096 else if (source.enmType == FsObjType_File)
3097 hrc = GuestSession::i_fileCopyFlagFromStr(strFlags, &source.Type.File.fCopyFlags);
3098 else
3099 return setError(E_INVALIDARG, tr("Source type %d invalid / not supported"), source.enmType);
3100 if (FAILED(hrc))
3101 return hrc;
3102
3103 SourceSet.push_back(source);
3104
3105 ++itSource;
3106 }
3107
3108 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3109}
3110
3111HRESULT GuestSession::copyToGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
3112 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
3113 ComPtr<IProgress> &aProgress)
3114{
3115 AutoCaller autoCaller(this);
3116 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3117
3118 const size_t cSources = aSources.size();
3119 if ( (aFilters.size() && aFilters.size() != cSources)
3120 || (aFlags.size() && aFlags.size() != cSources))
3121 {
3122 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
3123 }
3124
3125 GuestSessionFsSourceSet SourceSet;
3126
3127 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
3128 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
3129 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
3130
3131 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
3132
3133 while (itSource != aSources.end())
3134 {
3135 RTFSOBJINFO objInfo;
3136 int vrc = RTPathQueryInfo((*itSource).c_str(), &objInfo, RTFSOBJATTRADD_NOTHING);
3137 if ( RT_FAILURE(vrc)
3138 && !fContinueOnErrors)
3139 {
3140 return setError(E_FAIL, tr("Unable to query type for source '%s' (%Rrc)"), (*itSource).c_str(), vrc);
3141 }
3142
3143 Utf8Str strFlags;
3144 if (itFlags != aFlags.end())
3145 {
3146 strFlags = *itFlags;
3147 ++itFlags;
3148 }
3149
3150 Utf8Str strFilter;
3151 if (itFilter != aFilters.end())
3152 {
3153 strFilter = *itFilter;
3154 ++itFilter;
3155 }
3156
3157 GuestSessionFsSourceSpec source;
3158 source.strSource = *itSource;
3159 source.strFilter = strFilter;
3160 source.enmType = GuestBase::fileModeToFsObjType(objInfo.Attr.fMode);
3161 source.enmPathStyle = i_getPathStyle();
3162 source.fDryRun = false; /** @todo Implement support for a dry run. */
3163
3164 HRESULT hrc;
3165 if (source.enmType == FsObjType_Directory)
3166 {
3167 hrc = GuestSession::i_directoryCopyFlagFromStr(strFlags, &source.Type.Dir.fCopyFlags);
3168 source.Type.Dir.fFollowSymlinks = true; /** @todo Add a flag for that in DirectoryCopyFlag_T. Later. */
3169 source.Type.Dir.fRecursive = true; /* Implicit. */
3170 }
3171 else if (source.enmType == FsObjType_File)
3172 hrc = GuestSession::i_fileCopyFlagFromStr(strFlags, &source.Type.File.fCopyFlags);
3173 else
3174 return setError(E_INVALIDARG, tr("Source type %d invalid / not supported"), source.enmType);
3175 if (FAILED(hrc))
3176 return hrc;
3177
3178 SourceSet.push_back(source);
3179
3180 ++itSource;
3181 }
3182
3183 return i_copyToGuest(SourceSet, aDestination, aProgress);
3184}
3185
3186HRESULT GuestSession::directoryCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3187 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3188{
3189 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3190 ReturnComNotImplemented();
3191}
3192
3193HRESULT GuestSession::directoryCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3194 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3195{
3196 AutoCaller autoCaller(this);
3197 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3198
3199 uint32_t fFlags = DirectoryCopyFlag_None;
3200 if (aFlags.size())
3201 {
3202 for (size_t i = 0; i < aFlags.size(); i++)
3203 fFlags |= aFlags[i];
3204 }
3205
3206 GuestSessionFsSourceSet SourceSet;
3207
3208 GuestSessionFsSourceSpec source;
3209 source.strSource = aSource;
3210 source.enmType = FsObjType_Directory;
3211 source.enmPathStyle = i_getPathStyle();
3212 source.fDryRun = false; /** @todo Implement support for a dry run. */
3213 source.Type.Dir.fCopyFlags = (DirectoryCopyFlag_T)fFlags;
3214 source.Type.Dir.fRecursive = true; /* Implicit. */
3215
3216 SourceSet.push_back(source);
3217
3218 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3219}
3220
3221HRESULT GuestSession::directoryCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3222 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3223{
3224 AutoCaller autoCaller(this);
3225 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3226
3227 uint32_t fFlags = DirectoryCopyFlag_None;
3228 if (aFlags.size())
3229 {
3230 for (size_t i = 0; i < aFlags.size(); i++)
3231 fFlags |= aFlags[i];
3232 }
3233
3234 GuestSessionFsSourceSet SourceSet;
3235
3236 GuestSessionFsSourceSpec source;
3237 source.strSource = aSource;
3238 source.enmType = FsObjType_Directory;
3239 source.enmPathStyle = i_getPathStyle();
3240 source.fDryRun = false; /** @todo Implement support for a dry run. */
3241 source.Type.Dir.fCopyFlags = (DirectoryCopyFlag_T)fFlags;
3242 source.Type.Dir.fFollowSymlinks = true; /** @todo Add a flag for that in DirectoryCopyFlag_T. Later. */
3243 source.Type.Dir.fRecursive = true; /* Implicit. */
3244
3245 SourceSet.push_back(source);
3246
3247 return i_copyToGuest(SourceSet, aDestination, aProgress);
3248}
3249
3250HRESULT GuestSession::directoryCreate(const com::Utf8Str &aPath, ULONG aMode,
3251 const std::vector<DirectoryCreateFlag_T> &aFlags)
3252{
3253 AutoCaller autoCaller(this);
3254 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3255
3256 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3257 return setError(E_INVALIDARG, tr("No directory to create specified"));
3258
3259 uint32_t fFlags = DirectoryCreateFlag_None;
3260 if (aFlags.size())
3261 {
3262 for (size_t i = 0; i < aFlags.size(); i++)
3263 fFlags |= aFlags[i];
3264
3265 if (fFlags)
3266 if (!(fFlags & DirectoryCreateFlag_Parents))
3267 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), fFlags);
3268 }
3269
3270 HRESULT hrc = i_isStartedExternal();
3271 if (FAILED(hrc))
3272 return hrc;
3273
3274 LogFlowThisFuncEnter();
3275
3276 ComObjPtr <GuestDirectory> pDirectory; int rcGuest;
3277 int vrc = i_directoryCreate(aPath, (uint32_t)aMode, fFlags, &rcGuest);
3278 if (RT_FAILURE(vrc))
3279 {
3280 if (GuestProcess::i_isGuestError(vrc))
3281 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest,
3282 tr("Directory creation failed: %s"), GuestDirectory::i_guestErrorToString(rcGuest).c_str());
3283 else
3284 {
3285 switch (vrc)
3286 {
3287 case VERR_INVALID_PARAMETER:
3288 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: Invalid parameters given"));
3289 break;
3290
3291 case VERR_BROKEN_PIPE:
3292 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: Unexpectedly aborted"));
3293 break;
3294
3295 default:
3296 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: %Rrc"), vrc);
3297 break;
3298 }
3299 }
3300 }
3301
3302 return hrc;
3303}
3304
3305HRESULT GuestSession::directoryCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath,
3306 BOOL aSecure, com::Utf8Str &aDirectory)
3307{
3308 RT_NOREF(aMode, aSecure);
3309
3310 AutoCaller autoCaller(this);
3311 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3312
3313 if (RT_UNLIKELY((aTemplateName.c_str()) == NULL || *(aTemplateName.c_str()) == '\0'))
3314 return setError(E_INVALIDARG, tr("No template specified"));
3315 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3316 return setError(E_INVALIDARG, tr("No directory name specified"));
3317
3318 HRESULT hrc = i_isStartedExternal();
3319 if (FAILED(hrc))
3320 return hrc;
3321
3322 LogFlowThisFuncEnter();
3323
3324 int rcGuest;
3325 int vrc = i_fsCreateTemp(aTemplateName, aPath, true /* Directory */, aDirectory, &rcGuest);
3326 if (!RT_SUCCESS(vrc))
3327 {
3328 switch (vrc)
3329 {
3330 case VERR_GSTCTL_GUEST_ERROR:
3331 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3332 break;
3333
3334 default:
3335 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Temporary directory creation \"%s\" with template \"%s\" failed: %Rrc"),
3336 aPath.c_str(), aTemplateName.c_str(), vrc);
3337 break;
3338 }
3339 }
3340
3341 return hrc;
3342}
3343
3344HRESULT GuestSession::directoryExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3345{
3346 AutoCaller autoCaller(this);
3347 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3348
3349 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3350 return setError(E_INVALIDARG, tr("No directory to check existence for specified"));
3351
3352 HRESULT hrc = i_isStartedExternal();
3353 if (FAILED(hrc))
3354 return hrc;
3355
3356 LogFlowThisFuncEnter();
3357
3358 GuestFsObjData objData; int rcGuest;
3359 int vrc = i_directoryQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3360 if (RT_SUCCESS(vrc))
3361 *aExists = objData.mType == FsObjType_Directory;
3362 else
3363 {
3364 switch (vrc)
3365 {
3366 case VERR_GSTCTL_GUEST_ERROR:
3367 {
3368 switch (rcGuest)
3369 {
3370 case VERR_PATH_NOT_FOUND:
3371 *aExists = FALSE;
3372 break;
3373 default:
3374 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying directory existence \"%s\" failed: %s"),
3375 aPath.c_str(), GuestProcess::i_guestErrorToString(rcGuest).c_str());
3376 break;
3377 }
3378 break;
3379 }
3380
3381 default:
3382 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying directory existence \"%s\" failed: %Rrc"),
3383 aPath.c_str(), vrc);
3384 break;
3385 }
3386 }
3387
3388 return hrc;
3389}
3390
3391HRESULT GuestSession::directoryOpen(const com::Utf8Str &aPath, const com::Utf8Str &aFilter,
3392 const std::vector<DirectoryOpenFlag_T> &aFlags, ComPtr<IGuestDirectory> &aDirectory)
3393{
3394 AutoCaller autoCaller(this);
3395 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3396
3397 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3398 return setError(E_INVALIDARG, tr("No directory to open specified"));
3399 if (RT_UNLIKELY((aFilter.c_str()) != NULL && *(aFilter.c_str()) != '\0'))
3400 return setError(E_INVALIDARG, tr("Directory filters are not implemented yet"));
3401
3402 uint32_t fFlags = DirectoryOpenFlag_None;
3403 if (aFlags.size())
3404 {
3405 for (size_t i = 0; i < aFlags.size(); i++)
3406 fFlags |= aFlags[i];
3407
3408 if (fFlags)
3409 return setError(E_INVALIDARG, tr("Open flags (%#x) not implemented yet"), fFlags);
3410 }
3411
3412 HRESULT hrc = i_isStartedExternal();
3413 if (FAILED(hrc))
3414 return hrc;
3415
3416 LogFlowThisFuncEnter();
3417
3418 GuestDirectoryOpenInfo openInfo;
3419 openInfo.mPath = aPath;
3420 openInfo.mFilter = aFilter;
3421 openInfo.mFlags = fFlags;
3422
3423 ComObjPtr <GuestDirectory> pDirectory; int rcGuest;
3424 int vrc = i_directoryOpen(openInfo, pDirectory, &rcGuest);
3425 if (RT_SUCCESS(vrc))
3426 {
3427 /* Return directory object to the caller. */
3428 hrc = pDirectory.queryInterfaceTo(aDirectory.asOutParam());
3429 }
3430 else
3431 {
3432 switch (vrc)
3433 {
3434 case VERR_INVALID_PARAMETER:
3435 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening directory \"%s\" failed; invalid parameters given"),
3436 aPath.c_str());
3437 break;
3438
3439 case VERR_GSTCTL_GUEST_ERROR:
3440 hrc = GuestDirectory::i_setErrorExternal(this, rcGuest);
3441 break;
3442
3443 default:
3444 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3445 break;
3446 }
3447 }
3448
3449 return hrc;
3450}
3451
3452HRESULT GuestSession::directoryRemove(const com::Utf8Str &aPath)
3453{
3454 AutoCaller autoCaller(this);
3455 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3456
3457 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3458 return setError(E_INVALIDARG, tr("No directory to remove specified"));
3459
3460 HRESULT hrc = i_isStartedExternal();
3461 if (FAILED(hrc))
3462 return hrc;
3463
3464 LogFlowThisFuncEnter();
3465
3466 /* No flags; only remove the directory when empty. */
3467 uint32_t uFlags = 0;
3468
3469 int rcGuest;
3470 int vrc = i_directoryRemove(aPath, uFlags, &rcGuest);
3471 if (RT_FAILURE(vrc))
3472 {
3473 switch (vrc)
3474 {
3475 case VERR_NOT_SUPPORTED:
3476 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3477 tr("Handling removing guest directories not supported by installed Guest Additions"));
3478 break;
3479
3480 case VERR_GSTCTL_GUEST_ERROR:
3481 hrc = GuestDirectory::i_setErrorExternal(this, rcGuest);
3482 break;
3483
3484 default:
3485 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing guest directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3486 break;
3487 }
3488 }
3489
3490 return hrc;
3491}
3492
3493HRESULT GuestSession::directoryRemoveRecursive(const com::Utf8Str &aPath, const std::vector<DirectoryRemoveRecFlag_T> &aFlags,
3494 ComPtr<IProgress> &aProgress)
3495{
3496 RT_NOREF(aFlags);
3497
3498 AutoCaller autoCaller(this);
3499 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3500
3501 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3502 return setError(E_INVALIDARG, tr("No directory to remove recursively specified"));
3503
3504 uint32_t fFlags = DIRREMOVEREC_FLAG_NONE;
3505 if (aFlags.size())
3506 {
3507 for (size_t i = 0; i < aFlags.size(); i++)
3508 {
3509 switch (aFlags[i])
3510 {
3511 case DirectoryRemoveRecFlag_ContentAndDir:
3512 fFlags = DIRREMOVEREC_FLAG_RECURSIVE | DIRREMOVEREC_FLAG_CONTENT_AND_DIR;
3513 break;
3514
3515 case DirectoryRemoveRecFlag_ContentOnly:
3516 fFlags = DIRREMOVEREC_FLAG_RECURSIVE | DIRREMOVEREC_FLAG_CONTENT_ONLY;
3517 break;
3518
3519 default:
3520 return setError(E_INVALIDARG, tr("Invalid flags specified"));
3521 }
3522 }
3523 }
3524
3525 HRESULT hrc = i_isStartedExternal();
3526 if (FAILED(hrc))
3527 return hrc;
3528
3529 LogFlowThisFuncEnter();
3530
3531 ComObjPtr<Progress> pProgress;
3532 hrc = pProgress.createObject();
3533 if (SUCCEEDED(hrc))
3534 hrc = pProgress->init(static_cast<IGuestSession *>(this),
3535 Bstr(tr("Removing guest directory")).raw(),
3536 TRUE /*aCancelable*/);
3537 if (FAILED(hrc))
3538 return hrc;
3539
3540 /* Note: At the moment we don't supply progress information while
3541 * deleting a guest directory recursively. So just complete
3542 * the progress object right now. */
3543 /** @todo Implement progress reporting on guest directory deletion! */
3544 hrc = pProgress->i_notifyComplete(S_OK);
3545 if (FAILED(hrc))
3546 return hrc;
3547
3548 int rcGuest;
3549 int vrc = i_directoryRemove(aPath, fFlags, &rcGuest);
3550 if (RT_FAILURE(vrc))
3551 {
3552 switch (vrc)
3553 {
3554 case VERR_NOT_SUPPORTED:
3555 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3556 tr("Handling removing guest directories recursively not supported by installed Guest Additions"));
3557 break;
3558
3559 case VERR_GSTCTL_GUEST_ERROR:
3560 hrc = GuestFile::i_setErrorExternal(this, rcGuest);
3561 break;
3562
3563 default:
3564 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Recursively removing guest directory \"%s\" failed: %Rrc"),
3565 aPath.c_str(), vrc);
3566 break;
3567 }
3568 }
3569 else
3570 {
3571 pProgress.queryInterfaceTo(aProgress.asOutParam());
3572 }
3573
3574 return hrc;
3575}
3576
3577HRESULT GuestSession::environmentScheduleSet(const com::Utf8Str &aName, const com::Utf8Str &aValue)
3578{
3579 AutoCaller autoCaller(this);
3580 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3581
3582 HRESULT hrc;
3583 if (RT_LIKELY(aName.isNotEmpty()))
3584 {
3585 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3586 {
3587 LogFlowThisFuncEnter();
3588
3589 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3590 int vrc = mData.mEnvironmentChanges.setVariable(aName, aValue);
3591 if (RT_SUCCESS(vrc))
3592 hrc = S_OK;
3593 else
3594 hrc = setErrorVrc(vrc);
3595 }
3596 else
3597 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3598 }
3599 else
3600 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3601
3602 LogFlowThisFuncLeave();
3603 return hrc;
3604}
3605
3606HRESULT GuestSession::environmentScheduleUnset(const com::Utf8Str &aName)
3607{
3608 AutoCaller autoCaller(this);
3609 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3610
3611 HRESULT hrc;
3612 if (RT_LIKELY(aName.isNotEmpty()))
3613 {
3614 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3615 {
3616 LogFlowThisFuncEnter();
3617
3618 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3619 int vrc = mData.mEnvironmentChanges.unsetVariable(aName);
3620 if (RT_SUCCESS(vrc))
3621 hrc = S_OK;
3622 else
3623 hrc = setErrorVrc(vrc);
3624 }
3625 else
3626 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3627 }
3628 else
3629 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3630
3631 LogFlowThisFuncLeave();
3632 return hrc;
3633}
3634
3635HRESULT GuestSession::environmentGetBaseVariable(const com::Utf8Str &aName, com::Utf8Str &aValue)
3636{
3637 AutoCaller autoCaller(this);
3638 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3639
3640 HRESULT hrc;
3641 if (RT_LIKELY(aName.isNotEmpty()))
3642 {
3643 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3644 {
3645 LogFlowThisFuncEnter();
3646
3647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3648 if (mData.mpBaseEnvironment)
3649 {
3650 int vrc = mData.mpBaseEnvironment->getVariable(aName, &aValue);
3651 if (RT_SUCCESS(vrc))
3652 hrc = S_OK;
3653 else
3654 hrc = setErrorVrc(vrc);
3655 }
3656 else if (mData.mProtocolVersion < 99999)
3657 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
3658 else
3659 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
3660 }
3661 else
3662 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3663 }
3664 else
3665 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3666
3667 LogFlowThisFuncLeave();
3668 return hrc;
3669}
3670
3671HRESULT GuestSession::environmentDoesBaseVariableExist(const com::Utf8Str &aName, BOOL *aExists)
3672{
3673 AutoCaller autoCaller(this);
3674 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3675
3676 *aExists = FALSE;
3677
3678 HRESULT hrc;
3679 if (RT_LIKELY(aName.isNotEmpty()))
3680 {
3681 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3682 {
3683 LogFlowThisFuncEnter();
3684
3685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3686 if (mData.mpBaseEnvironment)
3687 {
3688 hrc = S_OK;
3689 *aExists = mData.mpBaseEnvironment->doesVariableExist(aName);
3690 }
3691 else if (mData.mProtocolVersion < 99999)
3692 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
3693 else
3694 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
3695 }
3696 else
3697 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3698 }
3699 else
3700 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3701
3702 LogFlowThisFuncLeave();
3703 return hrc;
3704}
3705
3706HRESULT GuestSession::fileCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath, BOOL aSecure,
3707 ComPtr<IGuestFile> &aFile)
3708{
3709 RT_NOREF(aTemplateName, aMode, aPath, aSecure, aFile);
3710 ReturnComNotImplemented();
3711}
3712
3713HRESULT GuestSession::fileExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3714{
3715 AutoCaller autoCaller(this);
3716 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3717
3718 /* By default we return non-existent. */
3719 *aExists = FALSE;
3720
3721 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3722 return S_OK;
3723
3724 HRESULT hrc = i_isStartedExternal();
3725 if (FAILED(hrc))
3726 return hrc;
3727
3728 LogFlowThisFuncEnter();
3729
3730 GuestFsObjData objData; int rcGuest;
3731 int vrc = i_fileQueryInfo(aPath, RT_BOOL(aFollowSymlinks), objData, &rcGuest);
3732 if (RT_SUCCESS(vrc))
3733 {
3734 *aExists = TRUE;
3735 return S_OK;
3736 }
3737
3738 switch (vrc)
3739 {
3740 case VERR_GSTCTL_GUEST_ERROR:
3741 {
3742 switch (rcGuest)
3743 {
3744 case VERR_PATH_NOT_FOUND:
3745 RT_FALL_THROUGH();
3746 case VERR_FILE_NOT_FOUND:
3747 break;
3748
3749 default:
3750 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3751 break;
3752 }
3753
3754 break;
3755 }
3756
3757 case VERR_NOT_A_FILE:
3758 break;
3759
3760 default:
3761 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying file information for \"%s\" failed: %Rrc"),
3762 aPath.c_str(), vrc);
3763 break;
3764 }
3765
3766 return hrc;
3767}
3768
3769HRESULT GuestSession::fileOpen(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3770 ULONG aCreationMode, ComPtr<IGuestFile> &aFile)
3771{
3772 LogFlowThisFuncEnter();
3773
3774 const std::vector<FileOpenExFlag_T> EmptyFlags;
3775 return fileOpenEx(aPath, aAccessMode, aOpenAction, FileSharingMode_All, aCreationMode, EmptyFlags, aFile);
3776}
3777
3778HRESULT GuestSession::fileOpenEx(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3779 FileSharingMode_T aSharingMode, ULONG aCreationMode,
3780 const std::vector<FileOpenExFlag_T> &aFlags, ComPtr<IGuestFile> &aFile)
3781{
3782 AutoCaller autoCaller(this);
3783 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3784
3785 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3786 return setError(E_INVALIDARG, tr("No file to open specified"));
3787
3788 HRESULT hrc = i_isStartedExternal();
3789 if (FAILED(hrc))
3790 return hrc;
3791
3792 LogFlowThisFuncEnter();
3793
3794 GuestFileOpenInfo openInfo;
3795 openInfo.mFilename = aPath;
3796 openInfo.mCreationMode = aCreationMode;
3797
3798 /* Validate aAccessMode. */
3799 switch (aAccessMode)
3800 {
3801 case FileAccessMode_ReadOnly:
3802 RT_FALL_THRU();
3803 case FileAccessMode_WriteOnly:
3804 RT_FALL_THRU();
3805 case FileAccessMode_ReadWrite:
3806 openInfo.mAccessMode = aAccessMode;
3807 break;
3808 case FileAccessMode_AppendOnly:
3809 RT_FALL_THRU();
3810 case FileAccessMode_AppendRead:
3811 return setError(E_NOTIMPL, tr("Append access modes are not yet implemented"));
3812 default:
3813 return setError(E_INVALIDARG, tr("Unknown FileAccessMode value %u (%#x)"), aAccessMode, aAccessMode);
3814 }
3815
3816 /* Validate aOpenAction to the old format. */
3817 switch (aOpenAction)
3818 {
3819 case FileOpenAction_OpenExisting:
3820 RT_FALL_THRU();
3821 case FileOpenAction_OpenOrCreate:
3822 RT_FALL_THRU();
3823 case FileOpenAction_CreateNew:
3824 RT_FALL_THRU();
3825 case FileOpenAction_CreateOrReplace:
3826 RT_FALL_THRU();
3827 case FileOpenAction_OpenExistingTruncated:
3828 RT_FALL_THRU();
3829 case FileOpenAction_AppendOrCreate:
3830 openInfo.mOpenAction = aOpenAction;
3831 break;
3832 default:
3833 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3834 }
3835
3836 /* Validate aSharingMode. */
3837 switch (aSharingMode)
3838 {
3839 case FileSharingMode_All:
3840 openInfo.mSharingMode = aSharingMode;
3841 break;
3842 case FileSharingMode_Read:
3843 case FileSharingMode_Write:
3844 case FileSharingMode_ReadWrite:
3845 case FileSharingMode_Delete:
3846 case FileSharingMode_ReadDelete:
3847 case FileSharingMode_WriteDelete:
3848 return setError(E_NOTIMPL, tr("Only FileSharingMode_All is currently implemented"));
3849
3850 default:
3851 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3852 }
3853
3854 /* Combine and validate flags. */
3855 uint32_t fOpenEx = 0;
3856 for (size_t i = 0; i < aFlags.size(); i++)
3857 fOpenEx = aFlags[i];
3858 if (fOpenEx)
3859 return setError(E_INVALIDARG, tr("Unsupported FileOpenExFlag value(s) in aFlags (%#x)"), fOpenEx);
3860 openInfo.mfOpenEx = fOpenEx;
3861
3862 ComObjPtr <GuestFile> pFile;
3863 int rcGuest;
3864 int vrc = i_fileOpenEx(aPath, aAccessMode, aOpenAction, aSharingMode, aCreationMode, aFlags, pFile, &rcGuest);
3865 if (RT_SUCCESS(vrc))
3866 /* Return directory object to the caller. */
3867 hrc = pFile.queryInterfaceTo(aFile.asOutParam());
3868 else
3869 {
3870 switch (vrc)
3871 {
3872 case VERR_NOT_SUPPORTED:
3873 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3874 tr("Handling guest files not supported by installed Guest Additions"));
3875 break;
3876
3877 case VERR_GSTCTL_GUEST_ERROR:
3878 hrc = GuestFile::i_setErrorExternal(this, rcGuest);
3879 break;
3880
3881 default:
3882 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3883 break;
3884 }
3885 }
3886
3887 return hrc;
3888}
3889
3890HRESULT GuestSession::fileQuerySize(const com::Utf8Str &aPath, BOOL aFollowSymlinks, LONG64 *aSize)
3891{
3892 AutoCaller autoCaller(this);
3893 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3894
3895 if (aPath.isEmpty())
3896 return setError(E_INVALIDARG, tr("No path specified"));
3897
3898 HRESULT hrc = i_isStartedExternal();
3899 if (FAILED(hrc))
3900 return hrc;
3901
3902 int64_t llSize; int rcGuest;
3903 int vrc = i_fileQuerySize(aPath, aFollowSymlinks != FALSE, &llSize, &rcGuest);
3904 if (RT_SUCCESS(vrc))
3905 {
3906 *aSize = llSize;
3907 }
3908 else
3909 {
3910 if (GuestProcess::i_isGuestError(vrc))
3911 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3912 else
3913 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying file size failed: %Rrc"), vrc);
3914 }
3915
3916 return hrc;
3917}
3918
3919HRESULT GuestSession::fsObjExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3920{
3921 AutoCaller autoCaller(this);
3922 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3923
3924 if (aPath.isEmpty())
3925 return setError(E_INVALIDARG, tr("No path specified"));
3926
3927 HRESULT hrc = i_isStartedExternal();
3928 if (FAILED(hrc))
3929 return hrc;
3930
3931 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
3932
3933 *aExists = false;
3934
3935 GuestFsObjData objData;
3936 int rcGuest;
3937 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3938 if (RT_SUCCESS(vrc))
3939 {
3940 *aExists = TRUE;
3941 }
3942 else
3943 {
3944 if (GuestProcess::i_isGuestError(vrc))
3945 {
3946 if ( rcGuest == VERR_NOT_A_FILE
3947 || rcGuest == VERR_PATH_NOT_FOUND
3948 || rcGuest == VERR_FILE_NOT_FOUND
3949 || rcGuest == VERR_INVALID_NAME)
3950 {
3951 hrc = S_OK; /* Ignore these vrc values. */
3952 }
3953 else
3954 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3955 }
3956 else
3957 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3958 }
3959
3960 return hrc;
3961}
3962
3963HRESULT GuestSession::fsObjQueryInfo(const com::Utf8Str &aPath, BOOL aFollowSymlinks, ComPtr<IGuestFsObjInfo> &aInfo)
3964{
3965 AutoCaller autoCaller(this);
3966 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3967
3968 if (aPath.isEmpty())
3969 return setError(E_INVALIDARG, tr("No path specified"));
3970
3971 HRESULT hrc = i_isStartedExternal();
3972 if (FAILED(hrc))
3973 return hrc;
3974
3975 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
3976
3977 GuestFsObjData Info; int rcGuest;
3978 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, Info, &rcGuest);
3979 if (RT_SUCCESS(vrc))
3980 {
3981 ComObjPtr<GuestFsObjInfo> ptrFsObjInfo;
3982 hrc = ptrFsObjInfo.createObject();
3983 if (SUCCEEDED(hrc))
3984 {
3985 vrc = ptrFsObjInfo->init(Info);
3986 if (RT_SUCCESS(vrc))
3987 hrc = ptrFsObjInfo.queryInterfaceTo(aInfo.asOutParam());
3988 else
3989 hrc = setErrorVrc(vrc);
3990 }
3991 }
3992 else
3993 {
3994 if (GuestProcess::i_isGuestError(vrc))
3995 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3996 else
3997 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3998 }
3999
4000 return hrc;
4001}
4002
4003HRESULT GuestSession::fsObjRemove(const com::Utf8Str &aPath)
4004{
4005 AutoCaller autoCaller(this);
4006 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4007
4008 if (RT_UNLIKELY(aPath.isEmpty()))
4009 return setError(E_INVALIDARG, tr("No path specified"));
4010
4011 HRESULT hrc = i_isStartedExternal();
4012 if (FAILED(hrc))
4013 return hrc;
4014
4015 LogFlowThisFunc(("aPath=%s\n", aPath.c_str()));
4016
4017 int rcGuest;
4018 int vrc = i_fileRemove(aPath, &rcGuest);
4019 if (RT_FAILURE(vrc))
4020 {
4021 if (GuestProcess::i_isGuestError(vrc))
4022 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
4023 else
4024 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4025 }
4026
4027 return hrc;
4028}
4029
4030HRESULT GuestSession::fsObjRemoveArray(const std::vector<com::Utf8Str> &aPaths, ComPtr<IProgress> &aProgress)
4031{
4032 AutoCaller autoCaller(this);
4033 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4034
4035 RT_NOREF(aPaths, aProgress);
4036
4037 return E_NOTIMPL;
4038}
4039
4040HRESULT GuestSession::fsObjRename(const com::Utf8Str &aSource,
4041 const com::Utf8Str &aDestination,
4042 const std::vector<FsObjRenameFlag_T> &aFlags)
4043{
4044 AutoCaller autoCaller(this);
4045 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4046
4047 if (RT_UNLIKELY(aSource.isEmpty()))
4048 return setError(E_INVALIDARG, tr("No source path specified"));
4049
4050 if (RT_UNLIKELY(aDestination.isEmpty()))
4051 return setError(E_INVALIDARG, tr("No destination path specified"));
4052
4053 HRESULT hrc = i_isStartedExternal();
4054 if (FAILED(hrc))
4055 return hrc;
4056
4057 /* Combine, validate and convert flags. */
4058 uint32_t fApiFlags = 0;
4059 for (size_t i = 0; i < aFlags.size(); i++)
4060 fApiFlags |= aFlags[i];
4061 if (fApiFlags & ~((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace))
4062 return setError(E_INVALIDARG, tr("Unknown rename flag: %#x"), fApiFlags);
4063
4064 LogFlowThisFunc(("aSource=%s, aDestination=%s\n", aSource.c_str(), aDestination.c_str()));
4065
4066 AssertCompile(FsObjRenameFlag_NoReplace == 0);
4067 AssertCompile(FsObjRenameFlag_Replace != 0);
4068 uint32_t fBackend;
4069 if ((fApiFlags & ((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace)) == FsObjRenameFlag_Replace)
4070 fBackend = PATHRENAME_FLAG_REPLACE;
4071 else
4072 fBackend = PATHRENAME_FLAG_NO_REPLACE;
4073
4074 /* Call worker to do the job. */
4075 int rcGuest;
4076 int vrc = i_pathRename(aSource, aDestination, fBackend, &rcGuest);
4077 if (RT_FAILURE(vrc))
4078 {
4079 switch (vrc)
4080 {
4081 case VERR_NOT_SUPPORTED:
4082 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4083 tr("Handling renaming guest directories not supported by installed Guest Additions"));
4084 break;
4085
4086 case VERR_GSTCTL_GUEST_ERROR:
4087 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Renaming guest directory failed: %Rrc"), rcGuest);
4088 break;
4089
4090 default:
4091 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming guest directory \"%s\" failed: %Rrc"),
4092 aSource.c_str(), vrc);
4093 break;
4094 }
4095 }
4096
4097 return hrc;
4098}
4099
4100HRESULT GuestSession::fsObjMove(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
4101 const std::vector<FsObjMoveFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
4102{
4103 RT_NOREF(aSource, aDestination, aFlags, aProgress);
4104 ReturnComNotImplemented();
4105}
4106
4107HRESULT GuestSession::fsObjMoveArray(const std::vector<com::Utf8Str> &aSource,
4108 const com::Utf8Str &aDestination,
4109 const std::vector<FsObjMoveFlag_T> &aFlags,
4110 ComPtr<IProgress> &aProgress)
4111{
4112 RT_NOREF(aSource, aDestination, aFlags, aProgress);
4113 ReturnComNotImplemented();
4114}
4115
4116HRESULT GuestSession::fsObjCopyArray(const std::vector<com::Utf8Str> &aSource,
4117 const com::Utf8Str &aDestination,
4118 const std::vector<FileCopyFlag_T> &aFlags,
4119 ComPtr<IProgress> &aProgress)
4120{
4121 RT_NOREF(aSource, aDestination, aFlags, aProgress);
4122 ReturnComNotImplemented();
4123}
4124
4125HRESULT GuestSession::fsObjSetACL(const com::Utf8Str &aPath, BOOL aFollowSymlinks, const com::Utf8Str &aAcl, ULONG aMode)
4126{
4127 RT_NOREF(aPath, aFollowSymlinks, aAcl, aMode);
4128 ReturnComNotImplemented();
4129}
4130
4131
4132HRESULT GuestSession::processCreate(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
4133 const std::vector<com::Utf8Str> &aEnvironment,
4134 const std::vector<ProcessCreateFlag_T> &aFlags,
4135 ULONG aTimeoutMS, ComPtr<IGuestProcess> &aGuestProcess)
4136{
4137 LogFlowThisFuncEnter();
4138
4139 std::vector<LONG> affinityIgnored;
4140 return processCreateEx(aExecutable, aArguments, aEnvironment, aFlags, aTimeoutMS, ProcessPriority_Default,
4141 affinityIgnored, aGuestProcess);
4142}
4143
4144HRESULT GuestSession::processCreateEx(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
4145 const std::vector<com::Utf8Str> &aEnvironment,
4146 const std::vector<ProcessCreateFlag_T> &aFlags, ULONG aTimeoutMS,
4147 ProcessPriority_T aPriority, const std::vector<LONG> &aAffinity,
4148 ComPtr<IGuestProcess> &aGuestProcess)
4149{
4150 AutoCaller autoCaller(this);
4151 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4152
4153 HRESULT hr = i_isStartedExternal();
4154 if (FAILED(hr))
4155 return hr;
4156
4157 /*
4158 * Must have an executable to execute. If none is given, we try use the
4159 * zero'th argument.
4160 */
4161 const char *pszExecutable = aExecutable.c_str();
4162 if (RT_UNLIKELY(pszExecutable == NULL || *pszExecutable == '\0'))
4163 {
4164 if (aArguments.size() > 0)
4165 pszExecutable = aArguments[0].c_str();
4166 if (pszExecutable == NULL || *pszExecutable == '\0')
4167 return setError(E_INVALIDARG, tr("No command to execute specified"));
4168 }
4169
4170 /* The rest of the input is being validated in i_processCreateEx(). */
4171
4172 LogFlowThisFuncEnter();
4173
4174 /*
4175 * Build the process startup info.
4176 */
4177 GuestProcessStartupInfo procInfo;
4178
4179 /* Executable and arguments. */
4180 procInfo.mExecutable = pszExecutable;
4181 if (aArguments.size())
4182 for (size_t i = 0; i < aArguments.size(); i++)
4183 procInfo.mArguments.push_back(aArguments[i]);
4184
4185 /* Combine the environment changes associated with the ones passed in by
4186 the caller, giving priority to the latter. The changes are putenv style
4187 and will be applied to the standard environment for the guest user. */
4188 int vrc = procInfo.mEnvironmentChanges.copy(mData.mEnvironmentChanges);
4189 if (RT_SUCCESS(vrc))
4190 vrc = procInfo.mEnvironmentChanges.applyPutEnvArray(aEnvironment);
4191 if (RT_SUCCESS(vrc))
4192 {
4193 /* Convert the flag array into a mask. */
4194 if (aFlags.size())
4195 for (size_t i = 0; i < aFlags.size(); i++)
4196 procInfo.mFlags |= aFlags[i];
4197
4198 procInfo.mTimeoutMS = aTimeoutMS;
4199
4200 /** @todo use RTCPUSET instead of archaic 64-bit variables! */
4201 if (aAffinity.size())
4202 for (size_t i = 0; i < aAffinity.size(); i++)
4203 if (aAffinity[i])
4204 procInfo.mAffinity |= (uint64_t)1 << i;
4205
4206 procInfo.mPriority = aPriority;
4207
4208 /*
4209 * Create a guest process object.
4210 */
4211 ComObjPtr<GuestProcess> pProcess;
4212 vrc = i_processCreateEx(procInfo, pProcess);
4213 if (RT_SUCCESS(vrc))
4214 {
4215 ComPtr<IGuestProcess> pIProcess;
4216 hr = pProcess.queryInterfaceTo(pIProcess.asOutParam());
4217 if (SUCCEEDED(hr))
4218 {
4219 /*
4220 * Start the process.
4221 */
4222 vrc = pProcess->i_startProcessAsync();
4223 if (RT_SUCCESS(vrc))
4224 {
4225 aGuestProcess = pIProcess;
4226
4227 LogFlowFuncLeaveRC(vrc);
4228 return S_OK;
4229 }
4230
4231 hr = setErrorVrc(vrc, tr("Failed to start guest process: %Rrc"), vrc);
4232 }
4233 }
4234 else if (vrc == VERR_GSTCTL_MAX_CID_OBJECTS_REACHED)
4235 hr = setErrorVrc(vrc, tr("Maximum number of concurrent guest processes per session (%u) reached"),
4236 VBOX_GUESTCTRL_MAX_OBJECTS);
4237 else
4238 hr = setErrorVrc(vrc, tr("Failed to create guest process object: %Rrc"), vrc);
4239 }
4240 else
4241 hr = setErrorVrc(vrc, tr("Failed to set up the environment: %Rrc"), vrc);
4242
4243 LogFlowFuncLeaveRC(vrc);
4244 return hr;
4245}
4246
4247HRESULT GuestSession::processGet(ULONG aPid, ComPtr<IGuestProcess> &aGuestProcess)
4248
4249{
4250 AutoCaller autoCaller(this);
4251 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4252
4253 if (aPid == 0)
4254 return setError(E_INVALIDARG, tr("No valid process ID (PID) specified"));
4255
4256 LogFlowThisFunc(("PID=%RU32\n", aPid));
4257
4258 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4259
4260 HRESULT hr = S_OK;
4261
4262 ComObjPtr<GuestProcess> pProcess;
4263 int rc = i_processGetByPID(aPid, &pProcess);
4264 if (RT_FAILURE(rc))
4265 hr = setError(E_INVALIDARG, tr("No process with PID %RU32 found"), aPid);
4266
4267 /* This will set (*aProcess) to NULL if pProgress is NULL. */
4268 HRESULT hr2 = pProcess.queryInterfaceTo(aGuestProcess.asOutParam());
4269 if (SUCCEEDED(hr))
4270 hr = hr2;
4271
4272 LogFlowThisFunc(("aProcess=%p, hr=%Rhrc\n", (IGuestProcess*)aGuestProcess, hr));
4273 return hr;
4274}
4275
4276HRESULT GuestSession::symlinkCreate(const com::Utf8Str &aSource, const com::Utf8Str &aTarget, SymlinkType_T aType)
4277{
4278 RT_NOREF(aSource, aTarget, aType);
4279 ReturnComNotImplemented();
4280}
4281
4282HRESULT GuestSession::symlinkExists(const com::Utf8Str &aSymlink, BOOL *aExists)
4283
4284{
4285 RT_NOREF(aSymlink, aExists);
4286 ReturnComNotImplemented();
4287}
4288
4289HRESULT GuestSession::symlinkRead(const com::Utf8Str &aSymlink, const std::vector<SymlinkReadFlag_T> &aFlags,
4290 com::Utf8Str &aTarget)
4291{
4292 RT_NOREF(aSymlink, aFlags, aTarget);
4293 ReturnComNotImplemented();
4294}
4295
4296HRESULT GuestSession::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
4297{
4298 AutoCaller autoCaller(this);
4299 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4300
4301 /* Note: No call to i_isReadyExternal() needed here, as the session might not has been started (yet). */
4302
4303 LogFlowThisFuncEnter();
4304
4305 HRESULT hrc = S_OK;
4306
4307 /*
4308 * Note: Do not hold any locks here while waiting!
4309 */
4310 int rcGuest; GuestSessionWaitResult_T waitResult;
4311 int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &rcGuest);
4312 if (RT_SUCCESS(vrc))
4313 *aReason = waitResult;
4314 else
4315 {
4316 switch (vrc)
4317 {
4318 case VERR_GSTCTL_GUEST_ERROR:
4319 hrc = GuestSession::i_setErrorExternal(this, rcGuest);
4320 break;
4321
4322 case VERR_TIMEOUT:
4323 *aReason = GuestSessionWaitResult_Timeout;
4324 break;
4325
4326 default:
4327 {
4328 const char *pszSessionName = mData.mSession.mName.c_str();
4329 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4330 tr("Waiting for guest session \"%s\" failed: %Rrc"),
4331 pszSessionName ? pszSessionName : tr("Unnamed"), vrc);
4332 break;
4333 }
4334 }
4335 }
4336
4337 LogFlowFuncLeaveRC(vrc);
4338 return hrc;
4339}
4340
4341HRESULT GuestSession::waitForArray(const std::vector<GuestSessionWaitForFlag_T> &aWaitFor, ULONG aTimeoutMS,
4342 GuestSessionWaitResult_T *aReason)
4343{
4344 AutoCaller autoCaller(this);
4345 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4346
4347 /* Note: No call to i_isReadyExternal() needed here, as the session might not has been started (yet). */
4348
4349 LogFlowThisFuncEnter();
4350
4351 /*
4352 * Note: Do not hold any locks here while waiting!
4353 */
4354 uint32_t fWaitFor = GuestSessionWaitForFlag_None;
4355 for (size_t i = 0; i < aWaitFor.size(); i++)
4356 fWaitFor |= aWaitFor[i];
4357
4358 return WaitFor(fWaitFor, aTimeoutMS, aReason);
4359}
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