VirtualBox

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

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

Guest Control: Guest directory creation fixes.

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