VirtualBox

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

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

Main: do not fail if VMM device port cannot be found

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

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