VirtualBox

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

Last change on this file since 50548 was 50548, checked in by vboxsync, 11 years ago

6813 - src-client/GuestDirectoryImpl.cpp - changes backed out due to error on winrel clbr 92400

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

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