VirtualBox

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

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

Main/Guest: Added a few codereview comments, mainly in the guest base class area. Replaced the object ID management in GuestSession with a classic allocation bitmap approach as that's much less memory intensive than 16 byte structs in a insert-only std::map and an additional map tracking free 32-bit IDs after we run out. bugref:9313

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 131.7 KB
Line 
1/* $Id: GuestSessionImpl.cpp 75861 2018-12-02 00:26:36Z 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.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_MAIN_GUESTSESSION
23#include "LoggingNew.h"
24
25#include "GuestImpl.h"
26#ifndef VBOX_WITH_GUEST_CONTROL
27# error "VBOX_WITH_GUEST_CONTROL must defined in this file"
28#endif
29#include "GuestSessionImpl.h"
30#include "GuestSessionImplTasks.h"
31#include "GuestCtrlImplPrivate.h"
32#include "VirtualBoxErrorInfoImpl.h"
33
34#include "Global.h"
35#include "AutoCaller.h"
36#include "ProgressImpl.h"
37#include "VBoxEvents.h"
38#include "VMMDev.h"
39#include "ThreadTask.h"
40
41#include <memory> /* For auto_ptr. */
42
43#include <iprt/cpp/utils.h> /* For unconst(). */
44#include <iprt/ctype.h>
45#include <iprt/env.h>
46#include <iprt/file.h> /* For CopyTo/From. */
47#include <iprt/path.h>
48#include <iprt/rand.h>
49
50#include <VBox/com/array.h>
51#include <VBox/com/listeners.h>
52#include <VBox/version.h>
53
54
55/**
56 * Base class representing an internal
57 * asynchronous session task.
58 */
59class GuestSessionTaskInternal : public ThreadTask
60{
61public:
62
63 GuestSessionTaskInternal(GuestSession *pSession)
64 : ThreadTask("GenericGuestSessionTaskInternal")
65 , mSession(pSession)
66 , mRC(VINF_SUCCESS) { }
67
68 virtual ~GuestSessionTaskInternal(void) { }
69
70 int rc(void) const { return mRC; }
71 bool isOk(void) const { return RT_SUCCESS(mRC); }
72 const ComObjPtr<GuestSession> &Session(void) const { return mSession; }
73
74protected:
75
76 const ComObjPtr<GuestSession> mSession;
77 int mRC;
78};
79
80/**
81 * Class for asynchronously opening a guest session.
82 */
83class GuestSessionTaskInternalOpen : public GuestSessionTaskInternal
84{
85public:
86
87 GuestSessionTaskInternalOpen(GuestSession *pSession)
88 : GuestSessionTaskInternal(pSession)
89 {
90 m_strTaskName = "gctlSesStart";
91 }
92
93 void handler()
94 {
95 GuestSession::i_startSessionThreadTask(this);
96 }
97};
98
99/**
100 * Internal listener class to serve events in an
101 * active manner, e.g. without polling delays.
102 */
103class GuestSessionListener
104{
105public:
106
107 GuestSessionListener(void)
108 {
109 }
110
111 virtual ~GuestSessionListener(void)
112 {
113 }
114
115 HRESULT init(GuestSession *pSession)
116 {
117 AssertPtrReturn(pSession, E_POINTER);
118 mSession = pSession;
119 return S_OK;
120 }
121
122 void uninit(void)
123 {
124 mSession = NULL;
125 }
126
127 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
128 {
129 switch (aType)
130 {
131 case VBoxEventType_OnGuestSessionStateChanged:
132 {
133 AssertPtrReturn(mSession, E_POINTER);
134 int rc2 = mSession->signalWaitEvent(aType, aEvent);
135 RT_NOREF(rc2);
136#ifdef DEBUG_andy
137 LogFlowFunc(("Signalling events of type=%RU32, session=%p resulted in rc=%Rrc\n",
138 aType, mSession, rc2));
139#endif
140 break;
141 }
142
143 default:
144 AssertMsgFailed(("Unhandled event %RU32\n", aType));
145 break;
146 }
147
148 return S_OK;
149 }
150
151private:
152
153 GuestSession *mSession;
154};
155typedef ListenerImpl<GuestSessionListener, GuestSession*> GuestSessionListenerImpl;
156
157VBOX_LISTENER_DECLARE(GuestSessionListenerImpl)
158
159// constructor / destructor
160/////////////////////////////////////////////////////////////////////////////
161
162DEFINE_EMPTY_CTOR_DTOR(GuestSession)
163
164HRESULT GuestSession::FinalConstruct(void)
165{
166 LogFlowThisFuncEnter();
167 return BaseFinalConstruct();
168}
169
170void GuestSession::FinalRelease(void)
171{
172 LogFlowThisFuncEnter();
173 uninit();
174 BaseFinalRelease();
175 LogFlowThisFuncLeave();
176}
177
178// public initializer/uninitializer for internal purposes only
179/////////////////////////////////////////////////////////////////////////////
180
181/**
182 * Initializes a guest session but does *not* open in on the guest side
183 * yet. This needs to be done via the openSession() / openSessionAsync calls.
184 *
185 * @return IPRT status code.
186 ** @todo Docs!
187 */
188int GuestSession::init(Guest *pGuest, const GuestSessionStartupInfo &ssInfo,
189 const GuestCredentials &guestCreds)
190{
191 LogFlowThisFunc(("pGuest=%p, ssInfo=%p, guestCreds=%p\n",
192 pGuest, &ssInfo, &guestCreds));
193
194 /* Enclose the state transition NotReady->InInit->Ready. */
195 AutoInitSpan autoInitSpan(this);
196 AssertReturn(autoInitSpan.isOk(), VERR_OBJECT_DESTROYED);
197
198 AssertPtrReturn(pGuest, VERR_INVALID_POINTER);
199
200 /*
201 * Initialize our data members from the input.
202 */
203 mParent = pGuest;
204
205 /* Copy over startup info. */
206 /** @todo Use an overloaded copy operator. Later. */
207 mData.mSession.mID = ssInfo.mID;
208 mData.mSession.mIsInternal = ssInfo.mIsInternal;
209 mData.mSession.mName = ssInfo.mName;
210 mData.mSession.mOpenFlags = ssInfo.mOpenFlags;
211 mData.mSession.mOpenTimeoutMS = ssInfo.mOpenTimeoutMS;
212
213 /* Copy over session credentials. */
214 /** @todo Use an overloaded copy operator. Later. */
215 mData.mCredentials.mUser = guestCreds.mUser;
216 mData.mCredentials.mPassword = guestCreds.mPassword;
217 mData.mCredentials.mDomain = guestCreds.mDomain;
218
219 /* Initialize the remainder of the data. */
220 mData.mRC = VINF_SUCCESS;
221 mData.mStatus = GuestSessionStatus_Undefined;
222 mData.mpBaseEnvironment = NULL;
223
224 /*
225 * Register an object for the session itself to clearly
226 * distinguish callbacks which are for this session directly, or for
227 * objects (like files, directories, ...) which are bound to this session.
228 */
229 int rc = i_objectRegister(SESSIONOBJECTTYPE_SESSION, &mData.mObjectID);
230 if (RT_SUCCESS(rc))
231 {
232 rc = mData.mEnvironmentChanges.initChangeRecord();
233 if (RT_SUCCESS(rc))
234 {
235 rc = RTCritSectInit(&mWaitEventCritSect);
236 AssertRC(rc);
237 }
238 }
239
240 if (RT_SUCCESS(rc))
241 rc = i_determineProtocolVersion();
242
243 if (RT_SUCCESS(rc))
244 {
245 /*
246 * <Replace this if you figure out what the code is doing.>
247 */
248 HRESULT hr = unconst(mEventSource).createObject();
249 if (SUCCEEDED(hr))
250 hr = mEventSource->init();
251 if (SUCCEEDED(hr))
252 {
253 try
254 {
255 GuestSessionListener *pListener = new GuestSessionListener();
256 ComObjPtr<GuestSessionListenerImpl> thisListener;
257 hr = thisListener.createObject();
258 if (SUCCEEDED(hr))
259 hr = thisListener->init(pListener, this); /* thisListener takes ownership of pListener. */
260 if (SUCCEEDED(hr))
261 {
262 com::SafeArray <VBoxEventType_T> eventTypes;
263 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
264 hr = mEventSource->RegisterListener(thisListener,
265 ComSafeArrayAsInParam(eventTypes),
266 TRUE /* Active listener */);
267 if (SUCCEEDED(hr))
268 {
269 mLocalListener = thisListener;
270
271 /*
272 * Mark this object as operational and return success.
273 */
274 autoInitSpan.setSucceeded();
275 LogFlowThisFunc(("mName=%s mID=%RU32 mIsInternal=%RTbool rc=VINF_SUCCESS\n",
276 mData.mSession.mName.c_str(), mData.mSession.mID, mData.mSession.mIsInternal));
277 return VINF_SUCCESS;
278 }
279 }
280 }
281 catch (std::bad_alloc &)
282 {
283 hr = E_OUTOFMEMORY;
284 }
285 }
286 rc = Global::vboxStatusCodeFromCOM(hr);
287 }
288
289 autoInitSpan.setFailed();
290 LogThisFunc(("Failed! mName=%s mID=%RU32 mIsInternal=%RTbool => rc=%Rrc\n",
291 mData.mSession.mName.c_str(), mData.mSession.mID, mData.mSession.mIsInternal, rc));
292 return rc;
293}
294
295/**
296 * Uninitializes the instance.
297 * Called from FinalRelease().
298 */
299void GuestSession::uninit(void)
300{
301 /* Enclose the state transition Ready->InUninit->NotReady. */
302 AutoUninitSpan autoUninitSpan(this);
303 if (autoUninitSpan.uninitDone())
304 return;
305
306 LogFlowThisFuncEnter();
307
308 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
309
310 LogFlowThisFunc(("Closing directories (%zu total)\n",
311 mData.mDirectories.size()));
312 for (SessionDirectories::iterator itDirs = mData.mDirectories.begin();
313 itDirs != mData.mDirectories.end(); ++itDirs)
314 {
315 itDirs->second->i_onRemove();
316 itDirs->second->uninit();
317 }
318 mData.mDirectories.clear();
319
320 LogFlowThisFunc(("Closing files (%zu total)\n",
321 mData.mFiles.size()));
322 for (SessionFiles::iterator itFiles = mData.mFiles.begin();
323 itFiles != mData.mFiles.end(); ++itFiles)
324 {
325 itFiles->second->i_onRemove();
326 itFiles->second->uninit();
327 }
328 mData.mFiles.clear();
329
330 LogFlowThisFunc(("Closing processes (%zu total)\n",
331 mData.mProcesses.size()));
332 for (SessionProcesses::iterator itProcs = mData.mProcesses.begin();
333 itProcs != mData.mProcesses.end(); ++itProcs)
334 {
335 itProcs->second->i_onRemove();
336 itProcs->second->uninit();
337 }
338 mData.mProcesses.clear();
339
340 /* Unregister the session's object ID. */
341 i_objectUnregister(mData.mObjectID);
342
343 mData.mObjects.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 idObject = pDirectory->getObjectID();
1020
1021 LogFlowFunc(("Removing directory (objectID=%RU32) ...\n", idObject));
1022
1023 int rc = i_objectUnregister(idObject);
1024 if (RT_FAILURE(rc))
1025 return rc;
1026
1027 SessionDirectories::iterator itDirs = mData.mDirectories.find(idObject);
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 idObject, 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 idObject;
1165 int rc = i_objectRegister(SESSIONOBJECTTYPE_DIRECTORY, &idObject);
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 */, idObject, 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[idObject] = 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 idObject = pFile->getObjectID();
1419
1420 LogFlowFunc(("Removing file (objectID=%RU32) ...\n", idObject));
1421
1422 int rc = i_objectUnregister(idObject);
1423 if (RT_FAILURE(rc))
1424 return rc;
1425
1426 SessionFiles::iterator itFiles = mData.mFiles.find(idObject);
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 idObject;
1523 int rc = i_objectRegister(SESSIONOBJECTTYPE_FILE, &idObject);
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 */, idObject, 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[idObject] = 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 with the session, i.e. allocates an object ID.
2038 *
2039 * @return VBox status code.
2040 * @retval VERR_GSTCTL_MAX_OBJECTS_REACHED if the maximum of concurrent objects
2041 * is reached.
2042 * @param enmType Session object type to register.
2043 * @param pidObject Where to return the object ID on success.
2044 */
2045int GuestSession::i_objectRegister(SESSIONOBJECTTYPE enmType, uint32_t *pidObject)
2046{
2047 /*
2048 * Pick a random bit as starting point. If it's in use, search forward
2049 * for a free one, wrapping around. We've reserved both the zero'th and
2050 * max-1 IDs (see Data constructor).
2051 */
2052 uint32_t idObject = RTRandU32Ex(1, VBOX_GUESTCTRL_MAX_OBJECTS - 2);
2053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2054 if (!ASMBitTestAndSet(&mData.bmObjectIds[0], idObject))
2055 { /* likely */ }
2056 else if (mData.mObjects.size() < VBOX_GUESTCTRL_MAX_OBJECTS - 2 /* First and last are not used */)
2057 {
2058 /* Forward search. */
2059 int iHit = ASMBitNextClear(&mData.bmObjectIds[0], VBOX_GUESTCTRL_MAX_OBJECTS, idObject);
2060 if (iHit < 0)
2061 iHit = ASMBitFirstClear(&mData.bmObjectIds[0], VBOX_GUESTCTRL_MAX_OBJECTS);
2062 AssertLogRelMsgReturn(iHit >= 0, ("object count: %#zu\n", mData.mObjects.size()), VERR_GSTCTL_MAX_OBJECTS_REACHED);
2063 idObject = iHit;
2064 AssertLogRelMsgReturn(!ASMBitTestAndSet(&mData.bmObjectIds[0], idObject), ("idObject=%#x\n", idObject), VERR_INTERNAL_ERROR_2);
2065 }
2066 else
2067 {
2068 LogFunc(("enmType=%RU32 -> VERR_GSTCTL_MAX_OBJECTS_REACHED!! (%zu objects)\n", enmType, mData.mObjects.size()));
2069 return VERR_GSTCTL_MAX_OBJECTS_REACHED;
2070 }
2071
2072 Log2Func(("enmType=%RU32 -> idObject=%RU32 (%zu objects)\n", enmType, idObject, mData.mObjects.size()));
2073
2074 try
2075 {
2076 mData.mObjects[idObject].enmType = enmType;
2077 mData.mObjects[idObject].msBirth = RTTimeMilliTS();
2078 }
2079 catch (std::bad_alloc &)
2080 {
2081 ASMBitClear(&mData.bmObjectIds[0], idObject);
2082 return VERR_NO_MEMORY;
2083 }
2084
2085 alock.release();
2086
2087 *pidObject = idObject;
2088 return VINF_SUCCESS;
2089}
2090
2091/**
2092 * Unregisters an object from a session.
2093 *
2094 * @retval VINF_SUCCESS on success.
2095 * @retval VERR_NOT_FOUND if the object ID was not found.
2096 * @param idObject Object ID to unregister.
2097 */
2098int GuestSession::i_objectUnregister(uint32_t idObject)
2099{
2100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2101
2102 int rc = VINF_SUCCESS;
2103 AssertMsgStmt(ASMBitTestAndClear(&mData.bmObjectIds, idObject), ("idObject=%#x\n", idObject), rc = VERR_NOT_FOUND);
2104
2105 SessionObjects::const_iterator ItObj = mData.mObjects.find(idObject);
2106 AssertMsgReturn(ItObj != mData.mObjects.end(), ("idObject=%#x\n", idObject), VERR_NOT_FOUND);
2107 mData.mObjects.erase(ItObj);
2108
2109 return rc;
2110}
2111
2112int GuestSession::i_pathRename(const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags, int *prcGuest)
2113{
2114 AssertReturn(!(uFlags & ~PATHRENAME_FLAG_VALID_MASK), VERR_INVALID_PARAMETER);
2115
2116 LogFlowThisFunc(("strSource=%s, strDest=%s, uFlags=0x%x\n",
2117 strSource.c_str(), strDest.c_str(), uFlags));
2118
2119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2120
2121 GuestWaitEvent *pEvent = NULL;
2122 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2123 if (RT_FAILURE(vrc))
2124 return vrc;
2125
2126 /* Prepare HGCM call. */
2127 VBOXHGCMSVCPARM paParms[8];
2128 int i = 0;
2129 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2130 HGCMSvcSetPv(&paParms[i++], (void*)strSource.c_str(),
2131 (ULONG)strSource.length() + 1);
2132 HGCMSvcSetPv(&paParms[i++], (void*)strDest.c_str(),
2133 (ULONG)strDest.length() + 1);
2134 HGCMSvcSetU32(&paParms[i++], uFlags);
2135
2136 alock.release(); /* Drop write lock before sending. */
2137
2138 vrc = i_sendCommand(HOST_PATH_RENAME, i, paParms);
2139 if (RT_SUCCESS(vrc))
2140 {
2141 vrc = pEvent->Wait(30 * 1000);
2142 if ( vrc == VERR_GSTCTL_GUEST_ERROR
2143 && prcGuest)
2144 *prcGuest = pEvent->GuestResult();
2145 }
2146
2147 unregisterWaitEvent(pEvent);
2148
2149 LogFlowFuncLeaveRC(vrc);
2150 return vrc;
2151}
2152
2153/**
2154 * Returns the user's absolute documents path, if any.
2155 *
2156 * @return VBox status code.
2157 * @param strPath Where to store the user's document path.
2158 * @param prcGuest Guest rc, when returning VERR_GSTCTL_GUEST_ERROR.
2159 * Any other return code indicates some host side error.
2160 */
2161int GuestSession::i_pathUserDocuments(Utf8Str &strPath, int *prcGuest)
2162{
2163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2164
2165 /** @todo Cache the user's document path? */
2166
2167 GuestWaitEvent *pEvent = NULL;
2168 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2169 if (RT_FAILURE(vrc))
2170 return vrc;
2171
2172 /* Prepare HGCM call. */
2173 VBOXHGCMSVCPARM paParms[2];
2174 int i = 0;
2175 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2176
2177 alock.release(); /* Drop write lock before sending. */
2178
2179 vrc = i_sendCommand(HOST_PATH_USER_DOCUMENTS, i, paParms);
2180 if (RT_SUCCESS(vrc))
2181 {
2182 vrc = pEvent->Wait(30 * 1000);
2183 if (RT_SUCCESS(vrc))
2184 {
2185 strPath = pEvent->Payload().ToString();
2186 }
2187 else
2188 {
2189 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2190 {
2191 if (prcGuest)
2192 *prcGuest = pEvent->GuestResult();
2193 }
2194 }
2195 }
2196
2197 unregisterWaitEvent(pEvent);
2198
2199 LogFlowFuncLeaveRC(vrc);
2200 return vrc;
2201}
2202
2203/**
2204 * Returns the user's absolute home path, if any.
2205 *
2206 * @return VBox status code.
2207 * @param strPath Where to store the user's home path.
2208 * @param prcGuest Guest rc, when returning VERR_GSTCTL_GUEST_ERROR.
2209 * Any other return code indicates some host side error.
2210 */
2211int GuestSession::i_pathUserHome(Utf8Str &strPath, int *prcGuest)
2212{
2213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2214
2215 /** @todo Cache the user's home path? */
2216
2217 GuestWaitEvent *pEvent = NULL;
2218 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2219 if (RT_FAILURE(vrc))
2220 return vrc;
2221
2222 /* Prepare HGCM call. */
2223 VBOXHGCMSVCPARM paParms[2];
2224 int i = 0;
2225 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2226
2227 alock.release(); /* Drop write lock before sending. */
2228
2229 vrc = i_sendCommand(HOST_PATH_USER_HOME, i, paParms);
2230 if (RT_SUCCESS(vrc))
2231 {
2232 vrc = pEvent->Wait(30 * 1000);
2233 if (RT_SUCCESS(vrc))
2234 {
2235 strPath = pEvent->Payload().ToString();
2236 }
2237 else
2238 {
2239 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2240 {
2241 if (prcGuest)
2242 *prcGuest = pEvent->GuestResult();
2243 }
2244 }
2245 }
2246
2247 unregisterWaitEvent(pEvent);
2248
2249 LogFlowFuncLeaveRC(vrc);
2250 return vrc;
2251}
2252
2253/**
2254 * Unregisters a process object from a session.
2255 *
2256 * @return VBox status code. VERR_NOT_FOUND if the process is not registered (anymore).
2257 * @param pProcess Process object to unregister from session.
2258 */
2259int GuestSession::i_processUnregister(GuestProcess *pProcess)
2260{
2261 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
2262
2263 LogFlowThisFunc(("pProcess=%p\n", pProcess));
2264
2265 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2266
2267 const uint32_t idObject = pProcess->getObjectID();
2268
2269 LogFlowFunc(("Removing process (objectID=%RU32) ...\n", idObject));
2270
2271 int rc = i_objectUnregister(idObject);
2272 if (RT_FAILURE(rc))
2273 return rc;
2274
2275 SessionProcesses::iterator itProcs = mData.mProcesses.find(idObject);
2276 AssertReturn(itProcs != mData.mProcesses.end(), VERR_NOT_FOUND);
2277
2278 /* Make sure to consume the pointer before the one of the iterator gets released. */
2279 ComObjPtr<GuestProcess> pProc = pProcess;
2280
2281 ULONG uPID;
2282 HRESULT hr = pProc->COMGETTER(PID)(&uPID);
2283 ComAssertComRC(hr);
2284
2285 LogFlowFunc(("Removing process ID=%RU32 (session %RU32, guest PID %RU32, now total %zu processes)\n",
2286 idObject, mData.mSession.mID, uPID, mData.mProcesses.size()));
2287
2288 rc = pProcess->i_onRemove();
2289 AssertRCReturn(rc, rc);
2290
2291 mData.mProcesses.erase(itProcs);
2292
2293 alock.release(); /* Release lock before firing off event. */
2294
2295 fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProc, uPID, false /* Process unregistered */);
2296
2297 pProc.setNull();
2298
2299 LogFlowFuncLeaveRC(rc);
2300 return rc;
2301}
2302
2303/**
2304 * Creates but does *not* start the process yet.
2305 *
2306 * See GuestProcess::startProcess() or GuestProcess::startProcessAsync() for
2307 * starting the process.
2308 *
2309 * @return IPRT status code.
2310 * @param procInfo
2311 * @param pProcess
2312 */
2313int GuestSession::i_processCreateEx(GuestProcessStartupInfo &procInfo, ComObjPtr<GuestProcess> &pProcess)
2314{
2315 LogFlowFunc(("mExe=%s, mFlags=%x, mTimeoutMS=%RU32\n",
2316 procInfo.mExecutable.c_str(), procInfo.mFlags, procInfo.mTimeoutMS));
2317#ifdef DEBUG
2318 if (procInfo.mArguments.size())
2319 {
2320 LogFlowFunc(("Arguments:"));
2321 ProcessArguments::const_iterator it = procInfo.mArguments.begin();
2322 while (it != procInfo.mArguments.end())
2323 {
2324 LogFlow((" %s", (*it).c_str()));
2325 ++it;
2326 }
2327 LogFlow(("\n"));
2328 }
2329#endif
2330
2331 /* Validate flags. */
2332 if (procInfo.mFlags)
2333 {
2334 if ( !(procInfo.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
2335 && !(procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
2336 && !(procInfo.mFlags & ProcessCreateFlag_Hidden)
2337 && !(procInfo.mFlags & ProcessCreateFlag_Profile)
2338 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
2339 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdErr))
2340 {
2341 return VERR_INVALID_PARAMETER;
2342 }
2343 }
2344
2345 if ( (procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
2346 && ( (procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
2347 || (procInfo.mFlags & ProcessCreateFlag_WaitForStdErr)
2348 )
2349 )
2350 {
2351 return VERR_INVALID_PARAMETER;
2352 }
2353
2354 /* Adjust timeout.
2355 * If set to 0, we define an infinite timeout (unlimited process run time). */
2356 if (procInfo.mTimeoutMS == 0)
2357 procInfo.mTimeoutMS = UINT32_MAX;
2358
2359 /** @todo Implement process priority + affinity. */
2360
2361 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2362
2363 /* Register a new object ID. */
2364 uint32_t idObject;
2365 int rc = i_objectRegister(SESSIONOBJECTTYPE_PROCESS, &idObject);
2366 if (RT_FAILURE(rc))
2367 return rc;
2368
2369 /* Create the process object. */
2370 HRESULT hr = pProcess.createObject();
2371 if (FAILED(hr))
2372 return VERR_COM_UNEXPECTED;
2373
2374 rc = pProcess->init(mParent->i_getConsole() /* Console */, this /* Session */, idObject,
2375 procInfo, mData.mpBaseEnvironment);
2376 if (RT_FAILURE(rc))
2377 return rc;
2378
2379 /* Add the created process to our map. */
2380 try
2381 {
2382 mData.mProcesses[idObject] = pProcess;
2383
2384 LogFlowFunc(("Added new process (Session: %RU32) with process ID=%RU32 (now total %zu processes)\n",
2385 mData.mSession.mID, idObject, mData.mProcesses.size()));
2386
2387 alock.release(); /* Release lock before firing off event. */
2388
2389 fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProcess, 0 /* PID */, true /* Process registered */);
2390 }
2391 catch (std::bad_alloc &)
2392 {
2393 rc = VERR_NO_MEMORY;
2394 }
2395
2396 return rc;
2397}
2398
2399inline bool GuestSession::i_processExists(uint32_t uProcessID, ComObjPtr<GuestProcess> *pProcess)
2400{
2401 SessionProcesses::const_iterator it = mData.mProcesses.find(uProcessID);
2402 if (it != mData.mProcesses.end())
2403 {
2404 if (pProcess)
2405 *pProcess = it->second;
2406 return true;
2407 }
2408 return false;
2409}
2410
2411inline int GuestSession::i_processGetByPID(ULONG uPID, ComObjPtr<GuestProcess> *pProcess)
2412{
2413 AssertReturn(uPID, false);
2414 /* pProcess is optional. */
2415
2416 SessionProcesses::iterator itProcs = mData.mProcesses.begin();
2417 for (; itProcs != mData.mProcesses.end(); ++itProcs)
2418 {
2419 ComObjPtr<GuestProcess> pCurProc = itProcs->second;
2420 AutoCaller procCaller(pCurProc);
2421 if (procCaller.rc())
2422 return VERR_COM_INVALID_OBJECT_STATE;
2423
2424 ULONG uCurPID;
2425 HRESULT hr = pCurProc->COMGETTER(PID)(&uCurPID);
2426 ComAssertComRC(hr);
2427
2428 if (uCurPID == uPID)
2429 {
2430 if (pProcess)
2431 *pProcess = pCurProc;
2432 return VINF_SUCCESS;
2433 }
2434 }
2435
2436 return VERR_NOT_FOUND;
2437}
2438
2439int GuestSession::i_sendCommand(uint32_t uFunction, uint32_t uParms, PVBOXHGCMSVCPARM paParms,
2440 uint64_t fDst /*= VBOX_GUESTCTRL_DST_SESSION*/)
2441{
2442 LogFlowThisFuncEnter();
2443
2444#ifndef VBOX_GUESTCTRL_TEST_CASE
2445 ComObjPtr<Console> pConsole = mParent->i_getConsole();
2446 Assert(!pConsole.isNull());
2447
2448 /* Forward the information to the VMM device. */
2449 VMMDev *pVMMDev = pConsole->i_getVMMDev();
2450 AssertPtr(pVMMDev);
2451
2452 LogFlowThisFunc(("uFunction=%RU32 (%s), uParms=%RU32\n", uFunction, GstCtrlHostFnName((guestControl::eHostFn)uFunction), uParms));
2453
2454 /* HACK ALERT! We extend the first parameter to 64-bit and use the
2455 two topmost bits for call destination information. */
2456 Assert(fDst == VBOX_GUESTCTRL_DST_SESSION || fDst == VBOX_GUESTCTRL_DST_ROOT_SVC || fDst == VBOX_GUESTCTRL_DST_BOTH);
2457 Assert(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT);
2458 paParms[0].type = VBOX_HGCM_SVC_PARM_64BIT;
2459 paParms[0].u.uint64 = (uint64_t)paParms[0].u.uint32 | fDst;
2460
2461 /* Make the call. */
2462 int vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uFunction, uParms, paParms);
2463 if (RT_FAILURE(vrc))
2464 {
2465 /** @todo What to do here? */
2466 }
2467#else
2468 /* Not needed within testcases. */
2469 int vrc = VINF_SUCCESS;
2470#endif
2471 LogFlowFuncLeaveRC(vrc);
2472 return vrc;
2473}
2474
2475/* static */
2476HRESULT GuestSession::i_setErrorExternal(VirtualBoxBase *pInterface, int rcGuest)
2477{
2478 AssertPtr(pInterface);
2479 AssertMsg(RT_FAILURE(rcGuest), ("Guest rc does not indicate a failure when setting error\n"));
2480
2481 return pInterface->setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, GuestSession::i_guestErrorToString(rcGuest).c_str());
2482}
2483
2484/* Does not do locking; caller is responsible for that! */
2485int GuestSession::i_setSessionStatus(GuestSessionStatus_T sessionStatus, int sessionRc)
2486{
2487 LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, sessionRc=%Rrc\n",
2488 mData.mStatus, sessionStatus, sessionRc));
2489
2490 if (sessionStatus == GuestSessionStatus_Error)
2491 {
2492 AssertMsg(RT_FAILURE(sessionRc), ("Guest rc must be an error (%Rrc)\n", sessionRc));
2493 /* Do not allow overwriting an already set error. If this happens
2494 * this means we forgot some error checking/locking somewhere. */
2495 AssertMsg(RT_SUCCESS(mData.mRC), ("Guest rc already set (to %Rrc)\n", mData.mRC));
2496 }
2497 else
2498 AssertMsg(RT_SUCCESS(sessionRc), ("Guest rc must not be an error (%Rrc)\n", sessionRc));
2499
2500 if (mData.mStatus != sessionStatus)
2501 {
2502 mData.mStatus = sessionStatus;
2503 mData.mRC = sessionRc;
2504
2505 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
2506 HRESULT hr = errorInfo.createObject();
2507 ComAssertComRC(hr);
2508 int rc2 = errorInfo->initEx(VBOX_E_IPRT_ERROR, sessionRc,
2509 COM_IIDOF(IGuestSession), getComponentName(),
2510 i_guestErrorToString(sessionRc));
2511 AssertRC(rc2);
2512
2513 fireGuestSessionStateChangedEvent(mEventSource, this,
2514 mData.mSession.mID, sessionStatus, errorInfo);
2515 }
2516
2517 return VINF_SUCCESS;
2518}
2519
2520int GuestSession::i_signalWaiters(GuestSessionWaitResult_T enmWaitResult, int rc /*= VINF_SUCCESS */)
2521{
2522 RT_NOREF(enmWaitResult, rc);
2523
2524 /*LogFlowThisFunc(("enmWaitResult=%d, rc=%Rrc, mWaitCount=%RU32, mWaitEvent=%p\n",
2525 enmWaitResult, rc, mData.mWaitCount, mData.mWaitEvent));*/
2526
2527 /* Note: No write locking here -- already done in the caller. */
2528
2529 int vrc = VINF_SUCCESS;
2530 /*if (mData.mWaitEvent)
2531 vrc = mData.mWaitEvent->Signal(enmWaitResult, rc);*/
2532 LogFlowFuncLeaveRC(vrc);
2533 return vrc;
2534}
2535
2536/**
2537 * Determines the protocol version (sets mData.mProtocolVersion).
2538 *
2539 * This is called from the init method prior to to establishing a guest
2540 * session.
2541 *
2542 * @return IPRT status code.
2543 */
2544int GuestSession::i_determineProtocolVersion(void)
2545{
2546 /*
2547 * We currently do this based on the reported guest additions version,
2548 * ASSUMING that VBoxService and VBoxDrv are at the same version.
2549 */
2550 ComObjPtr<Guest> pGuest = mParent;
2551 AssertReturn(!pGuest.isNull(), VERR_NOT_SUPPORTED);
2552 uint32_t uGaVersion = pGuest->i_getAdditionsVersion();
2553
2554 /* Everyone supports version one, if they support anything at all. */
2555 mData.mProtocolVersion = 1;
2556
2557 /* Guest control 2.0 was introduced with 4.3.0. */
2558 if (uGaVersion >= VBOX_FULL_VERSION_MAKE(4,3,0))
2559 mData.mProtocolVersion = 2; /* Guest control 2.0. */
2560
2561 LogFlowThisFunc(("uGaVersion=%u.%u.%u => mProtocolVersion=%u\n",
2562 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
2563 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
2564
2565 /*
2566 * Inform the user about outdated guest additions (VM release log).
2567 */
2568 if (mData.mProtocolVersion < 2)
2569 LogRelMax(3, (tr("Warning: Guest Additions v%u.%u.%u only supports the older guest control protocol version %u.\n"
2570 " Please upgrade GAs to the current version to get full guest control capabilities.\n"),
2571 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
2572 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
2573
2574 return VINF_SUCCESS;
2575}
2576
2577int GuestSession::i_waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, GuestSessionWaitResult_T &waitResult, int *prcGuest)
2578{
2579 LogFlowThisFuncEnter();
2580
2581 AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
2582
2583 /*LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p, prcGuest=%p\n",
2584 fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent, prcGuest));*/
2585
2586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2587
2588 /* Did some error occur before? Then skip waiting and return. */
2589 if (mData.mStatus == GuestSessionStatus_Error)
2590 {
2591 waitResult = GuestSessionWaitResult_Error;
2592 AssertMsg(RT_FAILURE(mData.mRC), ("No error rc (%Rrc) set when guest session indicated an error\n", mData.mRC));
2593 if (prcGuest)
2594 *prcGuest = mData.mRC; /* Return last set error. */
2595 return VERR_GSTCTL_GUEST_ERROR;
2596 }
2597
2598 /* Guest Additions < 4.3 don't support session handling, skip. */
2599 if (mData.mProtocolVersion < 2)
2600 {
2601 waitResult = GuestSessionWaitResult_WaitFlagNotSupported;
2602
2603 LogFlowThisFunc(("Installed Guest Additions don't support waiting for dedicated sessions, skipping\n"));
2604 return VINF_SUCCESS;
2605 }
2606
2607 waitResult = GuestSessionWaitResult_None;
2608 if (fWaitFlags & GuestSessionWaitForFlag_Terminate)
2609 {
2610 switch (mData.mStatus)
2611 {
2612 case GuestSessionStatus_Terminated:
2613 case GuestSessionStatus_Down:
2614 waitResult = GuestSessionWaitResult_Terminate;
2615 break;
2616
2617 case GuestSessionStatus_TimedOutKilled:
2618 case GuestSessionStatus_TimedOutAbnormally:
2619 waitResult = GuestSessionWaitResult_Timeout;
2620 break;
2621
2622 case GuestSessionStatus_Error:
2623 /* Handled above. */
2624 break;
2625
2626 case GuestSessionStatus_Started:
2627 waitResult = GuestSessionWaitResult_Start;
2628 break;
2629
2630 case GuestSessionStatus_Undefined:
2631 case GuestSessionStatus_Starting:
2632 /* Do the waiting below. */
2633 break;
2634
2635 default:
2636 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
2637 return VERR_NOT_IMPLEMENTED;
2638 }
2639 }
2640 else if (fWaitFlags & GuestSessionWaitForFlag_Start)
2641 {
2642 switch (mData.mStatus)
2643 {
2644 case GuestSessionStatus_Started:
2645 case GuestSessionStatus_Terminating:
2646 case GuestSessionStatus_Terminated:
2647 case GuestSessionStatus_Down:
2648 waitResult = GuestSessionWaitResult_Start;
2649 break;
2650
2651 case GuestSessionStatus_Error:
2652 waitResult = GuestSessionWaitResult_Error;
2653 break;
2654
2655 case GuestSessionStatus_TimedOutKilled:
2656 case GuestSessionStatus_TimedOutAbnormally:
2657 waitResult = GuestSessionWaitResult_Timeout;
2658 break;
2659
2660 case GuestSessionStatus_Undefined:
2661 case GuestSessionStatus_Starting:
2662 /* Do the waiting below. */
2663 break;
2664
2665 default:
2666 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
2667 return VERR_NOT_IMPLEMENTED;
2668 }
2669 }
2670
2671 LogFlowThisFunc(("sessionStatus=%RU32, sessionRc=%Rrc, waitResult=%RU32\n",
2672 mData.mStatus, mData.mRC, waitResult));
2673
2674 /* No waiting needed? Return immediately using the last set error. */
2675 if (waitResult != GuestSessionWaitResult_None)
2676 {
2677 if (prcGuest)
2678 *prcGuest = mData.mRC; /* Return last set error (if any). */
2679 return RT_SUCCESS(mData.mRC) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
2680 }
2681
2682 int vrc;
2683
2684 GuestWaitEvent *pEvent = NULL;
2685 GuestEventTypes eventTypes;
2686 try
2687 {
2688 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
2689
2690 vrc = registerWaitEventEx(mData.mSession.mID, mData.mObjectID, eventTypes, &pEvent);
2691 }
2692 catch (std::bad_alloc &)
2693 {
2694 vrc = VERR_NO_MEMORY;
2695 }
2696
2697 if (RT_FAILURE(vrc))
2698 return vrc;
2699
2700 alock.release(); /* Release lock before waiting. */
2701
2702 GuestSessionStatus_T sessionStatus;
2703 vrc = i_waitForStatusChange(pEvent, fWaitFlags,
2704 uTimeoutMS, &sessionStatus, prcGuest);
2705 if (RT_SUCCESS(vrc))
2706 {
2707 switch (sessionStatus)
2708 {
2709 case GuestSessionStatus_Started:
2710 waitResult = GuestSessionWaitResult_Start;
2711 break;
2712
2713 case GuestSessionStatus_Terminated:
2714 waitResult = GuestSessionWaitResult_Terminate;
2715 break;
2716
2717 case GuestSessionStatus_TimedOutKilled:
2718 case GuestSessionStatus_TimedOutAbnormally:
2719 waitResult = GuestSessionWaitResult_Timeout;
2720 break;
2721
2722 case GuestSessionStatus_Down:
2723 waitResult = GuestSessionWaitResult_Terminate;
2724 break;
2725
2726 case GuestSessionStatus_Error:
2727 waitResult = GuestSessionWaitResult_Error;
2728 break;
2729
2730 default:
2731 waitResult = GuestSessionWaitResult_Status;
2732 break;
2733 }
2734 }
2735
2736 unregisterWaitEvent(pEvent);
2737
2738 LogFlowFuncLeaveRC(vrc);
2739 return vrc;
2740}
2741
2742int GuestSession::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t fWaitFlags, uint32_t uTimeoutMS,
2743 GuestSessionStatus_T *pSessionStatus, int *prcGuest)
2744{
2745 RT_NOREF(fWaitFlags);
2746 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
2747
2748 VBoxEventType_T evtType;
2749 ComPtr<IEvent> pIEvent;
2750 int vrc = waitForEvent(pEvent, uTimeoutMS,
2751 &evtType, pIEvent.asOutParam());
2752 if (RT_SUCCESS(vrc))
2753 {
2754 Assert(evtType == VBoxEventType_OnGuestSessionStateChanged);
2755
2756 ComPtr<IGuestSessionStateChangedEvent> pChangedEvent = pIEvent;
2757 Assert(!pChangedEvent.isNull());
2758
2759 GuestSessionStatus_T sessionStatus;
2760 pChangedEvent->COMGETTER(Status)(&sessionStatus);
2761 if (pSessionStatus)
2762 *pSessionStatus = sessionStatus;
2763
2764 ComPtr<IVirtualBoxErrorInfo> errorInfo;
2765 HRESULT hr = pChangedEvent->COMGETTER(Error)(errorInfo.asOutParam());
2766 ComAssertComRC(hr);
2767
2768 LONG lGuestRc;
2769 hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
2770 ComAssertComRC(hr);
2771 if (RT_FAILURE((int)lGuestRc))
2772 vrc = VERR_GSTCTL_GUEST_ERROR;
2773 if (prcGuest)
2774 *prcGuest = (int)lGuestRc;
2775
2776 LogFlowThisFunc(("Status changed event for session ID=%RU32, new status is: %RU32 (%Rrc)\n",
2777 mData.mSession.mID, sessionStatus,
2778 RT_SUCCESS((int)lGuestRc) ? VINF_SUCCESS : (int)lGuestRc));
2779 }
2780
2781 LogFlowFuncLeaveRC(vrc);
2782 return vrc;
2783}
2784
2785// implementation of public methods
2786/////////////////////////////////////////////////////////////////////////////
2787
2788HRESULT GuestSession::close()
2789{
2790 AutoCaller autoCaller(this);
2791 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2792
2793 LogFlowThisFuncEnter();
2794
2795 /* Note: Don't check if the session is ready via i_isReadyExternal() here;
2796 * the session (already) could be in a stopped / aborted state. */
2797
2798 /* Close session on guest. */
2799 int rcGuest = VINF_SUCCESS;
2800 int vrc = i_closeSession(0 /* Flags */, 30 * 1000 /* Timeout */, &rcGuest);
2801 /* On failure don't return here, instead do all the cleanup
2802 * work first and then return an error. */
2803
2804 /* Remove ourselves from the session list. */
2805 AssertPtr(mParent);
2806 int vrc2 = mParent->i_sessionRemove(mData.mSession.mID);
2807 if (vrc2 == VERR_NOT_FOUND) /* Not finding the session anymore isn't critical. */
2808 vrc2 = VINF_SUCCESS;
2809
2810 if (RT_SUCCESS(vrc))
2811 vrc = vrc2;
2812
2813 LogFlowThisFunc(("Returning rc=%Rrc, rcGuest=%Rrc\n", vrc, rcGuest));
2814
2815 if (RT_FAILURE(vrc))
2816 {
2817 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2818 return GuestSession::i_setErrorExternal(this, rcGuest);
2819 return setError(VBOX_E_IPRT_ERROR, tr("Closing guest session failed with %Rrc"), vrc);
2820 }
2821
2822 return S_OK;
2823}
2824
2825HRESULT GuestSession::fileCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2826 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
2827{
2828 RT_NOREF(aSource, aDestination, aFlags, aProgress);
2829 ReturnComNotImplemented();
2830}
2831
2832HRESULT GuestSession::fileCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2833 const std::vector<FileCopyFlag_T> &aFlags,
2834 ComPtr<IProgress> &aProgress)
2835{
2836 AutoCaller autoCaller(this);
2837 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2838
2839 uint32_t fFlags = FileCopyFlag_None;
2840 if (aFlags.size())
2841 {
2842 for (size_t i = 0; i < aFlags.size(); i++)
2843 fFlags |= aFlags[i];
2844 }
2845
2846 GuestSessionFsSourceSet SourceSet;
2847
2848 GuestSessionFsSourceSpec source;
2849 source.strSource = aSource;
2850 source.enmType = FsObjType_File;
2851 source.enmPathStyle = i_getPathStyle();
2852 source.Type.File.fCopyFlags = (FileCopyFlag_T)fFlags;
2853
2854 SourceSet.push_back(source);
2855
2856 return i_copyFromGuest(SourceSet, aDestination, aProgress);
2857}
2858
2859HRESULT GuestSession::fileCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2860 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
2861{
2862 AutoCaller autoCaller(this);
2863 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2864
2865 uint32_t fFlags = FileCopyFlag_None;
2866 if (aFlags.size())
2867 {
2868 for (size_t i = 0; i < aFlags.size(); i++)
2869 fFlags |= aFlags[i];
2870 }
2871
2872 GuestSessionFsSourceSet SourceSet;
2873
2874 GuestSessionFsSourceSpec source;
2875 source.strSource = aSource;
2876 source.enmType = FsObjType_File;
2877 source.enmPathStyle = i_getPathStyle();
2878 source.Type.File.fCopyFlags = (FileCopyFlag_T)fFlags;
2879
2880 SourceSet.push_back(source);
2881
2882 return i_copyToGuest(SourceSet, aDestination, aProgress);
2883}
2884
2885HRESULT GuestSession::copyFromGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
2886 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
2887 ComPtr<IProgress> &aProgress)
2888{
2889 AutoCaller autoCaller(this);
2890 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2891
2892 const size_t cSources = aSources.size();
2893 if ( (aFilters.size() && aFilters.size() != cSources)
2894 || (aFlags.size() && aFlags.size() != cSources))
2895 {
2896 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
2897 }
2898
2899 GuestSessionFsSourceSet SourceSet;
2900
2901 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
2902 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
2903 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
2904
2905 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
2906 const bool fFollowSymlinks = true; /** @todo Ditto. */
2907
2908 while (itSource != aSources.end())
2909 {
2910 GuestFsObjData objData;
2911 int rcGuest;
2912 int vrc = i_fsQueryInfo(*(itSource), fFollowSymlinks, objData, &rcGuest);
2913 if ( RT_FAILURE(vrc)
2914 && !fContinueOnErrors)
2915 {
2916 if (GuestProcess::i_isGuestError(vrc))
2917 return setError(E_FAIL, tr("Unable to query type for source '%s': %s"), (*itSource).c_str(),
2918 GuestProcess::i_guestErrorToString(rcGuest).c_str());
2919 else
2920 return setError(E_FAIL, tr("Unable to query type for source '%s' (%Rrc)"), (*itSource).c_str(), vrc);
2921 }
2922
2923 Utf8Str strFlags;
2924 if (itFlags != aFlags.end())
2925 {
2926 strFlags = *itFlags;
2927 ++itFlags;
2928 }
2929
2930 Utf8Str strFilter;
2931 if (itFilter != aFilters.end())
2932 {
2933 strFilter = *itFilter;
2934 ++itFilter;
2935 }
2936
2937 GuestSessionFsSourceSpec source;
2938 source.strSource = *itSource;
2939 source.strFilter = strFilter;
2940 source.enmType = objData.mType;
2941 source.enmPathStyle = i_getPathStyle();
2942
2943 HRESULT hrc;
2944 if (source.enmType == FsObjType_Directory)
2945 {
2946 hrc = GuestSession::i_directoryCopyFlagFromStr(strFlags, &source.Type.Dir.fCopyFlags);
2947 source.Type.Dir.fRecursive = true; /* Implicit. */
2948 }
2949 else if (source.enmType == FsObjType_File)
2950 hrc = GuestSession::i_fileCopyFlagFromStr(strFlags, &source.Type.File.fCopyFlags);
2951 else
2952 return setError(E_INVALIDARG, tr("Source type %d invalid / not supported"), source.enmType);
2953 if (FAILED(hrc))
2954 return hrc;
2955
2956 SourceSet.push_back(source);
2957
2958 ++itSource;
2959 }
2960
2961 return i_copyFromGuest(SourceSet, aDestination, aProgress);
2962}
2963
2964HRESULT GuestSession::copyToGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
2965 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
2966 ComPtr<IProgress> &aProgress)
2967{
2968 AutoCaller autoCaller(this);
2969 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2970
2971 const size_t cSources = aSources.size();
2972 if ( (aFilters.size() && aFilters.size() != cSources)
2973 || (aFlags.size() && aFlags.size() != cSources))
2974 {
2975 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
2976 }
2977
2978 GuestSessionFsSourceSet SourceSet;
2979
2980 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
2981 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
2982 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
2983
2984 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
2985
2986 while (itSource != aSources.end())
2987 {
2988 RTFSOBJINFO objInfo;
2989 int vrc = RTPathQueryInfo((*itSource).c_str(), &objInfo, RTFSOBJATTRADD_NOTHING);
2990 if ( RT_FAILURE(vrc)
2991 && !fContinueOnErrors)
2992 {
2993 return setError(E_FAIL, tr("Unable to query type for source '%s' (%Rrc)"), (*itSource).c_str(), vrc);
2994 }
2995
2996 Utf8Str strFlags;
2997 if (itFlags != aFlags.end())
2998 {
2999 strFlags = *itFlags;
3000 ++itFlags;
3001 }
3002
3003 Utf8Str strFilter;
3004 if (itFilter != aFilters.end())
3005 {
3006 strFilter = *itFilter;
3007 ++itFilter;
3008 }
3009
3010 GuestSessionFsSourceSpec source;
3011 source.strSource = *itSource;
3012 source.strFilter = strFilter;
3013 source.enmType = GuestBase::fileModeToFsObjType(objInfo.Attr.fMode);
3014 source.enmPathStyle = i_getPathStyle();
3015
3016 HRESULT hrc;
3017 if (source.enmType == FsObjType_Directory)
3018 {
3019 hrc = GuestSession::i_directoryCopyFlagFromStr(strFlags, &source.Type.Dir.fCopyFlags);
3020 source.Type.Dir.fFollowSymlinks = true; /** @todo Add a flag for that in DirectoryCopyFlag_T. Later. */
3021 source.Type.Dir.fRecursive = true; /* Implicit. */
3022 }
3023 else if (source.enmType == FsObjType_File)
3024 hrc = GuestSession::i_fileCopyFlagFromStr(strFlags, &source.Type.File.fCopyFlags);
3025 else
3026 return setError(E_INVALIDARG, tr("Source type %d invalid / not supported"), source.enmType);
3027 if (FAILED(hrc))
3028 return hrc;
3029
3030 SourceSet.push_back(source);
3031
3032 ++itSource;
3033 }
3034
3035 return i_copyToGuest(SourceSet, aDestination, aProgress);
3036}
3037
3038HRESULT GuestSession::directoryCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3039 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3040{
3041 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3042 ReturnComNotImplemented();
3043}
3044
3045HRESULT GuestSession::directoryCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3046 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3047{
3048 AutoCaller autoCaller(this);
3049 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3050
3051 uint32_t fFlags = DirectoryCopyFlag_None;
3052 if (aFlags.size())
3053 {
3054 for (size_t i = 0; i < aFlags.size(); i++)
3055 fFlags |= aFlags[i];
3056 }
3057
3058 GuestSessionFsSourceSet SourceSet;
3059
3060 GuestSessionFsSourceSpec source;
3061 source.strSource = aSource;
3062 source.enmType = FsObjType_Directory;
3063 source.enmPathStyle = i_getPathStyle();
3064 source.Type.Dir.fCopyFlags = (DirectoryCopyFlag_T)fFlags;
3065 source.Type.Dir.fRecursive = true; /* Implicit. */
3066
3067 SourceSet.push_back(source);
3068
3069 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3070}
3071
3072HRESULT GuestSession::directoryCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3073 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3074{
3075 AutoCaller autoCaller(this);
3076 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3077
3078 uint32_t fFlags = DirectoryCopyFlag_None;
3079 if (aFlags.size())
3080 {
3081 for (size_t i = 0; i < aFlags.size(); i++)
3082 fFlags |= aFlags[i];
3083 }
3084
3085 GuestSessionFsSourceSet SourceSet;
3086
3087 GuestSessionFsSourceSpec source;
3088 source.strSource = aSource;
3089 source.enmType = FsObjType_Directory;
3090 source.enmPathStyle = i_getPathStyle();
3091 source.Type.Dir.fCopyFlags = (DirectoryCopyFlag_T)fFlags;
3092 source.Type.Dir.fFollowSymlinks = true; /** @todo Add a flag for that in DirectoryCopyFlag_T. Later. */
3093 source.Type.Dir.fRecursive = true; /* Implicit. */
3094
3095 SourceSet.push_back(source);
3096
3097 return i_copyToGuest(SourceSet, aDestination, aProgress);
3098}
3099
3100HRESULT GuestSession::directoryCreate(const com::Utf8Str &aPath, ULONG aMode,
3101 const std::vector<DirectoryCreateFlag_T> &aFlags)
3102{
3103 AutoCaller autoCaller(this);
3104 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3105
3106 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3107 return setError(E_INVALIDARG, tr("No directory to create specified"));
3108
3109 uint32_t fFlags = DirectoryCreateFlag_None;
3110 if (aFlags.size())
3111 {
3112 for (size_t i = 0; i < aFlags.size(); i++)
3113 fFlags |= aFlags[i];
3114
3115 if (fFlags)
3116 if (!(fFlags & DirectoryCreateFlag_Parents))
3117 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), fFlags);
3118 }
3119
3120 HRESULT hrc = i_isReadyExternal();
3121 if (FAILED(hrc))
3122 return hrc;
3123
3124 LogFlowThisFuncEnter();
3125
3126 ComObjPtr <GuestDirectory> pDirectory; int rcGuest;
3127 int vrc = i_directoryCreate(aPath, (uint32_t)aMode, fFlags, &rcGuest);
3128 if (RT_FAILURE(vrc))
3129 {
3130 if (GuestProcess::i_isGuestError(vrc))
3131 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest,
3132 tr("Directory creation failed: %s"), GuestDirectory::i_guestErrorToString(rcGuest).c_str());
3133 else
3134 {
3135 switch (vrc)
3136 {
3137 case VERR_INVALID_PARAMETER:
3138 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: Invalid parameters given"));
3139 break;
3140
3141 case VERR_BROKEN_PIPE:
3142 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: Unexpectedly aborted"));
3143 break;
3144
3145 default:
3146 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: %Rrc"), vrc);
3147 break;
3148 }
3149 }
3150 }
3151
3152 return hrc;
3153}
3154
3155HRESULT GuestSession::directoryCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath,
3156 BOOL aSecure, com::Utf8Str &aDirectory)
3157{
3158 RT_NOREF(aMode, aSecure);
3159
3160 AutoCaller autoCaller(this);
3161 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3162
3163 if (RT_UNLIKELY((aTemplateName.c_str()) == NULL || *(aTemplateName.c_str()) == '\0'))
3164 return setError(E_INVALIDARG, tr("No template specified"));
3165 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3166 return setError(E_INVALIDARG, tr("No directory name specified"));
3167
3168 HRESULT hrc = i_isReadyExternal();
3169 if (FAILED(hrc))
3170 return hrc;
3171
3172 LogFlowThisFuncEnter();
3173
3174 int rcGuest;
3175 int vrc = i_fsCreateTemp(aTemplateName, aPath, true /* Directory */, aDirectory, &rcGuest);
3176 if (!RT_SUCCESS(vrc))
3177 {
3178 switch (vrc)
3179 {
3180 case VERR_GSTCTL_GUEST_ERROR:
3181 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3182 break;
3183
3184 default:
3185 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Temporary directory creation \"%s\" with template \"%s\" failed: %Rrc"),
3186 aPath.c_str(), aTemplateName.c_str(), vrc);
3187 break;
3188 }
3189 }
3190
3191 return hrc;
3192}
3193
3194HRESULT GuestSession::directoryExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3195{
3196 AutoCaller autoCaller(this);
3197 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3198
3199 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3200 return setError(E_INVALIDARG, tr("No directory to check existence for specified"));
3201
3202 HRESULT hrc = i_isReadyExternal();
3203 if (FAILED(hrc))
3204 return hrc;
3205
3206 LogFlowThisFuncEnter();
3207
3208 GuestFsObjData objData; int rcGuest;
3209 int vrc = i_directoryQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3210 if (RT_SUCCESS(vrc))
3211 *aExists = objData.mType == FsObjType_Directory;
3212 else
3213 {
3214 switch (vrc)
3215 {
3216 case VERR_GSTCTL_GUEST_ERROR:
3217 {
3218 switch (rcGuest)
3219 {
3220 case VERR_PATH_NOT_FOUND:
3221 *aExists = FALSE;
3222 break;
3223 default:
3224 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying directory existence \"%s\" failed: %s"),
3225 aPath.c_str(), GuestProcess::i_guestErrorToString(rcGuest).c_str());
3226 break;
3227 }
3228 break;
3229 }
3230
3231 default:
3232 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying directory existence \"%s\" failed: %Rrc"),
3233 aPath.c_str(), vrc);
3234 break;
3235 }
3236 }
3237
3238 return hrc;
3239}
3240
3241HRESULT GuestSession::directoryOpen(const com::Utf8Str &aPath, const com::Utf8Str &aFilter,
3242 const std::vector<DirectoryOpenFlag_T> &aFlags, ComPtr<IGuestDirectory> &aDirectory)
3243{
3244 AutoCaller autoCaller(this);
3245 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3246
3247 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3248 return setError(E_INVALIDARG, tr("No directory to open specified"));
3249 if (RT_UNLIKELY((aFilter.c_str()) != NULL && *(aFilter.c_str()) != '\0'))
3250 return setError(E_INVALIDARG, tr("Directory filters are not implemented yet"));
3251
3252 uint32_t fFlags = DirectoryOpenFlag_None;
3253 if (aFlags.size())
3254 {
3255 for (size_t i = 0; i < aFlags.size(); i++)
3256 fFlags |= aFlags[i];
3257
3258 if (fFlags)
3259 return setError(E_INVALIDARG, tr("Open flags (%#x) not implemented yet"), fFlags);
3260 }
3261
3262 HRESULT hrc = i_isReadyExternal();
3263 if (FAILED(hrc))
3264 return hrc;
3265
3266 LogFlowThisFuncEnter();
3267
3268 GuestDirectoryOpenInfo openInfo;
3269 openInfo.mPath = aPath;
3270 openInfo.mFilter = aFilter;
3271 openInfo.mFlags = fFlags;
3272
3273 ComObjPtr <GuestDirectory> pDirectory; int rcGuest;
3274 int vrc = i_directoryOpen(openInfo, pDirectory, &rcGuest);
3275 if (RT_SUCCESS(vrc))
3276 {
3277 /* Return directory object to the caller. */
3278 hrc = pDirectory.queryInterfaceTo(aDirectory.asOutParam());
3279 }
3280 else
3281 {
3282 switch (vrc)
3283 {
3284 case VERR_INVALID_PARAMETER:
3285 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening directory \"%s\" failed; invalid parameters given"),
3286 aPath.c_str());
3287 break;
3288
3289 case VERR_GSTCTL_GUEST_ERROR:
3290 hrc = GuestDirectory::i_setErrorExternal(this, rcGuest);
3291 break;
3292
3293 default:
3294 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3295 break;
3296 }
3297 }
3298
3299 return hrc;
3300}
3301
3302HRESULT GuestSession::directoryRemove(const com::Utf8Str &aPath)
3303{
3304 AutoCaller autoCaller(this);
3305 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3306
3307 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3308 return setError(E_INVALIDARG, tr("No directory to remove specified"));
3309
3310 HRESULT hrc = i_isReadyExternal();
3311 if (FAILED(hrc))
3312 return hrc;
3313
3314 LogFlowThisFuncEnter();
3315
3316 /* No flags; only remove the directory when empty. */
3317 uint32_t uFlags = 0;
3318
3319 int rcGuest;
3320 int vrc = i_directoryRemove(aPath, uFlags, &rcGuest);
3321 if (RT_FAILURE(vrc))
3322 {
3323 switch (vrc)
3324 {
3325 case VERR_NOT_SUPPORTED:
3326 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3327 tr("Handling removing guest directories not supported by installed Guest Additions"));
3328 break;
3329
3330 case VERR_GSTCTL_GUEST_ERROR:
3331 hrc = GuestDirectory::i_setErrorExternal(this, rcGuest);
3332 break;
3333
3334 default:
3335 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing guest directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3336 break;
3337 }
3338 }
3339
3340 return hrc;
3341}
3342
3343HRESULT GuestSession::directoryRemoveRecursive(const com::Utf8Str &aPath, const std::vector<DirectoryRemoveRecFlag_T> &aFlags,
3344 ComPtr<IProgress> &aProgress)
3345{
3346 RT_NOREF(aFlags);
3347
3348 AutoCaller autoCaller(this);
3349 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3350
3351 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3352 return setError(E_INVALIDARG, tr("No directory to remove recursively specified"));
3353
3354/** @todo r=bird: Must check that the flags matches the hardcoded behavior
3355 * further down!! */
3356
3357 HRESULT hrc = i_isReadyExternal();
3358 if (FAILED(hrc))
3359 return hrc;
3360
3361 LogFlowThisFuncEnter();
3362
3363 ComObjPtr<Progress> pProgress;
3364 hrc = pProgress.createObject();
3365 if (SUCCEEDED(hrc))
3366 hrc = pProgress->init(static_cast<IGuestSession *>(this),
3367 Bstr(tr("Removing guest directory")).raw(),
3368 TRUE /*aCancelable*/);
3369 if (FAILED(hrc))
3370 return hrc;
3371
3372 /* Note: At the moment we don't supply progress information while
3373 * deleting a guest directory recursively. So just complete
3374 * the progress object right now. */
3375 /** @todo Implement progress reporting on guest directory deletion! */
3376 hrc = pProgress->i_notifyComplete(S_OK);
3377 if (FAILED(hrc))
3378 return hrc;
3379
3380 /* Remove the directory + all its contents. */
3381 uint32_t uFlags = DIRREMOVE_FLAG_RECURSIVE
3382 | DIRREMOVE_FLAG_CONTENT_AND_DIR;
3383 int rcGuest;
3384 int vrc = i_directoryRemove(aPath, uFlags, &rcGuest);
3385 if (RT_FAILURE(vrc))
3386 {
3387 switch (vrc)
3388 {
3389 case VERR_NOT_SUPPORTED:
3390 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3391 tr("Handling removing guest directories recursively not supported by installed Guest Additions"));
3392 break;
3393
3394 case VERR_GSTCTL_GUEST_ERROR:
3395 hrc = GuestFile::i_setErrorExternal(this, rcGuest);
3396 break;
3397
3398 default:
3399 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Recursively removing guest directory \"%s\" failed: %Rrc"),
3400 aPath.c_str(), vrc);
3401 break;
3402 }
3403 }
3404 else
3405 {
3406 pProgress.queryInterfaceTo(aProgress.asOutParam());
3407 }
3408
3409 return hrc;
3410}
3411
3412HRESULT GuestSession::environmentScheduleSet(const com::Utf8Str &aName, const com::Utf8Str &aValue)
3413{
3414 AutoCaller autoCaller(this);
3415 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3416
3417 HRESULT hrc;
3418 if (RT_LIKELY(aName.isNotEmpty()))
3419 {
3420 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3421 {
3422 LogFlowThisFuncEnter();
3423
3424 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3425 int vrc = mData.mEnvironmentChanges.setVariable(aName, aValue);
3426 if (RT_SUCCESS(vrc))
3427 hrc = S_OK;
3428 else
3429 hrc = setErrorVrc(vrc);
3430 }
3431 else
3432 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3433 }
3434 else
3435 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3436
3437 LogFlowThisFuncLeave();
3438 return hrc;
3439}
3440
3441HRESULT GuestSession::environmentScheduleUnset(const com::Utf8Str &aName)
3442{
3443 AutoCaller autoCaller(this);
3444 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3445
3446 HRESULT hrc;
3447 if (RT_LIKELY(aName.isNotEmpty()))
3448 {
3449 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3450 {
3451 LogFlowThisFuncEnter();
3452
3453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3454 int vrc = mData.mEnvironmentChanges.unsetVariable(aName);
3455 if (RT_SUCCESS(vrc))
3456 hrc = S_OK;
3457 else
3458 hrc = setErrorVrc(vrc);
3459 }
3460 else
3461 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3462 }
3463 else
3464 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3465
3466 LogFlowThisFuncLeave();
3467 return hrc;
3468}
3469
3470HRESULT GuestSession::environmentGetBaseVariable(const com::Utf8Str &aName, com::Utf8Str &aValue)
3471{
3472 AutoCaller autoCaller(this);
3473 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3474
3475 HRESULT hrc;
3476 if (RT_LIKELY(aName.isNotEmpty()))
3477 {
3478 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3479 {
3480 LogFlowThisFuncEnter();
3481
3482 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3483 if (mData.mpBaseEnvironment)
3484 {
3485 int vrc = mData.mpBaseEnvironment->getVariable(aName, &aValue);
3486 if (RT_SUCCESS(vrc))
3487 hrc = S_OK;
3488 else
3489 hrc = setErrorVrc(vrc);
3490 }
3491 else if (mData.mProtocolVersion < 99999)
3492 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
3493 else
3494 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
3495 }
3496 else
3497 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3498 }
3499 else
3500 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3501
3502 LogFlowThisFuncLeave();
3503 return hrc;
3504}
3505
3506HRESULT GuestSession::environmentDoesBaseVariableExist(const com::Utf8Str &aName, BOOL *aExists)
3507{
3508 AutoCaller autoCaller(this);
3509 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3510
3511 *aExists = FALSE;
3512
3513 HRESULT hrc;
3514 if (RT_LIKELY(aName.isNotEmpty()))
3515 {
3516 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3517 {
3518 LogFlowThisFuncEnter();
3519
3520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3521 if (mData.mpBaseEnvironment)
3522 {
3523 hrc = S_OK;
3524 *aExists = mData.mpBaseEnvironment->doesVariableExist(aName);
3525 }
3526 else if (mData.mProtocolVersion < 99999)
3527 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
3528 else
3529 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
3530 }
3531 else
3532 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3533 }
3534 else
3535 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3536
3537 LogFlowThisFuncLeave();
3538 return hrc;
3539}
3540
3541HRESULT GuestSession::fileCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath, BOOL aSecure,
3542 ComPtr<IGuestFile> &aFile)
3543{
3544 RT_NOREF(aTemplateName, aMode, aPath, aSecure, aFile);
3545 ReturnComNotImplemented();
3546}
3547
3548HRESULT GuestSession::fileExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3549{
3550 AutoCaller autoCaller(this);
3551 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3552
3553 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3554 {
3555 *aExists = FALSE;
3556 return S_OK;
3557 }
3558
3559 HRESULT hrc = i_isReadyExternal();
3560 if (FAILED(hrc))
3561 return hrc;
3562
3563 LogFlowThisFuncEnter();
3564
3565 GuestFsObjData objData; int rcGuest;
3566 int vrc = i_fileQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3567 if (RT_SUCCESS(vrc))
3568 {
3569 *aExists = TRUE;
3570 return S_OK;
3571 }
3572
3573 switch (vrc)
3574 {
3575 case VERR_GSTCTL_GUEST_ERROR:
3576 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3577 break;
3578
3579/** @todo r=bird: what about VERR_PATH_NOT_FOUND and VERR_FILE_NOT_FOUND?
3580 * Where does that get converted to *aExists = FALSE? */
3581 case VERR_NOT_A_FILE:
3582 *aExists = FALSE;
3583 break;
3584
3585 default:
3586 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying file information for \"%s\" failed: %Rrc"),
3587 aPath.c_str(), vrc);
3588 break;
3589 }
3590
3591 return hrc;
3592}
3593
3594HRESULT GuestSession::fileOpen(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3595 ULONG aCreationMode, ComPtr<IGuestFile> &aFile)
3596{
3597 LogFlowThisFuncEnter();
3598
3599 const std::vector<FileOpenExFlag_T> EmptyFlags;
3600 return fileOpenEx(aPath, aAccessMode, aOpenAction, FileSharingMode_All, aCreationMode, EmptyFlags, aFile);
3601}
3602
3603HRESULT GuestSession::fileOpenEx(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3604 FileSharingMode_T aSharingMode, ULONG aCreationMode,
3605 const std::vector<FileOpenExFlag_T> &aFlags, ComPtr<IGuestFile> &aFile)
3606{
3607 AutoCaller autoCaller(this);
3608 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3609
3610 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3611 return setError(E_INVALIDARG, tr("No file to open specified"));
3612
3613 HRESULT hrc = i_isReadyExternal();
3614 if (FAILED(hrc))
3615 return hrc;
3616
3617 LogFlowThisFuncEnter();
3618
3619 GuestFileOpenInfo openInfo;
3620 openInfo.mFileName = aPath;
3621 openInfo.mCreationMode = aCreationMode;
3622
3623 /* Validate aAccessMode. */
3624 switch (aAccessMode)
3625 {
3626 case FileAccessMode_ReadOnly:
3627 RT_FALL_THRU();
3628 case FileAccessMode_WriteOnly:
3629 RT_FALL_THRU();
3630 case FileAccessMode_ReadWrite:
3631 openInfo.mAccessMode = aAccessMode;
3632 break;
3633 case FileAccessMode_AppendOnly:
3634 RT_FALL_THRU();
3635 case FileAccessMode_AppendRead:
3636 return setError(E_NOTIMPL, tr("Append access modes are not yet implemented"));
3637 default:
3638 return setError(E_INVALIDARG, tr("Unknown FileAccessMode value %u (%#x)"), aAccessMode, aAccessMode);
3639 }
3640
3641 /* Validate aOpenAction to the old format. */
3642 switch (aOpenAction)
3643 {
3644 case FileOpenAction_OpenExisting:
3645 RT_FALL_THRU();
3646 case FileOpenAction_OpenOrCreate:
3647 RT_FALL_THRU();
3648 case FileOpenAction_CreateNew:
3649 RT_FALL_THRU();
3650 case FileOpenAction_CreateOrReplace:
3651 RT_FALL_THRU();
3652 case FileOpenAction_OpenExistingTruncated:
3653 RT_FALL_THRU();
3654 case FileOpenAction_AppendOrCreate:
3655 openInfo.mOpenAction = aOpenAction;
3656 break;
3657 default:
3658 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3659 }
3660
3661 /* Validate aSharingMode. */
3662 switch (aSharingMode)
3663 {
3664 case FileSharingMode_All:
3665 openInfo.mSharingMode = aSharingMode;
3666 break;
3667 case FileSharingMode_Read:
3668 case FileSharingMode_Write:
3669 case FileSharingMode_ReadWrite:
3670 case FileSharingMode_Delete:
3671 case FileSharingMode_ReadDelete:
3672 case FileSharingMode_WriteDelete:
3673 return setError(E_NOTIMPL, tr("Only FileSharingMode_All is currently implemented"));
3674
3675 default:
3676 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3677 }
3678
3679 /* Combine and validate flags. */
3680 uint32_t fOpenEx = 0;
3681 for (size_t i = 0; i < aFlags.size(); i++)
3682 fOpenEx = aFlags[i];
3683 if (fOpenEx)
3684 return setError(E_INVALIDARG, tr("Unsupported FileOpenExFlag value(s) in aFlags (%#x)"), fOpenEx);
3685 openInfo.mfOpenEx = fOpenEx;
3686
3687 ComObjPtr <GuestFile> pFile;
3688 int rcGuest;
3689 int vrc = i_fileOpenEx(aPath, aAccessMode, aOpenAction, aSharingMode, aCreationMode, aFlags, pFile, &rcGuest);
3690 if (RT_SUCCESS(vrc))
3691 /* Return directory object to the caller. */
3692 hrc = pFile.queryInterfaceTo(aFile.asOutParam());
3693 else
3694 {
3695 switch (vrc)
3696 {
3697 case VERR_NOT_SUPPORTED:
3698 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3699 tr("Handling guest files not supported by installed Guest Additions"));
3700 break;
3701
3702 case VERR_GSTCTL_GUEST_ERROR:
3703 hrc = GuestFile::i_setErrorExternal(this, rcGuest);
3704 break;
3705
3706 default:
3707 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3708 break;
3709 }
3710 }
3711
3712 return hrc;
3713}
3714
3715HRESULT GuestSession::fileQuerySize(const com::Utf8Str &aPath, BOOL aFollowSymlinks, LONG64 *aSize)
3716{
3717 AutoCaller autoCaller(this);
3718 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3719
3720 if (aPath.isEmpty())
3721 return setError(E_INVALIDARG, tr("No path specified"));
3722
3723 HRESULT hrc = i_isReadyExternal();
3724 if (FAILED(hrc))
3725 return hrc;
3726
3727 int64_t llSize; int rcGuest;
3728 int vrc = i_fileQuerySize(aPath, aFollowSymlinks != FALSE, &llSize, &rcGuest);
3729 if (RT_SUCCESS(vrc))
3730 {
3731 *aSize = llSize;
3732 }
3733 else
3734 {
3735 if (GuestProcess::i_isGuestError(vrc))
3736 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3737 else
3738 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying file size failed: %Rrc"), vrc);
3739 }
3740
3741 return hrc;
3742}
3743
3744HRESULT GuestSession::fsObjExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3745{
3746 AutoCaller autoCaller(this);
3747 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3748
3749 if (aPath.isEmpty())
3750 return setError(E_INVALIDARG, tr("No path specified"));
3751
3752 HRESULT hrc = i_isReadyExternal();
3753 if (FAILED(hrc))
3754 return hrc;
3755
3756 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
3757
3758 *aExists = false;
3759
3760 GuestFsObjData objData;
3761 int rcGuest;
3762 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3763 if (RT_SUCCESS(vrc))
3764 {
3765 *aExists = TRUE;
3766 }
3767 else
3768 {
3769 if (GuestProcess::i_isGuestError(vrc))
3770 {
3771 if ( rcGuest == VERR_NOT_A_FILE
3772 || rcGuest == VERR_PATH_NOT_FOUND
3773 || rcGuest == VERR_FILE_NOT_FOUND
3774 || rcGuest == VERR_INVALID_NAME)
3775 {
3776 hrc = S_OK; /* Ignore these vrc values. */
3777 }
3778 else
3779 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3780 }
3781 else
3782 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3783 }
3784
3785 return hrc;
3786}
3787
3788HRESULT GuestSession::fsObjQueryInfo(const com::Utf8Str &aPath, BOOL aFollowSymlinks, ComPtr<IGuestFsObjInfo> &aInfo)
3789{
3790 AutoCaller autoCaller(this);
3791 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3792
3793 if (aPath.isEmpty())
3794 return setError(E_INVALIDARG, tr("No path specified"));
3795
3796 HRESULT hrc = i_isReadyExternal();
3797 if (FAILED(hrc))
3798 return hrc;
3799
3800 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
3801
3802 GuestFsObjData Info; int rcGuest;
3803 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, Info, &rcGuest);
3804 if (RT_SUCCESS(vrc))
3805 {
3806 ComObjPtr<GuestFsObjInfo> ptrFsObjInfo;
3807 hrc = ptrFsObjInfo.createObject();
3808 if (SUCCEEDED(hrc))
3809 {
3810 vrc = ptrFsObjInfo->init(Info);
3811 if (RT_SUCCESS(vrc))
3812 hrc = ptrFsObjInfo.queryInterfaceTo(aInfo.asOutParam());
3813 else
3814 hrc = setErrorVrc(vrc);
3815 }
3816 }
3817 else
3818 {
3819 if (GuestProcess::i_isGuestError(vrc))
3820 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3821 else
3822 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3823 }
3824
3825 return hrc;
3826}
3827
3828HRESULT GuestSession::fsObjRemove(const com::Utf8Str &aPath)
3829{
3830 AutoCaller autoCaller(this);
3831 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3832
3833 if (RT_UNLIKELY(aPath.isEmpty()))
3834 return setError(E_INVALIDARG, tr("No path specified"));
3835
3836 HRESULT hrc = i_isReadyExternal();
3837 if (FAILED(hrc))
3838 return hrc;
3839
3840 LogFlowThisFunc(("aPath=%s\n", aPath.c_str()));
3841
3842 int rcGuest;
3843 int vrc = i_fileRemove(aPath, &rcGuest);
3844 if (RT_FAILURE(vrc))
3845 {
3846 if (GuestProcess::i_isGuestError(vrc))
3847 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3848 else
3849 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3850 }
3851
3852 return hrc;
3853}
3854
3855HRESULT GuestSession::fsObjRemoveArray(const std::vector<com::Utf8Str> &aPaths, ComPtr<IProgress> &aProgress)
3856{
3857 AutoCaller autoCaller(this);
3858 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3859
3860 RT_NOREF(aPaths, aProgress);
3861
3862 return E_NOTIMPL;
3863}
3864
3865HRESULT GuestSession::fsObjRename(const com::Utf8Str &aSource,
3866 const com::Utf8Str &aDestination,
3867 const std::vector<FsObjRenameFlag_T> &aFlags)
3868{
3869 AutoCaller autoCaller(this);
3870 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3871
3872 if (RT_UNLIKELY(aSource.isEmpty()))
3873 return setError(E_INVALIDARG, tr("No source path specified"));
3874
3875 if (RT_UNLIKELY(aDestination.isEmpty()))
3876 return setError(E_INVALIDARG, tr("No destination path specified"));
3877
3878 HRESULT hrc = i_isReadyExternal();
3879 if (FAILED(hrc))
3880 return hrc;
3881
3882 /* Combine, validate and convert flags. */
3883 uint32_t fApiFlags = 0;
3884 for (size_t i = 0; i < aFlags.size(); i++)
3885 fApiFlags |= aFlags[i];
3886 if (fApiFlags & ~((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace))
3887 return setError(E_INVALIDARG, tr("Unknown rename flag: %#x"), fApiFlags);
3888
3889 LogFlowThisFunc(("aSource=%s, aDestination=%s\n", aSource.c_str(), aDestination.c_str()));
3890
3891 AssertCompile(FsObjRenameFlag_NoReplace == 0);
3892 AssertCompile(FsObjRenameFlag_Replace != 0);
3893 uint32_t fBackend;
3894 if ((fApiFlags & ((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace)) == FsObjRenameFlag_Replace)
3895 fBackend = PATHRENAME_FLAG_REPLACE;
3896 else
3897 fBackend = PATHRENAME_FLAG_NO_REPLACE;
3898
3899 /* Call worker to do the job. */
3900 int rcGuest;
3901 int vrc = i_pathRename(aSource, aDestination, fBackend, &rcGuest);
3902 if (RT_FAILURE(vrc))
3903 {
3904 switch (vrc)
3905 {
3906 case VERR_NOT_SUPPORTED:
3907 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3908 tr("Handling renaming guest directories not supported by installed Guest Additions"));
3909 break;
3910
3911 case VERR_GSTCTL_GUEST_ERROR:
3912 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Renaming guest directory failed: %Rrc"), rcGuest);
3913 break;
3914
3915 default:
3916 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming guest directory \"%s\" failed: %Rrc"),
3917 aSource.c_str(), vrc);
3918 break;
3919 }
3920 }
3921
3922 return hrc;
3923}
3924
3925HRESULT GuestSession::fsObjMove(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3926 const std::vector<FsObjMoveFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3927{
3928 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3929 ReturnComNotImplemented();
3930}
3931
3932HRESULT GuestSession::fsObjSetACL(const com::Utf8Str &aPath, BOOL aFollowSymlinks, const com::Utf8Str &aAcl, ULONG aMode)
3933{
3934 RT_NOREF(aPath, aFollowSymlinks, aAcl, aMode);
3935 ReturnComNotImplemented();
3936}
3937
3938
3939HRESULT GuestSession::processCreate(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
3940 const std::vector<com::Utf8Str> &aEnvironment,
3941 const std::vector<ProcessCreateFlag_T> &aFlags,
3942 ULONG aTimeoutMS, ComPtr<IGuestProcess> &aGuestProcess)
3943{
3944 LogFlowThisFuncEnter();
3945
3946 std::vector<LONG> affinityIgnored;
3947 return processCreateEx(aExecutable, aArguments, aEnvironment, aFlags, aTimeoutMS, ProcessPriority_Default,
3948 affinityIgnored, aGuestProcess);
3949}
3950
3951HRESULT GuestSession::processCreateEx(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
3952 const std::vector<com::Utf8Str> &aEnvironment,
3953 const std::vector<ProcessCreateFlag_T> &aFlags, ULONG aTimeoutMS,
3954 ProcessPriority_T aPriority, const std::vector<LONG> &aAffinity,
3955 ComPtr<IGuestProcess> &aGuestProcess)
3956{
3957 AutoCaller autoCaller(this);
3958 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3959
3960 HRESULT hr = i_isReadyExternal();
3961 if (FAILED(hr))
3962 return hr;
3963
3964 /** @todo r=bird: Check input better? aPriority is passed on to the guest
3965 * without any validation. Flags not existing in this vbox version are
3966 * ignored, potentially doing something entirely different than what the
3967 * caller had in mind. */
3968
3969 /*
3970 * Must have an executable to execute. If none is given, we try use the
3971 * zero'th argument.
3972 */
3973 const char *pszExecutable = aExecutable.c_str();
3974 if (RT_UNLIKELY(pszExecutable == NULL || *pszExecutable == '\0'))
3975 {
3976 if (aArguments.size() > 0)
3977 pszExecutable = aArguments[0].c_str();
3978 if (pszExecutable == NULL || *pszExecutable == '\0')
3979 return setError(E_INVALIDARG, tr("No command to execute specified"));
3980 }
3981
3982 LogFlowThisFuncEnter();
3983
3984 /*
3985 * Build the process startup info.
3986 */
3987 GuestProcessStartupInfo procInfo;
3988
3989 /* Executable and arguments. */
3990 procInfo.mExecutable = pszExecutable;
3991 if (aArguments.size())
3992 for (size_t i = 0; i < aArguments.size(); i++)
3993 procInfo.mArguments.push_back(aArguments[i]);
3994
3995 /* Combine the environment changes associated with the ones passed in by
3996 the caller, giving priority to the latter. The changes are putenv style
3997 and will be applied to the standard environment for the guest user. */
3998 int vrc = procInfo.mEnvironmentChanges.copy(mData.mEnvironmentChanges);
3999 if (RT_SUCCESS(vrc))
4000 vrc = procInfo.mEnvironmentChanges.applyPutEnvArray(aEnvironment);
4001 if (RT_SUCCESS(vrc))
4002 {
4003 /* Convert the flag array into a mask. */
4004 if (aFlags.size())
4005 for (size_t i = 0; i < aFlags.size(); i++)
4006 procInfo.mFlags |= aFlags[i];
4007
4008 procInfo.mTimeoutMS = aTimeoutMS;
4009
4010 /** @todo use RTCPUSET instead of archaic 64-bit variables! */
4011 if (aAffinity.size())
4012 for (size_t i = 0; i < aAffinity.size(); i++)
4013 if (aAffinity[i])
4014 procInfo.mAffinity |= (uint64_t)1 << i;
4015
4016 procInfo.mPriority = aPriority;
4017
4018 /*
4019 * Create a guest process object.
4020 */
4021 ComObjPtr<GuestProcess> pProcess;
4022 vrc = i_processCreateEx(procInfo, pProcess);
4023 if (RT_SUCCESS(vrc))
4024 {
4025 ComPtr<IGuestProcess> pIProcess;
4026 hr = pProcess.queryInterfaceTo(pIProcess.asOutParam());
4027 if (SUCCEEDED(hr))
4028 {
4029 /*
4030 * Start the process.
4031 */
4032 vrc = pProcess->i_startProcessAsync();
4033 if (RT_SUCCESS(vrc))
4034 {
4035 aGuestProcess = pIProcess;
4036
4037 LogFlowFuncLeaveRC(vrc);
4038 return S_OK;
4039 }
4040
4041 hr = setErrorVrc(vrc, tr("Failed to start guest process: %Rrc"), vrc);
4042 }
4043 }
4044 else if (vrc == VERR_GSTCTL_MAX_OBJECTS_REACHED)
4045 hr = setErrorVrc(vrc, tr("Maximum number of concurrent guest processes per session (%u) reached"),
4046 VBOX_GUESTCTRL_MAX_OBJECTS);
4047 else
4048 hr = setErrorVrc(vrc, tr("Failed to create guest process object: %Rrc"), vrc);
4049 }
4050 else
4051 hr = setErrorVrc(vrc, tr("Failed to set up the environment: %Rrc"), vrc);
4052
4053 LogFlowFuncLeaveRC(vrc);
4054 return hr;
4055}
4056
4057HRESULT GuestSession::processGet(ULONG aPid, ComPtr<IGuestProcess> &aGuestProcess)
4058
4059{
4060 AutoCaller autoCaller(this);
4061 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4062
4063 if (aPid == 0)
4064 return setError(E_INVALIDARG, tr("No valid process ID (PID) specified"));
4065
4066 LogFlowThisFunc(("PID=%RU32\n", aPid));
4067
4068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4069
4070 HRESULT hr = S_OK;
4071
4072 ComObjPtr<GuestProcess> pProcess;
4073 int rc = i_processGetByPID(aPid, &pProcess);
4074 if (RT_FAILURE(rc))
4075 hr = setError(E_INVALIDARG, tr("No process with PID %RU32 found"), aPid);
4076
4077 /* This will set (*aProcess) to NULL if pProgress is NULL. */
4078 HRESULT hr2 = pProcess.queryInterfaceTo(aGuestProcess.asOutParam());
4079 if (SUCCEEDED(hr))
4080 hr = hr2;
4081
4082 LogFlowThisFunc(("aProcess=%p, hr=%Rhrc\n", (IGuestProcess*)aGuestProcess, hr));
4083 return hr;
4084}
4085
4086HRESULT GuestSession::symlinkCreate(const com::Utf8Str &aSource, const com::Utf8Str &aTarget, SymlinkType_T aType)
4087{
4088 RT_NOREF(aSource, aTarget, aType);
4089 ReturnComNotImplemented();
4090}
4091
4092HRESULT GuestSession::symlinkExists(const com::Utf8Str &aSymlink, BOOL *aExists)
4093
4094{
4095 RT_NOREF(aSymlink, aExists);
4096 ReturnComNotImplemented();
4097}
4098
4099HRESULT GuestSession::symlinkRead(const com::Utf8Str &aSymlink, const std::vector<SymlinkReadFlag_T> &aFlags,
4100 com::Utf8Str &aTarget)
4101{
4102 RT_NOREF(aSymlink, aFlags, aTarget);
4103 ReturnComNotImplemented();
4104}
4105
4106HRESULT GuestSession::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
4107{
4108 AutoCaller autoCaller(this);
4109 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4110
4111 /* Note: No call to i_isReadyExternal() needed here, as the session might not has been started (yet). */
4112
4113 LogFlowThisFuncEnter();
4114
4115 HRESULT hrc = S_OK;
4116
4117 /*
4118 * Note: Do not hold any locks here while waiting!
4119 */
4120 int rcGuest; GuestSessionWaitResult_T waitResult;
4121 int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &rcGuest);
4122 if (RT_SUCCESS(vrc))
4123 *aReason = waitResult;
4124 else
4125 {
4126 switch (vrc)
4127 {
4128 case VERR_GSTCTL_GUEST_ERROR:
4129 hrc = GuestSession::i_setErrorExternal(this, rcGuest);
4130 break;
4131
4132 case VERR_TIMEOUT:
4133 *aReason = GuestSessionWaitResult_Timeout;
4134 break;
4135
4136 default:
4137 {
4138 const char *pszSessionName = mData.mSession.mName.c_str();
4139 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4140 tr("Waiting for guest session \"%s\" failed: %Rrc"),
4141 pszSessionName ? pszSessionName : tr("Unnamed"), vrc);
4142 break;
4143 }
4144 }
4145 }
4146
4147 LogFlowFuncLeaveRC(vrc);
4148 return hrc;
4149}
4150
4151HRESULT GuestSession::waitForArray(const std::vector<GuestSessionWaitForFlag_T> &aWaitFor, ULONG aTimeoutMS,
4152 GuestSessionWaitResult_T *aReason)
4153{
4154 AutoCaller autoCaller(this);
4155 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4156
4157 /* Note: No call to i_isReadyExternal() needed here, as the session might not has been started (yet). */
4158
4159 LogFlowThisFuncEnter();
4160
4161 /*
4162 * Note: Do not hold any locks here while waiting!
4163 */
4164 uint32_t fWaitFor = GuestSessionWaitForFlag_None;
4165 for (size_t i = 0; i < aWaitFor.size(); i++)
4166 fWaitFor |= aWaitFor[i];
4167
4168 return WaitFor(fWaitFor, aTimeoutMS, aReason);
4169}
Note: See TracBrowser for help on using the repository browser.

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