VirtualBox

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

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

Guest Control: Added ability of specifying an optional current working directory to started guest processes. This needs Guest Additions which support this. bugref:8053

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