VirtualBox

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

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

Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 109.6 KB
Line 
1/* $Id: GuestSessionImpl.cpp 49632 2013-11-22 16:32:09Z 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(static_cast<IGuestSession*>(this));
215 if (FAILED(hr))
216 rc = VERR_COM_UNEXPECTED;
217 }
218 }
219
220 if (RT_SUCCESS(rc))
221 {
222 try
223 {
224 GuestSessionListener *pListener = new GuestSessionListener();
225 ComObjPtr<GuestSessionListenerImpl> thisListener;
226 hr = thisListener.createObject();
227 if (SUCCEEDED(hr))
228 hr = thisListener->init(pListener, this);
229
230 if (SUCCEEDED(hr))
231 {
232 com::SafeArray <VBoxEventType_T> eventTypes;
233 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
234 hr = mEventSource->RegisterListener(thisListener,
235 ComSafeArrayAsInParam(eventTypes),
236 TRUE /* Active listener */);
237 if (SUCCEEDED(hr))
238 {
239 mLocalListener = thisListener;
240
241 rc = RTCritSectInit(&mWaitEventCritSect);
242 AssertRC(rc);
243 }
244 else
245 rc = VERR_COM_UNEXPECTED;
246 }
247 else
248 rc = VERR_COM_UNEXPECTED;
249 }
250 catch(std::bad_alloc &)
251 {
252 rc = VERR_NO_MEMORY;
253 }
254 }
255
256 if (RT_SUCCESS(rc))
257 {
258 /* Confirm a successful initialization when it's the case. */
259 autoInitSpan.setSucceeded();
260 }
261 else
262 autoInitSpan.setFailed();
263
264 LogFlowThisFunc(("mName=%s, mID=%RU32, mIsInternal=%RTbool, rc=%Rrc\n",
265 mData.mSession.mName.c_str(), mData.mSession.mID, mData.mSession.mIsInternal, rc));
266 return rc;
267#endif /* VBOX_WITH_GUEST_CONTROL */
268}
269
270/**
271 * Uninitializes the instance.
272 * Called from FinalRelease().
273 */
274void GuestSession::uninit(void)
275{
276 /* 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, const Utf8Str &strName, int *pGuestRc)
869{
870 LogFlowThisFunc(("strTemplate=%s, strPath=%s, fDirectory=%RTbool, strName=%s\n",
871 strTemplate.c_str(), strPath.c_str(), fDirectory, strName.c_str()));
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 if (RT_SUCCESS(vrc))
897 vrc = GuestProcessTool::Run(this, procInfo, pGuestRc);
898
899 LogFlowFuncLeaveRC(vrc);
900 return vrc;
901}
902
903int GuestSession::directoryOpenInternal(const GuestDirectoryOpenInfo &openInfo,
904 ComObjPtr<GuestDirectory> &pDirectory, int *pGuestRc)
905{
906 LogFlowThisFunc(("strPath=%s, strPath=%s, uFlags=%x\n",
907 openInfo.mPath.c_str(), openInfo.mFilter.c_str(), openInfo.mFlags));
908
909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
910
911 int rc = VERR_MAX_PROCS_REACHED;
912 if (mData.mNumObjects >= VBOX_GUESTCTRL_MAX_OBJECTS)
913 return rc;
914
915 /* Create a new (host-based) directory ID and assign it. */
916 uint32_t uNewDirID = 0;
917 ULONG uTries = 0;
918
919 for (;;)
920 {
921 /* Is the directory ID already used? */
922 if (!directoryExists(uNewDirID, NULL /* pDirectory */))
923 {
924 /* Callback with context ID was not found. This means
925 * we can use this context ID for our new callback we want
926 * to add below. */
927 rc = VINF_SUCCESS;
928 break;
929 }
930 uNewDirID++;
931 if (uNewDirID == VBOX_GUESTCTRL_MAX_OBJECTS)
932 uNewDirID = 0;
933
934 if (++uTries == UINT32_MAX)
935 break; /* Don't try too hard. */
936 }
937
938 if (RT_FAILURE(rc))
939 return rc;
940
941 /* Create the directory object. */
942 HRESULT hr = pDirectory.createObject();
943 if (FAILED(hr))
944 return VERR_COM_UNEXPECTED;
945
946 Console *pConsole = mParent->getConsole();
947 AssertPtr(pConsole);
948
949 int vrc = pDirectory->init(pConsole, this /* Parent */,
950 uNewDirID, openInfo);
951 if (RT_FAILURE(vrc))
952 return vrc;
953
954 /*
955 * Since this is a synchronous guest call we have to
956 * register the file object first, releasing the session's
957 * lock and then proceed with the actual opening command
958 * -- otherwise the file's opening callback would hang
959 * because the session's lock still is in place.
960 */
961 try
962 {
963 /* Add the created directory to our map. */
964 mData.mDirectories[uNewDirID] = pDirectory;
965 mData.mNumObjects++;
966 Assert(mData.mNumObjects <= VBOX_GUESTCTRL_MAX_OBJECTS);
967
968 LogFlowFunc(("Added new guest directory \"%s\" (Session: %RU32) (now total %zu dirs, %RU32 objects)\n",
969 openInfo.mPath.c_str(), mData.mSession.mID, mData.mFiles.size(), mData.mNumObjects));
970
971 alock.release(); /* Release lock before firing off event. */
972
973 /** @todo Fire off a VBoxEventType_OnGuestDirectoryRegistered event? */
974 }
975 catch (std::bad_alloc &)
976 {
977 rc = VERR_NO_MEMORY;
978 }
979
980 if (RT_SUCCESS(rc))
981 {
982 /* Nothing further to do here yet. */
983 if (pGuestRc)
984 *pGuestRc = VINF_SUCCESS;
985 }
986
987 LogFlowFuncLeaveRC(vrc);
988 return vrc;
989}
990
991int GuestSession::dispatchToDirectory(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
992{
993 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
994
995 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
996 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
997
998 if (pSvcCb->mParms < 3)
999 return VERR_INVALID_PARAMETER;
1000
1001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1002
1003 uint32_t uDirID = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCtxCb->uContextID);
1004#ifdef DEBUG
1005 LogFlowFunc(("uDirID=%RU32 (%zu total)\n",
1006 uDirID, mData.mFiles.size()));
1007#endif
1008 int rc;
1009 SessionDirectories::const_iterator itDir
1010 = mData.mDirectories.find(uDirID);
1011 if (itDir != mData.mDirectories.end())
1012 {
1013 ComObjPtr<GuestDirectory> pDirectory(itDir->second);
1014 Assert(!pDirectory.isNull());
1015
1016 alock.release();
1017
1018 rc = pDirectory->callbackDispatcher(pCtxCb, pSvcCb);
1019 }
1020 else
1021 rc = VERR_NOT_FOUND;
1022
1023 LogFlowFuncLeaveRC(rc);
1024 return rc;
1025}
1026
1027int GuestSession::dispatchToFile(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
1028{
1029 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
1030
1031 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
1032 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
1033
1034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1035
1036 uint32_t uFileID = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCtxCb->uContextID);
1037#ifdef DEBUG
1038 LogFlowFunc(("uFileID=%RU32 (%zu total)\n",
1039 uFileID, mData.mFiles.size()));
1040#endif
1041 int rc;
1042 SessionFiles::const_iterator itFile
1043 = mData.mFiles.find(uFileID);
1044 if (itFile != mData.mFiles.end())
1045 {
1046 ComObjPtr<GuestFile> pFile(itFile->second);
1047 Assert(!pFile.isNull());
1048
1049 alock.release();
1050
1051 rc = pFile->callbackDispatcher(pCtxCb, pSvcCb);
1052 }
1053 else
1054 rc = VERR_NOT_FOUND;
1055
1056 LogFlowFuncLeaveRC(rc);
1057 return rc;
1058}
1059
1060int GuestSession::dispatchToObject(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
1061{
1062 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
1063
1064 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
1065 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
1066
1067 int rc;
1068 uint32_t uObjectID = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCtxCb->uContextID);
1069
1070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1071
1072 /* Since we don't know which type the object is, we need to through all
1073 * all objects. */
1074 /** @todo Speed this up by adding an object type to the callback context! */
1075 SessionProcesses::const_iterator itProc = mData.mProcesses.find(uObjectID);
1076 if (itProc == mData.mProcesses.end())
1077 {
1078 SessionFiles::const_iterator itFile = mData.mFiles.find(uObjectID);
1079 if (itFile != mData.mFiles.end())
1080 {
1081 alock.release();
1082
1083 rc = dispatchToFile(pCtxCb, pSvcCb);
1084 }
1085 else
1086 {
1087 SessionDirectories::const_iterator itDir = mData.mDirectories.find(uObjectID);
1088 if (itDir != mData.mDirectories.end())
1089 {
1090 alock.release();
1091
1092 rc = dispatchToDirectory(pCtxCb, pSvcCb);
1093 }
1094 else
1095 rc = VERR_NOT_FOUND;
1096 }
1097 }
1098 else
1099 {
1100 alock.release();
1101
1102 rc = dispatchToProcess(pCtxCb, pSvcCb);
1103 }
1104
1105 LogFlowFuncLeaveRC(rc);
1106 return rc;
1107}
1108
1109int GuestSession::dispatchToProcess(PVBOXGUESTCTRLHOSTCBCTX pCtxCb, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
1110{
1111 LogFlowFunc(("pCtxCb=%p, pSvcCb=%p\n", pCtxCb, pSvcCb));
1112
1113 AssertPtrReturn(pCtxCb, VERR_INVALID_POINTER);
1114 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
1115
1116 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1117
1118 uint32_t uProcessID = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pCtxCb->uContextID);
1119#ifdef DEBUG
1120 LogFlowFunc(("uProcessID=%RU32 (%zu total)\n",
1121 uProcessID, mData.mProcesses.size()));
1122#endif
1123 int rc;
1124 SessionProcesses::const_iterator itProc
1125 = mData.mProcesses.find(uProcessID);
1126 if (itProc != mData.mProcesses.end())
1127 {
1128#ifdef DEBUG_andy
1129 ULONG cRefs = itProc->second->AddRef();
1130 Assert(cRefs >= 2);
1131 LogFlowFunc(("pProcess=%p, cRefs=%RU32\n", &itProc->second, cRefs - 1));
1132 itProc->second->Release();
1133#endif
1134 ComObjPtr<GuestProcess> pProcess(itProc->second);
1135 Assert(!pProcess.isNull());
1136
1137 /* Set protocol version so that pSvcCb can
1138 * be interpreted right. */
1139 pCtxCb->uProtocol = mData.mProtocolVersion;
1140
1141 alock.release();
1142 rc = pProcess->callbackDispatcher(pCtxCb, pSvcCb);
1143 }
1144 else
1145 rc = VERR_NOT_FOUND;
1146
1147 LogFlowFuncLeaveRC(rc);
1148 return rc;
1149}
1150
1151int GuestSession::dispatchToThis(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCb)
1152{
1153 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
1154 AssertPtrReturn(pSvcCb, VERR_INVALID_POINTER);
1155
1156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1157
1158#ifdef DEBUG
1159 LogFlowThisFunc(("sessionID=%RU32, CID=%RU32, uFunction=%RU32, pSvcCb=%p\n",
1160 mData.mSession.mID, pCbCtx->uContextID, pCbCtx->uFunction, pSvcCb));
1161#endif
1162
1163 int rc;
1164 switch (pCbCtx->uFunction)
1165 {
1166 case GUEST_DISCONNECTED:
1167 /** @todo Handle closing all guest objects. */
1168 rc = VERR_INTERNAL_ERROR;
1169 break;
1170
1171 case GUEST_SESSION_NOTIFY: /* Guest Additions >= 4.3.0. */
1172 {
1173 rc = onSessionStatusChange(pCbCtx, pSvcCb);
1174 break;
1175 }
1176
1177 default:
1178 /* Silently skip unknown callbacks. */
1179 rc = VERR_NOT_SUPPORTED;
1180 break;
1181 }
1182
1183 LogFlowFuncLeaveRC(rc);
1184 return rc;
1185}
1186
1187inline bool GuestSession::fileExists(uint32_t uFileID, ComObjPtr<GuestFile> *pFile)
1188{
1189 SessionFiles::const_iterator it = mData.mFiles.find(uFileID);
1190 if (it != mData.mFiles.end())
1191 {
1192 if (pFile)
1193 *pFile = it->second;
1194 return true;
1195 }
1196 return false;
1197}
1198
1199int GuestSession::fileRemoveFromList(GuestFile *pFile)
1200{
1201 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1202
1203 int rc = VERR_NOT_FOUND;
1204
1205 SessionFiles::iterator itFiles = mData.mFiles.begin();
1206 while (itFiles != mData.mFiles.end())
1207 {
1208 if (pFile == itFiles->second)
1209 {
1210 /* Make sure to consume the pointer before the one of thfe
1211 * iterator gets released. */
1212 ComObjPtr<GuestFile> pCurFile = pFile;
1213
1214 Bstr strName;
1215 HRESULT hr = pCurFile->COMGETTER(FileName)(strName.asOutParam());
1216 ComAssertComRC(hr);
1217
1218 Assert(mData.mNumObjects);
1219 LogFlowThisFunc(("Removing guest file \"%s\" (Session: %RU32) (now total %zu files, %RU32 objects)\n",
1220 Utf8Str(strName).c_str(), mData.mSession.mID, mData.mFiles.size() - 1, mData.mNumObjects - 1));
1221
1222 rc = pFile->onRemove();
1223 mData.mFiles.erase(itFiles);
1224 mData.mNumObjects--;
1225
1226 alock.release(); /* Release lock before firing off event. */
1227
1228 fireGuestFileRegisteredEvent(mEventSource, this, pCurFile,
1229 false /* Unregistered */);
1230 pCurFile.setNull();
1231 break;
1232 }
1233
1234 itFiles++;
1235 }
1236
1237 LogFlowFuncLeaveRC(rc);
1238 return rc;
1239}
1240
1241int GuestSession::fileRemoveInternal(const Utf8Str &strPath, int *pGuestRc)
1242{
1243 LogFlowThisFunc(("strPath=%s\n", strPath.c_str()));
1244
1245 int vrc = VINF_SUCCESS;
1246
1247 GuestProcessStartupInfo procInfo;
1248 GuestProcessStream streamOut;
1249
1250 procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_RM);
1251 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
1252
1253 try
1254 {
1255 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
1256 procInfo.mArguments.push_back(strPath); /* The file we want to remove. */
1257 }
1258 catch (std::bad_alloc)
1259 {
1260 vrc = VERR_NO_MEMORY;
1261 }
1262
1263 if (RT_SUCCESS(vrc))
1264 vrc = GuestProcessTool::Run(this, procInfo, pGuestRc);
1265
1266 LogFlowFuncLeaveRC(vrc);
1267 return vrc;
1268}
1269
1270int GuestSession::fileOpenInternal(const GuestFileOpenInfo &openInfo,
1271 ComObjPtr<GuestFile> &pFile, int *pGuestRc)
1272{
1273 LogFlowThisFunc(("strPath=%s, strOpenMode=%s, strDisposition=%s, uCreationMode=%x, uOffset=%RU64\n",
1274 openInfo.mFileName.c_str(), openInfo.mOpenMode.c_str(), openInfo.mDisposition.c_str(),
1275 openInfo.mCreationMode, openInfo.mInitialOffset));
1276
1277 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1278
1279 /* Guest Additions < 4.3 don't support handling
1280 guest files, skip. */
1281 if (mData.mProtocolVersion < 2)
1282 {
1283 LogFlowThisFunc(("Installed Guest Additions don't support handling guest files, skipping\n"));
1284 return VERR_NOT_SUPPORTED;
1285 }
1286
1287 int rc = VERR_MAX_PROCS_REACHED;
1288 if (mData.mNumObjects >= VBOX_GUESTCTRL_MAX_OBJECTS)
1289 return rc;
1290
1291 /* Create a new (host-based) file ID and assign it. */
1292 uint32_t uNewFileID = 0;
1293 ULONG uTries = 0;
1294
1295 for (;;)
1296 {
1297 /* Is the file ID already used? */
1298 if (!fileExists(uNewFileID, NULL /* pFile */))
1299 {
1300 /* Callback with context ID was not found. This means
1301 * we can use this context ID for our new callback we want
1302 * to add below. */
1303 rc = VINF_SUCCESS;
1304 break;
1305 }
1306 uNewFileID++;
1307 if (uNewFileID == VBOX_GUESTCTRL_MAX_OBJECTS)
1308 uNewFileID = 0;
1309
1310 if (++uTries == UINT32_MAX)
1311 break; /* Don't try too hard. */
1312 }
1313
1314 if (RT_FAILURE(rc))
1315 return rc;
1316
1317 /* Create the directory object. */
1318 HRESULT hr = pFile.createObject();
1319 if (FAILED(hr))
1320 return VERR_COM_UNEXPECTED;
1321
1322 Console *pConsole = mParent->getConsole();
1323 AssertPtr(pConsole);
1324
1325 rc = pFile->init(pConsole, this /* GuestSession */,
1326 uNewFileID, openInfo);
1327 if (RT_FAILURE(rc))
1328 return rc;
1329
1330 /*
1331 * Since this is a synchronous guest call we have to
1332 * register the file object first, releasing the session's
1333 * lock and then proceed with the actual opening command
1334 * -- otherwise the file's opening callback would hang
1335 * because the session's lock still is in place.
1336 */
1337 try
1338 {
1339 /* Add the created file to our vector. */
1340 mData.mFiles[uNewFileID] = pFile;
1341 mData.mNumObjects++;
1342 Assert(mData.mNumObjects <= VBOX_GUESTCTRL_MAX_OBJECTS);
1343
1344 LogFlowFunc(("Added new guest file \"%s\" (Session: %RU32) (now total %zu files, %RU32 objects)\n",
1345 openInfo.mFileName.c_str(), mData.mSession.mID, mData.mFiles.size(), mData.mNumObjects));
1346
1347 alock.release(); /* Release lock before firing off event. */
1348
1349 fireGuestFileRegisteredEvent(mEventSource, this, pFile,
1350 true /* Registered */);
1351 }
1352 catch (std::bad_alloc &)
1353 {
1354 rc = VERR_NO_MEMORY;
1355 }
1356
1357 if (RT_SUCCESS(rc))
1358 {
1359 int guestRc;
1360 rc = pFile->openFile(30 * 1000 /* 30s timeout */, &guestRc);
1361 if ( rc == VERR_GSTCTL_GUEST_ERROR
1362 && pGuestRc)
1363 {
1364 *pGuestRc = guestRc;
1365 }
1366 }
1367
1368 LogFlowFuncLeaveRC(rc);
1369 return rc;
1370}
1371
1372int GuestSession::fileQueryInfoInternal(const Utf8Str &strPath, GuestFsObjData &objData, int *pGuestRc)
1373{
1374 LogFlowThisFunc(("strPath=%s\n", strPath.c_str()));
1375
1376 int vrc = fsQueryInfoInternal(strPath, objData, pGuestRc);
1377 if (RT_SUCCESS(vrc))
1378 {
1379 vrc = objData.mType == FsObjType_File
1380 ? VINF_SUCCESS : VERR_NOT_A_FILE;
1381 }
1382
1383 LogFlowFuncLeaveRC(vrc);
1384 return vrc;
1385}
1386
1387int GuestSession::fileQuerySizeInternal(const Utf8Str &strPath, int64_t *pllSize, int *pGuestRc)
1388{
1389 AssertPtrReturn(pllSize, VERR_INVALID_POINTER);
1390
1391 GuestFsObjData objData;
1392 int vrc = fileQueryInfoInternal(strPath, objData, pGuestRc);
1393 if (RT_SUCCESS(vrc))
1394 *pllSize = objData.mObjectSize;
1395
1396 return vrc;
1397}
1398
1399int GuestSession::fsQueryInfoInternal(const Utf8Str &strPath, GuestFsObjData &objData, int *pGuestRc)
1400{
1401 LogFlowThisFunc(("strPath=%s\n", strPath.c_str()));
1402
1403 int vrc = VINF_SUCCESS;
1404
1405 /** @todo Merge this with IGuestFile::queryInfo(). */
1406 GuestProcessStartupInfo procInfo;
1407 procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_STAT);
1408 procInfo.mFlags = ProcessCreateFlag_WaitForStdOut;
1409
1410 try
1411 {
1412 /* Construct arguments. */
1413 procInfo.mArguments.push_back(Utf8Str("--machinereadable"));
1414 procInfo.mArguments.push_back(strPath);
1415 }
1416 catch (std::bad_alloc)
1417 {
1418 vrc = VERR_NO_MEMORY;
1419 }
1420
1421 int guestRc; GuestCtrlStreamObjects stdOut;
1422 if (RT_SUCCESS(vrc))
1423 vrc = GuestProcessTool::RunEx(this, procInfo,
1424 &stdOut, 1 /* cStrmOutObjects */,
1425 &guestRc);
1426 if ( RT_SUCCESS(vrc)
1427 && RT_SUCCESS(guestRc))
1428 {
1429 if (!stdOut.empty())
1430 vrc = objData.FromStat(stdOut.at(0));
1431 else
1432 vrc = VERR_NO_DATA;
1433 }
1434
1435 if ( vrc == VERR_GSTCTL_GUEST_ERROR
1436 && pGuestRc)
1437 *pGuestRc = guestRc;
1438
1439 LogFlowThisFunc(("Returning rc=%Rrc, guestRc=%Rrc\n",
1440 vrc, guestRc));
1441 return vrc;
1442}
1443
1444const GuestCredentials& GuestSession::getCredentials(void)
1445{
1446 return mData.mCredentials;
1447}
1448
1449const GuestEnvironment& GuestSession::getEnvironment(void)
1450{
1451 return mData.mEnvironment;
1452}
1453
1454Utf8Str GuestSession::getName(void)
1455{
1456 return mData.mSession.mName;
1457}
1458
1459/* static */
1460Utf8Str GuestSession::guestErrorToString(int guestRc)
1461{
1462 Utf8Str strError;
1463
1464 /** @todo pData->u32Flags: int vs. uint32 -- IPRT errors are *negative* !!! */
1465 switch (guestRc)
1466 {
1467 case VERR_INVALID_VM_HANDLE:
1468 strError += Utf8StrFmt(tr("VMM device is not available (is the VM running?)"));
1469 break;
1470
1471 case VERR_HGCM_SERVICE_NOT_FOUND:
1472 strError += Utf8StrFmt(tr("The guest execution service is not available"));
1473 break;
1474
1475 case VERR_AUTHENTICATION_FAILURE:
1476 strError += Utf8StrFmt(tr("The specified user was not able to logon on guest"));
1477 break;
1478
1479 case VERR_TIMEOUT:
1480 strError += Utf8StrFmt(tr("The guest did not respond within time"));
1481 break;
1482
1483 case VERR_CANCELLED:
1484 strError += Utf8StrFmt(tr("The session operation was canceled"));
1485 break;
1486
1487 case VERR_PERMISSION_DENIED:
1488 strError += Utf8StrFmt(tr("Invalid user/password credentials"));
1489 break;
1490
1491 case VERR_MAX_PROCS_REACHED:
1492 strError += Utf8StrFmt(tr("Maximum number of concurrent guest processes has been reached"));
1493 break;
1494
1495 case VERR_NOT_EQUAL: /** @todo Imprecise to the user; can mean anything and all. */
1496 strError += Utf8StrFmt(tr("Unable to retrieve requested information"));
1497 break;
1498
1499 case VERR_NOT_FOUND:
1500 strError += Utf8StrFmt(tr("The guest execution service is not ready (yet)"));
1501 break;
1502
1503 default:
1504 strError += Utf8StrFmt("%Rrc", guestRc);
1505 break;
1506 }
1507
1508 return strError;
1509}
1510
1511/**
1512 * Checks if this session is ready state where it can handle
1513 * all session-bound actions (like guest processes, guest files).
1514 * Only used by official API methods. Will set an external
1515 * error when not ready.
1516 */
1517HRESULT GuestSession::isReadyExternal(void)
1518{
1519 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1520
1521 /** @todo Be a bit more informative. */
1522 if (mData.mStatus != GuestSessionStatus_Started)
1523 return setError(E_UNEXPECTED, tr("Session is not in started state"));
1524
1525 return S_OK;
1526}
1527
1528/**
1529 * Called by IGuest right before this session gets removed from
1530 * the public session list.
1531 */
1532int GuestSession::onRemove(void)
1533{
1534 LogFlowThisFuncEnter();
1535
1536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1537
1538 int vrc = VINF_SUCCESS;
1539
1540 /*
1541 * Note: The event source stuff holds references to this object,
1542 * so make sure that this is cleaned up *before* calling uninit.
1543 */
1544 if (!mEventSource.isNull())
1545 {
1546 mEventSource->UnregisterListener(mLocalListener);
1547
1548 mLocalListener.setNull();
1549 unconst(mEventSource).setNull();
1550 }
1551
1552 LogFlowFuncLeaveRC(vrc);
1553 return vrc;
1554}
1555
1556/** No locking! */
1557int GuestSession::onSessionStatusChange(PVBOXGUESTCTRLHOSTCBCTX pCbCtx, PVBOXGUESTCTRLHOSTCALLBACK pSvcCbData)
1558{
1559 AssertPtrReturn(pCbCtx, VERR_INVALID_POINTER);
1560 /* pCallback is optional. */
1561 AssertPtrReturn(pSvcCbData, VERR_INVALID_POINTER);
1562
1563 if (pSvcCbData->mParms < 3)
1564 return VERR_INVALID_PARAMETER;
1565
1566 CALLBACKDATA_SESSION_NOTIFY dataCb;
1567 /* pSvcCb->mpaParms[0] always contains the context ID. */
1568 int vrc = pSvcCbData->mpaParms[1].getUInt32(&dataCb.uType);
1569 AssertRCReturn(vrc, vrc);
1570 vrc = pSvcCbData->mpaParms[2].getUInt32(&dataCb.uResult);
1571 AssertRCReturn(vrc, vrc);
1572
1573 LogFlowThisFunc(("ID=%RU32, uType=%RU32, guestRc=%Rrc\n",
1574 mData.mSession.mID, dataCb.uType, dataCb.uResult));
1575
1576 GuestSessionStatus_T sessionStatus = GuestSessionStatus_Undefined;
1577
1578 int guestRc = dataCb.uResult; /** @todo uint32_t vs. int. */
1579 switch (dataCb.uType)
1580 {
1581 case GUEST_SESSION_NOTIFYTYPE_ERROR:
1582 sessionStatus = GuestSessionStatus_Error;
1583 break;
1584
1585 case GUEST_SESSION_NOTIFYTYPE_STARTED:
1586 sessionStatus = GuestSessionStatus_Started;
1587 break;
1588
1589 case GUEST_SESSION_NOTIFYTYPE_TEN:
1590 case GUEST_SESSION_NOTIFYTYPE_TES:
1591 case GUEST_SESSION_NOTIFYTYPE_TEA:
1592 sessionStatus = GuestSessionStatus_Terminated;
1593 break;
1594
1595 case GUEST_SESSION_NOTIFYTYPE_TOK:
1596 sessionStatus = GuestSessionStatus_TimedOutKilled;
1597 break;
1598
1599 case GUEST_SESSION_NOTIFYTYPE_TOA:
1600 sessionStatus = GuestSessionStatus_TimedOutAbnormally;
1601 break;
1602
1603 case GUEST_SESSION_NOTIFYTYPE_DWN:
1604 sessionStatus = GuestSessionStatus_Down;
1605 break;
1606
1607 case GUEST_SESSION_NOTIFYTYPE_UNDEFINED:
1608 default:
1609 vrc = VERR_NOT_SUPPORTED;
1610 break;
1611 }
1612
1613 if (RT_SUCCESS(vrc))
1614 {
1615 if (RT_FAILURE(guestRc))
1616 sessionStatus = GuestSessionStatus_Error;
1617 }
1618
1619 /* Set the session status. */
1620 if (RT_SUCCESS(vrc))
1621 vrc = setSessionStatus(sessionStatus, guestRc);
1622
1623 LogFlowThisFunc(("ID=%RU32, guestRc=%Rrc\n", mData.mSession.mID, guestRc));
1624
1625 LogFlowFuncLeaveRC(vrc);
1626 return vrc;
1627}
1628
1629int GuestSession::startSessionInternal(int *pGuestRc)
1630{
1631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1632
1633 LogFlowThisFunc(("mID=%RU32, mName=%s, uProtocolVersion=%RU32, openFlags=%x, openTimeoutMS=%RU32\n",
1634 mData.mSession.mID, mData.mSession.mName.c_str(), mData.mProtocolVersion,
1635 mData.mSession.mOpenFlags, mData.mSession.mOpenTimeoutMS));
1636
1637 /* Guest Additions < 4.3 don't support opening dedicated
1638 guest sessions. Simply return success here. */
1639 if (mData.mProtocolVersion < 2)
1640 {
1641 mData.mStatus = GuestSessionStatus_Started;
1642
1643 LogFlowThisFunc(("Installed Guest Additions don't support opening dedicated sessions, skipping\n"));
1644 return VINF_SUCCESS;
1645 }
1646
1647 if (mData.mStatus != GuestSessionStatus_Undefined)
1648 return VINF_SUCCESS;
1649
1650 /** @todo mData.mSession.uFlags validation. */
1651
1652 /* Set current session status. */
1653 mData.mStatus = GuestSessionStatus_Starting;
1654 mData.mRC = VINF_SUCCESS; /* Clear previous error, if any. */
1655
1656 int vrc;
1657
1658 GuestWaitEvent *pEvent = NULL;
1659 GuestEventTypes eventTypes;
1660 try
1661 {
1662 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
1663
1664 vrc = registerWaitEvent(mData.mSession.mID, 0 /* Object ID */,
1665 eventTypes, &pEvent);
1666 }
1667 catch (std::bad_alloc)
1668 {
1669 vrc = VERR_NO_MEMORY;
1670 }
1671
1672 if (RT_FAILURE(vrc))
1673 return vrc;
1674
1675 VBOXHGCMSVCPARM paParms[8];
1676
1677 int i = 0;
1678 paParms[i++].setUInt32(pEvent->ContextID());
1679 paParms[i++].setUInt32(mData.mProtocolVersion);
1680 paParms[i++].setPointer((void*)mData.mCredentials.mUser.c_str(),
1681 (ULONG)mData.mCredentials.mUser.length() + 1);
1682 paParms[i++].setPointer((void*)mData.mCredentials.mPassword.c_str(),
1683 (ULONG)mData.mCredentials.mPassword.length() + 1);
1684 paParms[i++].setPointer((void*)mData.mCredentials.mDomain.c_str(),
1685 (ULONG)mData.mCredentials.mDomain.length() + 1);
1686 paParms[i++].setUInt32(mData.mSession.mOpenFlags);
1687
1688 alock.release(); /* Drop write lock before sending. */
1689
1690 vrc = sendCommand(HOST_SESSION_CREATE, i, paParms);
1691 if (RT_SUCCESS(vrc))
1692 {
1693 vrc = waitForStatusChange(pEvent, GuestSessionWaitForFlag_Start,
1694 30 * 1000 /* 30s timeout */,
1695 NULL /* Session status */, pGuestRc);
1696 }
1697 else
1698 {
1699 /*
1700 * Unable to start guest session - update its current state.
1701 * Since there is no (official API) way to recover a failed guest session
1702 * this also marks the end state. Internally just calling this
1703 * same function again will work though.
1704 */
1705 mData.mStatus = GuestSessionStatus_Error;
1706 mData.mRC = vrc;
1707 }
1708
1709 unregisterWaitEvent(pEvent);
1710
1711 LogFlowFuncLeaveRC(vrc);
1712 return vrc;
1713}
1714
1715int GuestSession::startSessionAsync(void)
1716{
1717 LogFlowThisFuncEnter();
1718
1719 int vrc;
1720
1721 try
1722 {
1723 /* Asynchronously open the session on the guest by kicking off a
1724 * worker thread. */
1725 std::auto_ptr<GuestSessionTaskInternalOpen> pTask(new GuestSessionTaskInternalOpen(this));
1726 AssertReturn(pTask->isOk(), pTask->rc());
1727
1728 vrc = RTThreadCreate(NULL, GuestSession::startSessionThread,
1729 (void *)pTask.get(), 0,
1730 RTTHREADTYPE_MAIN_WORKER, 0,
1731 "gctlSesStart");
1732 if (RT_SUCCESS(vrc))
1733 {
1734 /* pTask is now owned by openSessionThread(), so release it. */
1735 pTask.release();
1736 }
1737 }
1738 catch(std::bad_alloc &)
1739 {
1740 vrc = VERR_NO_MEMORY;
1741 }
1742
1743 LogFlowFuncLeaveRC(vrc);
1744 return vrc;
1745}
1746
1747/* static */
1748DECLCALLBACK(int) GuestSession::startSessionThread(RTTHREAD Thread, void *pvUser)
1749{
1750 LogFlowFunc(("pvUser=%p\n", pvUser));
1751
1752 std::auto_ptr<GuestSessionTaskInternalOpen> pTask(static_cast<GuestSessionTaskInternalOpen*>(pvUser));
1753 AssertPtr(pTask.get());
1754
1755 const ComObjPtr<GuestSession> pSession(pTask->Session());
1756 Assert(!pSession.isNull());
1757
1758 AutoCaller autoCaller(pSession);
1759 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1760
1761 int vrc = pSession->startSessionInternal(NULL /* Guest rc, ignored */);
1762 /* Nothing to do here anymore. */
1763
1764 LogFlowFuncLeaveRC(vrc);
1765 return vrc;
1766}
1767
1768int GuestSession::pathRenameInternal(const Utf8Str &strSource, const Utf8Str &strDest,
1769 uint32_t uFlags, int *pGuestRc)
1770{
1771 AssertReturn(!(uFlags & ~PATHRENAME_FLAG_VALID_MASK), VERR_INVALID_PARAMETER);
1772
1773 LogFlowThisFunc(("strSource=%s, strDest=%s, uFlags=0x%x\n",
1774 strSource.c_str(), strDest.c_str(), uFlags));
1775
1776 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1777
1778 GuestWaitEvent *pEvent = NULL;
1779 int vrc = registerWaitEvent(mData.mSession.mID, 0 /* Object ID */,
1780 &pEvent);
1781 if (RT_FAILURE(vrc))
1782 return vrc;
1783
1784 /* Prepare HGCM call. */
1785 VBOXHGCMSVCPARM paParms[8];
1786 int i = 0;
1787 paParms[i++].setUInt32(pEvent->ContextID());
1788 paParms[i++].setPointer((void*)strSource.c_str(),
1789 (ULONG)strSource.length() + 1);
1790 paParms[i++].setPointer((void*)strDest.c_str(),
1791 (ULONG)strDest.length() + 1);
1792 paParms[i++].setUInt32(uFlags);
1793
1794 alock.release(); /* Drop write lock before sending. */
1795
1796 vrc = sendCommand(HOST_PATH_RENAME, i, paParms);
1797 if (RT_SUCCESS(vrc))
1798 {
1799 vrc = pEvent->Wait(30 * 1000);
1800 if ( vrc == VERR_GSTCTL_GUEST_ERROR
1801 && pGuestRc)
1802 *pGuestRc = pEvent->GuestResult();
1803 }
1804
1805 unregisterWaitEvent(pEvent);
1806
1807 LogFlowFuncLeaveRC(vrc);
1808 return vrc;
1809}
1810
1811int GuestSession::processRemoveFromList(GuestProcess *pProcess)
1812{
1813 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
1814
1815 LogFlowThisFunc(("pProcess=%p\n", pProcess));
1816
1817 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1818
1819 int rc = VERR_NOT_FOUND;
1820
1821 ULONG uPID;
1822 HRESULT hr = pProcess->COMGETTER(PID)(&uPID);
1823 ComAssertComRC(hr);
1824
1825 LogFlowFunc(("Removing process (PID=%RU32) ...\n", uPID));
1826
1827 SessionProcesses::iterator itProcs = mData.mProcesses.begin();
1828 while (itProcs != mData.mProcesses.end())
1829 {
1830 if (pProcess == itProcs->second)
1831 {
1832#ifdef DEBUG_andy
1833 ULONG cRefs = pProcess->AddRef();
1834 Assert(cRefs >= 2);
1835 LogFlowFunc(("pProcess=%p, cRefs=%RU32\n", pProcess, cRefs - 1));
1836 pProcess->Release();
1837#endif
1838 /* Make sure to consume the pointer before the one of the
1839 * iterator gets released. */
1840 ComObjPtr<GuestProcess> pProc = pProcess;
1841
1842 hr = pProc->COMGETTER(PID)(&uPID);
1843 ComAssertComRC(hr);
1844
1845 Assert(mData.mProcesses.size());
1846 Assert(mData.mNumObjects);
1847 LogFlowFunc(("Removing process ID=%RU32 (Session: %RU32), guest PID=%RU32 (now total %zu processes, %RU32 objects)\n",
1848 pProcess->getObjectID(), mData.mSession.mID, uPID, mData.mProcesses.size() - 1, mData.mNumObjects - 1));
1849
1850 rc = pProcess->onRemove();
1851 mData.mProcesses.erase(itProcs);
1852 mData.mNumObjects--;
1853
1854 alock.release(); /* Release lock before firing off event. */
1855
1856 fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProc,
1857 uPID, false /* Process unregistered */);
1858 pProc.setNull();
1859 break;
1860 }
1861
1862 itProcs++;
1863 }
1864
1865 LogFlowFuncLeaveRC(rc);
1866 return rc;
1867}
1868
1869/**
1870 * Creates but does *not* start the process yet. See GuestProcess::startProcess() or
1871 * GuestProcess::startProcessAsync() for that.
1872 *
1873 * @return IPRT status code.
1874 * @param procInfo
1875 * @param pProcess
1876 */
1877int GuestSession::processCreateExInteral(GuestProcessStartupInfo &procInfo, ComObjPtr<GuestProcess> &pProcess)
1878{
1879 LogFlowFunc(("mCmd=%s, mFlags=%x, mTimeoutMS=%RU32\n",
1880 procInfo.mCommand.c_str(), procInfo.mFlags, procInfo.mTimeoutMS));
1881#ifdef DEBUG
1882 if (procInfo.mArguments.size())
1883 {
1884 LogFlowFunc(("Arguments:"));
1885 ProcessArguments::const_iterator it = procInfo.mArguments.begin();
1886 while (it != procInfo.mArguments.end())
1887 {
1888 LogFlow((" %s", (*it).c_str()));
1889 it++;
1890 }
1891 LogFlow(("\n"));
1892 }
1893#endif
1894
1895 /* Validate flags. */
1896 if (procInfo.mFlags)
1897 {
1898 if ( !(procInfo.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
1899 && !(procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
1900 && !(procInfo.mFlags & ProcessCreateFlag_Hidden)
1901 && !(procInfo.mFlags & ProcessCreateFlag_NoProfile)
1902 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
1903 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdErr))
1904 {
1905 return VERR_INVALID_PARAMETER;
1906 }
1907 }
1908
1909 if ( (procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
1910 && ( (procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
1911 || (procInfo.mFlags & ProcessCreateFlag_WaitForStdErr)
1912 )
1913 )
1914 {
1915 return VERR_INVALID_PARAMETER;
1916 }
1917
1918 /* Adjust timeout. If set to 0, we define
1919 * an infinite timeout. */
1920 if (procInfo.mTimeoutMS == 0)
1921 procInfo.mTimeoutMS = UINT32_MAX;
1922
1923 /** @tood Implement process priority + affinity. */
1924
1925 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1926
1927 int rc = VERR_MAX_PROCS_REACHED;
1928 if (mData.mNumObjects >= VBOX_GUESTCTRL_MAX_OBJECTS)
1929 return rc;
1930
1931 /* Create a new (host-based) process ID and assign it. */
1932 uint32_t uNewProcessID = 0;
1933 ULONG uTries = 0;
1934
1935 for (;;)
1936 {
1937 /* Is the context ID already used? */
1938 if (!processExists(uNewProcessID, NULL /* pProcess */))
1939 {
1940 /* Callback with context ID was not found. This means
1941 * we can use this context ID for our new callback we want
1942 * to add below. */
1943 rc = VINF_SUCCESS;
1944 break;
1945 }
1946 uNewProcessID++;
1947 if (uNewProcessID == VBOX_GUESTCTRL_MAX_OBJECTS)
1948 uNewProcessID = 0;
1949
1950 if (++uTries == VBOX_GUESTCTRL_MAX_OBJECTS)
1951 break; /* Don't try too hard. */
1952 }
1953
1954 if (RT_FAILURE(rc))
1955 return rc;
1956
1957 /* Create the process object. */
1958 HRESULT hr = pProcess.createObject();
1959 if (FAILED(hr))
1960 return VERR_COM_UNEXPECTED;
1961
1962 rc = pProcess->init(mParent->getConsole() /* Console */, this /* Session */,
1963 uNewProcessID, procInfo);
1964 if (RT_FAILURE(rc))
1965 return rc;
1966
1967 /* Add the created process to our map. */
1968 try
1969 {
1970 mData.mProcesses[uNewProcessID] = pProcess;
1971 mData.mNumObjects++;
1972 Assert(mData.mNumObjects <= VBOX_GUESTCTRL_MAX_OBJECTS);
1973
1974 LogFlowFunc(("Added new process (Session: %RU32) with process ID=%RU32 (now total %zu processes, %RU32 objects)\n",
1975 mData.mSession.mID, uNewProcessID, mData.mProcesses.size(), mData.mNumObjects));
1976
1977 alock.release(); /* Release lock before firing off event. */
1978
1979 fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProcess,
1980 0 /* PID */, true /* Process registered */);
1981 }
1982 catch (std::bad_alloc &)
1983 {
1984 rc = VERR_NO_MEMORY;
1985 }
1986
1987 return rc;
1988}
1989
1990inline bool GuestSession::processExists(uint32_t uProcessID, ComObjPtr<GuestProcess> *pProcess)
1991{
1992 SessionProcesses::const_iterator it = mData.mProcesses.find(uProcessID);
1993 if (it != mData.mProcesses.end())
1994 {
1995 if (pProcess)
1996 *pProcess = it->second;
1997 return true;
1998 }
1999 return false;
2000}
2001
2002inline int GuestSession::processGetByPID(ULONG uPID, ComObjPtr<GuestProcess> *pProcess)
2003{
2004 AssertReturn(uPID, false);
2005 /* pProcess is optional. */
2006
2007 SessionProcesses::iterator itProcs = mData.mProcesses.begin();
2008 for (; itProcs != mData.mProcesses.end(); itProcs++)
2009 {
2010 ComObjPtr<GuestProcess> pCurProc = itProcs->second;
2011 AutoCaller procCaller(pCurProc);
2012 if (procCaller.rc())
2013 return VERR_COM_INVALID_OBJECT_STATE;
2014
2015 ULONG uCurPID;
2016 HRESULT hr = pCurProc->COMGETTER(PID)(&uCurPID);
2017 ComAssertComRC(hr);
2018
2019 if (uCurPID == uPID)
2020 {
2021 if (pProcess)
2022 *pProcess = pCurProc;
2023 return VINF_SUCCESS;
2024 }
2025 }
2026
2027 return VERR_NOT_FOUND;
2028}
2029
2030int GuestSession::sendCommand(uint32_t uFunction,
2031 uint32_t uParms, PVBOXHGCMSVCPARM paParms)
2032{
2033 LogFlowThisFuncEnter();
2034
2035#ifndef VBOX_GUESTCTRL_TEST_CASE
2036 ComObjPtr<Console> pConsole = mParent->getConsole();
2037 Assert(!pConsole.isNull());
2038
2039 /* Forward the information to the VMM device. */
2040 VMMDev *pVMMDev = pConsole->getVMMDev();
2041 AssertPtr(pVMMDev);
2042
2043 LogFlowThisFunc(("uFunction=%RU32, uParms=%RU32\n", uFunction, uParms));
2044 int vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uFunction, uParms, paParms);
2045 if (RT_FAILURE(vrc))
2046 {
2047 /** @todo What to do here? */
2048 }
2049#else
2050 /* Not needed within testcases. */
2051 int vrc = VINF_SUCCESS;
2052#endif
2053 LogFlowFuncLeaveRC(vrc);
2054 return vrc;
2055}
2056
2057/* static */
2058HRESULT GuestSession::setErrorExternal(VirtualBoxBase *pInterface, int guestRc)
2059{
2060 AssertPtr(pInterface);
2061 AssertMsg(RT_FAILURE(guestRc), ("Guest rc does not indicate a failure when setting error\n"));
2062
2063 return pInterface->setError(VBOX_E_IPRT_ERROR, GuestSession::guestErrorToString(guestRc).c_str());
2064}
2065
2066/* Does not do locking; caller is responsible for that! */
2067int GuestSession::setSessionStatus(GuestSessionStatus_T sessionStatus, int sessionRc)
2068{
2069 LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, sessionRc=%Rrc\n",
2070 mData.mStatus, sessionStatus, sessionRc));
2071
2072 if (sessionStatus == GuestSessionStatus_Error)
2073 {
2074 AssertMsg(RT_FAILURE(sessionRc), ("Guest rc must be an error (%Rrc)\n", sessionRc));
2075 /* Do not allow overwriting an already set error. If this happens
2076 * this means we forgot some error checking/locking somewhere. */
2077 AssertMsg(RT_SUCCESS(mData.mRC), ("Guest rc already set (to %Rrc)\n", mData.mRC));
2078 }
2079 else
2080 AssertMsg(RT_SUCCESS(sessionRc), ("Guest rc must not be an error (%Rrc)\n", sessionRc));
2081
2082 if (mData.mStatus != sessionStatus)
2083 {
2084 mData.mStatus = sessionStatus;
2085 mData.mRC = sessionRc;
2086
2087 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
2088 HRESULT hr = errorInfo.createObject();
2089 ComAssertComRC(hr);
2090 int rc2 = errorInfo->initEx(VBOX_E_IPRT_ERROR, sessionRc,
2091 COM_IIDOF(IGuestSession), getComponentName(),
2092 guestErrorToString(sessionRc));
2093 AssertRC(rc2);
2094
2095 fireGuestSessionStateChangedEvent(mEventSource, this,
2096 mData.mSession.mID, sessionStatus, errorInfo);
2097 }
2098
2099 return VINF_SUCCESS;
2100}
2101
2102int GuestSession::signalWaiters(GuestSessionWaitResult_T enmWaitResult, int rc /*= VINF_SUCCESS */)
2103{
2104 /*LogFlowThisFunc(("enmWaitResult=%d, rc=%Rrc, mWaitCount=%RU32, mWaitEvent=%p\n",
2105 enmWaitResult, rc, mData.mWaitCount, mData.mWaitEvent));*/
2106
2107 /* Note: No write locking here -- already done in the caller. */
2108
2109 int vrc = VINF_SUCCESS;
2110 /*if (mData.mWaitEvent)
2111 vrc = mData.mWaitEvent->Signal(enmWaitResult, rc);*/
2112 LogFlowFuncLeaveRC(vrc);
2113 return vrc;
2114}
2115
2116int GuestSession::startTaskAsync(const Utf8Str &strTaskDesc,
2117 GuestSessionTask *pTask, ComObjPtr<Progress> &pProgress)
2118{
2119 LogFlowThisFunc(("strTaskDesc=%s, pTask=%p\n", strTaskDesc.c_str(), pTask));
2120
2121 AssertPtrReturn(pTask, VERR_INVALID_POINTER);
2122
2123 /* Create the progress object. */
2124 HRESULT hr = pProgress.createObject();
2125 if (FAILED(hr))
2126 return VERR_COM_UNEXPECTED;
2127
2128 hr = pProgress->init(static_cast<IGuestSession*>(this),
2129 Bstr(strTaskDesc).raw(),
2130 TRUE /* aCancelable */);
2131 if (FAILED(hr))
2132 return VERR_COM_UNEXPECTED;
2133
2134 /* Initialize our worker task. */
2135 std::auto_ptr<GuestSessionTask> task(pTask);
2136
2137 int rc = task->RunAsync(strTaskDesc, pProgress);
2138 if (RT_FAILURE(rc))
2139 return rc;
2140
2141 /* Don't destruct on success. */
2142 task.release();
2143
2144 LogFlowFuncLeaveRC(rc);
2145 return rc;
2146}
2147
2148/**
2149 * Queries/collects information prior to establishing a guest session.
2150 * This is necessary to know which guest control protocol version to use,
2151 * among other things (later).
2152 *
2153 * @return IPRT status code.
2154 */
2155int GuestSession::queryInfo(void)
2156{
2157 /*
2158 * Try querying the guest control protocol version running on the guest.
2159 * This is done using the Guest Additions version
2160 */
2161 ComObjPtr<Guest> pGuest = mParent;
2162 Assert(!pGuest.isNull());
2163
2164 uint32_t uVerAdditions = pGuest->getAdditionsVersion();
2165 uint32_t uVBoxMajor = VBOX_FULL_VERSION_GET_MAJOR(uVerAdditions);
2166 uint32_t uVBoxMinor = VBOX_FULL_VERSION_GET_MINOR(uVerAdditions);
2167
2168#ifdef DEBUG_andy
2169 /* Hardcode the to-used protocol version; nice for testing side effects. */
2170 mData.mProtocolVersion = 2;
2171#else
2172 mData.mProtocolVersion = (
2173 /* VBox 5.0 and up. */
2174 uVBoxMajor >= 5
2175 /* VBox 4.3 and up. */
2176 || (uVBoxMajor == 4 && uVBoxMinor >= 3))
2177 ? 2 /* Guest control 2.0. */
2178 : 1; /* Legacy guest control (VBox < 4.3). */
2179 /* Build revision is ignored. */
2180#endif
2181
2182 LogFlowThisFunc(("uVerAdditions=%RU32 (%RU32.%RU32), mProtocolVersion=%RU32\n",
2183 uVerAdditions, uVBoxMajor, uVBoxMinor, mData.mProtocolVersion));
2184
2185 /* Tell the user but don't bitch too often. */
2186 static short s_gctrlLegacyWarning = 0;
2187 if ( mData.mProtocolVersion < 2
2188 && s_gctrlLegacyWarning++ < 3) /** @todo Find a bit nicer text. */
2189 LogRel((tr("Warning: Guest Additions are older (%ld.%ld) than host capabilities for guest control, please upgrade them. Using protocol version %ld now\n"),
2190 uVBoxMajor, uVBoxMinor, mData.mProtocolVersion));
2191
2192 return VINF_SUCCESS;
2193}
2194
2195int GuestSession::waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, GuestSessionWaitResult_T &waitResult, int *pGuestRc)
2196{
2197 LogFlowThisFuncEnter();
2198
2199 AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
2200
2201 /*LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p, pGuestRc=%p\n",
2202 fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent, pGuestRc));*/
2203
2204 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2205
2206 /* Did some error occur before? Then skip waiting and return. */
2207 if (mData.mStatus == GuestSessionStatus_Error)
2208 {
2209 waitResult = GuestSessionWaitResult_Error;
2210 AssertMsg(RT_FAILURE(mData.mRC), ("No error rc (%Rrc) set when guest session indicated an error\n", mData.mRC));
2211 if (pGuestRc)
2212 *pGuestRc = mData.mRC; /* Return last set error. */
2213 return VERR_GSTCTL_GUEST_ERROR;
2214 }
2215
2216 /* Guest Additions < 4.3 don't support session handling, skip. */
2217 if (mData.mProtocolVersion < 2)
2218 {
2219 waitResult = GuestSessionWaitResult_WaitFlagNotSupported;
2220
2221 LogFlowThisFunc(("Installed Guest Additions don't support waiting for dedicated sessions, skipping\n"));
2222 return VINF_SUCCESS;
2223 }
2224
2225 waitResult = GuestSessionWaitResult_None;
2226 if (fWaitFlags & GuestSessionWaitForFlag_Terminate)
2227 {
2228 switch (mData.mStatus)
2229 {
2230 case GuestSessionStatus_Terminated:
2231 case GuestSessionStatus_Down:
2232 waitResult = GuestSessionWaitResult_Terminate;
2233 break;
2234
2235 case GuestSessionStatus_TimedOutKilled:
2236 case GuestSessionStatus_TimedOutAbnormally:
2237 waitResult = GuestSessionWaitResult_Timeout;
2238 break;
2239
2240 case GuestSessionStatus_Error:
2241 /* Handled above. */
2242 break;
2243
2244 case GuestSessionStatus_Started:
2245 waitResult = GuestSessionWaitResult_Start;
2246 break;
2247
2248 case GuestSessionStatus_Undefined:
2249 case GuestSessionStatus_Starting:
2250 /* Do the waiting below. */
2251 break;
2252
2253 default:
2254 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
2255 return VERR_NOT_IMPLEMENTED;
2256 }
2257 }
2258 else if (fWaitFlags & GuestSessionWaitForFlag_Start)
2259 {
2260 switch (mData.mStatus)
2261 {
2262 case GuestSessionStatus_Started:
2263 case GuestSessionStatus_Terminating:
2264 case GuestSessionStatus_Terminated:
2265 case GuestSessionStatus_Down:
2266 waitResult = GuestSessionWaitResult_Start;
2267 break;
2268
2269 case GuestSessionStatus_Error:
2270 waitResult = GuestSessionWaitResult_Error;
2271 break;
2272
2273 case GuestSessionStatus_TimedOutKilled:
2274 case GuestSessionStatus_TimedOutAbnormally:
2275 waitResult = GuestSessionWaitResult_Timeout;
2276 break;
2277
2278 case GuestSessionStatus_Undefined:
2279 case GuestSessionStatus_Starting:
2280 /* Do the waiting below. */
2281 break;
2282
2283 default:
2284 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
2285 return VERR_NOT_IMPLEMENTED;
2286 }
2287 }
2288
2289 LogFlowThisFunc(("sessionStatus=%RU32, sessionRc=%Rrc, waitResult=%RU32\n",
2290 mData.mStatus, mData.mRC, waitResult));
2291
2292 /* No waiting needed? Return immediately using the last set error. */
2293 if (waitResult != GuestSessionWaitResult_None)
2294 {
2295 if (pGuestRc)
2296 *pGuestRc = mData.mRC; /* Return last set error (if any). */
2297 return RT_SUCCESS(mData.mRC) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
2298 }
2299
2300 int vrc;
2301
2302 GuestWaitEvent *pEvent = NULL;
2303 GuestEventTypes eventTypes;
2304 try
2305 {
2306 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
2307
2308 vrc = registerWaitEvent(mData.mSession.mID, 0 /* Object ID */,
2309 eventTypes, &pEvent);
2310 }
2311 catch (std::bad_alloc)
2312 {
2313 vrc = VERR_NO_MEMORY;
2314 }
2315
2316 if (RT_FAILURE(vrc))
2317 return vrc;
2318
2319 alock.release(); /* Release lock before waiting. */
2320
2321 GuestSessionStatus_T sessionStatus;
2322 vrc = waitForStatusChange(pEvent, fWaitFlags,
2323 uTimeoutMS, &sessionStatus, pGuestRc);
2324 if (RT_SUCCESS(vrc))
2325 {
2326 switch (sessionStatus)
2327 {
2328 case GuestSessionStatus_Started:
2329 waitResult = GuestSessionWaitResult_Start;
2330 break;
2331
2332 case GuestSessionStatus_Terminated:
2333 waitResult = GuestSessionWaitResult_Terminate;
2334 break;
2335
2336 case GuestSessionStatus_TimedOutKilled:
2337 case GuestSessionStatus_TimedOutAbnormally:
2338 waitResult = GuestSessionWaitResult_Timeout;
2339 break;
2340
2341 case GuestSessionStatus_Down:
2342 waitResult = GuestSessionWaitResult_Terminate;
2343 break;
2344
2345 case GuestSessionStatus_Error:
2346 waitResult = GuestSessionWaitResult_Error;
2347 break;
2348
2349 default:
2350 waitResult = GuestSessionWaitResult_Status;
2351 break;
2352 }
2353 }
2354
2355 unregisterWaitEvent(pEvent);
2356
2357 LogFlowFuncLeaveRC(vrc);
2358 return vrc;
2359}
2360
2361int GuestSession::waitForStatusChange(GuestWaitEvent *pEvent, uint32_t fWaitFlags, uint32_t uTimeoutMS,
2362 GuestSessionStatus_T *pSessionStatus, int *pGuestRc)
2363{
2364 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
2365
2366 VBoxEventType_T evtType;
2367 ComPtr<IEvent> pIEvent;
2368 int vrc = waitForEvent(pEvent, uTimeoutMS,
2369 &evtType, pIEvent.asOutParam());
2370 if (RT_SUCCESS(vrc))
2371 {
2372 Assert(evtType == VBoxEventType_OnGuestSessionStateChanged);
2373
2374 ComPtr<IGuestSessionStateChangedEvent> pChangedEvent = pIEvent;
2375 Assert(!pChangedEvent.isNull());
2376
2377 GuestSessionStatus_T sessionStatus;
2378 pChangedEvent->COMGETTER(Status)(&sessionStatus);
2379 if (pSessionStatus)
2380 *pSessionStatus = sessionStatus;
2381
2382 ComPtr<IVirtualBoxErrorInfo> errorInfo;
2383 HRESULT hr = pChangedEvent->COMGETTER(Error)(errorInfo.asOutParam());
2384 ComAssertComRC(hr);
2385
2386 LONG lGuestRc;
2387 hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
2388 ComAssertComRC(hr);
2389 if (RT_FAILURE((int)lGuestRc))
2390 vrc = VERR_GSTCTL_GUEST_ERROR;
2391 if (pGuestRc)
2392 *pGuestRc = (int)lGuestRc;
2393
2394 LogFlowThisFunc(("Status changed event for session ID=%RU32, new status is: %RU32 (%Rrc)\n",
2395 mData.mSession.mID, sessionStatus,
2396 RT_SUCCESS((int)lGuestRc) ? VINF_SUCCESS : (int)lGuestRc));
2397 }
2398
2399 LogFlowFuncLeaveRC(vrc);
2400 return vrc;
2401}
2402
2403// implementation of public methods
2404/////////////////////////////////////////////////////////////////////////////
2405
2406STDMETHODIMP GuestSession::Close(void)
2407{
2408#ifndef VBOX_WITH_GUEST_CONTROL
2409 ReturnComNotImplemented();
2410#else
2411 LogFlowThisFuncEnter();
2412
2413 AutoCaller autoCaller(this);
2414 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2415
2416 /* Close session on guest. */
2417 int guestRc = VINF_SUCCESS;
2418 int rc = closeSession(0 /* Flags */, 30 * 1000 /* Timeout */,
2419 &guestRc);
2420 /* On failure don't return here, instead do all the cleanup
2421 * work first and then return an error. */
2422
2423 /* Remove ourselves from the session list. */
2424 int rc2 = mParent->sessionRemove(this);
2425 if (rc2 == VERR_NOT_FOUND) /* Not finding the session anymore isn't critical. */
2426 rc2 = VINF_SUCCESS;
2427
2428 if (RT_SUCCESS(rc))
2429 rc = rc2;
2430
2431 LogFlowThisFunc(("Returning rc=%Rrc, guestRc=%Rrc\n",
2432 rc, guestRc));
2433 if (RT_FAILURE(rc))
2434 {
2435 if (rc == VERR_GSTCTL_GUEST_ERROR)
2436 return GuestSession::setErrorExternal(this, guestRc);
2437
2438 return setError(VBOX_E_IPRT_ERROR,
2439 tr("Closing guest session failed with %Rrc"), rc);
2440 }
2441
2442 return S_OK;
2443#endif /* VBOX_WITH_GUEST_CONTROL */
2444}
2445
2446STDMETHODIMP GuestSession::CopyFrom(IN_BSTR aSource, IN_BSTR aDest, ComSafeArrayIn(CopyFileFlag_T, aFlags), IProgress **aProgress)
2447{
2448#ifndef VBOX_WITH_GUEST_CONTROL
2449 ReturnComNotImplemented();
2450#else
2451 CheckComArgStrNotEmptyOrNull(aSource);
2452 CheckComArgStrNotEmptyOrNull(aDest);
2453 CheckComArgOutPointerValid(aProgress);
2454
2455 LogFlowThisFuncEnter();
2456
2457 if (RT_UNLIKELY((aSource) == NULL || *(aSource) == '\0'))
2458 return setError(E_INVALIDARG, tr("No source specified"));
2459 if (RT_UNLIKELY((aDest) == NULL || *(aDest) == '\0'))
2460 return setError(E_INVALIDARG, tr("No destination specified"));
2461
2462 AutoCaller autoCaller(this);
2463 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2464
2465 uint32_t fFlags = CopyFileFlag_None;
2466 if (aFlags)
2467 {
2468 com::SafeArray<CopyFileFlag_T> flags(ComSafeArrayInArg(aFlags));
2469 for (size_t i = 0; i < flags.size(); i++)
2470 fFlags |= flags[i];
2471 }
2472
2473 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2474
2475 HRESULT hr = S_OK;
2476
2477 try
2478 {
2479 ComObjPtr<Progress> pProgress;
2480 SessionTaskCopyFrom *pTask = new SessionTaskCopyFrom(this /* GuestSession */,
2481 Utf8Str(aSource), Utf8Str(aDest), fFlags);
2482 int rc = startTaskAsync(Utf8StrFmt(tr("Copying \"%ls\" from guest to \"%ls\" on the host"), aSource, aDest),
2483 pTask, pProgress);
2484 if (RT_SUCCESS(rc))
2485 {
2486 /* Return progress to the caller. */
2487 hr = pProgress.queryInterfaceTo(aProgress);
2488 }
2489 else
2490 hr = setError(VBOX_E_IPRT_ERROR,
2491 tr("Starting task for copying file \"%ls\" from guest to \"%ls\" on the host failed: %Rrc"), rc);
2492 }
2493 catch(std::bad_alloc &)
2494 {
2495 hr = E_OUTOFMEMORY;
2496 }
2497
2498 return hr;
2499#endif /* VBOX_WITH_GUEST_CONTROL */
2500}
2501
2502STDMETHODIMP GuestSession::CopyTo(IN_BSTR aSource, IN_BSTR aDest, ComSafeArrayIn(CopyFileFlag_T, aFlags), IProgress **aProgress)
2503{
2504#ifndef VBOX_WITH_GUEST_CONTROL
2505 ReturnComNotImplemented();
2506#else
2507 CheckComArgStrNotEmptyOrNull(aSource);
2508 CheckComArgStrNotEmptyOrNull(aDest);
2509 CheckComArgOutPointerValid(aProgress);
2510
2511 LogFlowThisFuncEnter();
2512
2513 if (RT_UNLIKELY((aSource) == NULL || *(aSource) == '\0'))
2514 return setError(E_INVALIDARG, tr("No source specified"));
2515 if (RT_UNLIKELY((aDest) == NULL || *(aDest) == '\0'))
2516 return setError(E_INVALIDARG, tr("No destination specified"));
2517
2518 AutoCaller autoCaller(this);
2519 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2520
2521 uint32_t fFlags = CopyFileFlag_None;
2522 if (aFlags)
2523 {
2524 com::SafeArray<CopyFileFlag_T> flags(ComSafeArrayInArg(aFlags));
2525 for (size_t i = 0; i < flags.size(); i++)
2526 fFlags |= flags[i];
2527 }
2528
2529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2530
2531 HRESULT hr = S_OK;
2532
2533 try
2534 {
2535 ComObjPtr<Progress> pProgress;
2536 SessionTaskCopyTo *pTask = new SessionTaskCopyTo(this /* GuestSession */,
2537 Utf8Str(aSource), Utf8Str(aDest), fFlags);
2538 AssertPtrReturn(pTask, E_OUTOFMEMORY);
2539 int rc = startTaskAsync(Utf8StrFmt(tr("Copying \"%ls\" from host to \"%ls\" on the guest"), aSource, aDest),
2540 pTask, pProgress);
2541 if (RT_SUCCESS(rc))
2542 {
2543 /* Return progress to the caller. */
2544 hr = pProgress.queryInterfaceTo(aProgress);
2545 }
2546 else
2547 hr = setError(VBOX_E_IPRT_ERROR,
2548 tr("Starting task for copying file \"%ls\" from host to \"%ls\" on the guest failed: %Rrc"), rc);
2549 }
2550 catch(std::bad_alloc &)
2551 {
2552 hr = E_OUTOFMEMORY;
2553 }
2554
2555 return hr;
2556#endif /* VBOX_WITH_GUEST_CONTROL */
2557}
2558
2559STDMETHODIMP GuestSession::DirectoryCreate(IN_BSTR aPath, ULONG aMode,
2560 ComSafeArrayIn(DirectoryCreateFlag_T, aFlags))
2561{
2562#ifndef VBOX_WITH_GUEST_CONTROL
2563 ReturnComNotImplemented();
2564#else
2565 LogFlowThisFuncEnter();
2566
2567 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2568 return setError(E_INVALIDARG, tr("No directory to create specified"));
2569
2570 AutoCaller autoCaller(this);
2571 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2572
2573 uint32_t fFlags = DirectoryCreateFlag_None;
2574 if (aFlags)
2575 {
2576 com::SafeArray<DirectoryCreateFlag_T> flags(ComSafeArrayInArg(aFlags));
2577 for (size_t i = 0; i < flags.size(); i++)
2578 fFlags |= flags[i];
2579
2580 if (fFlags)
2581 {
2582 if (!(fFlags & DirectoryCreateFlag_Parents))
2583 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), fFlags);
2584 }
2585 }
2586
2587 HRESULT hr = S_OK;
2588
2589 ComObjPtr <GuestDirectory> pDirectory; int guestRc;
2590 int rc = directoryCreateInternal(Utf8Str(aPath), (uint32_t)aMode, fFlags, &guestRc);
2591 if (RT_FAILURE(rc))
2592 {
2593 switch (rc)
2594 {
2595 case VERR_GSTCTL_GUEST_ERROR:
2596 /** @todo Handle VERR_NOT_EQUAL (meaning process exit code <> 0). */
2597 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Could not create directory"));
2598 break;
2599
2600 case VERR_INVALID_PARAMETER:
2601 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Invalid parameters given"));
2602 break;
2603
2604 case VERR_BROKEN_PIPE:
2605 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Unexpectedly aborted"));
2606 break;
2607
2608 default:
2609 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: %Rrc"), rc);
2610 break;
2611 }
2612 }
2613
2614 return hr;
2615#endif /* VBOX_WITH_GUEST_CONTROL */
2616}
2617
2618STDMETHODIMP GuestSession::DirectoryCreateTemp(IN_BSTR aTemplate, ULONG aMode, IN_BSTR aPath, BOOL aSecure, BSTR *aDirectory)
2619{
2620#ifndef VBOX_WITH_GUEST_CONTROL
2621 ReturnComNotImplemented();
2622#else
2623 LogFlowThisFuncEnter();
2624
2625 if (RT_UNLIKELY((aTemplate) == NULL || *(aTemplate) == '\0'))
2626 return setError(E_INVALIDARG, tr("No template specified"));
2627 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2628 return setError(E_INVALIDARG, tr("No directory name specified"));
2629 CheckComArgOutPointerValid(aDirectory);
2630
2631 AutoCaller autoCaller(this);
2632 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2633
2634 HRESULT hr = S_OK;
2635
2636 Utf8Str strName; int guestRc;
2637 int rc = objectCreateTempInternal(Utf8Str(aTemplate),
2638 Utf8Str(aPath),
2639 true /* Directory */, strName, &guestRc);
2640 if (RT_SUCCESS(rc))
2641 {
2642 strName.cloneTo(aDirectory);
2643 }
2644 else
2645 {
2646 switch (rc)
2647 {
2648 case VERR_GSTCTL_GUEST_ERROR:
2649 hr = GuestProcess::setErrorExternal(this, guestRc);
2650 break;
2651
2652 default:
2653 hr = setError(VBOX_E_IPRT_ERROR, tr("Temporary directory creation \"%s\" with template \"%s\" failed: %Rrc"),
2654 Utf8Str(aPath).c_str(), Utf8Str(aTemplate).c_str(), rc);
2655 break;
2656 }
2657 }
2658
2659 return hr;
2660#endif /* VBOX_WITH_GUEST_CONTROL */
2661}
2662
2663STDMETHODIMP GuestSession::DirectoryExists(IN_BSTR aPath, BOOL *aExists)
2664{
2665#ifndef VBOX_WITH_GUEST_CONTROL
2666 ReturnComNotImplemented();
2667#else
2668 LogFlowThisFuncEnter();
2669
2670 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2671 return setError(E_INVALIDARG, tr("No directory to check existence for specified"));
2672 CheckComArgOutPointerValid(aExists);
2673
2674 AutoCaller autoCaller(this);
2675 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2676
2677 HRESULT hr = S_OK;
2678
2679 GuestFsObjData objData; int guestRc;
2680 int rc = directoryQueryInfoInternal(Utf8Str(aPath), objData, &guestRc);
2681 if (RT_SUCCESS(rc))
2682 {
2683 *aExists = objData.mType == FsObjType_Directory;
2684 }
2685 else
2686 {
2687 switch (rc)
2688 {
2689 case VERR_GSTCTL_GUEST_ERROR:
2690 hr = GuestProcess::setErrorExternal(this, guestRc);
2691 break;
2692
2693 default:
2694 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying directory existence \"%s\" failed: %Rrc"),
2695 Utf8Str(aPath).c_str(), rc);
2696 break;
2697 }
2698 }
2699
2700 return hr;
2701#endif /* VBOX_WITH_GUEST_CONTROL */
2702}
2703
2704STDMETHODIMP GuestSession::DirectoryOpen(IN_BSTR aPath, IN_BSTR aFilter, ComSafeArrayIn(DirectoryOpenFlag_T, aFlags), IGuestDirectory **aDirectory)
2705{
2706#ifndef VBOX_WITH_GUEST_CONTROL
2707 ReturnComNotImplemented();
2708#else
2709 LogFlowThisFuncEnter();
2710
2711 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2712 return setError(E_INVALIDARG, tr("No directory to open specified"));
2713 if (RT_UNLIKELY((aFilter) != NULL && *(aFilter) != '\0'))
2714 return setError(E_INVALIDARG, tr("Directory filters are not implemented yet"));
2715 CheckComArgOutPointerValid(aDirectory);
2716
2717 AutoCaller autoCaller(this);
2718 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2719
2720 uint32_t fFlags = DirectoryOpenFlag_None;
2721 if (aFlags)
2722 {
2723 com::SafeArray<DirectoryOpenFlag_T> flags(ComSafeArrayInArg(aFlags));
2724 for (size_t i = 0; i < flags.size(); i++)
2725 fFlags |= flags[i];
2726
2727 if (fFlags)
2728 return setError(E_INVALIDARG, tr("Open flags (%#x) not implemented yet"), fFlags);
2729 }
2730
2731 HRESULT hr = S_OK;
2732
2733 GuestDirectoryOpenInfo openInfo;
2734 openInfo.mPath = Utf8Str(aPath);
2735 openInfo.mFilter = Utf8Str(aFilter);
2736 openInfo.mFlags = fFlags;
2737
2738 ComObjPtr <GuestDirectory> pDirectory; int guestRc;
2739 int rc = directoryOpenInternal(openInfo, pDirectory, &guestRc);
2740 if (RT_SUCCESS(rc))
2741 {
2742 /* Return directory object to the caller. */
2743 hr = pDirectory.queryInterfaceTo(aDirectory);
2744 }
2745 else
2746 {
2747 switch (rc)
2748 {
2749 case VERR_INVALID_PARAMETER:
2750 hr = setError(VBOX_E_IPRT_ERROR, tr("Opening directory \"%s\" failed; invalid parameters given",
2751 Utf8Str(aPath).c_str()));
2752 break;
2753
2754 case VERR_GSTCTL_GUEST_ERROR:
2755 hr = GuestDirectory::setErrorExternal(this, guestRc);
2756 break;
2757
2758 default:
2759 hr = setError(VBOX_E_IPRT_ERROR, tr("Opening directory \"%s\" failed: %Rrc"),
2760 Utf8Str(aPath).c_str(),rc);
2761 break;
2762 }
2763 }
2764
2765 return hr;
2766#endif /* VBOX_WITH_GUEST_CONTROL */
2767}
2768
2769STDMETHODIMP GuestSession::DirectoryQueryInfo(IN_BSTR aPath, IGuestFsObjInfo **aInfo)
2770{
2771#ifndef VBOX_WITH_GUEST_CONTROL
2772 ReturnComNotImplemented();
2773#else
2774 LogFlowThisFuncEnter();
2775
2776 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2777 return setError(E_INVALIDARG, tr("No directory to query information for specified"));
2778 CheckComArgOutPointerValid(aInfo);
2779
2780 AutoCaller autoCaller(this);
2781 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2782
2783 HRESULT hr = S_OK;
2784
2785 GuestFsObjData objData; int guestRc;
2786 int vrc = directoryQueryInfoInternal(Utf8Str(aPath), objData, &guestRc);
2787 if (RT_SUCCESS(vrc))
2788 {
2789 if (objData.mType == FsObjType_Directory)
2790 {
2791 ComObjPtr<GuestFsObjInfo> pFsObjInfo;
2792 hr = pFsObjInfo.createObject();
2793 if (FAILED(hr)) return hr;
2794
2795 vrc = pFsObjInfo->init(objData);
2796 if (RT_SUCCESS(vrc))
2797 {
2798 hr = pFsObjInfo.queryInterfaceTo(aInfo);
2799 if (FAILED(hr)) return hr;
2800 }
2801 }
2802 }
2803
2804 if (RT_FAILURE(vrc))
2805 {
2806 switch (vrc)
2807 {
2808 case VERR_GSTCTL_GUEST_ERROR:
2809 hr = GuestProcess::setErrorExternal(this, guestRc);
2810 break;
2811
2812 case VERR_NOT_A_DIRECTORY:
2813 hr = setError(VBOX_E_IPRT_ERROR, tr("Element \"%s\" exists but is not a directory",
2814 Utf8Str(aPath).c_str()));
2815 break;
2816
2817 default:
2818 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying directory information for \"%s\" failed: %Rrc"),
2819 Utf8Str(aPath).c_str(), vrc);
2820 break;
2821 }
2822 }
2823
2824 return hr;
2825#endif /* VBOX_WITH_GUEST_CONTROL */
2826}
2827
2828STDMETHODIMP GuestSession::DirectoryRemove(IN_BSTR aPath)
2829{
2830#ifndef VBOX_WITH_GUEST_CONTROL
2831 ReturnComNotImplemented();
2832#else
2833 LogFlowThisFuncEnter();
2834
2835 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2836 return setError(E_INVALIDARG, tr("No directory to remove specified"));
2837
2838 AutoCaller autoCaller(this);
2839 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2840
2841 HRESULT hr = isReadyExternal();
2842 if (FAILED(hr))
2843 return hr;
2844
2845 /* No flags; only remove the directory when empty. */
2846 uint32_t uFlags = 0;
2847
2848 int guestRc;
2849 int vrc = directoryRemoveInternal(Utf8Str(aPath), uFlags, &guestRc);
2850 if (RT_FAILURE(vrc))
2851 {
2852 switch (vrc)
2853 {
2854 case VERR_NOT_SUPPORTED:
2855 hr = setError(VBOX_E_IPRT_ERROR,
2856 tr("Handling removing guest directories not supported by installed Guest Additions"));
2857 break;
2858
2859 case VERR_GSTCTL_GUEST_ERROR:
2860 hr = GuestDirectory::setErrorExternal(this, guestRc);
2861 break;
2862
2863 default:
2864 hr = setError(VBOX_E_IPRT_ERROR, tr("Removing guest directory \"%s\" failed: %Rrc"),
2865 Utf8Str(aPath).c_str(), vrc);
2866 break;
2867 }
2868 }
2869
2870 return hr;
2871#endif /* VBOX_WITH_GUEST_CONTROL */
2872}
2873
2874STDMETHODIMP GuestSession::DirectoryRemoveRecursive(IN_BSTR aPath, ComSafeArrayIn(DirectoryRemoveRecFlag_T, aFlags), IProgress **aProgress)
2875{
2876#ifndef VBOX_WITH_GUEST_CONTROL
2877 ReturnComNotImplemented();
2878#else
2879 LogFlowThisFuncEnter();
2880
2881 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
2882 return setError(E_INVALIDARG, tr("No directory to remove recursively specified"));
2883
2884 AutoCaller autoCaller(this);
2885 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2886
2887 HRESULT hr = isReadyExternal();
2888 if (FAILED(hr))
2889 return hr;
2890
2891 ComObjPtr<Progress> pProgress;
2892 hr = pProgress.createObject();
2893 if (SUCCEEDED(hr))
2894 hr = pProgress->init(static_cast<IGuestSession *>(this),
2895 Bstr(tr("Removing guest directory")).raw(),
2896 TRUE /*aCancelable*/);
2897 if (FAILED(hr))
2898 return hr;
2899
2900 /* Note: At the moment we don't supply progress information while
2901 * deleting a guest directory recursively. So just complete
2902 * the progress object right now. */
2903 /** @todo Implement progress reporting on guest directory deletion! */
2904 hr = pProgress->notifyComplete(S_OK);
2905 if (FAILED(hr))
2906 return hr;
2907
2908 /* Remove the directory + all its contents. */
2909 uint32_t uFlags = DIRREMOVE_FLAG_RECURSIVE
2910 | DIRREMOVE_FLAG_CONTENT_AND_DIR;
2911 int guestRc;
2912 int vrc = directoryRemoveInternal(Utf8Str(aPath), uFlags, &guestRc);
2913 if (RT_FAILURE(vrc))
2914 {
2915 switch (vrc)
2916 {
2917 case VERR_NOT_SUPPORTED:
2918 hr = setError(VBOX_E_IPRT_ERROR,
2919 tr("Handling removing guest directories recursively not supported by installed Guest Additions"));
2920 break;
2921
2922 case VERR_GSTCTL_GUEST_ERROR:
2923 hr = GuestFile::setErrorExternal(this, guestRc);
2924 break;
2925
2926 default:
2927 hr = setError(VBOX_E_IPRT_ERROR, tr("Recursively removing guest directory \"%s\" failed: %Rrc"),
2928 Utf8Str(aPath).c_str(), vrc);
2929 break;
2930 }
2931 }
2932 else
2933 {
2934 pProgress.queryInterfaceTo(aProgress);
2935 }
2936
2937 return hr;
2938#endif /* VBOX_WITH_GUEST_CONTROL */
2939}
2940
2941STDMETHODIMP GuestSession::DirectoryRename(IN_BSTR aSource, IN_BSTR aDest, ComSafeArrayIn(PathRenameFlag_T, aFlags))
2942{
2943#ifndef VBOX_WITH_GUEST_CONTROL
2944 ReturnComNotImplemented();
2945#else
2946 LogFlowThisFuncEnter();
2947
2948 if (RT_UNLIKELY((aSource) == NULL || *(aSource) == '\0'))
2949 return setError(E_INVALIDARG, tr("No source directory to rename specified"));
2950
2951 if (RT_UNLIKELY((aDest) == NULL || *(aDest) == '\0'))
2952 return setError(E_INVALIDARG, tr("No destination directory to rename the source to specified"));
2953
2954 AutoCaller autoCaller(this);
2955 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2956
2957 HRESULT hr = isReadyExternal();
2958 if (FAILED(hr))
2959 return hr;
2960
2961 /* No flags; only remove the directory when empty. */
2962 uint32_t uFlags = 0;
2963
2964 int guestRc;
2965 int vrc = pathRenameInternal(Utf8Str(aSource), Utf8Str(aDest), uFlags, &guestRc);
2966 if (RT_FAILURE(vrc))
2967 {
2968 switch (vrc)
2969 {
2970 case VERR_NOT_SUPPORTED:
2971 hr = setError(VBOX_E_IPRT_ERROR,
2972 tr("Handling renaming guest directories not supported by installed Guest Additions"));
2973 break;
2974
2975 case VERR_GSTCTL_GUEST_ERROR:
2976 hr = setError(VBOX_E_IPRT_ERROR,
2977 tr("Renaming guest directory failed: %Rrc"), guestRc);
2978 break;
2979
2980 default:
2981 hr = setError(VBOX_E_IPRT_ERROR, tr("Renaming guest directory \"%s\" failed: %Rrc"),
2982 Utf8Str(aSource).c_str(), vrc);
2983 break;
2984 }
2985 }
2986
2987 return hr;
2988#endif /* VBOX_WITH_GUEST_CONTROL */
2989}
2990
2991STDMETHODIMP GuestSession::DirectorySetACL(IN_BSTR aPath, IN_BSTR aACL)
2992{
2993#ifndef VBOX_WITH_GUEST_CONTROL
2994 ReturnComNotImplemented();
2995#else
2996 LogFlowThisFuncEnter();
2997
2998 AutoCaller autoCaller(this);
2999 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3000
3001 ReturnComNotImplemented();
3002#endif /* VBOX_WITH_GUEST_CONTROL */
3003}
3004
3005STDMETHODIMP GuestSession::EnvironmentClear(void)
3006{
3007#ifndef VBOX_WITH_GUEST_CONTROL
3008 ReturnComNotImplemented();
3009#else
3010 LogFlowThisFuncEnter();
3011
3012 AutoCaller autoCaller(this);
3013 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3014
3015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3016
3017 mData.mEnvironment.Clear();
3018
3019 LogFlowThisFuncLeave();
3020 return S_OK;
3021#endif /* VBOX_WITH_GUEST_CONTROL */
3022}
3023
3024STDMETHODIMP GuestSession::EnvironmentGet(IN_BSTR aName, BSTR *aValue)
3025{
3026#ifndef VBOX_WITH_GUEST_CONTROL
3027 ReturnComNotImplemented();
3028#else
3029 LogFlowThisFuncEnter();
3030
3031 if (RT_UNLIKELY((aName) == NULL || *(aName) == '\0'))
3032 return setError(E_INVALIDARG, tr("No value name specified"));
3033
3034 CheckComArgOutPointerValid(aValue);
3035
3036 AutoCaller autoCaller(this);
3037 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3038
3039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3040
3041 Bstr strValue(mData.mEnvironment.Get(Utf8Str(aName)));
3042 strValue.cloneTo(aValue);
3043
3044 LogFlowThisFuncLeave();
3045 return S_OK;
3046#endif /* VBOX_WITH_GUEST_CONTROL */
3047}
3048
3049STDMETHODIMP GuestSession::EnvironmentSet(IN_BSTR aName, IN_BSTR aValue)
3050{
3051#ifndef VBOX_WITH_GUEST_CONTROL
3052 ReturnComNotImplemented();
3053#else
3054 LogFlowThisFuncEnter();
3055
3056 if (RT_UNLIKELY((aName) == NULL || *(aName) == '\0'))
3057 return setError(E_INVALIDARG, tr("No value name specified"));
3058
3059 AutoCaller autoCaller(this);
3060 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3061
3062 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3063
3064 int rc = mData.mEnvironment.Set(Utf8Str(aName), Utf8Str(aValue));
3065
3066 HRESULT hr = RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
3067 LogFlowFuncLeaveRC(hr);
3068 return hr;
3069#endif /* VBOX_WITH_GUEST_CONTROL */
3070}
3071
3072STDMETHODIMP GuestSession::EnvironmentUnset(IN_BSTR aName)
3073{
3074#ifndef VBOX_WITH_GUEST_CONTROL
3075 ReturnComNotImplemented();
3076#else
3077 LogFlowThisFuncEnter();
3078
3079 AutoCaller autoCaller(this);
3080 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3081
3082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3083
3084 mData.mEnvironment.Unset(Utf8Str(aName));
3085
3086 LogFlowThisFuncLeave();
3087 return S_OK;
3088#endif /* VBOX_WITH_GUEST_CONTROL */
3089}
3090
3091STDMETHODIMP GuestSession::FileCreateTemp(IN_BSTR aTemplate, ULONG aMode, IN_BSTR aPath, BOOL aSecure, IGuestFile **aFile)
3092{
3093#ifndef VBOX_WITH_GUEST_CONTROL
3094 ReturnComNotImplemented();
3095#else
3096 LogFlowThisFuncEnter();
3097
3098 AutoCaller autoCaller(this);
3099 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3100
3101 ReturnComNotImplemented();
3102#endif /* VBOX_WITH_GUEST_CONTROL */
3103}
3104
3105STDMETHODIMP GuestSession::FileExists(IN_BSTR aPath, BOOL *aExists)
3106{
3107#ifndef VBOX_WITH_GUEST_CONTROL
3108 ReturnComNotImplemented();
3109#else
3110 LogFlowThisFuncEnter();
3111
3112 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
3113 return setError(E_INVALIDARG, tr("No file to check existence for specified"));
3114 CheckComArgOutPointerValid(aExists);
3115
3116 AutoCaller autoCaller(this);
3117 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3118
3119 GuestFsObjData objData; int guestRc;
3120 int vrc = fileQueryInfoInternal(Utf8Str(aPath), objData, &guestRc);
3121 if (RT_SUCCESS(vrc))
3122 {
3123 *aExists = TRUE;
3124 return S_OK;
3125 }
3126
3127 HRESULT hr = S_OK;
3128
3129 switch (vrc)
3130 {
3131 case VERR_GSTCTL_GUEST_ERROR:
3132 hr = GuestProcess::setErrorExternal(this, guestRc);
3133 break;
3134
3135 case VERR_NOT_A_FILE:
3136 *aExists = FALSE;
3137 break;
3138
3139 default:
3140 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying file information for \"%s\" failed: %Rrc"),
3141 Utf8Str(aPath).c_str(), vrc);
3142 break;
3143 }
3144
3145 return hr;
3146#endif /* VBOX_WITH_GUEST_CONTROL */
3147}
3148
3149STDMETHODIMP GuestSession::FileRemove(IN_BSTR aPath)
3150{
3151#ifndef VBOX_WITH_GUEST_CONTROL
3152 ReturnComNotImplemented();
3153#else
3154 LogFlowThisFuncEnter();
3155
3156 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
3157 return setError(E_INVALIDARG, tr("No file to remove specified"));
3158
3159 AutoCaller autoCaller(this);
3160 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3161
3162 HRESULT hr = S_OK;
3163
3164 int guestRc;
3165 int vrc = fileRemoveInternal(Utf8Str(aPath), &guestRc);
3166 if (RT_FAILURE(vrc))
3167 {
3168 switch (vrc)
3169 {
3170 case VERR_GSTCTL_GUEST_ERROR:
3171 hr = GuestProcess::setErrorExternal(this, guestRc);
3172 break;
3173
3174 default:
3175 hr = setError(VBOX_E_IPRT_ERROR, tr("Removing file \"%s\" failed: %Rrc"),
3176 Utf8Str(aPath).c_str(), vrc);
3177 break;
3178 }
3179 }
3180
3181 return hr;
3182#endif /* VBOX_WITH_GUEST_CONTROL */
3183}
3184
3185STDMETHODIMP GuestSession::FileOpen(IN_BSTR aPath, IN_BSTR aOpenMode, IN_BSTR aDisposition, ULONG aCreationMode, IGuestFile **aFile)
3186{
3187#ifndef VBOX_WITH_GUEST_CONTROL
3188 ReturnComNotImplemented();
3189#else
3190 LogFlowThisFuncEnter();
3191
3192 Bstr strSharingMode = ""; /* Sharing mode is ignored. */
3193
3194 return FileOpenEx(aPath, aOpenMode, aDisposition, strSharingMode.raw(), aCreationMode,
3195 0 /* aOffset */, aFile);
3196#endif /* VBOX_WITH_GUEST_CONTROL */
3197}
3198
3199STDMETHODIMP GuestSession::FileOpenEx(IN_BSTR aPath, IN_BSTR aOpenMode, IN_BSTR aDisposition, IN_BSTR aSharingMode,
3200 ULONG aCreationMode, LONG64 aOffset, IGuestFile **aFile)
3201{
3202#ifndef VBOX_WITH_GUEST_CONTROL
3203 ReturnComNotImplemented();
3204#else
3205 LogFlowThisFuncEnter();
3206
3207 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
3208 return setError(E_INVALIDARG, tr("No file to open specified"));
3209 if (RT_UNLIKELY((aOpenMode) == NULL || *(aOpenMode) == '\0'))
3210 return setError(E_INVALIDARG, tr("No open mode specified"));
3211 if (RT_UNLIKELY((aDisposition) == NULL || *(aDisposition) == '\0'))
3212 return setError(E_INVALIDARG, tr("No disposition mode specified"));
3213 /* aSharingMode is optional. */
3214
3215 CheckComArgOutPointerValid(aFile);
3216
3217 AutoCaller autoCaller(this);
3218 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3219
3220 HRESULT hr = isReadyExternal();
3221 if (FAILED(hr))
3222 return hr;
3223
3224 /** @todo Validate creation mode. */
3225 uint32_t uCreationMode = 0;
3226
3227 GuestFileOpenInfo openInfo;
3228 openInfo.mFileName = Utf8Str(aPath);
3229 openInfo.mOpenMode = Utf8Str(aOpenMode);
3230 openInfo.mDisposition = Utf8Str(aDisposition);
3231 openInfo.mSharingMode = Utf8Str(aSharingMode);
3232 openInfo.mCreationMode = aCreationMode;
3233 openInfo.mInitialOffset = aOffset;
3234
3235 uint64_t uFlagsIgnored;
3236 int vrc = RTFileModeToFlagsEx(openInfo.mOpenMode.c_str(),
3237 openInfo.mDisposition.c_str(),
3238 openInfo.mSharingMode.c_str(),
3239 &uFlagsIgnored);
3240 if (RT_FAILURE(vrc))
3241 return setError(E_INVALIDARG, tr("Invalid open mode / disposition / sharing mode specified"));
3242
3243 ComObjPtr <GuestFile> pFile; int guestRc;
3244 vrc = fileOpenInternal(openInfo, pFile, &guestRc);
3245 if (RT_SUCCESS(vrc))
3246 {
3247 /* Return directory object to the caller. */
3248 hr = pFile.queryInterfaceTo(aFile);
3249 }
3250 else
3251 {
3252 switch (vrc)
3253 {
3254 case VERR_NOT_SUPPORTED:
3255 hr = setError(VBOX_E_IPRT_ERROR,
3256 tr("Handling guest files not supported by installed Guest Additions"));
3257 break;
3258
3259 case VERR_GSTCTL_GUEST_ERROR:
3260 hr = GuestFile::setErrorExternal(this, guestRc);
3261 break;
3262
3263 default:
3264 hr = setError(VBOX_E_IPRT_ERROR, tr("Opening guest file \"%s\" failed: %Rrc"),
3265 Utf8Str(aPath).c_str(), vrc);
3266 break;
3267 }
3268 }
3269
3270 return hr;
3271#endif /* VBOX_WITH_GUEST_CONTROL */
3272}
3273
3274STDMETHODIMP GuestSession::FileQueryInfo(IN_BSTR aPath, IGuestFsObjInfo **aInfo)
3275{
3276#ifndef VBOX_WITH_GUEST_CONTROL
3277 ReturnComNotImplemented();
3278#else
3279 LogFlowThisFuncEnter();
3280
3281 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
3282 return setError(E_INVALIDARG, tr("No file to query information for specified"));
3283 CheckComArgOutPointerValid(aInfo);
3284
3285 AutoCaller autoCaller(this);
3286 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3287
3288 HRESULT hr = S_OK;
3289
3290 GuestFsObjData objData; int guestRc;
3291 int vrc = fileQueryInfoInternal(Utf8Str(aPath), objData, &guestRc);
3292 if (RT_SUCCESS(vrc))
3293 {
3294 ComObjPtr<GuestFsObjInfo> pFsObjInfo;
3295 hr = pFsObjInfo.createObject();
3296 if (FAILED(hr)) return hr;
3297
3298 vrc = pFsObjInfo->init(objData);
3299 if (RT_SUCCESS(vrc))
3300 {
3301 hr = pFsObjInfo.queryInterfaceTo(aInfo);
3302 if (FAILED(hr)) return hr;
3303 }
3304 }
3305
3306 if (RT_FAILURE(vrc))
3307 {
3308 switch (vrc)
3309 {
3310 case VERR_GSTCTL_GUEST_ERROR:
3311 hr = GuestProcess::setErrorExternal(this, guestRc);
3312 break;
3313
3314 case VERR_NOT_A_FILE:
3315 hr = setError(VBOX_E_IPRT_ERROR, tr("Element exists but is not a file"));
3316 break;
3317
3318 default:
3319 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying file information failed: %Rrc"), vrc);
3320 break;
3321 }
3322 }
3323
3324 return hr;
3325#endif /* VBOX_WITH_GUEST_CONTROL */
3326}
3327
3328STDMETHODIMP GuestSession::FileQuerySize(IN_BSTR aPath, LONG64 *aSize)
3329{
3330#ifndef VBOX_WITH_GUEST_CONTROL
3331 ReturnComNotImplemented();
3332#else
3333 LogFlowThisFuncEnter();
3334
3335 if (RT_UNLIKELY((aPath) == NULL || *(aPath) == '\0'))
3336 return setError(E_INVALIDARG, tr("No file to query size for specified"));
3337 CheckComArgOutPointerValid(aSize);
3338
3339 AutoCaller autoCaller(this);
3340 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3341
3342 HRESULT hr = S_OK;
3343
3344 int64_t llSize; int guestRc;
3345 int vrc = fileQuerySizeInternal(Utf8Str(aPath), &llSize, &guestRc);
3346 if (RT_SUCCESS(vrc))
3347 {
3348 *aSize = llSize;
3349 }
3350 else
3351 {
3352 switch (vrc)
3353 {
3354 case VERR_GSTCTL_GUEST_ERROR:
3355 hr = GuestProcess::setErrorExternal(this, guestRc);
3356 break;
3357
3358 default:
3359 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying file size failed: %Rrc"), vrc);
3360 break;
3361 }
3362 }
3363
3364 return hr;
3365#endif /* VBOX_WITH_GUEST_CONTROL */
3366}
3367
3368STDMETHODIMP GuestSession::FileRename(IN_BSTR aSource, IN_BSTR aDest, ComSafeArrayIn(PathRenameFlag_T, aFlags))
3369{
3370#ifndef VBOX_WITH_GUEST_CONTROL
3371 ReturnComNotImplemented();
3372#else
3373 LogFlowThisFuncEnter();
3374
3375 if (RT_UNLIKELY((aSource) == NULL || *(aSource) == '\0'))
3376 return setError(E_INVALIDARG, tr("No source file to rename specified"));
3377
3378 if (RT_UNLIKELY((aDest) == NULL || *(aDest) == '\0'))
3379 return setError(E_INVALIDARG, tr("No destination file to rename the source to specified"));
3380
3381 AutoCaller autoCaller(this);
3382 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3383
3384 HRESULT hr = isReadyExternal();
3385 if (FAILED(hr))
3386 return hr;
3387
3388 /* No flags; only remove the directory when empty. */
3389 uint32_t uFlags = 0;
3390
3391 int guestRc;
3392 int vrc = pathRenameInternal(Utf8Str(aSource), Utf8Str(aDest), uFlags, &guestRc);
3393 if (RT_FAILURE(vrc))
3394 {
3395 switch (vrc)
3396 {
3397 case VERR_NOT_SUPPORTED:
3398 hr = setError(VBOX_E_IPRT_ERROR,
3399 tr("Handling renaming guest files not supported by installed Guest Additions"));
3400 break;
3401
3402 case VERR_GSTCTL_GUEST_ERROR:
3403 /** @todo Proper guestRc to text translation needed. */
3404 hr = setError(VBOX_E_IPRT_ERROR,
3405 tr("Renaming guest file failed: %Rrc"), guestRc);
3406 break;
3407
3408 default:
3409 hr = setError(VBOX_E_IPRT_ERROR, tr("Renaming guest file \"%s\" failed: %Rrc"),
3410 Utf8Str(aSource).c_str(), vrc);
3411 break;
3412 }
3413 }
3414
3415 return hr;
3416#endif /* VBOX_WITH_GUEST_CONTROL */
3417}
3418
3419STDMETHODIMP GuestSession::FileSetACL(IN_BSTR aPath, IN_BSTR aACL)
3420{
3421#ifndef VBOX_WITH_GUEST_CONTROL
3422 ReturnComNotImplemented();
3423#else
3424 LogFlowThisFuncEnter();
3425
3426 AutoCaller autoCaller(this);
3427 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3428
3429 ReturnComNotImplemented();
3430#endif /* VBOX_WITH_GUEST_CONTROL */
3431}
3432
3433STDMETHODIMP GuestSession::ProcessCreate(IN_BSTR aCommand, ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
3434 ComSafeArrayIn(ProcessCreateFlag_T, aFlags), ULONG aTimeoutMS, IGuestProcess **aProcess)
3435{
3436#ifndef VBOX_WITH_GUEST_CONTROL
3437 ReturnComNotImplemented();
3438#else
3439 LogFlowThisFuncEnter();
3440
3441 com::SafeArray<LONG> affinityIgnored;
3442
3443 return ProcessCreateEx(aCommand, ComSafeArrayInArg(aArguments), ComSafeArrayInArg(aEnvironment),
3444 ComSafeArrayInArg(aFlags), aTimeoutMS, ProcessPriority_Default, ComSafeArrayAsInParam(affinityIgnored), aProcess);
3445#endif /* VBOX_WITH_GUEST_CONTROL */
3446}
3447
3448STDMETHODIMP GuestSession::ProcessCreateEx(IN_BSTR aCommand, ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
3449 ComSafeArrayIn(ProcessCreateFlag_T, aFlags), ULONG aTimeoutMS,
3450 ProcessPriority_T aPriority, ComSafeArrayIn(LONG, aAffinity),
3451 IGuestProcess **aProcess)
3452{
3453#ifndef VBOX_WITH_GUEST_CONTROL
3454 ReturnComNotImplemented();
3455#else
3456 LogFlowThisFuncEnter();
3457
3458 if (RT_UNLIKELY((aCommand) == NULL || *(aCommand) == '\0'))
3459 return setError(E_INVALIDARG, tr("No command to execute specified"));
3460 CheckComArgOutPointerValid(aProcess);
3461
3462 AutoCaller autoCaller(this);
3463 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3464
3465 HRESULT hr = isReadyExternal();
3466 if (FAILED(hr))
3467 return hr;
3468
3469 GuestProcessStartupInfo procInfo;
3470 procInfo.mCommand = Utf8Str(aCommand);
3471
3472 if (aArguments)
3473 {
3474 com::SafeArray<IN_BSTR> arguments(ComSafeArrayInArg(aArguments));
3475 for (size_t i = 0; i < arguments.size(); i++)
3476 procInfo.mArguments.push_back(Utf8Str(arguments[i]));
3477 }
3478
3479 int rc = VINF_SUCCESS;
3480
3481 /*
3482 * Create the process environment:
3483 * - Apply the session environment in a first step, and
3484 * - Apply environment variables specified by this call to
3485 * have the chance of overwriting/deleting session entries.
3486 */
3487 procInfo.mEnvironment = mData.mEnvironment; /* Apply original session environment. */
3488
3489 if (aEnvironment)
3490 {
3491 com::SafeArray<IN_BSTR> environment(ComSafeArrayInArg(aEnvironment));
3492 for (size_t i = 0; i < environment.size() && RT_SUCCESS(rc); i++)
3493 rc = procInfo.mEnvironment.Set(Utf8Str(environment[i]));
3494 }
3495
3496 if (RT_SUCCESS(rc))
3497 {
3498 if (aFlags)
3499 {
3500 com::SafeArray<ProcessCreateFlag_T> flags(ComSafeArrayInArg(aFlags));
3501 for (size_t i = 0; i < flags.size(); i++)
3502 procInfo.mFlags |= flags[i];
3503 }
3504
3505 procInfo.mTimeoutMS = aTimeoutMS;
3506
3507 if (aAffinity)
3508 {
3509 com::SafeArray<LONG> affinity(ComSafeArrayInArg(aAffinity));
3510 for (size_t i = 0; i < affinity.size(); i++)
3511 {
3512 if (affinity[i])
3513 procInfo.mAffinity |= (uint64_t)1 << i;
3514 }
3515 }
3516
3517 procInfo.mPriority = aPriority;
3518
3519 ComObjPtr<GuestProcess> pProcess;
3520 rc = processCreateExInteral(procInfo, pProcess);
3521 if (RT_SUCCESS(rc))
3522 {
3523 /* Return guest session to the caller. */
3524 HRESULT hr2 = pProcess.queryInterfaceTo(aProcess);
3525 if (FAILED(hr2))
3526 rc = VERR_COM_OBJECT_NOT_FOUND;
3527
3528 if (RT_SUCCESS(rc))
3529 rc = pProcess->startProcessAsync();
3530 }
3531 }
3532
3533 if (RT_FAILURE(rc))
3534 {
3535 switch (rc)
3536 {
3537 case VERR_MAX_PROCS_REACHED:
3538 hr = setError(VBOX_E_IPRT_ERROR, tr("Maximum number of concurrent guest processes per session (%ld) reached"),
3539 VBOX_GUESTCTRL_MAX_OBJECTS);
3540 break;
3541
3542 /** @todo Add more errors here. */
3543
3544 default:
3545 hr = setError(VBOX_E_IPRT_ERROR, tr("Could not create guest process, rc=%Rrc"), rc);
3546 break;
3547 }
3548 }
3549
3550 LogFlowFuncLeaveRC(rc);
3551 return hr;
3552#endif /* VBOX_WITH_GUEST_CONTROL */
3553}
3554
3555STDMETHODIMP GuestSession::ProcessGet(ULONG aPID, IGuestProcess **aProcess)
3556{
3557#ifndef VBOX_WITH_GUEST_CONTROL
3558 ReturnComNotImplemented();
3559#else
3560 LogFlowThisFunc(("PID=%RU32\n", aPID));
3561
3562 CheckComArgOutPointerValid(aProcess);
3563 if (aPID == 0)
3564 return setError(E_INVALIDARG, tr("No valid process ID (PID) specified"));
3565
3566 AutoCaller autoCaller(this);
3567 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3568
3569 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3570
3571 HRESULT hr = S_OK;
3572
3573 ComObjPtr<GuestProcess> pProcess;
3574 int rc = processGetByPID(aPID, &pProcess);
3575 if (RT_FAILURE(rc))
3576 hr = setError(E_INVALIDARG, tr("No process with PID %RU32 found"), aPID);
3577
3578 /* This will set (*aProcess) to NULL if pProgress is NULL. */
3579 HRESULT hr2 = pProcess.queryInterfaceTo(aProcess);
3580 if (SUCCEEDED(hr))
3581 hr = hr2;
3582
3583 LogFlowThisFunc(("aProcess=%p, hr=%Rhrc\n", *aProcess, hr));
3584 return hr;
3585#endif /* VBOX_WITH_GUEST_CONTROL */
3586}
3587
3588STDMETHODIMP GuestSession::SymlinkCreate(IN_BSTR aSource, IN_BSTR aTarget, SymlinkType_T aType)
3589{
3590#ifndef VBOX_WITH_GUEST_CONTROL
3591 ReturnComNotImplemented();
3592#else
3593 LogFlowThisFuncEnter();
3594
3595 AutoCaller autoCaller(this);
3596 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3597
3598 ReturnComNotImplemented();
3599#endif /* VBOX_WITH_GUEST_CONTROL */
3600}
3601
3602STDMETHODIMP GuestSession::SymlinkExists(IN_BSTR aSymlink, BOOL *aExists)
3603{
3604#ifndef VBOX_WITH_GUEST_CONTROL
3605 ReturnComNotImplemented();
3606#else
3607 LogFlowThisFuncEnter();
3608
3609 AutoCaller autoCaller(this);
3610 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3611
3612 ReturnComNotImplemented();
3613#endif /* VBOX_WITH_GUEST_CONTROL */
3614}
3615
3616STDMETHODIMP GuestSession::SymlinkRead(IN_BSTR aSymlink, ComSafeArrayIn(SymlinkReadFlag_T, aFlags), BSTR *aTarget)
3617{
3618#ifndef VBOX_WITH_GUEST_CONTROL
3619 ReturnComNotImplemented();
3620#else
3621 LogFlowThisFuncEnter();
3622
3623 AutoCaller autoCaller(this);
3624 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3625
3626 ReturnComNotImplemented();
3627#endif /* VBOX_WITH_GUEST_CONTROL */
3628}
3629
3630STDMETHODIMP GuestSession::SymlinkRemoveDirectory(IN_BSTR aPath)
3631{
3632#ifndef VBOX_WITH_GUEST_CONTROL
3633 ReturnComNotImplemented();
3634#else
3635 LogFlowThisFuncEnter();
3636
3637 AutoCaller autoCaller(this);
3638 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3639
3640 ReturnComNotImplemented();
3641#endif /* VBOX_WITH_GUEST_CONTROL */
3642}
3643
3644STDMETHODIMP GuestSession::SymlinkRemoveFile(IN_BSTR aFile)
3645{
3646#ifndef VBOX_WITH_GUEST_CONTROL
3647 ReturnComNotImplemented();
3648#else
3649 LogFlowThisFuncEnter();
3650
3651 AutoCaller autoCaller(this);
3652 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3653
3654 ReturnComNotImplemented();
3655#endif /* VBOX_WITH_GUEST_CONTROL */
3656}
3657
3658STDMETHODIMP GuestSession::WaitFor(ULONG aWaitFlags, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
3659{
3660#ifndef VBOX_WITH_GUEST_CONTROL
3661 ReturnComNotImplemented();
3662#else
3663 LogFlowThisFuncEnter();
3664
3665 CheckComArgOutPointerValid(aReason);
3666
3667 AutoCaller autoCaller(this);
3668 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3669
3670 /*
3671 * Note: Do not hold any locks here while waiting!
3672 */
3673 HRESULT hr = S_OK;
3674
3675 int guestRc; GuestSessionWaitResult_T waitResult;
3676 int vrc = waitFor(aWaitFlags, aTimeoutMS, waitResult, &guestRc);
3677 if (RT_SUCCESS(vrc))
3678 {
3679 *aReason = waitResult;
3680 }
3681 else
3682 {
3683 switch (vrc)
3684 {
3685 case VERR_GSTCTL_GUEST_ERROR:
3686 hr = GuestSession::setErrorExternal(this, guestRc);
3687 break;
3688
3689 case VERR_TIMEOUT:
3690 *aReason = GuestSessionWaitResult_Timeout;
3691 break;
3692
3693 default:
3694 {
3695 const char *pszSessionName = mData.mSession.mName.c_str();
3696 hr = setError(VBOX_E_IPRT_ERROR,
3697 tr("Waiting for guest session \"%s\" failed: %Rrc"),
3698 pszSessionName ? pszSessionName : tr("Unnamed"), vrc);
3699 break;
3700 }
3701 }
3702 }
3703
3704 LogFlowFuncLeaveRC(vrc);
3705 return hr;
3706#endif /* VBOX_WITH_GUEST_CONTROL */
3707}
3708
3709STDMETHODIMP GuestSession::WaitForArray(ComSafeArrayIn(GuestSessionWaitForFlag_T, aFlags), ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
3710{
3711#ifndef VBOX_WITH_GUEST_CONTROL
3712 ReturnComNotImplemented();
3713#else
3714 LogFlowThisFuncEnter();
3715
3716 CheckComArgOutPointerValid(aReason);
3717
3718 AutoCaller autoCaller(this);
3719 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3720
3721 /*
3722 * Note: Do not hold any locks here while waiting!
3723 */
3724 uint32_t fWaitFor = GuestSessionWaitForFlag_None;
3725 com::SafeArray<GuestSessionWaitForFlag_T> flags(ComSafeArrayInArg(aFlags));
3726 for (size_t i = 0; i < flags.size(); i++)
3727 fWaitFor |= flags[i];
3728
3729 return WaitFor(fWaitFor, aTimeoutMS, aReason);
3730#endif /* VBOX_WITH_GUEST_CONTROL */
3731}
3732
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