VirtualBox

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

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

Guest Control/Main: Point out that dry run mode is not supported yet as a @todo.

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

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