VirtualBox

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

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

Guest Control/Main/HostService: Deal with stale clients, avoid deadlocks when doing massive output traffic.

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