VirtualBox

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

Last change on this file since 78750 was 78743, checked in by vboxsync, 6 years ago

Main/GuestSessionImpl.cpp: Cleaned up i_copyToGuest & i_copyFromGuest to deal with the troubles in testset:11579513. (untested) bugref:9320

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 138.3 KB
Line 
1/* $Id: GuestSessionImpl.cpp 78743 2019-05-25 13:39:09Z 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 VERR_COM_UNEXPECTED;
1134
1135 /* Register a new object ID. */
1136 uint32_t idObject;
1137 int rc = i_objectRegister(pDirectory, SESSIONOBJECTTYPE_DIRECTORY, &idObject);
1138 if (RT_FAILURE(rc))
1139 {
1140 pDirectory.setNull();
1141 return rc;
1142 }
1143
1144 Console *pConsole = mParent->i_getConsole();
1145 AssertPtr(pConsole);
1146
1147 int 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 int vrc;
2011 GuestSessionTaskInternalStart* pTask = NULL;
2012 try
2013 {
2014 pTask = new GuestSessionTaskInternalStart(this);
2015 if (!pTask->isOk())
2016 {
2017 delete pTask;
2018 LogFlow(("GuestSession: Could not create GuestSessionTaskInternalStart object\n"));
2019 throw VERR_MEMOBJ_INIT_FAILED;
2020 }
2021
2022 /* Asynchronously open the session on the guest by kicking off a worker thread. */
2023 /* Note: This function deletes pTask in case of exceptions, so there is no need in the call of delete operator. */
2024 HRESULT hrc = pTask->createThread();
2025 vrc = Global::vboxStatusCodeFromCOM(hrc);
2026 }
2027 catch(std::bad_alloc &)
2028 {
2029 vrc = VERR_NO_MEMORY;
2030 }
2031 catch(int eVRC)
2032 {
2033 vrc = eVRC;
2034 LogFlow(("GuestSession: Could not create thread for GuestSessionTaskInternalOpen task %Rrc\n", vrc));
2035 }
2036
2037 LogFlowFuncLeaveRC(vrc);
2038 return vrc;
2039}
2040
2041/**
2042 * Static function to start a guest session asynchronously.
2043 *
2044 * @returns IPRT status code.
2045 * @param pTask Task object to use for starting the guest session.
2046 */
2047/* static */
2048int GuestSession::i_startSessionThreadTask(GuestSessionTaskInternalStart *pTask)
2049{
2050 LogFlowFunc(("pTask=%p\n", pTask));
2051 AssertPtr(pTask);
2052
2053 const ComObjPtr<GuestSession> pSession(pTask->Session());
2054 Assert(!pSession.isNull());
2055
2056 AutoCaller autoCaller(pSession);
2057 if (FAILED(autoCaller.rc()))
2058 return VERR_COM_INVALID_OBJECT_STATE;
2059
2060 int vrc = pSession->i_startSession(NULL /* Guest rc, ignored */);
2061 /* Nothing to do here anymore. */
2062
2063 LogFlowFuncLeaveRC(vrc);
2064 return vrc;
2065}
2066
2067/**
2068 * Registers an object with the session, i.e. allocates an object ID.
2069 *
2070 * @return VBox status code.
2071 * @retval VERR_GSTCTL_MAX_OBJECTS_REACHED if the maximum of concurrent objects
2072 * is reached.
2073 * @param pObject Guest object to register (weak pointer). Optional.
2074 * @param enmType Session object type to register.
2075 * @param pidObject Where to return the object ID on success. Optional.
2076 */
2077int GuestSession::i_objectRegister(GuestObject *pObject, SESSIONOBJECTTYPE enmType, uint32_t *pidObject)
2078{
2079 /* pObject can be NULL. */
2080 /* pidObject is optional. */
2081
2082 /*
2083 * Pick a random bit as starting point. If it's in use, search forward
2084 * for a free one, wrapping around. We've reserved both the zero'th and
2085 * max-1 IDs (see Data constructor).
2086 */
2087 uint32_t idObject = RTRandU32Ex(1, VBOX_GUESTCTRL_MAX_OBJECTS - 2);
2088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2089 if (!ASMBitTestAndSet(&mData.bmObjectIds[0], idObject))
2090 { /* likely */ }
2091 else if (mData.mObjects.size() < VBOX_GUESTCTRL_MAX_OBJECTS - 2 /* First and last are not used */)
2092 {
2093 /* Forward search. */
2094 int iHit = ASMBitNextClear(&mData.bmObjectIds[0], VBOX_GUESTCTRL_MAX_OBJECTS, idObject);
2095 if (iHit < 0)
2096 iHit = ASMBitFirstClear(&mData.bmObjectIds[0], VBOX_GUESTCTRL_MAX_OBJECTS);
2097 AssertLogRelMsgReturn(iHit >= 0, ("object count: %#zu\n", mData.mObjects.size()), VERR_GSTCTL_MAX_CID_OBJECTS_REACHED);
2098 idObject = iHit;
2099 AssertLogRelMsgReturn(!ASMBitTestAndSet(&mData.bmObjectIds[0], idObject), ("idObject=%#x\n", idObject), VERR_INTERNAL_ERROR_2);
2100 }
2101 else
2102 {
2103 LogFunc(("Maximum number of objects reached (enmType=%RU32, %zu objects)\n", enmType, mData.mObjects.size()));
2104 return VERR_GSTCTL_MAX_CID_OBJECTS_REACHED;
2105 }
2106
2107 Log2Func(("enmType=%RU32 -> idObject=%RU32 (%zu objects)\n", enmType, idObject, mData.mObjects.size()));
2108
2109 try
2110 {
2111 mData.mObjects[idObject].pObject = pObject; /* Can be NULL. */
2112 mData.mObjects[idObject].enmType = enmType;
2113 mData.mObjects[idObject].msBirth = RTTimeMilliTS();
2114 }
2115 catch (std::bad_alloc &)
2116 {
2117 ASMBitClear(&mData.bmObjectIds[0], idObject);
2118 return VERR_NO_MEMORY;
2119 }
2120
2121 if (pidObject)
2122 *pidObject = idObject;
2123
2124 return VINF_SUCCESS;
2125}
2126
2127/**
2128 * Unregisters an object from the session objects list.
2129 *
2130 * @retval VINF_SUCCESS on success.
2131 * @retval VERR_NOT_FOUND if the object ID was not found.
2132 * @param idObject Object ID to unregister.
2133 */
2134int GuestSession::i_objectUnregister(uint32_t idObject)
2135{
2136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2137
2138 int rc = VINF_SUCCESS;
2139 AssertMsgStmt(ASMBitTestAndClear(&mData.bmObjectIds, idObject), ("idObject=%#x\n", idObject), rc = VERR_NOT_FOUND);
2140
2141 SessionObjects::iterator ItObj = mData.mObjects.find(idObject);
2142 AssertMsgReturn(ItObj != mData.mObjects.end(), ("idObject=%#x\n", idObject), VERR_NOT_FOUND);
2143 mData.mObjects.erase(ItObj);
2144
2145 return rc;
2146}
2147
2148/**
2149 * Unregisters all objects from the session list.
2150 *
2151 * @returns VBox status code.
2152 */
2153int GuestSession::i_objectsUnregister(void)
2154{
2155 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2156
2157 LogFlowThisFunc(("Unregistering directories (%zu total)\n", mData.mDirectories.size()));
2158
2159 SessionDirectories::iterator itDirs;
2160 while ((itDirs = mData.mDirectories.begin()) != mData.mDirectories.end())
2161 {
2162 alock.release();
2163 i_directoryUnregister(itDirs->second);
2164 alock.acquire();
2165 }
2166
2167 Assert(mData.mDirectories.size() == 0);
2168 mData.mDirectories.clear();
2169
2170 LogFlowThisFunc(("Unregistering files (%zu total)\n", mData.mFiles.size()));
2171
2172 SessionFiles::iterator itFiles;
2173 while ((itFiles = mData.mFiles.begin()) != mData.mFiles.end())
2174 {
2175 alock.release();
2176 i_fileUnregister(itFiles->second);
2177 alock.acquire();
2178 }
2179
2180 Assert(mData.mFiles.size() == 0);
2181 mData.mFiles.clear();
2182
2183 LogFlowThisFunc(("Unregistering processes (%zu total)\n", mData.mProcesses.size()));
2184
2185 SessionProcesses::iterator itProcs;
2186 while ((itProcs = mData.mProcesses.begin()) != mData.mProcesses.end())
2187 {
2188 alock.release();
2189 i_processUnregister(itProcs->second);
2190 alock.acquire();
2191 }
2192
2193 Assert(mData.mProcesses.size() == 0);
2194 mData.mProcesses.clear();
2195
2196 return VINF_SUCCESS;
2197}
2198
2199/**
2200 * Notifies all registered objects about a session status change.
2201 *
2202 * @returns VBox status code.
2203 * @param enmSessionStatus Session status to notify objects about.
2204 */
2205int GuestSession::i_objectsNotifyAboutStatusChange(GuestSessionStatus_T enmSessionStatus)
2206{
2207 LogFlowThisFunc(("enmSessionStatus=%RU32\n", enmSessionStatus));
2208
2209 int vrc = VINF_SUCCESS;
2210
2211 SessionObjects::iterator itObjs = mData.mObjects.begin();
2212 while (itObjs != mData.mObjects.end())
2213 {
2214 GuestObject *pObj = itObjs->second.pObject;
2215 if (pObj) /* pObject can be NULL (weak pointer). */
2216 {
2217 int vrc2 = pObj->i_onSessionStatusChange(enmSessionStatus);
2218 if (RT_SUCCESS(vrc))
2219 vrc = vrc2;
2220
2221 /* If the session got terminated, make sure to cancel all wait events for
2222 * the current object. */
2223 if (i_isTerminated())
2224 pObj->cancelWaitEvents();
2225 }
2226
2227 ++itObjs;
2228 }
2229
2230 LogFlowFuncLeaveRC(vrc);
2231 return vrc;
2232}
2233
2234int GuestSession::i_pathRename(const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags, int *prcGuest)
2235{
2236 AssertReturn(!(uFlags & ~PATHRENAME_FLAG_VALID_MASK), VERR_INVALID_PARAMETER);
2237
2238 LogFlowThisFunc(("strSource=%s, strDest=%s, uFlags=0x%x\n",
2239 strSource.c_str(), strDest.c_str(), uFlags));
2240
2241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2242
2243 GuestWaitEvent *pEvent = NULL;
2244 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2245 if (RT_FAILURE(vrc))
2246 return vrc;
2247
2248 /* Prepare HGCM call. */
2249 VBOXHGCMSVCPARM paParms[8];
2250 int i = 0;
2251 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2252 HGCMSvcSetPv(&paParms[i++], (void*)strSource.c_str(),
2253 (ULONG)strSource.length() + 1);
2254 HGCMSvcSetPv(&paParms[i++], (void*)strDest.c_str(),
2255 (ULONG)strDest.length() + 1);
2256 HGCMSvcSetU32(&paParms[i++], uFlags);
2257
2258 alock.release(); /* Drop write lock before sending. */
2259
2260 vrc = i_sendMessage(HOST_MSG_PATH_RENAME, i, paParms);
2261 if (RT_SUCCESS(vrc))
2262 {
2263 vrc = pEvent->Wait(30 * 1000);
2264 if ( vrc == VERR_GSTCTL_GUEST_ERROR
2265 && prcGuest)
2266 *prcGuest = pEvent->GuestResult();
2267 }
2268
2269 unregisterWaitEvent(pEvent);
2270
2271 LogFlowFuncLeaveRC(vrc);
2272 return vrc;
2273}
2274
2275/**
2276 * Returns the user's absolute documents path, if any.
2277 *
2278 * @return VBox status code.
2279 * @param strPath Where to store the user's document path.
2280 * @param prcGuest Guest rc, when returning VERR_GSTCTL_GUEST_ERROR.
2281 * Any other return code indicates some host side error.
2282 */
2283int GuestSession::i_pathUserDocuments(Utf8Str &strPath, int *prcGuest)
2284{
2285 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2286
2287 /** @todo Cache the user's document path? */
2288
2289 GuestWaitEvent *pEvent = NULL;
2290 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2291 if (RT_FAILURE(vrc))
2292 return vrc;
2293
2294 /* Prepare HGCM call. */
2295 VBOXHGCMSVCPARM paParms[2];
2296 int i = 0;
2297 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2298
2299 alock.release(); /* Drop write lock before sending. */
2300
2301 vrc = i_sendMessage(HOST_MSG_PATH_USER_DOCUMENTS, i, paParms);
2302 if (RT_SUCCESS(vrc))
2303 {
2304 vrc = pEvent->Wait(30 * 1000);
2305 if (RT_SUCCESS(vrc))
2306 {
2307 strPath = pEvent->Payload().ToString();
2308 }
2309 else
2310 {
2311 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2312 {
2313 if (prcGuest)
2314 *prcGuest = pEvent->GuestResult();
2315 }
2316 }
2317 }
2318
2319 unregisterWaitEvent(pEvent);
2320
2321 LogFlowFuncLeaveRC(vrc);
2322 return vrc;
2323}
2324
2325/**
2326 * Returns the user's absolute home path, if any.
2327 *
2328 * @return VBox status code.
2329 * @param strPath Where to store the user's home path.
2330 * @param prcGuest Guest rc, when returning VERR_GSTCTL_GUEST_ERROR.
2331 * Any other return code indicates some host side error.
2332 */
2333int GuestSession::i_pathUserHome(Utf8Str &strPath, int *prcGuest)
2334{
2335 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2336
2337 /** @todo Cache the user's home path? */
2338
2339 GuestWaitEvent *pEvent = NULL;
2340 int vrc = registerWaitEvent(mData.mSession.mID, mData.mObjectID, &pEvent);
2341 if (RT_FAILURE(vrc))
2342 return vrc;
2343
2344 /* Prepare HGCM call. */
2345 VBOXHGCMSVCPARM paParms[2];
2346 int i = 0;
2347 HGCMSvcSetU32(&paParms[i++], pEvent->ContextID());
2348
2349 alock.release(); /* Drop write lock before sending. */
2350
2351 vrc = i_sendMessage(HOST_MSG_PATH_USER_HOME, i, paParms);
2352 if (RT_SUCCESS(vrc))
2353 {
2354 vrc = pEvent->Wait(30 * 1000);
2355 if (RT_SUCCESS(vrc))
2356 {
2357 strPath = pEvent->Payload().ToString();
2358 }
2359 else
2360 {
2361 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2362 {
2363 if (prcGuest)
2364 *prcGuest = pEvent->GuestResult();
2365 }
2366 }
2367 }
2368
2369 unregisterWaitEvent(pEvent);
2370
2371 LogFlowFuncLeaveRC(vrc);
2372 return vrc;
2373}
2374
2375/**
2376 * Unregisters a process object from a session.
2377 *
2378 * @return VBox status code. VERR_NOT_FOUND if the process is not registered (anymore).
2379 * @param pProcess Process object to unregister from session.
2380 */
2381int GuestSession::i_processUnregister(GuestProcess *pProcess)
2382{
2383 AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
2384
2385 LogFlowThisFunc(("pProcess=%p\n", pProcess));
2386
2387 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2388
2389 const uint32_t idObject = pProcess->getObjectID();
2390
2391 LogFlowFunc(("Removing process (objectID=%RU32) ...\n", idObject));
2392
2393 int rc = i_objectUnregister(idObject);
2394 if (RT_FAILURE(rc))
2395 return rc;
2396
2397 SessionProcesses::iterator itProcs = mData.mProcesses.find(idObject);
2398 AssertReturn(itProcs != mData.mProcesses.end(), VERR_NOT_FOUND);
2399
2400 /* Make sure to consume the pointer before the one of the iterator gets released. */
2401 ComObjPtr<GuestProcess> pProc = pProcess;
2402
2403 ULONG uPID;
2404 HRESULT hr = pProc->COMGETTER(PID)(&uPID);
2405 ComAssertComRC(hr);
2406
2407 LogFlowFunc(("Removing process ID=%RU32 (session %RU32, guest PID %RU32, now total %zu processes)\n",
2408 idObject, mData.mSession.mID, uPID, mData.mProcesses.size()));
2409
2410 rc = pProcess->i_onUnregister();
2411 AssertRCReturn(rc, rc);
2412
2413 mData.mProcesses.erase(itProcs);
2414
2415 alock.release(); /* Release lock before firing off event. */
2416
2417 fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProc, uPID, false /* Process unregistered */);
2418
2419 pProc.setNull();
2420
2421 LogFlowFuncLeaveRC(rc);
2422 return rc;
2423}
2424
2425/**
2426 * Creates but does *not* start the process yet.
2427 *
2428 * See GuestProcess::startProcess() or GuestProcess::startProcessAsync() for
2429 * starting the process.
2430 *
2431 * @return IPRT status code.
2432 * @param procInfo
2433 * @param pProcess
2434 */
2435int GuestSession::i_processCreateEx(GuestProcessStartupInfo &procInfo, ComObjPtr<GuestProcess> &pProcess)
2436{
2437 LogFlowFunc(("mExe=%s, mFlags=%x, mTimeoutMS=%RU32\n",
2438 procInfo.mExecutable.c_str(), procInfo.mFlags, procInfo.mTimeoutMS));
2439#ifdef DEBUG
2440 if (procInfo.mArguments.size())
2441 {
2442 LogFlowFunc(("Arguments:"));
2443 ProcessArguments::const_iterator it = procInfo.mArguments.begin();
2444 while (it != procInfo.mArguments.end())
2445 {
2446 LogFlow((" %s", (*it).c_str()));
2447 ++it;
2448 }
2449 LogFlow(("\n"));
2450 }
2451#endif
2452
2453 /* Validate flags. */
2454 if (procInfo.mFlags)
2455 {
2456 if ( !(procInfo.mFlags & ProcessCreateFlag_IgnoreOrphanedProcesses)
2457 && !(procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
2458 && !(procInfo.mFlags & ProcessCreateFlag_Hidden)
2459 && !(procInfo.mFlags & ProcessCreateFlag_Profile)
2460 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
2461 && !(procInfo.mFlags & ProcessCreateFlag_WaitForStdErr))
2462 {
2463 return VERR_INVALID_PARAMETER;
2464 }
2465 }
2466
2467 if ( (procInfo.mFlags & ProcessCreateFlag_WaitForProcessStartOnly)
2468 && ( (procInfo.mFlags & ProcessCreateFlag_WaitForStdOut)
2469 || (procInfo.mFlags & ProcessCreateFlag_WaitForStdErr)
2470 )
2471 )
2472 {
2473 return VERR_INVALID_PARAMETER;
2474 }
2475
2476 if (procInfo.mPriority)
2477 {
2478 if (!(procInfo.mPriority & ProcessPriority_Default))
2479 return VERR_INVALID_PARAMETER;
2480 }
2481
2482 /* Adjust timeout.
2483 * If set to 0, we define an infinite timeout (unlimited process run time). */
2484 if (procInfo.mTimeoutMS == 0)
2485 procInfo.mTimeoutMS = UINT32_MAX;
2486
2487 /** @todo Implement process priority + affinity. */
2488
2489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2490
2491 /* Create the process object. */
2492 HRESULT hr = pProcess.createObject();
2493 if (FAILED(hr))
2494 return VERR_COM_UNEXPECTED;
2495
2496 /* Register a new object ID. */
2497 uint32_t idObject;
2498 int rc = i_objectRegister(pProcess, SESSIONOBJECTTYPE_PROCESS, &idObject);
2499 if (RT_FAILURE(rc))
2500 {
2501 pProcess.setNull();
2502 return rc;
2503 }
2504
2505 rc = pProcess->init(mParent->i_getConsole() /* Console */, this /* Session */, idObject,
2506 procInfo, mData.mpBaseEnvironment);
2507 if (RT_FAILURE(rc))
2508 return rc;
2509
2510 /* Add the created process to our map. */
2511 try
2512 {
2513 mData.mProcesses[idObject] = pProcess;
2514
2515 LogFlowFunc(("Added new process (Session: %RU32) with process ID=%RU32 (now total %zu processes)\n",
2516 mData.mSession.mID, idObject, mData.mProcesses.size()));
2517
2518 alock.release(); /* Release lock before firing off event. */
2519
2520 fireGuestProcessRegisteredEvent(mEventSource, this /* Session */, pProcess, 0 /* PID */, true /* Process registered */);
2521 }
2522 catch (std::bad_alloc &)
2523 {
2524 rc = VERR_NO_MEMORY;
2525 }
2526
2527 return rc;
2528}
2529
2530inline bool GuestSession::i_processExists(uint32_t uProcessID, ComObjPtr<GuestProcess> *pProcess)
2531{
2532 SessionProcesses::const_iterator it = mData.mProcesses.find(uProcessID);
2533 if (it != mData.mProcesses.end())
2534 {
2535 if (pProcess)
2536 *pProcess = it->second;
2537 return true;
2538 }
2539 return false;
2540}
2541
2542inline int GuestSession::i_processGetByPID(ULONG uPID, ComObjPtr<GuestProcess> *pProcess)
2543{
2544 AssertReturn(uPID, false);
2545 /* pProcess is optional. */
2546
2547 SessionProcesses::iterator itProcs = mData.mProcesses.begin();
2548 for (; itProcs != mData.mProcesses.end(); ++itProcs)
2549 {
2550 ComObjPtr<GuestProcess> pCurProc = itProcs->second;
2551 AutoCaller procCaller(pCurProc);
2552 if (procCaller.rc())
2553 return VERR_COM_INVALID_OBJECT_STATE;
2554
2555 ULONG uCurPID;
2556 HRESULT hr = pCurProc->COMGETTER(PID)(&uCurPID);
2557 ComAssertComRC(hr);
2558
2559 if (uCurPID == uPID)
2560 {
2561 if (pProcess)
2562 *pProcess = pCurProc;
2563 return VINF_SUCCESS;
2564 }
2565 }
2566
2567 return VERR_NOT_FOUND;
2568}
2569
2570int GuestSession::i_sendMessage(uint32_t uMessage, uint32_t uParms, PVBOXHGCMSVCPARM paParms,
2571 uint64_t fDst /*= VBOX_GUESTCTRL_DST_SESSION*/)
2572{
2573 LogFlowThisFuncEnter();
2574
2575#ifndef VBOX_GUESTCTRL_TEST_CASE
2576 ComObjPtr<Console> pConsole = mParent->i_getConsole();
2577 Assert(!pConsole.isNull());
2578
2579 /* Forward the information to the VMM device. */
2580 VMMDev *pVMMDev = pConsole->i_getVMMDev();
2581 AssertPtr(pVMMDev);
2582
2583 LogFlowThisFunc(("uMessage=%RU32 (%s), uParms=%RU32\n", uMessage, GstCtrlHostMsgtoStr((guestControl::eHostMsg)uMessage), uParms));
2584
2585 /* HACK ALERT! We extend the first parameter to 64-bit and use the
2586 two topmost bits for call destination information. */
2587 Assert(fDst == VBOX_GUESTCTRL_DST_SESSION || fDst == VBOX_GUESTCTRL_DST_ROOT_SVC || fDst == VBOX_GUESTCTRL_DST_BOTH);
2588 Assert(paParms[0].type == VBOX_HGCM_SVC_PARM_32BIT);
2589 paParms[0].type = VBOX_HGCM_SVC_PARM_64BIT;
2590 paParms[0].u.uint64 = (uint64_t)paParms[0].u.uint32 | fDst;
2591
2592 /* Make the call. */
2593 int vrc = pVMMDev->hgcmHostCall(HGCMSERVICE_NAME, uMessage, uParms, paParms);
2594 if (RT_FAILURE(vrc))
2595 {
2596 /** @todo What to do here? */
2597 }
2598#else
2599 /* Not needed within testcases. */
2600 int vrc = VINF_SUCCESS;
2601#endif
2602 LogFlowFuncLeaveRC(vrc);
2603 return vrc;
2604}
2605
2606/* static */
2607HRESULT GuestSession::i_setErrorExternal(VirtualBoxBase *pInterface, int rcGuest)
2608{
2609 AssertPtr(pInterface);
2610 AssertMsg(RT_FAILURE(rcGuest), ("Guest rc does not indicate a failure when setting error\n"));
2611
2612 return pInterface->setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, GuestSession::i_guestErrorToString(rcGuest).c_str());
2613}
2614
2615/* Does not do locking; caller is responsible for that! */
2616int GuestSession::i_setSessionStatus(GuestSessionStatus_T sessionStatus, int sessionRc)
2617{
2618 LogFlowThisFunc(("oldStatus=%RU32, newStatus=%RU32, sessionRc=%Rrc\n",
2619 mData.mStatus, sessionStatus, sessionRc));
2620
2621 if (sessionStatus == GuestSessionStatus_Error)
2622 {
2623 AssertMsg(RT_FAILURE(sessionRc), ("Guest rc must be an error (%Rrc)\n", sessionRc));
2624 /* Do not allow overwriting an already set error. If this happens
2625 * this means we forgot some error checking/locking somewhere. */
2626 AssertMsg(RT_SUCCESS(mData.mRC), ("Guest rc already set (to %Rrc)\n", mData.mRC));
2627 }
2628 else
2629 AssertMsg(RT_SUCCESS(sessionRc), ("Guest rc must not be an error (%Rrc)\n", sessionRc));
2630
2631 int vrc = VINF_SUCCESS;
2632
2633 if (mData.mStatus != sessionStatus)
2634 {
2635 mData.mStatus = sessionStatus;
2636 mData.mRC = sessionRc;
2637
2638 /* Make sure to notify all underlying objects first. */
2639 vrc = i_objectsNotifyAboutStatusChange(sessionStatus);
2640
2641 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
2642 HRESULT hr = errorInfo.createObject();
2643 ComAssertComRC(hr);
2644 int rc2 = errorInfo->initEx(VBOX_E_IPRT_ERROR, sessionRc,
2645 COM_IIDOF(IGuestSession), getComponentName(),
2646 i_guestErrorToString(sessionRc));
2647 AssertRC(rc2);
2648
2649 fireGuestSessionStateChangedEvent(mEventSource, this,
2650 mData.mSession.mID, sessionStatus, errorInfo);
2651 }
2652
2653 LogFlowFuncLeaveRC(vrc);
2654 return vrc;
2655}
2656
2657int GuestSession::i_signalWaiters(GuestSessionWaitResult_T enmWaitResult, int rc /*= VINF_SUCCESS */)
2658{
2659 RT_NOREF(enmWaitResult, rc);
2660
2661 /*LogFlowThisFunc(("enmWaitResult=%d, rc=%Rrc, mWaitCount=%RU32, mWaitEvent=%p\n",
2662 enmWaitResult, rc, mData.mWaitCount, mData.mWaitEvent));*/
2663
2664 /* Note: No write locking here -- already done in the caller. */
2665
2666 int vrc = VINF_SUCCESS;
2667 /*if (mData.mWaitEvent)
2668 vrc = mData.mWaitEvent->Signal(enmWaitResult, rc);*/
2669 LogFlowFuncLeaveRC(vrc);
2670 return vrc;
2671}
2672
2673/**
2674 * Determines the protocol version (sets mData.mProtocolVersion).
2675 *
2676 * This is called from the init method prior to to establishing a guest
2677 * session.
2678 *
2679 * @return IPRT status code.
2680 */
2681int GuestSession::i_determineProtocolVersion(void)
2682{
2683 /*
2684 * We currently do this based on the reported guest additions version,
2685 * ASSUMING that VBoxService and VBoxDrv are at the same version.
2686 */
2687 ComObjPtr<Guest> pGuest = mParent;
2688 AssertReturn(!pGuest.isNull(), VERR_NOT_SUPPORTED);
2689 uint32_t uGaVersion = pGuest->i_getAdditionsVersion();
2690
2691 /* Everyone supports version one, if they support anything at all. */
2692 mData.mProtocolVersion = 1;
2693
2694 /* Guest control 2.0 was introduced with 4.3.0. */
2695 if (uGaVersion >= VBOX_FULL_VERSION_MAKE(4,3,0))
2696 mData.mProtocolVersion = 2; /* Guest control 2.0. */
2697
2698 LogFlowThisFunc(("uGaVersion=%u.%u.%u => mProtocolVersion=%u\n",
2699 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
2700 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
2701
2702 /*
2703 * Inform the user about outdated guest additions (VM release log).
2704 */
2705 if (mData.mProtocolVersion < 2)
2706 LogRelMax(3, (tr("Warning: Guest Additions v%u.%u.%u only supports the older guest control protocol version %u.\n"
2707 " Please upgrade GAs to the current version to get full guest control capabilities.\n"),
2708 VBOX_FULL_VERSION_GET_MAJOR(uGaVersion), VBOX_FULL_VERSION_GET_MINOR(uGaVersion),
2709 VBOX_FULL_VERSION_GET_BUILD(uGaVersion), mData.mProtocolVersion));
2710
2711 return VINF_SUCCESS;
2712}
2713
2714int GuestSession::i_waitFor(uint32_t fWaitFlags, ULONG uTimeoutMS, GuestSessionWaitResult_T &waitResult, int *prcGuest)
2715{
2716 LogFlowThisFuncEnter();
2717
2718 AssertReturn(fWaitFlags, VERR_INVALID_PARAMETER);
2719
2720 /*LogFlowThisFunc(("fWaitFlags=0x%x, uTimeoutMS=%RU32, mStatus=%RU32, mWaitCount=%RU32, mWaitEvent=%p, prcGuest=%p\n",
2721 fWaitFlags, uTimeoutMS, mData.mStatus, mData.mWaitCount, mData.mWaitEvent, prcGuest));*/
2722
2723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2724
2725 /* Did some error occur before? Then skip waiting and return. */
2726 if (mData.mStatus == GuestSessionStatus_Error)
2727 {
2728 waitResult = GuestSessionWaitResult_Error;
2729 AssertMsg(RT_FAILURE(mData.mRC), ("No error rc (%Rrc) set when guest session indicated an error\n", mData.mRC));
2730 if (prcGuest)
2731 *prcGuest = mData.mRC; /* Return last set error. */
2732 return VERR_GSTCTL_GUEST_ERROR;
2733 }
2734
2735 /* Guest Additions < 4.3 don't support session handling, skip. */
2736 if (mData.mProtocolVersion < 2)
2737 {
2738 waitResult = GuestSessionWaitResult_WaitFlagNotSupported;
2739
2740 LogFlowThisFunc(("Installed Guest Additions don't support waiting for dedicated sessions, skipping\n"));
2741 return VINF_SUCCESS;
2742 }
2743
2744 waitResult = GuestSessionWaitResult_None;
2745 if (fWaitFlags & GuestSessionWaitForFlag_Terminate)
2746 {
2747 switch (mData.mStatus)
2748 {
2749 case GuestSessionStatus_Terminated:
2750 case GuestSessionStatus_Down:
2751 waitResult = GuestSessionWaitResult_Terminate;
2752 break;
2753
2754 case GuestSessionStatus_TimedOutKilled:
2755 case GuestSessionStatus_TimedOutAbnormally:
2756 waitResult = GuestSessionWaitResult_Timeout;
2757 break;
2758
2759 case GuestSessionStatus_Error:
2760 /* Handled above. */
2761 break;
2762
2763 case GuestSessionStatus_Started:
2764 waitResult = GuestSessionWaitResult_Start;
2765 break;
2766
2767 case GuestSessionStatus_Undefined:
2768 case GuestSessionStatus_Starting:
2769 /* Do the waiting below. */
2770 break;
2771
2772 default:
2773 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
2774 return VERR_NOT_IMPLEMENTED;
2775 }
2776 }
2777 else if (fWaitFlags & GuestSessionWaitForFlag_Start)
2778 {
2779 switch (mData.mStatus)
2780 {
2781 case GuestSessionStatus_Started:
2782 case GuestSessionStatus_Terminating:
2783 case GuestSessionStatus_Terminated:
2784 case GuestSessionStatus_Down:
2785 waitResult = GuestSessionWaitResult_Start;
2786 break;
2787
2788 case GuestSessionStatus_Error:
2789 waitResult = GuestSessionWaitResult_Error;
2790 break;
2791
2792 case GuestSessionStatus_TimedOutKilled:
2793 case GuestSessionStatus_TimedOutAbnormally:
2794 waitResult = GuestSessionWaitResult_Timeout;
2795 break;
2796
2797 case GuestSessionStatus_Undefined:
2798 case GuestSessionStatus_Starting:
2799 /* Do the waiting below. */
2800 break;
2801
2802 default:
2803 AssertMsgFailed(("Unhandled session status %RU32\n", mData.mStatus));
2804 return VERR_NOT_IMPLEMENTED;
2805 }
2806 }
2807
2808 LogFlowThisFunc(("sessionStatus=%RU32, sessionRc=%Rrc, waitResult=%RU32\n",
2809 mData.mStatus, mData.mRC, waitResult));
2810
2811 /* No waiting needed? Return immediately using the last set error. */
2812 if (waitResult != GuestSessionWaitResult_None)
2813 {
2814 if (prcGuest)
2815 *prcGuest = mData.mRC; /* Return last set error (if any). */
2816 return RT_SUCCESS(mData.mRC) ? VINF_SUCCESS : VERR_GSTCTL_GUEST_ERROR;
2817 }
2818
2819 int vrc;
2820
2821 GuestWaitEvent *pEvent = NULL;
2822 GuestEventTypes eventTypes;
2823 try
2824 {
2825 eventTypes.push_back(VBoxEventType_OnGuestSessionStateChanged);
2826
2827 vrc = registerWaitEventEx(mData.mSession.mID, mData.mObjectID, eventTypes, &pEvent);
2828 }
2829 catch (std::bad_alloc &)
2830 {
2831 vrc = VERR_NO_MEMORY;
2832 }
2833
2834 if (RT_FAILURE(vrc))
2835 return vrc;
2836
2837 alock.release(); /* Release lock before waiting. */
2838
2839 GuestSessionStatus_T sessionStatus;
2840 vrc = i_waitForStatusChange(pEvent, fWaitFlags,
2841 uTimeoutMS, &sessionStatus, prcGuest);
2842 if (RT_SUCCESS(vrc))
2843 {
2844 switch (sessionStatus)
2845 {
2846 case GuestSessionStatus_Started:
2847 waitResult = GuestSessionWaitResult_Start;
2848 break;
2849
2850 case GuestSessionStatus_Terminated:
2851 waitResult = GuestSessionWaitResult_Terminate;
2852 break;
2853
2854 case GuestSessionStatus_TimedOutKilled:
2855 case GuestSessionStatus_TimedOutAbnormally:
2856 waitResult = GuestSessionWaitResult_Timeout;
2857 break;
2858
2859 case GuestSessionStatus_Down:
2860 waitResult = GuestSessionWaitResult_Terminate;
2861 break;
2862
2863 case GuestSessionStatus_Error:
2864 waitResult = GuestSessionWaitResult_Error;
2865 break;
2866
2867 default:
2868 waitResult = GuestSessionWaitResult_Status;
2869 break;
2870 }
2871 }
2872
2873 unregisterWaitEvent(pEvent);
2874
2875 LogFlowFuncLeaveRC(vrc);
2876 return vrc;
2877}
2878
2879/**
2880 * Undocumented, you guess what it does.
2881 *
2882 * @note Similar code in GuestFile::i_waitForStatusChange() and
2883 * GuestProcess::i_waitForStatusChange().
2884 */
2885int GuestSession::i_waitForStatusChange(GuestWaitEvent *pEvent, uint32_t fWaitFlags, uint32_t uTimeoutMS,
2886 GuestSessionStatus_T *pSessionStatus, int *prcGuest)
2887{
2888 RT_NOREF(fWaitFlags);
2889 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
2890
2891 VBoxEventType_T evtType;
2892 ComPtr<IEvent> pIEvent;
2893 int vrc = waitForEvent(pEvent, uTimeoutMS,
2894 &evtType, pIEvent.asOutParam());
2895 if (RT_SUCCESS(vrc))
2896 {
2897 Assert(evtType == VBoxEventType_OnGuestSessionStateChanged);
2898
2899 ComPtr<IGuestSessionStateChangedEvent> pChangedEvent = pIEvent;
2900 Assert(!pChangedEvent.isNull());
2901
2902 GuestSessionStatus_T sessionStatus;
2903 pChangedEvent->COMGETTER(Status)(&sessionStatus);
2904 if (pSessionStatus)
2905 *pSessionStatus = sessionStatus;
2906
2907 ComPtr<IVirtualBoxErrorInfo> errorInfo;
2908 HRESULT hr = pChangedEvent->COMGETTER(Error)(errorInfo.asOutParam());
2909 ComAssertComRC(hr);
2910
2911 LONG lGuestRc;
2912 hr = errorInfo->COMGETTER(ResultDetail)(&lGuestRc);
2913 ComAssertComRC(hr);
2914 if (RT_FAILURE((int)lGuestRc))
2915 vrc = VERR_GSTCTL_GUEST_ERROR;
2916 if (prcGuest)
2917 *prcGuest = (int)lGuestRc;
2918
2919 LogFlowThisFunc(("Status changed event for session ID=%RU32, new status is: %RU32 (%Rrc)\n",
2920 mData.mSession.mID, sessionStatus,
2921 RT_SUCCESS((int)lGuestRc) ? VINF_SUCCESS : (int)lGuestRc));
2922 }
2923 /* waitForEvent may also return VERR_GSTCTL_GUEST_ERROR like we do above, so make prcGuest is set. */
2924 else if (vrc == VERR_GSTCTL_GUEST_ERROR && prcGuest)
2925 *prcGuest = pEvent->GuestResult();
2926 Assert(vrc != VERR_GSTCTL_GUEST_ERROR || !prcGuest || *prcGuest != (int)0xcccccccc);
2927
2928 LogFlowFuncLeaveRC(vrc);
2929 return vrc;
2930}
2931
2932// implementation of public methods
2933/////////////////////////////////////////////////////////////////////////////
2934
2935HRESULT GuestSession::close()
2936{
2937 AutoCaller autoCaller(this);
2938 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2939
2940 LogFlowThisFuncEnter();
2941
2942 /* Note: Don't check if the session is ready via i_isReadyExternal() here;
2943 * the session (already) could be in a stopped / aborted state. */
2944
2945 /* Close session on guest. */
2946 int rcGuest = VINF_SUCCESS;
2947 int vrc = i_closeSession(0 /* Flags */, 30 * 1000 /* Timeout */, &rcGuest);
2948 /* On failure don't return here, instead do all the cleanup
2949 * work first and then return an error. */
2950
2951 /* Remove ourselves from the session list. */
2952 AssertPtr(mParent);
2953 int vrc2 = mParent->i_sessionRemove(mData.mSession.mID);
2954 if (vrc2 == VERR_NOT_FOUND) /* Not finding the session anymore isn't critical. */
2955 vrc2 = VINF_SUCCESS;
2956
2957 if (RT_SUCCESS(vrc))
2958 vrc = vrc2;
2959
2960 LogFlowThisFunc(("Returning rc=%Rrc, rcGuest=%Rrc\n", vrc, rcGuest));
2961
2962 if (RT_FAILURE(vrc))
2963 {
2964 if (vrc == VERR_GSTCTL_GUEST_ERROR)
2965 return GuestSession::i_setErrorExternal(this, rcGuest);
2966 return setError(VBOX_E_IPRT_ERROR, tr("Closing guest session failed with %Rrc"), vrc);
2967 }
2968
2969 return S_OK;
2970}
2971
2972HRESULT GuestSession::fileCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2973 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
2974{
2975 RT_NOREF(aSource, aDestination, aFlags, aProgress);
2976 ReturnComNotImplemented();
2977}
2978
2979HRESULT GuestSession::fileCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2980 const std::vector<FileCopyFlag_T> &aFlags,
2981 ComPtr<IProgress> &aProgress)
2982{
2983 AutoCaller autoCaller(this);
2984 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2985
2986 uint32_t fFlags = FileCopyFlag_None;
2987 if (aFlags.size())
2988 {
2989 for (size_t i = 0; i < aFlags.size(); i++)
2990 fFlags |= aFlags[i];
2991 }
2992
2993 GuestSessionFsSourceSet SourceSet;
2994
2995 GuestSessionFsSourceSpec source;
2996 source.strSource = aSource;
2997 source.enmType = FsObjType_File;
2998 source.enmPathStyle = i_getPathStyle();
2999 source.fDryRun = false; /** @todo Implement support for a dry run. */
3000 source.Type.File.fCopyFlags = (FileCopyFlag_T)fFlags;
3001
3002 SourceSet.push_back(source);
3003
3004 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3005}
3006
3007HRESULT GuestSession::fileCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3008 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3009{
3010 AutoCaller autoCaller(this);
3011 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3012
3013 uint32_t fFlags = FileCopyFlag_None;
3014 if (aFlags.size())
3015 {
3016 for (size_t i = 0; i < aFlags.size(); i++)
3017 fFlags |= aFlags[i];
3018 }
3019
3020 GuestSessionFsSourceSet SourceSet;
3021
3022 /** @todo r=bird: The GuestSessionFsSourceSpec constructor does not zero the
3023 * members you aren't setting here and there are no hints about "input"
3024 * vs "task" members, so you have me worrying about using random stack by
3025 * accident somewhere... For instance Type.File.phFile sure sounds like
3026 * an input field and thus a disaster waiting to happen. */
3027 GuestSessionFsSourceSpec source;
3028 source.strSource = aSource;
3029 source.enmType = FsObjType_File;
3030 source.enmPathStyle = i_getPathStyle();
3031 source.fDryRun = false; /** @todo Implement support for a dry run. */
3032 source.Type.File.fCopyFlags = (FileCopyFlag_T)fFlags;
3033
3034 SourceSet.push_back(source);
3035
3036 return i_copyToGuest(SourceSet, aDestination, aProgress);
3037}
3038
3039HRESULT GuestSession::copyFromGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
3040 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
3041 ComPtr<IProgress> &aProgress)
3042{
3043 AutoCaller autoCaller(this);
3044 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3045
3046 const size_t cSources = aSources.size();
3047 if ( (aFilters.size() && aFilters.size() != cSources)
3048 || (aFlags.size() && aFlags.size() != cSources))
3049 {
3050 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
3051 }
3052
3053 GuestSessionFsSourceSet SourceSet;
3054
3055 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
3056 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
3057 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
3058
3059 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
3060 const bool fFollowSymlinks = true; /** @todo Ditto. */
3061
3062 while (itSource != aSources.end())
3063 {
3064 GuestFsObjData objData;
3065 int rcGuest;
3066 int vrc = i_fsQueryInfo(*(itSource), fFollowSymlinks, objData, &rcGuest);
3067 if ( RT_FAILURE(vrc)
3068 && !fContinueOnErrors)
3069 {
3070 if (GuestProcess::i_isGuestError(vrc))
3071 return setError(E_FAIL, tr("Unable to query type for source '%s': %s"), (*itSource).c_str(),
3072 GuestProcess::i_guestErrorToString(rcGuest).c_str());
3073 else
3074 return setError(E_FAIL, tr("Unable to query type for source '%s' (%Rrc)"), (*itSource).c_str(), vrc);
3075 }
3076
3077 Utf8Str strFlags;
3078 if (itFlags != aFlags.end())
3079 {
3080 strFlags = *itFlags;
3081 ++itFlags;
3082 }
3083
3084 Utf8Str strFilter;
3085 if (itFilter != aFilters.end())
3086 {
3087 strFilter = *itFilter;
3088 ++itFilter;
3089 }
3090
3091 GuestSessionFsSourceSpec source;
3092 source.strSource = *itSource;
3093 source.strFilter = strFilter;
3094 source.enmType = objData.mType;
3095 source.enmPathStyle = i_getPathStyle();
3096 source.fDryRun = false; /** @todo Implement support for a dry run. */
3097
3098 HRESULT hrc;
3099 if (source.enmType == FsObjType_Directory)
3100 {
3101 hrc = GuestSession::i_directoryCopyFlagFromStr(strFlags, &source.Type.Dir.fCopyFlags);
3102 source.Type.Dir.fRecursive = true; /* Implicit. */
3103 }
3104 else if (source.enmType == FsObjType_File)
3105 hrc = GuestSession::i_fileCopyFlagFromStr(strFlags, &source.Type.File.fCopyFlags);
3106 else
3107 return setError(E_INVALIDARG, tr("Source type %d invalid / not supported"), source.enmType);
3108 if (FAILED(hrc))
3109 return hrc;
3110
3111 SourceSet.push_back(source);
3112
3113 ++itSource;
3114 }
3115
3116 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3117}
3118
3119HRESULT GuestSession::copyToGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
3120 const std::vector<com::Utf8Str> &aFlags, const com::Utf8Str &aDestination,
3121 ComPtr<IProgress> &aProgress)
3122{
3123 AutoCaller autoCaller(this);
3124 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3125
3126 const size_t cSources = aSources.size();
3127 if ( (aFilters.size() && aFilters.size() != cSources)
3128 || (aFlags.size() && aFlags.size() != cSources))
3129 {
3130 return setError(E_INVALIDARG, tr("Parameter array sizes don't match to the number of sources specified"));
3131 }
3132
3133 GuestSessionFsSourceSet SourceSet;
3134
3135 std::vector<com::Utf8Str>::const_iterator itSource = aSources.begin();
3136 std::vector<com::Utf8Str>::const_iterator itFilter = aFilters.begin();
3137 std::vector<com::Utf8Str>::const_iterator itFlags = aFlags.begin();
3138
3139 const bool fContinueOnErrors = false; /** @todo Do we want a flag for that? */
3140
3141 while (itSource != aSources.end())
3142 {
3143 RTFSOBJINFO objInfo;
3144 int vrc = RTPathQueryInfo((*itSource).c_str(), &objInfo, RTFSOBJATTRADD_NOTHING);
3145 if ( RT_FAILURE(vrc)
3146 && !fContinueOnErrors)
3147 {
3148 return setError(E_FAIL, tr("Unable to query type for source '%s' (%Rrc)"), (*itSource).c_str(), vrc);
3149 }
3150
3151 Utf8Str strFlags;
3152 if (itFlags != aFlags.end())
3153 {
3154 strFlags = *itFlags;
3155 ++itFlags;
3156 }
3157
3158 Utf8Str strFilter;
3159 if (itFilter != aFilters.end())
3160 {
3161 strFilter = *itFilter;
3162 ++itFilter;
3163 }
3164
3165 GuestSessionFsSourceSpec source;
3166 source.strSource = *itSource;
3167 source.strFilter = strFilter;
3168 source.enmType = GuestBase::fileModeToFsObjType(objInfo.Attr.fMode);
3169 source.enmPathStyle = i_getPathStyle();
3170 source.fDryRun = false; /** @todo Implement support for a dry run. */
3171
3172 HRESULT hrc;
3173 if (source.enmType == FsObjType_Directory)
3174 {
3175 hrc = GuestSession::i_directoryCopyFlagFromStr(strFlags, &source.Type.Dir.fCopyFlags);
3176 source.Type.Dir.fFollowSymlinks = true; /** @todo Add a flag for that in DirectoryCopyFlag_T. Later. */
3177 source.Type.Dir.fRecursive = true; /* Implicit. */
3178 }
3179 else if (source.enmType == FsObjType_File)
3180 hrc = GuestSession::i_fileCopyFlagFromStr(strFlags, &source.Type.File.fCopyFlags);
3181 else
3182 return setError(E_INVALIDARG, tr("Source type %d invalid / not supported"), source.enmType);
3183 if (FAILED(hrc))
3184 return hrc;
3185
3186 SourceSet.push_back(source);
3187
3188 ++itSource;
3189 }
3190
3191 return i_copyToGuest(SourceSet, aDestination, aProgress);
3192}
3193
3194HRESULT GuestSession::directoryCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3195 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3196{
3197 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3198 ReturnComNotImplemented();
3199}
3200
3201HRESULT GuestSession::directoryCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3202 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3203{
3204 AutoCaller autoCaller(this);
3205 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3206
3207 uint32_t fFlags = DirectoryCopyFlag_None;
3208 if (aFlags.size())
3209 {
3210 for (size_t i = 0; i < aFlags.size(); i++)
3211 fFlags |= aFlags[i];
3212 }
3213
3214 GuestSessionFsSourceSet SourceSet;
3215
3216 GuestSessionFsSourceSpec source;
3217 source.strSource = aSource;
3218 source.enmType = FsObjType_Directory;
3219 source.enmPathStyle = i_getPathStyle();
3220 source.fDryRun = false; /** @todo Implement support for a dry run. */
3221 source.Type.Dir.fCopyFlags = (DirectoryCopyFlag_T)fFlags;
3222 source.Type.Dir.fRecursive = true; /* Implicit. */
3223
3224 SourceSet.push_back(source);
3225
3226 return i_copyFromGuest(SourceSet, aDestination, aProgress);
3227}
3228
3229HRESULT GuestSession::directoryCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3230 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3231{
3232 AutoCaller autoCaller(this);
3233 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3234
3235 uint32_t fFlags = DirectoryCopyFlag_None;
3236 if (aFlags.size())
3237 {
3238 for (size_t i = 0; i < aFlags.size(); i++)
3239 fFlags |= aFlags[i];
3240 }
3241
3242 GuestSessionFsSourceSet SourceSet;
3243
3244 GuestSessionFsSourceSpec source;
3245 source.strSource = aSource;
3246 source.enmType = FsObjType_Directory;
3247 source.enmPathStyle = i_getPathStyle();
3248 source.fDryRun = false; /** @todo Implement support for a dry run. */
3249 source.Type.Dir.fCopyFlags = (DirectoryCopyFlag_T)fFlags;
3250 source.Type.Dir.fFollowSymlinks = true; /** @todo Add a flag for that in DirectoryCopyFlag_T. Later. */
3251 source.Type.Dir.fRecursive = true; /* Implicit. */
3252
3253 SourceSet.push_back(source);
3254
3255 return i_copyToGuest(SourceSet, aDestination, aProgress);
3256}
3257
3258HRESULT GuestSession::directoryCreate(const com::Utf8Str &aPath, ULONG aMode,
3259 const std::vector<DirectoryCreateFlag_T> &aFlags)
3260{
3261 AutoCaller autoCaller(this);
3262 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3263
3264 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3265 return setError(E_INVALIDARG, tr("No directory to create specified"));
3266
3267 uint32_t fFlags = DirectoryCreateFlag_None;
3268 if (aFlags.size())
3269 {
3270 for (size_t i = 0; i < aFlags.size(); i++)
3271 fFlags |= aFlags[i];
3272
3273 if (fFlags)
3274 if (!(fFlags & DirectoryCreateFlag_Parents))
3275 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), fFlags);
3276 }
3277
3278 HRESULT hrc = i_isStartedExternal();
3279 if (FAILED(hrc))
3280 return hrc;
3281
3282 LogFlowThisFuncEnter();
3283
3284 ComObjPtr <GuestDirectory> pDirectory; int rcGuest;
3285 int vrc = i_directoryCreate(aPath, (uint32_t)aMode, fFlags, &rcGuest);
3286 if (RT_FAILURE(vrc))
3287 {
3288 if (GuestProcess::i_isGuestError(vrc))
3289 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest,
3290 tr("Directory creation failed: %s"), GuestDirectory::i_guestErrorToString(rcGuest).c_str());
3291 else
3292 {
3293 switch (vrc)
3294 {
3295 case VERR_INVALID_PARAMETER:
3296 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: Invalid parameters given"));
3297 break;
3298
3299 case VERR_BROKEN_PIPE:
3300 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: Unexpectedly aborted"));
3301 break;
3302
3303 default:
3304 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Directory creation failed: %Rrc"), vrc);
3305 break;
3306 }
3307 }
3308 }
3309
3310 return hrc;
3311}
3312
3313HRESULT GuestSession::directoryCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath,
3314 BOOL aSecure, com::Utf8Str &aDirectory)
3315{
3316 RT_NOREF(aMode, aSecure);
3317
3318 AutoCaller autoCaller(this);
3319 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3320
3321 if (RT_UNLIKELY((aTemplateName.c_str()) == NULL || *(aTemplateName.c_str()) == '\0'))
3322 return setError(E_INVALIDARG, tr("No template specified"));
3323 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3324 return setError(E_INVALIDARG, tr("No directory name specified"));
3325
3326 HRESULT hrc = i_isStartedExternal();
3327 if (FAILED(hrc))
3328 return hrc;
3329
3330 LogFlowThisFuncEnter();
3331
3332 int rcGuest;
3333 int vrc = i_fsCreateTemp(aTemplateName, aPath, true /* Directory */, aDirectory, &rcGuest);
3334 if (!RT_SUCCESS(vrc))
3335 {
3336 switch (vrc)
3337 {
3338 case VERR_GSTCTL_GUEST_ERROR:
3339 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3340 break;
3341
3342 default:
3343 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Temporary directory creation \"%s\" with template \"%s\" failed: %Rrc"),
3344 aPath.c_str(), aTemplateName.c_str(), vrc);
3345 break;
3346 }
3347 }
3348
3349 return hrc;
3350}
3351
3352HRESULT GuestSession::directoryExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3353{
3354 AutoCaller autoCaller(this);
3355 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3356
3357 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3358 return setError(E_INVALIDARG, tr("No directory to check existence for specified"));
3359
3360 HRESULT hrc = i_isStartedExternal();
3361 if (FAILED(hrc))
3362 return hrc;
3363
3364 LogFlowThisFuncEnter();
3365
3366 GuestFsObjData objData; int rcGuest;
3367 int vrc = i_directoryQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3368 if (RT_SUCCESS(vrc))
3369 *aExists = objData.mType == FsObjType_Directory;
3370 else
3371 {
3372 switch (vrc)
3373 {
3374 case VERR_GSTCTL_GUEST_ERROR:
3375 {
3376 switch (rcGuest)
3377 {
3378 case VERR_PATH_NOT_FOUND:
3379 *aExists = FALSE;
3380 break;
3381 default:
3382 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Querying directory existence \"%s\" failed: %s"),
3383 aPath.c_str(), GuestProcess::i_guestErrorToString(rcGuest).c_str());
3384 break;
3385 }
3386 break;
3387 }
3388
3389 default:
3390 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying directory existence \"%s\" failed: %Rrc"),
3391 aPath.c_str(), vrc);
3392 break;
3393 }
3394 }
3395
3396 return hrc;
3397}
3398
3399HRESULT GuestSession::directoryOpen(const com::Utf8Str &aPath, const com::Utf8Str &aFilter,
3400 const std::vector<DirectoryOpenFlag_T> &aFlags, ComPtr<IGuestDirectory> &aDirectory)
3401{
3402 AutoCaller autoCaller(this);
3403 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3404
3405 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3406 return setError(E_INVALIDARG, tr("No directory to open specified"));
3407 if (RT_UNLIKELY((aFilter.c_str()) != NULL && *(aFilter.c_str()) != '\0'))
3408 return setError(E_INVALIDARG, tr("Directory filters are not implemented yet"));
3409
3410 uint32_t fFlags = DirectoryOpenFlag_None;
3411 if (aFlags.size())
3412 {
3413 for (size_t i = 0; i < aFlags.size(); i++)
3414 fFlags |= aFlags[i];
3415
3416 if (fFlags)
3417 return setError(E_INVALIDARG, tr("Open flags (%#x) not implemented yet"), fFlags);
3418 }
3419
3420 HRESULT hrc = i_isStartedExternal();
3421 if (FAILED(hrc))
3422 return hrc;
3423
3424 LogFlowThisFuncEnter();
3425
3426 GuestDirectoryOpenInfo openInfo;
3427 openInfo.mPath = aPath;
3428 openInfo.mFilter = aFilter;
3429 openInfo.mFlags = fFlags;
3430
3431 ComObjPtr <GuestDirectory> pDirectory; int rcGuest;
3432 int vrc = i_directoryOpen(openInfo, pDirectory, &rcGuest);
3433 if (RT_SUCCESS(vrc))
3434 {
3435 /* Return directory object to the caller. */
3436 hrc = pDirectory.queryInterfaceTo(aDirectory.asOutParam());
3437 }
3438 else
3439 {
3440 switch (vrc)
3441 {
3442 case VERR_INVALID_PARAMETER:
3443 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening directory \"%s\" failed; invalid parameters given"),
3444 aPath.c_str());
3445 break;
3446
3447 case VERR_GSTCTL_GUEST_ERROR:
3448 hrc = GuestDirectory::i_setErrorExternal(this, rcGuest);
3449 break;
3450
3451 default:
3452 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3453 break;
3454 }
3455 }
3456
3457 return hrc;
3458}
3459
3460HRESULT GuestSession::directoryRemove(const com::Utf8Str &aPath)
3461{
3462 AutoCaller autoCaller(this);
3463 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3464
3465 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3466 return setError(E_INVALIDARG, tr("No directory to remove specified"));
3467
3468 HRESULT hrc = i_isStartedExternal();
3469 if (FAILED(hrc))
3470 return hrc;
3471
3472 LogFlowThisFuncEnter();
3473
3474 /* No flags; only remove the directory when empty. */
3475 uint32_t fFlags = DIRREMOVEREC_FLAG_NONE;
3476
3477 int rcGuest;
3478 int vrc = i_directoryRemove(aPath, fFlags, &rcGuest);
3479 if (RT_FAILURE(vrc))
3480 {
3481 switch (vrc)
3482 {
3483 case VERR_NOT_SUPPORTED:
3484 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3485 tr("Handling removing guest directories not supported by installed Guest Additions"));
3486 break;
3487
3488 case VERR_GSTCTL_GUEST_ERROR:
3489 hrc = GuestDirectory::i_setErrorExternal(this, rcGuest);
3490 break;
3491
3492 default:
3493 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing guest directory \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3494 break;
3495 }
3496 }
3497
3498 return hrc;
3499}
3500
3501HRESULT GuestSession::directoryRemoveRecursive(const com::Utf8Str &aPath, const std::vector<DirectoryRemoveRecFlag_T> &aFlags,
3502 ComPtr<IProgress> &aProgress)
3503{
3504 AutoCaller autoCaller(this);
3505 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3506
3507 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3508 return setError(E_INVALIDARG, tr("No directory to remove recursively specified"));
3509
3510 /* By default only delete empty directory structures, e.g. the operation will abort if there are
3511 * directories which are not empty. */
3512 uint32_t fFlags = DIRREMOVEREC_FLAG_RECURSIVE;
3513 if (aFlags.size())
3514 {
3515 for (size_t i = 0; i < aFlags.size(); i++)
3516 {
3517 switch (aFlags[i])
3518 {
3519 case DirectoryRemoveRecFlag_None: /* Skip. */
3520 continue;
3521
3522 case DirectoryRemoveRecFlag_ContentAndDir:
3523 fFlags |= DIRREMOVEREC_FLAG_CONTENT_AND_DIR;
3524 break;
3525
3526 case DirectoryRemoveRecFlag_ContentOnly:
3527 fFlags |= DIRREMOVEREC_FLAG_CONTENT_ONLY;
3528 break;
3529
3530 default:
3531 return setError(E_INVALIDARG, tr("Invalid flags specified"));
3532 }
3533 }
3534 }
3535
3536 HRESULT hrc = i_isStartedExternal();
3537 if (FAILED(hrc))
3538 return hrc;
3539
3540 LogFlowThisFuncEnter();
3541
3542 ComObjPtr<Progress> pProgress;
3543 hrc = pProgress.createObject();
3544 if (SUCCEEDED(hrc))
3545 hrc = pProgress->init(static_cast<IGuestSession *>(this),
3546 Bstr(tr("Removing guest directory")).raw(),
3547 TRUE /*aCancelable*/);
3548 if (FAILED(hrc))
3549 return hrc;
3550
3551 /* Note: At the moment we don't supply progress information while
3552 * deleting a guest directory recursively. So just complete
3553 * the progress object right now. */
3554 /** @todo Implement progress reporting on guest directory deletion! */
3555 hrc = pProgress->i_notifyComplete(S_OK);
3556 if (FAILED(hrc))
3557 return hrc;
3558
3559 int rcGuest;
3560 int vrc = i_directoryRemove(aPath, fFlags, &rcGuest);
3561 if (RT_FAILURE(vrc))
3562 {
3563 switch (vrc)
3564 {
3565 case VERR_NOT_SUPPORTED:
3566 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3567 tr("Handling removing guest directories recursively not supported by installed Guest Additions"));
3568 break;
3569
3570 case VERR_GSTCTL_GUEST_ERROR:
3571 hrc = GuestFile::i_setErrorExternal(this, rcGuest);
3572 break;
3573
3574 default:
3575 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Recursively removing guest directory \"%s\" failed: %Rrc"),
3576 aPath.c_str(), vrc);
3577 break;
3578 }
3579 }
3580 else
3581 {
3582 pProgress.queryInterfaceTo(aProgress.asOutParam());
3583 }
3584
3585 return hrc;
3586}
3587
3588HRESULT GuestSession::environmentScheduleSet(const com::Utf8Str &aName, const com::Utf8Str &aValue)
3589{
3590 AutoCaller autoCaller(this);
3591 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3592
3593 HRESULT hrc;
3594 if (RT_LIKELY(aName.isNotEmpty()))
3595 {
3596 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3597 {
3598 LogFlowThisFuncEnter();
3599
3600 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3601 int vrc = mData.mEnvironmentChanges.setVariable(aName, aValue);
3602 if (RT_SUCCESS(vrc))
3603 hrc = S_OK;
3604 else
3605 hrc = setErrorVrc(vrc);
3606 }
3607 else
3608 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3609 }
3610 else
3611 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3612
3613 LogFlowThisFuncLeave();
3614 return hrc;
3615}
3616
3617HRESULT GuestSession::environmentScheduleUnset(const com::Utf8Str &aName)
3618{
3619 AutoCaller autoCaller(this);
3620 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3621
3622 HRESULT hrc;
3623 if (RT_LIKELY(aName.isNotEmpty()))
3624 {
3625 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3626 {
3627 LogFlowThisFuncEnter();
3628
3629 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3630 int vrc = mData.mEnvironmentChanges.unsetVariable(aName);
3631 if (RT_SUCCESS(vrc))
3632 hrc = S_OK;
3633 else
3634 hrc = setErrorVrc(vrc);
3635 }
3636 else
3637 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3638 }
3639 else
3640 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3641
3642 LogFlowThisFuncLeave();
3643 return hrc;
3644}
3645
3646HRESULT GuestSession::environmentGetBaseVariable(const com::Utf8Str &aName, com::Utf8Str &aValue)
3647{
3648 AutoCaller autoCaller(this);
3649 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3650
3651 HRESULT hrc;
3652 if (RT_LIKELY(aName.isNotEmpty()))
3653 {
3654 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3655 {
3656 LogFlowThisFuncEnter();
3657
3658 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3659 if (mData.mpBaseEnvironment)
3660 {
3661 int vrc = mData.mpBaseEnvironment->getVariable(aName, &aValue);
3662 if (RT_SUCCESS(vrc))
3663 hrc = S_OK;
3664 else
3665 hrc = setErrorVrc(vrc);
3666 }
3667 else if (mData.mProtocolVersion < 99999)
3668 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
3669 else
3670 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
3671 }
3672 else
3673 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3674 }
3675 else
3676 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3677
3678 LogFlowThisFuncLeave();
3679 return hrc;
3680}
3681
3682HRESULT GuestSession::environmentDoesBaseVariableExist(const com::Utf8Str &aName, BOOL *aExists)
3683{
3684 AutoCaller autoCaller(this);
3685 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3686
3687 *aExists = FALSE;
3688
3689 HRESULT hrc;
3690 if (RT_LIKELY(aName.isNotEmpty()))
3691 {
3692 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3693 {
3694 LogFlowThisFuncEnter();
3695
3696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3697 if (mData.mpBaseEnvironment)
3698 {
3699 hrc = S_OK;
3700 *aExists = mData.mpBaseEnvironment->doesVariableExist(aName);
3701 }
3702 else if (mData.mProtocolVersion < 99999)
3703 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
3704 else
3705 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
3706 }
3707 else
3708 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3709 }
3710 else
3711 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3712
3713 LogFlowThisFuncLeave();
3714 return hrc;
3715}
3716
3717HRESULT GuestSession::fileCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath, BOOL aSecure,
3718 ComPtr<IGuestFile> &aFile)
3719{
3720 RT_NOREF(aTemplateName, aMode, aPath, aSecure, aFile);
3721 ReturnComNotImplemented();
3722}
3723
3724HRESULT GuestSession::fileExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3725{
3726 AutoCaller autoCaller(this);
3727 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3728
3729 /* By default we return non-existent. */
3730 *aExists = FALSE;
3731
3732 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3733 return S_OK;
3734
3735 HRESULT hrc = i_isStartedExternal();
3736 if (FAILED(hrc))
3737 return hrc;
3738
3739 LogFlowThisFuncEnter();
3740
3741 GuestFsObjData objData; int rcGuest;
3742 int vrc = i_fileQueryInfo(aPath, RT_BOOL(aFollowSymlinks), objData, &rcGuest);
3743 if (RT_SUCCESS(vrc))
3744 {
3745 *aExists = TRUE;
3746 return S_OK;
3747 }
3748
3749 switch (vrc)
3750 {
3751 case VERR_GSTCTL_GUEST_ERROR:
3752 {
3753 switch (rcGuest)
3754 {
3755 case VERR_PATH_NOT_FOUND:
3756 RT_FALL_THROUGH();
3757 case VERR_FILE_NOT_FOUND:
3758 break;
3759
3760 default:
3761 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3762 break;
3763 }
3764
3765 break;
3766 }
3767
3768 case VERR_NOT_A_FILE:
3769 break;
3770
3771 default:
3772 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying file information for \"%s\" failed: %Rrc"),
3773 aPath.c_str(), vrc);
3774 break;
3775 }
3776
3777 return hrc;
3778}
3779
3780HRESULT GuestSession::fileOpen(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3781 ULONG aCreationMode, ComPtr<IGuestFile> &aFile)
3782{
3783 LogFlowThisFuncEnter();
3784
3785 const std::vector<FileOpenExFlag_T> EmptyFlags;
3786 return fileOpenEx(aPath, aAccessMode, aOpenAction, FileSharingMode_All, aCreationMode, EmptyFlags, aFile);
3787}
3788
3789HRESULT GuestSession::fileOpenEx(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3790 FileSharingMode_T aSharingMode, ULONG aCreationMode,
3791 const std::vector<FileOpenExFlag_T> &aFlags, ComPtr<IGuestFile> &aFile)
3792{
3793 AutoCaller autoCaller(this);
3794 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3795
3796 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3797 return setError(E_INVALIDARG, tr("No file to open specified"));
3798
3799 HRESULT hrc = i_isStartedExternal();
3800 if (FAILED(hrc))
3801 return hrc;
3802
3803 LogFlowThisFuncEnter();
3804
3805 GuestFileOpenInfo openInfo;
3806 openInfo.mFilename = aPath;
3807 openInfo.mCreationMode = aCreationMode;
3808
3809 /* Validate aAccessMode. */
3810 switch (aAccessMode)
3811 {
3812 case FileAccessMode_ReadOnly:
3813 RT_FALL_THRU();
3814 case FileAccessMode_WriteOnly:
3815 RT_FALL_THRU();
3816 case FileAccessMode_ReadWrite:
3817 openInfo.mAccessMode = aAccessMode;
3818 break;
3819 case FileAccessMode_AppendOnly:
3820 RT_FALL_THRU();
3821 case FileAccessMode_AppendRead:
3822 return setError(E_NOTIMPL, tr("Append access modes are not yet implemented"));
3823 default:
3824 return setError(E_INVALIDARG, tr("Unknown FileAccessMode value %u (%#x)"), aAccessMode, aAccessMode);
3825 }
3826
3827 /* Validate aOpenAction to the old format. */
3828 switch (aOpenAction)
3829 {
3830 case FileOpenAction_OpenExisting:
3831 RT_FALL_THRU();
3832 case FileOpenAction_OpenOrCreate:
3833 RT_FALL_THRU();
3834 case FileOpenAction_CreateNew:
3835 RT_FALL_THRU();
3836 case FileOpenAction_CreateOrReplace:
3837 RT_FALL_THRU();
3838 case FileOpenAction_OpenExistingTruncated:
3839 RT_FALL_THRU();
3840 case FileOpenAction_AppendOrCreate:
3841 openInfo.mOpenAction = aOpenAction;
3842 break;
3843 default:
3844 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3845 }
3846
3847 /* Validate aSharingMode. */
3848 switch (aSharingMode)
3849 {
3850 case FileSharingMode_All:
3851 openInfo.mSharingMode = aSharingMode;
3852 break;
3853 case FileSharingMode_Read:
3854 case FileSharingMode_Write:
3855 case FileSharingMode_ReadWrite:
3856 case FileSharingMode_Delete:
3857 case FileSharingMode_ReadDelete:
3858 case FileSharingMode_WriteDelete:
3859 return setError(E_NOTIMPL, tr("Only FileSharingMode_All is currently implemented"));
3860
3861 default:
3862 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3863 }
3864
3865 /* Combine and validate flags. */
3866 uint32_t fOpenEx = 0;
3867 for (size_t i = 0; i < aFlags.size(); i++)
3868 fOpenEx = aFlags[i];
3869 if (fOpenEx)
3870 return setError(E_INVALIDARG, tr("Unsupported FileOpenExFlag value(s) in aFlags (%#x)"), fOpenEx);
3871 openInfo.mfOpenEx = fOpenEx;
3872
3873 ComObjPtr <GuestFile> pFile;
3874 int rcGuest;
3875 int vrc = i_fileOpenEx(aPath, aAccessMode, aOpenAction, aSharingMode, aCreationMode, aFlags, pFile, &rcGuest);
3876 if (RT_SUCCESS(vrc))
3877 /* Return directory object to the caller. */
3878 hrc = pFile.queryInterfaceTo(aFile.asOutParam());
3879 else
3880 {
3881 switch (vrc)
3882 {
3883 case VERR_NOT_SUPPORTED:
3884 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
3885 tr("Handling guest files not supported by installed Guest Additions"));
3886 break;
3887
3888 case VERR_GSTCTL_GUEST_ERROR:
3889 hrc = GuestFile::i_setErrorExternal(this, rcGuest);
3890 break;
3891
3892 default:
3893 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Opening guest file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3894 break;
3895 }
3896 }
3897
3898 return hrc;
3899}
3900
3901HRESULT GuestSession::fileQuerySize(const com::Utf8Str &aPath, BOOL aFollowSymlinks, LONG64 *aSize)
3902{
3903 AutoCaller autoCaller(this);
3904 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3905
3906 if (aPath.isEmpty())
3907 return setError(E_INVALIDARG, tr("No path specified"));
3908
3909 HRESULT hrc = i_isStartedExternal();
3910 if (FAILED(hrc))
3911 return hrc;
3912
3913 int64_t llSize; int rcGuest;
3914 int vrc = i_fileQuerySize(aPath, aFollowSymlinks != FALSE, &llSize, &rcGuest);
3915 if (RT_SUCCESS(vrc))
3916 {
3917 *aSize = llSize;
3918 }
3919 else
3920 {
3921 if (GuestProcess::i_isGuestError(vrc))
3922 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3923 else
3924 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Querying file size failed: %Rrc"), vrc);
3925 }
3926
3927 return hrc;
3928}
3929
3930HRESULT GuestSession::fsObjExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3931{
3932 AutoCaller autoCaller(this);
3933 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3934
3935 if (aPath.isEmpty())
3936 return setError(E_INVALIDARG, tr("No path specified"));
3937
3938 HRESULT hrc = i_isStartedExternal();
3939 if (FAILED(hrc))
3940 return hrc;
3941
3942 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
3943
3944 *aExists = false;
3945
3946 GuestFsObjData objData;
3947 int rcGuest;
3948 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3949 if (RT_SUCCESS(vrc))
3950 {
3951 *aExists = TRUE;
3952 }
3953 else
3954 {
3955 if (GuestProcess::i_isGuestError(vrc))
3956 {
3957 if ( rcGuest == VERR_NOT_A_FILE
3958 || rcGuest == VERR_PATH_NOT_FOUND
3959 || rcGuest == VERR_FILE_NOT_FOUND
3960 || rcGuest == VERR_INVALID_NAME)
3961 {
3962 hrc = S_OK; /* Ignore these vrc values. */
3963 }
3964 else
3965 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3966 }
3967 else
3968 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3969 }
3970
3971 return hrc;
3972}
3973
3974HRESULT GuestSession::fsObjQueryInfo(const com::Utf8Str &aPath, BOOL aFollowSymlinks, ComPtr<IGuestFsObjInfo> &aInfo)
3975{
3976 AutoCaller autoCaller(this);
3977 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3978
3979 if (aPath.isEmpty())
3980 return setError(E_INVALIDARG, tr("No path specified"));
3981
3982 HRESULT hrc = i_isStartedExternal();
3983 if (FAILED(hrc))
3984 return hrc;
3985
3986 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
3987
3988 GuestFsObjData Info; int rcGuest;
3989 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, Info, &rcGuest);
3990 if (RT_SUCCESS(vrc))
3991 {
3992 ComObjPtr<GuestFsObjInfo> ptrFsObjInfo;
3993 hrc = ptrFsObjInfo.createObject();
3994 if (SUCCEEDED(hrc))
3995 {
3996 vrc = ptrFsObjInfo->init(Info);
3997 if (RT_SUCCESS(vrc))
3998 hrc = ptrFsObjInfo.queryInterfaceTo(aInfo.asOutParam());
3999 else
4000 hrc = setErrorVrc(vrc);
4001 }
4002 }
4003 else
4004 {
4005 if (GuestProcess::i_isGuestError(vrc))
4006 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
4007 else
4008 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4009 }
4010
4011 return hrc;
4012}
4013
4014HRESULT GuestSession::fsObjRemove(const com::Utf8Str &aPath)
4015{
4016 AutoCaller autoCaller(this);
4017 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4018
4019 if (RT_UNLIKELY(aPath.isEmpty()))
4020 return setError(E_INVALIDARG, tr("No path specified"));
4021
4022 HRESULT hrc = i_isStartedExternal();
4023 if (FAILED(hrc))
4024 return hrc;
4025
4026 LogFlowThisFunc(("aPath=%s\n", aPath.c_str()));
4027
4028 int rcGuest;
4029 int vrc = i_fileRemove(aPath, &rcGuest);
4030 if (RT_FAILURE(vrc))
4031 {
4032 if (GuestProcess::i_isGuestError(vrc))
4033 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
4034 else
4035 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Removing file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
4036 }
4037
4038 return hrc;
4039}
4040
4041HRESULT GuestSession::fsObjRemoveArray(const std::vector<com::Utf8Str> &aPaths, ComPtr<IProgress> &aProgress)
4042{
4043 AutoCaller autoCaller(this);
4044 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4045
4046 RT_NOREF(aPaths, aProgress);
4047
4048 return E_NOTIMPL;
4049}
4050
4051HRESULT GuestSession::fsObjRename(const com::Utf8Str &aSource,
4052 const com::Utf8Str &aDestination,
4053 const std::vector<FsObjRenameFlag_T> &aFlags)
4054{
4055 AutoCaller autoCaller(this);
4056 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4057
4058 if (RT_UNLIKELY(aSource.isEmpty()))
4059 return setError(E_INVALIDARG, tr("No source path specified"));
4060
4061 if (RT_UNLIKELY(aDestination.isEmpty()))
4062 return setError(E_INVALIDARG, tr("No destination path specified"));
4063
4064 HRESULT hrc = i_isStartedExternal();
4065 if (FAILED(hrc))
4066 return hrc;
4067
4068 /* Combine, validate and convert flags. */
4069 uint32_t fApiFlags = 0;
4070 for (size_t i = 0; i < aFlags.size(); i++)
4071 fApiFlags |= aFlags[i];
4072 if (fApiFlags & ~((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace))
4073 return setError(E_INVALIDARG, tr("Unknown rename flag: %#x"), fApiFlags);
4074
4075 LogFlowThisFunc(("aSource=%s, aDestination=%s\n", aSource.c_str(), aDestination.c_str()));
4076
4077 AssertCompile(FsObjRenameFlag_NoReplace == 0);
4078 AssertCompile(FsObjRenameFlag_Replace != 0);
4079 uint32_t fBackend;
4080 if ((fApiFlags & ((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace)) == FsObjRenameFlag_Replace)
4081 fBackend = PATHRENAME_FLAG_REPLACE;
4082 else
4083 fBackend = PATHRENAME_FLAG_NO_REPLACE;
4084
4085 /* Call worker to do the job. */
4086 int rcGuest;
4087 int vrc = i_pathRename(aSource, aDestination, fBackend, &rcGuest);
4088 if (RT_FAILURE(vrc))
4089 {
4090 switch (vrc)
4091 {
4092 case VERR_NOT_SUPPORTED:
4093 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4094 tr("Handling renaming guest directories not supported by installed Guest Additions"));
4095 break;
4096
4097 case VERR_GSTCTL_GUEST_ERROR:
4098 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, rcGuest, tr("Renaming guest directory failed: %Rrc"), rcGuest);
4099 break;
4100
4101 default:
4102 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc, tr("Renaming guest directory \"%s\" failed: %Rrc"),
4103 aSource.c_str(), vrc);
4104 break;
4105 }
4106 }
4107
4108 return hrc;
4109}
4110
4111HRESULT GuestSession::fsObjMove(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
4112 const std::vector<FsObjMoveFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
4113{
4114 RT_NOREF(aSource, aDestination, aFlags, aProgress);
4115 ReturnComNotImplemented();
4116}
4117
4118HRESULT GuestSession::fsObjMoveArray(const std::vector<com::Utf8Str> &aSource,
4119 const com::Utf8Str &aDestination,
4120 const std::vector<FsObjMoveFlag_T> &aFlags,
4121 ComPtr<IProgress> &aProgress)
4122{
4123 RT_NOREF(aSource, aDestination, aFlags, aProgress);
4124 ReturnComNotImplemented();
4125}
4126
4127HRESULT GuestSession::fsObjCopyArray(const std::vector<com::Utf8Str> &aSource,
4128 const com::Utf8Str &aDestination,
4129 const std::vector<FileCopyFlag_T> &aFlags,
4130 ComPtr<IProgress> &aProgress)
4131{
4132 RT_NOREF(aSource, aDestination, aFlags, aProgress);
4133 ReturnComNotImplemented();
4134}
4135
4136HRESULT GuestSession::fsObjSetACL(const com::Utf8Str &aPath, BOOL aFollowSymlinks, const com::Utf8Str &aAcl, ULONG aMode)
4137{
4138 RT_NOREF(aPath, aFollowSymlinks, aAcl, aMode);
4139 ReturnComNotImplemented();
4140}
4141
4142
4143HRESULT GuestSession::processCreate(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
4144 const std::vector<com::Utf8Str> &aEnvironment,
4145 const std::vector<ProcessCreateFlag_T> &aFlags,
4146 ULONG aTimeoutMS, ComPtr<IGuestProcess> &aGuestProcess)
4147{
4148 LogFlowThisFuncEnter();
4149
4150 std::vector<LONG> affinityIgnored;
4151 return processCreateEx(aExecutable, aArguments, aEnvironment, aFlags, aTimeoutMS, ProcessPriority_Default,
4152 affinityIgnored, aGuestProcess);
4153}
4154
4155HRESULT GuestSession::processCreateEx(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
4156 const std::vector<com::Utf8Str> &aEnvironment,
4157 const std::vector<ProcessCreateFlag_T> &aFlags, ULONG aTimeoutMS,
4158 ProcessPriority_T aPriority, const std::vector<LONG> &aAffinity,
4159 ComPtr<IGuestProcess> &aGuestProcess)
4160{
4161 AutoCaller autoCaller(this);
4162 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4163
4164 HRESULT hr = i_isStartedExternal();
4165 if (FAILED(hr))
4166 return hr;
4167
4168 /*
4169 * Must have an executable to execute. If none is given, we try use the
4170 * zero'th argument.
4171 */
4172 const char *pszExecutable = aExecutable.c_str();
4173 if (RT_UNLIKELY(pszExecutable == NULL || *pszExecutable == '\0'))
4174 {
4175 if (aArguments.size() > 0)
4176 pszExecutable = aArguments[0].c_str();
4177 if (pszExecutable == NULL || *pszExecutable == '\0')
4178 return setError(E_INVALIDARG, tr("No command to execute specified"));
4179 }
4180
4181 /* The rest of the input is being validated in i_processCreateEx(). */
4182
4183 LogFlowThisFuncEnter();
4184
4185 /*
4186 * Build the process startup info.
4187 */
4188 GuestProcessStartupInfo procInfo;
4189
4190 /* Executable and arguments. */
4191 procInfo.mExecutable = pszExecutable;
4192 if (aArguments.size())
4193 for (size_t i = 0; i < aArguments.size(); i++)
4194 procInfo.mArguments.push_back(aArguments[i]);
4195
4196 /* Combine the environment changes associated with the ones passed in by
4197 the caller, giving priority to the latter. The changes are putenv style
4198 and will be applied to the standard environment for the guest user. */
4199 int vrc = procInfo.mEnvironmentChanges.copy(mData.mEnvironmentChanges);
4200 if (RT_SUCCESS(vrc))
4201 vrc = procInfo.mEnvironmentChanges.applyPutEnvArray(aEnvironment);
4202 if (RT_SUCCESS(vrc))
4203 {
4204 /* Convert the flag array into a mask. */
4205 if (aFlags.size())
4206 for (size_t i = 0; i < aFlags.size(); i++)
4207 procInfo.mFlags |= aFlags[i];
4208
4209 procInfo.mTimeoutMS = aTimeoutMS;
4210
4211 /** @todo use RTCPUSET instead of archaic 64-bit variables! */
4212 if (aAffinity.size())
4213 for (size_t i = 0; i < aAffinity.size(); i++)
4214 if (aAffinity[i])
4215 procInfo.mAffinity |= (uint64_t)1 << i;
4216
4217 procInfo.mPriority = aPriority;
4218
4219 /*
4220 * Create a guest process object.
4221 */
4222 ComObjPtr<GuestProcess> pProcess;
4223 vrc = i_processCreateEx(procInfo, pProcess);
4224 if (RT_SUCCESS(vrc))
4225 {
4226 ComPtr<IGuestProcess> pIProcess;
4227 hr = pProcess.queryInterfaceTo(pIProcess.asOutParam());
4228 if (SUCCEEDED(hr))
4229 {
4230 /*
4231 * Start the process.
4232 */
4233 vrc = pProcess->i_startProcessAsync();
4234 if (RT_SUCCESS(vrc))
4235 {
4236 aGuestProcess = pIProcess;
4237
4238 LogFlowFuncLeaveRC(vrc);
4239 return S_OK;
4240 }
4241
4242 hr = setErrorVrc(vrc, tr("Failed to start guest process: %Rrc"), vrc);
4243 }
4244 }
4245 else if (vrc == VERR_GSTCTL_MAX_CID_OBJECTS_REACHED)
4246 hr = setErrorVrc(vrc, tr("Maximum number of concurrent guest processes per session (%u) reached"),
4247 VBOX_GUESTCTRL_MAX_OBJECTS);
4248 else
4249 hr = setErrorVrc(vrc, tr("Failed to create guest process object: %Rrc"), vrc);
4250 }
4251 else
4252 hr = setErrorVrc(vrc, tr("Failed to set up the environment: %Rrc"), vrc);
4253
4254 LogFlowFuncLeaveRC(vrc);
4255 return hr;
4256}
4257
4258HRESULT GuestSession::processGet(ULONG aPid, ComPtr<IGuestProcess> &aGuestProcess)
4259
4260{
4261 AutoCaller autoCaller(this);
4262 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4263
4264 if (aPid == 0)
4265 return setError(E_INVALIDARG, tr("No valid process ID (PID) specified"));
4266
4267 LogFlowThisFunc(("PID=%RU32\n", aPid));
4268
4269 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4270
4271 HRESULT hr = S_OK;
4272
4273 ComObjPtr<GuestProcess> pProcess;
4274 int rc = i_processGetByPID(aPid, &pProcess);
4275 if (RT_FAILURE(rc))
4276 hr = setError(E_INVALIDARG, tr("No process with PID %RU32 found"), aPid);
4277
4278 /* This will set (*aProcess) to NULL if pProgress is NULL. */
4279 HRESULT hr2 = pProcess.queryInterfaceTo(aGuestProcess.asOutParam());
4280 if (SUCCEEDED(hr))
4281 hr = hr2;
4282
4283 LogFlowThisFunc(("aProcess=%p, hr=%Rhrc\n", (IGuestProcess*)aGuestProcess, hr));
4284 return hr;
4285}
4286
4287HRESULT GuestSession::symlinkCreate(const com::Utf8Str &aSource, const com::Utf8Str &aTarget, SymlinkType_T aType)
4288{
4289 RT_NOREF(aSource, aTarget, aType);
4290 ReturnComNotImplemented();
4291}
4292
4293HRESULT GuestSession::symlinkExists(const com::Utf8Str &aSymlink, BOOL *aExists)
4294
4295{
4296 RT_NOREF(aSymlink, aExists);
4297 ReturnComNotImplemented();
4298}
4299
4300HRESULT GuestSession::symlinkRead(const com::Utf8Str &aSymlink, const std::vector<SymlinkReadFlag_T> &aFlags,
4301 com::Utf8Str &aTarget)
4302{
4303 RT_NOREF(aSymlink, aFlags, aTarget);
4304 ReturnComNotImplemented();
4305}
4306
4307HRESULT GuestSession::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
4308{
4309 AutoCaller autoCaller(this);
4310 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4311
4312 /* Note: No call to i_isReadyExternal() needed here, as the session might not has been started (yet). */
4313
4314 LogFlowThisFuncEnter();
4315
4316 HRESULT hrc = S_OK;
4317
4318 /*
4319 * Note: Do not hold any locks here while waiting!
4320 */
4321 int rcGuest; GuestSessionWaitResult_T waitResult;
4322 int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &rcGuest);
4323 if (RT_SUCCESS(vrc))
4324 *aReason = waitResult;
4325 else
4326 {
4327 switch (vrc)
4328 {
4329 case VERR_GSTCTL_GUEST_ERROR:
4330 hrc = GuestSession::i_setErrorExternal(this, rcGuest);
4331 break;
4332
4333 case VERR_TIMEOUT:
4334 *aReason = GuestSessionWaitResult_Timeout;
4335 break;
4336
4337 default:
4338 {
4339 const char *pszSessionName = mData.mSession.mName.c_str();
4340 hrc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
4341 tr("Waiting for guest session \"%s\" failed: %Rrc"),
4342 pszSessionName ? pszSessionName : tr("Unnamed"), vrc);
4343 break;
4344 }
4345 }
4346 }
4347
4348 LogFlowFuncLeaveRC(vrc);
4349 return hrc;
4350}
4351
4352HRESULT GuestSession::waitForArray(const std::vector<GuestSessionWaitForFlag_T> &aWaitFor, ULONG aTimeoutMS,
4353 GuestSessionWaitResult_T *aReason)
4354{
4355 AutoCaller autoCaller(this);
4356 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4357
4358 /* Note: No call to i_isReadyExternal() needed here, as the session might not has been started (yet). */
4359
4360 LogFlowThisFuncEnter();
4361
4362 /*
4363 * Note: Do not hold any locks here while waiting!
4364 */
4365 uint32_t fWaitFor = GuestSessionWaitForFlag_None;
4366 for (size_t i = 0; i < aWaitFor.size(); i++)
4367 fWaitFor |= aWaitFor[i];
4368
4369 return WaitFor(fWaitFor, aTimeoutMS, aReason);
4370}
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