VirtualBox

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

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

Main/GuestCtrl: Reference counting bugfixes, handle more directoryCreate errors.

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

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