VirtualBox

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

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

GuestCtrl: Main/VBoxManage: More checks for object pointers, minor logging adjustments.

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

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