VirtualBox

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

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

Guest Control: Moved the copying host -> guest implementations from VBoxMange into Main, where it actually belongs. Also cleaned up and refactored the internal session task helpers into file / directory primitives, which can be reused for various different tasks.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 114.2 KB
Line 
1/* $Id: GuestSessionImpl.cpp 71213 2018-03-05 20:30:44Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Guest session handling.
4 */
5
6/*
7 * Copyright (C) 2012-2018 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_MAIN_GUESTSESSION
23#include "LoggingNew.h"
24
25#include "GuestImpl.h"
26#ifndef VBOX_WITH_GUEST_CONTROL
27# error "VBOX_WITH_GUEST_CONTROL must defined in this file"
28#endif
29#include "GuestSessionImpl.h"
30#include "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
2460 if (fFlags)
2461 return setError(E_NOTIMPL, tr("Flag(s) not yet implemented"));
2462
2463 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2464
2465 HRESULT hr = S_OK;
2466
2467 try
2468 {
2469 SessionTaskCopyFileFrom *pTask = NULL;
2470 ComObjPtr<Progress> pProgress;
2471 try
2472 {
2473 pTask = new SessionTaskCopyFileFrom(this /* GuestSession */, aSource, aDest, fFlags);
2474 }
2475 catch(...)
2476 {
2477 hr = setError(VBOX_E_IPRT_ERROR, tr("Failed to create SessionTaskCopyFileFrom object"));
2478 throw;
2479 }
2480
2481
2482 hr = pTask->Init(Utf8StrFmt(tr("Copying \"%s\" from guest to \"%s\" on the host"), aSource.c_str(), aDest.c_str()));
2483 if (FAILED(hr))
2484 {
2485 delete pTask;
2486 hr = setError(VBOX_E_IPRT_ERROR,
2487 tr("Creating progress object for SessionTaskCopyFileFrom object failed"));
2488 throw hr;
2489 }
2490
2491 hr = pTask->createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
2492
2493 if (SUCCEEDED(hr))
2494 {
2495 /* Return progress to the caller. */
2496 pProgress = pTask->GetProgressObject();
2497 hr = pProgress.queryInterfaceTo(aProgress.asOutParam());
2498 }
2499 else
2500 hr = setError(VBOX_E_IPRT_ERROR,
2501 tr("Starting thread for copying file \"%s\" from guest to \"%s\" on the host failed "),
2502 aSource.c_str(), aDest.c_str());
2503
2504 }
2505 catch(std::bad_alloc &)
2506 {
2507 hr = E_OUTOFMEMORY;
2508 }
2509 catch(HRESULT eHR)
2510 {
2511 hr = eHR;
2512 LogFlowThisFunc(("Exception was caught in the function \n"));
2513 }
2514
2515 return hr;
2516}
2517
2518HRESULT GuestSession::fileCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDest,
2519 const std::vector<FileCopyFlag_T> &aFlags, ComPtr<IProgress> &aProgress)
2520{
2521 LogFlowThisFuncEnter();
2522
2523 if (RT_UNLIKELY((aSource.c_str()) == NULL || *(aSource.c_str()) == '\0'))
2524 return setError(E_INVALIDARG, tr("No source specified"));
2525 if (RT_UNLIKELY((aDest.c_str()) == NULL || *(aDest.c_str()) == '\0'))
2526 return setError(E_INVALIDARG, tr("No destination specified"));
2527
2528 uint32_t fFlags = FileCopyFlag_None;
2529 if (aFlags.size())
2530 {
2531 for (size_t i = 0; i < aFlags.size(); i++)
2532 fFlags |= aFlags[i];
2533 }
2534
2535 if (fFlags)
2536 return setError(E_NOTIMPL, tr("Flag(s) not yet implemented"));
2537
2538 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2539
2540 HRESULT hr = S_OK;
2541
2542 try
2543 {
2544 SessionTaskCopyFileTo *pTask = NULL;
2545 ComObjPtr<Progress> pProgress;
2546 try
2547 {
2548 pTask = new SessionTaskCopyFileTo(this /* GuestSession */, aSource, aDest, fFlags);
2549 }
2550 catch(...)
2551 {
2552 hr = setError(VBOX_E_IPRT_ERROR, tr("Failed to create SessionTaskCopyFileTo object"));
2553 throw;
2554 }
2555
2556 hr = pTask->Init(Utf8StrFmt(tr("Copying \"%s\" from host to \"%s\" on the guest"), aSource.c_str(), aDest.c_str()));
2557 if (FAILED(hr))
2558 {
2559 delete pTask;
2560 hr = setError(VBOX_E_IPRT_ERROR,
2561 tr("Creating progress object for SessionTaskCopyFileTo object failed"));
2562 throw hr;
2563 }
2564
2565 hr = pTask->createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
2566
2567 if (SUCCEEDED(hr))
2568 {
2569 /* Return progress to the caller. */
2570 pProgress = pTask->GetProgressObject();
2571 hr = pProgress.queryInterfaceTo(aProgress.asOutParam());
2572 }
2573 else
2574 hr = setError(VBOX_E_IPRT_ERROR,
2575 tr("Starting thread for copying file \"%s\" from host to \"%s\" on the guest failed "),
2576 aSource.c_str(), aDest.c_str());
2577 }
2578 catch(std::bad_alloc &)
2579 {
2580 hr = E_OUTOFMEMORY;
2581 }
2582 catch(HRESULT eHR)
2583 {
2584 hr = eHR;
2585 LogFlowThisFunc(("Exception was caught in the function \n"));
2586 }
2587
2588 return hr;
2589}
2590
2591HRESULT GuestSession::directoryCopy(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2592 const std::vector<DirectoryCopyFlags_T> &aFlags, ComPtr<IProgress> &aProgress)
2593{
2594 RT_NOREF(aSource, aDestination, aFlags, aProgress);
2595 ReturnComNotImplemented();
2596}
2597
2598HRESULT GuestSession::directoryCopyFromGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2599 const std::vector<DirectoryCopyFlags_T> &aFlags, ComPtr<IProgress> &aProgress)
2600{
2601 RT_NOREF(aSource, aDestination, aFlags, aProgress);
2602 ReturnComNotImplemented();
2603}
2604
2605HRESULT GuestSession::directoryCopyToGuest(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
2606 const std::vector<DirectoryCopyFlags_T> &aFlags, ComPtr<IProgress> &aProgress)
2607{
2608 LogFlowThisFuncEnter();
2609
2610 if (RT_UNLIKELY((aSource.c_str()) == NULL || *(aSource.c_str()) == '\0'))
2611 return setError(E_INVALIDARG, tr("No source directory specified"));
2612
2613 if (RT_UNLIKELY((aDestination.c_str()) == NULL || *(aDestination.c_str()) == '\0'))
2614 return setError(E_INVALIDARG, tr("No destination directory specified"));
2615
2616 if (!RTPathExists(aSource.c_str()))
2617 return setError(E_INVALIDARG, tr("Source directory \"%s\" does not exist"), aSource.c_str());
2618
2619 uint32_t fFlags = DirectoryCopyFlags_None;
2620 if (aFlags.size())
2621 {
2622 for (size_t i = 0; i < aFlags.size(); i++)
2623 fFlags |= aFlags[i];
2624 }
2625 /** @todo Validate flags. */
2626
2627 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2628
2629 HRESULT hr = S_OK;
2630
2631 try
2632 {
2633 SessionTaskCopyDirTo *pTask = NULL;
2634 ComObjPtr<Progress> pProgress;
2635 try
2636 {
2637 pTask = new SessionTaskCopyDirTo(this /* GuestSession */, aSource, aDestination, "" /* strFilter */, fFlags);
2638 }
2639 catch(...)
2640 {
2641 hr = setError(VBOX_E_IPRT_ERROR, tr("Failed to create SessionTaskCopyDirTo object"));
2642 throw;
2643 }
2644
2645 hr = pTask->Init(Utf8StrFmt(tr("Copying directory \"%s\" from host to \"%s\" on the guest"),
2646 aSource.c_str(), aDestination.c_str()));
2647 if (FAILED(hr))
2648 {
2649 delete pTask;
2650 hr = setError(VBOX_E_IPRT_ERROR,
2651 tr("Creating progress object for SessionTaskCopyDirTo object failed"));
2652 throw hr;
2653 }
2654
2655 hr = pTask->createThreadWithType(RTTHREADTYPE_MAIN_HEAVY_WORKER);
2656
2657 if (SUCCEEDED(hr))
2658 {
2659 /* Return progress to the caller. */
2660 pProgress = pTask->GetProgressObject();
2661 hr = pProgress.queryInterfaceTo(aProgress.asOutParam());
2662 }
2663 else
2664 hr = setError(VBOX_E_IPRT_ERROR,
2665 tr("Starting thread for copying directory \"%s\" from host to \"%s\" on the guest failed"),
2666 aSource.c_str(), aDestination.c_str());
2667 }
2668 catch(std::bad_alloc &)
2669 {
2670 hr = E_OUTOFMEMORY;
2671 }
2672 catch(HRESULT eHR)
2673 {
2674 hr = eHR;
2675 LogFlowThisFunc(("Exception was caught in the function\n"));
2676 }
2677
2678 return hr;
2679}
2680
2681HRESULT GuestSession::directoryCreate(const com::Utf8Str &aPath, ULONG aMode,
2682 const std::vector<DirectoryCreateFlag_T> &aFlags)
2683{
2684 LogFlowThisFuncEnter();
2685
2686 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
2687 return setError(E_INVALIDARG, tr("No directory to create specified"));
2688
2689 uint32_t fFlags = DirectoryCreateFlag_None;
2690 if (aFlags.size())
2691 {
2692 for (size_t i = 0; i < aFlags.size(); i++)
2693 fFlags |= aFlags[i];
2694
2695 if (fFlags)
2696 if (!(fFlags & DirectoryCreateFlag_Parents))
2697 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), fFlags);
2698 }
2699
2700 HRESULT hr = S_OK;
2701
2702 ComObjPtr <GuestDirectory> pDirectory; int guestRc;
2703 int rc = i_directoryCreateInternal(aPath, (uint32_t)aMode, fFlags, &guestRc);
2704 if (RT_FAILURE(rc))
2705 {
2706 switch (rc)
2707 {
2708 case VERR_GSTCTL_GUEST_ERROR:
2709 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: %s",
2710 GuestDirectory::i_guestErrorToString(guestRc).c_str()));
2711 break;
2712
2713 case VERR_INVALID_PARAMETER:
2714 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Invalid parameters given"));
2715 break;
2716
2717 case VERR_BROKEN_PIPE:
2718 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: Unexpectedly aborted"));
2719 break;
2720
2721 default:
2722 hr = setError(VBOX_E_IPRT_ERROR, tr("Directory creation failed: %Rrc"), rc);
2723 break;
2724 }
2725 }
2726
2727 return hr;
2728}
2729
2730HRESULT GuestSession::directoryCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath,
2731 BOOL aSecure, com::Utf8Str &aDirectory)
2732{
2733 RT_NOREF(aMode, aSecure);
2734 LogFlowThisFuncEnter();
2735
2736 if (RT_UNLIKELY((aTemplateName.c_str()) == NULL || *(aTemplateName.c_str()) == '\0'))
2737 return setError(E_INVALIDARG, tr("No template specified"));
2738 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
2739 return setError(E_INVALIDARG, tr("No directory name specified"));
2740
2741 HRESULT hr = S_OK;
2742
2743 int guestRc;
2744 int rc = i_objectCreateTempInternal(aTemplateName,
2745 aPath,
2746 true /* Directory */, aDirectory, &guestRc);
2747 if (!RT_SUCCESS(rc))
2748 {
2749 switch (rc)
2750 {
2751 case VERR_GSTCTL_GUEST_ERROR:
2752 hr = GuestProcess::i_setErrorExternal(this, guestRc);
2753 break;
2754
2755 default:
2756 hr = setError(VBOX_E_IPRT_ERROR, tr("Temporary directory creation \"%s\" with template \"%s\" failed: %Rrc"),
2757 aPath.c_str(), aTemplateName.c_str(), rc);
2758 break;
2759 }
2760 }
2761
2762 return hr;
2763}
2764
2765HRESULT GuestSession::directoryExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
2766{
2767 LogFlowThisFuncEnter();
2768
2769 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
2770 return setError(E_INVALIDARG, tr("No directory to check existence for specified"));
2771
2772 HRESULT hr = S_OK;
2773
2774 GuestFsObjData objData; int guestRc;
2775 int rc = i_directoryQueryInfoInternal(aPath, aFollowSymlinks != FALSE, objData, &guestRc);
2776 if (RT_SUCCESS(rc))
2777 *aExists = objData.mType == FsObjType_Directory;
2778 else
2779 {
2780 switch (rc)
2781 {
2782 case VERR_GSTCTL_GUEST_ERROR:
2783 {
2784 switch (guestRc)
2785 {
2786 case VERR_PATH_NOT_FOUND:
2787 *aExists = FALSE;
2788 break;
2789 default:
2790 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying directory existence \"%s\" failed: %s"),
2791 aPath.c_str(), GuestProcess::i_guestErrorToString(guestRc).c_str());
2792 break;
2793 }
2794 break;
2795 }
2796
2797 default:
2798 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying directory existence \"%s\" failed: %Rrc"),
2799 aPath.c_str(), rc);
2800 break;
2801 }
2802 }
2803
2804 return hr;
2805}
2806
2807HRESULT GuestSession::directoryOpen(const com::Utf8Str &aPath, const com::Utf8Str &aFilter,
2808 const std::vector<DirectoryOpenFlag_T> &aFlags, ComPtr<IGuestDirectory> &aDirectory)
2809{
2810 LogFlowThisFuncEnter();
2811
2812 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
2813 return setError(E_INVALIDARG, tr("No directory to open specified"));
2814 if (RT_UNLIKELY((aFilter.c_str()) != NULL && *(aFilter.c_str()) != '\0'))
2815 return setError(E_INVALIDARG, tr("Directory filters are not implemented yet"));
2816
2817 uint32_t fFlags = DirectoryOpenFlag_None;
2818 if (aFlags.size())
2819 {
2820 for (size_t i = 0; i < aFlags.size(); i++)
2821 fFlags |= aFlags[i];
2822
2823 if (fFlags)
2824 return setError(E_INVALIDARG, tr("Open flags (%#x) not implemented yet"), fFlags);
2825 }
2826
2827 HRESULT hr = S_OK;
2828
2829 GuestDirectoryOpenInfo openInfo;
2830 openInfo.mPath = aPath;
2831 openInfo.mFilter = aFilter;
2832 openInfo.mFlags = fFlags;
2833
2834 ComObjPtr <GuestDirectory> pDirectory; int guestRc;
2835 int rc = i_directoryOpenInternal(openInfo, pDirectory, &guestRc);
2836 if (RT_SUCCESS(rc))
2837 {
2838 /* Return directory object to the caller. */
2839 hr = pDirectory.queryInterfaceTo(aDirectory.asOutParam());
2840 }
2841 else
2842 {
2843 switch (rc)
2844 {
2845 case VERR_INVALID_PARAMETER:
2846 hr = setError(VBOX_E_IPRT_ERROR, tr("Opening directory \"%s\" failed; invalid parameters given"),
2847 aPath.c_str());
2848 break;
2849
2850 case VERR_GSTCTL_GUEST_ERROR:
2851 hr = GuestDirectory::i_setErrorExternal(this, guestRc);
2852 break;
2853
2854 default:
2855 hr = setError(VBOX_E_IPRT_ERROR, tr("Opening directory \"%s\" failed: %Rrc"),
2856 aPath.c_str(),rc);
2857 break;
2858 }
2859 }
2860
2861 return hr;
2862}
2863
2864HRESULT GuestSession::directoryRemove(const com::Utf8Str &aPath)
2865{
2866 LogFlowThisFuncEnter();
2867
2868 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
2869 return setError(E_INVALIDARG, tr("No directory to remove specified"));
2870
2871 HRESULT hr = i_isReadyExternal();
2872 if (FAILED(hr))
2873 return hr;
2874
2875 /* No flags; only remove the directory when empty. */
2876 uint32_t uFlags = 0;
2877
2878 int guestRc;
2879 int vrc = i_directoryRemoveInternal(aPath, uFlags, &guestRc);
2880 if (RT_FAILURE(vrc))
2881 {
2882 switch (vrc)
2883 {
2884 case VERR_NOT_SUPPORTED:
2885 hr = setError(VBOX_E_IPRT_ERROR,
2886 tr("Handling removing guest directories not supported by installed Guest Additions"));
2887 break;
2888
2889 case VERR_GSTCTL_GUEST_ERROR:
2890 hr = GuestDirectory::i_setErrorExternal(this, guestRc);
2891 break;
2892
2893 default:
2894 hr = setError(VBOX_E_IPRT_ERROR, tr("Removing guest directory \"%s\" failed: %Rrc"),
2895 aPath.c_str(), vrc);
2896 break;
2897 }
2898 }
2899
2900 return hr;
2901}
2902
2903HRESULT GuestSession::directoryRemoveRecursive(const com::Utf8Str &aPath, const std::vector<DirectoryRemoveRecFlag_T> &aFlags,
2904 ComPtr<IProgress> &aProgress)
2905{
2906 RT_NOREF(aFlags);
2907 LogFlowThisFuncEnter();
2908
2909 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
2910 return setError(E_INVALIDARG, tr("No directory to remove recursively specified"));
2911
2912/** @todo r=bird: Must check that the flags matches the hardcoded behavior
2913 * further down!! */
2914
2915 HRESULT hr = i_isReadyExternal();
2916 if (FAILED(hr))
2917 return hr;
2918
2919 ComObjPtr<Progress> pProgress;
2920 hr = pProgress.createObject();
2921 if (SUCCEEDED(hr))
2922 hr = pProgress->init(static_cast<IGuestSession *>(this),
2923 Bstr(tr("Removing guest directory")).raw(),
2924 TRUE /*aCancelable*/);
2925 if (FAILED(hr))
2926 return hr;
2927
2928 /* Note: At the moment we don't supply progress information while
2929 * deleting a guest directory recursively. So just complete
2930 * the progress object right now. */
2931 /** @todo Implement progress reporting on guest directory deletion! */
2932 hr = pProgress->i_notifyComplete(S_OK);
2933 if (FAILED(hr))
2934 return hr;
2935
2936 /* Remove the directory + all its contents. */
2937 uint32_t uFlags = DIRREMOVE_FLAG_RECURSIVE
2938 | DIRREMOVE_FLAG_CONTENT_AND_DIR;
2939 int guestRc;
2940 int vrc = i_directoryRemoveInternal(aPath, uFlags, &guestRc);
2941 if (RT_FAILURE(vrc))
2942 {
2943 switch (vrc)
2944 {
2945 case VERR_NOT_SUPPORTED:
2946 hr = setError(VBOX_E_IPRT_ERROR,
2947 tr("Handling removing guest directories recursively not supported by installed Guest Additions"));
2948 break;
2949
2950 case VERR_GSTCTL_GUEST_ERROR:
2951 hr = GuestFile::i_setErrorExternal(this, guestRc);
2952 break;
2953
2954 default:
2955 hr = setError(VBOX_E_IPRT_ERROR, tr("Recursively removing guest directory \"%s\" failed: %Rrc"),
2956 aPath.c_str(), vrc);
2957 break;
2958 }
2959 }
2960 else
2961 {
2962 pProgress.queryInterfaceTo(aProgress.asOutParam());
2963 }
2964
2965 return hr;
2966}
2967
2968HRESULT GuestSession::environmentScheduleSet(const com::Utf8Str &aName, const com::Utf8Str &aValue)
2969{
2970 LogFlowThisFuncEnter();
2971
2972 HRESULT hrc;
2973 if (RT_LIKELY(aName.isNotEmpty()))
2974 {
2975 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
2976 {
2977 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2978 int vrc = mData.mEnvironmentChanges.setVariable(aName, aValue);
2979 if (RT_SUCCESS(vrc))
2980 hrc = S_OK;
2981 else
2982 hrc = setErrorVrc(vrc);
2983 }
2984 else
2985 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
2986 }
2987 else
2988 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
2989
2990 LogFlowThisFuncLeave();
2991 return hrc;
2992}
2993
2994HRESULT GuestSession::environmentScheduleUnset(const com::Utf8Str &aName)
2995{
2996 LogFlowThisFuncEnter();
2997 HRESULT hrc;
2998 if (RT_LIKELY(aName.isNotEmpty()))
2999 {
3000 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3001 {
3002 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3003 int vrc = mData.mEnvironmentChanges.unsetVariable(aName);
3004 if (RT_SUCCESS(vrc))
3005 hrc = S_OK;
3006 else
3007 hrc = setErrorVrc(vrc);
3008 }
3009 else
3010 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3011 }
3012 else
3013 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3014
3015 LogFlowThisFuncLeave();
3016 return hrc;
3017}
3018
3019HRESULT GuestSession::environmentGetBaseVariable(const com::Utf8Str &aName, com::Utf8Str &aValue)
3020{
3021 LogFlowThisFuncEnter();
3022 HRESULT hrc;
3023 if (RT_LIKELY(aName.isNotEmpty()))
3024 {
3025 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3026 {
3027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3028 if (mData.mpBaseEnvironment)
3029 {
3030 int vrc = mData.mpBaseEnvironment->getVariable(aName, &aValue);
3031 if (RT_SUCCESS(vrc))
3032 hrc = S_OK;
3033 else
3034 hrc = setErrorVrc(vrc);
3035 }
3036 else if (mData.mProtocolVersion < 99999)
3037 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
3038 else
3039 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
3040 }
3041 else
3042 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3043 }
3044 else
3045 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3046
3047 LogFlowThisFuncLeave();
3048 return hrc;
3049}
3050
3051HRESULT GuestSession::environmentDoesBaseVariableExist(const com::Utf8Str &aName, BOOL *aExists)
3052{
3053 LogFlowThisFuncEnter();
3054 *aExists = FALSE;
3055 HRESULT hrc;
3056 if (RT_LIKELY(aName.isNotEmpty()))
3057 {
3058 if (RT_LIKELY(strchr(aName.c_str(), '=') == NULL))
3059 {
3060 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3061 if (mData.mpBaseEnvironment)
3062 {
3063 hrc = S_OK;
3064 *aExists = mData.mpBaseEnvironment->doesVariableExist(aName);
3065 }
3066 else if (mData.mProtocolVersion < 99999)
3067 hrc = setError(VBOX_E_NOT_SUPPORTED, tr("The base environment feature is not supported by the guest additions"));
3068 else
3069 hrc = setError(VBOX_E_INVALID_OBJECT_STATE, tr("The base environment has not yet been reported by the guest"));
3070 }
3071 else
3072 hrc = setError(E_INVALIDARG, tr("The equal char is not allowed in environment variable names"));
3073 }
3074 else
3075 hrc = setError(E_INVALIDARG, tr("No variable name specified"));
3076
3077 LogFlowThisFuncLeave();
3078 return hrc;
3079}
3080
3081HRESULT GuestSession::fileCreateTemp(const com::Utf8Str &aTemplateName, ULONG aMode, const com::Utf8Str &aPath, BOOL aSecure,
3082 ComPtr<IGuestFile> &aFile)
3083{
3084 RT_NOREF(aTemplateName, aMode, aPath, aSecure, aFile);
3085 ReturnComNotImplemented();
3086}
3087
3088HRESULT GuestSession::fileExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3089{
3090 LogFlowThisFuncEnter();
3091
3092/** @todo r=bird: Treat empty file with a FALSE return. */
3093 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3094 return setError(E_INVALIDARG, tr("No file to check existence for specified"));
3095
3096 GuestFsObjData objData; int guestRc;
3097 int vrc = i_fileQueryInfoInternal(aPath, aFollowSymlinks != FALSE, objData, &guestRc);
3098 if (RT_SUCCESS(vrc))
3099 {
3100 *aExists = TRUE;
3101 return S_OK;
3102 }
3103
3104 HRESULT hr = S_OK;
3105
3106 switch (vrc)
3107 {
3108 case VERR_GSTCTL_GUEST_ERROR:
3109 hr = GuestProcess::i_setErrorExternal(this, guestRc);
3110 break;
3111
3112/** @todo r=bird: what about VERR_PATH_NOT_FOUND and VERR_FILE_NOT_FOUND?
3113 * Where does that get converted to *aExists = FALSE? */
3114 case VERR_NOT_A_FILE:
3115 *aExists = FALSE;
3116 break;
3117
3118 default:
3119 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying file information for \"%s\" failed: %Rrc"),
3120 aPath.c_str(), vrc);
3121 break;
3122 }
3123
3124 return hr;
3125}
3126
3127HRESULT GuestSession::fileOpen(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3128 ULONG aCreationMode, ComPtr<IGuestFile> &aFile)
3129{
3130 LogFlowThisFuncEnter();
3131 const std::vector<FileOpenExFlags_T> EmptyFlags;
3132 return fileOpenEx(aPath, aAccessMode, aOpenAction, FileSharingMode_All, aCreationMode, EmptyFlags, aFile);
3133}
3134
3135HRESULT GuestSession::fileOpenEx(const com::Utf8Str &aPath, FileAccessMode_T aAccessMode, FileOpenAction_T aOpenAction,
3136 FileSharingMode_T aSharingMode, ULONG aCreationMode,
3137 const std::vector<FileOpenExFlags_T> &aFlags, ComPtr<IGuestFile> &aFile)
3138{
3139 LogFlowThisFuncEnter();
3140
3141 if (RT_UNLIKELY((aPath.c_str()) == NULL || *(aPath.c_str()) == '\0'))
3142 return setError(E_INVALIDARG, tr("No file to open specified"));
3143
3144 HRESULT hr = i_isReadyExternal();
3145 if (FAILED(hr))
3146 return hr;
3147
3148 GuestFileOpenInfo openInfo;
3149 openInfo.mFileName = aPath;
3150 openInfo.mCreationMode = aCreationMode;
3151
3152 /* convert + validate aAccessMode to the old format. */
3153 openInfo.mAccessMode = aAccessMode;
3154 switch (aAccessMode)
3155 {
3156 case (FileAccessMode_T)FileAccessMode_ReadOnly: openInfo.mpszAccessMode = "r"; break;
3157 case (FileAccessMode_T)FileAccessMode_WriteOnly: openInfo.mpszAccessMode = "w"; break;
3158 case (FileAccessMode_T)FileAccessMode_ReadWrite: openInfo.mpszAccessMode = "r+"; break;
3159 case (FileAccessMode_T)FileAccessMode_AppendOnly:
3160 RT_FALL_THRU();
3161 case (FileAccessMode_T)FileAccessMode_AppendRead:
3162 return setError(E_NOTIMPL, tr("Append access modes are not yet implemented"));
3163 default:
3164 return setError(E_INVALIDARG, tr("Unknown FileAccessMode value %u (%#x)"), aAccessMode, aAccessMode);
3165 }
3166
3167 /* convert + validate aOpenAction to the old format. */
3168 openInfo.mOpenAction = aOpenAction;
3169 switch (aOpenAction)
3170 {
3171 case (FileOpenAction_T)FileOpenAction_OpenExisting: openInfo.mpszOpenAction = "oe"; break;
3172 case (FileOpenAction_T)FileOpenAction_OpenOrCreate: openInfo.mpszOpenAction = "oc"; break;
3173 case (FileOpenAction_T)FileOpenAction_CreateNew: openInfo.mpszOpenAction = "ce"; break;
3174 case (FileOpenAction_T)FileOpenAction_CreateOrReplace: openInfo.mpszOpenAction = "ca"; break;
3175 case (FileOpenAction_T)FileOpenAction_OpenExistingTruncated: openInfo.mpszOpenAction = "ot"; break;
3176 case (FileOpenAction_T)FileOpenAction_AppendOrCreate:
3177 openInfo.mpszOpenAction = "oa"; /** @todo get rid of this one and implement AppendOnly/AppendRead. */
3178 break;
3179 default:
3180 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3181 }
3182
3183 /* validate aSharingMode */
3184 openInfo.mSharingMode = aSharingMode;
3185 switch (aSharingMode)
3186 {
3187 case (FileSharingMode_T)FileSharingMode_All: /* OK */ break;
3188 case (FileSharingMode_T)FileSharingMode_Read:
3189 case (FileSharingMode_T)FileSharingMode_Write:
3190 case (FileSharingMode_T)FileSharingMode_ReadWrite:
3191 case (FileSharingMode_T)FileSharingMode_Delete:
3192 case (FileSharingMode_T)FileSharingMode_ReadDelete:
3193 case (FileSharingMode_T)FileSharingMode_WriteDelete:
3194 return setError(E_NOTIMPL, tr("Only FileSharingMode_All is currently implemented"));
3195
3196 default:
3197 return setError(E_INVALIDARG, tr("Unknown FileOpenAction value %u (%#x)"), aAccessMode, aAccessMode);
3198 }
3199
3200 /* Combine and validate flags. */
3201 uint32_t fOpenEx = 0;
3202 for (size_t i = 0; i < aFlags.size(); i++)
3203 fOpenEx = aFlags[i];
3204 if (fOpenEx)
3205 return setError(E_INVALIDARG, tr("Unsupported FileOpenExFlags values in aFlags (%#x)"), fOpenEx);
3206 openInfo.mfOpenEx = fOpenEx;
3207
3208 ComObjPtr <GuestFile> pFile;
3209 int guestRc;
3210 int vrc = i_fileOpenInternal(openInfo, pFile, &guestRc);
3211 if (RT_SUCCESS(vrc))
3212 /* Return directory object to the caller. */
3213 hr = pFile.queryInterfaceTo(aFile.asOutParam());
3214 else
3215 {
3216 switch (vrc)
3217 {
3218 case VERR_NOT_SUPPORTED:
3219 hr = setError(VBOX_E_IPRT_ERROR,
3220 tr("Handling guest files not supported by installed Guest Additions"));
3221 break;
3222
3223 case VERR_GSTCTL_GUEST_ERROR:
3224 hr = GuestFile::i_setErrorExternal(this, guestRc);
3225 break;
3226
3227 default:
3228 hr = setError(VBOX_E_IPRT_ERROR, tr("Opening guest file \"%s\" failed: %Rrc"),
3229 aPath.c_str(), vrc);
3230 break;
3231 }
3232 }
3233
3234 return hr;
3235}
3236
3237HRESULT GuestSession::fileQuerySize(const com::Utf8Str &aPath, BOOL aFollowSymlinks, LONG64 *aSize)
3238{
3239 if (aPath.isEmpty())
3240 return setError(E_INVALIDARG, tr("No path specified"));
3241
3242 HRESULT hr = S_OK;
3243
3244 int64_t llSize; int guestRc;
3245 int vrc = i_fileQuerySizeInternal(aPath, aFollowSymlinks != FALSE, &llSize, &guestRc);
3246 if (RT_SUCCESS(vrc))
3247 {
3248 *aSize = llSize;
3249 }
3250 else
3251 {
3252 if (GuestProcess::i_isGuestError(vrc))
3253 {
3254 hr = GuestProcess::i_setErrorExternal(this, guestRc);
3255 }
3256 else
3257 hr = setError(VBOX_E_IPRT_ERROR, tr("Querying file size failed: %Rrc"), vrc);
3258 }
3259
3260 return hr;
3261}
3262
3263HRESULT GuestSession::fsObjExists(const com::Utf8Str &aPath, BOOL aFollowSymlinks, BOOL *aExists)
3264{
3265 if (aPath.isEmpty())
3266 return setError(E_INVALIDARG, tr("No path specified"));
3267
3268 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
3269
3270 HRESULT hrc = S_OK;
3271
3272 *aExists = false;
3273
3274 GuestFsObjData objData;
3275 int rcGuest;
3276 int vrc = i_fsQueryInfoInternal(aPath, aFollowSymlinks != FALSE, objData, &rcGuest);
3277 if (RT_SUCCESS(vrc))
3278 {
3279 *aExists = TRUE;
3280 }
3281 else
3282 {
3283 if (GuestProcess::i_isGuestError(vrc))
3284 {
3285 if ( rcGuest == VERR_NOT_A_FILE
3286 || rcGuest == VERR_PATH_NOT_FOUND
3287 || rcGuest == VERR_FILE_NOT_FOUND
3288 || rcGuest == VERR_INVALID_NAME)
3289 {
3290 hrc = S_OK; /* Ignore these vrc values. */
3291 }
3292 else
3293 hrc = GuestProcess::i_setErrorExternal(this, rcGuest);
3294 }
3295 else
3296 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3297 }
3298
3299 return hrc;
3300}
3301
3302HRESULT GuestSession::fsObjQueryInfo(const com::Utf8Str &aPath, BOOL aFollowSymlinks, ComPtr<IGuestFsObjInfo> &aInfo)
3303{
3304 if (aPath.isEmpty())
3305 return setError(E_INVALIDARG, tr("No path specified"));
3306
3307 LogFlowThisFunc(("aPath=%s, aFollowSymlinks=%RTbool\n", aPath.c_str(), RT_BOOL(aFollowSymlinks)));
3308
3309 HRESULT hrc = S_OK;
3310
3311 GuestFsObjData Info; int guestRc;
3312 int vrc = i_fsQueryInfoInternal(aPath, aFollowSymlinks != FALSE, Info, &guestRc);
3313 if (RT_SUCCESS(vrc))
3314 {
3315 ComObjPtr<GuestFsObjInfo> ptrFsObjInfo;
3316 hrc = ptrFsObjInfo.createObject();
3317 if (SUCCEEDED(hrc))
3318 {
3319 vrc = ptrFsObjInfo->init(Info);
3320 if (RT_SUCCESS(vrc))
3321 hrc = ptrFsObjInfo.queryInterfaceTo(aInfo.asOutParam());
3322 else
3323 hrc = setErrorVrc(vrc);
3324 }
3325 }
3326 else
3327 {
3328 if (GuestProcess::i_isGuestError(vrc))
3329 {
3330 hrc = GuestProcess::i_setErrorExternal(this, guestRc);
3331 }
3332 else
3333 hrc = setErrorVrc(vrc, tr("Querying file information for \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3334 }
3335
3336 return hrc;
3337}
3338
3339HRESULT GuestSession::fsObjRemove(const com::Utf8Str &aPath)
3340{
3341 if (RT_UNLIKELY(aPath.isEmpty()))
3342 return setError(E_INVALIDARG, tr("No path specified"));
3343
3344 LogFlowThisFunc(("aPath=%s\n", aPath.c_str()));
3345
3346 HRESULT hrc = S_OK;
3347
3348 int guestRc;
3349 int vrc = i_fileRemoveInternal(aPath, &guestRc);
3350 if (RT_FAILURE(vrc))
3351 {
3352 if (GuestProcess::i_isGuestError(vrc))
3353 {
3354 hrc = GuestProcess::i_setErrorExternal(this, guestRc);
3355 }
3356 else
3357 hrc = setError(VBOX_E_IPRT_ERROR, tr("Removing file \"%s\" failed: %Rrc"), aPath.c_str(), vrc);
3358 }
3359
3360 return hrc;
3361}
3362
3363HRESULT GuestSession::fsObjRename(const com::Utf8Str &aSource,
3364 const com::Utf8Str &aDestination,
3365 const std::vector<FsObjRenameFlag_T> &aFlags)
3366{
3367 if (RT_UNLIKELY(aSource.isEmpty()))
3368 return setError(E_INVALIDARG, tr("No source path specified"));
3369
3370 if (RT_UNLIKELY(aDestination.isEmpty()))
3371 return setError(E_INVALIDARG, tr("No destination path specified"));
3372
3373 LogFlowThisFunc(("aSource=%s, aDestination=%s\n", aSource.c_str(), aDestination.c_str()));
3374
3375 HRESULT hr = i_isReadyExternal();
3376 if (FAILED(hr))
3377 return hr;
3378
3379 /* Combine, validate and convert flags. */
3380 uint32_t fApiFlags = 0;
3381 for (size_t i = 0; i < aFlags.size(); i++)
3382 fApiFlags |= aFlags[i];
3383 if (fApiFlags & ~((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace))
3384 return setError(E_INVALIDARG, tr("Unknown rename flag: %#x"), fApiFlags);
3385
3386 AssertCompile(FsObjRenameFlag_NoReplace == 0);
3387 AssertCompile(FsObjRenameFlag_Replace != 0);
3388 uint32_t fBackend;
3389 if ((fApiFlags & ((uint32_t)FsObjRenameFlag_NoReplace | (uint32_t)FsObjRenameFlag_Replace)) == FsObjRenameFlag_Replace)
3390 fBackend = PATHRENAME_FLAG_REPLACE;
3391 else
3392 fBackend = PATHRENAME_FLAG_NO_REPLACE;
3393
3394 /* Call worker to do the job. */
3395 int guestRc;
3396 int vrc = i_pathRenameInternal(aSource, aDestination, fBackend, &guestRc);
3397 if (RT_FAILURE(vrc))
3398 {
3399 switch (vrc)
3400 {
3401 case VERR_NOT_SUPPORTED:
3402 hr = setError(VBOX_E_IPRT_ERROR,
3403 tr("Handling renaming guest directories not supported by installed Guest Additions"));
3404 break;
3405
3406 case VERR_GSTCTL_GUEST_ERROR:
3407 hr = setError(VBOX_E_IPRT_ERROR,
3408 tr("Renaming guest directory failed: %Rrc"), guestRc);
3409 break;
3410
3411 default:
3412 hr = setError(VBOX_E_IPRT_ERROR, tr("Renaming guest directory \"%s\" failed: %Rrc"),
3413 aSource.c_str(), vrc);
3414 break;
3415 }
3416 }
3417
3418 return hr;
3419}
3420
3421HRESULT GuestSession::fsObjMove(const com::Utf8Str &aSource, const com::Utf8Str &aDestination,
3422 const std::vector<FsObjMoveFlags_T> &aFlags, ComPtr<IProgress> &aProgress)
3423{
3424 RT_NOREF(aSource, aDestination, aFlags, aProgress);
3425 ReturnComNotImplemented();
3426}
3427
3428HRESULT GuestSession::fsObjSetACL(const com::Utf8Str &aPath, BOOL aFollowSymlinks, const com::Utf8Str &aAcl, ULONG aMode)
3429{
3430 RT_NOREF(aPath, aFollowSymlinks, aAcl, aMode);
3431 ReturnComNotImplemented();
3432}
3433
3434
3435HRESULT GuestSession::processCreate(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
3436 const std::vector<com::Utf8Str> &aEnvironment,
3437 const std::vector<ProcessCreateFlag_T> &aFlags,
3438 ULONG aTimeoutMS, ComPtr<IGuestProcess> &aGuestProcess)
3439{
3440 LogFlowThisFuncEnter();
3441
3442 std::vector<LONG> affinityIgnored;
3443
3444 return processCreateEx(aExecutable, aArguments, aEnvironment, aFlags, aTimeoutMS, ProcessPriority_Default,
3445 affinityIgnored, aGuestProcess);
3446}
3447
3448HRESULT GuestSession::processCreateEx(const com::Utf8Str &aExecutable, const std::vector<com::Utf8Str> &aArguments,
3449 const std::vector<com::Utf8Str> &aEnvironment,
3450 const std::vector<ProcessCreateFlag_T> &aFlags, ULONG aTimeoutMS,
3451 ProcessPriority_T aPriority, const std::vector<LONG> &aAffinity,
3452 ComPtr<IGuestProcess> &aGuestProcess)
3453{
3454 LogFlowThisFuncEnter();
3455
3456 /** @todo r=bird: Check input better? aPriority is passed on to the guest
3457 * without any validation. Flags not existing in this vbox version are
3458 * ignored, potentially doing something entirely different than what the
3459 * caller had in mind. */
3460
3461 /*
3462 * Must have an executable to execute. If none is given, we try use the
3463 * zero'th argument.
3464 */
3465 const char *pszExecutable = aExecutable.c_str();
3466 if (RT_UNLIKELY(pszExecutable == NULL || *pszExecutable == '\0'))
3467 {
3468 if (aArguments.size() > 0)
3469 pszExecutable = aArguments[0].c_str();
3470 if (pszExecutable == NULL || *pszExecutable == '\0')
3471 return setError(E_INVALIDARG, tr("No command to execute specified"));
3472 }
3473
3474 /*
3475 * Check the session.
3476 */
3477 HRESULT hr = i_isReadyExternal();
3478 if (FAILED(hr))
3479 return hr;
3480
3481 /*
3482 * Build the process startup info.
3483 */
3484 GuestProcessStartupInfo procInfo;
3485
3486 /* Executable and arguments. */
3487 procInfo.mExecutable = pszExecutable;
3488 if (aArguments.size())
3489 for (size_t i = 0; i < aArguments.size(); i++)
3490 procInfo.mArguments.push_back(aArguments[i]);
3491
3492 /* Combine the environment changes associated with the ones passed in by
3493 the caller, giving priority to the latter. The changes are putenv style
3494 and will be applied to the standard environment for the guest user. */
3495 int vrc = procInfo.mEnvironmentChanges.copy(mData.mEnvironmentChanges);
3496 if (RT_SUCCESS(vrc))
3497 vrc = procInfo.mEnvironmentChanges.applyPutEnvArray(aEnvironment);
3498 if (RT_SUCCESS(vrc))
3499 {
3500 /* Convert the flag array into a mask. */
3501 if (aFlags.size())
3502 for (size_t i = 0; i < aFlags.size(); i++)
3503 procInfo.mFlags |= aFlags[i];
3504
3505 procInfo.mTimeoutMS = aTimeoutMS;
3506
3507 /** @todo use RTCPUSET instead of archaic 64-bit variables! */
3508 if (aAffinity.size())
3509 for (size_t i = 0; i < aAffinity.size(); i++)
3510 if (aAffinity[i])
3511 procInfo.mAffinity |= (uint64_t)1 << i;
3512
3513 procInfo.mPriority = aPriority;
3514
3515 /*
3516 * Create a guest process object.
3517 */
3518 ComObjPtr<GuestProcess> pProcess;
3519 vrc = i_processCreateExInternal(procInfo, pProcess);
3520 if (RT_SUCCESS(vrc))
3521 {
3522 ComPtr<IGuestProcess> pIProcess;
3523 hr = pProcess.queryInterfaceTo(pIProcess.asOutParam());
3524 if (SUCCEEDED(hr))
3525 {
3526 /*
3527 * Start the process.
3528 */
3529 vrc = pProcess->i_startProcessAsync();
3530 if (RT_SUCCESS(vrc))
3531 {
3532 aGuestProcess = pIProcess;
3533
3534 LogFlowFuncLeaveRC(vrc);
3535 return S_OK;
3536 }
3537
3538 hr = setErrorVrc(vrc, tr("Failed to start guest process: %Rrc"), vrc);
3539 }
3540 }
3541 else if (vrc == VERR_MAX_PROCS_REACHED)
3542 hr = setErrorVrc(vrc, tr("Maximum number of concurrent guest processes per session (%u) reached"),
3543 VBOX_GUESTCTRL_MAX_OBJECTS);
3544 else
3545 hr = setErrorVrc(vrc, tr("Failed to create guest process object: %Rrc"), vrc);
3546 }
3547 else
3548 hr = setErrorVrc(vrc, tr("Failed to set up the environment: %Rrc"), vrc);
3549
3550 LogFlowFuncLeaveRC(vrc);
3551 return hr;
3552}
3553
3554HRESULT GuestSession::processGet(ULONG aPid, ComPtr<IGuestProcess> &aGuestProcess)
3555
3556{
3557 LogFlowThisFunc(("PID=%RU32\n", aPid));
3558
3559 if (aPid == 0)
3560 return setError(E_INVALIDARG, tr("No valid process ID (PID) specified"));
3561
3562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3563
3564 HRESULT hr = S_OK;
3565
3566 ComObjPtr<GuestProcess> pProcess;
3567 int rc = i_processGetByPID(aPid, &pProcess);
3568 if (RT_FAILURE(rc))
3569 hr = setError(E_INVALIDARG, tr("No process with PID %RU32 found"), aPid);
3570
3571 /* This will set (*aProcess) to NULL if pProgress is NULL. */
3572 HRESULT hr2 = pProcess.queryInterfaceTo(aGuestProcess.asOutParam());
3573 if (SUCCEEDED(hr))
3574 hr = hr2;
3575
3576 LogFlowThisFunc(("aProcess=%p, hr=%Rhrc\n", (IGuestProcess*)aGuestProcess, hr));
3577 return hr;
3578}
3579
3580HRESULT GuestSession::symlinkCreate(const com::Utf8Str &aSource, const com::Utf8Str &aTarget, SymlinkType_T aType)
3581{
3582 RT_NOREF(aSource, aTarget, aType);
3583 ReturnComNotImplemented();
3584}
3585
3586HRESULT GuestSession::symlinkExists(const com::Utf8Str &aSymlink, BOOL *aExists)
3587
3588{
3589 RT_NOREF(aSymlink, aExists);
3590 ReturnComNotImplemented();
3591}
3592
3593HRESULT GuestSession::symlinkRead(const com::Utf8Str &aSymlink, const std::vector<SymlinkReadFlag_T> &aFlags,
3594 com::Utf8Str &aTarget)
3595{
3596 RT_NOREF(aSymlink, aFlags, aTarget);
3597 ReturnComNotImplemented();
3598}
3599
3600HRESULT GuestSession::waitFor(ULONG aWaitFor, ULONG aTimeoutMS, GuestSessionWaitResult_T *aReason)
3601{
3602 LogFlowThisFuncEnter();
3603
3604 /*
3605 * Note: Do not hold any locks here while waiting!
3606 */
3607 HRESULT hr = S_OK;
3608
3609 int guestRc; GuestSessionWaitResult_T waitResult;
3610 int vrc = i_waitFor(aWaitFor, aTimeoutMS, waitResult, &guestRc);
3611 if (RT_SUCCESS(vrc))
3612 *aReason = waitResult;
3613 else
3614 {
3615 switch (vrc)
3616 {
3617 case VERR_GSTCTL_GUEST_ERROR:
3618 hr = GuestSession::i_setErrorExternal(this, guestRc);
3619 break;
3620
3621 case VERR_TIMEOUT:
3622 *aReason = GuestSessionWaitResult_Timeout;
3623 break;
3624
3625 default:
3626 {
3627 const char *pszSessionName = mData.mSession.mName.c_str();
3628 hr = setError(VBOX_E_IPRT_ERROR,
3629 tr("Waiting for guest session \"%s\" failed: %Rrc"),
3630 pszSessionName ? pszSessionName : tr("Unnamed"), vrc);
3631 break;
3632 }
3633 }
3634 }
3635
3636 LogFlowFuncLeaveRC(vrc);
3637 return hr;
3638}
3639
3640HRESULT GuestSession::waitForArray(const std::vector<GuestSessionWaitForFlag_T> &aWaitFor, ULONG aTimeoutMS,
3641 GuestSessionWaitResult_T *aReason)
3642{
3643 LogFlowThisFuncEnter();
3644
3645 /*
3646 * Note: Do not hold any locks here while waiting!
3647 */
3648 uint32_t fWaitFor = GuestSessionWaitForFlag_None;
3649 for (size_t i = 0; i < aWaitFor.size(); i++)
3650 fWaitFor |= aWaitFor[i];
3651
3652 return WaitFor(fWaitFor, aTimeoutMS, aReason);
3653}
3654
Note: See TracBrowser for help on using the repository browser.

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