VirtualBox

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

Last change on this file since 80836 was 80828, checked in by vboxsync, 5 years ago

Main/Guest*: Create enviornment blocks with RTENV_CREATE_F_ALLOW_EQUAL_FIRST_IN_VAR for windows guests.

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