VirtualBox

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

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

Guest Control: Bugfix for no output data retrieved.

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