VirtualBox

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

Last change on this file since 83547 was 83533, checked in by vboxsync, 5 years ago

Guest Control/Main: Fixed file open ex flags checks in GuestSession::[i_]fileOpenEx() and removed unused GuestFileOpenInfo struct in IGuestSession::fileOpenEx(). bugref:9320

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