VirtualBox

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

最後變更 在這個檔案從60494是 60494,由 vboxsync 提交於 9 年 前

GuestControl/Main: Fixed broken internal tools handling for guest side (argv0).

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette