VirtualBox

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

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

Main/GuestSessionImpl: Proposing more efficient and flexible copy flag parsing code. bugref:9135

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