VirtualBox

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

Last change on this file since 71619 was 71560, checked in by vboxsync, 7 years ago

Guest Control/Main: Added more AutoCaller checks for public APIs.

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

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