VirtualBox

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

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

Complete API, but leave it unimplemented

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