VirtualBox

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

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

Main: a few fixes for -Wunused -Wconversion

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

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