VirtualBox

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

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

Guest Control/Main: Made the IGuestSession::copyToGuest() and IGuestSession::copyFromGuest() API calls more flexible in terms of not having to pass the flags and filter arrays. Untested.

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