VirtualBox

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

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

Main,Config.kmk: GCC 8.2.0 fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 129.4 KB
Line 
1/* $Id: GuestSessionImpl.cpp 73505 2018-08-05 13:58:10Z 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<FsObjType_T> &aTypes, const std::vector<com::Utf8Str> &aFlags,
2895 const com::Utf8Str &aDestination, 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() != cSources
2902 || aTypes.size() != cSources
2903 || aFlags.size() != cSources)
2904 {
2905 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
2906 }
2907
2908 GuestSessionFsSourceSet SourceSet;
2909
2910 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
2911 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
2912 std::vector<FsObjType_T>::const_iterator itType = aTypes.begin();
2913 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
2914
2915 while (itSource != aSources.end())
2916 {
2917 GuestSessionFsSourceSpec source;
2918 source.strSource = *itSource;
2919 source.strFilter = *itFilter;
2920 source.enmType = *itType;
2921 source.enmPathStyle = i_getPathStyle();
2922
2923 HRESULT hrc;
2924 if (source.enmType == FsObjType_Directory)
2925 {
2926 hrc = GuestSession::i_directoryCopyFlagFromStr(*itFlags, &source.Type.Dir.fCopyFlags);
2927 source.Type.Dir.fRecursive = true; /* Implicit. */
2928 }
2929 else if (source.enmType == FsObjType_File)
2930 hrc = GuestSession::i_fileCopyFlagFromStr(*itFlags, &source.Type.File.fCopyFlags);
2931 else
2932 return setError(E_INVALIDARG, tr("Source type %d invalid / not supported"), source.enmType);
2933 if (FAILED(hrc))
2934 return hrc;
2935
2936 SourceSet.push_back(source);
2937
2938 ++itSource;
2939 ++itFilter;
2940 ++itType;
2941 ++itFlags;
2942 }
2943
2944 return i_copyFromGuest(SourceSet, aDestination, aProgress);
2945}
2946
2947HRESULT GuestSession::copyToGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
2948 const std::vector<FsObjType_T> &aTypes, const std::vector<com::Utf8Str> &aFlags,
2949 const com::Utf8Str &aDestination, ComPtr<IProgress> &aProgress)
2950{
2951 AutoCaller autoCaller(this);
2952 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2953
2954 const size_t cSources = aSources.size();
2955 if ( aFilters.size() != cSources
2956 || aTypes.size() != cSources
2957 || aFlags.size() != cSources)
2958 {
2959 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
2960 }
2961
2962 GuestSessionFsSourceSet SourceSet;
2963
2964 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
2965 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
2966 std::vector<FsObjType_T>::const_iterator itType = aTypes.begin();
2967 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
2968
2969 while (itSource != aSources.end())
2970 {
2971 GuestSessionFsSourceSpec source;
2972 source.strSource = *itSource;
2973 source.strFilter = *itFilter;
2974 source.enmType = *itType;
2975 source.enmPathStyle = i_getPathStyle();
2976
2977 HRESULT hrc;
2978 if (source.enmType == FsObjType_Directory)
2979 {
2980 hrc = GuestSession::i_directoryCopyFlagFromStr(*itFlags, &source.Type.Dir.fCopyFlags);
2981 source.Type.Dir.fFollowSymlinks = true; /** @todo Add a flag for that in DirectoryCopyFlag_T. Later. */
2982 source.Type.Dir.fRecursive = true; /* Implicit. */
2983 }
2984 else if (source.enmType == FsObjType_File)
2985 hrc = GuestSession::i_fileCopyFlagFromStr(*itFlags, &source.Type.File.fCopyFlags);
2986 else
2987 return setError(E_INVALIDARG, tr("Source type %d invalid / not supported"), source.enmType);
2988 if (FAILED(hrc))
2989 return hrc;
2990
2991 SourceSet.push_back(source);
2992
2993 ++itSource;
2994 ++itFilter;
2995 ++itType;
2996 ++itFlags;
2997 }
2998
2999 return i_copyToGuest(SourceSet, aDestination, aProgress);
3000}
3001
3002HRESULT GuestSession::directoryCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3003 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3004{
3005 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3006 ReturnComNotImplemented();
3007}
3008
3009HRESULT GuestSession::directoryCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3010 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3011{
3012 AutoCaller autoCaller(this);
3013 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3014
3015 uint32_t fFlags = DirectoryCopyFlag_None;
3016 if (aFlags.size())
3017 {
3018 for (size_t i = 0; i < aFlags.size(); i++)
3019 fFlags |= aFlags[i];
3020 }
3021
3022 GuestSessionFsSourceSet SourceSet;
3023
3024 GuestSessionFsSourceSpec source;
3025 source.strSource = aSource;
3026 source.enmType = FsObjType_Directory;
3027 source.enmPathStyle = i_getPathStyle();
3028 source.Type.Dir.fCopyFlags = (DirectoryCopyFlag_T)fFlags;
3029 source.Type.Dir.fRecursive = true; /* Implicit. */
3030
3031 SourceSet.push_back(source);
3032
3033 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3034}
3035
3036HRESULT GuestSession::directoryCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3037 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3038{
3039 AutoCaller autoCaller(this);
3040 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3041
3042 uint32_t fFlags = DirectoryCopyFlag_None;
3043 if (aFlags.size())
3044 {
3045 for (size_t i = 0; i < aFlags.size(); i++)
3046 fFlags |= aFlags[i];
3047 }
3048
3049 GuestSessionFsSourceSet SourceSet;
3050
3051 GuestSessionFsSourceSpec source;
3052 source.strSource = aSource;
3053 source.enmType = FsObjType_Directory;
3054 source.enmPathStyle = i_getPathStyle();
3055 source.Type.Dir.fCopyFlags = (DirectoryCopyFlag_T)fFlags;
3056 source.Type.Dir.fFollowSymlinks = true; /** @todo Add a flag for that in DirectoryCopyFlag_T. Later. */
3057 source.Type.Dir.fRecursive = true; /* Implicit. */
3058
3059 SourceSet.push_back(source);
3060
3061 return i_copyToGuest(SourceSet, aDestination, aProgress);
3062}
3063
3064HRESULT GuestSession::directoryCreate(const com::Utf8Str &aPath, ULONG aMode,
3065 const std::vector<DirectoryCreateFlag_T> &aFlags)
3066{
3067 AutoCaller autoCaller(this);
3068 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3069
3070 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3071 return setError(E_INVALIDARG, tr("No directory to create specified"));
3072
3073 uint32_t fFlags = DirectoryCreateFlag_None;
3074 if (aFlags.size())
3075 {
3076 for (size_t i = 0; i < aFlags.size(); i++)
3077 fFlags |= aFlags[i];
3078
3079 if (fFlags)
3080 if (!(fFlags & DirectoryCreateFlag_Parents))
3081 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), fFlags);
3082 }
3083
3084 HRESULT hrc = i_isReadyExternal();
3085 if (FAILED(hrc))
3086 return hrc;
3087
3088 LogFlowThisFuncEnter();
3089
3090 ComObjPtr <GuestDirectory> pDirectory; int rcGuest;
3091 int vrc = i_directoryCreate(aPath, (uint32_t)aMode, fFlags, &rcGuest);
3092 if (RT_FAILURE(vrc))
3093 {
3094 if (GuestProcess::i_isGuestError(vrc))
3095 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest,
3096 tr("Directory creation failed: %s"), GuestDirectory::i_guestErrorToString(rcGuest).c_str());
3097 else
3098 {
3099 switch (vrc)
3100 {
3101 case VERR_INVALID_PARAMETER:
3102 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: Invalid parameters given"));
3103 break;
3104
3105 case VERR_BROKEN_PIPE:
3106 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: Unexpectedly aborted"));
3107 break;
3108
3109 default:
3110 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: %Rrc"), vrc);
3111 break;
3112 }
3113 }
3114 }
3115
3116 return hrc;
3117}
3118
3119HRESULT GuestSession::directoryCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath,
3120 BOOL aSecure, com::Utf8Str &aDirectory)
3121{
3122 RT_NOREF(aMode, aSecure);
3123
3124 AutoCaller autoCaller(this);
3125 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3126
3127 if (RT_UNLIKELY((aTemplateName.c_str()) == NULL || *(aTemplateName.c_str()) == '\0'))
3128 return setError(E_INVALIDARG, tr("No template specified"));
3129 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3130 return setError(E_INVALIDARG, tr("No directory name specified"));
3131
3132 HRESULT hrc = i_isReadyExternal();
3133 if (FAILED(hrc))
3134 return hrc;
3135
3136 LogFlowThisFuncEnter();
3137
3138 int rcGuest;
3139 int vrc = i_fsCreateTemp(aTemplateName, aPath, true /* Directory */, aDirectory, &rcGuest);
3140 if (!RT_SUCCESS(vrc))
3141 {
3142 switch (vrc)
3143 {
3144 case VERR_GSTCTL_GUEST_ERROR:
3145 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3146 break;
3147
3148 default:
3149 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Temporary directory creation \"%s\" with template \"%s\" failed: %Rrc"),
3150 aPath.c_str(), aTemplateName.c_str(), vrc);
3151 break;
3152 }
3153 }
3154
3155 return hrc;
3156}
3157
3158HRESULT GuestSession::directoryExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3159{
3160 AutoCaller autoCaller(this);
3161 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3162
3163 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3164 return setError(E_INVALIDARG, tr("No directory to check existence for specified"));
3165
3166 HRESULT hrc = i_isReadyExternal();
3167 if (FAILED(hrc))
3168 return hrc;
3169
3170 LogFlowThisFuncEnter();
3171
3172 GuestFsObjData objData; int rcGuest;
3173 int vrc = i_directoryQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3174 if (RT_SUCCESS(vrc))
3175 *aExists = objData.mType == FsObjType_Directory;
3176 else
3177 {
3178 switch (vrc)
3179 {
3180 case VERR_GSTCTL_GUEST_ERROR:
3181 {
3182 switch (rcGuest)
3183 {
3184 case VERR_PATH_NOT_FOUND:
3185 *aExists = FALSE;
3186 break;
3187 default:
3188 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying directory existence \"%s\" failed: %s"),
3189 aPath.c_str(), GuestProcess::i_guestErrorToString(rcGuest).c_str());
3190 break;
3191 }
3192 break;
3193 }
3194
3195 default:
3196 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying directory existence \"%s\" failed: %Rrc"),
3197 aPath.c_str(), vrc);
3198 break;
3199 }
3200 }
3201
3202 return hrc;
3203}
3204
3205HRESULT GuestSession::directoryOpen(const com::Utf8Str &aPath, const com::Utf8Str &aFilter,
3206 const std::vector<DirectoryOpenFlag_T> &aFlags, ComPtr<IGuestDirectory> &aDirectory)
3207{
3208 AutoCaller autoCaller(this);
3209 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3210
3211 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3212 return setError(E_INVALIDARG, tr("No directory to open specified"));
3213 if (RT_UNLIKELY((aFilter.c_str()) != NULL && *(aFilter.c_str()) != '\0'))
3214 return setError(E_INVALIDARG, tr("Directory filters are not implemented yet"));
3215
3216 uint32_t fFlags = DirectoryOpenFlag_None;
3217 if (aFlags.size())
3218 {
3219 for (size_t i = 0; i < aFlags.size(); i++)
3220 fFlags |= aFlags[i];
3221
3222 if (fFlags)
3223 return setError(E_INVALIDARG, tr("Open flags (%#x) not implemented yet"), fFlags);
3224 }
3225
3226 HRESULT hrc = i_isReadyExternal();
3227 if (FAILED(hrc))
3228 return hrc;
3229
3230 LogFlowThisFuncEnter();
3231
3232 GuestDirectoryOpenInfo openInfo;
3233 openInfo.mPath = aPath;
3234 openInfo.mFilter = aFilter;
3235 openInfo.mFlags = fFlags;
3236
3237 ComObjPtr <GuestDirectory> pDirectory; int rcGuest;
3238 int vrc = i_directoryOpen(openInfo, pDirectory, &rcGuest);
3239 if (RT_SUCCESS(vrc))
3240 {
3241 /* Return directory object to the caller. */
3242 hrc = pDirectory.queryInterfaceTo(aDirectory.asOutParam());
3243 }
3244 else
3245 {
3246 switch (vrc)
3247 {
3248 case VERR_INVALID_PARAMETER:
3249 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening directory \"%s\" failed; invalid parameters given"),
3250 aPath.c_str());
3251 break;
3252
3253 case VERR_GSTCTL_GUEST_ERROR:
3254 hrc = GuestDirectory::i_setErrorExternal(this, rcGuest);
3255 break;
3256
3257 default:
3258 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3259 break;
3260 }
3261 }
3262
3263 return hrc;
3264}
3265
3266HRESULT GuestSession::directoryRemove(const com::Utf8Str &aPath)
3267{
3268 AutoCaller autoCaller(this);
3269 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3270
3271 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3272 return setError(E_INVALIDARG, tr("No directory to remove specified"));
3273
3274 HRESULT hrc = i_isReadyExternal();
3275 if (FAILED(hrc))
3276 return hrc;
3277
3278 LogFlowThisFuncEnter();
3279
3280 /* No flags; only remove the directory when empty. */
3281 uint32_t uFlags = 0;
3282
3283 int rcGuest;
3284 int vrc = i_directoryRemove(aPath, uFlags, &rcGuest);
3285 if (RT_FAILURE(vrc))
3286 {
3287 switch (vrc)
3288 {
3289 case VERR_NOT_SUPPORTED:
3290 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3291 tr("Handling removing guest directories not supported by installed Guest Additions"));
3292 break;
3293
3294 case VERR_GSTCTL_GUEST_ERROR:
3295 hrc = GuestDirectory::i_setErrorExternal(this, rcGuest);
3296 break;
3297
3298 default:
3299 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing guest directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3300 break;
3301 }
3302 }
3303
3304 return hrc;
3305}
3306
3307HRESULT GuestSession::directoryRemoveRecursive(const com::Utf8Str &aPath, const std::vector<DirectoryRemoveRecFlag_T> &aFlags,
3308 ComPtr<IProgress> &aProgress)
3309{
3310 RT_NOREF(aFlags);
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 recursively specified"));
3317
3318/** @todo r=bird: Must check that the flags matches the hardcoded behavior
3319 * further down!! */
3320
3321 HRESULT hrc = i_isReadyExternal();
3322 if (FAILED(hrc))
3323 return hrc;
3324
3325 LogFlowThisFuncEnter();
3326
3327 ComObjPtr<Progress> pProgress;
3328 hrc = pProgress.createObject();
3329 if (SUCCEEDED(hrc))
3330 hrc = pProgress->init(static_cast<IGuestSession *>(this),
3331 Bstr(tr("Removing guest directory")).raw(),
3332 TRUE /*aCancelable*/);
3333 if (FAILED(hrc))
3334 return hrc;
3335
3336 /* Note: At the moment we don't supply progress information while
3337 * deleting a guest directory recursively. So just complete
3338 * the progress object right now. */
3339 /** @todo Implement progress reporting on guest directory deletion! */
3340 hrc = pProgress->i_notifyComplete(S_OK);
3341 if (FAILED(hrc))
3342 return hrc;
3343
3344 /* Remove the directory + all its contents. */
3345 uint32_t uFlags = DIRREMOVE_FLAG_RECURSIVE
3346 | DIRREMOVE_FLAG_CONTENT_AND_DIR;
3347 int rcGuest;
3348 int vrc = i_directoryRemove(aPath, uFlags, &rcGuest);
3349 if (RT_FAILURE(vrc))
3350 {
3351 switch (vrc)
3352 {
3353 case VERR_NOT_SUPPORTED:
3354 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3355 tr("Handling removing guest directories recursively not supported by installed Guest Additions"));
3356 break;
3357
3358 case VERR_GSTCTL_GUEST_ERROR:
3359 hrc = GuestFile::i_setErrorExternal(this, rcGuest);
3360 break;
3361
3362 default:
3363 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Recursively removing guest directory \"%s\" failed: %Rrc"),
3364 aPath.c_str(), vrc);
3365 break;
3366 }
3367 }
3368 else
3369 {
3370 pProgress.queryInterfaceTo(aProgress.asOutParam());
3371 }
3372
3373 return hrc;
3374}
3375
3376HRESULT GuestSession::environmentScheduleSet(const com::Utf8Str &aName, const com::Utf8Str &aValue)
3377{
3378 AutoCaller autoCaller(this);
3379 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3380
3381 HRESULT hrc;
3382 if (RT_LIKELY(aName.isNotEmpty()))
3383 {
3384 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3385 {
3386 LogFlowThisFuncEnter();
3387
3388 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3389 int vrc = mData.mEnvironmentChanges.setVariable(aName, aValue);
3390 if (RT_SUCCESS(vrc))
3391 hrc = S_OK;
3392 else
3393 hrc = setErrorVrc(vrc);
3394 }
3395 else
3396 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3397 }
3398 else
3399 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3400
3401 LogFlowThisFuncLeave();
3402 return hrc;
3403}
3404
3405HRESULT GuestSession::environmentScheduleUnset(const com::Utf8Str &aName)
3406{
3407 AutoCaller autoCaller(this);
3408 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3409
3410 HRESULT hrc;
3411 if (RT_LIKELY(aName.isNotEmpty()))
3412 {
3413 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3414 {
3415 LogFlowThisFuncEnter();
3416
3417 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3418 int vrc = mData.mEnvironmentChanges.unsetVariable(aName);
3419 if (RT_SUCCESS(vrc))
3420 hrc = S_OK;
3421 else
3422 hrc = setErrorVrc(vrc);
3423 }
3424 else
3425 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3426 }
3427 else
3428 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3429
3430 LogFlowThisFuncLeave();
3431 return hrc;
3432}
3433
3434HRESULT GuestSession::environmentGetBaseVariable(const com::Utf8Str &aName, com::Utf8Str &aValue)
3435{
3436 AutoCaller autoCaller(this);
3437 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3438
3439 HRESULT hrc;
3440 if (RT_LIKELY(aName.isNotEmpty()))
3441 {
3442 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3443 {
3444 LogFlowThisFuncEnter();
3445
3446 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3447 if (mData.mpBaseEnvironment)
3448 {
3449 int vrc = mData.mpBaseEnvironment->getVariable(aName, &aValue);
3450 if (RT_SUCCESS(vrc))
3451 hrc = S_OK;
3452 else
3453 hrc = setErrorVrc(vrc);
3454 }
3455 else if (mData.mProtocolVersion < 99999)
3456 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
3457 else
3458 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
3459 }
3460 else
3461 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3462 }
3463 else
3464 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3465
3466 LogFlowThisFuncLeave();
3467 return hrc;
3468}
3469
3470HRESULT GuestSession::environmentDoesBaseVariableExist(const com::Utf8Str &aName, BOOL *aExists)
3471{
3472 AutoCaller autoCaller(this);
3473 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3474
3475 *aExists = FALSE;
3476
3477 HRESULT hrc;
3478 if (RT_LIKELY(aName.isNotEmpty()))
3479 {
3480 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3481 {
3482 LogFlowThisFuncEnter();
3483
3484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3485 if (mData.mpBaseEnvironment)
3486 {
3487 hrc = S_OK;
3488 *aExists = mData.mpBaseEnvironment->doesVariableExist(aName);
3489 }
3490 else if (mData.mProtocolVersion < 99999)
3491 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
3492 else
3493 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
3494 }
3495 else
3496 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3497 }
3498 else
3499 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3500
3501 LogFlowThisFuncLeave();
3502 return hrc;
3503}
3504
3505HRESULT GuestSession::fileCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath, BOOL aSecure,
3506 ComPtr<IGuestFile> &aFile)
3507{
3508 RT_NOREF(aTemplateName, aMode, aPath, aSecure, aFile);
3509 ReturnComNotImplemented();
3510}
3511
3512HRESULT GuestSession::fileExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3513{
3514 AutoCaller autoCaller(this);
3515 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3516
3517 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3518 {
3519 *aExists = FALSE;
3520 return S_OK;
3521 }
3522
3523 HRESULT hrc = i_isReadyExternal();
3524 if (FAILED(hrc))
3525 return hrc;
3526
3527 LogFlowThisFuncEnter();
3528
3529 GuestFsObjData objData; int rcGuest;
3530 int vrc = i_fileQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3531 if (RT_SUCCESS(vrc))
3532 {
3533 *aExists = TRUE;
3534 return S_OK;
3535 }
3536
3537 switch (vrc)
3538 {
3539 case VERR_GSTCTL_GUEST_ERROR:
3540 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3541 break;
3542
3543/** @todo r=bird: what about VERR_PATH_NOT_FOUND and VERR_FILE_NOT_FOUND?
3544 * Where does that get converted to *aExists = FALSE? */
3545 case VERR_NOT_A_FILE:
3546 *aExists = FALSE;
3547 break;
3548
3549 default:
3550 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying file information for \"%s\" failed: %Rrc"),
3551 aPath.c_str(), vrc);
3552 break;
3553 }
3554
3555 return hrc;
3556}
3557
3558HRESULT GuestSession::fileOpen(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3559 ULONG aCreationMode, ComPtr<IGuestFile> &aFile)
3560{
3561 LogFlowThisFuncEnter();
3562
3563 const std::vector<FileOpenExFlag_T> EmptyFlags;
3564 return fileOpenEx(aPath, aAccessMode, aOpenAction, FileSharingMode_All, aCreationMode, EmptyFlags, aFile);
3565}
3566
3567HRESULT GuestSession::fileOpenEx(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3568 FileSharingMode_T aSharingMode, ULONG aCreationMode,
3569 const std::vector<FileOpenExFlag_T> &aFlags, ComPtr<IGuestFile> &aFile)
3570{
3571 AutoCaller autoCaller(this);
3572 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3573
3574 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3575 return setError(E_INVALIDARG, tr("No file to open specified"));
3576
3577 HRESULT hrc = i_isReadyExternal();
3578 if (FAILED(hrc))
3579 return hrc;
3580
3581 LogFlowThisFuncEnter();
3582
3583 GuestFileOpenInfo openInfo;
3584 openInfo.mFileName = aPath;
3585 openInfo.mCreationMode = aCreationMode;
3586
3587 /* Validate aAccessMode. */
3588 switch (aAccessMode)
3589 {
3590 case FileAccessMode_ReadOnly:
3591 RT_FALL_THRU();
3592 case FileAccessMode_WriteOnly:
3593 RT_FALL_THRU();
3594 case FileAccessMode_ReadWrite:
3595 openInfo.mAccessMode = aAccessMode;
3596 break;
3597 case FileAccessMode_AppendOnly:
3598 RT_FALL_THRU();
3599 case FileAccessMode_AppendRead:
3600 return setError(E_NOTIMPL, tr("Append access modes are not yet implemented"));
3601 default:
3602 return setError(E_INVALIDARG, tr("Unknown FileAccessMode value %u (%#x)"), aAccessMode, aAccessMode);
3603 }
3604
3605 /* Validate aOpenAction to the old format. */
3606 switch (aOpenAction)
3607 {
3608 case FileOpenAction_OpenExisting:
3609 RT_FALL_THRU();
3610 case FileOpenAction_OpenOrCreate:
3611 RT_FALL_THRU();
3612 case FileOpenAction_CreateNew:
3613 RT_FALL_THRU();
3614 case FileOpenAction_CreateOrReplace:
3615 RT_FALL_THRU();
3616 case FileOpenAction_OpenExistingTruncated:
3617 RT_FALL_THRU();
3618 case FileOpenAction_AppendOrCreate:
3619 openInfo.mOpenAction = aOpenAction;
3620 break;
3621 default:
3622 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3623 }
3624
3625 /* Validate aSharingMode. */
3626 switch (aSharingMode)
3627 {
3628 case FileSharingMode_All:
3629 openInfo.mSharingMode = aSharingMode;
3630 break;
3631 case FileSharingMode_Read:
3632 case FileSharingMode_Write:
3633 case FileSharingMode_ReadWrite:
3634 case FileSharingMode_Delete:
3635 case FileSharingMode_ReadDelete:
3636 case FileSharingMode_WriteDelete:
3637 return setError(E_NOTIMPL, tr("Only FileSharingMode_All is currently implemented"));
3638
3639 default:
3640 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3641 }
3642
3643 /* Combine and validate flags. */
3644 uint32_t fOpenEx = 0;
3645 for (size_t i = 0; i < aFlags.size(); i++)
3646 fOpenEx = aFlags[i];
3647 if (fOpenEx)
3648 return setError(E_INVALIDARG, tr("Unsupported FileOpenExFlag value(s) in aFlags (%#x)"), fOpenEx);
3649 openInfo.mfOpenEx = fOpenEx;
3650
3651 ComObjPtr <GuestFile> pFile;
3652 int rcGuest;
3653 int vrc = i_fileOpenEx(aPath, aAccessMode, aOpenAction, aSharingMode, aCreationMode, aFlags, pFile, &rcGuest);
3654 if (RT_SUCCESS(vrc))
3655 /* Return directory object to the caller. */
3656 hrc = pFile.queryInterfaceTo(aFile.asOutParam());
3657 else
3658 {
3659 switch (vrc)
3660 {
3661 case VERR_NOT_SUPPORTED:
3662 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3663 tr("Handling guest files not supported by installed Guest Additions"));
3664 break;
3665
3666 case VERR_GSTCTL_GUEST_ERROR:
3667 hrc = GuestFile::i_setErrorExternal(this, rcGuest);
3668 break;
3669
3670 default:
3671 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3672 break;
3673 }
3674 }
3675
3676 return hrc;
3677}
3678
3679HRESULT GuestSession::fileQuerySize(const com::Utf8Str &aPath, BOOL aFollowSymlinks, LONG64 *aSize)
3680{
3681 AutoCaller autoCaller(this);
3682 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3683
3684 if (aPath.isEmpty())
3685 return setError(E_INVALIDARG, tr("No path specified"));
3686
3687 HRESULT hrc = i_isReadyExternal();
3688 if (FAILED(hrc))
3689 return hrc;
3690
3691 int64_t llSize; int rcGuest;
3692 int vrc = i_fileQuerySize(aPath, aFollowSymlinks != FALSE, &llSize, &rcGuest);
3693 if (RT_SUCCESS(vrc))
3694 {
3695 *aSize = llSize;
3696 }
3697 else
3698 {
3699 if (GuestProcess::i_isGuestError(vrc))
3700 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3701 else
3702 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying file size failed: %Rrc"), vrc);
3703 }
3704
3705 return hrc;
3706}
3707
3708HRESULT GuestSession::fsObjExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3709{
3710 AutoCaller autoCaller(this);
3711 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3712
3713 if (aPath.isEmpty())
3714 return setError(E_INVALIDARG, tr("No path specified"));
3715
3716 HRESULT hrc = i_isReadyExternal();
3717 if (FAILED(hrc))
3718 return hrc;
3719
3720 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
3721
3722 *aExists = false;
3723
3724 GuestFsObjData objData;
3725 int rcGuest;
3726 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3727 if (RT_SUCCESS(vrc))
3728 {
3729 *aExists = TRUE;
3730 }
3731 else
3732 {
3733 if (GuestProcess::i_isGuestError(vrc))
3734 {
3735 if ( rcGuest == VERR_NOT_A_FILE
3736 || rcGuest == VERR_PATH_NOT_FOUND
3737 || rcGuest == VERR_FILE_NOT_FOUND
3738 || rcGuest == VERR_INVALID_NAME)
3739 {
3740 hrc = S_OK; /* Ignore these vrc values. */
3741 }
3742 else
3743 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3744 }
3745 else
3746 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3747 }
3748
3749 return hrc;
3750}
3751
3752HRESULT GuestSession::fsObjQueryInfo(const com::Utf8Str &aPath, BOOL aFollowSymlinks, ComPtr<IGuestFsObjInfo> &aInfo)
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 GuestFsObjData Info; int rcGuest;
3767 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, Info, &rcGuest);
3768 if (RT_SUCCESS(vrc))
3769 {
3770 ComObjPtr<GuestFsObjInfo> ptrFsObjInfo;
3771 hrc = ptrFsObjInfo.createObject();
3772 if (SUCCEEDED(hrc))
3773 {
3774 vrc = ptrFsObjInfo->init(Info);
3775 if (RT_SUCCESS(vrc))
3776 hrc = ptrFsObjInfo.queryInterfaceTo(aInfo.asOutParam());
3777 else
3778 hrc = setErrorVrc(vrc);
3779 }
3780 }
3781 else
3782 {
3783 if (GuestProcess::i_isGuestError(vrc))
3784 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3785 else
3786 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3787 }
3788
3789 return hrc;
3790}
3791
3792HRESULT GuestSession::fsObjRemove(const com::Utf8Str &aPath)
3793{
3794 AutoCaller autoCaller(this);
3795 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3796
3797 if (RT_UNLIKELY(aPath.isEmpty()))
3798 return setError(E_INVALIDARG, tr("No path specified"));
3799
3800 HRESULT hrc = i_isReadyExternal();
3801 if (FAILED(hrc))
3802 return hrc;
3803
3804 LogFlowThisFunc(("aPath=%s\n", aPath.c_str()));
3805
3806 int rcGuest;
3807 int vrc = i_fileRemove(aPath, &rcGuest);
3808 if (RT_FAILURE(vrc))
3809 {
3810 if (GuestProcess::i_isGuestError(vrc))
3811 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3812 else
3813 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3814 }
3815
3816 return hrc;
3817}
3818
3819HRESULT GuestSession::fsObjRename(const com::Utf8Str &aSource,
3820 const com::Utf8Str &aDestination,
3821 const std::vector<FsObjRenameFlag_T> &aFlags)
3822{
3823 AutoCaller autoCaller(this);
3824 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3825
3826 if (RT_UNLIKELY(aSource.isEmpty()))
3827 return setError(E_INVALIDARG, tr("No source path specified"));
3828
3829 if (RT_UNLIKELY(aDestination.isEmpty()))
3830 return setError(E_INVALIDARG, tr("No destination path specified"));
3831
3832 HRESULT hrc = i_isReadyExternal();
3833 if (FAILED(hrc))
3834 return hrc;
3835
3836 /* Combine, validate and convert flags. */
3837 uint32_t fApiFlags = 0;
3838 for (size_t i = 0; i < aFlags.size(); i++)
3839 fApiFlags |= aFlags[i];
3840 if (fApiFlags & ~((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace))
3841 return setError(E_INVALIDARG, tr("Unknown rename flag: %#x"), fApiFlags);
3842
3843 LogFlowThisFunc(("aSource=%s, aDestination=%s\n", aSource.c_str(), aDestination.c_str()));
3844
3845 AssertCompile(FsObjRenameFlag_NoReplace == 0);
3846 AssertCompile(FsObjRenameFlag_Replace != 0);
3847 uint32_t fBackend;
3848 if ((fApiFlags & ((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace)) == FsObjRenameFlag_Replace)
3849 fBackend = PATHRENAME_FLAG_REPLACE;
3850 else
3851 fBackend = PATHRENAME_FLAG_NO_REPLACE;
3852
3853 /* Call worker to do the job. */
3854 int rcGuest;
3855 int vrc = i_pathRename(aSource, aDestination, fBackend, &rcGuest);
3856 if (RT_FAILURE(vrc))
3857 {
3858 switch (vrc)
3859 {
3860 case VERR_NOT_SUPPORTED:
3861 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3862 tr("Handling renaming guest directories not supported by installed Guest Additions"));
3863 break;
3864
3865 case VERR_GSTCTL_GUEST_ERROR:
3866 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Renaming guest directory failed: %Rrc"), rcGuest);
3867 break;
3868
3869 default:
3870 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming guest directory \"%s\" failed: %Rrc"),
3871 aSource.c_str(), vrc);
3872 break;
3873 }
3874 }
3875
3876 return hrc;
3877}
3878
3879HRESULT GuestSession::fsObjMove(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3880 const std::vector<FsObjMoveFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3881{
3882 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3883 ReturnComNotImplemented();
3884}
3885
3886HRESULT GuestSession::fsObjSetACL(const com::Utf8Str &aPath, BOOL aFollowSymlinks, const com::Utf8Str &aAcl, ULONG aMode)
3887{
3888 RT_NOREF(aPath, aFollowSymlinks, aAcl, aMode);
3889 ReturnComNotImplemented();
3890}
3891
3892
3893HRESULT GuestSession::processCreate(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
3894 const std::vector<com::Utf8Str> &aEnvironment,
3895 const std::vector<ProcessCreateFlag_T> &aFlags,
3896 ULONG aTimeoutMS, ComPtr<IGuestProcess> &aGuestProcess)
3897{
3898 LogFlowThisFuncEnter();
3899
3900 std::vector<LONG> affinityIgnored;
3901 return processCreateEx(aExecutable, aArguments, aEnvironment, aFlags, aTimeoutMS, ProcessPriority_Default,
3902 affinityIgnored, aGuestProcess);
3903}
3904
3905HRESULT GuestSession::processCreateEx(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
3906 const std::vector<com::Utf8Str> &aEnvironment,
3907 const std::vector<ProcessCreateFlag_T> &aFlags, ULONG aTimeoutMS,
3908 ProcessPriority_T aPriority, const std::vector<LONG> &aAffinity,
3909 ComPtr<IGuestProcess> &aGuestProcess)
3910{
3911 AutoCaller autoCaller(this);
3912 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3913
3914 HRESULT hr = i_isReadyExternal();
3915 if (FAILED(hr))
3916 return hr;
3917
3918 /** @todo r=bird: Check input better? aPriority is passed on to the guest
3919 * without any validation. Flags not existing in this vbox version are
3920 * ignored, potentially doing something entirely different than what the
3921 * caller had in mind. */
3922
3923 /*
3924 * Must have an executable to execute. If none is given, we try use the
3925 * zero'th argument.
3926 */
3927 const char *pszExecutable = aExecutable.c_str();
3928 if (RT_UNLIKELY(pszExecutable == NULL || *pszExecutable == '\0'))
3929 {
3930 if (aArguments.size() > 0)
3931 pszExecutable = aArguments[0].c_str();
3932 if (pszExecutable == NULL || *pszExecutable == '\0')
3933 return setError(E_INVALIDARG, tr("No command to execute specified"));
3934 }
3935
3936 LogFlowThisFuncEnter();
3937
3938 /*
3939 * Build the process startup info.
3940 */
3941 GuestProcessStartupInfo procInfo;
3942
3943 /* Executable and arguments. */
3944 procInfo.mExecutable = pszExecutable;
3945 if (aArguments.size())
3946 for (size_t i = 0; i < aArguments.size(); i++)
3947 procInfo.mArguments.push_back(aArguments[i]);
3948
3949 /* Combine the environment changes associated with the ones passed in by
3950 the caller, giving priority to the latter. The changes are putenv style
3951 and will be applied to the standard environment for the guest user. */
3952 int vrc = procInfo.mEnvironmentChanges.copy(mData.mEnvironmentChanges);
3953 if (RT_SUCCESS(vrc))
3954 vrc = procInfo.mEnvironmentChanges.applyPutEnvArray(aEnvironment);
3955 if (RT_SUCCESS(vrc))
3956 {
3957 /* Convert the flag array into a mask. */
3958 if (aFlags.size())
3959 for (size_t i = 0; i < aFlags.size(); i++)
3960 procInfo.mFlags |= aFlags[i];
3961
3962 procInfo.mTimeoutMS = aTimeoutMS;
3963
3964 /** @todo use RTCPUSET instead of archaic 64-bit variables! */
3965 if (aAffinity.size())
3966 for (size_t i = 0; i < aAffinity.size(); i++)
3967 if (aAffinity[i])
3968 procInfo.mAffinity |= (uint64_t)1 << i;
3969
3970 procInfo.mPriority = aPriority;
3971
3972 /*
3973 * Create a guest process object.
3974 */
3975 ComObjPtr<GuestProcess> pProcess;
3976 vrc = i_processCreateEx(procInfo, pProcess);
3977 if (RT_SUCCESS(vrc))
3978 {
3979 ComPtr<IGuestProcess> pIProcess;
3980 hr = pProcess.queryInterfaceTo(pIProcess.asOutParam());
3981 if (SUCCEEDED(hr))
3982 {
3983 /*
3984 * Start the process.
3985 */
3986 vrc = pProcess->i_startProcessAsync();
3987 if (RT_SUCCESS(vrc))
3988 {
3989 aGuestProcess = pIProcess;
3990
3991 LogFlowFuncLeaveRC(vrc);
3992 return S_OK;
3993 }
3994
3995 hr = setErrorVrc(vrc, tr("Failed to start guest process: %Rrc"), vrc);
3996 }
3997 }
3998 else if (vrc == VERR_GSTCTL_MAX_OBJECTS_REACHED)
3999 hr = setErrorVrc(vrc, tr("Maximum number of concurrent guest processes per session (%u) reached"),
4000 VBOX_GUESTCTRL_MAX_OBJECTS);
4001 else
4002 hr = setErrorVrc(vrc, tr("Failed to create guest process object: %Rrc"), vrc);
4003 }
4004 else
4005 hr = setErrorVrc(vrc, tr("Failed to set up the environment: %Rrc"), vrc);
4006
4007 LogFlowFuncLeaveRC(vrc);
4008 return hr;
4009}
4010
4011HRESULT GuestSession::processGet(ULONG aPid, ComPtr<IGuestProcess> &aGuestProcess)
4012
4013{
4014 AutoCaller autoCaller(this);
4015 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4016
4017 if (aPid == 0)
4018 return setError(E_INVALIDARG, tr("No valid process ID (PID) specified"));
4019
4020 LogFlowThisFunc(("PID=%RU32\n", aPid));
4021
4022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4023
4024 HRESULT hr = S_OK;
4025
4026 ComObjPtr<GuestProcess> pProcess;
4027 int rc = i_processGetByPID(aPid, &pProcess);
4028 if (RT_FAILURE(rc))
4029 hr = setError(E_INVALIDARG, tr("No process with PID %RU32 found"), aPid);
4030
4031 /* This will set (*aProcess) to NULL if pProgress is NULL. */
4032 HRESULT hr2 = pProcess.queryInterfaceTo(aGuestProcess.asOutParam());
4033 if (SUCCEEDED(hr))
4034 hr = hr2;
4035
4036 LogFlowThisFunc(("aProcess=%p, hr=%Rhrc\n", (IGuestProcess*)aGuestProcess, hr));
4037 return hr;
4038}
4039
4040HRESULT GuestSession::symlinkCreate(const com::Utf8Str &aSource, const com::Utf8Str &aTarget, SymlinkType_T aType)
4041{
4042 RT_NOREF(aSource, aTarget, aType);
4043 ReturnComNotImplemented();
4044}
4045
4046HRESULT GuestSession::symlinkExists(const com::Utf8Str &aSymlink, BOOL *aExists)
4047
4048{
4049 RT_NOREF(aSymlink, aExists);
4050 ReturnComNotImplemented();
4051}
4052
4053HRESULT GuestSession::symlinkRead(const com::Utf8Str &aSymlink, const std::vector<SymlinkReadFlag_T> &aFlags,
4054 com::Utf8Str &aTarget)
4055{
4056 RT_NOREF(aSymlink, aFlags, aTarget);
4057 ReturnComNotImplemented();
4058}
4059
4060HRESULT GuestSession::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
4061{
4062 AutoCaller autoCaller(this);
4063 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4064
4065 /* Note: No call to i_isReadyExternal() needed here, as the session might not has been started (yet). */
4066
4067 LogFlowThisFuncEnter();
4068
4069 HRESULT hrc = S_OK;
4070
4071 /*
4072 * Note: Do not hold any locks here while waiting!
4073 */
4074 int rcGuest; GuestSessionWaitResult_T waitResult;
4075 int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &rcGuest);
4076 if (RT_SUCCESS(vrc))
4077 *aReason = waitResult;
4078 else
4079 {
4080 switch (vrc)
4081 {
4082 case VERR_GSTCTL_GUEST_ERROR:
4083 hrc = GuestSession::i_setErrorExternal(this, rcGuest);
4084 break;
4085
4086 case VERR_TIMEOUT:
4087 *aReason = GuestSessionWaitResult_Timeout;
4088 break;
4089
4090 default:
4091 {
4092 const char *pszSessionName = mData.mSession.mName.c_str();
4093 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4094 tr("Waiting for guest session \"%s\" failed: %Rrc"),
4095 pszSessionName ? pszSessionName : tr("Unnamed"), vrc);
4096 break;
4097 }
4098 }
4099 }
4100
4101 LogFlowFuncLeaveRC(vrc);
4102 return hrc;
4103}
4104
4105HRESULT GuestSession::waitForArray(const std::vector<GuestSessionWaitForFlag_T> &aWaitFor, ULONG aTimeoutMS,
4106 GuestSessionWaitResult_T *aReason)
4107{
4108 AutoCaller autoCaller(this);
4109 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4110
4111 /* Note: No call to i_isReadyExternal() needed here, as the session might not has been started (yet). */
4112
4113 LogFlowThisFuncEnter();
4114
4115 /*
4116 * Note: Do not hold any locks here while waiting!
4117 */
4118 uint32_t fWaitFor = GuestSessionWaitForFlag_None;
4119 for (size_t i = 0; i < aWaitFor.size(); i++)
4120 fWaitFor |= aWaitFor[i];
4121
4122 return WaitFor(fWaitFor, aTimeoutMS, aReason);
4123}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use