VirtualBox

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

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

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

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 132.1 KB
Line 
1/* $Id: GuestSessionImpl.cpp 75801 2018-11-29 01:54:46Z 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 /*
1233 * Find the object.
1234 */
1235 int rc = VERR_NOT_FOUND;
1236 const uint32_t idObject = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCtxCb->uContextID);
1237 SessionObjects::const_iterator itObjs = mData.mObjects.find(idObject);
1238 if (itObjs != mData.mObjects.end())
1239 {
1240 /* Set protocol version so that pSvcCb can be interpreted right. */
1241 pCtxCb->uProtocol = mData.mProtocolVersion;
1242
1243 switch (itObjs->second.enmType)
1244 {
1245 case SESSIONOBJECTTYPE_ANONYMOUS:
1246 rc = VERR_NOT_SUPPORTED;
1247 break;
1248
1249 case SESSIONOBJECTTYPE_SESSION:
1250 {
1251 alock.release();
1252
1253 rc = i_dispatchToThis(pCtxCb, pSvcCb);
1254 break;
1255 }
1256 case SESSIONOBJECTTYPE_DIRECTORY:
1257 {
1258 SessionDirectories::const_iterator itDir = mData.mDirectories.find(idObject);
1259 if (itDir != mData.mDirectories.end())
1260 {
1261 ComObjPtr<GuestDirectory> pDirectory(itDir->second);
1262 Assert(!pDirectory.isNull());
1263
1264 alock.release();
1265
1266 rc = pDirectory->i_callbackDispatcher(pCtxCb, pSvcCb);
1267 }
1268 break;
1269 }
1270 case SESSIONOBJECTTYPE_FILE:
1271 {
1272 SessionFiles::const_iterator itFile = mData.mFiles.find(idObject);
1273 if (itFile != mData.mFiles.end())
1274 {
1275 ComObjPtr<GuestFile> pFile(itFile->second);
1276 Assert(!pFile.isNull());
1277
1278 alock.release();
1279
1280 rc = pFile->i_callbackDispatcher(pCtxCb, pSvcCb);
1281 }
1282 break;
1283 }
1284 case SESSIONOBJECTTYPE_PROCESS:
1285 {
1286 SessionProcesses::const_iterator itProc = mData.mProcesses.find(idObject);
1287 if (itProc != mData.mProcesses.end())
1288 {
1289 ComObjPtr<GuestProcess> pProcess(itProc->second);
1290 Assert(!pProcess.isNull());
1291
1292 alock.release();
1293
1294 rc = pProcess->i_callbackDispatcher(pCtxCb, pSvcCb);
1295 }
1296 break;
1297 }
1298 default:
1299 AssertMsgFailed(("%d\n", itObjs->second.enmType));
1300 rc = VERR_INTERNAL_ERROR_4;
1301 break;
1302 }
1303 }
1304
1305 LogFlowFuncLeaveRC(rc);
1306 return rc;
1307}
1308
1309int GuestSession::i_dispatchToThis(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
1310{
1311 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
1312 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
1313
1314 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1315
1316 LogFlowThisFunc(("sessionID=%RU32, CID=%RU32, uFunction=%RU32, pSvcCb=%p\n",
1317 mData.mSession.mID, pCbCtx->uContextID, pCbCtx->uFunction, pSvcCb));
1318 int rc;
1319 switch (pCbCtx->uFunction)
1320 {
1321 case GUEST_DISCONNECTED:
1322 /** @todo Handle closing all guest objects. */
1323 rc = VERR_INTERNAL_ERROR;
1324 break;
1325
1326 case GUEST_SESSION_NOTIFY: /* Guest Additions >= 4.3.0. */
1327 {
1328 rc = i_onSessionStatusChange(pCbCtx, pSvcCb);
1329 break;
1330 }
1331
1332 default:
1333 rc = dispatchGeneric(pCbCtx, pSvcCb);
1334 break;
1335 }
1336
1337 LogFlowFuncLeaveRC(rc);
1338 return rc;
1339}
1340
1341/**
1342 * Validates and extracts file copy flags from a comma-separated string.
1343 *
1344 * @return COM status, error set on failure
1345 * @param strFlags String to extract flags from.
1346 * @param pfFlags Where to store the extracted (and validated) flags.
1347 */
1348HRESULT GuestSession::i_fileCopyFlagFromStr(const com::Utf8Str &strFlags, FileCopyFlag_T *pfFlags)
1349{
1350 unsigned fFlags = (unsigned)FileCopyFlag_None;
1351
1352 /* Validate and set flags. */
1353 if (strFlags.isNotEmpty())
1354 {
1355 const char *pszNext = strFlags.c_str();
1356 for (;;)
1357 {
1358 /* Find the next keyword, ignoring all whitespace. */
1359 pszNext = RTStrStripL(pszNext);
1360
1361 const char * const pszComma = strchr(pszNext, ',');
1362 size_t cchKeyword = pszComma ? pszComma - pszNext : strlen(pszNext);
1363 while (cchKeyword > 0 && RT_C_IS_SPACE(pszNext[cchKeyword - 1]))
1364 cchKeyword--;
1365
1366 if (cchKeyword > 0)
1367 {
1368 /* Convert keyword to flag. */
1369#define MATCH_KEYWORD(a_szKeyword) ( cchKeyword == sizeof(a_szKeyword) - 1U \
1370 && memcmp(pszNext, a_szKeyword, sizeof(a_szKeyword) - 1U) == 0)
1371 if (MATCH_KEYWORD("NoReplace"))
1372 fFlags |= (unsigned)FileCopyFlag_NoReplace;
1373 else if (MATCH_KEYWORD("FollowLinks"))
1374 fFlags |= (unsigned)FileCopyFlag_FollowLinks;
1375 else if (MATCH_KEYWORD("Update"))
1376 fFlags |= (unsigned)FileCopyFlag_Update;
1377 else
1378 return setError(E_INVALIDARG, tr("Invalid file copy flag: %.*s"), (int)cchKeyword, pszNext);
1379#undef MATCH_KEYWORD
1380 }
1381 if (!pszComma)
1382 break;
1383 pszNext = pszComma + 1;
1384 }
1385 }
1386
1387 if (pfFlags)
1388 *pfFlags = (FileCopyFlag_T)fFlags;
1389 return S_OK;
1390}
1391
1392inline bool GuestSession::i_fileExists(uint32_t uFileID, ComObjPtr<GuestFile> *pFile)
1393{
1394 SessionFiles::const_iterator it = mData.mFiles.find(uFileID);
1395 if (it != mData.mFiles.end())
1396 {
1397 if (pFile)
1398 *pFile = it->second;
1399 return true;
1400 }
1401 return false;
1402}
1403
1404/**
1405 * Unregisters a file object from a session.
1406 *
1407 * @return VBox status code. VERR_NOT_FOUND if the file is not registered (anymore).
1408 * @param pFile File object to unregister from session.
1409 */
1410int GuestSession::i_fileUnregister(GuestFile *pFile)
1411{
1412 AssertPtrReturn(pFile, VERR_INVALID_POINTER);
1413
1414 LogFlowThisFunc(("pFile=%p\n", pFile));
1415
1416 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1417
1418 const uint32_t uObjectID = pFile->getObjectID();
1419
1420 LogFlowFunc(("Removing file (objectID=%RU32) ...\n", uObjectID));
1421
1422 int rc = i_objectUnregister(uObjectID);
1423 if (RT_FAILURE(rc))
1424 return rc;
1425
1426 SessionFiles::iterator itFiles = mData.mFiles.find(uObjectID);
1427 AssertReturn(itFiles != mData.mFiles.end(), VERR_NOT_FOUND);
1428
1429 /* Make sure to consume the pointer before the one of the iterator gets released. */
1430 ComObjPtr<GuestFile> pFileConsumed = pFile;
1431
1432 LogFlowFunc(("Removing file ID=%RU32 (session %RU32, now total %zu files)\n",
1433 pFileConsumed->getObjectID(), mData.mSession.mID, mData.mFiles.size()));
1434
1435 rc = pFileConsumed->i_onRemove();
1436 AssertRCReturn(rc, rc);
1437
1438 mData.mFiles.erase(itFiles);
1439
1440 alock.release(); /* Release lock before firing off event. */
1441
1442 fireGuestFileRegisteredEvent(mEventSource, this, pFileConsumed, false /* Unregistered */);
1443
1444 pFileConsumed.setNull();
1445
1446 LogFlowFuncLeaveRC(rc);
1447 return rc;
1448}
1449
1450int GuestSession::i_fileRemove(const Utf8Str &strPath, int *prcGuest)
1451{
1452 LogFlowThisFunc(("strPath=%s\n", strPath.c_str()));
1453
1454 int vrc = VINF_SUCCESS;
1455
1456 GuestProcessStartupInfo procInfo;
1457 GuestProcessStream streamOut;
1458
1459 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
1460 procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_RM);
1461
1462 try
1463 {
1464 procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */
1465 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
1466 procInfo.mArguments.push_back("--"); /* strPath could be '--help', which is a valid filename. */
1467 procInfo.mArguments.push_back(strPath); /* The file we want to remove. */
1468 }
1469 catch (std::bad_alloc &)
1470 {
1471 vrc = VERR_NO_MEMORY;
1472 }
1473
1474 if (RT_SUCCESS(vrc))
1475 vrc = GuestProcessTool::run(this, procInfo, prcGuest);
1476
1477 LogFlowFuncLeaveRC(vrc);
1478 return vrc;
1479}
1480
1481int GuestSession::i_fileOpenEx(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
1482 FileSharingMode_T aSharingMode, ULONG aCreationMode, const std::vector<FileOpenExFlag_T> &aFlags,
1483 ComObjPtr<GuestFile> &pFile, int *prcGuest)
1484{
1485 GuestFileOpenInfo openInfo;
1486 RT_ZERO(openInfo);
1487
1488 openInfo.mFileName = aPath;
1489 openInfo.mCreationMode = aCreationMode;
1490 openInfo.mAccessMode = aAccessMode;
1491 openInfo.mOpenAction = aOpenAction;
1492 openInfo.mSharingMode = aSharingMode;
1493
1494 /* Combine and validate flags. */
1495 uint32_t fOpenEx = 0;
1496 for (size_t i = 0; i < aFlags.size(); i++)
1497 fOpenEx = aFlags[i];
1498 if (fOpenEx)
1499 return VERR_INVALID_PARAMETER; /* FileOpenExFlag not implemented yet. */
1500 openInfo.mfOpenEx = fOpenEx;
1501
1502 return i_fileOpen(openInfo, pFile, prcGuest);
1503}
1504
1505int GuestSession::i_fileOpen(const GuestFileOpenInfo &openInfo, ComObjPtr<GuestFile> &pFile, int *prcGuest)
1506{
1507 LogFlowThisFunc(("strFile=%s, enmAccessMode=0x%x, enmOpenAction=0x%x, uCreationMode=%RU32, mfOpenEx=%RU32\n",
1508 openInfo.mFileName.c_str(), openInfo.mAccessMode, openInfo.mOpenAction, openInfo.mCreationMode,
1509 openInfo.mfOpenEx));
1510
1511 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1512
1513 /* Guest Additions < 4.3 don't support handling guest files, skip. */
1514 if (mData.mProtocolVersion < 2)
1515 {
1516 if (prcGuest)
1517 *prcGuest = VERR_NOT_SUPPORTED;
1518 return VERR_GSTCTL_GUEST_ERROR;
1519 }
1520
1521 /* Register a new object ID. */
1522 uint32_t uObjectID;
1523 int rc = i_objectRegister(SESSIONOBJECTTYPE_FILE, &uObjectID);
1524 if (RT_FAILURE(rc))
1525 return rc;
1526
1527 /* Create the directory object. */
1528 HRESULT hr = pFile.createObject();
1529 if (FAILED(hr))
1530 return VERR_COM_UNEXPECTED;
1531
1532 Console *pConsole = mParent->i_getConsole();
1533 AssertPtr(pConsole);
1534
1535 rc = pFile->init(pConsole, this /* GuestSession */, uObjectID, openInfo);
1536 if (RT_FAILURE(rc))
1537 return rc;
1538
1539 /*
1540 * Since this is a synchronous guest call we have to
1541 * register the file object first, releasing the session's
1542 * lock and then proceed with the actual opening command
1543 * -- otherwise the file's opening callback would hang
1544 * because the session's lock still is in place.
1545 */
1546 try
1547 {
1548 /* Add the created file to our vector. */
1549 mData.mFiles[uObjectID] = pFile;
1550
1551 LogFlowFunc(("Added new guest file \"%s\" (Session: %RU32) (now total %zu files)\n",
1552 openInfo.mFileName.c_str(), mData.mSession.mID, mData.mFiles.size()));
1553
1554 alock.release(); /* Release lock before firing off event. */
1555
1556 fireGuestFileRegisteredEvent(mEventSource, this, pFile, true /* Registered */);
1557 }
1558 catch (std::bad_alloc &)
1559 {
1560 rc = VERR_NO_MEMORY;
1561 }
1562
1563 if (RT_SUCCESS(rc))
1564 {
1565 int rcGuest;
1566 rc = pFile->i_openFile(30 * 1000 /* 30s timeout */, &rcGuest);
1567 if ( rc == VERR_GSTCTL_GUEST_ERROR
1568 && prcGuest)
1569 {
1570 *prcGuest = rcGuest;
1571 }
1572 }
1573
1574 LogFlowFuncLeaveRC(rc);
1575 return rc;
1576}
1577
1578int GuestSession::i_fileQueryInfo(const Utf8Str &strPath, bool fFollowSymlinks, GuestFsObjData &objData, int *prcGuest)
1579{
1580 LogFlowThisFunc(("strPath=%s fFollowSymlinks=%RTbool\n", strPath.c_str(), fFollowSymlinks));
1581
1582 int vrc = i_fsQueryInfo(strPath, fFollowSymlinks, objData, prcGuest);
1583 if (RT_SUCCESS(vrc))
1584 {
1585 vrc = objData.mType == FsObjType_File
1586 ? VINF_SUCCESS : VERR_NOT_A_FILE;
1587 }
1588
1589 LogFlowFuncLeaveRC(vrc);
1590 return vrc;
1591}
1592
1593int GuestSession::i_fileQuerySize(const Utf8Str &strPath, bool fFollowSymlinks, int64_t *pllSize, int *prcGuest)
1594{
1595 AssertPtrReturn(pllSize, VERR_INVALID_POINTER);
1596
1597 GuestFsObjData objData;
1598 int vrc = i_fileQueryInfo(strPath, fFollowSymlinks, objData, prcGuest);
1599 if (RT_SUCCESS(vrc))
1600 *pllSize = objData.mObjectSize;
1601
1602 return vrc;
1603}
1604
1605/**
1606 * Queries information of a file system object (file, directory, ...).
1607 *
1608 * @return IPRT status code.
1609 * @param strPath Path to file system object to query information for.
1610 * @param fFollowSymlinks Whether to follow symbolic links or not.
1611 * @param objData Where to return the file system object data, if found.
1612 * @param prcGuest Guest rc, when returning VERR_GSTCTL_GUEST_ERROR.
1613 * Any other return code indicates some host side error.
1614 */
1615int GuestSession::i_fsQueryInfo(const Utf8Str &strPath, bool fFollowSymlinks, GuestFsObjData &objData, int *prcGuest)
1616{
1617 LogFlowThisFunc(("strPath=%s\n", strPath.c_str()));
1618
1619 /** @todo Merge this with IGuestFile::queryInfo(). */
1620 GuestProcessStartupInfo procInfo;
1621 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
1622 try
1623 {
1624 procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_STAT);
1625 procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */
1626 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
1627 if (fFollowSymlinks)
1628 procInfo.mArguments.push_back(Utf8Str("-L"));
1629 procInfo.mArguments.push_back("--"); /* strPath could be '--help', which is a valid filename. */
1630 procInfo.mArguments.push_back(strPath);
1631 }
1632 catch (std::bad_alloc &)
1633 {
1634 Log(("Out of memory!\n"));
1635 return VERR_NO_MEMORY;
1636 }
1637
1638 int vrcGuest = VERR_IPE_UNINITIALIZED_STATUS;
1639 GuestCtrlStreamObjects stdOut;
1640 int vrc = GuestProcessTool::runEx(this, procInfo,
1641 &stdOut, 1 /* cStrmOutObjects */,
1642 &vrcGuest);
1643 if (!GuestProcess::i_isGuestError(vrc))
1644 {
1645 if (!stdOut.empty())
1646 {
1647 vrc = objData.FromStat(stdOut.at(0));
1648 if (RT_FAILURE(vrc))
1649 {
1650 vrcGuest = vrc;
1651 if (prcGuest)
1652 *prcGuest = vrc;
1653 vrc = VERR_GSTCTL_GUEST_ERROR;
1654 }
1655 }
1656 else
1657 vrc = VERR_BROKEN_PIPE;
1658 }
1659 else if (prcGuest)
1660 *prcGuest = vrcGuest;
1661
1662 LogFlowThisFunc(("Returning vrc=%Rrc, vrcGuest=%Rrc\n", vrc, vrcGuest));
1663 return vrc;
1664}
1665
1666const GuestCredentials& GuestSession::i_getCredentials(void)
1667{
1668 return mData.mCredentials;
1669}
1670
1671Utf8Str GuestSession::i_getName(void)
1672{
1673 return mData.mSession.mName;
1674}
1675
1676/* static */
1677Utf8Str GuestSession::i_guestErrorToString(int rcGuest)
1678{
1679 Utf8Str strError;
1680
1681 /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
1682 switch (rcGuest)
1683 {
1684 case VERR_INVALID_VM_HANDLE:
1685 strError += Utf8StrFmt(tr("VMM device is not available (is the VM running?)"));
1686 break;
1687
1688 case VERR_HGCM_SERVICE_NOT_FOUND:
1689 strError += Utf8StrFmt(tr("The guest execution service is not available"));
1690 break;
1691
1692 case VERR_ACCOUNT_RESTRICTED:
1693 strError += Utf8StrFmt(tr("The specified user account on the guest is restricted and can't be used to logon"));
1694 break;
1695
1696 case VERR_AUTHENTICATION_FAILURE:
1697 strError += Utf8StrFmt(tr("The specified user was not able to logon on guest"));
1698 break;
1699
1700 case VERR_TIMEOUT:
1701 strError += Utf8StrFmt(tr("The guest did not respond within time"));
1702 break;
1703
1704 case VERR_CANCELLED:
1705 strError += Utf8StrFmt(tr("The session operation was canceled"));
1706 break;
1707
1708 case VERR_PERMISSION_DENIED: /** @todo r=bird: This is probably completely and utterly misleading. VERR_AUTHENTICATION_FAILURE could have this message. */
1709 strError += Utf8StrFmt(tr("Invalid user/password credentials"));
1710 break;
1711
1712 case VERR_GSTCTL_MAX_OBJECTS_REACHED:
1713 strError += Utf8StrFmt(tr("Maximum number of concurrent guest processes has been reached"));
1714 break;
1715
1716 case VERR_NOT_FOUND:
1717 strError += Utf8StrFmt(tr("The guest execution service is not ready (yet)"));
1718 break;
1719
1720 default:
1721 strError += Utf8StrFmt("%Rrc", rcGuest);
1722 break;
1723 }
1724
1725 return strError;
1726}
1727
1728/**
1729 * Checks if this session is ready state where it can handle
1730 * all session-bound actions (like guest processes, guest files).
1731 * Only used by official API methods. Will set an external
1732 * error when not ready.
1733 */
1734HRESULT GuestSession::i_isReadyExternal(void)
1735{
1736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1737
1738 /** @todo Be a bit more informative. */
1739 if (mData.mStatus != GuestSessionStatus_Started)
1740 return setError(E_UNEXPECTED, tr("Session is not in started state"));
1741
1742 return S_OK;
1743}
1744
1745/**
1746 * Called by IGuest right before this session gets removed from
1747 * the public session list.
1748 */
1749int GuestSession::i_onRemove(void)
1750{
1751 LogFlowThisFuncEnter();
1752
1753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1754
1755 int vrc = VINF_SUCCESS;
1756
1757 /*
1758 * Note: The event source stuff holds references to this object,
1759 * so make sure that this is cleaned up *before* calling uninit.
1760 */
1761 if (!mEventSource.isNull())
1762 {
1763 mEventSource->UnregisterListener(mLocalListener);
1764
1765 mLocalListener.setNull();
1766 unconst(mEventSource).setNull();
1767 }
1768
1769 LogFlowFuncLeaveRC(vrc);
1770 return vrc;
1771}
1772
1773/** No locking! */
1774int GuestSession::i_onSessionStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
1775{
1776 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
1777 /* pCallback is optional. */
1778 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
1779
1780 if (pSvcCbData->mParms < 3)
1781 return VERR_INVALID_PARAMETER;
1782
1783 CALLBACKDATA_SESSION_NOTIFY dataCb;
1784 /* pSvcCb->mpaParms[0] always contains the context ID. */
1785 int vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[1], &dataCb.uType);
1786 AssertRCReturn(vrc, vrc);
1787 vrc = HGCMSvcGetU32(&pSvcCbData->mpaParms[2], &dataCb.uResult);
1788 AssertRCReturn(vrc, vrc);
1789
1790 LogFlowThisFunc(("ID=%RU32, uType=%RU32, rcGuest=%Rrc\n",
1791 mData.mSession.mID, dataCb.uType, dataCb.uResult));
1792
1793 GuestSessionStatus_T sessionStatus = GuestSessionStatus_Undefined;
1794
1795 int rcGuest = dataCb.uResult; /** @todo uint32_t vs. int. */
1796 switch (dataCb.uType)
1797 {
1798 case GUEST_SESSION_NOTIFYTYPE_ERROR:
1799 sessionStatus = GuestSessionStatus_Error;
1800 break;
1801
1802 case GUEST_SESSION_NOTIFYTYPE_STARTED:
1803 sessionStatus = GuestSessionStatus_Started;
1804#if 0 /** @todo If we get some environment stuff along with this kind notification: */
1805 const char *pszzEnvBlock = ...;
1806 uint32_t cbEnvBlock = ...;
1807 if (!mData.mpBaseEnvironment)
1808 {
1809 GuestEnvironment *pBaseEnv;
1810 try { pBaseEnv = new GuestEnvironment(); } catch (std::bad_alloc &) { pBaseEnv = NULL; }
1811 if (pBaseEnv)
1812 {
1813 int vrc = pBaseEnv->initNormal();
1814 if (RT_SUCCESS(vrc))
1815 vrc = pBaseEnv->copyUtf8Block(pszzEnvBlock, cbEnvBlock);
1816 if (RT_SUCCESS(vrc))
1817 mData.mpBaseEnvironment = pBaseEnv;
1818 else
1819 pBaseEnv->release();
1820 }
1821 }
1822#endif
1823 break;
1824
1825 case GUEST_SESSION_NOTIFYTYPE_TEN:
1826 case GUEST_SESSION_NOTIFYTYPE_TES:
1827 case GUEST_SESSION_NOTIFYTYPE_TEA:
1828 sessionStatus = GuestSessionStatus_Terminated;
1829 break;
1830
1831 case GUEST_SESSION_NOTIFYTYPE_TOK:
1832 sessionStatus = GuestSessionStatus_TimedOutKilled;
1833 break;
1834
1835 case GUEST_SESSION_NOTIFYTYPE_TOA:
1836 sessionStatus = GuestSessionStatus_TimedOutAbnormally;
1837 break;
1838
1839 case GUEST_SESSION_NOTIFYTYPE_DWN:
1840 sessionStatus = GuestSessionStatus_Down;
1841 break;
1842
1843 case GUEST_SESSION_NOTIFYTYPE_UNDEFINED:
1844 default:
1845 vrc = VERR_NOT_SUPPORTED;
1846 break;
1847 }
1848
1849 if (RT_SUCCESS(vrc))
1850 {
1851 if (RT_FAILURE(rcGuest))
1852 sessionStatus = GuestSessionStatus_Error;
1853 }
1854
1855 /* Set the session status. */
1856 if (RT_SUCCESS(vrc))
1857 vrc = i_setSessionStatus(sessionStatus, rcGuest);
1858
1859 LogFlowThisFunc(("ID=%RU32, rcGuest=%Rrc\n", mData.mSession.mID, rcGuest));
1860
1861 LogFlowFuncLeaveRC(vrc);
1862 return vrc;
1863}
1864
1865PathStyle_T GuestSession::i_getPathStyle(void)
1866{
1867 PathStyle_T enmPathStyle;
1868
1869 VBOXOSTYPE enmOsType = mParent->i_getGuestOSType();
1870 if (enmOsType < VBOXOSTYPE_DOS)
1871 {
1872 LogFlowFunc(("returns PathStyle_Unknown\n"));
1873 enmPathStyle = PathStyle_Unknown;
1874 }
1875 else if (enmOsType < VBOXOSTYPE_Linux)
1876 {
1877 LogFlowFunc(("returns PathStyle_DOS\n"));
1878 enmPathStyle = PathStyle_DOS;
1879 }
1880 else
1881 {
1882 LogFlowFunc(("returns PathStyle_UNIX\n"));
1883 enmPathStyle = PathStyle_UNIX;
1884 }
1885
1886 return enmPathStyle;
1887}
1888
1889int GuestSession::i_startSession(int *prcGuest)
1890{
1891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1892
1893 LogFlowThisFunc(("mID=%RU32, mName=%s, uProtocolVersion=%RU32, openFlags=%x, openTimeoutMS=%RU32\n",
1894 mData.mSession.mID, mData.mSession.mName.c_str(), mData.mProtocolVersion,
1895 mData.mSession.mOpenFlags, mData.mSession.mOpenTimeoutMS));
1896
1897 /* Guest Additions < 4.3 don't support opening dedicated
1898 guest sessions. Simply return success here. */
1899 if (mData.mProtocolVersion < 2)
1900 {
1901 mData.mStatus = GuestSessionStatus_Started;
1902
1903 LogFlowThisFunc(("Installed Guest Additions don't support opening dedicated sessions, skipping\n"));
1904 return VINF_SUCCESS;
1905 }
1906
1907 if (mData.mStatus != GuestSessionStatus_Undefined)
1908 return VINF_SUCCESS;
1909
1910 /** @todo mData.mSession.uFlags validation. */
1911
1912 /* Set current session status. */
1913 mData.mStatus = GuestSessionStatus_Starting;
1914 mData.mRC = VINF_SUCCESS; /* Clear previous error, if any. */
1915
1916 int vrc;
1917
1918 GuestWaitEvent *pEvent = NULL;
1919 GuestEventTypes eventTypes;
1920 try
1921 {
1922 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
1923
1924 vrc = registerWaitEventEx(mData.mSession.mID, mData.mObjectID, eventTypes, &pEvent);
1925 }
1926 catch (std::bad_alloc &)
1927 {
1928 vrc = VERR_NO_MEMORY;
1929 }
1930
1931 if (RT_FAILURE(vrc))
1932 return vrc;
1933
1934 VBOXHGCMSVCPARM paParms[8];
1935
1936 int i = 0;
1937 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
1938 HGCMSvcSetU32(&paParms[i++], mData.mProtocolVersion);
1939 HGCMSvcSetPv(&paParms[i++], (void*)mData.mCredentials.mUser.c_str(),
1940 (ULONG)mData.mCredentials.mUser.length() + 1);
1941 HGCMSvcSetPv(&paParms[i++], (void*)mData.mCredentials.mPassword.c_str(),
1942 (ULONG)mData.mCredentials.mPassword.length() + 1);
1943 HGCMSvcSetPv(&paParms[i++], (void*)mData.mCredentials.mDomain.c_str(),
1944 (ULONG)mData.mCredentials.mDomain.length() + 1);
1945 HGCMSvcSetU32(&paParms[i++], mData.mSession.mOpenFlags);
1946
1947 alock.release(); /* Drop write lock before sending. */
1948
1949 vrc = i_sendCommand(HOST_SESSION_CREATE, i, paParms, VBOX_GUESTCTRL_DST_ROOT_SVC);
1950 if (RT_SUCCESS(vrc))
1951 {
1952 vrc = i_waitForStatusChange(pEvent, GuestSessionWaitForFlag_Start,
1953 30 * 1000 /* 30s timeout */,
1954 NULL /* Session status */, prcGuest);
1955 }
1956 else
1957 {
1958 /*
1959 * Unable to start guest session - update its current state.
1960 * Since there is no (official API) way to recover a failed guest session
1961 * this also marks the end state. Internally just calling this
1962 * same function again will work though.
1963 */
1964 mData.mStatus = GuestSessionStatus_Error;
1965 mData.mRC = vrc;
1966 }
1967
1968 unregisterWaitEvent(pEvent);
1969
1970 LogFlowFuncLeaveRC(vrc);
1971 return vrc;
1972}
1973
1974int GuestSession::i_startSessionAsync(void)
1975{
1976 LogFlowThisFuncEnter();
1977
1978 int vrc;
1979 GuestSessionTaskInternalOpen* pTask = NULL;
1980 try
1981 {
1982 pTask = new GuestSessionTaskInternalOpen(this);
1983 if (!pTask->isOk())
1984 {
1985 delete pTask;
1986 LogFlow(("GuestSession: Could not create GuestSessionTaskInternalOpen object \n"));
1987 throw VERR_MEMOBJ_INIT_FAILED;
1988 }
1989
1990 /* Asynchronously open the session on the guest by kicking off a
1991 * worker thread. */
1992 //this function delete pTask in case of exceptions, so there is no need in the call of delete operator
1993 HRESULT hrc = pTask->createThread();
1994 vrc = Global::vboxStatusCodeFromCOM(hrc);
1995 }
1996 catch(std::bad_alloc &)
1997 {
1998 vrc = VERR_NO_MEMORY;
1999 }
2000 catch(int eVRC)
2001 {
2002 vrc = eVRC;
2003 LogFlow(("GuestSession: Could not create thread for GuestSessionTaskInternalOpen task %Rrc\n", vrc));
2004 }
2005
2006 LogFlowFuncLeaveRC(vrc);
2007 return vrc;
2008}
2009
2010/* static */
2011void GuestSession::i_startSessionThreadTask(GuestSessionTaskInternalOpen *pTask)
2012{
2013 LogFlowFunc(("pTask=%p\n", pTask));
2014 AssertPtr(pTask);
2015
2016 const ComObjPtr<GuestSession> pSession(pTask->Session());
2017 Assert(!pSession.isNull());
2018
2019 AutoCaller autoCaller(pSession);
2020 if (FAILED(autoCaller.rc()))
2021 return;
2022
2023 int vrc = pSession->i_startSession(NULL /* Guest rc, ignored */);
2024/** @todo
2025 *
2026 * r=bird: Is it okay to ignore @a vrc here?
2027 *
2028 */
2029
2030 /* Nothing to do here anymore. */
2031
2032 LogFlowFuncLeaveRC(vrc);
2033 NOREF(vrc);
2034}
2035
2036/**
2037 * Registers an object to a session.
2038 *
2039 * @return VBox status code.
2040 * @param enmType Session object type to register.
2041 * @param puObjectID Returns registered object ID on success. Optional.
2042 */
2043int GuestSession::i_objectRegister(SESSIONOBJECTTYPE enmType, uint32_t *puObjectID)
2044{
2045 return i_objectRegisterEx(enmType, 0 /* fFlags */, puObjectID);
2046}
2047
2048/**
2049 * Registers an object to a session, extended version.
2050 *
2051 * @return VBox status code. VERR_GSTCTL_MAX_OBJECTS_REACHED if the maximum of concurrent objects is reached.
2052 * @param enmType Session object type to register.
2053 * @param fFlags Registration flags. Not used yet and must be 0.
2054 * @param puObjectID Returns registered object ID on success. Optional.
2055 */
2056int GuestSession::i_objectRegisterEx(SESSIONOBJECTTYPE enmType, uint32_t fFlags, uint32_t *puObjectID)
2057{
2058 RT_NOREF(fFlags);
2059 AssertReturn(fFlags == 0, VERR_INVALID_PARAMETER);
2060
2061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2062
2063 int rc = VINF_SUCCESS;
2064
2065 uint32_t uObjectID = 0; /* Shut up MSVC. */
2066
2067 if (mData.mObjects.size() < VBOX_GUESTCTRL_MAX_OBJECTS - 1 /* Minus 1 for the session itself */)
2068 {
2069 SessionObjects::reverse_iterator itRend = mData.mObjects.rbegin();
2070 if (itRend != mData.mObjects.rend())
2071 uObjectID = itRend->first + 1; /* Last key plus 1. */
2072 else
2073 uObjectID = 0;
2074 }
2075 else
2076 {
2077 /* Utilize our "free list" to get the next free object ID in the map. */
2078 if (mData.mObjectsFree.size())
2079 {
2080 /* Always re-use the oldest object ID to avoid trouble. */
2081 uObjectID = mData.mObjectsFree.front();
2082 mData.mObjectsFree.pop_front();
2083 }
2084 else
2085 rc = VERR_GSTCTL_MAX_OBJECTS_REACHED;
2086 }
2087
2088 Log2Func(("enmType=%RU32, fFlags=%RU32 -> uObjectID=%RU32 (%zu objects, %zu on free list), rc=%Rrc\n",
2089 enmType, fFlags, uObjectID, mData.mObjects.size(), mData.mObjectsFree.size(), rc));
2090
2091 if (RT_SUCCESS(rc))
2092 {
2093 mData.mObjects[uObjectID].enmType = enmType;
2094 mData.mObjects[uObjectID].tsCreatedMs = RTTimeMilliTS();
2095
2096 if (puObjectID)
2097 *puObjectID = uObjectID;
2098 }
2099
2100 return rc;
2101}
2102
2103/**
2104 * Unregisters a formerly registered object from a session.
2105 *
2106 * @return VBox status code. VERR_NOT_FOUND if object to unregister was not found.
2107 * @param uObjectID Object ID to unregister.
2108 */
2109int GuestSession::i_objectUnregister(uint32_t uObjectID)
2110{
2111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2112
2113 SessionObjects::const_iterator itObj = mData.mObjects.find(uObjectID);
2114 if (itObj != mData.mObjects.end())
2115 {
2116 Log2Func(("uObjectID=%RU32 (now %zu objects in free list)\n", uObjectID, mData.mObjectsFree.size()));
2117
2118 /* Note: Do not remove object from object list (mData.mObjects) here, as we continue operating
2119 * on the free list (mData.mObjectsFree) if we reached the object list's maximum. */
2120
2121 mData.mObjectsFree.push_back(uObjectID);
2122 Assert(mData.mObjectsFree.size() <= VBOX_GUESTCTRL_MAX_OBJECTS);
2123 return VINF_SUCCESS;
2124 }
2125
2126 AssertFailed();
2127 return VERR_NOT_FOUND;
2128}
2129
2130int GuestSession::i_pathRename(const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags, int *prcGuest)
2131{
2132 AssertReturn(!(uFlags & ~PATHRENAME_FLAG_VALID_MASK), VERR_INVALID_PARAMETER);
2133
2134 LogFlowThisFunc(("strSource=%s, strDest=%s, uFlags=0x%x\n",
2135 strSource.c_str(), strDest.c_str(), uFlags));
2136
2137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2138
2139 GuestWaitEvent *pEvent = NULL;
2140 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2141 if (RT_FAILURE(vrc))
2142 return vrc;
2143
2144 /* Prepare HGCM call. */
2145 VBOXHGCMSVCPARM paParms[8];
2146 int i = 0;
2147 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2148 HGCMSvcSetPv(&paParms[i++], (void*)strSource.c_str(),
2149 (ULONG)strSource.length() + 1);
2150 HGCMSvcSetPv(&paParms[i++], (void*)strDest.c_str(),
2151 (ULONG)strDest.length() + 1);
2152 HGCMSvcSetU32(&paParms[i++], uFlags);
2153
2154 alock.release(); /* Drop write lock before sending. */
2155
2156 vrc = i_sendCommand(HOST_PATH_RENAME, i, paParms);
2157 if (RT_SUCCESS(vrc))
2158 {
2159 vrc = pEvent->Wait(30 * 1000);
2160 if ( vrc == VERR_GSTCTL_GUEST_ERROR
2161 && prcGuest)
2162 *prcGuest = pEvent->GuestResult();
2163 }
2164
2165 unregisterWaitEvent(pEvent);
2166
2167 LogFlowFuncLeaveRC(vrc);
2168 return vrc;
2169}
2170
2171/**
2172 * Returns the user's absolute documents path, if any.
2173 *
2174 * @return VBox status code.
2175 * @param strPath Where to store the user's document path.
2176 * @param prcGuest Guest rc, when returning VERR_GSTCTL_GUEST_ERROR.
2177 * Any other return code indicates some host side error.
2178 */
2179int GuestSession::i_pathUserDocuments(Utf8Str &strPath, int *prcGuest)
2180{
2181 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2182
2183 /** @todo Cache the user's document path? */
2184
2185 GuestWaitEvent *pEvent = NULL;
2186 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2187 if (RT_FAILURE(vrc))
2188 return vrc;
2189
2190 /* Prepare HGCM call. */
2191 VBOXHGCMSVCPARM paParms[2];
2192 int i = 0;
2193 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2194
2195 alock.release(); /* Drop write lock before sending. */
2196
2197 vrc = i_sendCommand(HOST_PATH_USER_DOCUMENTS, i, paParms);
2198 if (RT_SUCCESS(vrc))
2199 {
2200 vrc = pEvent->Wait(30 * 1000);
2201 if (RT_SUCCESS(vrc))
2202 {
2203 strPath = pEvent->Payload().ToString();
2204 }
2205 else
2206 {
2207 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2208 {
2209 if (prcGuest)
2210 *prcGuest = pEvent->GuestResult();
2211 }
2212 }
2213 }
2214
2215 unregisterWaitEvent(pEvent);
2216
2217 LogFlowFuncLeaveRC(vrc);
2218 return vrc;
2219}
2220
2221/**
2222 * Returns the user's absolute home path, if any.
2223 *
2224 * @return VBox status code.
2225 * @param strPath Where to store the user's home path.
2226 * @param prcGuest Guest rc, when returning VERR_GSTCTL_GUEST_ERROR.
2227 * Any other return code indicates some host side error.
2228 */
2229int GuestSession::i_pathUserHome(Utf8Str &strPath, int *prcGuest)
2230{
2231 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2232
2233 /** @todo Cache the user's home path? */
2234
2235 GuestWaitEvent *pEvent = NULL;
2236 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2237 if (RT_FAILURE(vrc))
2238 return vrc;
2239
2240 /* Prepare HGCM call. */
2241 VBOXHGCMSVCPARM paParms[2];
2242 int i = 0;
2243 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2244
2245 alock.release(); /* Drop write lock before sending. */
2246
2247 vrc = i_sendCommand(HOST_PATH_USER_HOME, i, paParms);
2248 if (RT_SUCCESS(vrc))
2249 {
2250 vrc = pEvent->Wait(30 * 1000);
2251 if (RT_SUCCESS(vrc))
2252 {
2253 strPath = pEvent->Payload().ToString();
2254 }
2255 else
2256 {
2257 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2258 {
2259 if (prcGuest)
2260 *prcGuest = pEvent->GuestResult();
2261 }
2262 }
2263 }
2264
2265 unregisterWaitEvent(pEvent);
2266
2267 LogFlowFuncLeaveRC(vrc);
2268 return vrc;
2269}
2270
2271/**
2272 * Unregisters a process object from a session.
2273 *
2274 * @return VBox status code. VERR_NOT_FOUND if the process is not registered (anymore).
2275 * @param pProcess Process object to unregister from session.
2276 */
2277int GuestSession::i_processUnregister(GuestProcess *pProcess)
2278{
2279 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
2280
2281 LogFlowThisFunc(("pProcess=%p\n", pProcess));
2282
2283 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2284
2285 const uint32_t uObjectID = pProcess->getObjectID();
2286
2287 LogFlowFunc(("Removing process (objectID=%RU32) ...\n", uObjectID));
2288
2289 int rc = i_objectUnregister(uObjectID);
2290 if (RT_FAILURE(rc))
2291 return rc;
2292
2293 SessionProcesses::iterator itProcs = mData.mProcesses.find(uObjectID);
2294 AssertReturn(itProcs != mData.mProcesses.end(), VERR_NOT_FOUND);
2295
2296 /* Make sure to consume the pointer before the one of the iterator gets released. */
2297 ComObjPtr<GuestProcess> pProc = pProcess;
2298
2299 ULONG uPID;
2300 HRESULT hr = pProc->COMGETTER(PID)(&uPID);
2301 ComAssertComRC(hr);
2302
2303 LogFlowFunc(("Removing process ID=%RU32 (session %RU32, guest PID %RU32, now total %zu processes)\n",
2304 uObjectID, mData.mSession.mID, uPID, mData.mProcesses.size()));
2305
2306 rc = pProcess->i_onRemove();
2307 AssertRCReturn(rc, rc);
2308
2309 mData.mProcesses.erase(itProcs);
2310
2311 alock.release(); /* Release lock before firing off event. */
2312
2313 fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProc, uPID, false /* Process unregistered */);
2314
2315 pProc.setNull();
2316
2317 LogFlowFuncLeaveRC(rc);
2318 return rc;
2319}
2320
2321/**
2322 * Creates but does *not* start the process yet.
2323 *
2324 * See GuestProcess::startProcess() or GuestProcess::startProcessAsync() for
2325 * starting the process.
2326 *
2327 * @return IPRT status code.
2328 * @param procInfo
2329 * @param pProcess
2330 */
2331int GuestSession::i_processCreateEx(GuestProcessStartupInfo &procInfo, ComObjPtr<GuestProcess> &pProcess)
2332{
2333 LogFlowFunc(("mExe=%s, mFlags=%x, mTimeoutMS=%RU32\n",
2334 procInfo.mExecutable.c_str(), procInfo.mFlags, procInfo.mTimeoutMS));
2335#ifdef DEBUG
2336 if (procInfo.mArguments.size())
2337 {
2338 LogFlowFunc(("Arguments:"));
2339 ProcessArguments::const_iterator it = procInfo.mArguments.begin();
2340 while (it != procInfo.mArguments.end())
2341 {
2342 LogFlow((" %s", (*it).c_str()));
2343 ++it;
2344 }
2345 LogFlow(("\n"));
2346 }
2347#endif
2348
2349 /* Validate flags. */
2350 if (procInfo.mFlags)
2351 {
2352 if ( !(procInfo.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
2353 && !(procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
2354 && !(procInfo.mFlags & ProcessCreateFlag_Hidden)
2355 && !(procInfo.mFlags & ProcessCreateFlag_Profile)
2356 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
2357 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdErr))
2358 {
2359 return VERR_INVALID_PARAMETER;
2360 }
2361 }
2362
2363 if ( (procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
2364 && ( (procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
2365 || (procInfo.mFlags & ProcessCreateFlag_WaitForStdErr)
2366 )
2367 )
2368 {
2369 return VERR_INVALID_PARAMETER;
2370 }
2371
2372 /* Adjust timeout.
2373 * If set to 0, we define an infinite timeout (unlimited process run time). */
2374 if (procInfo.mTimeoutMS == 0)
2375 procInfo.mTimeoutMS = UINT32_MAX;
2376
2377 /** @todo Implement process priority + affinity. */
2378
2379 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2380
2381 /* Register a new object ID. */
2382 uint32_t uObjectID;
2383 int rc = i_objectRegister(SESSIONOBJECTTYPE_PROCESS, &uObjectID);
2384 if (RT_FAILURE(rc))
2385 return rc;
2386
2387 /* Create the process object. */
2388 HRESULT hr = pProcess.createObject();
2389 if (FAILED(hr))
2390 return VERR_COM_UNEXPECTED;
2391
2392 rc = pProcess->init(mParent->i_getConsole() /* Console */, this /* Session */, uObjectID,
2393 procInfo, mData.mpBaseEnvironment);
2394 if (RT_FAILURE(rc))
2395 return rc;
2396
2397 /* Add the created process to our map. */
2398 try
2399 {
2400 mData.mProcesses[uObjectID] = pProcess;
2401
2402 LogFlowFunc(("Added new process (Session: %RU32) with process ID=%RU32 (now total %zu processes)\n",
2403 mData.mSession.mID, uObjectID, mData.mProcesses.size()));
2404
2405 alock.release(); /* Release lock before firing off event. */
2406
2407 fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProcess, 0 /* PID */, true /* Process registered */);
2408 }
2409 catch (std::bad_alloc &)
2410 {
2411 rc = VERR_NO_MEMORY;
2412 }
2413
2414 return rc;
2415}
2416
2417inline bool GuestSession::i_processExists(uint32_t uProcessID, ComObjPtr<GuestProcess> *pProcess)
2418{
2419 SessionProcesses::const_iterator it = mData.mProcesses.find(uProcessID);
2420 if (it != mData.mProcesses.end())
2421 {
2422 if (pProcess)
2423 *pProcess = it->second;
2424 return true;
2425 }
2426 return false;
2427}
2428
2429inline int GuestSession::i_processGetByPID(ULONG uPID, ComObjPtr<GuestProcess> *pProcess)
2430{
2431 AssertReturn(uPID, false);
2432 /* pProcess is optional. */
2433
2434 SessionProcesses::iterator itProcs = mData.mProcesses.begin();
2435 for (; itProcs != mData.mProcesses.end(); ++itProcs)
2436 {
2437 ComObjPtr<GuestProcess> pCurProc = itProcs->second;
2438 AutoCaller procCaller(pCurProc);
2439 if (procCaller.rc())
2440 return VERR_COM_INVALID_OBJECT_STATE;
2441
2442 ULONG uCurPID;
2443 HRESULT hr = pCurProc->COMGETTER(PID)(&uCurPID);
2444 ComAssertComRC(hr);
2445
2446 if (uCurPID == uPID)
2447 {
2448 if (pProcess)
2449 *pProcess = pCurProc;
2450 return VINF_SUCCESS;
2451 }
2452 }
2453
2454 return VERR_NOT_FOUND;
2455}
2456
2457int GuestSession::i_sendCommand(uint32_t uFunction, uint32_t uParms, PVBOXHGCMSVCPARM paParms,
2458 uint64_t fDst /*= VBOX_GUESTCTRL_DST_SESSION*/)
2459{
2460 LogFlowThisFuncEnter();
2461
2462#ifndef VBOX_GUESTCTRL_TEST_CASE
2463 ComObjPtr<Console> pConsole = mParent->i_getConsole();
2464 Assert(!pConsole.isNull());
2465
2466 /* Forward the information to the VMM device. */
2467 VMMDev *pVMMDev = pConsole->i_getVMMDev();
2468 AssertPtr(pVMMDev);
2469
2470 LogFlowThisFunc(("uFunction=%RU32 (%s), uParms=%RU32\n", uFunction, GstCtrlHostFnName((guestControl::eHostFn)uFunction), uParms));
2471
2472 /* HACK ALERT! We extend the first parameter to 64-bit and use the
2473 two topmost bits for call destination information. */
2474 Assert(fDst == VBOX_GUESTCTRL_DST_SESSION || fDst == VBOX_GUESTCTRL_DST_ROOT_SVC || fDst == VBOX_GUESTCTRL_DST_BOTH);
2475 Assert(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT);
2476 paParms[0].type = VBOX_HGCM_SVC_PARM_64BIT;
2477 paParms[0].u.uint64 = (uint64_t)paParms[0].u.uint32 | fDst;
2478
2479 /* Make the call. */
2480 int vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uFunction, uParms, paParms);
2481 if (RT_FAILURE(vrc))
2482 {
2483 /** @todo What to do here? */
2484 }
2485#else
2486 /* Not needed within testcases. */
2487 int vrc = VINF_SUCCESS;
2488#endif
2489 LogFlowFuncLeaveRC(vrc);
2490 return vrc;
2491}
2492
2493/* static */
2494HRESULT GuestSession::i_setErrorExternal(VirtualBoxBase *pInterface, int rcGuest)
2495{
2496 AssertPtr(pInterface);
2497 AssertMsg(RT_FAILURE(rcGuest), ("Guest rc does not indicate a failure when setting error\n"));
2498
2499 return pInterface->setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, GuestSession::i_guestErrorToString(rcGuest).c_str());
2500}
2501
2502/* Does not do locking; caller is responsible for that! */
2503int GuestSession::i_setSessionStatus(GuestSessionStatus_T sessionStatus, int sessionRc)
2504{
2505 LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, sessionRc=%Rrc\n",
2506 mData.mStatus, sessionStatus, sessionRc));
2507
2508 if (sessionStatus == GuestSessionStatus_Error)
2509 {
2510 AssertMsg(RT_FAILURE(sessionRc), ("Guest rc must be an error (%Rrc)\n", sessionRc));
2511 /* Do not allow overwriting an already set error. If this happens
2512 * this means we forgot some error checking/locking somewhere. */
2513 AssertMsg(RT_SUCCESS(mData.mRC), ("Guest rc already set (to %Rrc)\n", mData.mRC));
2514 }
2515 else
2516 AssertMsg(RT_SUCCESS(sessionRc), ("Guest rc must not be an error (%Rrc)\n", sessionRc));
2517
2518 if (mData.mStatus != sessionStatus)
2519 {
2520 mData.mStatus = sessionStatus;
2521 mData.mRC = sessionRc;
2522
2523 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
2524 HRESULT hr = errorInfo.createObject();
2525 ComAssertComRC(hr);
2526 int rc2 = errorInfo->initEx(VBOX_E_IPRT_ERROR, sessionRc,
2527 COM_IIDOF(IGuestSession), getComponentName(),
2528 i_guestErrorToString(sessionRc));
2529 AssertRC(rc2);
2530
2531 fireGuestSessionStateChangedEvent(mEventSource, this,
2532 mData.mSession.mID, sessionStatus, errorInfo);
2533 }
2534
2535 return VINF_SUCCESS;
2536}
2537
2538int GuestSession::i_signalWaiters(GuestSessionWaitResult_T enmWaitResult, int rc /*= VINF_SUCCESS */)
2539{
2540 RT_NOREF(enmWaitResult, rc);
2541
2542 /*LogFlowThisFunc(("enmWaitResult=%d, rc=%Rrc, mWaitCount=%RU32, mWaitEvent=%p\n",
2543 enmWaitResult, rc, mData.mWaitCount, mData.mWaitEvent));*/
2544
2545 /* Note: No write locking here -- already done in the caller. */
2546
2547 int vrc = VINF_SUCCESS;
2548 /*if (mData.mWaitEvent)
2549 vrc = mData.mWaitEvent->Signal(enmWaitResult, rc);*/
2550 LogFlowFuncLeaveRC(vrc);
2551 return vrc;
2552}
2553
2554/**
2555 * Determines the protocol version (sets mData.mProtocolVersion).
2556 *
2557 * This is called from the init method prior to to establishing a guest
2558 * session.
2559 *
2560 * @return IPRT status code.
2561 */
2562int GuestSession::i_determineProtocolVersion(void)
2563{
2564 /*
2565 * We currently do this based on the reported guest additions version,
2566 * ASSUMING that VBoxService and VBoxDrv are at the same version.
2567 */
2568 ComObjPtr<Guest> pGuest = mParent;
2569 AssertReturn(!pGuest.isNull(), VERR_NOT_SUPPORTED);
2570 uint32_t uGaVersion = pGuest->i_getAdditionsVersion();
2571
2572 /* Everyone supports version one, if they support anything at all. */
2573 mData.mProtocolVersion = 1;
2574
2575 /* Guest control 2.0 was introduced with 4.3.0. */
2576 if (uGaVersion >= VBOX_FULL_VERSION_MAKE(4,3,0))
2577 mData.mProtocolVersion = 2; /* Guest control 2.0. */
2578
2579 LogFlowThisFunc(("uGaVersion=%u.%u.%u => mProtocolVersion=%u\n",
2580 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
2581 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
2582
2583 /*
2584 * Inform the user about outdated guest additions (VM release log).
2585 */
2586 if (mData.mProtocolVersion < 2)
2587 LogRelMax(3, (tr("Warning: Guest Additions v%u.%u.%u only supports the older guest control protocol version %u.\n"
2588 " Please upgrade GAs to the current version to get full guest control capabilities.\n"),
2589 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
2590 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
2591
2592 return VINF_SUCCESS;
2593}
2594
2595int GuestSession::i_waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, GuestSessionWaitResult_T &waitResult, int *prcGuest)
2596{
2597 LogFlowThisFuncEnter();
2598
2599 AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
2600
2601 /*LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p, prcGuest=%p\n",
2602 fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent, prcGuest));*/
2603
2604 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2605
2606 /* Did some error occur before? Then skip waiting and return. */
2607 if (mData.mStatus == GuestSessionStatus_Error)
2608 {
2609 waitResult = GuestSessionWaitResult_Error;
2610 AssertMsg(RT_FAILURE(mData.mRC), ("No error rc (%Rrc) set when guest session indicated an error\n", mData.mRC));
2611 if (prcGuest)
2612 *prcGuest = mData.mRC; /* Return last set error. */
2613 return VERR_GSTCTL_GUEST_ERROR;
2614 }
2615
2616 /* Guest Additions < 4.3 don't support session handling, skip. */
2617 if (mData.mProtocolVersion < 2)
2618 {
2619 waitResult = GuestSessionWaitResult_WaitFlagNotSupported;
2620
2621 LogFlowThisFunc(("Installed Guest Additions don't support waiting for dedicated sessions, skipping\n"));
2622 return VINF_SUCCESS;
2623 }
2624
2625 waitResult = GuestSessionWaitResult_None;
2626 if (fWaitFlags & GuestSessionWaitForFlag_Terminate)
2627 {
2628 switch (mData.mStatus)
2629 {
2630 case GuestSessionStatus_Terminated:
2631 case GuestSessionStatus_Down:
2632 waitResult = GuestSessionWaitResult_Terminate;
2633 break;
2634
2635 case GuestSessionStatus_TimedOutKilled:
2636 case GuestSessionStatus_TimedOutAbnormally:
2637 waitResult = GuestSessionWaitResult_Timeout;
2638 break;
2639
2640 case GuestSessionStatus_Error:
2641 /* Handled above. */
2642 break;
2643
2644 case GuestSessionStatus_Started:
2645 waitResult = GuestSessionWaitResult_Start;
2646 break;
2647
2648 case GuestSessionStatus_Undefined:
2649 case GuestSessionStatus_Starting:
2650 /* Do the waiting below. */
2651 break;
2652
2653 default:
2654 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
2655 return VERR_NOT_IMPLEMENTED;
2656 }
2657 }
2658 else if (fWaitFlags & GuestSessionWaitForFlag_Start)
2659 {
2660 switch (mData.mStatus)
2661 {
2662 case GuestSessionStatus_Started:
2663 case GuestSessionStatus_Terminating:
2664 case GuestSessionStatus_Terminated:
2665 case GuestSessionStatus_Down:
2666 waitResult = GuestSessionWaitResult_Start;
2667 break;
2668
2669 case GuestSessionStatus_Error:
2670 waitResult = GuestSessionWaitResult_Error;
2671 break;
2672
2673 case GuestSessionStatus_TimedOutKilled:
2674 case GuestSessionStatus_TimedOutAbnormally:
2675 waitResult = GuestSessionWaitResult_Timeout;
2676 break;
2677
2678 case GuestSessionStatus_Undefined:
2679 case GuestSessionStatus_Starting:
2680 /* Do the waiting below. */
2681 break;
2682
2683 default:
2684 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
2685 return VERR_NOT_IMPLEMENTED;
2686 }
2687 }
2688
2689 LogFlowThisFunc(("sessionStatus=%RU32, sessionRc=%Rrc, waitResult=%RU32\n",
2690 mData.mStatus, mData.mRC, waitResult));
2691
2692 /* No waiting needed? Return immediately using the last set error. */
2693 if (waitResult != GuestSessionWaitResult_None)
2694 {
2695 if (prcGuest)
2696 *prcGuest = mData.mRC; /* Return last set error (if any). */
2697 return RT_SUCCESS(mData.mRC) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
2698 }
2699
2700 int vrc;
2701
2702 GuestWaitEvent *pEvent = NULL;
2703 GuestEventTypes eventTypes;
2704 try
2705 {
2706 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
2707
2708 vrc = registerWaitEventEx(mData.mSession.mID, mData.mObjectID, eventTypes, &pEvent);
2709 }
2710 catch (std::bad_alloc &)
2711 {
2712 vrc = VERR_NO_MEMORY;
2713 }
2714
2715 if (RT_FAILURE(vrc))
2716 return vrc;
2717
2718 alock.release(); /* Release lock before waiting. */
2719
2720 GuestSessionStatus_T sessionStatus;
2721 vrc = i_waitForStatusChange(pEvent, fWaitFlags,
2722 uTimeoutMS, &sessionStatus, prcGuest);
2723 if (RT_SUCCESS(vrc))
2724 {
2725 switch (sessionStatus)
2726 {
2727 case GuestSessionStatus_Started:
2728 waitResult = GuestSessionWaitResult_Start;
2729 break;
2730
2731 case GuestSessionStatus_Terminated:
2732 waitResult = GuestSessionWaitResult_Terminate;
2733 break;
2734
2735 case GuestSessionStatus_TimedOutKilled:
2736 case GuestSessionStatus_TimedOutAbnormally:
2737 waitResult = GuestSessionWaitResult_Timeout;
2738 break;
2739
2740 case GuestSessionStatus_Down:
2741 waitResult = GuestSessionWaitResult_Terminate;
2742 break;
2743
2744 case GuestSessionStatus_Error:
2745 waitResult = GuestSessionWaitResult_Error;
2746 break;
2747
2748 default:
2749 waitResult = GuestSessionWaitResult_Status;
2750 break;
2751 }
2752 }
2753
2754 unregisterWaitEvent(pEvent);
2755
2756 LogFlowFuncLeaveRC(vrc);
2757 return vrc;
2758}
2759
2760int GuestSession::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t fWaitFlags, uint32_t uTimeoutMS,
2761 GuestSessionStatus_T *pSessionStatus, int *prcGuest)
2762{
2763 RT_NOREF(fWaitFlags);
2764 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
2765
2766 VBoxEventType_T evtType;
2767 ComPtr<IEvent> pIEvent;
2768 int vrc = waitForEvent(pEvent, uTimeoutMS,
2769 &evtType, pIEvent.asOutParam());
2770 if (RT_SUCCESS(vrc))
2771 {
2772 Assert(evtType == VBoxEventType_OnGuestSessionStateChanged);
2773
2774 ComPtr<IGuestSessionStateChangedEvent> pChangedEvent = pIEvent;
2775 Assert(!pChangedEvent.isNull());
2776
2777 GuestSessionStatus_T sessionStatus;
2778 pChangedEvent->COMGETTER(Status)(&sessionStatus);
2779 if (pSessionStatus)
2780 *pSessionStatus = sessionStatus;
2781
2782 ComPtr<IVirtualBoxErrorInfo> errorInfo;
2783 HRESULT hr = pChangedEvent->COMGETTER(Error)(errorInfo.asOutParam());
2784 ComAssertComRC(hr);
2785
2786 LONG lGuestRc;
2787 hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
2788 ComAssertComRC(hr);
2789 if (RT_FAILURE((int)lGuestRc))
2790 vrc = VERR_GSTCTL_GUEST_ERROR;
2791 if (prcGuest)
2792 *prcGuest = (int)lGuestRc;
2793
2794 LogFlowThisFunc(("Status changed event for session ID=%RU32, new status is: %RU32 (%Rrc)\n",
2795 mData.mSession.mID, sessionStatus,
2796 RT_SUCCESS((int)lGuestRc) ? VINF_SUCCESS : (int)lGuestRc));
2797 }
2798
2799 LogFlowFuncLeaveRC(vrc);
2800 return vrc;
2801}
2802
2803// implementation of public methods
2804/////////////////////////////////////////////////////////////////////////////
2805
2806HRESULT GuestSession::close()
2807{
2808 AutoCaller autoCaller(this);
2809 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2810
2811 LogFlowThisFuncEnter();
2812
2813 /* Note: Don't check if the session is ready via i_isReadyExternal() here;
2814 * the session (already) could be in a stopped / aborted state. */
2815
2816 /* Close session on guest. */
2817 int rcGuest = VINF_SUCCESS;
2818 int vrc = i_closeSession(0 /* Flags */, 30 * 1000 /* Timeout */, &rcGuest);
2819 /* On failure don't return here, instead do all the cleanup
2820 * work first and then return an error. */
2821
2822 /* Remove ourselves from the session list. */
2823 AssertPtr(mParent);
2824 int vrc2 = mParent->i_sessionRemove(mData.mSession.mID);
2825 if (vrc2 == VERR_NOT_FOUND) /* Not finding the session anymore isn't critical. */
2826 vrc2 = VINF_SUCCESS;
2827
2828 if (RT_SUCCESS(vrc))
2829 vrc = vrc2;
2830
2831 LogFlowThisFunc(("Returning rc=%Rrc, rcGuest=%Rrc\n", vrc, rcGuest));
2832
2833 if (RT_FAILURE(vrc))
2834 {
2835 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2836 return GuestSession::i_setErrorExternal(this, rcGuest);
2837 return setError(VBOX_E_IPRT_ERROR, tr("Closing guest session failed with %Rrc"), vrc);
2838 }
2839
2840 return S_OK;
2841}
2842
2843HRESULT GuestSession::fileCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2844 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
2845{
2846 RT_NOREF(aSource, aDestination, aFlags, aProgress);
2847 ReturnComNotImplemented();
2848}
2849
2850HRESULT GuestSession::fileCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2851 const std::vector<FileCopyFlag_T> &aFlags,
2852 ComPtr<IProgress> &aProgress)
2853{
2854 AutoCaller autoCaller(this);
2855 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2856
2857 uint32_t fFlags = FileCopyFlag_None;
2858 if (aFlags.size())
2859 {
2860 for (size_t i = 0; i < aFlags.size(); i++)
2861 fFlags |= aFlags[i];
2862 }
2863
2864 GuestSessionFsSourceSet SourceSet;
2865
2866 GuestSessionFsSourceSpec source;
2867 source.strSource = aSource;
2868 source.enmType = FsObjType_File;
2869 source.enmPathStyle = i_getPathStyle();
2870 source.Type.File.fCopyFlags = (FileCopyFlag_T)fFlags;
2871
2872 SourceSet.push_back(source);
2873
2874 return i_copyFromGuest(SourceSet, aDestination, aProgress);
2875}
2876
2877HRESULT GuestSession::fileCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2878 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
2879{
2880 AutoCaller autoCaller(this);
2881 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2882
2883 uint32_t fFlags = FileCopyFlag_None;
2884 if (aFlags.size())
2885 {
2886 for (size_t i = 0; i < aFlags.size(); i++)
2887 fFlags |= aFlags[i];
2888 }
2889
2890 GuestSessionFsSourceSet SourceSet;
2891
2892 GuestSessionFsSourceSpec source;
2893 source.strSource = aSource;
2894 source.enmType = FsObjType_File;
2895 source.enmPathStyle = i_getPathStyle();
2896 source.Type.File.fCopyFlags = (FileCopyFlag_T)fFlags;
2897
2898 SourceSet.push_back(source);
2899
2900 return i_copyToGuest(SourceSet, aDestination, aProgress);
2901}
2902
2903HRESULT GuestSession::copyFromGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
2904 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
2905 ComPtr<IProgress> &aProgress)
2906{
2907 AutoCaller autoCaller(this);
2908 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2909
2910 const size_t cSources = aSources.size();
2911 if ( (aFilters.size() && aFilters.size() != cSources)
2912 || (aFlags.size() && aFlags.size() != cSources))
2913 {
2914 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
2915 }
2916
2917 GuestSessionFsSourceSet SourceSet;
2918
2919 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
2920 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
2921 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
2922
2923 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
2924 const bool fFollowSymlinks = true; /** @todo Ditto. */
2925
2926 while (itSource != aSources.end())
2927 {
2928 GuestFsObjData objData;
2929 int rcGuest;
2930 int vrc = i_fsQueryInfo(*(itSource), fFollowSymlinks, objData, &rcGuest);
2931 if ( RT_FAILURE(vrc)
2932 && !fContinueOnErrors)
2933 {
2934 if (GuestProcess::i_isGuestError(vrc))
2935 return setError(E_FAIL, tr("Unable to query type for source '%s': %s"), (*itSource).c_str(),
2936 GuestProcess::i_guestErrorToString(rcGuest).c_str());
2937 else
2938 return setError(E_FAIL, tr("Unable to query type for source '%s' (%Rrc)"), (*itSource).c_str(), vrc);
2939 }
2940
2941 Utf8Str strFlags;
2942 if (itFlags != aFlags.end())
2943 {
2944 strFlags = *itFlags;
2945 ++itFlags;
2946 }
2947
2948 Utf8Str strFilter;
2949 if (itFilter != aFilters.end())
2950 {
2951 strFilter = *itFilter;
2952 ++itFilter;
2953 }
2954
2955 GuestSessionFsSourceSpec source;
2956 source.strSource = *itSource;
2957 source.strFilter = strFilter;
2958 source.enmType = objData.mType;
2959 source.enmPathStyle = i_getPathStyle();
2960
2961 HRESULT hrc;
2962 if (source.enmType == FsObjType_Directory)
2963 {
2964 hrc = GuestSession::i_directoryCopyFlagFromStr(strFlags, &source.Type.Dir.fCopyFlags);
2965 source.Type.Dir.fRecursive = true; /* Implicit. */
2966 }
2967 else if (source.enmType == FsObjType_File)
2968 hrc = GuestSession::i_fileCopyFlagFromStr(strFlags, &source.Type.File.fCopyFlags);
2969 else
2970 return setError(E_INVALIDARG, tr("Source type %d invalid / not supported"), source.enmType);
2971 if (FAILED(hrc))
2972 return hrc;
2973
2974 SourceSet.push_back(source);
2975
2976 ++itSource;
2977 }
2978
2979 return i_copyFromGuest(SourceSet, aDestination, aProgress);
2980}
2981
2982HRESULT GuestSession::copyToGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
2983 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
2984 ComPtr<IProgress> &aProgress)
2985{
2986 AutoCaller autoCaller(this);
2987 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2988
2989 const size_t cSources = aSources.size();
2990 if ( (aFilters.size() && aFilters.size() != cSources)
2991 || (aFlags.size() && aFlags.size() != cSources))
2992 {
2993 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
2994 }
2995
2996 GuestSessionFsSourceSet SourceSet;
2997
2998 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
2999 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
3000 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
3001
3002 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
3003
3004 while (itSource != aSources.end())
3005 {
3006 RTFSOBJINFO objInfo;
3007 int vrc = RTPathQueryInfo((*itSource).c_str(), &objInfo, RTFSOBJATTRADD_NOTHING);
3008 if ( RT_FAILURE(vrc)
3009 && !fContinueOnErrors)
3010 {
3011 return setError(E_FAIL, tr("Unable to query type for source '%s' (%Rrc)"), (*itSource).c_str(), vrc);
3012 }
3013
3014 Utf8Str strFlags;
3015 if (itFlags != aFlags.end())
3016 {
3017 strFlags = *itFlags;
3018 ++itFlags;
3019 }
3020
3021 Utf8Str strFilter;
3022 if (itFilter != aFilters.end())
3023 {
3024 strFilter = *itFilter;
3025 ++itFilter;
3026 }
3027
3028 GuestSessionFsSourceSpec source;
3029 source.strSource = *itSource;
3030 source.strFilter = strFilter;
3031 source.enmType = GuestBase::fileModeToFsObjType(objInfo.Attr.fMode);
3032 source.enmPathStyle = i_getPathStyle();
3033
3034 HRESULT hrc;
3035 if (source.enmType == FsObjType_Directory)
3036 {
3037 hrc = GuestSession::i_directoryCopyFlagFromStr(strFlags, &source.Type.Dir.fCopyFlags);
3038 source.Type.Dir.fFollowSymlinks = true; /** @todo Add a flag for that in DirectoryCopyFlag_T. Later. */
3039 source.Type.Dir.fRecursive = true; /* Implicit. */
3040 }
3041 else if (source.enmType == FsObjType_File)
3042 hrc = GuestSession::i_fileCopyFlagFromStr(strFlags, &source.Type.File.fCopyFlags);
3043 else
3044 return setError(E_INVALIDARG, tr("Source type %d invalid / not supported"), source.enmType);
3045 if (FAILED(hrc))
3046 return hrc;
3047
3048 SourceSet.push_back(source);
3049
3050 ++itSource;
3051 }
3052
3053 return i_copyToGuest(SourceSet, aDestination, aProgress);
3054}
3055
3056HRESULT GuestSession::directoryCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3057 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3058{
3059 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3060 ReturnComNotImplemented();
3061}
3062
3063HRESULT GuestSession::directoryCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3064 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3065{
3066 AutoCaller autoCaller(this);
3067 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3068
3069 uint32_t fFlags = DirectoryCopyFlag_None;
3070 if (aFlags.size())
3071 {
3072 for (size_t i = 0; i < aFlags.size(); i++)
3073 fFlags |= aFlags[i];
3074 }
3075
3076 GuestSessionFsSourceSet SourceSet;
3077
3078 GuestSessionFsSourceSpec source;
3079 source.strSource = aSource;
3080 source.enmType = FsObjType_Directory;
3081 source.enmPathStyle = i_getPathStyle();
3082 source.Type.Dir.fCopyFlags = (DirectoryCopyFlag_T)fFlags;
3083 source.Type.Dir.fRecursive = true; /* Implicit. */
3084
3085 SourceSet.push_back(source);
3086
3087 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3088}
3089
3090HRESULT GuestSession::directoryCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3091 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3092{
3093 AutoCaller autoCaller(this);
3094 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3095
3096 uint32_t fFlags = DirectoryCopyFlag_None;
3097 if (aFlags.size())
3098 {
3099 for (size_t i = 0; i < aFlags.size(); i++)
3100 fFlags |= aFlags[i];
3101 }
3102
3103 GuestSessionFsSourceSet SourceSet;
3104
3105 GuestSessionFsSourceSpec source;
3106 source.strSource = aSource;
3107 source.enmType = FsObjType_Directory;
3108 source.enmPathStyle = i_getPathStyle();
3109 source.Type.Dir.fCopyFlags = (DirectoryCopyFlag_T)fFlags;
3110 source.Type.Dir.fFollowSymlinks = true; /** @todo Add a flag for that in DirectoryCopyFlag_T. Later. */
3111 source.Type.Dir.fRecursive = true; /* Implicit. */
3112
3113 SourceSet.push_back(source);
3114
3115 return i_copyToGuest(SourceSet, aDestination, aProgress);
3116}
3117
3118HRESULT GuestSession::directoryCreate(const com::Utf8Str &aPath, ULONG aMode,
3119 const std::vector<DirectoryCreateFlag_T> &aFlags)
3120{
3121 AutoCaller autoCaller(this);
3122 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3123
3124 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3125 return setError(E_INVALIDARG, tr("No directory to create specified"));
3126
3127 uint32_t fFlags = DirectoryCreateFlag_None;
3128 if (aFlags.size())
3129 {
3130 for (size_t i = 0; i < aFlags.size(); i++)
3131 fFlags |= aFlags[i];
3132
3133 if (fFlags)
3134 if (!(fFlags & DirectoryCreateFlag_Parents))
3135 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), fFlags);
3136 }
3137
3138 HRESULT hrc = i_isReadyExternal();
3139 if (FAILED(hrc))
3140 return hrc;
3141
3142 LogFlowThisFuncEnter();
3143
3144 ComObjPtr <GuestDirectory> pDirectory; int rcGuest;
3145 int vrc = i_directoryCreate(aPath, (uint32_t)aMode, fFlags, &rcGuest);
3146 if (RT_FAILURE(vrc))
3147 {
3148 if (GuestProcess::i_isGuestError(vrc))
3149 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest,
3150 tr("Directory creation failed: %s"), GuestDirectory::i_guestErrorToString(rcGuest).c_str());
3151 else
3152 {
3153 switch (vrc)
3154 {
3155 case VERR_INVALID_PARAMETER:
3156 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: Invalid parameters given"));
3157 break;
3158
3159 case VERR_BROKEN_PIPE:
3160 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: Unexpectedly aborted"));
3161 break;
3162
3163 default:
3164 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: %Rrc"), vrc);
3165 break;
3166 }
3167 }
3168 }
3169
3170 return hrc;
3171}
3172
3173HRESULT GuestSession::directoryCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath,
3174 BOOL aSecure, com::Utf8Str &aDirectory)
3175{
3176 RT_NOREF(aMode, aSecure);
3177
3178 AutoCaller autoCaller(this);
3179 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3180
3181 if (RT_UNLIKELY((aTemplateName.c_str()) == NULL || *(aTemplateName.c_str()) == '\0'))
3182 return setError(E_INVALIDARG, tr("No template specified"));
3183 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3184 return setError(E_INVALIDARG, tr("No directory name specified"));
3185
3186 HRESULT hrc = i_isReadyExternal();
3187 if (FAILED(hrc))
3188 return hrc;
3189
3190 LogFlowThisFuncEnter();
3191
3192 int rcGuest;
3193 int vrc = i_fsCreateTemp(aTemplateName, aPath, true /* Directory */, aDirectory, &rcGuest);
3194 if (!RT_SUCCESS(vrc))
3195 {
3196 switch (vrc)
3197 {
3198 case VERR_GSTCTL_GUEST_ERROR:
3199 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3200 break;
3201
3202 default:
3203 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Temporary directory creation \"%s\" with template \"%s\" failed: %Rrc"),
3204 aPath.c_str(), aTemplateName.c_str(), vrc);
3205 break;
3206 }
3207 }
3208
3209 return hrc;
3210}
3211
3212HRESULT GuestSession::directoryExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3213{
3214 AutoCaller autoCaller(this);
3215 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3216
3217 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3218 return setError(E_INVALIDARG, tr("No directory to check existence for specified"));
3219
3220 HRESULT hrc = i_isReadyExternal();
3221 if (FAILED(hrc))
3222 return hrc;
3223
3224 LogFlowThisFuncEnter();
3225
3226 GuestFsObjData objData; int rcGuest;
3227 int vrc = i_directoryQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3228 if (RT_SUCCESS(vrc))
3229 *aExists = objData.mType == FsObjType_Directory;
3230 else
3231 {
3232 switch (vrc)
3233 {
3234 case VERR_GSTCTL_GUEST_ERROR:
3235 {
3236 switch (rcGuest)
3237 {
3238 case VERR_PATH_NOT_FOUND:
3239 *aExists = FALSE;
3240 break;
3241 default:
3242 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying directory existence \"%s\" failed: %s"),
3243 aPath.c_str(), GuestProcess::i_guestErrorToString(rcGuest).c_str());
3244 break;
3245 }
3246 break;
3247 }
3248
3249 default:
3250 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying directory existence \"%s\" failed: %Rrc"),
3251 aPath.c_str(), vrc);
3252 break;
3253 }
3254 }
3255
3256 return hrc;
3257}
3258
3259HRESULT GuestSession::directoryOpen(const com::Utf8Str &aPath, const com::Utf8Str &aFilter,
3260 const std::vector<DirectoryOpenFlag_T> &aFlags, ComPtr<IGuestDirectory> &aDirectory)
3261{
3262 AutoCaller autoCaller(this);
3263 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3264
3265 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3266 return setError(E_INVALIDARG, tr("No directory to open specified"));
3267 if (RT_UNLIKELY((aFilter.c_str()) != NULL && *(aFilter.c_str()) != '\0'))
3268 return setError(E_INVALIDARG, tr("Directory filters are not implemented yet"));
3269
3270 uint32_t fFlags = DirectoryOpenFlag_None;
3271 if (aFlags.size())
3272 {
3273 for (size_t i = 0; i < aFlags.size(); i++)
3274 fFlags |= aFlags[i];
3275
3276 if (fFlags)
3277 return setError(E_INVALIDARG, tr("Open flags (%#x) not implemented yet"), fFlags);
3278 }
3279
3280 HRESULT hrc = i_isReadyExternal();
3281 if (FAILED(hrc))
3282 return hrc;
3283
3284 LogFlowThisFuncEnter();
3285
3286 GuestDirectoryOpenInfo openInfo;
3287 openInfo.mPath = aPath;
3288 openInfo.mFilter = aFilter;
3289 openInfo.mFlags = fFlags;
3290
3291 ComObjPtr <GuestDirectory> pDirectory; int rcGuest;
3292 int vrc = i_directoryOpen(openInfo, pDirectory, &rcGuest);
3293 if (RT_SUCCESS(vrc))
3294 {
3295 /* Return directory object to the caller. */
3296 hrc = pDirectory.queryInterfaceTo(aDirectory.asOutParam());
3297 }
3298 else
3299 {
3300 switch (vrc)
3301 {
3302 case VERR_INVALID_PARAMETER:
3303 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening directory \"%s\" failed; invalid parameters given"),
3304 aPath.c_str());
3305 break;
3306
3307 case VERR_GSTCTL_GUEST_ERROR:
3308 hrc = GuestDirectory::i_setErrorExternal(this, rcGuest);
3309 break;
3310
3311 default:
3312 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3313 break;
3314 }
3315 }
3316
3317 return hrc;
3318}
3319
3320HRESULT GuestSession::directoryRemove(const com::Utf8Str &aPath)
3321{
3322 AutoCaller autoCaller(this);
3323 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3324
3325 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3326 return setError(E_INVALIDARG, tr("No directory to remove specified"));
3327
3328 HRESULT hrc = i_isReadyExternal();
3329 if (FAILED(hrc))
3330 return hrc;
3331
3332 LogFlowThisFuncEnter();
3333
3334 /* No flags; only remove the directory when empty. */
3335 uint32_t uFlags = 0;
3336
3337 int rcGuest;
3338 int vrc = i_directoryRemove(aPath, uFlags, &rcGuest);
3339 if (RT_FAILURE(vrc))
3340 {
3341 switch (vrc)
3342 {
3343 case VERR_NOT_SUPPORTED:
3344 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3345 tr("Handling removing guest directories not supported by installed Guest Additions"));
3346 break;
3347
3348 case VERR_GSTCTL_GUEST_ERROR:
3349 hrc = GuestDirectory::i_setErrorExternal(this, rcGuest);
3350 break;
3351
3352 default:
3353 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing guest directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3354 break;
3355 }
3356 }
3357
3358 return hrc;
3359}
3360
3361HRESULT GuestSession::directoryRemoveRecursive(const com::Utf8Str &aPath, const std::vector<DirectoryRemoveRecFlag_T> &aFlags,
3362 ComPtr<IProgress> &aProgress)
3363{
3364 RT_NOREF(aFlags);
3365
3366 AutoCaller autoCaller(this);
3367 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3368
3369 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3370 return setError(E_INVALIDARG, tr("No directory to remove recursively specified"));
3371
3372/** @todo r=bird: Must check that the flags matches the hardcoded behavior
3373 * further down!! */
3374
3375 HRESULT hrc = i_isReadyExternal();
3376 if (FAILED(hrc))
3377 return hrc;
3378
3379 LogFlowThisFuncEnter();
3380
3381 ComObjPtr<Progress> pProgress;
3382 hrc = pProgress.createObject();
3383 if (SUCCEEDED(hrc))
3384 hrc = pProgress->init(static_cast<IGuestSession *>(this),
3385 Bstr(tr("Removing guest directory")).raw(),
3386 TRUE /*aCancelable*/);
3387 if (FAILED(hrc))
3388 return hrc;
3389
3390 /* Note: At the moment we don't supply progress information while
3391 * deleting a guest directory recursively. So just complete
3392 * the progress object right now. */
3393 /** @todo Implement progress reporting on guest directory deletion! */
3394 hrc = pProgress->i_notifyComplete(S_OK);
3395 if (FAILED(hrc))
3396 return hrc;
3397
3398 /* Remove the directory + all its contents. */
3399 uint32_t uFlags = DIRREMOVE_FLAG_RECURSIVE
3400 | DIRREMOVE_FLAG_CONTENT_AND_DIR;
3401 int rcGuest;
3402 int vrc = i_directoryRemove(aPath, uFlags, &rcGuest);
3403 if (RT_FAILURE(vrc))
3404 {
3405 switch (vrc)
3406 {
3407 case VERR_NOT_SUPPORTED:
3408 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3409 tr("Handling removing guest directories recursively not supported by installed Guest Additions"));
3410 break;
3411
3412 case VERR_GSTCTL_GUEST_ERROR:
3413 hrc = GuestFile::i_setErrorExternal(this, rcGuest);
3414 break;
3415
3416 default:
3417 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Recursively removing guest directory \"%s\" failed: %Rrc"),
3418 aPath.c_str(), vrc);
3419 break;
3420 }
3421 }
3422 else
3423 {
3424 pProgress.queryInterfaceTo(aProgress.asOutParam());
3425 }
3426
3427 return hrc;
3428}
3429
3430HRESULT GuestSession::environmentScheduleSet(const com::Utf8Str &aName, const com::Utf8Str &aValue)
3431{
3432 AutoCaller autoCaller(this);
3433 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3434
3435 HRESULT hrc;
3436 if (RT_LIKELY(aName.isNotEmpty()))
3437 {
3438 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3439 {
3440 LogFlowThisFuncEnter();
3441
3442 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3443 int vrc = mData.mEnvironmentChanges.setVariable(aName, aValue);
3444 if (RT_SUCCESS(vrc))
3445 hrc = S_OK;
3446 else
3447 hrc = setErrorVrc(vrc);
3448 }
3449 else
3450 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3451 }
3452 else
3453 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3454
3455 LogFlowThisFuncLeave();
3456 return hrc;
3457}
3458
3459HRESULT GuestSession::environmentScheduleUnset(const com::Utf8Str &aName)
3460{
3461 AutoCaller autoCaller(this);
3462 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3463
3464 HRESULT hrc;
3465 if (RT_LIKELY(aName.isNotEmpty()))
3466 {
3467 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3468 {
3469 LogFlowThisFuncEnter();
3470
3471 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3472 int vrc = mData.mEnvironmentChanges.unsetVariable(aName);
3473 if (RT_SUCCESS(vrc))
3474 hrc = S_OK;
3475 else
3476 hrc = setErrorVrc(vrc);
3477 }
3478 else
3479 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3480 }
3481 else
3482 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3483
3484 LogFlowThisFuncLeave();
3485 return hrc;
3486}
3487
3488HRESULT GuestSession::environmentGetBaseVariable(const com::Utf8Str &aName, com::Utf8Str &aValue)
3489{
3490 AutoCaller autoCaller(this);
3491 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3492
3493 HRESULT hrc;
3494 if (RT_LIKELY(aName.isNotEmpty()))
3495 {
3496 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3497 {
3498 LogFlowThisFuncEnter();
3499
3500 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3501 if (mData.mpBaseEnvironment)
3502 {
3503 int vrc = mData.mpBaseEnvironment->getVariable(aName, &aValue);
3504 if (RT_SUCCESS(vrc))
3505 hrc = S_OK;
3506 else
3507 hrc = setErrorVrc(vrc);
3508 }
3509 else if (mData.mProtocolVersion < 99999)
3510 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
3511 else
3512 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
3513 }
3514 else
3515 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3516 }
3517 else
3518 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3519
3520 LogFlowThisFuncLeave();
3521 return hrc;
3522}
3523
3524HRESULT GuestSession::environmentDoesBaseVariableExist(const com::Utf8Str &aName, BOOL *aExists)
3525{
3526 AutoCaller autoCaller(this);
3527 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3528
3529 *aExists = FALSE;
3530
3531 HRESULT hrc;
3532 if (RT_LIKELY(aName.isNotEmpty()))
3533 {
3534 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3535 {
3536 LogFlowThisFuncEnter();
3537
3538 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3539 if (mData.mpBaseEnvironment)
3540 {
3541 hrc = S_OK;
3542 *aExists = mData.mpBaseEnvironment->doesVariableExist(aName);
3543 }
3544 else if (mData.mProtocolVersion < 99999)
3545 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
3546 else
3547 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
3548 }
3549 else
3550 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3551 }
3552 else
3553 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3554
3555 LogFlowThisFuncLeave();
3556 return hrc;
3557}
3558
3559HRESULT GuestSession::fileCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath, BOOL aSecure,
3560 ComPtr<IGuestFile> &aFile)
3561{
3562 RT_NOREF(aTemplateName, aMode, aPath, aSecure, aFile);
3563 ReturnComNotImplemented();
3564}
3565
3566HRESULT GuestSession::fileExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3567{
3568 AutoCaller autoCaller(this);
3569 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3570
3571 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3572 {
3573 *aExists = FALSE;
3574 return S_OK;
3575 }
3576
3577 HRESULT hrc = i_isReadyExternal();
3578 if (FAILED(hrc))
3579 return hrc;
3580
3581 LogFlowThisFuncEnter();
3582
3583 GuestFsObjData objData; int rcGuest;
3584 int vrc = i_fileQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3585 if (RT_SUCCESS(vrc))
3586 {
3587 *aExists = TRUE;
3588 return S_OK;
3589 }
3590
3591 switch (vrc)
3592 {
3593 case VERR_GSTCTL_GUEST_ERROR:
3594 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3595 break;
3596
3597/** @todo r=bird: what about VERR_PATH_NOT_FOUND and VERR_FILE_NOT_FOUND?
3598 * Where does that get converted to *aExists = FALSE? */
3599 case VERR_NOT_A_FILE:
3600 *aExists = FALSE;
3601 break;
3602
3603 default:
3604 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying file information for \"%s\" failed: %Rrc"),
3605 aPath.c_str(), vrc);
3606 break;
3607 }
3608
3609 return hrc;
3610}
3611
3612HRESULT GuestSession::fileOpen(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3613 ULONG aCreationMode, ComPtr<IGuestFile> &aFile)
3614{
3615 LogFlowThisFuncEnter();
3616
3617 const std::vector<FileOpenExFlag_T> EmptyFlags;
3618 return fileOpenEx(aPath, aAccessMode, aOpenAction, FileSharingMode_All, aCreationMode, EmptyFlags, aFile);
3619}
3620
3621HRESULT GuestSession::fileOpenEx(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3622 FileSharingMode_T aSharingMode, ULONG aCreationMode,
3623 const std::vector<FileOpenExFlag_T> &aFlags, ComPtr<IGuestFile> &aFile)
3624{
3625 AutoCaller autoCaller(this);
3626 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3627
3628 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3629 return setError(E_INVALIDARG, tr("No file to open specified"));
3630
3631 HRESULT hrc = i_isReadyExternal();
3632 if (FAILED(hrc))
3633 return hrc;
3634
3635 LogFlowThisFuncEnter();
3636
3637 GuestFileOpenInfo openInfo;
3638 openInfo.mFileName = aPath;
3639 openInfo.mCreationMode = aCreationMode;
3640
3641 /* Validate aAccessMode. */
3642 switch (aAccessMode)
3643 {
3644 case FileAccessMode_ReadOnly:
3645 RT_FALL_THRU();
3646 case FileAccessMode_WriteOnly:
3647 RT_FALL_THRU();
3648 case FileAccessMode_ReadWrite:
3649 openInfo.mAccessMode = aAccessMode;
3650 break;
3651 case FileAccessMode_AppendOnly:
3652 RT_FALL_THRU();
3653 case FileAccessMode_AppendRead:
3654 return setError(E_NOTIMPL, tr("Append access modes are not yet implemented"));
3655 default:
3656 return setError(E_INVALIDARG, tr("Unknown FileAccessMode value %u (%#x)"), aAccessMode, aAccessMode);
3657 }
3658
3659 /* Validate aOpenAction to the old format. */
3660 switch (aOpenAction)
3661 {
3662 case FileOpenAction_OpenExisting:
3663 RT_FALL_THRU();
3664 case FileOpenAction_OpenOrCreate:
3665 RT_FALL_THRU();
3666 case FileOpenAction_CreateNew:
3667 RT_FALL_THRU();
3668 case FileOpenAction_CreateOrReplace:
3669 RT_FALL_THRU();
3670 case FileOpenAction_OpenExistingTruncated:
3671 RT_FALL_THRU();
3672 case FileOpenAction_AppendOrCreate:
3673 openInfo.mOpenAction = aOpenAction;
3674 break;
3675 default:
3676 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3677 }
3678
3679 /* Validate aSharingMode. */
3680 switch (aSharingMode)
3681 {
3682 case FileSharingMode_All:
3683 openInfo.mSharingMode = aSharingMode;
3684 break;
3685 case FileSharingMode_Read:
3686 case FileSharingMode_Write:
3687 case FileSharingMode_ReadWrite:
3688 case FileSharingMode_Delete:
3689 case FileSharingMode_ReadDelete:
3690 case FileSharingMode_WriteDelete:
3691 return setError(E_NOTIMPL, tr("Only FileSharingMode_All is currently implemented"));
3692
3693 default:
3694 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3695 }
3696
3697 /* Combine and validate flags. */
3698 uint32_t fOpenEx = 0;
3699 for (size_t i = 0; i < aFlags.size(); i++)
3700 fOpenEx = aFlags[i];
3701 if (fOpenEx)
3702 return setError(E_INVALIDARG, tr("Unsupported FileOpenExFlag value(s) in aFlags (%#x)"), fOpenEx);
3703 openInfo.mfOpenEx = fOpenEx;
3704
3705 ComObjPtr <GuestFile> pFile;
3706 int rcGuest;
3707 int vrc = i_fileOpenEx(aPath, aAccessMode, aOpenAction, aSharingMode, aCreationMode, aFlags, pFile, &rcGuest);
3708 if (RT_SUCCESS(vrc))
3709 /* Return directory object to the caller. */
3710 hrc = pFile.queryInterfaceTo(aFile.asOutParam());
3711 else
3712 {
3713 switch (vrc)
3714 {
3715 case VERR_NOT_SUPPORTED:
3716 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3717 tr("Handling guest files not supported by installed Guest Additions"));
3718 break;
3719
3720 case VERR_GSTCTL_GUEST_ERROR:
3721 hrc = GuestFile::i_setErrorExternal(this, rcGuest);
3722 break;
3723
3724 default:
3725 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3726 break;
3727 }
3728 }
3729
3730 return hrc;
3731}
3732
3733HRESULT GuestSession::fileQuerySize(const com::Utf8Str &aPath, BOOL aFollowSymlinks, LONG64 *aSize)
3734{
3735 AutoCaller autoCaller(this);
3736 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3737
3738 if (aPath.isEmpty())
3739 return setError(E_INVALIDARG, tr("No path specified"));
3740
3741 HRESULT hrc = i_isReadyExternal();
3742 if (FAILED(hrc))
3743 return hrc;
3744
3745 int64_t llSize; int rcGuest;
3746 int vrc = i_fileQuerySize(aPath, aFollowSymlinks != FALSE, &llSize, &rcGuest);
3747 if (RT_SUCCESS(vrc))
3748 {
3749 *aSize = llSize;
3750 }
3751 else
3752 {
3753 if (GuestProcess::i_isGuestError(vrc))
3754 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3755 else
3756 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying file size failed: %Rrc"), vrc);
3757 }
3758
3759 return hrc;
3760}
3761
3762HRESULT GuestSession::fsObjExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3763{
3764 AutoCaller autoCaller(this);
3765 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3766
3767 if (aPath.isEmpty())
3768 return setError(E_INVALIDARG, tr("No path specified"));
3769
3770 HRESULT hrc = i_isReadyExternal();
3771 if (FAILED(hrc))
3772 return hrc;
3773
3774 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
3775
3776 *aExists = false;
3777
3778 GuestFsObjData objData;
3779 int rcGuest;
3780 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3781 if (RT_SUCCESS(vrc))
3782 {
3783 *aExists = TRUE;
3784 }
3785 else
3786 {
3787 if (GuestProcess::i_isGuestError(vrc))
3788 {
3789 if ( rcGuest == VERR_NOT_A_FILE
3790 || rcGuest == VERR_PATH_NOT_FOUND
3791 || rcGuest == VERR_FILE_NOT_FOUND
3792 || rcGuest == VERR_INVALID_NAME)
3793 {
3794 hrc = S_OK; /* Ignore these vrc values. */
3795 }
3796 else
3797 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3798 }
3799 else
3800 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3801 }
3802
3803 return hrc;
3804}
3805
3806HRESULT GuestSession::fsObjQueryInfo(const com::Utf8Str &aPath, BOOL aFollowSymlinks, ComPtr<IGuestFsObjInfo> &aInfo)
3807{
3808 AutoCaller autoCaller(this);
3809 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3810
3811 if (aPath.isEmpty())
3812 return setError(E_INVALIDARG, tr("No path specified"));
3813
3814 HRESULT hrc = i_isReadyExternal();
3815 if (FAILED(hrc))
3816 return hrc;
3817
3818 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
3819
3820 GuestFsObjData Info; int rcGuest;
3821 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, Info, &rcGuest);
3822 if (RT_SUCCESS(vrc))
3823 {
3824 ComObjPtr<GuestFsObjInfo> ptrFsObjInfo;
3825 hrc = ptrFsObjInfo.createObject();
3826 if (SUCCEEDED(hrc))
3827 {
3828 vrc = ptrFsObjInfo->init(Info);
3829 if (RT_SUCCESS(vrc))
3830 hrc = ptrFsObjInfo.queryInterfaceTo(aInfo.asOutParam());
3831 else
3832 hrc = setErrorVrc(vrc);
3833 }
3834 }
3835 else
3836 {
3837 if (GuestProcess::i_isGuestError(vrc))
3838 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3839 else
3840 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3841 }
3842
3843 return hrc;
3844}
3845
3846HRESULT GuestSession::fsObjRemove(const com::Utf8Str &aPath)
3847{
3848 AutoCaller autoCaller(this);
3849 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3850
3851 if (RT_UNLIKELY(aPath.isEmpty()))
3852 return setError(E_INVALIDARG, tr("No path specified"));
3853
3854 HRESULT hrc = i_isReadyExternal();
3855 if (FAILED(hrc))
3856 return hrc;
3857
3858 LogFlowThisFunc(("aPath=%s\n", aPath.c_str()));
3859
3860 int rcGuest;
3861 int vrc = i_fileRemove(aPath, &rcGuest);
3862 if (RT_FAILURE(vrc))
3863 {
3864 if (GuestProcess::i_isGuestError(vrc))
3865 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3866 else
3867 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3868 }
3869
3870 return hrc;
3871}
3872
3873HRESULT GuestSession::fsObjRemoveArray(const std::vector<com::Utf8Str> &aPaths, ComPtr<IProgress> &aProgress)
3874{
3875 AutoCaller autoCaller(this);
3876 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3877
3878 RT_NOREF(aPaths, aProgress);
3879
3880 return E_NOTIMPL;
3881}
3882
3883HRESULT GuestSession::fsObjRename(const com::Utf8Str &aSource,
3884 const com::Utf8Str &aDestination,
3885 const std::vector<FsObjRenameFlag_T> &aFlags)
3886{
3887 AutoCaller autoCaller(this);
3888 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3889
3890 if (RT_UNLIKELY(aSource.isEmpty()))
3891 return setError(E_INVALIDARG, tr("No source path specified"));
3892
3893 if (RT_UNLIKELY(aDestination.isEmpty()))
3894 return setError(E_INVALIDARG, tr("No destination path specified"));
3895
3896 HRESULT hrc = i_isReadyExternal();
3897 if (FAILED(hrc))
3898 return hrc;
3899
3900 /* Combine, validate and convert flags. */
3901 uint32_t fApiFlags = 0;
3902 for (size_t i = 0; i < aFlags.size(); i++)
3903 fApiFlags |= aFlags[i];
3904 if (fApiFlags & ~((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace))
3905 return setError(E_INVALIDARG, tr("Unknown rename flag: %#x"), fApiFlags);
3906
3907 LogFlowThisFunc(("aSource=%s, aDestination=%s\n", aSource.c_str(), aDestination.c_str()));
3908
3909 AssertCompile(FsObjRenameFlag_NoReplace == 0);
3910 AssertCompile(FsObjRenameFlag_Replace != 0);
3911 uint32_t fBackend;
3912 if ((fApiFlags & ((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace)) == FsObjRenameFlag_Replace)
3913 fBackend = PATHRENAME_FLAG_REPLACE;
3914 else
3915 fBackend = PATHRENAME_FLAG_NO_REPLACE;
3916
3917 /* Call worker to do the job. */
3918 int rcGuest;
3919 int vrc = i_pathRename(aSource, aDestination, fBackend, &rcGuest);
3920 if (RT_FAILURE(vrc))
3921 {
3922 switch (vrc)
3923 {
3924 case VERR_NOT_SUPPORTED:
3925 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3926 tr("Handling renaming guest directories not supported by installed Guest Additions"));
3927 break;
3928
3929 case VERR_GSTCTL_GUEST_ERROR:
3930 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Renaming guest directory failed: %Rrc"), rcGuest);
3931 break;
3932
3933 default:
3934 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming guest directory \"%s\" failed: %Rrc"),
3935 aSource.c_str(), vrc);
3936 break;
3937 }
3938 }
3939
3940 return hrc;
3941}
3942
3943HRESULT GuestSession::fsObjMove(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3944 const std::vector<FsObjMoveFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3945{
3946 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3947 ReturnComNotImplemented();
3948}
3949
3950HRESULT GuestSession::fsObjSetACL(const com::Utf8Str &aPath, BOOL aFollowSymlinks, const com::Utf8Str &aAcl, ULONG aMode)
3951{
3952 RT_NOREF(aPath, aFollowSymlinks, aAcl, aMode);
3953 ReturnComNotImplemented();
3954}
3955
3956
3957HRESULT GuestSession::processCreate(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
3958 const std::vector<com::Utf8Str> &aEnvironment,
3959 const std::vector<ProcessCreateFlag_T> &aFlags,
3960 ULONG aTimeoutMS, ComPtr<IGuestProcess> &aGuestProcess)
3961{
3962 LogFlowThisFuncEnter();
3963
3964 std::vector<LONG> affinityIgnored;
3965 return processCreateEx(aExecutable, aArguments, aEnvironment, aFlags, aTimeoutMS, ProcessPriority_Default,
3966 affinityIgnored, aGuestProcess);
3967}
3968
3969HRESULT GuestSession::processCreateEx(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
3970 const std::vector<com::Utf8Str> &aEnvironment,
3971 const std::vector<ProcessCreateFlag_T> &aFlags, ULONG aTimeoutMS,
3972 ProcessPriority_T aPriority, const std::vector<LONG> &aAffinity,
3973 ComPtr<IGuestProcess> &aGuestProcess)
3974{
3975 AutoCaller autoCaller(this);
3976 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3977
3978 HRESULT hr = i_isReadyExternal();
3979 if (FAILED(hr))
3980 return hr;
3981
3982 /** @todo r=bird: Check input better? aPriority is passed on to the guest
3983 * without any validation. Flags not existing in this vbox version are
3984 * ignored, potentially doing something entirely different than what the
3985 * caller had in mind. */
3986
3987 /*
3988 * Must have an executable to execute. If none is given, we try use the
3989 * zero'th argument.
3990 */
3991 const char *pszExecutable = aExecutable.c_str();
3992 if (RT_UNLIKELY(pszExecutable == NULL || *pszExecutable == '\0'))
3993 {
3994 if (aArguments.size() > 0)
3995 pszExecutable = aArguments[0].c_str();
3996 if (pszExecutable == NULL || *pszExecutable == '\0')
3997 return setError(E_INVALIDARG, tr("No command to execute specified"));
3998 }
3999
4000 LogFlowThisFuncEnter();
4001
4002 /*
4003 * Build the process startup info.
4004 */
4005 GuestProcessStartupInfo procInfo;
4006
4007 /* Executable and arguments. */
4008 procInfo.mExecutable = pszExecutable;
4009 if (aArguments.size())
4010 for (size_t i = 0; i < aArguments.size(); i++)
4011 procInfo.mArguments.push_back(aArguments[i]);
4012
4013 /* Combine the environment changes associated with the ones passed in by
4014 the caller, giving priority to the latter. The changes are putenv style
4015 and will be applied to the standard environment for the guest user. */
4016 int vrc = procInfo.mEnvironmentChanges.copy(mData.mEnvironmentChanges);
4017 if (RT_SUCCESS(vrc))
4018 vrc = procInfo.mEnvironmentChanges.applyPutEnvArray(aEnvironment);
4019 if (RT_SUCCESS(vrc))
4020 {
4021 /* Convert the flag array into a mask. */
4022 if (aFlags.size())
4023 for (size_t i = 0; i < aFlags.size(); i++)
4024 procInfo.mFlags |= aFlags[i];
4025
4026 procInfo.mTimeoutMS = aTimeoutMS;
4027
4028 /** @todo use RTCPUSET instead of archaic 64-bit variables! */
4029 if (aAffinity.size())
4030 for (size_t i = 0; i < aAffinity.size(); i++)
4031 if (aAffinity[i])
4032 procInfo.mAffinity |= (uint64_t)1 << i;
4033
4034 procInfo.mPriority = aPriority;
4035
4036 /*
4037 * Create a guest process object.
4038 */
4039 ComObjPtr<GuestProcess> pProcess;
4040 vrc = i_processCreateEx(procInfo, pProcess);
4041 if (RT_SUCCESS(vrc))
4042 {
4043 ComPtr<IGuestProcess> pIProcess;
4044 hr = pProcess.queryInterfaceTo(pIProcess.asOutParam());
4045 if (SUCCEEDED(hr))
4046 {
4047 /*
4048 * Start the process.
4049 */
4050 vrc = pProcess->i_startProcessAsync();
4051 if (RT_SUCCESS(vrc))
4052 {
4053 aGuestProcess = pIProcess;
4054
4055 LogFlowFuncLeaveRC(vrc);
4056 return S_OK;
4057 }
4058
4059 hr = setErrorVrc(vrc, tr("Failed to start guest process: %Rrc"), vrc);
4060 }
4061 }
4062 else if (vrc == VERR_GSTCTL_MAX_OBJECTS_REACHED)
4063 hr = setErrorVrc(vrc, tr("Maximum number of concurrent guest processes per session (%u) reached"),
4064 VBOX_GUESTCTRL_MAX_OBJECTS);
4065 else
4066 hr = setErrorVrc(vrc, tr("Failed to create guest process object: %Rrc"), vrc);
4067 }
4068 else
4069 hr = setErrorVrc(vrc, tr("Failed to set up the environment: %Rrc"), vrc);
4070
4071 LogFlowFuncLeaveRC(vrc);
4072 return hr;
4073}
4074
4075HRESULT GuestSession::processGet(ULONG aPid, ComPtr<IGuestProcess> &aGuestProcess)
4076
4077{
4078 AutoCaller autoCaller(this);
4079 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4080
4081 if (aPid == 0)
4082 return setError(E_INVALIDARG, tr("No valid process ID (PID) specified"));
4083
4084 LogFlowThisFunc(("PID=%RU32\n", aPid));
4085
4086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4087
4088 HRESULT hr = S_OK;
4089
4090 ComObjPtr<GuestProcess> pProcess;
4091 int rc = i_processGetByPID(aPid, &pProcess);
4092 if (RT_FAILURE(rc))
4093 hr = setError(E_INVALIDARG, tr("No process with PID %RU32 found"), aPid);
4094
4095 /* This will set (*aProcess) to NULL if pProgress is NULL. */
4096 HRESULT hr2 = pProcess.queryInterfaceTo(aGuestProcess.asOutParam());
4097 if (SUCCEEDED(hr))
4098 hr = hr2;
4099
4100 LogFlowThisFunc(("aProcess=%p, hr=%Rhrc\n", (IGuestProcess*)aGuestProcess, hr));
4101 return hr;
4102}
4103
4104HRESULT GuestSession::symlinkCreate(const com::Utf8Str &aSource, const com::Utf8Str &aTarget, SymlinkType_T aType)
4105{
4106 RT_NOREF(aSource, aTarget, aType);
4107 ReturnComNotImplemented();
4108}
4109
4110HRESULT GuestSession::symlinkExists(const com::Utf8Str &aSymlink, BOOL *aExists)
4111
4112{
4113 RT_NOREF(aSymlink, aExists);
4114 ReturnComNotImplemented();
4115}
4116
4117HRESULT GuestSession::symlinkRead(const com::Utf8Str &aSymlink, const std::vector<SymlinkReadFlag_T> &aFlags,
4118 com::Utf8Str &aTarget)
4119{
4120 RT_NOREF(aSymlink, aFlags, aTarget);
4121 ReturnComNotImplemented();
4122}
4123
4124HRESULT GuestSession::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
4125{
4126 AutoCaller autoCaller(this);
4127 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4128
4129 /* Note: No call to i_isReadyExternal() needed here, as the session might not has been started (yet). */
4130
4131 LogFlowThisFuncEnter();
4132
4133 HRESULT hrc = S_OK;
4134
4135 /*
4136 * Note: Do not hold any locks here while waiting!
4137 */
4138 int rcGuest; GuestSessionWaitResult_T waitResult;
4139 int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &rcGuest);
4140 if (RT_SUCCESS(vrc))
4141 *aReason = waitResult;
4142 else
4143 {
4144 switch (vrc)
4145 {
4146 case VERR_GSTCTL_GUEST_ERROR:
4147 hrc = GuestSession::i_setErrorExternal(this, rcGuest);
4148 break;
4149
4150 case VERR_TIMEOUT:
4151 *aReason = GuestSessionWaitResult_Timeout;
4152 break;
4153
4154 default:
4155 {
4156 const char *pszSessionName = mData.mSession.mName.c_str();
4157 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4158 tr("Waiting for guest session \"%s\" failed: %Rrc"),
4159 pszSessionName ? pszSessionName : tr("Unnamed"), vrc);
4160 break;
4161 }
4162 }
4163 }
4164
4165 LogFlowFuncLeaveRC(vrc);
4166 return hrc;
4167}
4168
4169HRESULT GuestSession::waitForArray(const std::vector<GuestSessionWaitForFlag_T> &aWaitFor, ULONG aTimeoutMS,
4170 GuestSessionWaitResult_T *aReason)
4171{
4172 AutoCaller autoCaller(this);
4173 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4174
4175 /* Note: No call to i_isReadyExternal() needed here, as the session might not has been started (yet). */
4176
4177 LogFlowThisFuncEnter();
4178
4179 /*
4180 * Note: Do not hold any locks here while waiting!
4181 */
4182 uint32_t fWaitFor = GuestSessionWaitForFlag_None;
4183 for (size_t i = 0; i < aWaitFor.size(); i++)
4184 fWaitFor |= aWaitFor[i];
4185
4186 return WaitFor(fWaitFor, aTimeoutMS, aReason);
4187}
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