VirtualBox

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

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

Main: Renamed IGuestSession::environment to IGuestSession::environmentChanges, IGuestSession::environmentSet to IGuestSession::environmentScheduleSet, IGuestSession::environmentUnset to IGuestSession::environmentScheduleUnset. Introduced read only IGuestSession::environmentBase attribute and associated IGuestSession::environmentGetBaseVariable and IGuestSession::environmentDoesBaseVariableExist for future exploitations. Changed IGuestProcess::environment back to it's originally documented behavior, though that means it will fail with VBOX_E_NOT_SUPPORTED until the session base environment is implemented.

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