VirtualBox

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

Last change on this file since 94521 was 93168, checked in by vboxsync, 2 years ago

Guest Control/Main: Use i_setSessionStatus() in GuestSession::i_startSession() to have and use one centralized way of setting a session's status.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use