VirtualBox

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

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

backed out r108916

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