VirtualBox

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

Last change on this file since 72001 was 72001, checked in by vboxsync, 7 years ago

Guest Control/Main: Implemented stubs IGuestSession:copy[From|To]Guest() ( @bugref{9135} ) and do validation and heavy lifting in internal helper functions i_copyFromGuest() and i_copyToGuest(), also for the rest of the API copy functions.

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

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