VirtualBox

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

最後變更 在這個檔案從78666是 78666,由 vboxsync 提交於 6 年 前

Main/GuestControl: Better error message from copyToGuest and various related cleanups and todo notes. Tip: Pass objects like GuestSessionFsSourceSet by reference instead of copies all the time. bugref:9320

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

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