VirtualBox

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

Last change on this file since 29960 was 29867, checked in by vboxsync, 14 years ago

r=bird: todos

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