VirtualBox

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

Last change on this file since 49359 was 49359, checked in by vboxsync, 12 years ago

Guest Control: two non-initialized variables

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette