VirtualBox

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

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

Guest Control: Dropped the "Internal" suffix from function, as the "i_" prefix now states that it's an internal function.

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