VirtualBox

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

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

IGuestSession: Added a pathStyle attribute (read-only) that translates the OS type reported by the guest additions into a DOS, UNIX or Unknown path styles values. Added two methods fsExists and fsQueryInfo that aren't as narrow minded as fileQueryInfo and directoryQueryInfo and has parameters for how to treat symbolic links.

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

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