VirtualBox

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

Last change on this file since 93163 was 93163, checked in by vboxsync, 3 years ago

Guest Control/Main: Fixed outdated comments.

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

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