VirtualBox

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

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

Modified IGuestSession::fileOpenEx: removed the 'offset' parameter and added generic flags parameter so we can easily add features later when we need them (see RTFILE_O_XXX for inspiration). Changed FileSeekOrigin_Set to FileSeekOrigin_Begin.

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