VirtualBox

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

最後變更 在這個檔案從92916是 92916,由 vboxsync 提交於 3 年 前

Guest Control/Main: Added lots of missing docs [Doxygen fixes].

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

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