VirtualBox

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

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

Guest Control/Main: Got rid of the "types" array in IGuestSession::copyFromGuest() and IGuestSession::copyToGuest(); the source type will now be resolved internally within those methods.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use