VirtualBox

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

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

Guest Control/[Directory|File]CopyFrom: Factored out the fileCopyFrom session task routines to also implement copying entire directories from the guest within Main. Work in progress.

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