VirtualBox

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

Last change on this file since 79133 was 79133, checked in by vboxsync, 5 years ago

tdAddGuestCtrl.py,Main: Added @todos for incorrect directoryOpen behavior. bugref:9320

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

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