VirtualBox

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

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

Guest Control: Initial commit (work in progress, disabled by default). bugref:9783

IGuestDirectory:

Added new attributes id + status + an own event source. Also added for rewind support via rewind().

New event types for guest directory [un]registration, state changes and entry reads.

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

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