VirtualBox

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

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

Guest Control: Update (prepareExecuteArgs() not used).

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