VirtualBox

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

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

Guest Control: Update (first stuff for piping output).

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