VirtualBox

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

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

Build fix.

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