VirtualBox

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

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

VBoxGuestControl: Optimizing message handling - part 1. bugref:9313

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

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