VirtualBox

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

Last change on this file since 71667 was 71648, checked in by vboxsync, 7 years ago

Guest Control/Main: Added a dedicated header file for guest session tasks.

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