VirtualBox

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

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

Main/GuestSession: fixed a few format specifiers

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