VirtualBox

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

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

Guest Control: Be more specific when reporting session termination statuses.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 154.0 KB
 
1/* $Id: GuestSessionImpl.cpp 92987 2021-12-16 15:50:22Z 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 LogRel(("Guest Control: Session #%RU32 was terminated normally with exit code %#x\n",
2044 mData.mSession.mID, dataCb.uResult));
2045 sessionStatus = GuestSessionStatus_Terminated;
2046 break;
2047
2048 case GUEST_SESSION_NOTIFYTYPE_TEA:
2049 LogRel(("Guest Control: Session #%RU32 was terminated abnormally\n", mData.mSession.mID));
2050 sessionStatus = GuestSessionStatus_Terminated;
2051 /* dataCb.uResult is undefined. */
2052 break;
2053
2054 case GUEST_SESSION_NOTIFYTYPE_TES:
2055 LogRel(("Guest Control: Session #%RU32 was terminated via signal %#x\n", mData.mSession.mID, dataCb.uResult));
2056 sessionStatus = GuestSessionStatus_Terminated;
2057 break;
2058
2059 case GUEST_SESSION_NOTIFYTYPE_TOK:
2060 sessionStatus = GuestSessionStatus_TimedOutKilled;
2061 LogRel(("Guest Control: Session #%RU32 timed out and was killed\n", mData.mSession.mID));
2062 break;
2063
2064 case GUEST_SESSION_NOTIFYTYPE_TOA:
2065 sessionStatus = GuestSessionStatus_TimedOutAbnormally;
2066 LogRel(("Guest Control: Session #%RU32 timed out and was not killed successfully\n", mData.mSession.mID));
2067 break;
2068
2069 case GUEST_SESSION_NOTIFYTYPE_DWN:
2070 sessionStatus = GuestSessionStatus_Down;
2071 LogRel(("Guest Control: Session #%RU32 got killed as guest service/OS is down\n", mData.mSession.mID));
2072 break;
2073
2074 case GUEST_SESSION_NOTIFYTYPE_UNDEFINED:
2075 default:
2076 vrc = VERR_NOT_SUPPORTED;
2077 break;
2078 }
2079
2080 /* Leave the lock, as i_setSessionStatus() below will require a write lock for actually
2081 * committing the session state. */
2082 alock.release();
2083
2084 if (RT_SUCCESS(vrc))
2085 {
2086 if (RT_FAILURE(rcGuest))
2087 sessionStatus = GuestSessionStatus_Error;
2088 }
2089
2090 /* Set the session status. */
2091 if (RT_SUCCESS(vrc))
2092 vrc = i_setSessionStatus(sessionStatus, rcGuest);
2093
2094 LogFlowThisFunc(("ID=%RU32, rcGuest=%Rrc\n", mData.mSession.mID, rcGuest));
2095
2096 LogFlowFuncLeaveRC(vrc);
2097 return vrc;
2098}
2099
2100/**
2101 * Returns the path separation style used on the guest.
2102 *
2103 * @returns Separation style used on the guest.
2104 */
2105PathStyle_T GuestSession::i_getPathStyle(void)
2106{
2107 PathStyle_T enmPathStyle;
2108
2109 VBOXOSTYPE enmOsType = mParent->i_getGuestOSType();
2110 if (enmOsType < VBOXOSTYPE_DOS)
2111 {
2112 LogFlowFunc(("returns PathStyle_Unknown\n"));
2113 enmPathStyle = PathStyle_Unknown;
2114 }
2115 else if (enmOsType < VBOXOSTYPE_Linux)
2116 {
2117 LogFlowFunc(("returns PathStyle_DOS\n"));
2118 enmPathStyle = PathStyle_DOS;
2119 }
2120 else
2121 {
2122 LogFlowFunc(("returns PathStyle_UNIX\n"));
2123 enmPathStyle = PathStyle_UNIX;
2124 }
2125
2126 return enmPathStyle;
2127}
2128
2129/**
2130 * Starts the guest session on the guest.
2131 *
2132 * @returns VBox status code.
2133 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
2134 * was returned. Optional.
2135 */
2136int GuestSession::i_startSession(int *prcGuest)
2137{
2138 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2139
2140 LogFlowThisFunc(("mID=%RU32, mName=%s, uProtocolVersion=%RU32, openFlags=%x, openTimeoutMS=%RU32\n",
2141 mData.mSession.mID, mData.mSession.mName.c_str(), mData.mProtocolVersion,
2142 mData.mSession.mOpenFlags, mData.mSession.mOpenTimeoutMS));
2143
2144 /* Guest Additions < 4.3 don't support opening dedicated
2145 guest sessions. Simply return success here. */
2146 if (mData.mProtocolVersion < 2)
2147 {
2148 mData.mStatus = GuestSessionStatus_Started;
2149
2150 LogFlowThisFunc(("Installed Guest Additions don't support opening dedicated sessions, skipping\n"));
2151 return VINF_SUCCESS;
2152 }
2153
2154 if (mData.mStatus != GuestSessionStatus_Undefined)
2155 return VINF_SUCCESS;
2156
2157 /** @todo mData.mSession.uFlags validation. */
2158
2159 /* Set current session status. */
2160 mData.mStatus = GuestSessionStatus_Starting;
2161 mData.mRC = VINF_SUCCESS; /* Clear previous error, if any. */
2162
2163 int vrc;
2164
2165 GuestWaitEvent *pEvent = NULL;
2166 GuestEventTypes eventTypes;
2167 try
2168 {
2169 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
2170
2171 vrc = registerWaitEventEx(mData.mSession.mID, mData.mObjectID, eventTypes, &pEvent);
2172 }
2173 catch (std::bad_alloc &)
2174 {
2175 vrc = VERR_NO_MEMORY;
2176 }
2177
2178 if (RT_FAILURE(vrc))
2179 return vrc;
2180
2181 VBOXHGCMSVCPARM paParms[8];
2182
2183 int i = 0;
2184 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2185 HGCMSvcSetU32(&paParms[i++], mData.mProtocolVersion);
2186 HGCMSvcSetPv(&paParms[i++], (void*)mData.mCredentials.mUser.c_str(),
2187 (ULONG)mData.mCredentials.mUser.length() + 1);
2188 HGCMSvcSetPv(&paParms[i++], (void*)mData.mCredentials.mPassword.c_str(),
2189 (ULONG)mData.mCredentials.mPassword.length() + 1);
2190 HGCMSvcSetPv(&paParms[i++], (void*)mData.mCredentials.mDomain.c_str(),
2191 (ULONG)mData.mCredentials.mDomain.length() + 1);
2192 HGCMSvcSetU32(&paParms[i++], mData.mSession.mOpenFlags);
2193
2194 alock.release(); /* Drop write lock before sending. */
2195
2196 vrc = i_sendMessage(HOST_MSG_SESSION_CREATE, i, paParms, VBOX_GUESTCTRL_DST_ROOT_SVC);
2197 if (RT_SUCCESS(vrc))
2198 {
2199 vrc = i_waitForStatusChange(pEvent, GuestSessionWaitForFlag_Start,
2200 30 * 1000 /* 30s timeout */,
2201 NULL /* Session status */, prcGuest);
2202 }
2203 else
2204 {
2205 /*
2206 * Unable to start guest session - update its current state.
2207 * Since there is no (official API) way to recover a failed guest session
2208 * this also marks the end state. Internally just calling this
2209 * same function again will work though.
2210 */
2211 mData.mStatus = GuestSessionStatus_Error;
2212 mData.mRC = vrc;
2213 }
2214
2215 unregisterWaitEvent(pEvent);
2216
2217 LogFlowFuncLeaveRC(vrc);
2218 return vrc;
2219}
2220
2221/**
2222 * Starts the guest session asynchronously in a separate worker thread.
2223 *
2224 * @returns IPRT status code.
2225 */
2226int GuestSession::i_startSessionAsync(void)
2227{
2228 LogFlowThisFuncEnter();
2229
2230 /* Create task: */
2231 GuestSessionTaskInternalStart *pTask = NULL;
2232 try
2233 {
2234 pTask = new GuestSessionTaskInternalStart(this);
2235 }
2236 catch (std::bad_alloc &)
2237 {
2238 return VERR_NO_MEMORY;
2239 }
2240 if (pTask->isOk())
2241 {
2242 /* Kick off the thread: */
2243 HRESULT hrc = pTask->createThread();
2244 pTask = NULL; /* Not valid anymore, not even on failure! */
2245 if (SUCCEEDED(hrc))
2246 {
2247 LogFlowFuncLeaveRC(VINF_SUCCESS);
2248 return VINF_SUCCESS;
2249 }
2250 LogFlow(("GuestSession: Failed to create thread for GuestSessionTaskInternalOpen task.\n"));
2251 }
2252 else
2253 LogFlow(("GuestSession: GuestSessionTaskInternalStart creation failed: %Rhrc.\n", pTask->rc()));
2254 LogFlowFuncLeaveRC(VERR_GENERAL_FAILURE);
2255 return VERR_GENERAL_FAILURE;
2256}
2257
2258/**
2259 * Static function to start a guest session asynchronously.
2260 *
2261 * @returns IPRT status code.
2262 * @param pTask Task object to use for starting the guest session.
2263 */
2264/* static */
2265int GuestSession::i_startSessionThreadTask(GuestSessionTaskInternalStart *pTask)
2266{
2267 LogFlowFunc(("pTask=%p\n", pTask));
2268 AssertPtr(pTask);
2269
2270 const ComObjPtr<GuestSession> pSession(pTask->Session());
2271 Assert(!pSession.isNull());
2272
2273 AutoCaller autoCaller(pSession);
2274 if (FAILED(autoCaller.rc()))
2275 return VERR_COM_INVALID_OBJECT_STATE;
2276
2277 int vrc = pSession->i_startSession(NULL /* Guest rc, ignored */);
2278 /* Nothing to do here anymore. */
2279
2280 LogFlowFuncLeaveRC(vrc);
2281 return vrc;
2282}
2283
2284/**
2285 * Registers an object with the session, i.e. allocates an object ID.
2286 *
2287 * @return VBox status code.
2288 * @retval VERR_GSTCTL_MAX_OBJECTS_REACHED if the maximum of concurrent objects
2289 * is reached.
2290 * @param pObject Guest object to register (weak pointer). Optional.
2291 * @param enmType Session object type to register.
2292 * @param pidObject Where to return the object ID on success. Optional.
2293 */
2294int GuestSession::i_objectRegister(GuestObject *pObject, SESSIONOBJECTTYPE enmType, uint32_t *pidObject)
2295{
2296 /* pObject can be NULL. */
2297 /* pidObject is optional. */
2298
2299 /*
2300 * Pick a random bit as starting point. If it's in use, search forward
2301 * for a free one, wrapping around. We've reserved both the zero'th and
2302 * max-1 IDs (see Data constructor).
2303 */
2304 uint32_t idObject = RTRandU32Ex(1, VBOX_GUESTCTRL_MAX_OBJECTS - 2);
2305 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2306 if (!ASMBitTestAndSet(&mData.bmObjectIds[0], idObject))
2307 { /* likely */ }
2308 else if (mData.mObjects.size() < VBOX_GUESTCTRL_MAX_OBJECTS - 2 /* First and last are not used */)
2309 {
2310 /* Forward search. */
2311 int iHit = ASMBitNextClear(&mData.bmObjectIds[0], VBOX_GUESTCTRL_MAX_OBJECTS, idObject);
2312 if (iHit < 0)
2313 iHit = ASMBitFirstClear(&mData.bmObjectIds[0], VBOX_GUESTCTRL_MAX_OBJECTS);
2314 AssertLogRelMsgReturn(iHit >= 0, ("object count: %#zu\n", mData.mObjects.size()), VERR_GSTCTL_MAX_CID_OBJECTS_REACHED);
2315 idObject = iHit;
2316 AssertLogRelMsgReturn(!ASMBitTestAndSet(&mData.bmObjectIds[0], idObject), ("idObject=%#x\n", idObject), VERR_INTERNAL_ERROR_2);
2317 }
2318 else
2319 {
2320 LogFunc(("Maximum number of objects reached (enmType=%RU32, %zu objects)\n", enmType, mData.mObjects.size()));
2321 return VERR_GSTCTL_MAX_CID_OBJECTS_REACHED;
2322 }
2323
2324 Log2Func(("enmType=%RU32 -> idObject=%RU32 (%zu objects)\n", enmType, idObject, mData.mObjects.size()));
2325
2326 try
2327 {
2328 mData.mObjects[idObject].pObject = pObject; /* Can be NULL. */
2329 mData.mObjects[idObject].enmType = enmType;
2330 mData.mObjects[idObject].msBirth = RTTimeMilliTS();
2331 }
2332 catch (std::bad_alloc &)
2333 {
2334 ASMBitClear(&mData.bmObjectIds[0], idObject);
2335 return VERR_NO_MEMORY;
2336 }
2337
2338 if (pidObject)
2339 *pidObject = idObject;
2340
2341 return VINF_SUCCESS;
2342}
2343
2344/**
2345 * Unregisters an object from the session objects list.
2346 *
2347 * @retval VINF_SUCCESS on success.
2348 * @retval VERR_NOT_FOUND if the object ID was not found.
2349 * @param idObject Object ID to unregister.
2350 */
2351int GuestSession::i_objectUnregister(uint32_t idObject)
2352{
2353 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2354
2355 int rc = VINF_SUCCESS;
2356 AssertMsgStmt(ASMBitTestAndClear(&mData.bmObjectIds, idObject), ("idObject=%#x\n", idObject), rc = VERR_NOT_FOUND);
2357
2358 SessionObjects::iterator ItObj = mData.mObjects.find(idObject);
2359 AssertMsgReturn(ItObj != mData.mObjects.end(), ("idObject=%#x\n", idObject), VERR_NOT_FOUND);
2360 mData.mObjects.erase(ItObj);
2361
2362 return rc;
2363}
2364
2365/**
2366 * Unregisters all objects from the session list.
2367 *
2368 * @returns VBox status code.
2369 */
2370int GuestSession::i_objectsUnregister(void)
2371{
2372 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2373
2374 LogFlowThisFunc(("Unregistering directories (%zu total)\n", mData.mDirectories.size()));
2375
2376 SessionDirectories::iterator itDirs;
2377 while ((itDirs = mData.mDirectories.begin()) != mData.mDirectories.end())
2378 {
2379 alock.release();
2380 i_directoryUnregister(itDirs->second);
2381 alock.acquire();
2382 }
2383
2384 Assert(mData.mDirectories.size() == 0);
2385 mData.mDirectories.clear();
2386
2387 LogFlowThisFunc(("Unregistering files (%zu total)\n", mData.mFiles.size()));
2388
2389 SessionFiles::iterator itFiles;
2390 while ((itFiles = mData.mFiles.begin()) != mData.mFiles.end())
2391 {
2392 alock.release();
2393 i_fileUnregister(itFiles->second);
2394 alock.acquire();
2395 }
2396
2397 Assert(mData.mFiles.size() == 0);
2398 mData.mFiles.clear();
2399
2400 LogFlowThisFunc(("Unregistering processes (%zu total)\n", mData.mProcesses.size()));
2401
2402 SessionProcesses::iterator itProcs;
2403 while ((itProcs = mData.mProcesses.begin()) != mData.mProcesses.end())
2404 {
2405 alock.release();
2406 i_processUnregister(itProcs->second);
2407 alock.acquire();
2408 }
2409
2410 Assert(mData.mProcesses.size() == 0);
2411 mData.mProcesses.clear();
2412
2413 return VINF_SUCCESS;
2414}
2415
2416/**
2417 * Notifies all registered objects about a guest session status change.
2418 *
2419 * @returns VBox status code.
2420 * @param enmSessionStatus Session status to notify objects about.
2421 */
2422int GuestSession::i_objectsNotifyAboutStatusChange(GuestSessionStatus_T enmSessionStatus)
2423{
2424 LogFlowThisFunc(("enmSessionStatus=%RU32\n", enmSessionStatus));
2425
2426 int vrc = VINF_SUCCESS;
2427
2428 SessionObjects::iterator itObjs = mData.mObjects.begin();
2429 while (itObjs != mData.mObjects.end())
2430 {
2431 GuestObject *pObj = itObjs->second.pObject;
2432 if (pObj) /* pObject can be NULL (weak pointer). */
2433 {
2434 int vrc2 = pObj->i_onSessionStatusChange(enmSessionStatus);
2435 if (RT_SUCCESS(vrc))
2436 vrc = vrc2;
2437
2438 /* If the session got terminated, make sure to cancel all wait events for
2439 * the current object. */
2440 if (i_isTerminated())
2441 pObj->cancelWaitEvents();
2442 }
2443
2444 ++itObjs;
2445 }
2446
2447 LogFlowFuncLeaveRC(vrc);
2448 return vrc;
2449}
2450
2451/**
2452 * Renames a path on the guest.
2453 *
2454 * @returns VBox status code.
2455 * @returns VERR_GSTCTL_GUEST_ERROR on received guest error.
2456 * @param strSource Source path on guest to rename.
2457 * @param strDest Destination path on guest to rename \a strSource to.
2458 * @param uFlags Renaming flags.
2459 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
2460 * was returned. Optional.
2461 * @note Takes the write lock.
2462 */
2463int GuestSession::i_pathRename(const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags, int *prcGuest)
2464{
2465 AssertReturn(!(uFlags & ~PATHRENAME_FLAG_VALID_MASK), VERR_INVALID_PARAMETER);
2466
2467 LogFlowThisFunc(("strSource=%s, strDest=%s, uFlags=0x%x\n",
2468 strSource.c_str(), strDest.c_str(), uFlags));
2469
2470 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2471
2472 GuestWaitEvent *pEvent = NULL;
2473 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2474 if (RT_FAILURE(vrc))
2475 return vrc;
2476
2477 /* Prepare HGCM call. */
2478 VBOXHGCMSVCPARM paParms[8];
2479 int i = 0;
2480 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2481 HGCMSvcSetPv(&paParms[i++], (void*)strSource.c_str(),
2482 (ULONG)strSource.length() + 1);
2483 HGCMSvcSetPv(&paParms[i++], (void*)strDest.c_str(),
2484 (ULONG)strDest.length() + 1);
2485 HGCMSvcSetU32(&paParms[i++], uFlags);
2486
2487 alock.release(); /* Drop write lock before sending. */
2488
2489 vrc = i_sendMessage(HOST_MSG_PATH_RENAME, i, paParms);
2490 if (RT_SUCCESS(vrc))
2491 {
2492 vrc = pEvent->Wait(30 * 1000);
2493 if ( vrc == VERR_GSTCTL_GUEST_ERROR
2494 && prcGuest)
2495 *prcGuest = pEvent->GuestResult();
2496 }
2497
2498 unregisterWaitEvent(pEvent);
2499
2500 LogFlowFuncLeaveRC(vrc);
2501 return vrc;
2502}
2503
2504/**
2505 * Returns the user's absolute documents path, if any.
2506 *
2507 * @return VBox status code.
2508 * @param strPath Where to store the user's document path.
2509 * @param prcGuest Guest rc, when returning VERR_GSTCTL_GUEST_ERROR.
2510 * Any other return code indicates some host side error.
2511 */
2512int GuestSession::i_pathUserDocuments(Utf8Str &strPath, int *prcGuest)
2513{
2514 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2515
2516 /** @todo Cache the user's document path? */
2517
2518 GuestWaitEvent *pEvent = NULL;
2519 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2520 if (RT_FAILURE(vrc))
2521 return vrc;
2522
2523 /* Prepare HGCM call. */
2524 VBOXHGCMSVCPARM paParms[2];
2525 int i = 0;
2526 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2527
2528 alock.release(); /* Drop write lock before sending. */
2529
2530 vrc = i_sendMessage(HOST_MSG_PATH_USER_DOCUMENTS, i, paParms);
2531 if (RT_SUCCESS(vrc))
2532 {
2533 vrc = pEvent->Wait(30 * 1000);
2534 if (RT_SUCCESS(vrc))
2535 {
2536 strPath = pEvent->Payload().ToString();
2537 }
2538 else
2539 {
2540 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2541 {
2542 if (prcGuest)
2543 *prcGuest = pEvent->GuestResult();
2544 }
2545 }
2546 }
2547
2548 unregisterWaitEvent(pEvent);
2549
2550 LogFlowFuncLeaveRC(vrc);
2551 return vrc;
2552}
2553
2554/**
2555 * Returns the user's absolute home path, if any.
2556 *
2557 * @return VBox status code.
2558 * @param strPath Where to store the user's home path.
2559 * @param prcGuest Guest rc, when returning VERR_GSTCTL_GUEST_ERROR.
2560 * Any other return code indicates some host side error.
2561 */
2562int GuestSession::i_pathUserHome(Utf8Str &strPath, int *prcGuest)
2563{
2564 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2565
2566 /** @todo Cache the user's home path? */
2567
2568 GuestWaitEvent *pEvent = NULL;
2569 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2570 if (RT_FAILURE(vrc))
2571 return vrc;
2572
2573 /* Prepare HGCM call. */
2574 VBOXHGCMSVCPARM paParms[2];
2575 int i = 0;
2576 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2577
2578 alock.release(); /* Drop write lock before sending. */
2579
2580 vrc = i_sendMessage(HOST_MSG_PATH_USER_HOME, i, paParms);
2581 if (RT_SUCCESS(vrc))
2582 {
2583 vrc = pEvent->Wait(30 * 1000);
2584 if (RT_SUCCESS(vrc))
2585 {
2586 strPath = pEvent->Payload().ToString();
2587 }
2588 else
2589 {
2590 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2591 {
2592 if (prcGuest)
2593 *prcGuest = pEvent->GuestResult();
2594 }
2595 }
2596 }
2597
2598 unregisterWaitEvent(pEvent);
2599
2600 LogFlowFuncLeaveRC(vrc);
2601 return vrc;
2602}
2603
2604/**
2605 * Unregisters a process object from a guest session.
2606 *
2607 * @return VBox status code. VERR_NOT_FOUND if the process is not registered (anymore).
2608 * @param pProcess Process object to unregister from session.
2609 */
2610int GuestSession::i_processUnregister(GuestProcess *pProcess)
2611{
2612 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
2613
2614 LogFlowThisFunc(("pProcess=%p\n", pProcess));
2615
2616 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2617
2618 const uint32_t idObject = pProcess->getObjectID();
2619
2620 LogFlowFunc(("Removing process (objectID=%RU32) ...\n", idObject));
2621
2622 int rc = i_objectUnregister(idObject);
2623 if (RT_FAILURE(rc))
2624 return rc;
2625
2626 SessionProcesses::iterator itProcs = mData.mProcesses.find(idObject);
2627 AssertReturn(itProcs != mData.mProcesses.end(), VERR_NOT_FOUND);
2628
2629 /* Make sure to consume the pointer before the one of the iterator gets released. */
2630 ComObjPtr<GuestProcess> pProc = pProcess;
2631
2632 ULONG uPID;
2633 HRESULT hr = pProc->COMGETTER(PID)(&uPID);
2634 ComAssertComRC(hr);
2635
2636 LogFlowFunc(("Removing process ID=%RU32 (session %RU32, guest PID %RU32, now total %zu processes)\n",
2637 idObject, mData.mSession.mID, uPID, mData.mProcesses.size()));
2638
2639 rc = pProcess->i_onUnregister();
2640 AssertRCReturn(rc, rc);
2641
2642 mData.mProcesses.erase(itProcs);
2643
2644 alock.release(); /* Release lock before firing off event. */
2645
2646 ::FireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProc, uPID, false /* Process unregistered */);
2647
2648 pProc.setNull();
2649
2650 LogFlowFuncLeaveRC(rc);
2651 return rc;
2652}
2653
2654/**
2655 * Creates but does *not* start the process yet.
2656 *
2657 * See GuestProcess::startProcess() or GuestProcess::startProcessAsync() for
2658 * starting the process.
2659 *
2660 * @return IPRT status code.
2661 * @param procInfo
2662 * @param pProcess
2663 */
2664int GuestSession::i_processCreateEx(GuestProcessStartupInfo &procInfo, ComObjPtr<GuestProcess> &pProcess)
2665{
2666 LogFlowFunc(("mExe=%s, mFlags=%x, mTimeoutMS=%RU32\n",
2667 procInfo.mExecutable.c_str(), procInfo.mFlags, procInfo.mTimeoutMS));
2668#ifdef DEBUG
2669 if (procInfo.mArguments.size())
2670 {
2671 LogFlowFunc(("Arguments:"));
2672 ProcessArguments::const_iterator it = procInfo.mArguments.begin();
2673 while (it != procInfo.mArguments.end())
2674 {
2675 LogFlow((" %s", (*it).c_str()));
2676 ++it;
2677 }
2678 LogFlow(("\n"));
2679 }
2680#endif
2681
2682 /* Validate flags. */
2683 if (procInfo.mFlags)
2684 {
2685 if ( !(procInfo.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
2686 && !(procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
2687 && !(procInfo.mFlags & ProcessCreateFlag_Hidden)
2688 && !(procInfo.mFlags & ProcessCreateFlag_Profile)
2689 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
2690 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdErr))
2691 {
2692 return VERR_INVALID_PARAMETER;
2693 }
2694 }
2695
2696 if ( (procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
2697 && ( (procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
2698 || (procInfo.mFlags & ProcessCreateFlag_WaitForStdErr)
2699 )
2700 )
2701 {
2702 return VERR_INVALID_PARAMETER;
2703 }
2704
2705 if (procInfo.mPriority)
2706 {
2707 if (!(procInfo.mPriority & ProcessPriority_Default))
2708 return VERR_INVALID_PARAMETER;
2709 }
2710
2711 /* Adjust timeout.
2712 * If set to 0, we define an infinite timeout (unlimited process run time). */
2713 if (procInfo.mTimeoutMS == 0)
2714 procInfo.mTimeoutMS = UINT32_MAX;
2715
2716 /** @todo Implement process priority + affinity. */
2717
2718 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2719
2720 /* Create the process object. */
2721 HRESULT hr = pProcess.createObject();
2722 if (FAILED(hr))
2723 return VERR_COM_UNEXPECTED;
2724
2725 /* Register a new object ID. */
2726 uint32_t idObject;
2727 int rc = i_objectRegister(pProcess, SESSIONOBJECTTYPE_PROCESS, &idObject);
2728 if (RT_FAILURE(rc))
2729 {
2730 pProcess.setNull();
2731 return rc;
2732 }
2733
2734 rc = pProcess->init(mParent->i_getConsole() /* Console */, this /* Session */, idObject,
2735 procInfo, mData.mpBaseEnvironment);
2736 if (RT_FAILURE(rc))
2737 return rc;
2738
2739 /* Add the created process to our map. */
2740 try
2741 {
2742 mData.mProcesses[idObject] = pProcess;
2743
2744 LogFlowFunc(("Added new process (Session: %RU32) with process ID=%RU32 (now total %zu processes)\n",
2745 mData.mSession.mID, idObject, mData.mProcesses.size()));
2746
2747 alock.release(); /* Release lock before firing off event. */
2748
2749 ::FireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProcess, 0 /* PID */, true /* Process registered */);
2750 }
2751 catch (std::bad_alloc &)
2752 {
2753 rc = VERR_NO_MEMORY;
2754 }
2755
2756 return rc;
2757}
2758
2759/**
2760 * Checks if a process object exists and optionally returns its object.
2761 *
2762 * @returns \c true if process object exists, or \c false if not.
2763 * @param uProcessID ID of process object to check.
2764 * @param pProcess Where to return the found process object on success.
2765 *
2766 * @note No locking done!
2767 */
2768inline bool GuestSession::i_processExists(uint32_t uProcessID, ComObjPtr<GuestProcess> *pProcess)
2769{
2770 SessionProcesses::const_iterator it = mData.mProcesses.find(uProcessID);
2771 if (it != mData.mProcesses.end())
2772 {
2773 if (pProcess)
2774 *pProcess = it->second;
2775 return true;
2776 }
2777 return false;
2778}
2779
2780/**
2781 * Returns the process object from a guest PID.
2782 *
2783 * @returns VBox status code.
2784 * @param uPID Guest PID to get process object for.
2785 * @param pProcess Where to return the process object on success.
2786 *
2787 * @note No locking done!
2788 */
2789inline int GuestSession::i_processGetByPID(ULONG uPID, ComObjPtr<GuestProcess> *pProcess)
2790{
2791 AssertReturn(uPID, false);
2792 /* pProcess is optional. */
2793
2794 SessionProcesses::iterator itProcs = mData.mProcesses.begin();
2795 for (; itProcs != mData.mProcesses.end(); ++itProcs)
2796 {
2797 ComObjPtr<GuestProcess> pCurProc = itProcs->second;
2798 AutoCaller procCaller(pCurProc);
2799 if (!procCaller.isOk())
2800 return VERR_COM_INVALID_OBJECT_STATE;
2801
2802 ULONG uCurPID;
2803 HRESULT hr = pCurProc->COMGETTER(PID)(&uCurPID);
2804 ComAssertComRC(hr);
2805
2806 if (uCurPID == uPID)
2807 {
2808 if (pProcess)
2809 *pProcess = pCurProc;
2810 return VINF_SUCCESS;
2811 }
2812 }
2813
2814 return VERR_NOT_FOUND;
2815}
2816
2817/**
2818 * Sends a message to the HGCM host service.
2819 *
2820 * @returns VBox status code.
2821 * @param uMessage Message ID to send.
2822 * @param uParms Number of parameters in \a paParms to send.
2823 * @param paParms Array of HGCM parameters to send.
2824 * @param fDst Host message destination flags of type VBOX_GUESTCTRL_DST_XXX.
2825 */
2826int GuestSession::i_sendMessage(uint32_t uMessage, uint32_t uParms, PVBOXHGCMSVCPARM paParms,
2827 uint64_t fDst /*= VBOX_GUESTCTRL_DST_SESSION*/)
2828{
2829 LogFlowThisFuncEnter();
2830
2831#ifndef VBOX_GUESTCTRL_TEST_CASE
2832 ComObjPtr<Console> pConsole = mParent->i_getConsole();
2833 Assert(!pConsole.isNull());
2834
2835 /* Forward the information to the VMM device. */
2836 VMMDev *pVMMDev = pConsole->i_getVMMDev();
2837 AssertPtr(pVMMDev);
2838
2839 LogFlowThisFunc(("uMessage=%RU32 (%s), uParms=%RU32\n", uMessage, GstCtrlHostMsgtoStr((guestControl::eHostMsg)uMessage), uParms));
2840
2841 /* HACK ALERT! We extend the first parameter to 64-bit and use the
2842 two topmost bits for call destination information. */
2843 Assert(fDst == VBOX_GUESTCTRL_DST_SESSION || fDst == VBOX_GUESTCTRL_DST_ROOT_SVC || fDst == VBOX_GUESTCTRL_DST_BOTH);
2844 Assert(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT);
2845 paParms[0].type = VBOX_HGCM_SVC_PARM_64BIT;
2846 paParms[0].u.uint64 = (uint64_t)paParms[0].u.uint32 | fDst;
2847
2848 /* Make the call. */
2849 int vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uMessage, uParms, paParms);
2850 if (RT_FAILURE(vrc))
2851 {
2852 /** @todo What to do here? */
2853 }
2854#else
2855 /* Not needed within testcases. */
2856 int vrc = VINF_SUCCESS;
2857#endif
2858 LogFlowFuncLeaveRC(vrc);
2859 return vrc;
2860}
2861
2862/**
2863 * Sets the guest session's current status.
2864 *
2865 * @returns VBox status code.
2866 * @param sessionStatus Session status to set.
2867 * @param sessionRc Session result to set (for error handling).
2868 *
2869 * @note Takes the write lock.
2870 */
2871int GuestSession::i_setSessionStatus(GuestSessionStatus_T sessionStatus, int sessionRc)
2872{
2873 LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, sessionRc=%Rrc\n",
2874 mData.mStatus, sessionStatus, sessionRc));
2875
2876 if (sessionStatus == GuestSessionStatus_Error)
2877 {
2878 AssertMsg(RT_FAILURE(sessionRc), ("Guest rc must be an error (%Rrc)\n", sessionRc));
2879 /* Do not allow overwriting an already set error. If this happens
2880 * this means we forgot some error checking/locking somewhere. */
2881 AssertMsg(RT_SUCCESS(mData.mRC), ("Guest rc already set (to %Rrc)\n", mData.mRC));
2882 }
2883 else
2884 AssertMsg(RT_SUCCESS(sessionRc), ("Guest rc must not be an error (%Rrc)\n", sessionRc));
2885
2886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2887
2888 int vrc = VINF_SUCCESS;
2889
2890 if (mData.mStatus != sessionStatus)
2891 {
2892 mData.mStatus = sessionStatus;
2893 mData.mRC = sessionRc;
2894
2895 /* Make sure to notify all underlying objects first. */
2896 vrc = i_objectsNotifyAboutStatusChange(sessionStatus);
2897
2898 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
2899 HRESULT hr = errorInfo.createObject();
2900 ComAssertComRC(hr);
2901 int rc2 = errorInfo->initEx(VBOX_E_IPRT_ERROR, sessionRc,
2902 COM_IIDOF(IGuestSession), getComponentName(),
2903 i_guestErrorToString(sessionRc));
2904 AssertRC(rc2);
2905
2906 alock.release(); /* Release lock before firing off event. */
2907
2908 ::FireGuestSessionStateChangedEvent(mEventSource, this, mData.mSession.mID, sessionStatus, errorInfo);
2909 }
2910
2911 LogFlowFuncLeaveRC(vrc);
2912 return vrc;
2913}
2914
2915/** @todo Unused --remove? */
2916int GuestSession::i_signalWaiters(GuestSessionWaitResult_T enmWaitResult, int rc /*= VINF_SUCCESS */)
2917{
2918 RT_NOREF(enmWaitResult, rc);
2919
2920 /*LogFlowThisFunc(("enmWaitResult=%d, rc=%Rrc, mWaitCount=%RU32, mWaitEvent=%p\n",
2921 enmWaitResult, rc, mData.mWaitCount, mData.mWaitEvent));*/
2922
2923 /* Note: No write locking here -- already done in the caller. */
2924
2925 int vrc = VINF_SUCCESS;
2926 /*if (mData.mWaitEvent)
2927 vrc = mData.mWaitEvent->Signal(enmWaitResult, rc);*/
2928 LogFlowFuncLeaveRC(vrc);
2929 return vrc;
2930}
2931
2932/**
2933 * Shuts down (and optionally powers off / reboots) the guest.
2934 * Needs supported Guest Additions installed.
2935 *
2936 * @returns VBox status code. VERR_NOT_SUPPORTED if not supported by Guest Additions.
2937 * @param fFlags Guest shutdown flags.
2938 * @param prcGuest Guest rc, when returning VERR_GSTCTL_GUEST_ERROR.
2939 * Any other return code indicates some host side error.
2940 */
2941int GuestSession::i_shutdown(uint32_t fFlags, int *prcGuest)
2942{
2943 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2944
2945 AssertPtrReturn(mParent, VERR_INVALID_POINTER);
2946 if (!(mParent->i_getGuestControlFeatures0() & VBOX_GUESTCTRL_GF_0_SHUTDOWN))
2947 return VERR_NOT_SUPPORTED;
2948
2949 LogRel(("Guest Control: Shutting down guest (flags = %#x) ...\n", fFlags));
2950
2951 GuestWaitEvent *pEvent = NULL;
2952 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2953 if (RT_FAILURE(vrc))
2954 return vrc;
2955
2956 /* Prepare HGCM call. */
2957 VBOXHGCMSVCPARM paParms[2];
2958 int i = 0;
2959 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2960 HGCMSvcSetU32(&paParms[i++], fFlags);
2961
2962 alock.release(); /* Drop write lock before sending. */
2963
2964 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
2965
2966 vrc = i_sendMessage(HOST_MSG_SHUTDOWN, i, paParms);
2967 if (RT_SUCCESS(vrc))
2968 {
2969 vrc = pEvent->Wait(30 * 1000);
2970 if (RT_FAILURE(vrc))
2971 {
2972 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2973 rcGuest = pEvent->GuestResult();
2974 }
2975 }
2976
2977 if (RT_FAILURE(vrc))
2978 {
2979 LogRel(("Guest Control: Shutting down guest failed, rc=%Rrc\n",
2980 vrc == VERR_GSTCTL_GUEST_ERROR ? rcGuest : vrc));
2981
2982 if ( vrc == VERR_GSTCTL_GUEST_ERROR
2983 && prcGuest)
2984 *prcGuest = rcGuest;
2985 }
2986
2987 unregisterWaitEvent(pEvent);
2988
2989 LogFlowFuncLeaveRC(vrc);
2990 return vrc;
2991}
2992
2993/**
2994 * Determines the protocol version (sets mData.mProtocolVersion).
2995 *
2996 * This is called from the init method prior to to establishing a guest
2997 * session.
2998 *
2999 * @returns VBox status code.
3000 */
3001int GuestSession::i_determineProtocolVersion(void)
3002{
3003 /*
3004 * We currently do this based on the reported Guest Additions version,
3005 * ASSUMING that VBoxService and VBoxDrv are at the same version.
3006 */
3007 ComObjPtr<Guest> pGuest = mParent;
3008 AssertReturn(!pGuest.isNull(), VERR_NOT_SUPPORTED);
3009 uint32_t uGaVersion = pGuest->i_getAdditionsVersion();
3010
3011 /* Everyone supports version one, if they support anything at all. */
3012 mData.mProtocolVersion = 1;
3013
3014 /* Guest control 2.0 was introduced with 4.3.0. */
3015 if (uGaVersion >= VBOX_FULL_VERSION_MAKE(4,3,0))
3016 mData.mProtocolVersion = 2; /* Guest control 2.0. */
3017
3018 LogFlowThisFunc(("uGaVersion=%u.%u.%u => mProtocolVersion=%u\n",
3019 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
3020 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
3021
3022 /*
3023 * Inform the user about outdated Guest Additions (VM release log).
3024 */
3025 if (mData.mProtocolVersion < 2)
3026 LogRelMax(3, ("Warning: Guest Additions v%u.%u.%u only supports the older guest control protocol version %u.\n"
3027 " Please upgrade GAs to the current version to get full guest control capabilities.\n",
3028 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
3029 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
3030
3031 return VINF_SUCCESS;
3032}
3033
3034/**
3035 * Waits for guest session events.
3036 *
3037 * @returns VBox status code.
3038 * @returns VERR_GSTCTL_GUEST_ERROR on received guest error.
3039 * @param fWaitFlags Wait flags to use.
3040 * @param uTimeoutMS Timeout (in ms) to wait.
3041 * @param waitResult Where to return the wait result on success.
3042 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
3043 * was returned. Optional.
3044 *
3045 * @note Takes the read lock.
3046 */
3047int GuestSession::i_waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, GuestSessionWaitResult_T &waitResult, int *prcGuest)
3048{
3049 LogFlowThisFuncEnter();
3050
3051 AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
3052
3053 /*LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p, prcGuest=%p\n",
3054 fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent, prcGuest));*/
3055
3056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3057
3058 /* Did some error occur before? Then skip waiting and return. */
3059 if (mData.mStatus == GuestSessionStatus_Error)
3060 {
3061 waitResult = GuestSessionWaitResult_Error;
3062 AssertMsg(RT_FAILURE(mData.mRC), ("No error rc (%Rrc) set when guest session indicated an error\n", mData.mRC));
3063 if (prcGuest)
3064 *prcGuest = mData.mRC; /* Return last set error. */
3065 return VERR_GSTCTL_GUEST_ERROR;
3066 }
3067
3068 /* Guest Additions < 4.3 don't support session handling, skip. */
3069 if (mData.mProtocolVersion < 2)
3070 {
3071 waitResult = GuestSessionWaitResult_WaitFlagNotSupported;
3072
3073 LogFlowThisFunc(("Installed Guest Additions don't support waiting for dedicated sessions, skipping\n"));
3074 return VINF_SUCCESS;
3075 }
3076
3077 waitResult = GuestSessionWaitResult_None;
3078 if (fWaitFlags & GuestSessionWaitForFlag_Terminate)
3079 {
3080 switch (mData.mStatus)
3081 {
3082 case GuestSessionStatus_Terminated:
3083 case GuestSessionStatus_Down:
3084 waitResult = GuestSessionWaitResult_Terminate;
3085 break;
3086
3087 case GuestSessionStatus_TimedOutKilled:
3088 case GuestSessionStatus_TimedOutAbnormally:
3089 waitResult = GuestSessionWaitResult_Timeout;
3090 break;
3091
3092 case GuestSessionStatus_Error:
3093 /* Handled above. */
3094 break;
3095
3096 case GuestSessionStatus_Started:
3097 waitResult = GuestSessionWaitResult_Start;
3098 break;
3099
3100 case GuestSessionStatus_Undefined:
3101 case GuestSessionStatus_Starting:
3102 /* Do the waiting below. */
3103 break;
3104
3105 default:
3106 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
3107 return VERR_NOT_IMPLEMENTED;
3108 }
3109 }
3110 else if (fWaitFlags & GuestSessionWaitForFlag_Start)
3111 {
3112 switch (mData.mStatus)
3113 {
3114 case GuestSessionStatus_Started:
3115 case GuestSessionStatus_Terminating:
3116 case GuestSessionStatus_Terminated:
3117 case GuestSessionStatus_Down:
3118 waitResult = GuestSessionWaitResult_Start;
3119 break;
3120
3121 case GuestSessionStatus_Error:
3122 waitResult = GuestSessionWaitResult_Error;
3123 break;
3124
3125 case GuestSessionStatus_TimedOutKilled:
3126 case GuestSessionStatus_TimedOutAbnormally:
3127 waitResult = GuestSessionWaitResult_Timeout;
3128 break;
3129
3130 case GuestSessionStatus_Undefined:
3131 case GuestSessionStatus_Starting:
3132 /* Do the waiting below. */
3133 break;
3134
3135 default:
3136 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
3137 return VERR_NOT_IMPLEMENTED;
3138 }
3139 }
3140
3141 LogFlowThisFunc(("sessionStatus=%RU32, sessionRc=%Rrc, waitResult=%RU32\n",
3142 mData.mStatus, mData.mRC, waitResult));
3143
3144 /* No waiting needed? Return immediately using the last set error. */
3145 if (waitResult != GuestSessionWaitResult_None)
3146 {
3147 if (prcGuest)
3148 *prcGuest = mData.mRC; /* Return last set error (if any). */
3149 return RT_SUCCESS(mData.mRC) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
3150 }
3151
3152 int vrc;
3153
3154 GuestWaitEvent *pEvent = NULL;
3155 GuestEventTypes eventTypes;
3156 try
3157 {
3158 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
3159
3160 vrc = registerWaitEventEx(mData.mSession.mID, mData.mObjectID, eventTypes, &pEvent);
3161 }
3162 catch (std::bad_alloc &)
3163 {
3164 vrc = VERR_NO_MEMORY;
3165 }
3166
3167 if (RT_FAILURE(vrc))
3168 return vrc;
3169
3170 alock.release(); /* Release lock before waiting. */
3171
3172 GuestSessionStatus_T sessionStatus;
3173 vrc = i_waitForStatusChange(pEvent, fWaitFlags,
3174 uTimeoutMS, &sessionStatus, prcGuest);
3175 if (RT_SUCCESS(vrc))
3176 {
3177 switch (sessionStatus)
3178 {
3179 case GuestSessionStatus_Started:
3180 waitResult = GuestSessionWaitResult_Start;
3181 break;
3182
3183 case GuestSessionStatus_Terminated:
3184 waitResult = GuestSessionWaitResult_Terminate;
3185 break;
3186
3187 case GuestSessionStatus_TimedOutKilled:
3188 case GuestSessionStatus_TimedOutAbnormally:
3189 waitResult = GuestSessionWaitResult_Timeout;
3190 break;
3191
3192 case GuestSessionStatus_Down:
3193 waitResult = GuestSessionWaitResult_Terminate;
3194 break;
3195
3196 case GuestSessionStatus_Error:
3197 waitResult = GuestSessionWaitResult_Error;
3198 break;
3199
3200 default:
3201 waitResult = GuestSessionWaitResult_Status;
3202 break;
3203 }
3204 }
3205
3206 unregisterWaitEvent(pEvent);
3207
3208 LogFlowFuncLeaveRC(vrc);
3209 return vrc;
3210}
3211
3212/**
3213 * Waits for guest session status changes.
3214 *
3215 * @returns VBox status code.
3216 * @returns VERR_GSTCTL_GUEST_ERROR on received guest error.
3217 * @param pEvent Wait event to use for waiting.
3218 * @param fWaitFlags Wait flags to use.
3219 * @param uTimeoutMS Timeout (in ms) to wait.
3220 * @param pSessionStatus Where to return the guest session status.
3221 * @param prcGuest Where to return the guest error when VERR_GSTCTL_GUEST_ERROR
3222 * was returned. Optional.
3223 *
3224 * @note Takes the read lock.
3225 */
3226int GuestSession::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t fWaitFlags, uint32_t uTimeoutMS,
3227 GuestSessionStatus_T *pSessionStatus, int *prcGuest)
3228{
3229 RT_NOREF(fWaitFlags);
3230 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
3231
3232 VBoxEventType_T evtType;
3233 ComPtr<IEvent> pIEvent;
3234 int vrc = waitForEvent(pEvent, uTimeoutMS,
3235 &evtType, pIEvent.asOutParam());
3236 if (RT_SUCCESS(vrc))
3237 {
3238 Assert(evtType == VBoxEventType_OnGuestSessionStateChanged);
3239
3240 ComPtr<IGuestSessionStateChangedEvent> pChangedEvent = pIEvent;
3241 Assert(!pChangedEvent.isNull());
3242
3243 GuestSessionStatus_T sessionStatus;
3244 pChangedEvent->COMGETTER(Status)(&sessionStatus);
3245 if (pSessionStatus)
3246 *pSessionStatus = sessionStatus;
3247
3248 ComPtr<IVirtualBoxErrorInfo> errorInfo;
3249 HRESULT hr = pChangedEvent->COMGETTER(Error)(errorInfo.asOutParam());
3250 ComAssertComRC(hr);
3251
3252 LONG lGuestRc;
3253 hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
3254 ComAssertComRC(hr);
3255 if (RT_FAILURE((int)lGuestRc))
3256 vrc = VERR_GSTCTL_GUEST_ERROR;
3257 if (prcGuest)
3258 *prcGuest = (int)lGuestRc;
3259
3260 LogFlowThisFunc(("Status changed event for session ID=%RU32, new status is: %RU32 (%Rrc)\n",
3261 mData.mSession.mID, sessionStatus,
3262 RT_SUCCESS((int)lGuestRc) ? VINF_SUCCESS : (int)lGuestRc));
3263 }
3264 /* waitForEvent may also return VERR_GSTCTL_GUEST_ERROR like we do above, so make prcGuest is set. */
3265 else if (vrc == VERR_GSTCTL_GUEST_ERROR && prcGuest)
3266 *prcGuest = pEvent->GuestResult();
3267 Assert(vrc != VERR_GSTCTL_GUEST_ERROR || !prcGuest || *prcGuest != (int)0xcccccccc);
3268
3269 LogFlowFuncLeaveRC(vrc);
3270 return vrc;
3271}
3272
3273// implementation of public methods
3274/////////////////////////////////////////////////////////////////////////////
3275
3276HRESULT GuestSession::close()
3277{
3278 LogFlowThisFuncEnter();
3279
3280 /* Note: Don't check if the session is ready via i_isReadyExternal() here;
3281 * the session (already) could be in a stopped / aborted state. */
3282
3283 int vrc = VINF_SUCCESS; /* Shut up MSVC. */
3284 int rcGuest = VINF_SUCCESS;
3285
3286 uint32_t msTimeout = 30 * 1000; /* 30s timeout by default */
3287 for (int i = 0; i < 10; i++)
3288 {
3289 if (i)
3290 {
3291 LogRel(("Guest Control: Closing session #%RU32 timed out (%RU32s timeout, attempt %d/10), retrying ...\n",
3292 mData.mSession.mID, msTimeout / RT_MS_1SEC, i + 1));
3293 msTimeout += RT_MS_10SEC; /* Slightly increase the timeout. */
3294 }
3295
3296 /* Close session on guest. */
3297 vrc = i_closeSession(0 /* Flags */, msTimeout, &rcGuest);
3298 if ( RT_SUCCESS(vrc)
3299 || vrc != VERR_TIMEOUT) /* If something else happened there is no point in retrying further. */
3300 break;
3301 }
3302
3303 /* On failure don't return here, instead do all the cleanup
3304 * work first and then return an error. */
3305
3306 /* Remove ourselves from the session list. */
3307 AssertPtr(mParent);
3308 int vrc2 = mParent->i_sessionRemove(mData.mSession.mID);
3309 if (vrc2 == VERR_NOT_FOUND) /* Not finding the session anymore isn't critical. */
3310 vrc2 = VINF_SUCCESS;
3311
3312 if (RT_SUCCESS(vrc))
3313 vrc = vrc2;
3314
3315 LogFlowThisFunc(("Returning rc=%Rrc, rcGuest=%Rrc\n", vrc, rcGuest));
3316
3317 if (RT_FAILURE(vrc))
3318 {
3319 if (vrc == VERR_GSTCTL_GUEST_ERROR)
3320 {
3321 GuestErrorInfo ge(GuestErrorInfo::Type_Session, rcGuest, mData.mSession.mName.c_str());
3322 return setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Closing guest session failed: %s"),
3323 GuestBase::getErrorAsString(ge).c_str());
3324 }
3325 return setError(VBOX_E_IPRT_ERROR, tr("Closing guest session \"%s\" failed with %Rrc"),
3326 mData.mSession.mName.c_str(), vrc);
3327 }
3328
3329 return S_OK;
3330}
3331
3332HRESULT GuestSession::fileCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3333 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3334{
3335 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3336 ReturnComNotImplemented();
3337}
3338
3339HRESULT GuestSession::fileCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3340 const std::vector<FileCopyFlag_T> &aFlags,
3341 ComPtr<IProgress> &aProgress)
3342{
3343 uint32_t fFlags = FileCopyFlag_None;
3344 if (aFlags.size())
3345 {
3346 for (size_t i = 0; i < aFlags.size(); i++)
3347 fFlags |= aFlags[i];
3348
3349 const uint32_t fValidFlags = FileCopyFlag_None | FileCopyFlag_NoReplace | FileCopyFlag_FollowLinks | FileCopyFlag_Update;
3350 if (fFlags & ~fValidFlags)
3351 return setError(E_INVALIDARG,tr("Unknown flags: flags value %#x, invalid: %#x"), fFlags, fFlags & ~fValidFlags);
3352 }
3353
3354 GuestSessionFsSourceSet SourceSet;
3355
3356 GuestSessionFsSourceSpec source;
3357 source.strSource = aSource;
3358 source.enmType = FsObjType_File;
3359 source.enmPathStyle = i_getPathStyle();
3360 source.fDryRun = false; /** @todo Implement support for a dry run. */
3361 source.Type.File.fCopyFlags = (FileCopyFlag_T)fFlags;
3362
3363 SourceSet.push_back(source);
3364
3365 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3366}
3367
3368HRESULT GuestSession::fileCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3369 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3370{
3371 uint32_t fFlags = FileCopyFlag_None;
3372 if (aFlags.size())
3373 {
3374 for (size_t i = 0; i < aFlags.size(); i++)
3375 fFlags |= aFlags[i];
3376
3377 const uint32_t fValidFlags = FileCopyFlag_None | FileCopyFlag_NoReplace | FileCopyFlag_FollowLinks | FileCopyFlag_Update;
3378 if (fFlags & ~fValidFlags)
3379 return setError(E_INVALIDARG,tr("Unknown flags: flags value %#x, invalid: %#x"), fFlags, fFlags & ~fValidFlags);
3380 }
3381
3382 GuestSessionFsSourceSet SourceSet;
3383
3384 GuestSessionFsSourceSpec source;
3385 source.strSource = aSource;
3386 source.enmType = FsObjType_File;
3387 source.enmPathStyle = i_getPathStyle();
3388 source.fDryRun = false; /** @todo Implement support for a dry run. */
3389 source.Type.File.fCopyFlags = (FileCopyFlag_T)fFlags;
3390
3391 SourceSet.push_back(source);
3392
3393 return i_copyToGuest(SourceSet, aDestination, aProgress);
3394}
3395
3396HRESULT GuestSession::copyFromGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
3397 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
3398 ComPtr<IProgress> &aProgress)
3399{
3400 const size_t cSources = aSources.size();
3401 if ( (aFilters.size() && aFilters.size() != cSources)
3402 || (aFlags.size() && aFlags.size() != cSources))
3403 {
3404 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
3405 }
3406
3407 GuestSessionFsSourceSet SourceSet;
3408
3409 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
3410 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
3411 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
3412
3413 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
3414 const bool fFollowSymlinks = true; /** @todo Ditto. */
3415
3416 while (itSource != aSources.end())
3417 {
3418 GuestFsObjData objData;
3419 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3420 int vrc = i_fsQueryInfo(*(itSource), fFollowSymlinks, objData, &rcGuest);
3421 if ( RT_FAILURE(vrc)
3422 && !fContinueOnErrors)
3423 {
3424 if (GuestProcess::i_isGuestError(vrc))
3425 {
3426 GuestErrorInfo ge(GuestErrorInfo::Type_Process, rcGuest, (*itSource).c_str());
3427 return setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying type for guest source failed: %s"),
3428 GuestBase::getErrorAsString(ge).c_str());
3429 }
3430 return setError(E_FAIL, tr("Querying type for guest source \"%s\" failed: %Rrc"), (*itSource).c_str(), vrc);
3431 }
3432
3433 Utf8Str strFlags;
3434 if (itFlags != aFlags.end())
3435 {
3436 strFlags = *itFlags;
3437 ++itFlags;
3438 }
3439
3440 Utf8Str strFilter;
3441 if (itFilter != aFilters.end())
3442 {
3443 strFilter = *itFilter;
3444 ++itFilter;
3445 }
3446
3447 GuestSessionFsSourceSpec source;
3448 source.strSource = *itSource;
3449 source.strFilter = strFilter;
3450 source.enmType = objData.mType;
3451 source.enmPathStyle = i_getPathStyle();
3452 source.fDryRun = false; /** @todo Implement support for a dry run. */
3453
3454 HRESULT hrc;
3455 if (source.enmType == FsObjType_Directory)
3456 {
3457 hrc = GuestSession::i_directoryCopyFlagFromStr(strFlags, &source.Type.Dir.fCopyFlags);
3458 }
3459 else if (source.enmType == FsObjType_File)
3460 hrc = GuestSession::i_fileCopyFlagFromStr(strFlags, &source.Type.File.fCopyFlags);
3461 else
3462 return setError(E_INVALIDARG, tr("Source type %#x invalid / not supported"), source.enmType);
3463 if (FAILED(hrc))
3464 return hrc;
3465
3466 SourceSet.push_back(source);
3467
3468 ++itSource;
3469 }
3470
3471 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3472}
3473
3474HRESULT GuestSession::copyToGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
3475 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
3476 ComPtr<IProgress> &aProgress)
3477{
3478 const size_t cSources = aSources.size();
3479 if ( (aFilters.size() && aFilters.size() != cSources)
3480 || (aFlags.size() && aFlags.size() != cSources))
3481 {
3482 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
3483 }
3484
3485 GuestSessionFsSourceSet SourceSet;
3486
3487 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
3488 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
3489 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
3490
3491 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
3492
3493 while (itSource != aSources.end())
3494 {
3495 RTFSOBJINFO objInfo;
3496 int vrc = RTPathQueryInfo((*itSource).c_str(), &objInfo, RTFSOBJATTRADD_NOTHING);
3497 if ( RT_FAILURE(vrc)
3498 && !fContinueOnErrors)
3499 {
3500 return setError(E_FAIL, tr("Unable to query type for source '%s' (%Rrc)"), (*itSource).c_str(), vrc);
3501 }
3502
3503 Utf8Str strFlags;
3504 if (itFlags != aFlags.end())
3505 {
3506 strFlags = *itFlags;
3507 ++itFlags;
3508 }
3509
3510 Utf8Str strFilter;
3511 if (itFilter != aFilters.end())
3512 {
3513 strFilter = *itFilter;
3514 ++itFilter;
3515 }
3516
3517 GuestSessionFsSourceSpec source;
3518 source.strSource = *itSource;
3519 source.strFilter = strFilter;
3520 source.enmType = GuestBase::fileModeToFsObjType(objInfo.Attr.fMode);
3521 source.enmPathStyle = i_getPathStyle();
3522 source.fDryRun = false; /** @todo Implement support for a dry run. */
3523
3524 HRESULT hrc;
3525 if (source.enmType == FsObjType_Directory)
3526 {
3527 hrc = GuestSession::i_directoryCopyFlagFromStr(strFlags, &source.Type.Dir.fCopyFlags);
3528 }
3529 else if (source.enmType == FsObjType_File)
3530 hrc = GuestSession::i_fileCopyFlagFromStr(strFlags, &source.Type.File.fCopyFlags);
3531 else
3532 return setError(E_INVALIDARG, tr("Source type %#x invalid / not supported"), source.enmType);
3533 if (FAILED(hrc))
3534 return hrc;
3535
3536 SourceSet.push_back(source);
3537
3538 ++itSource;
3539 }
3540
3541 /* (Re-)Validate stuff. */
3542 if (RT_UNLIKELY(SourceSet.size() == 0)) /* At least one source must be present. */
3543 return setError(E_INVALIDARG, tr("No sources specified"));
3544 if (RT_UNLIKELY(SourceSet[0].strSource.isEmpty()))
3545 return setError(E_INVALIDARG, tr("First source entry is empty"));
3546 if (RT_UNLIKELY(aDestination.isEmpty()))
3547 return setError(E_INVALIDARG, tr("No destination specified"));
3548
3549 return i_copyToGuest(SourceSet, aDestination, aProgress);
3550}
3551
3552HRESULT GuestSession::directoryCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3553 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3554{
3555 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3556 ReturnComNotImplemented();
3557}
3558
3559HRESULT GuestSession::directoryCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3560 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3561{
3562 uint32_t fFlags = DirectoryCopyFlag_None;
3563 if (aFlags.size())
3564 {
3565 for (size_t i = 0; i < aFlags.size(); i++)
3566 fFlags |= aFlags[i];
3567
3568 const uint32_t fValidFlags = DirectoryCopyFlag_None | DirectoryCopyFlag_CopyIntoExisting | DirectoryCopyFlag_Recursive
3569 | DirectoryCopyFlag_FollowLinks;
3570 if (fFlags & ~fValidFlags)
3571 return setError(E_INVALIDARG,tr("Unknown flags: flags value %#x, invalid: %#x"), fFlags, fFlags & ~fValidFlags);
3572 }
3573
3574 GuestSessionFsSourceSet SourceSet;
3575
3576 GuestSessionFsSourceSpec source;
3577 source.strSource = aSource;
3578 source.enmType = FsObjType_Directory;
3579 source.enmPathStyle = i_getPathStyle();
3580 source.fDryRun = false; /** @todo Implement support for a dry run. */
3581 source.Type.Dir.fCopyFlags = (DirectoryCopyFlag_T)fFlags;
3582
3583 SourceSet.push_back(source);
3584
3585 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3586}
3587
3588HRESULT GuestSession::directoryCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3589 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3590{
3591 uint32_t fFlags = DirectoryCopyFlag_None;
3592 if (aFlags.size())
3593 {
3594 for (size_t i = 0; i < aFlags.size(); i++)
3595 fFlags |= aFlags[i];
3596
3597 const uint32_t fValidFlags = DirectoryCopyFlag_None | DirectoryCopyFlag_CopyIntoExisting | DirectoryCopyFlag_Recursive
3598 | DirectoryCopyFlag_FollowLinks;
3599 if (fFlags & ~fValidFlags)
3600 return setError(E_INVALIDARG,tr("Unknown flags: flags value %#x, invalid: %#x"), fFlags, fFlags & ~fValidFlags);
3601 }
3602
3603 GuestSessionFsSourceSet SourceSet;
3604
3605 GuestSessionFsSourceSpec source;
3606 source.strSource = aSource;
3607 source.enmType = FsObjType_Directory;
3608 source.enmPathStyle = i_getPathStyle();
3609 source.fDryRun = false; /** @todo Implement support for a dry run. */
3610 source.Type.Dir.fCopyFlags = (DirectoryCopyFlag_T)fFlags;
3611
3612 SourceSet.push_back(source);
3613
3614 return i_copyToGuest(SourceSet, aDestination, aProgress);
3615}
3616
3617HRESULT GuestSession::directoryCreate(const com::Utf8Str &aPath, ULONG aMode,
3618 const std::vector<DirectoryCreateFlag_T> &aFlags)
3619{
3620 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3621 return setError(E_INVALIDARG, tr("No directory to create specified"));
3622
3623 uint32_t fFlags = DirectoryCreateFlag_None;
3624 if (aFlags.size())
3625 {
3626 for (size_t i = 0; i < aFlags.size(); i++)
3627 fFlags |= aFlags[i];
3628
3629 if (fFlags & ~DirectoryCreateFlag_Parents)
3630 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), fFlags);
3631 }
3632
3633 HRESULT hrc = i_isStartedExternal();
3634 if (FAILED(hrc))
3635 return hrc;
3636
3637 LogFlowThisFuncEnter();
3638
3639 ComObjPtr <GuestDirectory> pDirectory; int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3640 int vrc = i_directoryCreate(aPath, (uint32_t)aMode, fFlags, &rcGuest);
3641 if (RT_FAILURE(vrc))
3642 {
3643 if (GuestProcess::i_isGuestError(vrc))
3644 {
3645 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, rcGuest, aPath.c_str());
3646 return setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Guest directory creation failed: %s"),
3647 GuestBase::getErrorAsString(ge).c_str());
3648 }
3649 switch (vrc)
3650 {
3651 case VERR_INVALID_PARAMETER:
3652 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Guest directory creation failed: Invalid parameters given"));
3653 break;
3654
3655 case VERR_BROKEN_PIPE:
3656 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Guest directory creation failed: Unexpectedly aborted"));
3657 break;
3658
3659 default:
3660 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Guest directory creation failed: %Rrc"), vrc);
3661 break;
3662 }
3663 }
3664
3665 return hrc;
3666}
3667
3668HRESULT GuestSession::directoryCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath,
3669 BOOL aSecure, com::Utf8Str &aDirectory)
3670{
3671 if (RT_UNLIKELY((aTemplateName.c_str()) == NULL || *(aTemplateName.c_str()) == '\0'))
3672 return setError(E_INVALIDARG, tr("No template specified"));
3673 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3674 return setError(E_INVALIDARG, tr("No directory name specified"));
3675 if (!aSecure) /* Ignore what mode is specified when a secure temp thing needs to be created. */
3676 if (RT_UNLIKELY(!(aMode & ~07777)))
3677 return setError(E_INVALIDARG, tr("Mode invalid (must be specified in octal mode)"));
3678
3679 HRESULT hrc = i_isStartedExternal();
3680 if (FAILED(hrc))
3681 return hrc;
3682
3683 LogFlowThisFuncEnter();
3684
3685 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3686 int vrc = i_fsCreateTemp(aTemplateName, aPath, true /* Directory */, aDirectory, aMode, RT_BOOL(aSecure), &rcGuest);
3687 if (!RT_SUCCESS(vrc))
3688 {
3689 switch (vrc)
3690 {
3691 case VERR_GSTCTL_GUEST_ERROR:
3692 {
3693 GuestErrorInfo ge(GuestErrorInfo::Type_ToolMkTemp, rcGuest, aPath.c_str());
3694 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Temporary guest directory creation failed: %s"),
3695 GuestBase::getErrorAsString(ge).c_str());
3696 break;
3697 }
3698 default:
3699 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Temporary guest directory creation \"%s\" with template \"%s\" failed: %Rrc"),
3700 aPath.c_str(), aTemplateName.c_str(), vrc);
3701 break;
3702 }
3703 }
3704
3705 return hrc;
3706}
3707
3708HRESULT GuestSession::directoryExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3709{
3710 if (RT_UNLIKELY(aPath.isEmpty()))
3711 return setError(E_INVALIDARG, tr("Empty path"));
3712
3713 HRESULT hrc = i_isStartedExternal();
3714 if (FAILED(hrc))
3715 return hrc;
3716
3717 LogFlowThisFuncEnter();
3718
3719 GuestFsObjData objData;
3720 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3721
3722 int vrc = i_directoryQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3723 if (RT_SUCCESS(vrc))
3724 *aExists = TRUE;
3725 else
3726 {
3727 switch (vrc)
3728 {
3729 case VERR_GSTCTL_GUEST_ERROR:
3730 {
3731 switch (rcGuest)
3732 {
3733 case VERR_PATH_NOT_FOUND:
3734 *aExists = FALSE;
3735 break;
3736 default:
3737 {
3738 GuestErrorInfo ge(GuestErrorInfo::Type_ToolStat, rcGuest, aPath.c_str());
3739 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying directory existence failed: %s"),
3740 GuestBase::getErrorAsString(ge).c_str());
3741 break;
3742 }
3743 }
3744 break;
3745 }
3746
3747 case VERR_NOT_A_DIRECTORY:
3748 {
3749 *aExists = FALSE;
3750 break;
3751 }
3752
3753 default:
3754 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying directory existence \"%s\" failed: %Rrc"),
3755 aPath.c_str(), vrc);
3756 break;
3757 }
3758 }
3759
3760 return hrc;
3761}
3762
3763HRESULT GuestSession::directoryOpen(const com::Utf8Str &aPath, const com::Utf8Str &aFilter,
3764 const std::vector<DirectoryOpenFlag_T> &aFlags, ComPtr<IGuestDirectory> &aDirectory)
3765{
3766 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3767 return setError(E_INVALIDARG, tr("No directory to open specified"));
3768 if (RT_UNLIKELY((aFilter.c_str()) != NULL && *(aFilter.c_str()) != '\0'))
3769 return setError(E_INVALIDARG, tr("Directory filters are not implemented yet"));
3770
3771 uint32_t fFlags = DirectoryOpenFlag_None;
3772 if (aFlags.size())
3773 {
3774 for (size_t i = 0; i < aFlags.size(); i++)
3775 fFlags |= aFlags[i];
3776
3777 if (fFlags)
3778 return setError(E_INVALIDARG, tr("Open flags (%#x) not implemented yet"), fFlags);
3779 }
3780
3781 HRESULT hrc = i_isStartedExternal();
3782 if (FAILED(hrc))
3783 return hrc;
3784
3785 LogFlowThisFuncEnter();
3786
3787 GuestDirectoryOpenInfo openInfo;
3788 openInfo.mPath = aPath;
3789 openInfo.mFilter = aFilter;
3790 openInfo.mFlags = fFlags;
3791
3792 ComObjPtr<GuestDirectory> pDirectory; int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3793 int vrc = i_directoryOpen(openInfo, pDirectory, &rcGuest);
3794 if (RT_SUCCESS(vrc))
3795 {
3796 /* Return directory object to the caller. */
3797 hrc = pDirectory.queryInterfaceTo(aDirectory.asOutParam());
3798 }
3799 else
3800 {
3801 switch (vrc)
3802 {
3803 case VERR_INVALID_PARAMETER:
3804 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest directory \"%s\" failed; invalid parameters given"),
3805 aPath.c_str());
3806 break;
3807
3808 case VERR_GSTCTL_GUEST_ERROR:
3809 {
3810 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, rcGuest, aPath.c_str());
3811 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Opening guest directory failed: %s"),
3812 GuestBase::getErrorAsString(ge).c_str());
3813 break;
3814 }
3815 default:
3816 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3817 break;
3818 }
3819 }
3820
3821 return hrc;
3822}
3823
3824HRESULT GuestSession::directoryRemove(const com::Utf8Str &aPath)
3825{
3826 if (RT_UNLIKELY(aPath.c_str() == NULL || *aPath.c_str() == '\0'))
3827 return setError(E_INVALIDARG, tr("No directory to remove specified"));
3828
3829 HRESULT hrc = i_isStartedExternal();
3830 if (FAILED(hrc))
3831 return hrc;
3832
3833 LogFlowThisFuncEnter();
3834
3835 /* No flags; only remove the directory when empty. */
3836 uint32_t fFlags = DIRREMOVEREC_FLAG_NONE;
3837
3838 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3839 int vrc = i_directoryRemove(aPath, fFlags, &rcGuest);
3840 if (RT_FAILURE(vrc))
3841 {
3842 switch (vrc)
3843 {
3844 case VERR_NOT_SUPPORTED:
3845 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3846 tr("Handling removing guest directories not supported by installed Guest Additions"));
3847 break;
3848
3849 case VERR_GSTCTL_GUEST_ERROR:
3850 {
3851 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, rcGuest, aPath.c_str());
3852 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Removing guest directory failed: %s"),
3853 GuestBase::getErrorAsString(ge).c_str());
3854 break;
3855 }
3856 default:
3857 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing guest directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3858 break;
3859 }
3860 }
3861
3862 return hrc;
3863}
3864
3865HRESULT GuestSession::directoryRemoveRecursive(const com::Utf8Str &aPath, const std::vector<DirectoryRemoveRecFlag_T> &aFlags,
3866 ComPtr<IProgress> &aProgress)
3867{
3868 if (RT_UNLIKELY(aPath.c_str() == NULL || *aPath.c_str() == '\0'))
3869 return setError(E_INVALIDARG, tr("No directory to remove recursively specified"));
3870
3871 /* By defautl remove recursively as the function name implies. */
3872 uint32_t fFlags = DIRREMOVEREC_FLAG_RECURSIVE;
3873 if (aFlags.size())
3874 {
3875 for (size_t i = 0; i < aFlags.size(); i++)
3876 {
3877 switch (aFlags[i])
3878 {
3879 case DirectoryRemoveRecFlag_None: /* Skip. */
3880 continue;
3881
3882 case DirectoryRemoveRecFlag_ContentAndDir:
3883 fFlags |= DIRREMOVEREC_FLAG_CONTENT_AND_DIR;
3884 break;
3885
3886 case DirectoryRemoveRecFlag_ContentOnly:
3887 fFlags |= DIRREMOVEREC_FLAG_CONTENT_ONLY;
3888 break;
3889
3890 default:
3891 return setError(E_INVALIDARG, tr("Invalid flags specified"));
3892 }
3893 }
3894 }
3895
3896 HRESULT hrc = i_isStartedExternal();
3897 if (FAILED(hrc))
3898 return hrc;
3899
3900 LogFlowThisFuncEnter();
3901
3902 ComObjPtr<Progress> pProgress;
3903 hrc = pProgress.createObject();
3904 if (SUCCEEDED(hrc))
3905 hrc = pProgress->init(static_cast<IGuestSession *>(this),
3906 Bstr(tr("Removing guest directory")).raw(),
3907 TRUE /*aCancelable*/);
3908 if (FAILED(hrc))
3909 return hrc;
3910
3911 /* Note: At the moment we don't supply progress information while
3912 * deleting a guest directory recursively. So just complete
3913 * the progress object right now. */
3914 /** @todo Implement progress reporting on guest directory deletion! */
3915 hrc = pProgress->i_notifyComplete(S_OK);
3916 if (FAILED(hrc))
3917 return hrc;
3918
3919 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
3920 int vrc = i_directoryRemove(aPath, fFlags, &rcGuest);
3921 if (RT_FAILURE(vrc))
3922 {
3923 switch (vrc)
3924 {
3925 case VERR_NOT_SUPPORTED:
3926 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3927 tr("Handling removing guest directories recursively not supported by installed Guest Additions"));
3928 break;
3929
3930 case VERR_GSTCTL_GUEST_ERROR:
3931 {
3932 GuestErrorInfo ge(GuestErrorInfo::Type_Directory, rcGuest, aPath.c_str());
3933 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Recursively removing guest directory failed: %s"),
3934 GuestBase::getErrorAsString(ge).c_str());
3935 break;
3936 }
3937 default:
3938 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Recursively removing guest directory \"%s\" failed: %Rrc"),
3939 aPath.c_str(), vrc);
3940 break;
3941 }
3942 }
3943 else
3944 {
3945 pProgress.queryInterfaceTo(aProgress.asOutParam());
3946 }
3947
3948 return hrc;
3949}
3950
3951HRESULT GuestSession::environmentScheduleSet(const com::Utf8Str &aName, const com::Utf8Str &aValue)
3952{
3953 LogFlowThisFuncEnter();
3954 int vrc;
3955 {
3956 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3957 vrc = mData.mEnvironmentChanges.setVariable(aName, aValue);
3958 }
3959 HRESULT hrc;
3960 if (RT_SUCCESS(vrc))
3961 hrc = S_OK;
3962 else if (vrc == VERR_ENV_INVALID_VAR_NAME)
3963 hrc = setError(E_INVALIDARG, tr("Invalid environment variable name '%s'"), aName.c_str());
3964 else
3965 hrc = setErrorVrc(vrc, tr("Failed to schedule setting environment variable '%s' to '%s'"), aName.c_str(), aValue.c_str());
3966
3967 LogFlowThisFuncLeave();
3968 return hrc;
3969}
3970
3971HRESULT GuestSession::environmentScheduleUnset(const com::Utf8Str &aName)
3972{
3973 LogFlowThisFuncEnter();
3974 int vrc;
3975 {
3976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3977 vrc = mData.mEnvironmentChanges.unsetVariable(aName);
3978 }
3979 HRESULT hrc;
3980 if (RT_SUCCESS(vrc))
3981 hrc = S_OK;
3982 else if (vrc == VERR_ENV_INVALID_VAR_NAME)
3983 hrc = setError(E_INVALIDARG, tr("Invalid environment variable name '%s'"), aName.c_str());
3984 else
3985 hrc = setErrorVrc(vrc, tr("Failed to schedule unsetting environment variable '%s'"), aName.c_str());
3986
3987 LogFlowThisFuncLeave();
3988 return hrc;
3989}
3990
3991HRESULT GuestSession::environmentGetBaseVariable(const com::Utf8Str &aName, com::Utf8Str &aValue)
3992{
3993 LogFlowThisFuncEnter();
3994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3995
3996 HRESULT hrc;
3997 if (mData.mpBaseEnvironment)
3998 {
3999 int vrc = mData.mpBaseEnvironment->getVariable(aName, &aValue);
4000 if (RT_SUCCESS(vrc))
4001 hrc = S_OK;
4002 else if (vrc == VERR_ENV_INVALID_VAR_NAME)
4003 hrc = setError(E_INVALIDARG, tr("Invalid environment variable name '%s'"), aName.c_str());
4004 else
4005 hrc = setErrorVrc(vrc);
4006 }
4007 else if (mData.mProtocolVersion < 99999)
4008 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the Guest Additions"));
4009 else
4010 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
4011
4012 LogFlowThisFuncLeave();
4013 return hrc;
4014}
4015
4016HRESULT GuestSession::environmentDoesBaseVariableExist(const com::Utf8Str &aName, BOOL *aExists)
4017{
4018 LogFlowThisFuncEnter();
4019 *aExists = FALSE;
4020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4021
4022 HRESULT hrc;
4023 if (mData.mpBaseEnvironment)
4024 {
4025 hrc = S_OK;
4026 *aExists = mData.mpBaseEnvironment->doesVariableExist(aName);
4027 }
4028 else if (mData.mProtocolVersion < 99999)
4029 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the Guest Additions"));
4030 else
4031 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
4032
4033 LogFlowThisFuncLeave();
4034 return hrc;
4035}
4036
4037HRESULT GuestSession::fileCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath, BOOL aSecure,
4038 ComPtr<IGuestFile> &aFile)
4039{
4040 RT_NOREF(aTemplateName, aMode, aPath, aSecure, aFile);
4041 ReturnComNotImplemented();
4042}
4043
4044HRESULT GuestSession::fileExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
4045{
4046 /* By default we return non-existent. */
4047 *aExists = FALSE;
4048
4049 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
4050 return S_OK;
4051
4052 HRESULT hrc = i_isStartedExternal();
4053 if (FAILED(hrc))
4054 return hrc;
4055
4056 LogFlowThisFuncEnter();
4057
4058 GuestFsObjData objData; int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4059 int vrc = i_fileQueryInfo(aPath, RT_BOOL(aFollowSymlinks), objData, &rcGuest);
4060 if (RT_SUCCESS(vrc))
4061 {
4062 *aExists = TRUE;
4063 return S_OK;
4064 }
4065
4066 switch (vrc)
4067 {
4068 case VERR_GSTCTL_GUEST_ERROR:
4069 {
4070 switch (rcGuest)
4071 {
4072 case VERR_PATH_NOT_FOUND:
4073 RT_FALL_THROUGH();
4074 case VERR_FILE_NOT_FOUND:
4075 break;
4076
4077 default:
4078 {
4079 GuestErrorInfo ge(GuestErrorInfo::Type_ToolStat, rcGuest, aPath.c_str());
4080 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying guest file existence failed: %s"),
4081 GuestBase::getErrorAsString(ge).c_str());
4082 break;
4083 }
4084 }
4085
4086 break;
4087 }
4088
4089 case VERR_NOT_A_FILE:
4090 break;
4091
4092 default:
4093 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying guest file information for \"%s\" failed: %Rrc"),
4094 aPath.c_str(), vrc);
4095 break;
4096 }
4097
4098 return hrc;
4099}
4100
4101HRESULT GuestSession::fileOpen(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
4102 ULONG aCreationMode, ComPtr<IGuestFile> &aFile)
4103{
4104 LogFlowThisFuncEnter();
4105
4106 const std::vector<FileOpenExFlag_T> EmptyFlags;
4107 return fileOpenEx(aPath, aAccessMode, aOpenAction, FileSharingMode_All, aCreationMode, EmptyFlags, aFile);
4108}
4109
4110HRESULT GuestSession::fileOpenEx(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
4111 FileSharingMode_T aSharingMode, ULONG aCreationMode,
4112 const std::vector<FileOpenExFlag_T> &aFlags, ComPtr<IGuestFile> &aFile)
4113{
4114 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
4115 return setError(E_INVALIDARG, tr("No file to open specified"));
4116
4117 HRESULT hrc = i_isStartedExternal();
4118 if (FAILED(hrc))
4119 return hrc;
4120
4121 LogFlowThisFuncEnter();
4122
4123 /* Validate aAccessMode. */
4124 switch (aAccessMode)
4125 {
4126 case FileAccessMode_ReadOnly:
4127 RT_FALL_THRU();
4128 case FileAccessMode_WriteOnly:
4129 RT_FALL_THRU();
4130 case FileAccessMode_ReadWrite:
4131 break;
4132 case FileAccessMode_AppendOnly:
4133 RT_FALL_THRU();
4134 case FileAccessMode_AppendRead:
4135 return setError(E_NOTIMPL, tr("Append access modes are not yet implemented"));
4136 default:
4137 return setError(E_INVALIDARG, tr("Unknown FileAccessMode value %u (%#x)"), aAccessMode, aAccessMode);
4138 }
4139
4140 /* Validate aOpenAction to the old format. */
4141 switch (aOpenAction)
4142 {
4143 case FileOpenAction_OpenExisting:
4144 RT_FALL_THRU();
4145 case FileOpenAction_OpenOrCreate:
4146 RT_FALL_THRU();
4147 case FileOpenAction_CreateNew:
4148 RT_FALL_THRU();
4149 case FileOpenAction_CreateOrReplace:
4150 RT_FALL_THRU();
4151 case FileOpenAction_OpenExistingTruncated:
4152 RT_FALL_THRU();
4153 case FileOpenAction_AppendOrCreate:
4154 break;
4155 default:
4156 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
4157 }
4158
4159 /* Validate aSharingMode. */
4160 switch (aSharingMode)
4161 {
4162 case FileSharingMode_All:
4163 break;
4164 case FileSharingMode_Read:
4165 case FileSharingMode_Write:
4166 case FileSharingMode_ReadWrite:
4167 case FileSharingMode_Delete:
4168 case FileSharingMode_ReadDelete:
4169 case FileSharingMode_WriteDelete:
4170 return setError(E_NOTIMPL, tr("Only FileSharingMode_All is currently implemented"));
4171
4172 default:
4173 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
4174 }
4175
4176 /* Combine and validate flags. */
4177 uint32_t fOpenEx = 0;
4178 for (size_t i = 0; i < aFlags.size(); i++)
4179 fOpenEx |= aFlags[i];
4180 if (fOpenEx)
4181 return setError(E_INVALIDARG, tr("Unsupported FileOpenExFlag value(s) in aFlags (%#x)"), fOpenEx);
4182
4183 ComObjPtr <GuestFile> pFile;
4184 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4185 int vrc = i_fileOpenEx(aPath, aAccessMode, aOpenAction, aSharingMode, aCreationMode, aFlags, pFile, &rcGuest);
4186 if (RT_SUCCESS(vrc))
4187 /* Return directory object to the caller. */
4188 hrc = pFile.queryInterfaceTo(aFile.asOutParam());
4189 else
4190 {
4191 switch (vrc)
4192 {
4193 case VERR_NOT_SUPPORTED:
4194 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4195 tr("Handling guest files not supported by installed Guest Additions"));
4196 break;
4197
4198 case VERR_GSTCTL_GUEST_ERROR:
4199 {
4200 GuestErrorInfo ge(GuestErrorInfo::Type_File, rcGuest, aPath.c_str());
4201 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Opening guest file failed: %s"),
4202 GuestBase::getErrorAsString(ge).c_str());
4203 break;
4204 }
4205 default:
4206 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4207 break;
4208 }
4209 }
4210
4211 return hrc;
4212}
4213
4214HRESULT GuestSession::fileQuerySize(const com::Utf8Str &aPath, BOOL aFollowSymlinks, LONG64 *aSize)
4215{
4216 if (aPath.isEmpty())
4217 return setError(E_INVALIDARG, tr("No path specified"));
4218
4219 HRESULT hrc = i_isStartedExternal();
4220 if (FAILED(hrc))
4221 return hrc;
4222
4223 int64_t llSize; int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4224 int vrc = i_fileQuerySize(aPath, aFollowSymlinks != FALSE, &llSize, &rcGuest);
4225 if (RT_SUCCESS(vrc))
4226 {
4227 *aSize = llSize;
4228 }
4229 else
4230 {
4231 if (GuestProcess::i_isGuestError(vrc))
4232 {
4233 GuestErrorInfo ge(GuestErrorInfo::Type_ToolStat, rcGuest, aPath.c_str());
4234 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying guest file size failed: %s"),
4235 GuestBase::getErrorAsString(ge).c_str());
4236 }
4237 else
4238 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying guest file size of \"%s\" failed: %Rrc"),
4239 vrc, aPath.c_str());
4240 }
4241
4242 return hrc;
4243}
4244
4245HRESULT GuestSession::fsObjExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
4246{
4247 if (aPath.isEmpty())
4248 return setError(E_INVALIDARG, tr("No path specified"));
4249
4250 HRESULT hrc = i_isStartedExternal();
4251 if (FAILED(hrc))
4252 return hrc;
4253
4254 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
4255
4256 *aExists = false;
4257
4258 GuestFsObjData objData;
4259 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4260 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
4261 if (RT_SUCCESS(vrc))
4262 {
4263 *aExists = TRUE;
4264 }
4265 else
4266 {
4267 if (GuestProcess::i_isGuestError(vrc))
4268 {
4269 if ( rcGuest == VERR_NOT_A_FILE
4270 || rcGuest == VERR_PATH_NOT_FOUND
4271 || rcGuest == VERR_FILE_NOT_FOUND
4272 || rcGuest == VERR_INVALID_NAME)
4273 {
4274 hrc = S_OK; /* Ignore these vrc values. */
4275 }
4276 else
4277 {
4278 GuestErrorInfo ge(GuestErrorInfo::Type_ToolStat, rcGuest, aPath.c_str());
4279 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying guest file existence information failed: %s"),
4280 GuestBase::getErrorAsString(ge).c_str());
4281 }
4282 }
4283 else
4284 hrc = setErrorVrc(vrc, tr("Querying guest file existence information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4285 }
4286
4287 return hrc;
4288}
4289
4290HRESULT GuestSession::fsObjQueryInfo(const com::Utf8Str &aPath, BOOL aFollowSymlinks, ComPtr<IGuestFsObjInfo> &aInfo)
4291{
4292 if (aPath.isEmpty())
4293 return setError(E_INVALIDARG, tr("No path specified"));
4294
4295 HRESULT hrc = i_isStartedExternal();
4296 if (FAILED(hrc))
4297 return hrc;
4298
4299 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
4300
4301 GuestFsObjData Info; int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4302 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, Info, &rcGuest);
4303 if (RT_SUCCESS(vrc))
4304 {
4305 ComObjPtr<GuestFsObjInfo> ptrFsObjInfo;
4306 hrc = ptrFsObjInfo.createObject();
4307 if (SUCCEEDED(hrc))
4308 {
4309 vrc = ptrFsObjInfo->init(Info);
4310 if (RT_SUCCESS(vrc))
4311 hrc = ptrFsObjInfo.queryInterfaceTo(aInfo.asOutParam());
4312 else
4313 hrc = setErrorVrc(vrc);
4314 }
4315 }
4316 else
4317 {
4318 if (GuestProcess::i_isGuestError(vrc))
4319 {
4320 GuestErrorInfo ge(GuestErrorInfo::Type_ToolStat, rcGuest, aPath.c_str());
4321 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying guest file information failed: %s"),
4322 GuestBase::getErrorAsString(ge).c_str());
4323 }
4324 else
4325 hrc = setErrorVrc(vrc, tr("Querying guest file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4326 }
4327
4328 return hrc;
4329}
4330
4331HRESULT GuestSession::fsObjRemove(const com::Utf8Str &aPath)
4332{
4333 if (RT_UNLIKELY(aPath.isEmpty()))
4334 return setError(E_INVALIDARG, tr("No path specified"));
4335
4336 HRESULT hrc = i_isStartedExternal();
4337 if (FAILED(hrc))
4338 return hrc;
4339
4340 LogFlowThisFunc(("aPath=%s\n", aPath.c_str()));
4341
4342 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4343 int vrc = i_fileRemove(aPath, &rcGuest);
4344 if (RT_FAILURE(vrc))
4345 {
4346 if (GuestProcess::i_isGuestError(vrc))
4347 {
4348 GuestErrorInfo ge(GuestErrorInfo::Type_ToolRm, rcGuest, aPath.c_str());
4349 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Removing guest file failed: %s"),
4350 GuestBase::getErrorAsString(ge).c_str());
4351 }
4352 else
4353 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing guest file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4354 }
4355
4356 return hrc;
4357}
4358
4359HRESULT GuestSession::fsObjRemoveArray(const std::vector<com::Utf8Str> &aPaths, ComPtr<IProgress> &aProgress)
4360{
4361 RT_NOREF(aPaths, aProgress);
4362 return E_NOTIMPL;
4363}
4364
4365HRESULT GuestSession::fsObjRename(const com::Utf8Str &aSource,
4366 const com::Utf8Str &aDestination,
4367 const std::vector<FsObjRenameFlag_T> &aFlags)
4368{
4369 if (RT_UNLIKELY(aSource.isEmpty()))
4370 return setError(E_INVALIDARG, tr("No source path specified"));
4371
4372 if (RT_UNLIKELY(aDestination.isEmpty()))
4373 return setError(E_INVALIDARG, tr("No destination path specified"));
4374
4375 HRESULT hrc = i_isStartedExternal();
4376 if (FAILED(hrc))
4377 return hrc;
4378
4379 /* Combine, validate and convert flags. */
4380 uint32_t fApiFlags = 0;
4381 for (size_t i = 0; i < aFlags.size(); i++)
4382 fApiFlags |= aFlags[i];
4383 if (fApiFlags & ~((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace))
4384 return setError(E_INVALIDARG, tr("Unknown rename flag: %#x"), fApiFlags);
4385
4386 LogFlowThisFunc(("aSource=%s, aDestination=%s\n", aSource.c_str(), aDestination.c_str()));
4387
4388 AssertCompile(FsObjRenameFlag_NoReplace == 0);
4389 AssertCompile(FsObjRenameFlag_Replace != 0);
4390 uint32_t fBackend;
4391 if ((fApiFlags & ((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace)) == FsObjRenameFlag_Replace)
4392 fBackend = PATHRENAME_FLAG_REPLACE;
4393 else
4394 fBackend = PATHRENAME_FLAG_NO_REPLACE;
4395
4396 /* Call worker to do the job. */
4397 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS;
4398 int vrc = i_pathRename(aSource, aDestination, fBackend, &rcGuest);
4399 if (RT_FAILURE(vrc))
4400 {
4401 switch (vrc)
4402 {
4403 case VERR_NOT_SUPPORTED:
4404 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4405 tr("Handling renaming guest paths not supported by installed Guest Additions"));
4406 break;
4407
4408 case VERR_GSTCTL_GUEST_ERROR:
4409 {
4410 GuestErrorInfo ge(GuestErrorInfo::Type_Process, rcGuest, aSource.c_str());
4411 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Renaming guest path failed: %s"),
4412 GuestBase::getErrorAsString(ge).c_str());
4413 break;
4414 }
4415 default:
4416 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming guest path \"%s\" failed: %Rrc"),
4417 aSource.c_str(), vrc);
4418 break;
4419 }
4420 }
4421
4422 return hrc;
4423}
4424
4425HRESULT GuestSession::fsObjMove(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
4426 const std::vector<FsObjMoveFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
4427{
4428 RT_NOREF(aSource, aDestination, aFlags, aProgress);
4429 ReturnComNotImplemented();
4430}
4431
4432HRESULT GuestSession::fsObjMoveArray(const std::vector<com::Utf8Str> &aSource,
4433 const com::Utf8Str &aDestination,
4434 const std::vector<FsObjMoveFlag_T> &aFlags,
4435 ComPtr<IProgress> &aProgress)
4436{
4437 RT_NOREF(aSource, aDestination, aFlags, aProgress);
4438 ReturnComNotImplemented();
4439}
4440
4441HRESULT GuestSession::fsObjCopyArray(const std::vector<com::Utf8Str> &aSource,
4442 const com::Utf8Str &aDestination,
4443 const std::vector<FileCopyFlag_T> &aFlags,
4444 ComPtr<IProgress> &aProgress)
4445{
4446 RT_NOREF(aSource, aDestination, aFlags, aProgress);
4447 ReturnComNotImplemented();
4448}
4449
4450HRESULT GuestSession::fsObjSetACL(const com::Utf8Str &aPath, BOOL aFollowSymlinks, const com::Utf8Str &aAcl, ULONG aMode)
4451{
4452 RT_NOREF(aPath, aFollowSymlinks, aAcl, aMode);
4453 ReturnComNotImplemented();
4454}
4455
4456
4457HRESULT GuestSession::processCreate(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
4458 const std::vector<com::Utf8Str> &aEnvironment,
4459 const std::vector<ProcessCreateFlag_T> &aFlags,
4460 ULONG aTimeoutMS, ComPtr<IGuestProcess> &aGuestProcess)
4461{
4462 LogFlowThisFuncEnter();
4463
4464 std::vector<LONG> affinityIgnored;
4465 return processCreateEx(aExecutable, aArguments, aEnvironment, aFlags, aTimeoutMS, ProcessPriority_Default,
4466 affinityIgnored, aGuestProcess);
4467}
4468
4469HRESULT GuestSession::processCreateEx(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
4470 const std::vector<com::Utf8Str> &aEnvironment,
4471 const std::vector<ProcessCreateFlag_T> &aFlags, ULONG aTimeoutMS,
4472 ProcessPriority_T aPriority, const std::vector<LONG> &aAffinity,
4473 ComPtr<IGuestProcess> &aGuestProcess)
4474{
4475 HRESULT hr = i_isStartedExternal();
4476 if (FAILED(hr))
4477 return hr;
4478
4479 /*
4480 * Must have an executable to execute. If none is given, we try use the
4481 * zero'th argument.
4482 */
4483 const char *pszExecutable = aExecutable.c_str();
4484 if (RT_UNLIKELY(pszExecutable == NULL || *pszExecutable == '\0'))
4485 {
4486 if (aArguments.size() > 0)
4487 pszExecutable = aArguments[0].c_str();
4488 if (pszExecutable == NULL || *pszExecutable == '\0')
4489 return setError(E_INVALIDARG, tr("No command to execute specified"));
4490 }
4491
4492 /* The rest of the input is being validated in i_processCreateEx(). */
4493
4494 LogFlowThisFuncEnter();
4495
4496 /*
4497 * Build the process startup info.
4498 */
4499 GuestProcessStartupInfo procInfo;
4500
4501 /* Executable and arguments. */
4502 procInfo.mExecutable = pszExecutable;
4503 if (aArguments.size())
4504 {
4505 for (size_t i = 0; i < aArguments.size(); i++)
4506 procInfo.mArguments.push_back(aArguments[i]);
4507 }
4508 else /* If no arguments were given, add the executable as argv[0] by default. */
4509 procInfo.mArguments.push_back(procInfo.mExecutable);
4510
4511 /* Combine the environment changes associated with the ones passed in by
4512 the caller, giving priority to the latter. The changes are putenv style
4513 and will be applied to the standard environment for the guest user. */
4514 int vrc = procInfo.mEnvironmentChanges.copy(mData.mEnvironmentChanges);
4515 if (RT_SUCCESS(vrc))
4516 {
4517 size_t idxError = ~(size_t)0;
4518 vrc = procInfo.mEnvironmentChanges.applyPutEnvArray(aEnvironment, &idxError);
4519 if (RT_SUCCESS(vrc))
4520 {
4521 /* Convert the flag array into a mask. */
4522 if (aFlags.size())
4523 for (size_t i = 0; i < aFlags.size(); i++)
4524 procInfo.mFlags |= aFlags[i];
4525
4526 procInfo.mTimeoutMS = aTimeoutMS;
4527
4528 /** @todo use RTCPUSET instead of archaic 64-bit variables! */
4529 if (aAffinity.size())
4530 for (size_t i = 0; i < aAffinity.size(); i++)
4531 if (aAffinity[i])
4532 procInfo.mAffinity |= (uint64_t)1 << i;
4533
4534 procInfo.mPriority = aPriority;
4535
4536 /*
4537 * Create a guest process object.
4538 */
4539 ComObjPtr<GuestProcess> pProcess;
4540 vrc = i_processCreateEx(procInfo, pProcess);
4541 if (RT_SUCCESS(vrc))
4542 {
4543 ComPtr<IGuestProcess> pIProcess;
4544 hr = pProcess.queryInterfaceTo(pIProcess.asOutParam());
4545 if (SUCCEEDED(hr))
4546 {
4547 /*
4548 * Start the process.
4549 */
4550 vrc = pProcess->i_startProcessAsync();
4551 if (RT_SUCCESS(vrc))
4552 {
4553 aGuestProcess = pIProcess;
4554
4555 LogFlowFuncLeaveRC(vrc);
4556 return S_OK;
4557 }
4558
4559 hr = setErrorVrc(vrc, tr("Failed to start guest process: %Rrc"), vrc);
4560 }
4561 }
4562 else if (vrc == VERR_GSTCTL_MAX_CID_OBJECTS_REACHED)
4563 hr = setErrorVrc(vrc, tr("Maximum number of concurrent guest processes per session (%u) reached"),
4564 VBOX_GUESTCTRL_MAX_OBJECTS);
4565 else
4566 hr = setErrorVrc(vrc, tr("Failed to create guest process object: %Rrc"), vrc);
4567 }
4568 else
4569 hr = setErrorBoth(vrc == VERR_ENV_INVALID_VAR_NAME ? E_INVALIDARG : Global::vboxStatusCodeToCOM(vrc), vrc,
4570 tr("Failed to apply environment variable '%s', index %u (%Rrc)'"),
4571 aEnvironment[idxError].c_str(), idxError, vrc);
4572 }
4573 else
4574 hr = setErrorVrc(vrc, tr("Failed to set up the environment: %Rrc"), vrc);
4575
4576 LogFlowFuncLeaveRC(vrc);
4577 return hr;
4578}
4579
4580HRESULT GuestSession::processGet(ULONG aPid, ComPtr<IGuestProcess> &aGuestProcess)
4581
4582{
4583 if (aPid == 0)
4584 return setError(E_INVALIDARG, tr("No valid process ID (PID) specified"));
4585
4586 LogFlowThisFunc(("PID=%RU32\n", aPid));
4587
4588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4589
4590 HRESULT hr = S_OK;
4591
4592 ComObjPtr<GuestProcess> pProcess;
4593 int rc = i_processGetByPID(aPid, &pProcess);
4594 if (RT_FAILURE(rc))
4595 hr = setError(E_INVALIDARG, tr("No process with PID %RU32 found"), aPid);
4596
4597 /* This will set (*aProcess) to NULL if pProgress is NULL. */
4598 HRESULT hr2 = pProcess.queryInterfaceTo(aGuestProcess.asOutParam());
4599 if (SUCCEEDED(hr))
4600 hr = hr2;
4601
4602 LogFlowThisFunc(("aProcess=%p, hr=%Rhrc\n", (IGuestProcess*)aGuestProcess, hr));
4603 return hr;
4604}
4605
4606HRESULT GuestSession::symlinkCreate(const com::Utf8Str &aSource, const com::Utf8Str &aTarget, SymlinkType_T aType)
4607{
4608 RT_NOREF(aSource, aTarget, aType);
4609 ReturnComNotImplemented();
4610}
4611
4612HRESULT GuestSession::symlinkExists(const com::Utf8Str &aSymlink, BOOL *aExists)
4613
4614{
4615 RT_NOREF(aSymlink, aExists);
4616 ReturnComNotImplemented();
4617}
4618
4619HRESULT GuestSession::symlinkRead(const com::Utf8Str &aSymlink, const std::vector<SymlinkReadFlag_T> &aFlags,
4620 com::Utf8Str &aTarget)
4621{
4622 RT_NOREF(aSymlink, aFlags, aTarget);
4623 ReturnComNotImplemented();
4624}
4625
4626HRESULT GuestSession::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
4627{
4628 /* Note: No call to i_isReadyExternal() needed here, as the session might not has been started (yet). */
4629
4630 LogFlowThisFuncEnter();
4631
4632 HRESULT hrc = S_OK;
4633
4634 /*
4635 * Note: Do not hold any locks here while waiting!
4636 */
4637 int rcGuest = VERR_IPE_UNINITIALIZED_STATUS; GuestSessionWaitResult_T waitResult;
4638 int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &rcGuest);
4639 if (RT_SUCCESS(vrc))
4640 *aReason = waitResult;
4641 else
4642 {
4643 switch (vrc)
4644 {
4645 case VERR_GSTCTL_GUEST_ERROR:
4646 {
4647 GuestErrorInfo ge(GuestErrorInfo::Type_Session, rcGuest, mData.mSession.mName.c_str());
4648 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Waiting for guest process failed: %s"),
4649 GuestBase::getErrorAsString(ge).c_str());
4650 break;
4651 }
4652 case VERR_TIMEOUT:
4653 *aReason = GuestSessionWaitResult_Timeout;
4654 break;
4655
4656 default:
4657 {
4658 const char *pszSessionName = mData.mSession.mName.c_str();
4659 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4660 tr("Waiting for guest session \"%s\" failed: %Rrc"),
4661 pszSessionName ? pszSessionName : tr("Unnamed"), vrc);
4662 break;
4663 }
4664 }
4665 }
4666
4667 LogFlowFuncLeaveRC(vrc);
4668 return hrc;
4669}
4670
4671HRESULT GuestSession::waitForArray(const std::vector<GuestSessionWaitForFlag_T> &aWaitFor, ULONG aTimeoutMS,
4672 GuestSessionWaitResult_T *aReason)
4673{
4674 /* Note: No call to i_isReadyExternal() needed here, as the session might not has been started (yet). */
4675
4676 LogFlowThisFuncEnter();
4677
4678 /*
4679 * Note: Do not hold any locks here while waiting!
4680 */
4681 uint32_t fWaitFor = GuestSessionWaitForFlag_None;
4682 for (size_t i = 0; i < aWaitFor.size(); i++)
4683 fWaitFor |= aWaitFor[i];
4684
4685 return WaitFor(fWaitFor, aTimeoutMS, aReason);
4686}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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