VirtualBox

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

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

*: scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use