VirtualBox

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

Last change on this file since 55588 was 55588, checked in by vboxsync, 10 years ago

Main: Environment cleanup in the guest control area. The GuestEnvironment class implementation has been replaced by one based on IPRT's RTEnv API and it has been changed into a way of recording environment changes, thus renamed to GuestEnvironmentChanges.

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