VirtualBox

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

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

Main/GuestImpl: more correct handling of the RTStrAPrintf() result

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