VirtualBox

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

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

Guest Control/Main: Validate flags for GuestSession::directoryCopyFromGuest / GuestSession::directoryCopyToGuest().

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