VirtualBox

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

Last change on this file since 71524 was 71414, checked in by vboxsync, 7 years ago

Build fix.

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

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