VirtualBox

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

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

Guest Control/Main: Also check for flag DirectoryRemoveRecFlag_None in directoryRemoveRecursive().

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