VirtualBox

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

Last change on this file since 55590 was 55590, checked in by vboxsync, 10 years ago

Main: Removed IGuestSession::environmentClear because it duplicates the writable IGuestSession::environment attribute. Removed IGuestSession::environmentGet because it's not capable of returning empty value and variables scheduled for unsetting, and it could very easily mislead anyone into thinking you can use it to get useful thing like 'windir' and 'TEMP' from the guest.

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