VirtualBox

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

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

pr7179. GuestSession, GuestProcess, GuestControl classes have been modified.

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

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