VirtualBox

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

Last change on this file since 101381 was 100325, checked in by vboxsync, 11 months ago

Guest Control/Main: Return from GuestSession::i_sendMessage() in case the VMMDev pointer isn't available anymore.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use