VirtualBox

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

Last change on this file since 63156 was 63156, checked in by vboxsync, 9 years ago

GuestSession::i_objectCreateTempInternal,GuestSession::i_fsQueryInfoInternal: guestRc _was_ used uninitialized and pGuestRc not set when returning VERR_GSTCTL_GUEST_ERROR (local variable was updated instead). Changed procInfo initialization to return immediately when out of memory, as it makes the code flow unnecessarily complicated to do the single return point thing. The mExecutable was not inside the try/catch, as it need to be as it allocates memory too.

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

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