VirtualBox

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

Last change on this file since 61572 was 60640, checked in by vboxsync, 9 years ago

Guest Control: src-client/GuestSessionImpl.cpp: Resolved a todo.

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