VirtualBox

source: vbox/trunk/src/VBox/Main/GuestImpl.cpp@ 29445

Last change on this file since 29445 was 29309, checked in by vboxsync, 15 years ago

Wording

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 45.5 KB
Line 
1/* $Id: GuestImpl.cpp 29309 2010-05-10 15:44:55Z vboxsync $ */
2
3/** @file
4 *
5 * VirtualBox COM class implementation
6 */
7
8/*
9 * Copyright (C) 2006-2008 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20#include "GuestImpl.h"
21
22#include "Global.h"
23#include "ConsoleImpl.h"
24#include "ProgressImpl.h"
25#include "VMMDev.h"
26
27#include "AutoCaller.h"
28#include "Logging.h"
29
30#include <VBox/VMMDev.h>
31#ifdef VBOX_WITH_GUEST_CONTROL
32# include <VBox/com/array.h>
33#endif
34#include <iprt/cpp/utils.h>
35#include <iprt/getopt.h>
36#include <VBox/pgm.h>
37
38// defines
39/////////////////////////////////////////////////////////////////////////////
40
41// constructor / destructor
42/////////////////////////////////////////////////////////////////////////////
43
44DEFINE_EMPTY_CTOR_DTOR (Guest)
45
46HRESULT Guest::FinalConstruct()
47{
48 return S_OK;
49}
50
51void Guest::FinalRelease()
52{
53 uninit ();
54}
55
56// public methods only for internal purposes
57/////////////////////////////////////////////////////////////////////////////
58
59/**
60 * Initializes the guest object.
61 */
62HRESULT Guest::init (Console *aParent)
63{
64 LogFlowThisFunc(("aParent=%p\n", aParent));
65
66 ComAssertRet(aParent, E_INVALIDARG);
67
68 /* Enclose the state transition NotReady->InInit->Ready */
69 AutoInitSpan autoInitSpan(this);
70 AssertReturn(autoInitSpan.isOk(), E_FAIL);
71
72 unconst(mParent) = aParent;
73
74 /* mData.mAdditionsActive is FALSE */
75
76 /* Confirm a successful initialization when it's the case */
77 autoInitSpan.setSucceeded();
78
79 ULONG aMemoryBalloonSize;
80 HRESULT ret = mParent->machine()->COMGETTER(MemoryBalloonSize)(&aMemoryBalloonSize);
81 if (ret == S_OK)
82 mMemoryBalloonSize = aMemoryBalloonSize;
83 else
84 mMemoryBalloonSize = 0; /* Default is no ballooning */
85
86 mStatUpdateInterval = 0; /* Default is not to report guest statistics at all */
87
88 /* Clear statistics. */
89 for (unsigned i = 0 ; i < GUESTSTATTYPE_MAX; i++)
90 mCurrentGuestStat[i] = 0;
91
92#ifdef VBOX_WITH_GUEST_CONTROL
93 /* Init the context ID counter at 1000. */
94 mNextContextID = 1000;
95#endif
96
97 return S_OK;
98}
99
100/**
101 * Uninitializes the instance and sets the ready flag to FALSE.
102 * Called either from FinalRelease() or by the parent when it gets destroyed.
103 */
104void Guest::uninit()
105{
106 LogFlowThisFunc(("\n"));
107
108#ifdef VBOX_WITH_GUEST_CONTROL
109 /*
110 * Cleanup must be done *before* AutoUninitSpan to cancel all
111 * all outstanding waits in API functions (which hold AutoCaller
112 * ref counts).
113 */
114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
115
116 /* Clean up callback data. */
117 CallbackListIter it;
118 for (it = mCallbackList.begin(); it != mCallbackList.end(); it++)
119 destroyCtrlCallbackContext(it);
120
121 /* Clear process list. */
122 mGuestProcessList.clear();
123#endif
124
125 /* Enclose the state transition Ready->InUninit->NotReady */
126 AutoUninitSpan autoUninitSpan(this);
127 if (autoUninitSpan.uninitDone())
128 return;
129
130 unconst(mParent) = NULL;
131}
132
133// IGuest properties
134/////////////////////////////////////////////////////////////////////////////
135
136STDMETHODIMP Guest::COMGETTER(OSTypeId) (BSTR *aOSTypeId)
137{
138 CheckComArgOutPointerValid(aOSTypeId);
139
140 AutoCaller autoCaller(this);
141 if (FAILED(autoCaller.rc())) return autoCaller.rc();
142
143 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
144
145 // redirect the call to IMachine if no additions are installed
146 if (mData.mAdditionsVersion.isEmpty())
147 return mParent->machine()->COMGETTER(OSTypeId)(aOSTypeId);
148
149 mData.mOSTypeId.cloneTo(aOSTypeId);
150
151 return S_OK;
152}
153
154STDMETHODIMP Guest::COMGETTER(AdditionsActive) (BOOL *aAdditionsActive)
155{
156 CheckComArgOutPointerValid(aAdditionsActive);
157
158 AutoCaller autoCaller(this);
159 if (FAILED(autoCaller.rc())) return autoCaller.rc();
160
161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
162
163 *aAdditionsActive = mData.mAdditionsActive;
164
165 return S_OK;
166}
167
168STDMETHODIMP Guest::COMGETTER(AdditionsVersion) (BSTR *aAdditionsVersion)
169{
170 CheckComArgOutPointerValid(aAdditionsVersion);
171
172 AutoCaller autoCaller(this);
173 if (FAILED(autoCaller.rc())) return autoCaller.rc();
174
175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
176
177 mData.mAdditionsVersion.cloneTo(aAdditionsVersion);
178
179 return S_OK;
180}
181
182STDMETHODIMP Guest::COMGETTER(SupportsSeamless) (BOOL *aSupportsSeamless)
183{
184 CheckComArgOutPointerValid(aSupportsSeamless);
185
186 AutoCaller autoCaller(this);
187 if (FAILED(autoCaller.rc())) return autoCaller.rc();
188
189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
190
191 *aSupportsSeamless = mData.mSupportsSeamless;
192
193 return S_OK;
194}
195
196STDMETHODIMP Guest::COMGETTER(SupportsGraphics) (BOOL *aSupportsGraphics)
197{
198 CheckComArgOutPointerValid(aSupportsGraphics);
199
200 AutoCaller autoCaller(this);
201 if (FAILED(autoCaller.rc())) return autoCaller.rc();
202
203 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
204
205 *aSupportsGraphics = mData.mSupportsGraphics;
206
207 return S_OK;
208}
209
210STDMETHODIMP Guest::COMGETTER(PageFusionEnabled) (BOOL *enabled)
211{
212 return E_NOTIMPL;
213}
214
215STDMETHODIMP Guest::COMSETTER(PageFusionEnabled) (BOOL enabled)
216{
217 return E_NOTIMPL;
218}
219
220STDMETHODIMP Guest::COMGETTER(MemoryBalloonSize) (ULONG *aMemoryBalloonSize)
221{
222 CheckComArgOutPointerValid(aMemoryBalloonSize);
223
224 AutoCaller autoCaller(this);
225 if (FAILED(autoCaller.rc())) return autoCaller.rc();
226
227 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
228
229 *aMemoryBalloonSize = mMemoryBalloonSize;
230
231 return S_OK;
232}
233
234STDMETHODIMP Guest::COMSETTER(MemoryBalloonSize) (ULONG aMemoryBalloonSize)
235{
236 AutoCaller autoCaller(this);
237 if (FAILED(autoCaller.rc())) return autoCaller.rc();
238
239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
240
241 /* We must be 100% sure that IMachine::COMSETTER(MemoryBalloonSize)
242 * does not call us back in any way! */
243 HRESULT ret = mParent->machine()->COMSETTER(MemoryBalloonSize)(aMemoryBalloonSize);
244 if (ret == S_OK)
245 {
246 mMemoryBalloonSize = aMemoryBalloonSize;
247 /* forward the information to the VMM device */
248 VMMDev *vmmDev = mParent->getVMMDev();
249 if (vmmDev)
250 vmmDev->getVMMDevPort()->pfnSetMemoryBalloon(vmmDev->getVMMDevPort(), aMemoryBalloonSize);
251 }
252
253 return ret;
254}
255
256STDMETHODIMP Guest::COMGETTER(StatisticsUpdateInterval)(ULONG *aUpdateInterval)
257{
258 CheckComArgOutPointerValid(aUpdateInterval);
259
260 AutoCaller autoCaller(this);
261 if (FAILED(autoCaller.rc())) return autoCaller.rc();
262
263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
264
265 *aUpdateInterval = mStatUpdateInterval;
266 return S_OK;
267}
268
269STDMETHODIMP Guest::COMSETTER(StatisticsUpdateInterval)(ULONG aUpdateInterval)
270{
271 AutoCaller autoCaller(this);
272 if (FAILED(autoCaller.rc())) return autoCaller.rc();
273
274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
275
276 mStatUpdateInterval = aUpdateInterval;
277 /* forward the information to the VMM device */
278 VMMDev *vmmDev = mParent->getVMMDev();
279 if (vmmDev)
280 vmmDev->getVMMDevPort()->pfnSetStatisticsInterval(vmmDev->getVMMDevPort(), aUpdateInterval);
281
282 return S_OK;
283}
284
285STDMETHODIMP Guest::InternalGetStatistics(ULONG *aCpuUser, ULONG *aCpuKernel, ULONG *aCpuIdle,
286 ULONG *aMemTotal, ULONG *aMemFree, ULONG *aMemBalloon, ULONG *aMemShared,
287 ULONG *aMemCache, ULONG *aPageTotal,
288 ULONG *aMemAllocTotal, ULONG *aMemFreeTotal, ULONG *aMemBalloonTotal, ULONG *aMemSharedTotal)
289{
290 CheckComArgOutPointerValid(aCpuUser);
291 CheckComArgOutPointerValid(aCpuKernel);
292 CheckComArgOutPointerValid(aCpuIdle);
293 CheckComArgOutPointerValid(aMemTotal);
294 CheckComArgOutPointerValid(aMemFree);
295 CheckComArgOutPointerValid(aMemBalloon);
296 CheckComArgOutPointerValid(aMemShared);
297 CheckComArgOutPointerValid(aMemCache);
298 CheckComArgOutPointerValid(aPageTotal);
299 CheckComArgOutPointerValid(aMemAllocTotal);
300 CheckComArgOutPointerValid(aMemFreeTotal);
301 CheckComArgOutPointerValid(aMemBalloonTotal);
302 CheckComArgOutPointerValid(aMemSharedTotal);
303
304 AutoCaller autoCaller(this);
305 if (FAILED(autoCaller.rc())) return autoCaller.rc();
306
307 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
308
309 *aCpuUser = mCurrentGuestStat[GUESTSTATTYPE_CPUUSER];
310 *aCpuKernel = mCurrentGuestStat[GUESTSTATTYPE_CPUKERNEL];
311 *aCpuIdle = mCurrentGuestStat[GUESTSTATTYPE_CPUIDLE];
312 *aMemTotal = mCurrentGuestStat[GUESTSTATTYPE_MEMTOTAL] * (_4K/_1K); /* page (4K) -> 1KB units */
313 *aMemFree = mCurrentGuestStat[GUESTSTATTYPE_MEMFREE] * (_4K/_1K); /* page (4K) -> 1KB units */
314 *aMemBalloon = mCurrentGuestStat[GUESTSTATTYPE_MEMBALLOON] * (_4K/_1K); /* page (4K) -> 1KB units */
315 *aMemCache = mCurrentGuestStat[GUESTSTATTYPE_MEMCACHE] * (_4K/_1K); /* page (4K) -> 1KB units */
316 *aPageTotal = mCurrentGuestStat[GUESTSTATTYPE_PAGETOTAL] * (_4K/_1K); /* page (4K) -> 1KB units */
317 *aMemShared = 0; /** todo */
318
319 Console::SafeVMPtr pVM (mParent);
320 if (pVM.isOk())
321 {
322 uint64_t uFreeTotal, uAllocTotal, uBalloonedTotal;
323 *aMemFreeTotal = 0;
324 int rc = PGMR3QueryVMMMemoryStats(pVM.raw(), &uAllocTotal, &uFreeTotal, &uBalloonedTotal);
325 AssertRC(rc);
326 if (rc == VINF_SUCCESS)
327 {
328 *aMemAllocTotal = (ULONG)(uAllocTotal / _1K); /* bytes -> KB */
329 *aMemFreeTotal = (ULONG)(uFreeTotal / _1K);
330 *aMemBalloonTotal = (ULONG)(uBalloonedTotal / _1K);
331 *aMemSharedTotal = 0; /** todo */
332 }
333 }
334 else
335 *aMemFreeTotal = 0;
336
337 return S_OK;
338}
339
340HRESULT Guest::SetStatistic(ULONG aCpuId, GUESTSTATTYPE enmType, ULONG aVal)
341{
342 AutoCaller autoCaller(this);
343 if (FAILED(autoCaller.rc())) return autoCaller.rc();
344
345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
346
347 if (enmType >= GUESTSTATTYPE_MAX)
348 return E_INVALIDARG;
349
350 mCurrentGuestStat[enmType] = aVal;
351 return S_OK;
352}
353
354STDMETHODIMP Guest::SetCredentials(IN_BSTR aUserName, IN_BSTR aPassword,
355 IN_BSTR aDomain, BOOL aAllowInteractiveLogon)
356{
357 AutoCaller autoCaller(this);
358 if (FAILED(autoCaller.rc())) return autoCaller.rc();
359
360 /* forward the information to the VMM device */
361 VMMDev *vmmDev = mParent->getVMMDev();
362 if (vmmDev)
363 {
364 uint32_t u32Flags = VMMDEV_SETCREDENTIALS_GUESTLOGON;
365 if (!aAllowInteractiveLogon)
366 u32Flags = VMMDEV_SETCREDENTIALS_NOLOCALLOGON;
367
368 vmmDev->getVMMDevPort()->pfnSetCredentials(vmmDev->getVMMDevPort(),
369 Utf8Str(aUserName).raw(), Utf8Str(aPassword).raw(),
370 Utf8Str(aDomain).raw(), u32Flags);
371 return S_OK;
372 }
373
374 return setError(VBOX_E_VM_ERROR,
375 tr("VMM device is not available (is the VM running?)"));
376}
377
378#ifdef VBOX_WITH_GUEST_CONTROL
379/**
380 * Appends environment variables to the environment block. Each var=value pair is separated
381 * by NULL (\0) sequence. The whole block will be stored in one blob and disassembled on the
382 * guest side later to fit into the HGCM param structure.
383 *
384 * @returns VBox status code.
385 *
386 * @todo
387 *
388 */
389int Guest::prepareExecuteEnv(const char *pszEnv, void **ppvList, uint32_t *pcbList, uint32_t *pcEnv)
390{
391 int rc = VINF_SUCCESS;
392 uint32_t cbLen = strlen(pszEnv);
393 if (*ppvList)
394 {
395 uint32_t cbNewLen = *pcbList + cbLen + 1; /* Include zero termination. */
396 char *pvTmp = (char*)RTMemRealloc(*ppvList, cbNewLen);
397 if (NULL == pvTmp)
398 {
399 rc = VERR_NO_MEMORY;
400 }
401 else
402 {
403 memcpy(pvTmp + *pcbList, pszEnv, cbLen);
404 pvTmp[cbNewLen - 1] = '\0'; /* Add zero termination. */
405 *ppvList = (void**)pvTmp;
406 }
407 }
408 else
409 {
410 char *pcTmp;
411 if (RTStrAPrintf(&pcTmp, "%s", pszEnv) > 0)
412 {
413 *ppvList = (void**)pcTmp;
414 /* Reset counters. */
415 *pcEnv = 0;
416 *pcbList = 0;
417 }
418 }
419 if (RT_SUCCESS(rc))
420 {
421 *pcbList += cbLen + 1; /* Include zero termination. */
422 *pcEnv += 1; /* Increase env pairs count. */
423 }
424 return rc;
425}
426
427/**
428 * Static callback function for receiving updates on guest control commands
429 * from the guest. Acts as a dispatcher for the actual class instance.
430 *
431 * @returns VBox status code.
432 *
433 * @todo
434 *
435 */
436DECLCALLBACK(int) Guest::doGuestCtrlNotification(void *pvExtension,
437 uint32_t u32Function,
438 void *pvParms,
439 uint32_t cbParms)
440{
441 using namespace guestControl;
442
443 /*
444 * No locking, as this is purely a notification which does not make any
445 * changes to the object state.
446 */
447 LogFlowFunc(("pvExtension = %p, u32Function = %d, pvParms = %p, cbParms = %d\n",
448 pvExtension, u32Function, pvParms, cbParms));
449 ComObjPtr<Guest> pGuest = reinterpret_cast<Guest *>(pvExtension);
450
451 int rc = VINF_SUCCESS;
452 if (u32Function == GUEST_EXEC_SEND_STATUS)
453 {
454 LogFlowFunc(("GUEST_EXEC_SEND_STATUS\n"));
455
456 PHOSTEXECCALLBACKDATA pCBData = reinterpret_cast<PHOSTEXECCALLBACKDATA>(pvParms);
457 AssertPtr(pCBData);
458 AssertReturn(sizeof(HOSTEXECCALLBACKDATA) == cbParms, VERR_INVALID_PARAMETER);
459 AssertReturn(HOSTEXECCALLBACKDATAMAGIC == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
460
461 rc = pGuest->notifyCtrlExec(u32Function, pCBData);
462 }
463 else if (u32Function == GUEST_EXEC_SEND_OUTPUT)
464 {
465 LogFlowFunc(("GUEST_EXEC_SEND_OUTPUT\n"));
466
467 PHOSTEXECOUTCALLBACKDATA pCBData = reinterpret_cast<PHOSTEXECOUTCALLBACKDATA>(pvParms);
468 AssertPtr(pCBData);
469 AssertReturn(sizeof(HOSTEXECOUTCALLBACKDATA) == cbParms, VERR_INVALID_PARAMETER);
470 AssertReturn(HOSTEXECOUTCALLBACKDATAMAGIC == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
471
472 rc = pGuest->notifyCtrlExecOut(u32Function, pCBData);
473 }
474 else
475 rc = VERR_NOT_SUPPORTED;
476 return rc;
477}
478
479/* Function for handling the execution start/termination notification. */
480int Guest::notifyCtrlExec(uint32_t u32Function,
481 PHOSTEXECCALLBACKDATA pData)
482{
483 LogFlowFuncEnter();
484 int rc = VINF_SUCCESS;
485
486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
487
488 AssertPtr(pData);
489 CallbackListIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
490
491 /* Callback can be called several times. */
492 if (it != mCallbackList.end())
493 {
494 PHOSTEXECCALLBACKDATA pCBData = (HOSTEXECCALLBACKDATA*)it->pvData;
495 AssertPtr(pCBData);
496
497 pCBData->u32PID = pData->u32PID;
498 pCBData->u32Status = pData->u32Status;
499 pCBData->u32Flags = pData->u32Flags;
500 /** @todo Copy void* buffer contents! */
501
502 /* Was progress canceled before? */
503 BOOL fCancelled;
504 it->pProgress->COMGETTER(Canceled)(&fCancelled);
505
506 /* Do progress handling. */
507 Utf8Str errMsg;
508 HRESULT rc2 = S_OK;
509 switch (pData->u32Status)
510 {
511 case PROC_STS_STARTED:
512 break;
513
514 case PROC_STS_TEN: /* Terminated normally. */
515 if ( !it->pProgress->getCompleted()
516 && !fCancelled)
517 {
518 it->pProgress->notifyComplete(S_OK);
519 }
520 break;
521
522 case PROC_STS_TEA: /* Terminated abnormally. */
523 errMsg = Utf8StrFmt(Guest::tr("Process terminated abnormally with status '%u'"),
524 pCBData->u32Flags);
525 break;
526
527 case PROC_STS_TES: /* Terminated through signal. */
528 errMsg = Utf8StrFmt(Guest::tr("Process terminated via signal with status '%u'"),
529 pCBData->u32Flags);
530 break;
531
532 case PROC_STS_TOK:
533 errMsg = Utf8StrFmt(Guest::tr("Process timed out and was killed"));
534 break;
535
536 case PROC_STS_TOA:
537 errMsg = Utf8StrFmt(Guest::tr("Process timed out and could not be killed"));
538 break;
539
540 case PROC_STS_DWN:
541 errMsg = Utf8StrFmt(Guest::tr("Process exited because system is shutting down"));
542 break;
543
544 default:
545 break;
546 }
547
548 /* Handle process list. */
549 /** @todo What happens on/deal with PID reuse? */
550 /** @todo How to deal with multiple updates at once? */
551 GuestProcessIter it_proc = getProcessByPID(pCBData->u32PID);
552 if (it_proc == mGuestProcessList.end())
553 {
554 /* Not found, add to list. */
555 GuestProcess p;
556 p.mPID = pCBData->u32PID;
557 p.mStatus = pCBData->u32Status;
558 p.mExitCode = pCBData->u32Flags; /* Contains exit code. */
559 p.mFlags = 0;
560
561 mGuestProcessList.push_back(p);
562 }
563 else /* Update list. */
564 {
565 it_proc->mStatus = pCBData->u32Status;
566 it_proc->mExitCode = pCBData->u32Flags; /* Contains exit code. */
567 it_proc->mFlags = 0;
568 }
569
570 if ( !it->pProgress.isNull()
571 && errMsg.length())
572 {
573 if ( !it->pProgress->getCompleted()
574 && !fCancelled)
575 {
576 it->pProgress->notifyComplete(E_FAIL /** @todo Find a better rc! */, COM_IIDOF(IGuest),
577 (CBSTR)Guest::getComponentName(), errMsg.c_str());
578 LogFlowFunc(("Callback (context ID=%u, status=%u) progress marked as completed\n",
579 pData->hdr.u32ContextID, pData->u32Status));
580 }
581 else
582 LogFlowFunc(("Callback (context ID=%u, status=%u) progress already marked as completed\n",
583 pData->hdr.u32ContextID, pData->u32Status));
584 }
585 ASMAtomicWriteBool(&it->bCalled, true);
586 }
587 else
588 LogFlowFunc(("Unexpected callback (magic=%u, context ID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));
589 LogFlowFuncLeave();
590 return rc;
591}
592
593/* Function for handling the execution output notification. */
594int Guest::notifyCtrlExecOut(uint32_t u32Function,
595 PHOSTEXECOUTCALLBACKDATA pData)
596{
597 LogFlowFuncEnter();
598 int rc = VINF_SUCCESS;
599
600 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
601
602 AssertPtr(pData);
603 CallbackListIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
604 if (it != mCallbackList.end())
605 {
606 Assert(!it->bCalled);
607 PHOSTEXECOUTCALLBACKDATA pCBData = (HOSTEXECOUTCALLBACKDATA*)it->pvData;
608 AssertPtr(pCBData);
609
610 pCBData->u32PID = pData->u32PID;
611 pCBData->u32HandleId = pData->u32HandleId;
612 pCBData->u32Flags = pData->u32Flags;
613
614 /* Make sure we really got something! */
615 if ( pData->cbData
616 && pData->pvData)
617 {
618 /* Allocate data buffer and copy it */
619 pCBData->pvData = RTMemAlloc(pData->cbData);
620 pCBData->cbData = pData->cbData;
621
622 AssertReturn(pCBData->pvData, VERR_NO_MEMORY);
623 memcpy(pCBData->pvData, pData->pvData, pData->cbData);
624 }
625 else
626 {
627 pCBData->pvData = NULL;
628 pCBData->cbData = 0;
629 }
630 ASMAtomicWriteBool(&it->bCalled, true);
631 }
632 else
633 LogFlowFunc(("Unexpected callback (magic=%u, context ID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));
634 LogFlowFuncLeave();
635 return rc;
636}
637
638Guest::CallbackListIter Guest::getCtrlCallbackContextByID(uint32_t u32ContextID)
639{
640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
641
642 /** @todo Maybe use a map instead of list for fast context lookup. */
643 CallbackListIter it;
644 for (it = mCallbackList.begin(); it != mCallbackList.end(); it++)
645 {
646 if (it->mContextID == u32ContextID)
647 return (it);
648 }
649 return it;
650}
651
652Guest::GuestProcessIter Guest::getProcessByPID(uint32_t u32PID)
653{
654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
655
656 /** @todo Maybe use a map instead of list for fast context lookup. */
657 GuestProcessIter it;
658 for (it = mGuestProcessList.begin(); it != mGuestProcessList.end(); it++)
659 {
660 if (it->mPID == u32PID)
661 return (it);
662 }
663 return it;
664}
665
666/* No locking here; */
667void Guest::destroyCtrlCallbackContext(Guest::CallbackListIter it)
668{
669 LogFlowFuncEnter();
670 if (it->pvData)
671 {
672 RTMemFree(it->pvData);
673 it->pvData = NULL;
674 it->cbData = 0;
675
676 /* Notify outstanding waits for progress ... */
677 if (!it->pProgress.isNull())
678 {
679 /* Only cancel if not canceled before! */
680 BOOL fCancelled;
681 if (SUCCEEDED(it->pProgress->COMGETTER(Canceled)(&fCancelled)) && !fCancelled)
682 it->pProgress->Cancel();
683 /*
684 * Do *not NULL pProgress here, because waiting function like executeProcess()
685 * will still rely on this object for checking whether they have to give up!
686 */
687 }
688 }
689 LogFlowFuncLeave();
690}
691
692/* Adds a callback with a user provided data block and an optional progress object
693 * to the callback list. A callback is identified by a unique context ID which is used
694 * to identify a callback from the guest side. */
695uint32_t Guest::addCtrlCallbackContext(eVBoxGuestCtrlCallbackType enmType, void *pvData, uint32_t cbData, Progress *pProgress)
696{
697 LogFlowFuncEnter();
698 uint32_t uNewContext = ASMAtomicIncU32(&mNextContextID);
699 if (uNewContext == UINT32_MAX)
700 ASMAtomicUoWriteU32(&mNextContextID, 1000);
701
702 /** @todo Put this stuff into a constructor! */
703 CallbackContext context;
704 context.mContextID = uNewContext;
705 context.mType = enmType;
706 context.bCalled = false;
707 context.pvData = pvData;
708 context.cbData = cbData;
709 context.pProgress = pProgress;
710
711 uint32_t nCallbacks;
712 {
713 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
714 mCallbackList.push_back(context);
715 nCallbacks = mCallbackList.size();
716 }
717
718#if 0
719 if (nCallbacks > 256) /* Don't let the container size get too big! */
720 {
721 Guest::CallbackListIter it = mCallbackList.begin();
722 destroyCtrlCallbackContext(it);
723 {
724 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
725 mCallbackList.erase(it);
726 }
727 }
728#endif
729
730 LogFlowFuncLeave();
731 return uNewContext;
732}
733#endif /* VBOX_WITH_GUEST_CONTROL */
734
735STDMETHODIMP Guest::ExecuteProcess(IN_BSTR aCommand, ULONG aFlags,
736 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
737 IN_BSTR aStdIn, IN_BSTR aStdOut, IN_BSTR aStdErr,
738 IN_BSTR aUserName, IN_BSTR aPassword,
739 ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress)
740{
741#ifndef VBOX_WITH_GUEST_CONTROL
742 ReturnComNotImplemented();
743#else /* VBOX_WITH_GUEST_CONTROL */
744 using namespace guestControl;
745
746 CheckComArgStrNotEmptyOrNull(aCommand);
747 CheckComArgOutPointerValid(aPID);
748 CheckComArgOutPointerValid(aProgress);
749
750 AutoCaller autoCaller(this);
751 if (FAILED(autoCaller.rc())) return autoCaller.rc();
752
753 if (aFlags != 0) /* Flags are not supported at the moment. */
754 return E_INVALIDARG;
755
756 HRESULT rc = S_OK;
757
758 try
759 {
760 /*
761 * Create progress object.
762 */
763 ComObjPtr <Progress> progress;
764 rc = progress.createObject();
765 if (SUCCEEDED(rc))
766 {
767 rc = progress->init(static_cast<IGuest*>(this),
768 BstrFmt(tr("Executing process")),
769 TRUE);
770 }
771 if (FAILED(rc)) return rc;
772
773 /*
774 * Prepare process execution.
775 */
776 int vrc = VINF_SUCCESS;
777 Utf8Str Utf8Command(aCommand);
778
779 /* Adjust timeout */
780 if (aTimeoutMS == 0)
781 aTimeoutMS = UINT32_MAX;
782
783 /* Prepare arguments. */
784 char **papszArgv = NULL;
785 uint32_t uNumArgs = 0;
786 if (aArguments > 0)
787 {
788 com::SafeArray<IN_BSTR> args(ComSafeArrayInArg(aArguments));
789 uNumArgs = args.size();
790 papszArgv = (char**)RTMemAlloc(sizeof(char*) * (uNumArgs + 1));
791 AssertReturn(papszArgv, E_OUTOFMEMORY);
792 for (unsigned i = 0; RT_SUCCESS(vrc) && i < uNumArgs; i++)
793 vrc = RTStrAPrintf(&papszArgv[i], "%s", Utf8Str(args[i]).raw());
794 papszArgv[uNumArgs] = NULL;
795 }
796
797 Utf8Str Utf8StdIn(aStdIn);
798 Utf8Str Utf8StdOut(aStdOut);
799 Utf8Str Utf8StdErr(aStdErr);
800 Utf8Str Utf8UserName(aUserName);
801 Utf8Str Utf8Password(aPassword);
802 if (RT_SUCCESS(vrc))
803 {
804 uint32_t uContextID = 0;
805
806 char *pszArgs = NULL;
807 if (uNumArgs > 0)
808 vrc = RTGetOptArgvToString(&pszArgs, papszArgv, 0);
809 if (RT_SUCCESS(vrc))
810 {
811 uint32_t cbArgs = pszArgs ? strlen(pszArgs) + 1 : 0; /* Include terminating zero. */
812
813 /* Prepare environment. */
814 void *pvEnv = NULL;
815 uint32_t uNumEnv = 0;
816 uint32_t cbEnv = 0;
817 if (aEnvironment > 0)
818 {
819 com::SafeArray<IN_BSTR> env(ComSafeArrayInArg(aEnvironment));
820
821 for (unsigned i = 0; i < env.size(); i++)
822 {
823 vrc = prepareExecuteEnv(Utf8Str(env[i]).raw(), &pvEnv, &cbEnv, &uNumEnv);
824 if (RT_FAILURE(vrc))
825 break;
826 }
827 }
828
829 if (RT_SUCCESS(vrc))
830 {
831 PHOSTEXECCALLBACKDATA pData = (HOSTEXECCALLBACKDATA*)RTMemAlloc(sizeof(HOSTEXECCALLBACKDATA));
832 AssertReturn(pData, VBOX_E_IPRT_ERROR);
833 uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_START,
834 pData, sizeof(HOSTEXECCALLBACKDATA), progress);
835 Assert(uContextID > 0);
836
837 VBOXHGCMSVCPARM paParms[15];
838 int i = 0;
839 paParms[i++].setUInt32(uContextID);
840 paParms[i++].setPointer((void*)Utf8Command.raw(), (uint32_t)strlen(Utf8Command.raw()) + 1);
841 paParms[i++].setUInt32(aFlags);
842 paParms[i++].setUInt32(uNumArgs);
843 paParms[i++].setPointer((void*)pszArgs, cbArgs);
844 paParms[i++].setUInt32(uNumEnv);
845 paParms[i++].setUInt32(cbEnv);
846 paParms[i++].setPointer((void*)pvEnv, cbEnv);
847 paParms[i++].setPointer((void*)Utf8StdIn.raw(), (uint32_t)strlen(Utf8StdIn.raw()) + 1);
848 paParms[i++].setPointer((void*)Utf8StdOut.raw(), (uint32_t)strlen(Utf8StdOut.raw()) + 1);
849 paParms[i++].setPointer((void*)Utf8StdErr.raw(), (uint32_t)strlen(Utf8StdErr.raw()) + 1);
850 paParms[i++].setPointer((void*)Utf8UserName.raw(), (uint32_t)strlen(Utf8UserName.raw()) + 1);
851 paParms[i++].setPointer((void*)Utf8Password.raw(), (uint32_t)strlen(Utf8Password.raw()) + 1);
852 paParms[i++].setUInt32(aTimeoutMS);
853
854 VMMDev *vmmDev;
855 {
856 /* Make sure mParent is valid, so set the read lock while using.
857 * Do not keep this lock while doing the actual call, because in the meanwhile
858 * another thread could request a write lock which would be a bad idea ... */
859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
860
861 /* Forward the information to the VMM device. */
862 AssertPtr(mParent);
863 vmmDev = mParent->getVMMDev();
864 }
865
866 if (vmmDev)
867 {
868 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
869 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_CMD,
870 i, paParms);
871 }
872 else
873 vrc = VERR_INVALID_VM_HANDLE;
874 RTMemFree(pvEnv);
875 }
876 RTStrFree(pszArgs);
877 }
878 if (RT_SUCCESS(vrc))
879 {
880 LogFlowFunc(("Waiting for HGCM callback (timeout=%ldms) ...\n", aTimeoutMS));
881
882 /*
883 * Wait for the HGCM low level callback until the process
884 * has been started (or something went wrong). This is necessary to
885 * get the PID.
886 */
887 CallbackListIter it = getCtrlCallbackContextByID(uContextID);
888 BOOL fCanceled = FALSE;
889 if (it != mCallbackList.end())
890 {
891 uint64_t u64Started = RTTimeMilliTS();
892 while (!it->bCalled)
893 {
894 /* Check for timeout. */
895 unsigned cMsWait;
896 if (aTimeoutMS == RT_INDEFINITE_WAIT)
897 cMsWait = 10;
898 else
899 {
900 uint64_t cMsElapsed = RTTimeMilliTS() - u64Started;
901 if (cMsElapsed >= aTimeoutMS)
902 break; /* Timed out. */
903 cMsWait = RT_MIN(10, aTimeoutMS - (uint32_t)cMsElapsed);
904 }
905
906 /* Check for manual stop. */
907 if (!it->pProgress.isNull())
908 {
909 rc = it->pProgress->COMGETTER(Canceled)(&fCanceled);
910 if (FAILED(rc)) throw rc;
911 if (fCanceled)
912 break; /* Client wants to abort. */
913 }
914 RTThreadSleep(cMsWait);
915 }
916 }
917
918 /* Was the whole thing canceled? */
919 if (!fCanceled)
920 {
921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
922
923 PHOSTEXECCALLBACKDATA pData = (HOSTEXECCALLBACKDATA*)it->pvData;
924 Assert(it->cbData == sizeof(HOSTEXECCALLBACKDATA));
925 AssertPtr(pData);
926
927 if (it->bCalled)
928 {
929 /* Did we get some status? */
930 switch (pData->u32Status)
931 {
932 case PROC_STS_STARTED:
933 /* Process is (still) running; get PID. */
934 *aPID = pData->u32PID;
935 break;
936
937 /* In any other case the process either already
938 * terminated or something else went wrong, so no PID ... */
939 case PROC_STS_TEN: /* Terminated normally. */
940 case PROC_STS_TEA: /* Terminated abnormally. */
941 case PROC_STS_TES: /* Terminated through signal. */
942 case PROC_STS_TOK:
943 case PROC_STS_TOA:
944 case PROC_STS_DWN:
945 /*
946 * Process (already) ended, but we want to get the
947 * PID anyway to retrieve the output in a later call.
948 */
949 *aPID = pData->u32PID;
950 break;
951
952 case PROC_STS_ERROR:
953 vrc = pData->u32Flags; /* u32Flags member contains IPRT error code. */
954 break;
955
956 default:
957 vrc = VERR_INVALID_PARAMETER; /* Unknown status, should never happen! */
958 break;
959 }
960 }
961 else /* If callback not called within time ... well, that's a timeout! */
962 vrc = VERR_TIMEOUT;
963
964 /*
965 * Do *not* remove the callback yet - we might wait with the IProgress object on something
966 * else (like end of process) ...
967 */
968 if (RT_FAILURE(vrc))
969 {
970 if (vrc == VERR_FILE_NOT_FOUND) /* This is the most likely error. */
971 {
972 rc = setError(VBOX_E_IPRT_ERROR,
973 tr("The file '%s' was not found on guest"), Utf8Command.raw());
974 }
975 else if (vrc == VERR_BAD_EXE_FORMAT)
976 {
977 rc = setError(VBOX_E_IPRT_ERROR,
978 tr("The file '%s' is not an executable format on guest"), Utf8Command.raw());
979 }
980 else if (vrc == VERR_LOGON_FAILURE)
981 {
982 rc = setError(VBOX_E_IPRT_ERROR,
983 tr("The specified user '%s' was not able to logon on guest"), Utf8UserName.raw());
984 }
985 else if (vrc == VERR_TIMEOUT)
986 {
987 rc = setError(VBOX_E_IPRT_ERROR,
988 tr("The guest did not respond within time (%ums)"), aTimeoutMS);
989 }
990 else if (vrc == VERR_INVALID_PARAMETER)
991 {
992 rc = setError(VBOX_E_IPRT_ERROR,
993 tr("The guest reported an unknown process status (%u)"), pData->u32Status);
994 }
995 else
996 {
997 rc = setError(E_UNEXPECTED,
998 tr("The service call failed with error %Rrc"), vrc);
999 }
1000 }
1001 else /* Execution went fine. */
1002 {
1003 /* Return the progress to the caller. */
1004 progress.queryInterfaceTo(aProgress);
1005 }
1006 }
1007 else /* Operation was canceled. */
1008 {
1009 rc = setError(VBOX_E_IPRT_ERROR,
1010 tr("The operation was canceled."));
1011 }
1012 }
1013 else /* HGCM related error codes .*/
1014 {
1015 if (vrc == VERR_INVALID_VM_HANDLE)
1016 {
1017 rc = setError(VBOX_E_VM_ERROR,
1018 tr("VMM device is not available (is the VM running?)"));
1019 }
1020 else if (vrc == VERR_TIMEOUT)
1021 {
1022 rc = setError(VBOX_E_VM_ERROR,
1023 tr("The guest execution service is not ready"));
1024 }
1025 else /* HGCM call went wrong. */
1026 {
1027 rc = setError(E_UNEXPECTED,
1028 tr("The HGCM call failed with error %Rrc"), vrc);
1029 }
1030 }
1031
1032 for (unsigned i = 0; i < uNumArgs; i++)
1033 RTMemFree(papszArgv[i]);
1034 RTMemFree(papszArgv);
1035 }
1036 }
1037 catch (std::bad_alloc &)
1038 {
1039 rc = E_OUTOFMEMORY;
1040 }
1041 return rc;
1042#endif /* VBOX_WITH_GUEST_CONTROL */
1043}
1044
1045STDMETHODIMP Guest::GetProcessOutput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, ULONG64 aSize, ComSafeArrayOut(BYTE, aData))
1046{
1047#ifndef VBOX_WITH_GUEST_CONTROL
1048 ReturnComNotImplemented();
1049#else /* VBOX_WITH_GUEST_CONTROL */
1050 using namespace guestControl;
1051
1052 CheckComArgExpr(aPID, aPID > 0);
1053
1054 if (aFlags != 0) /* Flags are not supported at the moment. */
1055 return E_INVALIDARG;
1056
1057 AutoCaller autoCaller(this);
1058 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1059
1060 HRESULT rc = S_OK;
1061
1062 try
1063 {
1064 /*
1065 * Create progress object.
1066 * Note that we need at least a local progress object here in order
1067 * to get notified when someone cancels the operation.
1068 */
1069 ComObjPtr <Progress> progress;
1070 rc = progress.createObject();
1071 if (SUCCEEDED(rc))
1072 {
1073 rc = progress->init(static_cast<IGuest*>(this),
1074 BstrFmt(tr("Getting output of process")),
1075 TRUE);
1076 }
1077 if (FAILED(rc)) return rc;
1078
1079 /* Adjust timeout */
1080 if (aTimeoutMS == 0)
1081 aTimeoutMS = UINT32_MAX;
1082
1083 /* Search for existing PID. */
1084 PHOSTEXECOUTCALLBACKDATA pData = (HOSTEXECOUTCALLBACKDATA*)RTMemAlloc(sizeof(HOSTEXECOUTCALLBACKDATA));
1085 AssertReturn(pData, VBOX_E_IPRT_ERROR);
1086 uint32_t uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT,
1087 pData, sizeof(HOSTEXECOUTCALLBACKDATA), progress);
1088 Assert(uContextID > 0);
1089
1090 size_t cbData = (size_t)RT_MIN(aSize, _64K);
1091 com::SafeArray<BYTE> outputData(cbData);
1092
1093 VBOXHGCMSVCPARM paParms[5];
1094 int i = 0;
1095 paParms[i++].setUInt32(uContextID);
1096 paParms[i++].setUInt32(aPID);
1097 paParms[i++].setUInt32(aFlags); /** @todo Should represent stdout and/or stderr. */
1098
1099 int vrc = VINF_SUCCESS;
1100
1101 {
1102 VMMDev *vmmDev;
1103 {
1104 /* Make sure mParent is valid, so set the read lock while using.
1105 * Do not keep this lock while doing the actual call, because in the meanwhile
1106 * another thread could request a write lock which would be a bad idea ... */
1107 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1108
1109 /* Forward the information to the VMM device. */
1110 AssertPtr(mParent);
1111 vmmDev = mParent->getVMMDev();
1112 }
1113
1114 if (vmmDev)
1115 {
1116 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
1117 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_GET_OUTPUT,
1118 i, paParms);
1119 }
1120 }
1121
1122 if (RT_SUCCESS(vrc))
1123 {
1124 LogFlowFunc(("Waiting for HGCM callback (timeout=%ldms) ...\n", aTimeoutMS));
1125
1126 /*
1127 * Wait for the HGCM low level callback until the process
1128 * has been started (or something went wrong). This is necessary to
1129 * get the PID.
1130 */
1131 CallbackListIter it = getCtrlCallbackContextByID(uContextID);
1132 BOOL fCanceled = FALSE;
1133 if (it != mCallbackList.end())
1134 {
1135 uint64_t u64Started = RTTimeMilliTS();
1136 while (!it->bCalled)
1137 {
1138 /* Check for timeout. */
1139 unsigned cMsWait;
1140 if (aTimeoutMS == RT_INDEFINITE_WAIT)
1141 cMsWait = 10;
1142 else
1143 {
1144 uint64_t cMsElapsed = RTTimeMilliTS() - u64Started;
1145 if (cMsElapsed >= aTimeoutMS)
1146 break; /* Timed out. */
1147 cMsWait = RT_MIN(10, aTimeoutMS - (uint32_t)cMsElapsed);
1148 }
1149
1150 /* Check for manual stop. */
1151 if (!it->pProgress.isNull())
1152 {
1153 rc = it->pProgress->COMGETTER(Canceled)(&fCanceled);
1154 if (FAILED(rc)) throw rc;
1155 if (fCanceled)
1156 break; /* Client wants to abort. */
1157 }
1158 RTThreadSleep(cMsWait);
1159 }
1160
1161 /* Was the whole thing canceled? */
1162 if (!fCanceled)
1163 {
1164 if (it->bCalled)
1165 {
1166 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1167
1168 /* Did we get some output? */
1169 pData = (HOSTEXECOUTCALLBACKDATA*)it->pvData;
1170 Assert(it->cbData == sizeof(HOSTEXECOUTCALLBACKDATA));
1171 AssertPtr(pData);
1172
1173 if (pData->cbData)
1174 {
1175 /* Do we need to resize the array? */
1176 if (pData->cbData > cbData)
1177 outputData.resize(pData->cbData);
1178
1179 /* Fill output in supplied out buffer. */
1180 memcpy(outputData.raw(), pData->pvData, pData->cbData);
1181 outputData.resize(pData->cbData); /* Shrink to fit actual buffer size. */
1182 }
1183 else
1184 vrc = VERR_NO_DATA; /* This is not an error we want to report to COM. */
1185 }
1186 else /* If callback not called within time ... well, that's a timeout! */
1187 vrc = VERR_TIMEOUT;
1188 }
1189 else /* Operation was canceled. */
1190 vrc = VERR_CANCELLED;
1191
1192 if (RT_FAILURE(vrc))
1193 {
1194 if (vrc == VERR_NO_DATA)
1195 {
1196 /* This is not an error we want to report to COM. */
1197 }
1198 else if (vrc == VERR_TIMEOUT)
1199 {
1200 rc = setError(VBOX_E_IPRT_ERROR,
1201 tr("The guest did not output within time (%ums)"), aTimeoutMS);
1202 }
1203 else if (vrc == VERR_CANCELLED)
1204 {
1205 rc = setError(VBOX_E_IPRT_ERROR,
1206 tr("The operation was canceled."));
1207 }
1208 else
1209 {
1210 rc = setError(E_UNEXPECTED,
1211 tr("The service call failed with error %Rrc"), vrc);
1212 }
1213 }
1214
1215 {
1216 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1217 destroyCtrlCallbackContext(it);
1218 }
1219 }
1220 else /* PID lookup failed. */
1221 rc = setError(VBOX_E_IPRT_ERROR,
1222 tr("Process (PID %u) not found!"), aPID);
1223 }
1224 else /* HGCM operation failed. */
1225 rc = setError(E_UNEXPECTED,
1226 tr("The HGCM call failed with error %Rrc"), vrc);
1227
1228 /* Cleanup. */
1229 progress->uninit();
1230 progress.setNull();
1231
1232 /* If something failed (or there simply was no data, indicated by VERR_NO_DATA,
1233 * we return an empty array so that the frontend knows when to give up. */
1234 if (RT_FAILURE(vrc) || FAILED(rc))
1235 outputData.resize(0);
1236 outputData.detachTo(ComSafeArrayOutArg(aData));
1237 }
1238 catch (std::bad_alloc &)
1239 {
1240 rc = E_OUTOFMEMORY;
1241 }
1242 return rc;
1243#endif
1244}
1245
1246STDMETHODIMP Guest::GetProcessStatus(ULONG aPID, ULONG *aExitCode, ULONG *aFlags, ULONG *aStatus)
1247{
1248#ifndef VBOX_WITH_GUEST_CONTROL
1249 ReturnComNotImplemented();
1250#else /* VBOX_WITH_GUEST_CONTROL */
1251 using namespace guestControl;
1252
1253 AutoCaller autoCaller(this);
1254 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1255
1256 HRESULT rc = S_OK;
1257
1258 try
1259 {
1260 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1261
1262 GuestProcessIterConst it;
1263 for (it = mGuestProcessList.begin(); it != mGuestProcessList.end(); it++)
1264 {
1265 if (it->mPID == aPID)
1266 break;
1267 }
1268
1269 if (it != mGuestProcessList.end())
1270 {
1271 *aExitCode = it->mExitCode;
1272 *aFlags = it->mFlags;
1273 *aStatus = it->mStatus;
1274 }
1275 else
1276 rc = setError(VBOX_E_IPRT_ERROR,
1277 tr("Process (PID %u) not found!"), aPID);
1278 }
1279 catch (std::bad_alloc &)
1280 {
1281 rc = E_OUTOFMEMORY;
1282 }
1283 return rc;
1284#endif
1285}
1286
1287// public methods only for internal purposes
1288/////////////////////////////////////////////////////////////////////////////
1289
1290void Guest::setAdditionsVersion(Bstr aVersion, VBOXOSTYPE aOsType)
1291{
1292 AutoCaller autoCaller(this);
1293 AssertComRCReturnVoid (autoCaller.rc());
1294
1295 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1296
1297 mData.mAdditionsVersion = aVersion;
1298 mData.mAdditionsActive = !aVersion.isEmpty();
1299 /* Older Additions didn't have this finer grained capability bit,
1300 * so enable it by default. Newer Additions will disable it immediately
1301 * if relevant. */
1302 mData.mSupportsGraphics = mData.mAdditionsActive;
1303
1304 mData.mOSTypeId = Global::OSTypeId (aOsType);
1305}
1306
1307void Guest::setSupportsSeamless (BOOL aSupportsSeamless)
1308{
1309 AutoCaller autoCaller(this);
1310 AssertComRCReturnVoid (autoCaller.rc());
1311
1312 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1313
1314 mData.mSupportsSeamless = aSupportsSeamless;
1315}
1316
1317void Guest::setSupportsGraphics (BOOL aSupportsGraphics)
1318{
1319 AutoCaller autoCaller(this);
1320 AssertComRCReturnVoid (autoCaller.rc());
1321
1322 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1323
1324 mData.mSupportsGraphics = aSupportsGraphics;
1325}
1326/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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