VirtualBox

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

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

Guest Control/Main: Check for unexpected event types in GuestSession::i_waitForStatusChange(). bugref:10157

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

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