VirtualBox

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

Last change on this file since 63423 was 63251, checked in by vboxsync, 8 years ago

Main: warnings

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