VirtualBox

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

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

pr7179. Fixes and improvement in the classes GuestSessionTask, GuestSession, GuestProcess, ThreadTask

  • 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 58552 2015-11-03 14:55:58Z 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 = VINF_SUCCESS;
1723
1724 GuestSessionTaskInternalOpen* pTask = NULL;
1725 try
1726 {
1727 pTask = new GuestSessionTaskInternalOpen(this);
1728 if (!pTask->isOk())
1729 {
1730 delete pTask;
1731 LogFlow(("GuestSession: Could not create GuestSessionTaskInternalOpen object \n"));
1732 throw VERR_MEMOBJ_INIT_FAILED;
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(int eVRC)
1746 {
1747 vrc = eVRC;
1748 LogFlow(("GuestSession: Could not create thread for GuestSessionTaskInternalOpen task %Rrc\n", vrc));
1749 }
1750
1751 LogFlowFuncLeaveRC(vrc);
1752 return vrc;
1753}
1754
1755/* static */
1756DECLCALLBACK(int) GuestSession::i_startSessionThread(RTTHREAD Thread, void *pvUser)
1757{
1758 LogFlowFunc(("pvUser=%p\n", pvUser));
1759
1760
1761 GuestSessionTaskInternalOpen* pTask = static_cast<GuestSessionTaskInternalOpen*>(pvUser);
1762 AssertPtr(pTask);
1763
1764 const ComObjPtr<GuestSession> pSession(pTask->Session());
1765 Assert(!pSession.isNull());
1766
1767 AutoCaller autoCaller(pSession);
1768 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1769
1770 int vrc = pSession->i_startSessionInternal(NULL /* Guest rc, ignored */);
1771 /* Nothing to do here anymore. */
1772
1773 LogFlowFuncLeaveRC(vrc);
1774 return vrc;
1775}
1776
1777int GuestSession::i_pathRenameInternal(const Utf8Str &strSource, const Utf8Str &strDest,
1778 uint32_t uFlags, int *pGuestRc)
1779{
1780 AssertReturn(!(uFlags & ~PATHRENAME_FLAG_VALID_MASK), VERR_INVALID_PARAMETER);
1781
1782 LogFlowThisFunc(("strSource=%s, strDest=%s, uFlags=0x%x\n",
1783 strSource.c_str(), strDest.c_str(), uFlags));
1784
1785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1786
1787 GuestWaitEvent *pEvent = NULL;
1788 int vrc = registerWaitEvent(mData.mSession.mID, 0 /* Object ID */,
1789 &pEvent);
1790 if (RT_FAILURE(vrc))
1791 return vrc;
1792
1793 /* Prepare HGCM call. */
1794 VBOXHGCMSVCPARM paParms[8];
1795 int i = 0;
1796 paParms[i++].setUInt32(pEvent->ContextID());
1797 paParms[i++].setPointer((void*)strSource.c_str(),
1798 (ULONG)strSource.length() + 1);
1799 paParms[i++].setPointer((void*)strDest.c_str(),
1800 (ULONG)strDest.length() + 1);
1801 paParms[i++].setUInt32(uFlags);
1802
1803 alock.release(); /* Drop write lock before sending. */
1804
1805 vrc = i_sendCommand(HOST_PATH_RENAME, i, paParms);
1806 if (RT_SUCCESS(vrc))
1807 {
1808 vrc = pEvent->Wait(30 * 1000);
1809 if ( vrc == VERR_GSTCTL_GUEST_ERROR
1810 && pGuestRc)
1811 *pGuestRc = pEvent->GuestResult();
1812 }
1813
1814 unregisterWaitEvent(pEvent);
1815
1816 LogFlowFuncLeaveRC(vrc);
1817 return vrc;
1818}
1819
1820int GuestSession::i_processRemoveFromList(GuestProcess *pProcess)
1821{
1822 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
1823
1824 LogFlowThisFunc(("pProcess=%p\n", pProcess));
1825
1826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1827
1828 int rc = VERR_NOT_FOUND;
1829
1830 ULONG uPID;
1831 HRESULT hr = pProcess->COMGETTER(PID)(&uPID);
1832 ComAssertComRC(hr);
1833
1834 LogFlowFunc(("Removing process (PID=%RU32) ...\n", uPID));
1835
1836 SessionProcesses::iterator itProcs = mData.mProcesses.begin();
1837 while (itProcs != mData.mProcesses.end())
1838 {
1839 if (pProcess == itProcs->second)
1840 {
1841#ifdef DEBUG_andy
1842 ULONG cRefs = pProcess->AddRef();
1843 Assert(cRefs >= 2);
1844 LogFlowFunc(("pProcess=%p, cRefs=%RU32\n", pProcess, cRefs - 1));
1845 pProcess->Release();
1846#endif
1847 /* Make sure to consume the pointer before the one of the
1848 * iterator gets released. */
1849 ComObjPtr<GuestProcess> pProc = pProcess;
1850
1851 hr = pProc->COMGETTER(PID)(&uPID);
1852 ComAssertComRC(hr);
1853
1854 Assert(mData.mProcesses.size());
1855 Assert(mData.mNumObjects);
1856 LogFlowFunc(("Removing process ID=%RU32 (Session: %RU32), guest PID=%RU32 (now total %zu processes, %RU32 objects)\n",
1857 pProcess->getObjectID(), mData.mSession.mID, uPID, mData.mProcesses.size() - 1, mData.mNumObjects - 1));
1858
1859 rc = pProcess->i_onRemove();
1860 mData.mProcesses.erase(itProcs);
1861 mData.mNumObjects--;
1862
1863 alock.release(); /* Release lock before firing off event. */
1864
1865 fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProc,
1866 uPID, false /* Process unregistered */);
1867 pProc.setNull();
1868 break;
1869 }
1870
1871 ++itProcs;
1872 }
1873
1874 LogFlowFuncLeaveRC(rc);
1875 return rc;
1876}
1877
1878/**
1879 * Creates but does *not* start the process yet.
1880 *
1881 * See GuestProcess::startProcess() or GuestProcess::startProcessAsync() for
1882 * starting the process.
1883 *
1884 * @return IPRT status code.
1885 * @param procInfo
1886 * @param pProcess
1887 */
1888int GuestSession::i_processCreateExInternal(GuestProcessStartupInfo &procInfo, ComObjPtr<GuestProcess> &pProcess)
1889{
1890 LogFlowFunc(("mExe=%s, mFlags=%x, mTimeoutMS=%RU32\n",
1891 procInfo.mExecutable.c_str(), procInfo.mFlags, procInfo.mTimeoutMS));
1892#ifdef DEBUG
1893 if (procInfo.mArguments.size())
1894 {
1895 LogFlowFunc(("Arguments:"));
1896 ProcessArguments::const_iterator it = procInfo.mArguments.begin();
1897 while (it != procInfo.mArguments.end())
1898 {
1899 LogFlow((" %s", (*it).c_str()));
1900 ++it;
1901 }
1902 LogFlow(("\n"));
1903 }
1904#endif
1905
1906 /* Validate flags. */
1907 if (procInfo.mFlags)
1908 {
1909 if ( !(procInfo.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
1910 && !(procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
1911 && !(procInfo.mFlags & ProcessCreateFlag_Hidden)
1912 && !(procInfo.mFlags & ProcessCreateFlag_NoProfile)
1913 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
1914 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdErr))
1915 {
1916 return VERR_INVALID_PARAMETER;
1917 }
1918 }
1919
1920 if ( (procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
1921 && ( (procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
1922 || (procInfo.mFlags & ProcessCreateFlag_WaitForStdErr)
1923 )
1924 )
1925 {
1926 return VERR_INVALID_PARAMETER;
1927 }
1928
1929 /* Adjust timeout. If set to 0, we define
1930 * an infinite timeout. */
1931 if (procInfo.mTimeoutMS == 0)
1932 procInfo.mTimeoutMS = UINT32_MAX;
1933
1934 /** @tood Implement process priority + affinity. */
1935
1936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1937
1938 int rc = VERR_MAX_PROCS_REACHED;
1939 if (mData.mNumObjects >= VBOX_GUESTCTRL_MAX_OBJECTS)
1940 return rc;
1941
1942 /* Create a new (host-based) process ID and assign it. */
1943 uint32_t uNewProcessID = 0;
1944 ULONG uTries = 0;
1945
1946 for (;;)
1947 {
1948 /* Is the context ID already used? */
1949 if (!i_processExists(uNewProcessID, NULL /* pProcess */))
1950 {
1951 /* Callback with context ID was not found. This means
1952 * we can use this context ID for our new callback we want
1953 * to add below. */
1954 rc = VINF_SUCCESS;
1955 break;
1956 }
1957 uNewProcessID++;
1958 if (uNewProcessID == VBOX_GUESTCTRL_MAX_OBJECTS)
1959 uNewProcessID = 0;
1960
1961 if (++uTries == VBOX_GUESTCTRL_MAX_OBJECTS)
1962 break; /* Don't try too hard. */
1963 }
1964
1965 if (RT_FAILURE(rc))
1966 return rc;
1967
1968 /* Create the process object. */
1969 HRESULT hr = pProcess.createObject();
1970 if (FAILED(hr))
1971 return VERR_COM_UNEXPECTED;
1972
1973 rc = pProcess->init(mParent->i_getConsole() /* Console */, this /* Session */,
1974 uNewProcessID, procInfo, mData.mpBaseEnvironment);
1975 if (RT_FAILURE(rc))
1976 return rc;
1977
1978 /* Add the created process to our map. */
1979 try
1980 {
1981 mData.mProcesses[uNewProcessID] = pProcess;
1982 mData.mNumObjects++;
1983 Assert(mData.mNumObjects <= VBOX_GUESTCTRL_MAX_OBJECTS);
1984
1985 LogFlowFunc(("Added new process (Session: %RU32) with process ID=%RU32 (now total %zu processes, %RU32 objects)\n",
1986 mData.mSession.mID, uNewProcessID, mData.mProcesses.size(), mData.mNumObjects));
1987
1988 alock.release(); /* Release lock before firing off event. */
1989
1990 fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProcess,
1991 0 /* PID */, true /* Process registered */);
1992 }
1993 catch (std::bad_alloc &)
1994 {
1995 rc = VERR_NO_MEMORY;
1996 }
1997
1998 return rc;
1999}
2000
2001inline bool GuestSession::i_processExists(uint32_t uProcessID, ComObjPtr<GuestProcess> *pProcess)
2002{
2003 SessionProcesses::const_iterator it = mData.mProcesses.find(uProcessID);
2004 if (it != mData.mProcesses.end())
2005 {
2006 if (pProcess)
2007 *pProcess = it->second;
2008 return true;
2009 }
2010 return false;
2011}
2012
2013inline int GuestSession::i_processGetByPID(ULONG uPID, ComObjPtr<GuestProcess> *pProcess)
2014{
2015 AssertReturn(uPID, false);
2016 /* pProcess is optional. */
2017
2018 SessionProcesses::iterator itProcs = mData.mProcesses.begin();
2019 for (; itProcs != mData.mProcesses.end(); ++itProcs)
2020 {
2021 ComObjPtr<GuestProcess> pCurProc = itProcs->second;
2022 AutoCaller procCaller(pCurProc);
2023 if (procCaller.rc())
2024 return VERR_COM_INVALID_OBJECT_STATE;
2025
2026 ULONG uCurPID;
2027 HRESULT hr = pCurProc->COMGETTER(PID)(&uCurPID);
2028 ComAssertComRC(hr);
2029
2030 if (uCurPID == uPID)
2031 {
2032 if (pProcess)
2033 *pProcess = pCurProc;
2034 return VINF_SUCCESS;
2035 }
2036 }
2037
2038 return VERR_NOT_FOUND;
2039}
2040
2041int GuestSession::i_sendCommand(uint32_t uFunction,
2042 uint32_t uParms, PVBOXHGCMSVCPARM paParms)
2043{
2044 LogFlowThisFuncEnter();
2045
2046#ifndef VBOX_GUESTCTRL_TEST_CASE
2047 ComObjPtr<Console> pConsole = mParent->i_getConsole();
2048 Assert(!pConsole.isNull());
2049
2050 /* Forward the information to the VMM device. */
2051 VMMDev *pVMMDev = pConsole->i_getVMMDev();
2052 AssertPtr(pVMMDev);
2053
2054 LogFlowThisFunc(("uFunction=%RU32, uParms=%RU32\n", uFunction, uParms));
2055 int vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uFunction, uParms, paParms);
2056 if (RT_FAILURE(vrc))
2057 {
2058 /** @todo What to do here? */
2059 }
2060#else
2061 /* Not needed within testcases. */
2062 int vrc = VINF_SUCCESS;
2063#endif
2064 LogFlowFuncLeaveRC(vrc);
2065 return vrc;
2066}
2067
2068/* static */
2069HRESULT GuestSession::i_setErrorExternal(VirtualBoxBase *pInterface, int guestRc)
2070{
2071 AssertPtr(pInterface);
2072 AssertMsg(RT_FAILURE(guestRc), ("Guest rc does not indicate a failure when setting error\n"));
2073
2074 return pInterface->setError(VBOX_E_IPRT_ERROR, GuestSession::i_guestErrorToString(guestRc).c_str());
2075}
2076
2077/* Does not do locking; caller is responsible for that! */
2078int GuestSession::i_setSessionStatus(GuestSessionStatus_T sessionStatus, int sessionRc)
2079{
2080 LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, sessionRc=%Rrc\n",
2081 mData.mStatus, sessionStatus, sessionRc));
2082
2083 if (sessionStatus == GuestSessionStatus_Error)
2084 {
2085 AssertMsg(RT_FAILURE(sessionRc), ("Guest rc must be an error (%Rrc)\n", sessionRc));
2086 /* Do not allow overwriting an already set error. If this happens
2087 * this means we forgot some error checking/locking somewhere. */
2088 AssertMsg(RT_SUCCESS(mData.mRC), ("Guest rc already set (to %Rrc)\n", mData.mRC));
2089 }
2090 else
2091 AssertMsg(RT_SUCCESS(sessionRc), ("Guest rc must not be an error (%Rrc)\n", sessionRc));
2092
2093 if (mData.mStatus != sessionStatus)
2094 {
2095 mData.mStatus = sessionStatus;
2096 mData.mRC = sessionRc;
2097
2098 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
2099 HRESULT hr = errorInfo.createObject();
2100 ComAssertComRC(hr);
2101 int rc2 = errorInfo->initEx(VBOX_E_IPRT_ERROR, sessionRc,
2102 COM_IIDOF(IGuestSession), getComponentName(),
2103 i_guestErrorToString(sessionRc));
2104 AssertRC(rc2);
2105
2106 fireGuestSessionStateChangedEvent(mEventSource, this,
2107 mData.mSession.mID, sessionStatus, errorInfo);
2108 }
2109
2110 return VINF_SUCCESS;
2111}
2112
2113int GuestSession::i_signalWaiters(GuestSessionWaitResult_T enmWaitResult, int rc /*= VINF_SUCCESS */)
2114{
2115 /*LogFlowThisFunc(("enmWaitResult=%d, rc=%Rrc, mWaitCount=%RU32, mWaitEvent=%p\n",
2116 enmWaitResult, rc, mData.mWaitCount, mData.mWaitEvent));*/
2117
2118 /* Note: No write locking here -- already done in the caller. */
2119
2120 int vrc = VINF_SUCCESS;
2121 /*if (mData.mWaitEvent)
2122 vrc = mData.mWaitEvent->Signal(enmWaitResult, rc);*/
2123 LogFlowFuncLeaveRC(vrc);
2124 return vrc;
2125}
2126
2127int GuestSession::i_startTaskAsync(const Utf8Str &strTaskDesc,
2128 GuestSessionTask *pTask, ComObjPtr<Progress> &pProgress)
2129{
2130 LogFlowThisFunc(("strTaskDesc=%s, pTask=%p\n", strTaskDesc.c_str(), pTask));
2131
2132 AssertPtrReturn(pTask, VERR_INVALID_POINTER);
2133
2134 /* Create the progress object. */
2135 HRESULT hr = pProgress.createObject();
2136 if (FAILED(hr))
2137 return VERR_COM_UNEXPECTED;
2138
2139 hr = pProgress->init(static_cast<IGuestSession*>(this),
2140 Bstr(strTaskDesc).raw(),
2141 TRUE /* aCancelable */);
2142 if (FAILED(hr))
2143 return VERR_COM_UNEXPECTED;
2144
2145 /* Initialize our worker task. */
2146 std::auto_ptr<GuestSessionTask> task(pTask);
2147 int rc = task->RunAsync(strTaskDesc, pProgress);
2148 if (RT_FAILURE(rc))
2149 return rc;
2150
2151 /* Don't destruct on success. */
2152 task.release();
2153
2154 LogFlowFuncLeaveRC(rc);
2155 return rc;
2156}
2157
2158/**
2159 * Determines the protocol version (sets mData.mProtocolVersion).
2160 *
2161 * This is called from the init method prior to to establishing a guest
2162 * session.
2163 *
2164 * @return IPRT status code.
2165 */
2166int GuestSession::i_determineProtocolVersion(void)
2167{
2168 /*
2169 * We currently do this based on the reported guest additions version,
2170 * ASSUMING that VBoxService and VBoxDrv are at the same version.
2171 */
2172 ComObjPtr<Guest> pGuest = mParent;
2173 AssertReturn(!pGuest.isNull(), VERR_NOT_SUPPORTED);
2174 uint32_t uGaVersion = pGuest->i_getAdditionsVersion();
2175
2176 /* Everyone supports version one, if they support anything at all. */
2177 mData.mProtocolVersion = 1;
2178
2179 /* Guest control 2.0 was introduced with 4.3.0. */
2180 if (uGaVersion >= VBOX_FULL_VERSION_MAKE(4,3,0))
2181 mData.mProtocolVersion = 2; /* Guest control 2.0. */
2182
2183 LogFlowThisFunc(("uGaVersion=%u.%u.%u => mProtocolVersion=%u\n",
2184 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
2185 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
2186
2187 /*
2188 * Inform the user about outdated guest additions (VM release log).
2189 */
2190 if (mData.mProtocolVersion < 2)
2191 LogRelMax(3, (tr("Warning: Guest Additions v%u.%u.%u only supports the older guest control protocol version %u.\n"
2192 " Please upgrade GAs to the current version to get full guest control capabilities.\n"),
2193 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
2194 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
2195
2196 return VINF_SUCCESS;
2197}
2198
2199int GuestSession::i_waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, GuestSessionWaitResult_T &waitResult, int *pGuestRc)
2200{
2201 LogFlowThisFuncEnter();
2202
2203 AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
2204
2205 /*LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p, pGuestRc=%p\n",
2206 fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent, pGuestRc));*/
2207
2208 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2209
2210 /* Did some error occur before? Then skip waiting and return. */
2211 if (mData.mStatus == GuestSessionStatus_Error)
2212 {
2213 waitResult = GuestSessionWaitResult_Error;
2214 AssertMsg(RT_FAILURE(mData.mRC), ("No error rc (%Rrc) set when guest session indicated an error\n", mData.mRC));
2215 if (pGuestRc)
2216 *pGuestRc = mData.mRC; /* Return last set error. */
2217 return VERR_GSTCTL_GUEST_ERROR;
2218 }
2219
2220 /* Guest Additions < 4.3 don't support session handling, skip. */
2221 if (mData.mProtocolVersion < 2)
2222 {
2223 waitResult = GuestSessionWaitResult_WaitFlagNotSupported;
2224
2225 LogFlowThisFunc(("Installed Guest Additions don't support waiting for dedicated sessions, skipping\n"));
2226 return VINF_SUCCESS;
2227 }
2228
2229 waitResult = GuestSessionWaitResult_None;
2230 if (fWaitFlags & GuestSessionWaitForFlag_Terminate)
2231 {
2232 switch (mData.mStatus)
2233 {
2234 case GuestSessionStatus_Terminated:
2235 case GuestSessionStatus_Down:
2236 waitResult = GuestSessionWaitResult_Terminate;
2237 break;
2238
2239 case GuestSessionStatus_TimedOutKilled:
2240 case GuestSessionStatus_TimedOutAbnormally:
2241 waitResult = GuestSessionWaitResult_Timeout;
2242 break;
2243
2244 case GuestSessionStatus_Error:
2245 /* Handled above. */
2246 break;
2247
2248 case GuestSessionStatus_Started:
2249 waitResult = GuestSessionWaitResult_Start;
2250 break;
2251
2252 case GuestSessionStatus_Undefined:
2253 case GuestSessionStatus_Starting:
2254 /* Do the waiting below. */
2255 break;
2256
2257 default:
2258 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
2259 return VERR_NOT_IMPLEMENTED;
2260 }
2261 }
2262 else if (fWaitFlags & GuestSessionWaitForFlag_Start)
2263 {
2264 switch (mData.mStatus)
2265 {
2266 case GuestSessionStatus_Started:
2267 case GuestSessionStatus_Terminating:
2268 case GuestSessionStatus_Terminated:
2269 case GuestSessionStatus_Down:
2270 waitResult = GuestSessionWaitResult_Start;
2271 break;
2272
2273 case GuestSessionStatus_Error:
2274 waitResult = GuestSessionWaitResult_Error;
2275 break;
2276
2277 case GuestSessionStatus_TimedOutKilled:
2278 case GuestSessionStatus_TimedOutAbnormally:
2279 waitResult = GuestSessionWaitResult_Timeout;
2280 break;
2281
2282 case GuestSessionStatus_Undefined:
2283 case GuestSessionStatus_Starting:
2284 /* Do the waiting below. */
2285 break;
2286
2287 default:
2288 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
2289 return VERR_NOT_IMPLEMENTED;
2290 }
2291 }
2292
2293 LogFlowThisFunc(("sessionStatus=%RU32, sessionRc=%Rrc, waitResult=%RU32\n",
2294 mData.mStatus, mData.mRC, waitResult));
2295
2296 /* No waiting needed? Return immediately using the last set error. */
2297 if (waitResult != GuestSessionWaitResult_None)
2298 {
2299 if (pGuestRc)
2300 *pGuestRc = mData.mRC; /* Return last set error (if any). */
2301 return RT_SUCCESS(mData.mRC) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
2302 }
2303
2304 int vrc;
2305
2306 GuestWaitEvent *pEvent = NULL;
2307 GuestEventTypes eventTypes;
2308 try
2309 {
2310 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
2311
2312 vrc = registerWaitEvent(mData.mSession.mID, 0 /* Object ID */,
2313 eventTypes, &pEvent);
2314 }
2315 catch (std::bad_alloc)
2316 {
2317 vrc = VERR_NO_MEMORY;
2318 }
2319
2320 if (RT_FAILURE(vrc))
2321 return vrc;
2322
2323 alock.release(); /* Release lock before waiting. */
2324
2325 GuestSessionStatus_T sessionStatus;
2326 vrc = i_waitForStatusChange(pEvent, fWaitFlags,
2327 uTimeoutMS, &sessionStatus, pGuestRc);
2328 if (RT_SUCCESS(vrc))
2329 {
2330 switch (sessionStatus)
2331 {
2332 case GuestSessionStatus_Started:
2333 waitResult = GuestSessionWaitResult_Start;
2334 break;
2335
2336 case GuestSessionStatus_Terminated:
2337 waitResult = GuestSessionWaitResult_Terminate;
2338 break;
2339
2340 case GuestSessionStatus_TimedOutKilled:
2341 case GuestSessionStatus_TimedOutAbnormally:
2342 waitResult = GuestSessionWaitResult_Timeout;
2343 break;
2344
2345 case GuestSessionStatus_Down:
2346 waitResult = GuestSessionWaitResult_Terminate;
2347 break;
2348
2349 case GuestSessionStatus_Error:
2350 waitResult = GuestSessionWaitResult_Error;
2351 break;
2352
2353 default:
2354 waitResult = GuestSessionWaitResult_Status;
2355 break;
2356 }
2357 }
2358
2359 unregisterWaitEvent(pEvent);
2360
2361 LogFlowFuncLeaveRC(vrc);
2362 return vrc;
2363}
2364
2365int GuestSession::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t fWaitFlags, uint32_t uTimeoutMS,
2366 GuestSessionStatus_T *pSessionStatus, int *pGuestRc)
2367{
2368 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
2369
2370 VBoxEventType_T evtType;
2371 ComPtr<IEvent> pIEvent;
2372 int vrc = waitForEvent(pEvent, uTimeoutMS,
2373 &evtType, pIEvent.asOutParam());
2374 if (RT_SUCCESS(vrc))
2375 {
2376 Assert(evtType == VBoxEventType_OnGuestSessionStateChanged);
2377
2378 ComPtr<IGuestSessionStateChangedEvent> pChangedEvent = pIEvent;
2379 Assert(!pChangedEvent.isNull());
2380
2381 GuestSessionStatus_T sessionStatus;
2382 pChangedEvent->COMGETTER(Status)(&sessionStatus);
2383 if (pSessionStatus)
2384 *pSessionStatus = sessionStatus;
2385
2386 ComPtr<IVirtualBoxErrorInfo> errorInfo;
2387 HRESULT hr = pChangedEvent->COMGETTER(Error)(errorInfo.asOutParam());
2388 ComAssertComRC(hr);
2389
2390 LONG lGuestRc;
2391 hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
2392 ComAssertComRC(hr);
2393 if (RT_FAILURE((int)lGuestRc))
2394 vrc = VERR_GSTCTL_GUEST_ERROR;
2395 if (pGuestRc)
2396 *pGuestRc = (int)lGuestRc;
2397
2398 LogFlowThisFunc(("Status changed event for session ID=%RU32, new status is: %RU32 (%Rrc)\n",
2399 mData.mSession.mID, sessionStatus,
2400 RT_SUCCESS((int)lGuestRc) ? VINF_SUCCESS : (int)lGuestRc));
2401 }
2402
2403 LogFlowFuncLeaveRC(vrc);
2404 return vrc;
2405}
2406
2407// implementation of public methods
2408/////////////////////////////////////////////////////////////////////////////
2409
2410HRESULT GuestSession::close()
2411{
2412 LogFlowThisFuncEnter();
2413
2414 AutoCaller autoCaller(this);
2415 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2416
2417 /* Close session on guest. */
2418 int guestRc = VINF_SUCCESS;
2419 int rc = i_closeSession(0 /* Flags */, 30 * 1000 /* Timeout */,
2420 &guestRc);
2421 /* On failure don't return here, instead do all the cleanup
2422 * work first and then return an error. */
2423
2424 /* Remove ourselves from the session list. */
2425 int rc2 = mParent->i_sessionRemove(this);
2426 if (rc2 == VERR_NOT_FOUND) /* Not finding the session anymore isn't critical. */
2427 rc2 = VINF_SUCCESS;
2428
2429 if (RT_SUCCESS(rc))
2430 rc = rc2;
2431
2432 LogFlowThisFunc(("Returning rc=%Rrc, guestRc=%Rrc\n",
2433 rc, guestRc));
2434 if (RT_FAILURE(rc))
2435 {
2436 if (rc == VERR_GSTCTL_GUEST_ERROR)
2437 return GuestSession::i_setErrorExternal(this, guestRc);
2438
2439 return setError(VBOX_E_IPRT_ERROR,
2440 tr("Closing guest session failed with %Rrc"), rc);
2441 }
2442
2443 return S_OK;
2444}
2445
2446HRESULT GuestSession::fileCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2447 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
2448{
2449 ReturnComNotImplemented();
2450}
2451
2452HRESULT GuestSession::fileCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDest,
2453 const std::vector<FileCopyFlag_T> &aFlags,
2454 ComPtr<IProgress> &aProgress)
2455{
2456 LogFlowThisFuncEnter();
2457
2458 if (RT_UNLIKELY((aSource.c_str()) == NULL || *(aSource.c_str()) == '\0'))
2459 return setError(E_INVALIDARG, tr("No source specified"));
2460 if (RT_UNLIKELY((aDest.c_str()) == NULL || *(aDest.c_str()) == '\0'))
2461 return setError(E_INVALIDARG, tr("No destination specified"));
2462
2463 uint32_t fFlags = FileCopyFlag_None;
2464 if (aFlags.size())
2465 {
2466 for (size_t i = 0; i < aFlags.size(); i++)
2467 fFlags |= aFlags[i];
2468 }
2469/** @todo r=bird: fend off flags we don't implement here! */
2470
2471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2472
2473 HRESULT hr = S_OK;
2474
2475 try
2476 {
2477 SessionTaskCopyFrom *pTask = NULL;
2478 ComObjPtr<Progress> pProgress;
2479 try
2480 {
2481 pTask = new SessionTaskCopyFrom(this /* GuestSession */, aSource, aDest, fFlags);
2482 }
2483 catch(...)
2484 {
2485 hr = setError(VBOX_E_IPRT_ERROR, tr("Failed to create SessionTaskCopyFrom object "));
2486 throw;
2487 }
2488
2489
2490 hr = pTask->Init(Utf8StrFmt(tr("Copying \"%s\" from guest to \"%s\" on the host"), aSource.c_str(), aDest.c_str()));
2491 if (FAILED(hr))
2492 {
2493 delete pTask;
2494 hr = setError(VBOX_E_IPRT_ERROR,
2495 tr("Creating progress object for SessionTaskCopyFrom object failed"));
2496 throw hr;
2497 }
2498
2499 hr = pTask->createThread(NULL, RTTHREADTYPE_MAIN_HEAVY_WORKER);
2500
2501 if (SUCCEEDED(hr))
2502 {
2503 /* Return progress to the caller. */
2504 pProgress = pTask->GetProgressObject();
2505 hr = pProgress.queryInterfaceTo(aProgress.asOutParam());
2506 }
2507 else
2508 hr = setError(VBOX_E_IPRT_ERROR,
2509 tr("Starting thread for copying file \"%s\" from guest to \"%s\" on the host failed "),
2510 aSource.c_str(), aDest.c_str());
2511
2512 }
2513 catch(std::bad_alloc &)
2514 {
2515 hr = E_OUTOFMEMORY;
2516 }
2517 catch(HRESULT eHR)
2518 {
2519 hr = eHR;
2520 LogFlowThisFunc(("Exception was caught in the function \n"));
2521 }
2522
2523 return hr;
2524}
2525
2526HRESULT GuestSession::fileCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDest,
2527 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
2528{
2529 LogFlowThisFuncEnter();
2530
2531 if (RT_UNLIKELY((aSource.c_str()) == NULL || *(aSource.c_str()) == '\0'))
2532 return setError(E_INVALIDARG, tr("No source specified"));
2533 if (RT_UNLIKELY((aDest.c_str()) == NULL || *(aDest.c_str()) == '\0'))
2534 return setError(E_INVALIDARG, tr("No destination specified"));
2535
2536 uint32_t fFlags = FileCopyFlag_None;
2537 if (aFlags.size())
2538 {
2539 for (size_t i = 0; i < aFlags.size(); i++)
2540 fFlags |= aFlags[i];
2541 }
2542/** @todo r=bird: fend off flags we don't implement here! */
2543
2544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2545
2546 HRESULT hr = S_OK;
2547
2548 try
2549 {
2550 SessionTaskCopyTo *pTask = NULL;
2551 ComObjPtr<Progress> pProgress;
2552 try
2553 {
2554 pTask = new SessionTaskCopyTo(this /* GuestSession */, aSource, aDest, fFlags);
2555 }
2556 catch(...)
2557 {
2558 hr = setError(VBOX_E_IPRT_ERROR, tr("Failed to create SessionTaskCopyTo object "));
2559 throw;
2560 }
2561
2562
2563 hr = pTask->Init(Utf8StrFmt(tr("Copying \"%s\" from host to \"%s\" on the guest"), aSource.c_str(), aDest.c_str()));
2564 if (FAILED(hr))
2565 {
2566 delete pTask;
2567 hr = setError(VBOX_E_IPRT_ERROR,
2568 tr("Creating progress object for SessionTaskCopyTo object failed"));
2569 throw hr;
2570 }
2571
2572 hr = pTask->createThread(NULL, RTTHREADTYPE_MAIN_HEAVY_WORKER);
2573
2574 if (SUCCEEDED(hr))
2575 {
2576 /* Return progress to the caller. */
2577 pProgress = pTask->GetProgressObject();
2578 hr = pProgress.queryInterfaceTo(aProgress.asOutParam());
2579 }
2580 else
2581 hr = setError(VBOX_E_IPRT_ERROR,
2582 tr("Starting thread for copying file \"%s\" from guest to \"%s\" on the host failed "),
2583 aSource.c_str(), aDest.c_str());
2584 }
2585 catch(std::bad_alloc &)
2586 {
2587 hr = E_OUTOFMEMORY;
2588 }
2589 catch(HRESULT eHR)
2590 {
2591 hr = eHR;
2592 LogFlowThisFunc(("Exception was caught in the function \n"));
2593 }
2594
2595 return hr;
2596}
2597
2598HRESULT GuestSession::directoryCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2599 const std::vector<DirectoryCopyFlags_T> &aFlags, ComPtr<IProgress> &aProgress)
2600{
2601 ReturnComNotImplemented();
2602}
2603
2604HRESULT GuestSession::directoryCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2605 const std::vector<DirectoryCopyFlags_T> &aFlags, ComPtr<IProgress> &aProgress)
2606{
2607 ReturnComNotImplemented();
2608}
2609
2610HRESULT GuestSession::directoryCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2611 const std::vector<DirectoryCopyFlags_T> &aFlags, ComPtr<IProgress> &aProgress)
2612{
2613 ReturnComNotImplemented();
2614}
2615
2616HRESULT GuestSession::directoryCreate(const com::Utf8Str &aPath, ULONG aMode,
2617 const std::vector<DirectoryCreateFlag_T> &aFlags)
2618{
2619 LogFlowThisFuncEnter();
2620
2621 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
2622 return setError(E_INVALIDARG, tr("No directory to create specified"));
2623
2624 uint32_t fFlags = DirectoryCreateFlag_None;
2625 if (aFlags.size())
2626 {
2627 for (size_t i = 0; i < aFlags.size(); i++)
2628 fFlags |= aFlags[i];
2629
2630 if (fFlags)
2631 if (!(fFlags & DirectoryCreateFlag_Parents))
2632 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), fFlags);
2633 }
2634
2635 HRESULT hr = S_OK;
2636
2637 ComObjPtr <GuestDirectory> pDirectory; int guestRc;
2638 int rc = i_directoryCreateInternal(aPath, (uint32_t)aMode, fFlags, &guestRc);
2639 if (RT_FAILURE(rc))
2640 {
2641 switch (rc)
2642 {
2643 case VERR_GSTCTL_GUEST_ERROR:
2644 /** @todo Handle VERR_NOT_EQUAL (meaning process exit code <> 0). */
2645 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Could not create directory"));
2646 break;
2647
2648 case VERR_INVALID_PARAMETER:
2649 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Invalid parameters given"));
2650 break;
2651
2652 case VERR_BROKEN_PIPE:
2653 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Unexpectedly aborted"));
2654 break;
2655
2656 default:
2657 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: %Rrc"), rc);
2658 break;
2659 }
2660 }
2661
2662 return hr;
2663}
2664
2665HRESULT GuestSession::directoryCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath,
2666 BOOL aSecure, com::Utf8Str &aDirectory)
2667{
2668 LogFlowThisFuncEnter();
2669
2670 if (RT_UNLIKELY((aTemplateName.c_str()) == NULL || *(aTemplateName.c_str()) == '\0'))
2671 return setError(E_INVALIDARG, tr("No template specified"));
2672 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
2673 return setError(E_INVALIDARG, tr("No directory name specified"));
2674
2675 HRESULT hr = S_OK;
2676
2677 int guestRc;
2678 int rc = i_objectCreateTempInternal(aTemplateName,
2679 aPath,
2680 true /* Directory */, aDirectory, &guestRc);
2681 if (!RT_SUCCESS(rc))
2682 {
2683 switch (rc)
2684 {
2685 case VERR_GSTCTL_GUEST_ERROR:
2686 hr = GuestProcess::i_setErrorExternal(this, guestRc);
2687 break;
2688
2689 default:
2690 hr = setError(VBOX_E_IPRT_ERROR, tr("Temporary directory creation \"%s\" with template \"%s\" failed: %Rrc"),
2691 aPath.c_str(), aTemplateName.c_str(), rc);
2692 break;
2693 }
2694 }
2695
2696 return hr;
2697}
2698
2699HRESULT GuestSession::directoryExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
2700{
2701 LogFlowThisFuncEnter();
2702
2703 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
2704 return setError(E_INVALIDARG, tr("No directory to check existence for specified"));
2705
2706 HRESULT hr = S_OK;
2707
2708 GuestFsObjData objData; int guestRc;
2709 int rc = i_directoryQueryInfoInternal(aPath, aFollowSymlinks != FALSE, objData, &guestRc);
2710 if (RT_SUCCESS(rc))
2711 *aExists = objData.mType == FsObjType_Directory;
2712 else
2713 {
2714 /** @todo r=bird: Looks like this code raises errors if the directory doesn't
2715 * exist... That's of course not right. */
2716 switch (rc)
2717 {
2718 case VERR_GSTCTL_GUEST_ERROR:
2719 hr = GuestProcess::i_setErrorExternal(this, guestRc);
2720 break;
2721
2722 default:
2723 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying directory existence \"%s\" failed: %Rrc"),
2724 aPath.c_str(), rc);
2725 break;
2726 }
2727 }
2728
2729 return hr;
2730}
2731
2732HRESULT GuestSession::directoryOpen(const com::Utf8Str &aPath, const com::Utf8Str &aFilter,
2733 const std::vector<DirectoryOpenFlag_T> &aFlags, ComPtr<IGuestDirectory> &aDirectory)
2734{
2735 LogFlowThisFuncEnter();
2736
2737 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
2738 return setError(E_INVALIDARG, tr("No directory to open specified"));
2739 if (RT_UNLIKELY((aFilter.c_str()) != NULL && *(aFilter.c_str()) != '\0'))
2740 return setError(E_INVALIDARG, tr("Directory filters are not implemented yet"));
2741
2742 uint32_t fFlags = DirectoryOpenFlag_None;
2743 if (aFlags.size())
2744 {
2745 for (size_t i = 0; i < aFlags.size(); i++)
2746 fFlags |= aFlags[i];
2747
2748 if (fFlags)
2749 return setError(E_INVALIDARG, tr("Open flags (%#x) not implemented yet"), fFlags);
2750 }
2751
2752 HRESULT hr = S_OK;
2753
2754 GuestDirectoryOpenInfo openInfo;
2755 openInfo.mPath = aPath;
2756 openInfo.mFilter = aFilter;
2757 openInfo.mFlags = fFlags;
2758
2759 ComObjPtr <GuestDirectory> pDirectory; int guestRc;
2760 int rc = i_directoryOpenInternal(openInfo, pDirectory, &guestRc);
2761 if (RT_SUCCESS(rc))
2762 {
2763 /* Return directory object to the caller. */
2764 hr = pDirectory.queryInterfaceTo(aDirectory.asOutParam());
2765 }
2766 else
2767 {
2768 switch (rc)
2769 {
2770 case VERR_INVALID_PARAMETER:
2771 hr = setError(VBOX_E_IPRT_ERROR, tr("Opening directory \"%s\" failed; invalid parameters given"),
2772 aPath.c_str());
2773 break;
2774
2775 case VERR_GSTCTL_GUEST_ERROR:
2776 hr = GuestDirectory::i_setErrorExternal(this, guestRc);
2777 break;
2778
2779 default:
2780 hr = setError(VBOX_E_IPRT_ERROR, tr("Opening directory \"%s\" failed: %Rrc"),
2781 aPath.c_str(),rc);
2782 break;
2783 }
2784 }
2785
2786 return hr;
2787}
2788
2789HRESULT GuestSession::directoryRemove(const com::Utf8Str &aPath)
2790{
2791 LogFlowThisFuncEnter();
2792
2793 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
2794 return setError(E_INVALIDARG, tr("No directory to remove specified"));
2795
2796 HRESULT hr = i_isReadyExternal();
2797 if (FAILED(hr))
2798 return hr;
2799
2800 /* No flags; only remove the directory when empty. */
2801 uint32_t uFlags = 0;
2802
2803 int guestRc;
2804 int vrc = i_directoryRemoveInternal(aPath, uFlags, &guestRc);
2805 if (RT_FAILURE(vrc))
2806 {
2807 switch (vrc)
2808 {
2809 case VERR_NOT_SUPPORTED:
2810 hr = setError(VBOX_E_IPRT_ERROR,
2811 tr("Handling removing guest directories not supported by installed Guest Additions"));
2812 break;
2813
2814 case VERR_GSTCTL_GUEST_ERROR:
2815 hr = GuestDirectory::i_setErrorExternal(this, guestRc);
2816 break;
2817
2818 default:
2819 hr = setError(VBOX_E_IPRT_ERROR, tr("Removing guest directory \"%s\" failed: %Rrc"),
2820 aPath.c_str(), vrc);
2821 break;
2822 }
2823 }
2824
2825 return hr;
2826}
2827
2828HRESULT GuestSession::directoryRemoveRecursive(const com::Utf8Str &aPath, const std::vector<DirectoryRemoveRecFlag_T> &aFlags,
2829 ComPtr<IProgress> &aProgress)
2830{
2831 LogFlowThisFuncEnter();
2832
2833 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
2834 return setError(E_INVALIDARG, tr("No directory to remove recursively specified"));
2835
2836/** @todo r=bird: Must check that the flags matches the hardcoded behavior
2837 * further down!! */
2838
2839 HRESULT hr = i_isReadyExternal();
2840 if (FAILED(hr))
2841 return hr;
2842
2843 ComObjPtr<Progress> pProgress;
2844 hr = pProgress.createObject();
2845 if (SUCCEEDED(hr))
2846 hr = pProgress->init(static_cast<IGuestSession *>(this),
2847 Bstr(tr("Removing guest directory")).raw(),
2848 TRUE /*aCancelable*/);
2849 if (FAILED(hr))
2850 return hr;
2851
2852 /* Note: At the moment we don't supply progress information while
2853 * deleting a guest directory recursively. So just complete
2854 * the progress object right now. */
2855 /** @todo Implement progress reporting on guest directory deletion! */
2856 hr = pProgress->i_notifyComplete(S_OK);
2857 if (FAILED(hr))
2858 return hr;
2859
2860 /* Remove the directory + all its contents. */
2861 uint32_t uFlags = DIRREMOVE_FLAG_RECURSIVE
2862 | DIRREMOVE_FLAG_CONTENT_AND_DIR;
2863 int guestRc;
2864 int vrc = i_directoryRemoveInternal(aPath, uFlags, &guestRc);
2865 if (RT_FAILURE(vrc))
2866 {
2867 switch (vrc)
2868 {
2869 case VERR_NOT_SUPPORTED:
2870 hr = setError(VBOX_E_IPRT_ERROR,
2871 tr("Handling removing guest directories recursively not supported by installed Guest Additions"));
2872 break;
2873
2874 case VERR_GSTCTL_GUEST_ERROR:
2875 hr = GuestFile::i_setErrorExternal(this, guestRc);
2876 break;
2877
2878 default:
2879 hr = setError(VBOX_E_IPRT_ERROR, tr("Recursively removing guest directory \"%s\" failed: %Rrc"),
2880 aPath.c_str(), vrc);
2881 break;
2882 }
2883 }
2884 else
2885 {
2886 pProgress.queryInterfaceTo(aProgress.asOutParam());
2887 }
2888
2889 return hr;
2890}
2891
2892HRESULT GuestSession::environmentScheduleSet(const com::Utf8Str &aName, const com::Utf8Str &aValue)
2893{
2894 LogFlowThisFuncEnter();
2895
2896 HRESULT hrc;
2897 if (RT_LIKELY(aName.isNotEmpty()))
2898 {
2899 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
2900 {
2901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2902 int vrc = mData.mEnvironmentChanges.setVariable(aName, aValue);
2903 if (RT_SUCCESS(vrc))
2904 hrc = S_OK;
2905 else
2906 hrc = setErrorVrc(vrc);
2907 }
2908 else
2909 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
2910 }
2911 else
2912 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
2913
2914 LogFlowThisFuncLeave();
2915 return hrc;
2916}
2917
2918HRESULT GuestSession::environmentScheduleUnset(const com::Utf8Str &aName)
2919{
2920 LogFlowThisFuncEnter();
2921 HRESULT hrc;
2922 if (RT_LIKELY(aName.isNotEmpty()))
2923 {
2924 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
2925 {
2926 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2927 int vrc = mData.mEnvironmentChanges.unsetVariable(aName);
2928 if (RT_SUCCESS(vrc))
2929 hrc = S_OK;
2930 else
2931 hrc = setErrorVrc(vrc);
2932 }
2933 else
2934 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
2935 }
2936 else
2937 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
2938
2939 LogFlowThisFuncLeave();
2940 return hrc;
2941}
2942
2943HRESULT GuestSession::environmentGetBaseVariable(const com::Utf8Str &aName, com::Utf8Str &aValue)
2944{
2945 LogFlowThisFuncEnter();
2946 HRESULT hrc;
2947 if (RT_LIKELY(aName.isNotEmpty()))
2948 {
2949 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
2950 {
2951 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2952 if (mData.mpBaseEnvironment)
2953 {
2954 int vrc = mData.mpBaseEnvironment->getVariable(aName, &aValue);
2955 if (RT_SUCCESS(vrc))
2956 hrc = S_OK;
2957 else
2958 hrc = setErrorVrc(vrc);
2959 }
2960 else if (mData.mProtocolVersion < 99999)
2961 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
2962 else
2963 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
2964 }
2965 else
2966 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
2967 }
2968 else
2969 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
2970
2971 LogFlowThisFuncLeave();
2972 return hrc;
2973}
2974
2975HRESULT GuestSession::environmentDoesBaseVariableExist(const com::Utf8Str &aName, BOOL *aExists)
2976{
2977 LogFlowThisFuncEnter();
2978 *aExists = FALSE;
2979 HRESULT hrc;
2980 if (RT_LIKELY(aName.isNotEmpty()))
2981 {
2982 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
2983 {
2984 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2985 if (mData.mpBaseEnvironment)
2986 {
2987 hrc = S_OK;
2988 *aExists = mData.mpBaseEnvironment->doesVariableExist(aName);
2989 }
2990 else if (mData.mProtocolVersion < 99999)
2991 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
2992 else
2993 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
2994 }
2995 else
2996 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
2997 }
2998 else
2999 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3000
3001 LogFlowThisFuncLeave();
3002 return hrc;
3003}
3004
3005HRESULT GuestSession::fileCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath, BOOL aSecure,
3006 ComPtr<IGuestFile> &aFile)
3007{
3008 ReturnComNotImplemented();
3009}
3010
3011HRESULT GuestSession::fileExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3012{
3013 LogFlowThisFuncEnter();
3014
3015/** @todo r=bird: Treat empty file with a FALSE return. */
3016 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3017 return setError(E_INVALIDARG, tr("No file to check existence for specified"));
3018
3019 GuestFsObjData objData; int guestRc;
3020 int vrc = i_fileQueryInfoInternal(aPath, aFollowSymlinks != FALSE, objData, &guestRc);
3021 if (RT_SUCCESS(vrc))
3022 {
3023 *aExists = TRUE;
3024 return S_OK;
3025 }
3026
3027 HRESULT hr = S_OK;
3028
3029 switch (vrc)
3030 {
3031 case VERR_GSTCTL_GUEST_ERROR:
3032 hr = GuestProcess::i_setErrorExternal(this, guestRc);
3033 break;
3034
3035/** @todo r=bird: what about VERR_PATH_NOT_FOUND and VERR_FILE_NOT_FOUND?
3036 * Where does that get converted to *aExists = FALSE? */
3037 case VERR_NOT_A_FILE:
3038 *aExists = FALSE;
3039 break;
3040
3041 default:
3042 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying file information for \"%s\" failed: %Rrc"),
3043 aPath.c_str(), vrc);
3044 break;
3045 }
3046
3047 return hr;
3048}
3049
3050HRESULT GuestSession::fileOpen(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3051 ULONG aCreationMode, ComPtr<IGuestFile> &aFile)
3052{
3053 LogFlowThisFuncEnter();
3054 const std::vector<FileOpenExFlags_T> EmptyFlags;
3055 return fileOpenEx(aPath, aAccessMode, aOpenAction, FileSharingMode_All, aCreationMode, EmptyFlags, aFile);
3056}
3057
3058HRESULT GuestSession::fileOpenEx(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3059 FileSharingMode_T aSharingMode, ULONG aCreationMode,
3060 const std::vector<FileOpenExFlags_T> &aFlags, ComPtr<IGuestFile> &aFile)
3061{
3062 LogFlowThisFuncEnter();
3063
3064 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3065 return setError(E_INVALIDARG, tr("No file to open specified"));
3066
3067 HRESULT hr = i_isReadyExternal();
3068 if (FAILED(hr))
3069 return hr;
3070
3071 GuestFileOpenInfo openInfo;
3072 openInfo.mFileName = aPath;
3073 openInfo.mCreationMode = aCreationMode;
3074
3075 /* convert + validate aAccessMode to the old format. */
3076 openInfo.mAccessMode = aAccessMode;
3077 switch (aAccessMode)
3078 {
3079 case (FileAccessMode_T)FileAccessMode_ReadOnly: openInfo.mpszAccessMode = "r"; break;
3080 case (FileAccessMode_T)FileAccessMode_WriteOnly: openInfo.mpszAccessMode = "w"; break;
3081 case (FileAccessMode_T)FileAccessMode_ReadWrite: openInfo.mpszAccessMode = "r+"; break;
3082 case (FileAccessMode_T)FileAccessMode_AppendOnly:
3083 /* fall thru */
3084 case (FileAccessMode_T)FileAccessMode_AppendRead:
3085 return setError(E_NOTIMPL, tr("Append access modes are not yet implemented"));
3086 default:
3087 return setError(E_INVALIDARG, tr("Unknown FileAccessMode value %u (%#x)"), aAccessMode, aAccessMode);
3088 }
3089
3090 /* convert + validate aOpenAction to the old format. */
3091 openInfo.mOpenAction = aOpenAction;
3092 switch (aOpenAction)
3093 {
3094 case (FileOpenAction_T)FileOpenAction_OpenExisting: openInfo.mpszOpenAction = "oe"; break;
3095 case (FileOpenAction_T)FileOpenAction_OpenOrCreate: openInfo.mpszOpenAction = "oc"; break;
3096 case (FileOpenAction_T)FileOpenAction_CreateNew: openInfo.mpszOpenAction = "ce"; break;
3097 case (FileOpenAction_T)FileOpenAction_CreateOrReplace: openInfo.mpszOpenAction = "ca"; break;
3098 case (FileOpenAction_T)FileOpenAction_OpenExistingTruncated: openInfo.mpszOpenAction = "ot"; break;
3099 case (FileOpenAction_T)FileOpenAction_AppendOrCreate:
3100 openInfo.mpszOpenAction = "oa"; /** @todo get rid of this one and implement AppendOnly/AppendRead. */
3101 break;
3102 default:
3103 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3104 }
3105
3106 /* validate aSharingMode */
3107 openInfo.mSharingMode = aSharingMode;
3108 switch (aSharingMode)
3109 {
3110 case (FileSharingMode_T)FileSharingMode_All: /* OK */ break;
3111 case (FileSharingMode_T)FileSharingMode_Read:
3112 case (FileSharingMode_T)FileSharingMode_Write:
3113 case (FileSharingMode_T)FileSharingMode_ReadWrite:
3114 case (FileSharingMode_T)FileSharingMode_Delete:
3115 case (FileSharingMode_T)FileSharingMode_ReadDelete:
3116 case (FileSharingMode_T)FileSharingMode_WriteDelete:
3117 return setError(E_NOTIMPL, tr("Only FileSharingMode_All is currently implemented"));
3118
3119 default:
3120 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3121 }
3122
3123 /* Combine and validate flags. */
3124 uint32_t fOpenEx = 0;
3125 for (size_t i = 0; i < aFlags.size(); i++)
3126 fOpenEx = aFlags[i];
3127 if (fOpenEx)
3128 return setError(E_INVALIDARG, tr("Unsupported FileOpenExFlags values in aFlags (%#x)"), fOpenEx);
3129 openInfo.mfOpenEx = fOpenEx;
3130
3131 ComObjPtr <GuestFile> pFile;
3132 int guestRc;
3133 int vrc = i_fileOpenInternal(openInfo, pFile, &guestRc);
3134 if (RT_SUCCESS(vrc))
3135 /* Return directory object to the caller. */
3136 hr = pFile.queryInterfaceTo(aFile.asOutParam());
3137 else
3138 {
3139 switch (vrc)
3140 {
3141 case VERR_NOT_SUPPORTED:
3142 hr = setError(VBOX_E_IPRT_ERROR,
3143 tr("Handling guest files not supported by installed Guest Additions"));
3144 break;
3145
3146 case VERR_GSTCTL_GUEST_ERROR:
3147 hr = GuestFile::i_setErrorExternal(this, guestRc);
3148 break;
3149
3150 default:
3151 hr = setError(VBOX_E_IPRT_ERROR, tr("Opening guest file \"%s\" failed: %Rrc"),
3152 aPath.c_str(), vrc);
3153 break;
3154 }
3155 }
3156
3157 return hr;
3158}
3159
3160HRESULT GuestSession::fileQuerySize(const com::Utf8Str &aPath, BOOL aFollowSymlinks, LONG64 *aSize)
3161{
3162 LogFlowThisFuncEnter();
3163
3164 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3165 return setError(E_INVALIDARG, tr("No file to query size for specified"));
3166
3167 HRESULT hr = S_OK;
3168
3169 int64_t llSize; int guestRc;
3170 int vrc = i_fileQuerySizeInternal(aPath, aFollowSymlinks != FALSE, &llSize, &guestRc);
3171 if (RT_SUCCESS(vrc))
3172 *aSize = llSize;
3173 else
3174 {
3175 switch (vrc)
3176 {
3177 case VERR_GSTCTL_GUEST_ERROR:
3178 hr = GuestProcess::i_setErrorExternal(this, guestRc);
3179 break;
3180
3181 default:
3182 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying file size failed: %Rrc"), vrc);
3183 break;
3184 }
3185 }
3186
3187 return hr;
3188}
3189
3190HRESULT GuestSession::fsObjExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3191{
3192 LogFlowThisFuncEnter();
3193
3194 HRESULT hrc = S_OK;
3195 *aExists = false;
3196 if (RT_LIKELY(aPath.isNotEmpty()))
3197 {
3198 GuestFsObjData objData;
3199 int rcGuest;
3200 int vrc = i_fsQueryInfoInternal(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3201 if (RT_SUCCESS(vrc))
3202 *aExists = TRUE;
3203 else if ( vrc == VERR_NOT_A_FILE
3204 || vrc == VERR_PATH_NOT_FOUND
3205 || vrc == VERR_FILE_NOT_FOUND
3206 || vrc == VERR_INVALID_NAME)
3207 hrc = S_OK; /* Ignore these vrc values. */
3208 else if (vrc == VERR_GSTCTL_GUEST_ERROR) /** @todo What _is_ rcGuest, really? Stuff like VERR_NOT_A_FILE too?? */
3209 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3210 else
3211 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3212 }
3213 /* else: If the file name is empty, there is no way it can exists. So, don't
3214 be a tedious and return E_INVALIDARG, simply return FALSE. */
3215 LogFlowThisFuncLeave();
3216 return hrc;
3217}
3218
3219HRESULT GuestSession::fsObjQueryInfo(const com::Utf8Str &aPath, BOOL aFollowSymlinks, ComPtr<IGuestFsObjInfo> &aInfo)
3220{
3221 LogFlowThisFuncEnter();
3222
3223 HRESULT hrc = S_OK;
3224 if (RT_LIKELY(aPath.isNotEmpty()))
3225 {
3226 GuestFsObjData Info;
3227 int rcGuest;
3228 int vrc = i_fsQueryInfoInternal(aPath, aFollowSymlinks != FALSE, Info, &rcGuest);
3229 if (RT_SUCCESS(vrc))
3230 {
3231 ComObjPtr<GuestFsObjInfo> ptrFsObjInfo;
3232 hrc = ptrFsObjInfo.createObject();
3233 if (SUCCEEDED(hrc))
3234 {
3235 vrc = ptrFsObjInfo->init(Info);
3236 if (RT_SUCCESS(vrc))
3237 hrc = ptrFsObjInfo.queryInterfaceTo(aInfo.asOutParam());
3238 else
3239 hrc = setErrorVrc(vrc);
3240 }
3241 }
3242 else if (vrc == VERR_GSTCTL_GUEST_ERROR)
3243 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3244 else
3245 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3246 }
3247 else
3248 hrc = setError(E_INVALIDARG, tr("the path parameter must not be empty/NULL"));
3249 LogFlowThisFuncLeave();
3250 return hrc;
3251}
3252
3253HRESULT GuestSession::fsObjRemove(const com::Utf8Str &aPath)
3254{
3255 LogFlowThisFuncEnter();
3256
3257 if (RT_UNLIKELY(aPath.isEmpty()))
3258 return setError(E_INVALIDARG, tr("Empty path specified"));
3259
3260 HRESULT hr = S_OK;
3261
3262 int guestRc;
3263 int vrc = i_fileRemoveInternal(aPath, &guestRc);
3264 if (RT_FAILURE(vrc))
3265 {
3266 switch (vrc)
3267 {
3268 case VERR_GSTCTL_GUEST_ERROR:
3269 hr = GuestProcess::i_setErrorExternal(this, guestRc);
3270 break;
3271
3272 default:
3273 hr = setError(VBOX_E_IPRT_ERROR, tr("Removing file \"%s\" failed: %Rrc"),
3274 aPath.c_str(), vrc);
3275 break;
3276 }
3277 }
3278
3279 return hr;
3280}
3281
3282HRESULT GuestSession::fsObjRename(const com::Utf8Str &aSource,
3283 const com::Utf8Str &aDestination,
3284 const std::vector<FsObjRenameFlag_T> &aFlags)
3285{
3286 LogFlowThisFuncEnter();
3287
3288 if (RT_UNLIKELY(aSource.isEmpty()))
3289 return setError(E_INVALIDARG, tr("No source path specified"));
3290
3291 if (RT_UNLIKELY(aDestination.isEmpty()))
3292 return setError(E_INVALIDARG, tr("No destination path specified"));
3293
3294 HRESULT hr = i_isReadyExternal();
3295 if (FAILED(hr))
3296 return hr;
3297
3298 /* Combine, validate and convert flags. */
3299 uint32_t fApiFlags = 0;
3300 for (size_t i = 0; i < aFlags.size(); i++)
3301 fApiFlags |= aFlags[i];
3302 if (fApiFlags & ~((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace))
3303 return setError(E_INVALIDARG, tr("Unknown rename flag: %#x"), fApiFlags);
3304
3305 AssertCompile(FsObjRenameFlag_NoReplace == 0);
3306 AssertCompile(FsObjRenameFlag_Replace != 0);
3307 uint32_t fBackend;
3308 if ((fApiFlags & ((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace)) == FsObjRenameFlag_Replace)
3309 fBackend = PATHRENAME_FLAG_REPLACE;
3310 else
3311 fBackend = PATHRENAME_FLAG_NO_REPLACE;
3312
3313 /* Call worker to do the job. */
3314 int guestRc;
3315 int vrc = i_pathRenameInternal(aSource, aDestination, fBackend, &guestRc);
3316 if (RT_FAILURE(vrc))
3317 {
3318 switch (vrc)
3319 {
3320 case VERR_NOT_SUPPORTED:
3321 hr = setError(VBOX_E_IPRT_ERROR,
3322 tr("Handling renaming guest directories not supported by installed Guest Additions"));
3323 break;
3324
3325 case VERR_GSTCTL_GUEST_ERROR:
3326 hr = setError(VBOX_E_IPRT_ERROR,
3327 tr("Renaming guest directory failed: %Rrc"), guestRc);
3328 break;
3329
3330 default:
3331 hr = setError(VBOX_E_IPRT_ERROR, tr("Renaming guest directory \"%s\" failed: %Rrc"),
3332 aSource.c_str(), vrc);
3333 break;
3334 }
3335 }
3336
3337 return hr;
3338}
3339
3340HRESULT GuestSession::fsObjMove(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3341 const std::vector<FsObjMoveFlags_T> &aFlags, ComPtr<IProgress> &aProgress)
3342{
3343 ReturnComNotImplemented();
3344}
3345
3346HRESULT GuestSession::fsObjSetACL(const com::Utf8Str &aPath, BOOL aFollowSymlinks, const com::Utf8Str &aAcl, ULONG aMode)
3347{
3348 ReturnComNotImplemented();
3349}
3350
3351
3352HRESULT GuestSession::processCreate(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
3353 const std::vector<com::Utf8Str> &aEnvironment,
3354 const std::vector<ProcessCreateFlag_T> &aFlags,
3355 ULONG aTimeoutMS, ComPtr<IGuestProcess> &aGuestProcess)
3356{
3357 LogFlowThisFuncEnter();
3358
3359 std::vector<LONG> affinityIgnored;
3360
3361 return processCreateEx(aExecutable, aArguments, aEnvironment, aFlags, aTimeoutMS, ProcessPriority_Default,
3362 affinityIgnored, aGuestProcess);
3363}
3364
3365HRESULT GuestSession::processCreateEx(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
3366 const std::vector<com::Utf8Str> &aEnvironment,
3367 const std::vector<ProcessCreateFlag_T> &aFlags, ULONG aTimeoutMS,
3368 ProcessPriority_T aPriority, const std::vector<LONG> &aAffinity,
3369 ComPtr<IGuestProcess> &aGuestProcess)
3370{
3371 LogFlowThisFuncEnter();
3372
3373 /** @todo r=bird: Check input better? aPriority is passed on to the guest
3374 * without any validation. Flags not existing in this vbox version are
3375 * ignored, potentially doing something entirely different than what the
3376 * caller had in mind. */
3377
3378 /*
3379 * Must have an executable to execute. If none is given, we try use the
3380 * zero'th argument.
3381 */
3382 const char *pszExecutable = aExecutable.c_str();
3383 if (RT_UNLIKELY(pszExecutable == NULL || *pszExecutable == '\0'))
3384 {
3385 if (aArguments.size() > 0)
3386 pszExecutable = aArguments[0].c_str();
3387 if (pszExecutable == NULL || *pszExecutable == '\0')
3388 return setError(E_INVALIDARG, tr("No command to execute specified"));
3389 }
3390
3391 /*
3392 * Check the session.
3393 */
3394 HRESULT hr = i_isReadyExternal();
3395 if (FAILED(hr))
3396 return hr;
3397
3398 /*
3399 * Build the process startup info.
3400 */
3401 GuestProcessStartupInfo procInfo;
3402
3403 /* Executable and arguments. */
3404 procInfo.mExecutable = pszExecutable;
3405 if (aArguments.size())
3406 for (size_t i = 0; i < aArguments.size(); i++)
3407 procInfo.mArguments.push_back(aArguments[i]);
3408
3409 /* Combine the environment changes associated with the ones passed in by
3410 the caller, giving priority to the latter. The changes are putenv style
3411 and will be applied to the standard environment for the guest user. */
3412 int vrc = procInfo.mEnvironmentChanges.copy(mData.mEnvironmentChanges);
3413 if (RT_SUCCESS(vrc))
3414 vrc = procInfo.mEnvironmentChanges.applyPutEnvArray(aEnvironment);
3415 if (RT_SUCCESS(vrc))
3416 {
3417 /* Convert the flag array into a mask. */
3418 if (aFlags.size())
3419 for (size_t i = 0; i < aFlags.size(); i++)
3420 procInfo.mFlags |= aFlags[i];
3421
3422 procInfo.mTimeoutMS = aTimeoutMS;
3423
3424 /** @todo use RTCPUSET instead of archaic 64-bit variables! */
3425 if (aAffinity.size())
3426 for (size_t i = 0; i < aAffinity.size(); i++)
3427 if (aAffinity[i])
3428 procInfo.mAffinity |= (uint64_t)1 << i;
3429
3430 procInfo.mPriority = aPriority;
3431
3432 /*
3433 * Create a guest process object.
3434 */
3435 ComObjPtr<GuestProcess> pProcess;
3436 vrc = i_processCreateExInternal(procInfo, pProcess);
3437 if (RT_SUCCESS(vrc))
3438 {
3439 /* Return guest session to the caller. */
3440 hr = pProcess.queryInterfaceTo(aGuestProcess.asOutParam());
3441 if (SUCCEEDED(hr))
3442 {
3443 /*
3444 * Start the process.
3445 */
3446 vrc = pProcess->i_startProcessAsync();
3447 if (RT_SUCCESS(vrc))
3448 {
3449 LogFlowFuncLeaveRC(vrc);
3450 return S_OK;
3451 }
3452
3453 hr = setErrorVrc(vrc, tr("Failed to start guest process: %Rrc"), vrc);
3454 /** @todo r=bird: What happens to the interface that *aGuestProcess points to
3455 * now? Looks like a leak or an undocument hack of sorts... */
3456 }
3457 }
3458 else if (vrc == VERR_MAX_PROCS_REACHED)
3459 hr = setErrorVrc(vrc, tr("Maximum number of concurrent guest processes per session (%u) reached"),
3460 VBOX_GUESTCTRL_MAX_OBJECTS);
3461 else
3462 hr = setErrorVrc(vrc, tr("Failed to create guest process object: %Rrc"), vrc);
3463 }
3464 else
3465 hr = setErrorVrc(vrc, tr("Failed to set up the environment: %Rrc"), vrc);
3466
3467 LogFlowFuncLeaveRC(vrc);
3468 return hr;
3469}
3470
3471HRESULT GuestSession::processGet(ULONG aPid, ComPtr<IGuestProcess> &aGuestProcess)
3472
3473{
3474 LogFlowThisFunc(("PID=%RU32\n", aPid));
3475
3476 if (aPid == 0)
3477 return setError(E_INVALIDARG, tr("No valid process ID (PID) specified"));
3478
3479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3480
3481 HRESULT hr = S_OK;
3482
3483 ComObjPtr<GuestProcess> pProcess;
3484 int rc = i_processGetByPID(aPid, &pProcess);
3485 if (RT_FAILURE(rc))
3486 hr = setError(E_INVALIDARG, tr("No process with PID %RU32 found"), aPid);
3487
3488 /* This will set (*aProcess) to NULL if pProgress is NULL. */
3489 HRESULT hr2 = pProcess.queryInterfaceTo(aGuestProcess.asOutParam());
3490 if (SUCCEEDED(hr))
3491 hr = hr2;
3492
3493 LogFlowThisFunc(("aProcess=%p, hr=%Rhrc\n", (IGuestProcess*)aGuestProcess, hr));
3494 return hr;
3495}
3496
3497HRESULT GuestSession::symlinkCreate(const com::Utf8Str &aSource, const com::Utf8Str &aTarget, SymlinkType_T aType)
3498{
3499 ReturnComNotImplemented();
3500}
3501
3502HRESULT GuestSession::symlinkExists(const com::Utf8Str &aSymlink, BOOL *aExists)
3503
3504{
3505 ReturnComNotImplemented();
3506}
3507
3508HRESULT GuestSession::symlinkRead(const com::Utf8Str &aSymlink, const std::vector<SymlinkReadFlag_T> &aFlags,
3509 com::Utf8Str &aTarget)
3510{
3511 ReturnComNotImplemented();
3512}
3513
3514HRESULT GuestSession::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
3515{
3516 LogFlowThisFuncEnter();
3517
3518 /*
3519 * Note: Do not hold any locks here while waiting!
3520 */
3521 HRESULT hr = S_OK;
3522
3523 int guestRc; GuestSessionWaitResult_T waitResult;
3524 int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &guestRc);
3525 if (RT_SUCCESS(vrc))
3526 *aReason = waitResult;
3527 else
3528 {
3529 switch (vrc)
3530 {
3531 case VERR_GSTCTL_GUEST_ERROR:
3532 hr = GuestSession::i_setErrorExternal(this, guestRc);
3533 break;
3534
3535 case VERR_TIMEOUT:
3536 *aReason = GuestSessionWaitResult_Timeout;
3537 break;
3538
3539 default:
3540 {
3541 const char *pszSessionName = mData.mSession.mName.c_str();
3542 hr = setError(VBOX_E_IPRT_ERROR,
3543 tr("Waiting for guest session \"%s\" failed: %Rrc"),
3544 pszSessionName ? pszSessionName : tr("Unnamed"), vrc);
3545 break;
3546 }
3547 }
3548 }
3549
3550 LogFlowFuncLeaveRC(vrc);
3551 return hr;
3552}
3553
3554HRESULT GuestSession::waitForArray(const std::vector<GuestSessionWaitForFlag_T> &aWaitFor, ULONG aTimeoutMS,
3555 GuestSessionWaitResult_T *aReason)
3556{
3557 LogFlowThisFuncEnter();
3558
3559 /*
3560 * Note: Do not hold any locks here while waiting!
3561 */
3562 uint32_t fWaitFor = GuestSessionWaitForFlag_None;
3563 for (size_t i = 0; i < aWaitFor.size(); i++)
3564 fWaitFor |= aWaitFor[i];
3565
3566 return WaitFor(fWaitFor, aTimeoutMS, aReason);
3567}
3568
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