VirtualBox

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

Last change on this file since 71977 was 71977, 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: 126.8 KB
Line 
1/* $Id: GuestSessionImpl.cpp 71977 2018-04-23 11:14:27Z 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 try
2633 {
2634 GuestSessionTaskCopyFrom *pTask = NULL;
2635 ComObjPtr<Progress> pProgress;
2636 try
2637 {
2638 GuestSessionFsSourceSpec source;
2639 source.strSource = aSource;
2640 source.enmType = GuestSessionFsSourceType_File;
2641 source.enmPathStyle = i_getPathStyle();
2642 source.Type.File.fCopyFlags = (FileCopyFlag_T)fFlags;
2643
2644 GuestSessionFsSourceSet vecSources;
2645 vecSources.push_back(source);
2646
2647 pTask = new GuestSessionTaskCopyFrom(this /* GuestSession */, vecSources, aDest);
2648 }
2649 catch(...)
2650 {
2651 hrc = setError(VBOX_E_IPRT_ERROR, tr("Failed to create SessionTaskCopyFrom object"));
2652 throw;
2653 }
2654
2655
2656 hrc = pTask->Init(Utf8StrFmt(tr("Copying \"%s\" from guest to \"%s\" on the host"), aSource.c_str(), aDest.c_str()));
2657 if (FAILED(hrc))
2658 {
2659 delete pTask;
2660 hrc = setError(VBOX_E_IPRT_ERROR,
2661 tr("Creating progress object for SessionTaskCopyFrom object failed"));
2662 throw hrc;
2663 }
2664
2665 hrc = pTask->createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
2666
2667 if (SUCCEEDED(hrc))
2668 {
2669 /* Return progress to the caller. */
2670 pProgress = pTask->GetProgressObject();
2671 hrc = pProgress.queryInterfaceTo(aProgress.asOutParam());
2672 }
2673 else
2674 hrc = setError(VBOX_E_IPRT_ERROR,
2675 tr("Starting thread for copying file \"%s\" from guest to \"%s\" on the host failed "),
2676 aSource.c_str(), aDest.c_str());
2677
2678 }
2679 catch(std::bad_alloc &)
2680 {
2681 hrc = E_OUTOFMEMORY;
2682 }
2683 catch(HRESULT eHR)
2684 {
2685 hrc = eHR;
2686 LogFlowThisFunc(("Exception was caught in the function \n"));
2687 }
2688
2689 return hrc;
2690}
2691
2692HRESULT GuestSession::fileCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDest,
2693 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
2694{
2695 AutoCaller autoCaller(this);
2696 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2697
2698 if (RT_UNLIKELY((aSource.c_str()) == NULL || *(aSource.c_str()) == '\0'))
2699 return setError(E_INVALIDARG, tr("No source specified"));
2700 if (RT_UNLIKELY((aDest.c_str()) == NULL || *(aDest.c_str()) == '\0'))
2701 return setError(E_INVALIDARG, tr("No destination specified"));
2702
2703 uint32_t fFlags = FileCopyFlag_None;
2704 if (aFlags.size())
2705 {
2706 for (size_t i = 0; i < aFlags.size(); i++)
2707 fFlags |= aFlags[i];
2708 }
2709
2710 if (fFlags)
2711 {
2712 if ( !(fFlags & FileCopyFlag_NoReplace)
2713 && !(fFlags & FileCopyFlag_FollowLinks)
2714 && !(fFlags & FileCopyFlag_Update))
2715 {
2716 return setError(E_NOTIMPL, tr("Invalid / not (yet) implemented flag(s) specified"));
2717 }
2718 }
2719
2720 HRESULT hrc = i_isReadyExternal();
2721 if (FAILED(hrc))
2722 return hrc;
2723
2724 LogFlowThisFuncEnter();
2725
2726 try
2727 {
2728 GuestSessionTaskCopyTo *pTask = NULL;
2729 ComObjPtr<Progress> pProgress;
2730 try
2731 {
2732 GuestSessionFsSourceSpec source;
2733 source.strSource = aSource;
2734 source.enmType = GuestSessionFsSourceType_File;
2735#if RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS
2736 source.enmPathStyle = PathStyle_DOS;
2737#else
2738 source.enmPathStyle = PathStyle_UNIX;
2739#endif
2740 source.Type.File.fCopyFlags = (FileCopyFlag_T)fFlags;
2741
2742 GuestSessionFsSourceSet vecSources;
2743 vecSources.push_back(source);
2744
2745 pTask = new GuestSessionTaskCopyTo(this /* GuestSession */, vecSources, aDest);
2746 }
2747 catch(...)
2748 {
2749 hrc = setError(VBOX_E_IPRT_ERROR, tr("Failed to create SessionTaskCopyTo object"));
2750 throw;
2751 }
2752
2753 hrc = pTask->Init(Utf8StrFmt(tr("Copying \"%s\" from host to \"%s\" on the guest"), aSource.c_str(), aDest.c_str()));
2754 if (FAILED(hrc))
2755 {
2756 delete pTask;
2757 hrc = setError(VBOX_E_IPRT_ERROR,
2758 tr("Creating progress object for SessionTaskCopyTo object failed"));
2759 throw hrc;
2760 }
2761
2762 hrc = pTask->createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
2763
2764 if (SUCCEEDED(hrc))
2765 {
2766 /* Return progress to the caller. */
2767 pProgress = pTask->GetProgressObject();
2768 hrc = pProgress.queryInterfaceTo(aProgress.asOutParam());
2769 }
2770 else
2771 hrc = setError(VBOX_E_IPRT_ERROR,
2772 tr("Starting thread for copying file \"%s\" from host to \"%s\" on the guest failed "),
2773 aSource.c_str(), aDest.c_str());
2774 }
2775 catch(std::bad_alloc &)
2776 {
2777 hrc = E_OUTOFMEMORY;
2778 }
2779 catch(HRESULT eHR)
2780 {
2781 hrc = eHR;
2782 LogFlowThisFunc(("Exception was caught in the function \n"));
2783 }
2784
2785 return hrc;
2786}
2787
2788HRESULT GuestSession::copyFromGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
2789 const std::vector<FsObjType_T> &aTypes, const std::vector<GuestCopyFlag_T> &aFlags,
2790 const com::Utf8Str &aDestination, ComPtr<IProgress> &aProgress)
2791{
2792 RT_NOREF(aSources, aFilters, aTypes, aFlags, aDestination, aProgress);
2793 ReturnComNotImplemented();
2794}
2795
2796HRESULT GuestSession::copyToGuest(const std::vector<com::Utf8Str> &aSources, const std::vector<com::Utf8Str> &aFilters,
2797 const std::vector<FsObjType_T> &aTypes, const std::vector<GuestCopyFlag_T> &aFlags,
2798 const com::Utf8Str &aDestination, ComPtr<IProgress> &aProgress)
2799{
2800 RT_NOREF(aSources, aFilters, aTypes, aFlags, aDestination, aProgress);
2801 ReturnComNotImplemented();
2802}
2803
2804HRESULT GuestSession::directoryCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2805 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
2806{
2807 RT_NOREF(aSource, aDestination, aFlags, aProgress);
2808 ReturnComNotImplemented();
2809}
2810
2811HRESULT GuestSession::directoryCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2812 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
2813{
2814 AutoCaller autoCaller(this);
2815 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2816
2817 if (RT_UNLIKELY((aSource.c_str()) == NULL || *(aSource.c_str()) == '\0'))
2818 return setError(E_INVALIDARG, tr("No source directory specified"));
2819
2820 if (RT_UNLIKELY((aDestination.c_str()) == NULL || *(aDestination.c_str()) == '\0'))
2821 return setError(E_INVALIDARG, tr("No destination directory specified"));
2822
2823 uint32_t fFlags = DirectoryCopyFlag_None;
2824 if (aFlags.size())
2825 {
2826 for (size_t i = 0; i < aFlags.size(); i++)
2827 fFlags |= aFlags[i];
2828 }
2829
2830 if (fFlags)
2831 {
2832 if (!(fFlags & DirectoryCopyFlag_CopyIntoExisting))
2833 return setError(E_INVALIDARG, tr("Invalid flags specified"));
2834 }
2835
2836 HRESULT hrc = i_isReadyExternal();
2837 if (FAILED(hrc))
2838 return hrc;
2839
2840 LogFlowThisFuncEnter();
2841
2842 try
2843 {
2844 GuestSessionTaskCopyFrom *pTask = NULL;
2845 ComObjPtr<Progress> pProgress;
2846 try
2847 {
2848 GuestSessionFsSourceSpec source;
2849 source.strSource = aSource;
2850 source.enmType = GuestSessionFsSourceType_Dir;
2851 source.enmPathStyle = i_getPathStyle();
2852 source.Type.Dir.fCopyFlags = (DirectoryCopyFlag_T)fFlags;
2853
2854 GuestSessionFsSourceSet vecSources;
2855 vecSources.push_back(source);
2856
2857 pTask = new GuestSessionTaskCopyFrom(this /* GuestSession */, vecSources, aDestination);
2858 }
2859 catch(...)
2860 {
2861 hrc = setError(VBOX_E_IPRT_ERROR, tr("Failed to create SessionTaskCopyFrom object"));
2862 throw;
2863 }
2864
2865 hrc = pTask->Init(Utf8StrFmt(tr("Copying directory \"%s\" from guest to \"%s\" on the host"),
2866 aSource.c_str(), aDestination.c_str()));
2867 if (FAILED(hrc))
2868 {
2869 delete pTask;
2870 hrc = setError(VBOX_E_IPRT_ERROR,
2871 tr("Creating progress object for SessionTaskCopyDirFrom object failed"));
2872 throw hrc;
2873 }
2874
2875 hrc = pTask->createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
2876
2877 if (SUCCEEDED(hrc))
2878 {
2879 /* Return progress to the caller. */
2880 pProgress = pTask->GetProgressObject();
2881 hrc = pProgress.queryInterfaceTo(aProgress.asOutParam());
2882 }
2883 else
2884 hrc = setError(VBOX_E_IPRT_ERROR,
2885 tr("Starting thread for copying directory \"%s\" from guest to \"%s\" on the host failed"),
2886 aSource.c_str(), aDestination.c_str());
2887 }
2888 catch(std::bad_alloc &)
2889 {
2890 hrc = E_OUTOFMEMORY;
2891 }
2892 catch(HRESULT eHR)
2893 {
2894 hrc = eHR;
2895 LogFlowThisFunc(("Exception was caught in the function\n"));
2896 }
2897
2898 return hrc;
2899}
2900
2901HRESULT GuestSession::directoryCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2902 const std::vector<DirectoryCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
2903{
2904 AutoCaller autoCaller(this);
2905 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2906
2907 if (RT_UNLIKELY((aSource.c_str()) == NULL || *(aSource.c_str()) == '\0'))
2908 return setError(E_INVALIDARG, tr("No source directory specified"));
2909
2910 if (RT_UNLIKELY((aDestination.c_str()) == NULL || *(aDestination.c_str()) == '\0'))
2911 return setError(E_INVALIDARG, tr("No destination directory specified"));
2912
2913 if (!RTPathExists(aSource.c_str()))
2914 return setError(E_INVALIDARG, tr("Source directory \"%s\" does not exist"), aSource.c_str());
2915
2916 uint32_t fFlags = DirectoryCopyFlag_None;
2917 if (aFlags.size())
2918 {
2919 for (size_t i = 0; i < aFlags.size(); i++)
2920 fFlags |= aFlags[i];
2921 }
2922
2923 if (fFlags)
2924 {
2925 if (!(fFlags & DirectoryCopyFlag_CopyIntoExisting))
2926 return setError(E_INVALIDARG, tr("Invalid flags specified"));
2927 }
2928
2929 HRESULT hrc = i_isReadyExternal();
2930 if (FAILED(hrc))
2931 return hrc;
2932
2933 LogFlowThisFuncEnter();
2934
2935 try
2936 {
2937 GuestSessionTaskCopyTo *pTask = NULL;
2938 ComObjPtr<Progress> pProgress;
2939 try
2940 {
2941 GuestSessionFsSourceSpec source;
2942 source.strSource = aSource;
2943 source.enmType = GuestSessionFsSourceType_Dir;
2944#if RTPATH_STYLE == RTPATH_STR_F_STYLE_DOS
2945 source.enmPathStyle = PathStyle_DOS;
2946#else
2947 source.enmPathStyle = PathStyle_UNIX;
2948#endif
2949 source.fFollowSymlinks = true;
2950 source.Type.Dir.fRecursive = true;
2951 source.Type.Dir.fCopyFlags = (DirectoryCopyFlag_T)fFlags;
2952
2953 GuestSessionFsSourceSet vecSources;
2954 vecSources.push_back(source);
2955
2956 pTask = new GuestSessionTaskCopyTo(this /* GuestSession */, vecSources, aDestination);
2957 }
2958 catch(...)
2959 {
2960 hrc = setError(VBOX_E_IPRT_ERROR, tr("Failed to create SessionTaskCopyTo object"));
2961 throw;
2962 }
2963
2964 hrc = pTask->Init(Utf8StrFmt(tr("Copying directory \"%s\" from host to \"%s\" on the guest"),
2965 aSource.c_str(), aDestination.c_str()));
2966 if (FAILED(hrc))
2967 {
2968 delete pTask;
2969 hrc = setError(VBOX_E_IPRT_ERROR,
2970 tr("Creating progress object for SessionTaskCopyDirTo object failed"));
2971 throw hrc;
2972 }
2973
2974 hrc = pTask->createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
2975
2976 if (SUCCEEDED(hrc))
2977 {
2978 /* Return progress to the caller. */
2979 pProgress = pTask->GetProgressObject();
2980 hrc = pProgress.queryInterfaceTo(aProgress.asOutParam());
2981 }
2982 else
2983 hrc = setError(VBOX_E_IPRT_ERROR,
2984 tr("Starting thread for copying directory \"%s\" from host to \"%s\" on the guest failed"),
2985 aSource.c_str(), aDestination.c_str());
2986 }
2987 catch(std::bad_alloc &)
2988 {
2989 hrc = E_OUTOFMEMORY;
2990 }
2991 catch(HRESULT eHR)
2992 {
2993 hrc = eHR;
2994 LogFlowThisFunc(("Exception was caught in the function\n"));
2995 }
2996
2997 return hrc;
2998}
2999
3000HRESULT GuestSession::directoryCreate(const com::Utf8Str &aPath, ULONG aMode,
3001 const std::vector<DirectoryCreateFlag_T> &aFlags)
3002{
3003 AutoCaller autoCaller(this);
3004 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3005
3006 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3007 return setError(E_INVALIDARG, tr("No directory to create specified"));
3008
3009 uint32_t fFlags = DirectoryCreateFlag_None;
3010 if (aFlags.size())
3011 {
3012 for (size_t i = 0; i < aFlags.size(); i++)
3013 fFlags |= aFlags[i];
3014
3015 if (fFlags)
3016 if (!(fFlags & DirectoryCreateFlag_Parents))
3017 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), fFlags);
3018 }
3019
3020 HRESULT hrc = i_isReadyExternal();
3021 if (FAILED(hrc))
3022 return hrc;
3023
3024 LogFlowThisFuncEnter();
3025
3026 ComObjPtr <GuestDirectory> pDirectory; int rcGuest;
3027 int rc = i_directoryCreate(aPath, (uint32_t)aMode, fFlags, &rcGuest);
3028 if (RT_FAILURE(rc))
3029 {
3030 if (GuestProcess::i_isGuestError(rc))
3031 {
3032 hrc = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: %s"),
3033 GuestDirectory::i_guestErrorToString(rcGuest).c_str());
3034 }
3035 else
3036 {
3037 switch (rc)
3038 {
3039 case VERR_INVALID_PARAMETER:
3040 hrc = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Invalid parameters given"));
3041 break;
3042
3043 case VERR_BROKEN_PIPE:
3044 hrc = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Unexpectedly aborted"));
3045 break;
3046
3047 default:
3048 hrc = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: %Rrc"), rc);
3049 break;
3050 }
3051 }
3052 }
3053
3054 return hrc;
3055}
3056
3057HRESULT GuestSession::directoryCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath,
3058 BOOL aSecure, com::Utf8Str &aDirectory)
3059{
3060 RT_NOREF(aMode, aSecure);
3061
3062 AutoCaller autoCaller(this);
3063 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3064
3065 if (RT_UNLIKELY((aTemplateName.c_str()) == NULL || *(aTemplateName.c_str()) == '\0'))
3066 return setError(E_INVALIDARG, tr("No template specified"));
3067 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3068 return setError(E_INVALIDARG, tr("No directory name specified"));
3069
3070 HRESULT hrc = i_isReadyExternal();
3071 if (FAILED(hrc))
3072 return hrc;
3073
3074 LogFlowThisFuncEnter();
3075
3076 int rcGuest;
3077 int rc = i_fsCreateTemp(aTemplateName, aPath, true /* Directory */, aDirectory, &rcGuest);
3078 if (!RT_SUCCESS(rc))
3079 {
3080 switch (rc)
3081 {
3082 case VERR_GSTCTL_GUEST_ERROR:
3083 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3084 break;
3085
3086 default:
3087 hrc = setError(VBOX_E_IPRT_ERROR, tr("Temporary directory creation \"%s\" with template \"%s\" failed: %Rrc"),
3088 aPath.c_str(), aTemplateName.c_str(), rc);
3089 break;
3090 }
3091 }
3092
3093 return hrc;
3094}
3095
3096HRESULT GuestSession::directoryExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3097{
3098 AutoCaller autoCaller(this);
3099 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3100
3101 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3102 return setError(E_INVALIDARG, tr("No directory to check existence for specified"));
3103
3104 HRESULT hrc = i_isReadyExternal();
3105 if (FAILED(hrc))
3106 return hrc;
3107
3108 LogFlowThisFuncEnter();
3109
3110 GuestFsObjData objData; int rcGuest;
3111 int rc = i_directoryQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3112 if (RT_SUCCESS(rc))
3113 *aExists = objData.mType == FsObjType_Directory;
3114 else
3115 {
3116 switch (rc)
3117 {
3118 case VERR_GSTCTL_GUEST_ERROR:
3119 {
3120 switch (rcGuest)
3121 {
3122 case VERR_PATH_NOT_FOUND:
3123 *aExists = FALSE;
3124 break;
3125 default:
3126 hrc = setError(VBOX_E_IPRT_ERROR, tr("Querying directory existence \"%s\" failed: %s"),
3127 aPath.c_str(), GuestProcess::i_guestErrorToString(rcGuest).c_str());
3128 break;
3129 }
3130 break;
3131 }
3132
3133 default:
3134 hrc = setError(VBOX_E_IPRT_ERROR, tr("Querying directory existence \"%s\" failed: %Rrc"),
3135 aPath.c_str(), rc);
3136 break;
3137 }
3138 }
3139
3140 return hrc;
3141}
3142
3143HRESULT GuestSession::directoryOpen(const com::Utf8Str &aPath, const com::Utf8Str &aFilter,
3144 const std::vector<DirectoryOpenFlag_T> &aFlags, ComPtr<IGuestDirectory> &aDirectory)
3145{
3146 AutoCaller autoCaller(this);
3147 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3148
3149 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3150 return setError(E_INVALIDARG, tr("No directory to open specified"));
3151 if (RT_UNLIKELY((aFilter.c_str()) != NULL && *(aFilter.c_str()) != '\0'))
3152 return setError(E_INVALIDARG, tr("Directory filters are not implemented yet"));
3153
3154 uint32_t fFlags = DirectoryOpenFlag_None;
3155 if (aFlags.size())
3156 {
3157 for (size_t i = 0; i < aFlags.size(); i++)
3158 fFlags |= aFlags[i];
3159
3160 if (fFlags)
3161 return setError(E_INVALIDARG, tr("Open flags (%#x) not implemented yet"), fFlags);
3162 }
3163
3164 HRESULT hrc = i_isReadyExternal();
3165 if (FAILED(hrc))
3166 return hrc;
3167
3168 LogFlowThisFuncEnter();
3169
3170 GuestDirectoryOpenInfo openInfo;
3171 openInfo.mPath = aPath;
3172 openInfo.mFilter = aFilter;
3173 openInfo.mFlags = fFlags;
3174
3175 ComObjPtr <GuestDirectory> pDirectory; int rcGuest;
3176 int rc = i_directoryOpen(openInfo, pDirectory, &rcGuest);
3177 if (RT_SUCCESS(rc))
3178 {
3179 /* Return directory object to the caller. */
3180 hrc = pDirectory.queryInterfaceTo(aDirectory.asOutParam());
3181 }
3182 else
3183 {
3184 switch (rc)
3185 {
3186 case VERR_INVALID_PARAMETER:
3187 hrc = setError(VBOX_E_IPRT_ERROR, tr("Opening directory \"%s\" failed; invalid parameters given"),
3188 aPath.c_str());
3189 break;
3190
3191 case VERR_GSTCTL_GUEST_ERROR:
3192 hrc = GuestDirectory::i_setErrorExternal(this, rcGuest);
3193 break;
3194
3195 default:
3196 hrc = setError(VBOX_E_IPRT_ERROR, tr("Opening directory \"%s\" failed: %Rrc"),
3197 aPath.c_str(),rc);
3198 break;
3199 }
3200 }
3201
3202 return hrc;
3203}
3204
3205HRESULT GuestSession::directoryRemove(const com::Utf8Str &aPath)
3206{
3207 AutoCaller autoCaller(this);
3208 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3209
3210 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3211 return setError(E_INVALIDARG, tr("No directory to remove specified"));
3212
3213 HRESULT hrc = i_isReadyExternal();
3214 if (FAILED(hrc))
3215 return hrc;
3216
3217 LogFlowThisFuncEnter();
3218
3219 /* No flags; only remove the directory when empty. */
3220 uint32_t uFlags = 0;
3221
3222 int rcGuest;
3223 int vrc = i_directoryRemove(aPath, uFlags, &rcGuest);
3224 if (RT_FAILURE(vrc))
3225 {
3226 switch (vrc)
3227 {
3228 case VERR_NOT_SUPPORTED:
3229 hrc = setError(VBOX_E_IPRT_ERROR,
3230 tr("Handling removing guest directories not supported by installed Guest Additions"));
3231 break;
3232
3233 case VERR_GSTCTL_GUEST_ERROR:
3234 hrc = GuestDirectory::i_setErrorExternal(this, rcGuest);
3235 break;
3236
3237 default:
3238 hrc = setError(VBOX_E_IPRT_ERROR, tr("Removing guest directory \"%s\" failed: %Rrc"),
3239 aPath.c_str(), vrc);
3240 break;
3241 }
3242 }
3243
3244 return hrc;
3245}
3246
3247HRESULT GuestSession::directoryRemoveRecursive(const com::Utf8Str &aPath, const std::vector<DirectoryRemoveRecFlag_T> &aFlags,
3248 ComPtr<IProgress> &aProgress)
3249{
3250 RT_NOREF(aFlags);
3251
3252 AutoCaller autoCaller(this);
3253 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3254
3255 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3256 return setError(E_INVALIDARG, tr("No directory to remove recursively specified"));
3257
3258/** @todo r=bird: Must check that the flags matches the hardcoded behavior
3259 * further down!! */
3260
3261 HRESULT hrc = i_isReadyExternal();
3262 if (FAILED(hrc))
3263 return hrc;
3264
3265 LogFlowThisFuncEnter();
3266
3267 ComObjPtr<Progress> pProgress;
3268 hrc = pProgress.createObject();
3269 if (SUCCEEDED(hrc))
3270 hrc = pProgress->init(static_cast<IGuestSession *>(this),
3271 Bstr(tr("Removing guest directory")).raw(),
3272 TRUE /*aCancelable*/);
3273 if (FAILED(hrc))
3274 return hrc;
3275
3276 /* Note: At the moment we don't supply progress information while
3277 * deleting a guest directory recursively. So just complete
3278 * the progress object right now. */
3279 /** @todo Implement progress reporting on guest directory deletion! */
3280 hrc = pProgress->i_notifyComplete(S_OK);
3281 if (FAILED(hrc))
3282 return hrc;
3283
3284 /* Remove the directory + all its contents. */
3285 uint32_t uFlags = DIRREMOVE_FLAG_RECURSIVE
3286 | DIRREMOVE_FLAG_CONTENT_AND_DIR;
3287 int rcGuest;
3288 int vrc = i_directoryRemove(aPath, uFlags, &rcGuest);
3289 if (RT_FAILURE(vrc))
3290 {
3291 switch (vrc)
3292 {
3293 case VERR_NOT_SUPPORTED:
3294 hrc = setError(VBOX_E_IPRT_ERROR,
3295 tr("Handling removing guest directories recursively not supported by installed Guest Additions"));
3296 break;
3297
3298 case VERR_GSTCTL_GUEST_ERROR:
3299 hrc = GuestFile::i_setErrorExternal(this, rcGuest);
3300 break;
3301
3302 default:
3303 hrc = setError(VBOX_E_IPRT_ERROR, tr("Recursively removing guest directory \"%s\" failed: %Rrc"),
3304 aPath.c_str(), vrc);
3305 break;
3306 }
3307 }
3308 else
3309 {
3310 pProgress.queryInterfaceTo(aProgress.asOutParam());
3311 }
3312
3313 return hrc;
3314}
3315
3316HRESULT GuestSession::environmentScheduleSet(const com::Utf8Str &aName, const com::Utf8Str &aValue)
3317{
3318 AutoCaller autoCaller(this);
3319 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3320
3321 HRESULT hrc;
3322 if (RT_LIKELY(aName.isNotEmpty()))
3323 {
3324 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3325 {
3326 LogFlowThisFuncEnter();
3327
3328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3329 int vrc = mData.mEnvironmentChanges.setVariable(aName, aValue);
3330 if (RT_SUCCESS(vrc))
3331 hrc = S_OK;
3332 else
3333 hrc = setErrorVrc(vrc);
3334 }
3335 else
3336 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3337 }
3338 else
3339 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3340
3341 LogFlowThisFuncLeave();
3342 return hrc;
3343}
3344
3345HRESULT GuestSession::environmentScheduleUnset(const com::Utf8Str &aName)
3346{
3347 AutoCaller autoCaller(this);
3348 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3349
3350 HRESULT hrc;
3351 if (RT_LIKELY(aName.isNotEmpty()))
3352 {
3353 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3354 {
3355 LogFlowThisFuncEnter();
3356
3357 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3358 int vrc = mData.mEnvironmentChanges.unsetVariable(aName);
3359 if (RT_SUCCESS(vrc))
3360 hrc = S_OK;
3361 else
3362 hrc = setErrorVrc(vrc);
3363 }
3364 else
3365 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3366 }
3367 else
3368 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3369
3370 LogFlowThisFuncLeave();
3371 return hrc;
3372}
3373
3374HRESULT GuestSession::environmentGetBaseVariable(const com::Utf8Str &aName, com::Utf8Str &aValue)
3375{
3376 AutoCaller autoCaller(this);
3377 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3378
3379 HRESULT hrc;
3380 if (RT_LIKELY(aName.isNotEmpty()))
3381 {
3382 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3383 {
3384 LogFlowThisFuncEnter();
3385
3386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3387 if (mData.mpBaseEnvironment)
3388 {
3389 int vrc = mData.mpBaseEnvironment->getVariable(aName, &aValue);
3390 if (RT_SUCCESS(vrc))
3391 hrc = S_OK;
3392 else
3393 hrc = setErrorVrc(vrc);
3394 }
3395 else if (mData.mProtocolVersion < 99999)
3396 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
3397 else
3398 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
3399 }
3400 else
3401 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3402 }
3403 else
3404 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3405
3406 LogFlowThisFuncLeave();
3407 return hrc;
3408}
3409
3410HRESULT GuestSession::environmentDoesBaseVariableExist(const com::Utf8Str &aName, BOOL *aExists)
3411{
3412 AutoCaller autoCaller(this);
3413 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3414
3415 *aExists = FALSE;
3416
3417 HRESULT hrc;
3418 if (RT_LIKELY(aName.isNotEmpty()))
3419 {
3420 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3421 {
3422 LogFlowThisFuncEnter();
3423
3424 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3425 if (mData.mpBaseEnvironment)
3426 {
3427 hrc = S_OK;
3428 *aExists = mData.mpBaseEnvironment->doesVariableExist(aName);
3429 }
3430 else if (mData.mProtocolVersion < 99999)
3431 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
3432 else
3433 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
3434 }
3435 else
3436 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3437 }
3438 else
3439 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3440
3441 LogFlowThisFuncLeave();
3442 return hrc;
3443}
3444
3445HRESULT GuestSession::fileCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath, BOOL aSecure,
3446 ComPtr<IGuestFile> &aFile)
3447{
3448 RT_NOREF(aTemplateName, aMode, aPath, aSecure, aFile);
3449 ReturnComNotImplemented();
3450}
3451
3452HRESULT GuestSession::fileExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3453{
3454 AutoCaller autoCaller(this);
3455 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3456
3457 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3458 {
3459 *aExists = FALSE;
3460 return S_OK;
3461 }
3462
3463 HRESULT hrc = i_isReadyExternal();
3464 if (FAILED(hrc))
3465 return hrc;
3466
3467 LogFlowThisFuncEnter();
3468
3469 GuestFsObjData objData; int rcGuest;
3470 int vrc = i_fileQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3471 if (RT_SUCCESS(vrc))
3472 {
3473 *aExists = TRUE;
3474 return S_OK;
3475 }
3476
3477 switch (vrc)
3478 {
3479 case VERR_GSTCTL_GUEST_ERROR:
3480 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3481 break;
3482
3483/** @todo r=bird: what about VERR_PATH_NOT_FOUND and VERR_FILE_NOT_FOUND?
3484 * Where does that get converted to *aExists = FALSE? */
3485 case VERR_NOT_A_FILE:
3486 *aExists = FALSE;
3487 break;
3488
3489 default:
3490 hrc = setError(VBOX_E_IPRT_ERROR, tr("Querying file information for \"%s\" failed: %Rrc"),
3491 aPath.c_str(), vrc);
3492 break;
3493 }
3494
3495 return hrc;
3496}
3497
3498HRESULT GuestSession::fileOpen(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3499 ULONG aCreationMode, ComPtr<IGuestFile> &aFile)
3500{
3501 LogFlowThisFuncEnter();
3502
3503 const std::vector<FileOpenExFlag_T> EmptyFlags;
3504 return fileOpenEx(aPath, aAccessMode, aOpenAction, FileSharingMode_All, aCreationMode, EmptyFlags, aFile);
3505}
3506
3507HRESULT GuestSession::fileOpenEx(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3508 FileSharingMode_T aSharingMode, ULONG aCreationMode,
3509 const std::vector<FileOpenExFlag_T> &aFlags, ComPtr<IGuestFile> &aFile)
3510{
3511 AutoCaller autoCaller(this);
3512 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3513
3514 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3515 return setError(E_INVALIDARG, tr("No file to open specified"));
3516
3517 HRESULT hrc = i_isReadyExternal();
3518 if (FAILED(hrc))
3519 return hrc;
3520
3521 LogFlowThisFuncEnter();
3522
3523 GuestFileOpenInfo openInfo;
3524 openInfo.mFileName = aPath;
3525 openInfo.mCreationMode = aCreationMode;
3526
3527 /* Validate aAccessMode. */
3528 switch (aAccessMode)
3529 {
3530 case (FileAccessMode_T)FileAccessMode_ReadOnly:
3531 RT_FALL_THRU();
3532 case (FileAccessMode_T)FileAccessMode_WriteOnly:
3533 RT_FALL_THRU();
3534 case (FileAccessMode_T)FileAccessMode_ReadWrite:
3535 openInfo.mAccessMode = aAccessMode;
3536 break;
3537 case (FileAccessMode_T)FileAccessMode_AppendOnly:
3538 RT_FALL_THRU();
3539 case (FileAccessMode_T)FileAccessMode_AppendRead:
3540 return setError(E_NOTIMPL, tr("Append access modes are not yet implemented"));
3541 default:
3542 return setError(E_INVALIDARG, tr("Unknown FileAccessMode value %u (%#x)"), aAccessMode, aAccessMode);
3543 }
3544
3545 /* Validate aOpenAction to the old format. */
3546 switch (aOpenAction)
3547 {
3548 case (FileOpenAction_T)FileOpenAction_OpenExisting:
3549 RT_FALL_THRU();
3550 case (FileOpenAction_T)FileOpenAction_OpenOrCreate:
3551 RT_FALL_THRU();
3552 case (FileOpenAction_T)FileOpenAction_CreateNew:
3553 RT_FALL_THRU();
3554 case (FileOpenAction_T)FileOpenAction_CreateOrReplace:
3555 RT_FALL_THRU();
3556 case (FileOpenAction_T)FileOpenAction_OpenExistingTruncated:
3557 RT_FALL_THRU();
3558 case (FileOpenAction_T)FileOpenAction_AppendOrCreate:
3559 openInfo.mOpenAction = aOpenAction;
3560 break;
3561 default:
3562 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3563 }
3564
3565 /* Validate aSharingMode. */
3566 switch (aSharingMode)
3567 {
3568 case (FileSharingMode_T)FileSharingMode_All:
3569 openInfo.mSharingMode = aSharingMode;
3570 break;
3571 case (FileSharingMode_T)FileSharingMode_Read:
3572 case (FileSharingMode_T)FileSharingMode_Write:
3573 case (FileSharingMode_T)FileSharingMode_ReadWrite:
3574 case (FileSharingMode_T)FileSharingMode_Delete:
3575 case (FileSharingMode_T)FileSharingMode_ReadDelete:
3576 case (FileSharingMode_T)FileSharingMode_WriteDelete:
3577 return setError(E_NOTIMPL, tr("Only FileSharingMode_All is currently implemented"));
3578
3579 default:
3580 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3581 }
3582
3583 /* Combine and validate flags. */
3584 uint32_t fOpenEx = 0;
3585 for (size_t i = 0; i < aFlags.size(); i++)
3586 fOpenEx = aFlags[i];
3587 if (fOpenEx)
3588 return setError(E_INVALIDARG, tr("Unsupported FileOpenExFlag value(s) in aFlags (%#x)"), fOpenEx);
3589 openInfo.mfOpenEx = fOpenEx;
3590
3591 ComObjPtr <GuestFile> pFile;
3592 int rcGuest;
3593 int vrc = i_fileOpenEx(aPath, aAccessMode, aOpenAction, aSharingMode, aCreationMode, aFlags, pFile, &rcGuest);
3594 if (RT_SUCCESS(vrc))
3595 /* Return directory object to the caller. */
3596 hrc = pFile.queryInterfaceTo(aFile.asOutParam());
3597 else
3598 {
3599 switch (vrc)
3600 {
3601 case VERR_NOT_SUPPORTED:
3602 hrc = setError(VBOX_E_IPRT_ERROR,
3603 tr("Handling guest files not supported by installed Guest Additions"));
3604 break;
3605
3606 case VERR_GSTCTL_GUEST_ERROR:
3607 hrc = GuestFile::i_setErrorExternal(this, rcGuest);
3608 break;
3609
3610 default:
3611 hrc = setError(VBOX_E_IPRT_ERROR, tr("Opening guest file \"%s\" failed: %Rrc"),
3612 aPath.c_str(), vrc);
3613 break;
3614 }
3615 }
3616
3617 return hrc;
3618}
3619
3620HRESULT GuestSession::fileQuerySize(const com::Utf8Str &aPath, BOOL aFollowSymlinks, LONG64 *aSize)
3621{
3622 AutoCaller autoCaller(this);
3623 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3624
3625 if (aPath.isEmpty())
3626 return setError(E_INVALIDARG, tr("No path specified"));
3627
3628 HRESULT hrc = i_isReadyExternal();
3629 if (FAILED(hrc))
3630 return hrc;
3631
3632 int64_t llSize; int rcGuest;
3633 int vrc = i_fileQuerySize(aPath, aFollowSymlinks != FALSE, &llSize, &rcGuest);
3634 if (RT_SUCCESS(vrc))
3635 {
3636 *aSize = llSize;
3637 }
3638 else
3639 {
3640 if (GuestProcess::i_isGuestError(vrc))
3641 {
3642 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3643 }
3644 else
3645 hrc = setError(VBOX_E_IPRT_ERROR, tr("Querying file size failed: %Rrc"), vrc);
3646 }
3647
3648 return hrc;
3649}
3650
3651HRESULT GuestSession::fsObjExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3652{
3653 AutoCaller autoCaller(this);
3654 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3655
3656 if (aPath.isEmpty())
3657 return setError(E_INVALIDARG, tr("No path specified"));
3658
3659 HRESULT hrc = i_isReadyExternal();
3660 if (FAILED(hrc))
3661 return hrc;
3662
3663 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
3664
3665 *aExists = false;
3666
3667 GuestFsObjData objData;
3668 int rcGuest;
3669 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3670 if (RT_SUCCESS(vrc))
3671 {
3672 *aExists = TRUE;
3673 }
3674 else
3675 {
3676 if (GuestProcess::i_isGuestError(vrc))
3677 {
3678 if ( rcGuest == VERR_NOT_A_FILE
3679 || rcGuest == VERR_PATH_NOT_FOUND
3680 || rcGuest == VERR_FILE_NOT_FOUND
3681 || rcGuest == VERR_INVALID_NAME)
3682 {
3683 hrc = S_OK; /* Ignore these vrc values. */
3684 }
3685 else
3686 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3687 }
3688 else
3689 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3690 }
3691
3692 return hrc;
3693}
3694
3695HRESULT GuestSession::fsObjQueryInfo(const com::Utf8Str &aPath, BOOL aFollowSymlinks, ComPtr<IGuestFsObjInfo> &aInfo)
3696{
3697 AutoCaller autoCaller(this);
3698 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3699
3700 if (aPath.isEmpty())
3701 return setError(E_INVALIDARG, tr("No path specified"));
3702
3703 HRESULT hrc = i_isReadyExternal();
3704 if (FAILED(hrc))
3705 return hrc;
3706
3707 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
3708
3709 GuestFsObjData Info; int rcGuest;
3710 int vrc = i_fsQueryInfo(aPath, aFollowSymlinks != FALSE, Info, &rcGuest);
3711 if (RT_SUCCESS(vrc))
3712 {
3713 ComObjPtr<GuestFsObjInfo> ptrFsObjInfo;
3714 hrc = ptrFsObjInfo.createObject();
3715 if (SUCCEEDED(hrc))
3716 {
3717 vrc = ptrFsObjInfo->init(Info);
3718 if (RT_SUCCESS(vrc))
3719 hrc = ptrFsObjInfo.queryInterfaceTo(aInfo.asOutParam());
3720 else
3721 hrc = setErrorVrc(vrc);
3722 }
3723 }
3724 else
3725 {
3726 if (GuestProcess::i_isGuestError(vrc))
3727 {
3728 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3729 }
3730 else
3731 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3732 }
3733
3734 return hrc;
3735}
3736
3737HRESULT GuestSession::fsObjRemove(const com::Utf8Str &aPath)
3738{
3739 AutoCaller autoCaller(this);
3740 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3741
3742 if (RT_UNLIKELY(aPath.isEmpty()))
3743 return setError(E_INVALIDARG, tr("No path specified"));
3744
3745 HRESULT hrc = i_isReadyExternal();
3746 if (FAILED(hrc))
3747 return hrc;
3748
3749 LogFlowThisFunc(("aPath=%s\n", aPath.c_str()));
3750
3751 int rcGuest;
3752 int vrc = i_fileRemove(aPath, &rcGuest);
3753 if (RT_FAILURE(vrc))
3754 {
3755 if (GuestProcess::i_isGuestError(vrc))
3756 {
3757 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3758 }
3759 else
3760 hrc = setError(VBOX_E_IPRT_ERROR, tr("Removing file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3761 }
3762
3763 return hrc;
3764}
3765
3766HRESULT GuestSession::fsObjRename(const com::Utf8Str &aSource,
3767 const com::Utf8Str &aDestination,
3768 const std::vector<FsObjRenameFlag_T> &aFlags)
3769{
3770 AutoCaller autoCaller(this);
3771 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3772
3773 if (RT_UNLIKELY(aSource.isEmpty()))
3774 return setError(E_INVALIDARG, tr("No source path specified"));
3775
3776 if (RT_UNLIKELY(aDestination.isEmpty()))
3777 return setError(E_INVALIDARG, tr("No destination path specified"));
3778
3779 HRESULT hrc = i_isReadyExternal();
3780 if (FAILED(hrc))
3781 return hrc;
3782
3783 /* Combine, validate and convert flags. */
3784 uint32_t fApiFlags = 0;
3785 for (size_t i = 0; i < aFlags.size(); i++)
3786 fApiFlags |= aFlags[i];
3787 if (fApiFlags & ~((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace))
3788 return setError(E_INVALIDARG, tr("Unknown rename flag: %#x"), fApiFlags);
3789
3790 LogFlowThisFunc(("aSource=%s, aDestination=%s\n", aSource.c_str(), aDestination.c_str()));
3791
3792 AssertCompile(FsObjRenameFlag_NoReplace == 0);
3793 AssertCompile(FsObjRenameFlag_Replace != 0);
3794 uint32_t fBackend;
3795 if ((fApiFlags & ((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace)) == FsObjRenameFlag_Replace)
3796 fBackend = PATHRENAME_FLAG_REPLACE;
3797 else
3798 fBackend = PATHRENAME_FLAG_NO_REPLACE;
3799
3800 /* Call worker to do the job. */
3801 int rcGuest;
3802 int vrc = i_pathRename(aSource, aDestination, fBackend, &rcGuest);
3803 if (RT_FAILURE(vrc))
3804 {
3805 switch (vrc)
3806 {
3807 case VERR_NOT_SUPPORTED:
3808 hrc = setError(VBOX_E_IPRT_ERROR,
3809 tr("Handling renaming guest directories not supported by installed Guest Additions"));
3810 break;
3811
3812 case VERR_GSTCTL_GUEST_ERROR:
3813 hrc = setError(VBOX_E_IPRT_ERROR,
3814 tr("Renaming guest directory failed: %Rrc"), rcGuest);
3815 break;
3816
3817 default:
3818 hrc = setError(VBOX_E_IPRT_ERROR, tr("Renaming guest directory \"%s\" failed: %Rrc"),
3819 aSource.c_str(), vrc);
3820 break;
3821 }
3822 }
3823
3824 return hrc;
3825}
3826
3827HRESULT GuestSession::fsObjMove(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3828 const std::vector<FsObjMoveFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
3829{
3830 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3831 ReturnComNotImplemented();
3832}
3833
3834HRESULT GuestSession::fsObjSetACL(const com::Utf8Str &aPath, BOOL aFollowSymlinks, const com::Utf8Str &aAcl, ULONG aMode)
3835{
3836 RT_NOREF(aPath, aFollowSymlinks, aAcl, aMode);
3837 ReturnComNotImplemented();
3838}
3839
3840
3841HRESULT GuestSession::processCreate(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
3842 const std::vector<com::Utf8Str> &aEnvironment,
3843 const std::vector<ProcessCreateFlag_T> &aFlags,
3844 ULONG aTimeoutMS, ComPtr<IGuestProcess> &aGuestProcess)
3845{
3846 LogFlowThisFuncEnter();
3847
3848 std::vector<LONG> affinityIgnored;
3849 return processCreateEx(aExecutable, aArguments, aEnvironment, aFlags, aTimeoutMS, ProcessPriority_Default,
3850 affinityIgnored, aGuestProcess);
3851}
3852
3853HRESULT GuestSession::processCreateEx(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
3854 const std::vector<com::Utf8Str> &aEnvironment,
3855 const std::vector<ProcessCreateFlag_T> &aFlags, ULONG aTimeoutMS,
3856 ProcessPriority_T aPriority, const std::vector<LONG> &aAffinity,
3857 ComPtr<IGuestProcess> &aGuestProcess)
3858{
3859 AutoCaller autoCaller(this);
3860 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3861
3862 HRESULT hr = i_isReadyExternal();
3863 if (FAILED(hr))
3864 return hr;
3865
3866 /** @todo r=bird: Check input better? aPriority is passed on to the guest
3867 * without any validation. Flags not existing in this vbox version are
3868 * ignored, potentially doing something entirely different than what the
3869 * caller had in mind. */
3870
3871 /*
3872 * Must have an executable to execute. If none is given, we try use the
3873 * zero'th argument.
3874 */
3875 const char *pszExecutable = aExecutable.c_str();
3876 if (RT_UNLIKELY(pszExecutable == NULL || *pszExecutable == '\0'))
3877 {
3878 if (aArguments.size() > 0)
3879 pszExecutable = aArguments[0].c_str();
3880 if (pszExecutable == NULL || *pszExecutable == '\0')
3881 return setError(E_INVALIDARG, tr("No command to execute specified"));
3882 }
3883
3884 LogFlowThisFuncEnter();
3885
3886 /*
3887 * Build the process startup info.
3888 */
3889 GuestProcessStartupInfo procInfo;
3890
3891 /* Executable and arguments. */
3892 procInfo.mExecutable = pszExecutable;
3893 if (aArguments.size())
3894 for (size_t i = 0; i < aArguments.size(); i++)
3895 procInfo.mArguments.push_back(aArguments[i]);
3896
3897 /* Combine the environment changes associated with the ones passed in by
3898 the caller, giving priority to the latter. The changes are putenv style
3899 and will be applied to the standard environment for the guest user. */
3900 int vrc = procInfo.mEnvironmentChanges.copy(mData.mEnvironmentChanges);
3901 if (RT_SUCCESS(vrc))
3902 vrc = procInfo.mEnvironmentChanges.applyPutEnvArray(aEnvironment);
3903 if (RT_SUCCESS(vrc))
3904 {
3905 /* Convert the flag array into a mask. */
3906 if (aFlags.size())
3907 for (size_t i = 0; i < aFlags.size(); i++)
3908 procInfo.mFlags |= aFlags[i];
3909
3910 procInfo.mTimeoutMS = aTimeoutMS;
3911
3912 /** @todo use RTCPUSET instead of archaic 64-bit variables! */
3913 if (aAffinity.size())
3914 for (size_t i = 0; i < aAffinity.size(); i++)
3915 if (aAffinity[i])
3916 procInfo.mAffinity |= (uint64_t)1 << i;
3917
3918 procInfo.mPriority = aPriority;
3919
3920 /*
3921 * Create a guest process object.
3922 */
3923 ComObjPtr<GuestProcess> pProcess;
3924 vrc = i_processCreateEx(procInfo, pProcess);
3925 if (RT_SUCCESS(vrc))
3926 {
3927 ComPtr<IGuestProcess> pIProcess;
3928 hr = pProcess.queryInterfaceTo(pIProcess.asOutParam());
3929 if (SUCCEEDED(hr))
3930 {
3931 /*
3932 * Start the process.
3933 */
3934 vrc = pProcess->i_startProcessAsync();
3935 if (RT_SUCCESS(vrc))
3936 {
3937 aGuestProcess = pIProcess;
3938
3939 LogFlowFuncLeaveRC(vrc);
3940 return S_OK;
3941 }
3942
3943 hr = setErrorVrc(vrc, tr("Failed to start guest process: %Rrc"), vrc);
3944 }
3945 }
3946 else if (vrc == VERR_GSTCTL_MAX_OBJECTS_REACHED)
3947 hr = setErrorVrc(vrc, tr("Maximum number of concurrent guest processes per session (%u) reached"),
3948 VBOX_GUESTCTRL_MAX_OBJECTS);
3949 else
3950 hr = setErrorVrc(vrc, tr("Failed to create guest process object: %Rrc"), vrc);
3951 }
3952 else
3953 hr = setErrorVrc(vrc, tr("Failed to set up the environment: %Rrc"), vrc);
3954
3955 LogFlowFuncLeaveRC(vrc);
3956 return hr;
3957}
3958
3959HRESULT GuestSession::processGet(ULONG aPid, ComPtr<IGuestProcess> &aGuestProcess)
3960
3961{
3962 AutoCaller autoCaller(this);
3963 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3964
3965 if (aPid == 0)
3966 return setError(E_INVALIDARG, tr("No valid process ID (PID) specified"));
3967
3968 LogFlowThisFunc(("PID=%RU32\n", aPid));
3969
3970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3971
3972 HRESULT hr = S_OK;
3973
3974 ComObjPtr<GuestProcess> pProcess;
3975 int rc = i_processGetByPID(aPid, &pProcess);
3976 if (RT_FAILURE(rc))
3977 hr = setError(E_INVALIDARG, tr("No process with PID %RU32 found"), aPid);
3978
3979 /* This will set (*aProcess) to NULL if pProgress is NULL. */
3980 HRESULT hr2 = pProcess.queryInterfaceTo(aGuestProcess.asOutParam());
3981 if (SUCCEEDED(hr))
3982 hr = hr2;
3983
3984 LogFlowThisFunc(("aProcess=%p, hr=%Rhrc\n", (IGuestProcess*)aGuestProcess, hr));
3985 return hr;
3986}
3987
3988HRESULT GuestSession::symlinkCreate(const com::Utf8Str &aSource, const com::Utf8Str &aTarget, SymlinkType_T aType)
3989{
3990 RT_NOREF(aSource, aTarget, aType);
3991 ReturnComNotImplemented();
3992}
3993
3994HRESULT GuestSession::symlinkExists(const com::Utf8Str &aSymlink, BOOL *aExists)
3995
3996{
3997 RT_NOREF(aSymlink, aExists);
3998 ReturnComNotImplemented();
3999}
4000
4001HRESULT GuestSession::symlinkRead(const com::Utf8Str &aSymlink, const std::vector<SymlinkReadFlag_T> &aFlags,
4002 com::Utf8Str &aTarget)
4003{
4004 RT_NOREF(aSymlink, aFlags, aTarget);
4005 ReturnComNotImplemented();
4006}
4007
4008HRESULT GuestSession::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
4009{
4010 AutoCaller autoCaller(this);
4011 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4012
4013 /* Note: No call to i_isReadyExternal() needed here, as the session might not has been started (yet). */
4014
4015 LogFlowThisFuncEnter();
4016
4017 HRESULT hrc = S_OK;
4018
4019 /*
4020 * Note: Do not hold any locks here while waiting!
4021 */
4022 int rcGuest; GuestSessionWaitResult_T waitResult;
4023 int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &rcGuest);
4024 if (RT_SUCCESS(vrc))
4025 *aReason = waitResult;
4026 else
4027 {
4028 switch (vrc)
4029 {
4030 case VERR_GSTCTL_GUEST_ERROR:
4031 hrc = GuestSession::i_setErrorExternal(this, rcGuest);
4032 break;
4033
4034 case VERR_TIMEOUT:
4035 *aReason = GuestSessionWaitResult_Timeout;
4036 break;
4037
4038 default:
4039 {
4040 const char *pszSessionName = mData.mSession.mName.c_str();
4041 hrc = setError(VBOX_E_IPRT_ERROR,
4042 tr("Waiting for guest session \"%s\" failed: %Rrc"),
4043 pszSessionName ? pszSessionName : tr("Unnamed"), vrc);
4044 break;
4045 }
4046 }
4047 }
4048
4049 LogFlowFuncLeaveRC(vrc);
4050 return hrc;
4051}
4052
4053HRESULT GuestSession::waitForArray(const std::vector<GuestSessionWaitForFlag_T> &aWaitFor, ULONG aTimeoutMS,
4054 GuestSessionWaitResult_T *aReason)
4055{
4056 AutoCaller autoCaller(this);
4057 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4058
4059 /* Note: No call to i_isReadyExternal() needed here, as the session might not has been started (yet). */
4060
4061 LogFlowThisFuncEnter();
4062
4063 /*
4064 * Note: Do not hold any locks here while waiting!
4065 */
4066 uint32_t fWaitFor = GuestSessionWaitForFlag_None;
4067 for (size_t i = 0; i < aWaitFor.size(); i++)
4068 fWaitFor |= aWaitFor[i];
4069
4070 return WaitFor(fWaitFor, aTimeoutMS, aReason);
4071}
Note: See TracBrowser for help on using the repository browser.

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