VirtualBox

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

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

Guest Control/Main/VBoxService: Update on locking, bugfixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 40.7 KB
Line 
1/* $Id: GuestImpl.cpp 28943 2010-04-30 15:47:23Z vboxsync $ */
2
3/** @file
4 *
5 * VirtualBox COM class implementation
6 */
7
8/*
9 * Copyright (C) 2006-2008 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20#include "GuestImpl.h"
21
22#include "Global.h"
23#include "ConsoleImpl.h"
24#include "ProgressImpl.h"
25#include "VMMDev.h"
26
27#include "AutoCaller.h"
28#include "Logging.h"
29
30#include <VBox/VMMDev.h>
31#ifdef VBOX_WITH_GUEST_CONTROL
32# include <VBox/com/array.h>
33#endif
34#include <iprt/cpp/utils.h>
35#include <iprt/getopt.h>
36#include <VBox/pgm.h>
37
38// defines
39/////////////////////////////////////////////////////////////////////////////
40
41// constructor / destructor
42/////////////////////////////////////////////////////////////////////////////
43
44DEFINE_EMPTY_CTOR_DTOR (Guest)
45
46HRESULT Guest::FinalConstruct()
47{
48 return S_OK;
49}
50
51void Guest::FinalRelease()
52{
53 uninit ();
54}
55
56// public methods only for internal purposes
57/////////////////////////////////////////////////////////////////////////////
58
59/**
60 * Initializes the guest object.
61 */
62HRESULT Guest::init (Console *aParent)
63{
64 LogFlowThisFunc(("aParent=%p\n", aParent));
65
66 ComAssertRet(aParent, E_INVALIDARG);
67
68 /* Enclose the state transition NotReady->InInit->Ready */
69 AutoInitSpan autoInitSpan(this);
70 AssertReturn(autoInitSpan.isOk(), E_FAIL);
71
72 unconst(mParent) = aParent;
73
74 /* mData.mAdditionsActive is FALSE */
75
76 /* Confirm a successful initialization when it's the case */
77 autoInitSpan.setSucceeded();
78
79 ULONG aMemoryBalloonSize;
80 HRESULT ret = mParent->machine()->COMGETTER(MemoryBalloonSize)(&aMemoryBalloonSize);
81 if (ret == S_OK)
82 mMemoryBalloonSize = aMemoryBalloonSize;
83 else
84 mMemoryBalloonSize = 0; /* Default is no ballooning */
85
86 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 AssertPtr(pData);
465 CallbackListIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
466
467 /* Callback can be called several times. */
468 if (it != mCallbackList.end())
469 {
470 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
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 /* Was progress canceled before? */
481 BOOL fCancelled;
482 it->pProgress->COMGETTER(Canceled)(&fCancelled);
483
484 /* Do progress handling. */
485 Utf8Str errMsg;
486 HRESULT rc2 = S_OK;
487 switch (pData->u32Status)
488 {
489 case PROC_STS_STARTED:
490 break;
491
492 case PROC_STS_TEN: /* Terminated normally. */
493 if ( !it->pProgress->getCompleted()
494 && !fCancelled)
495 {
496 it->pProgress->notifyComplete(S_OK);
497 }
498 break;
499
500 case PROC_STS_TEA: /* Terminated abnormally. */
501 errMsg = Utf8StrFmt(Guest::tr("Process terminated abnormally with status '%u'"),
502 pCBData->u32Flags);
503 break;
504
505 case PROC_STS_TES: /* Terminated through signal. */
506 errMsg = Utf8StrFmt(Guest::tr("Process terminated via signal with status '%u'"),
507 pCBData->u32Flags);
508 break;
509
510 case PROC_STS_TOK:
511 errMsg = Utf8StrFmt(Guest::tr("Process timed out and was killed"));
512 break;
513
514 case PROC_STS_TOA:
515 errMsg = Utf8StrFmt(Guest::tr("Process timed out and could not be killed"));
516 break;
517
518 case PROC_STS_DWN:
519 errMsg = Utf8StrFmt(Guest::tr("Process exited because system is shutting down"));
520 break;
521
522 default:
523 break;
524 }
525
526 /* Handle process list. */
527 /** @todo What happens on/deal with PID reuse? */
528 /** @todo How to deal with multiple updates at once? */
529 GuestProcessIter it_proc = getProcessByPID(pCBData->u32PID);
530 if (it_proc == mGuestProcessList.end())
531 {
532 /* Not found, add to list. */
533 GuestProcess p;
534 p.mPID = pCBData->u32PID;
535 p.mStatus = pCBData->u32Status;
536 p.mExitCode = pCBData->u32Flags; /* Contains exit code. */
537 p.mFlags = 0;
538
539 mGuestProcessList.push_back(p);
540 }
541 else /* Update list. */
542 {
543 it_proc->mStatus = pCBData->u32Status;
544 it_proc->mExitCode = pCBData->u32Flags; /* Contains exit code. */
545 it_proc->mFlags = 0;
546 }
547
548 if ( !it->pProgress.isNull()
549 && errMsg.length())
550 {
551 if ( !it->pProgress->getCompleted()
552 && !fCancelled)
553 {
554 it->pProgress->notifyComplete(E_FAIL /** @todo Find a better rc! */, COM_IIDOF(IGuest),
555 (CBSTR)Guest::getComponentName(), errMsg.c_str());
556 LogFlowFunc(("Callback (context ID=%u, status=%u) progress marked as completed\n",
557 pData->hdr.u32ContextID, pData->u32Status));
558 }
559 else
560 LogFlowFunc(("Callback (context ID=%u, status=%u) progress already marked as completed\n",
561 pData->hdr.u32ContextID, pData->u32Status));
562 }
563 ASMAtomicWriteBool(&it->bCalled, true);
564 }
565 else
566 LogFlowFunc(("Unexpected callback (magic=%u, context ID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));
567 LogFlowFuncLeave();
568 return rc;
569}
570
571/* Function for handling the execution output notification. */
572int Guest::notifyCtrlExecOut(uint32_t u32Function,
573 PHOSTEXECOUTCALLBACKDATA pData)
574{
575 LogFlowFuncEnter();
576 int rc = VINF_SUCCESS;
577
578 AssertPtr(pData);
579 CallbackListIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
580 if (it != mCallbackList.end())
581 {
582 Assert(!it->bCalled);
583 PHOSTEXECOUTCALLBACKDATA pCBData = (HOSTEXECOUTCALLBACKDATA*)it->pvData;
584 AssertPtr(pCBData);
585
586 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
587
588 pCBData->u32PID = pData->u32PID;
589 pCBData->u32HandleId = pData->u32HandleId;
590 pCBData->u32Flags = pData->u32Flags;
591
592 /* Make sure we really got something! */
593 if ( pData->cbData
594 && pData->pvData)
595 {
596 /* Allocate data buffer and copy it */
597 pCBData->pvData = RTMemAlloc(pData->cbData);
598 AssertReturn(pCBData->pvData, VERR_NO_MEMORY);
599 memcpy(pCBData->pvData, pData->pvData, pData->cbData);
600 pCBData->cbData = pData->cbData;
601 }
602 else
603 {
604 pCBData->pvData = NULL;
605 pCBData->cbData = 0;
606 }
607 ASMAtomicWriteBool(&it->bCalled, true);
608 }
609 else
610 LogFlowFunc(("Unexpected callback (magic=%u, context ID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));
611 LogFlowFuncLeave();
612 return rc;
613}
614
615Guest::CallbackListIter Guest::getCtrlCallbackContextByID(uint32_t u32ContextID)
616{
617 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
618
619 /** @todo Maybe use a map instead of list for fast context lookup. */
620 CallbackListIter it;
621 for (it = mCallbackList.begin(); it != mCallbackList.end(); it++)
622 {
623 if (it->mContextID == u32ContextID)
624 return (it);
625 }
626 return it;
627}
628
629Guest::GuestProcessIter Guest::getProcessByPID(uint32_t u32PID)
630{
631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
632
633 /** @todo Maybe use a map instead of list for fast context lookup. */
634 GuestProcessIter it;
635 for (it = mGuestProcessList.begin(); it != mGuestProcessList.end(); it++)
636 {
637 if (it->mPID == u32PID)
638 return (it);
639 }
640 return it;
641}
642
643void Guest::removeCtrlCallbackContext(Guest::CallbackListIter it)
644{
645 LogFlowFuncEnter();
646 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
647 if (it->pvData)
648 {
649 RTMemFree(it->pvData);
650 it->pvData = NULL;
651 it->cbData = 0;
652
653 /* Notify outstanding waits for progress ... */
654 if ( !it->pProgress.isNull()
655 && !it->pProgress->getCompleted())
656 {
657 /* Only notify as complete if not canceled! */
658 BOOL fCancelled;
659 if (SUCCEEDED(it->pProgress->COMGETTER(Canceled)(&fCancelled)) && !fCancelled)
660 it->pProgress->notifyComplete(S_OK);
661 it->pProgress = NULL;
662 }
663 }
664 LogFlowFuncLeave();
665}
666
667/* Adds a callback with a user provided data block and an optional progress object
668 * to the callback list. A callback is identified by a unique context ID which is used
669 * to identify a callback from the guest side. */
670uint32_t Guest::addCtrlCallbackContext(eVBoxGuestCtrlCallbackType enmType, void *pvData, uint32_t cbData, Progress *pProgress)
671{
672 LogFlowFuncEnter();
673 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
674 uint32_t uNewContext = ASMAtomicIncU32(&mNextContextID);
675 if (uNewContext == UINT32_MAX)
676 ASMAtomicUoWriteU32(&mNextContextID, 1000);
677
678 CallbackContext context;
679 context.mContextID = uNewContext;
680 context.mType = enmType;
681 context.bCalled = false;
682 context.pvData = pvData;
683 context.cbData = cbData;
684 context.pProgress = pProgress;
685
686 mCallbackList.push_back(context);
687 if (mCallbackList.size() > 256) /* Don't let the container size get too big! */
688 {
689 Guest::CallbackListIter it = mCallbackList.begin();
690 removeCtrlCallbackContext(it);
691 mCallbackList.erase(it);
692 }
693
694 LogFlowFuncLeave();
695 return uNewContext;
696}
697#endif /* VBOX_WITH_GUEST_CONTROL */
698
699STDMETHODIMP Guest::ExecuteProcess(IN_BSTR aCommand, ULONG aFlags,
700 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
701 IN_BSTR aStdIn, IN_BSTR aStdOut, IN_BSTR aStdErr,
702 IN_BSTR aUserName, IN_BSTR aPassword,
703 ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress)
704{
705#ifndef VBOX_WITH_GUEST_CONTROL
706 ReturnComNotImplemented();
707#else /* VBOX_WITH_GUEST_CONTROL */
708 using namespace guestControl;
709
710 CheckComArgStrNotEmptyOrNull(aCommand);
711 CheckComArgOutPointerValid(aPID);
712 CheckComArgOutPointerValid(aProgress);
713 if (aFlags != 0) /* Flags are not supported at the moment. */
714 return E_INVALIDARG;
715
716 HRESULT rc = S_OK;
717
718 try
719 {
720 AutoCaller autoCaller(this);
721 if (FAILED(autoCaller.rc())) return autoCaller.rc();
722
723 /*
724 * Create progress object.
725 */
726 ComObjPtr <Progress> progress;
727 rc = progress.createObject();
728 if (SUCCEEDED(rc))
729 {
730 rc = progress->init(static_cast<IGuest*>(this),
731 BstrFmt(tr("Executing process")),
732 TRUE);
733 }
734 if (FAILED(rc)) return rc;
735
736 /*
737 * Prepare process execution.
738 */
739 int vrc = VINF_SUCCESS;
740 Utf8Str Utf8Command(aCommand);
741
742 /* Adjust timeout */
743 if (aTimeoutMS == 0)
744 aTimeoutMS = UINT32_MAX;
745
746 /* Prepare arguments. */
747 char **papszArgv = NULL;
748 uint32_t uNumArgs = 0;
749 if (aArguments > 0)
750 {
751 com::SafeArray<IN_BSTR> args(ComSafeArrayInArg(aArguments));
752 uNumArgs = args.size();
753 papszArgv = (char**)RTMemAlloc(sizeof(char*) * (uNumArgs + 1));
754 AssertReturn(papszArgv, VERR_NO_MEMORY);
755 for (unsigned i = 0; RT_SUCCESS(vrc) && i < uNumArgs; i++)
756 vrc = RTStrAPrintf(&papszArgv[i], "%s", Utf8Str(args[i]).raw());
757 papszArgv[uNumArgs] = NULL;
758 }
759
760 Utf8Str Utf8StdIn(aStdIn);
761 Utf8Str Utf8StdOut(aStdOut);
762 Utf8Str Utf8StdErr(aStdErr);
763 Utf8Str Utf8UserName(aUserName);
764 Utf8Str Utf8Password(aPassword);
765 if (RT_SUCCESS(vrc))
766 {
767 uint32_t uContextID = 0;
768
769 char *pszArgs = NULL;
770 if (uNumArgs > 0)
771 vrc = RTGetOptArgvToString(&pszArgs, papszArgv, 0);
772 if (RT_SUCCESS(vrc))
773 {
774 uint32_t cbArgs = pszArgs ? strlen(pszArgs) + 1 : 0; /* Include terminating zero. */
775
776 /* Prepare environment. */
777 void *pvEnv = NULL;
778 uint32_t uNumEnv = 0;
779 uint32_t cbEnv = 0;
780 if (aEnvironment > 0)
781 {
782 com::SafeArray<IN_BSTR> env(ComSafeArrayInArg(aEnvironment));
783
784 for (unsigned i = 0; i < env.size(); i++)
785 {
786 vrc = prepareExecuteEnv(Utf8Str(env[i]).raw(), &pvEnv, &cbEnv, &uNumEnv);
787 if (RT_FAILURE(vrc))
788 break;
789 }
790 }
791
792 if (RT_SUCCESS(vrc))
793 {
794 PHOSTEXECCALLBACKDATA pData = (HOSTEXECCALLBACKDATA*)RTMemAlloc(sizeof(HOSTEXECCALLBACKDATA));
795 AssertReturn(pData, VBOX_E_IPRT_ERROR);
796 uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_START,
797 pData, sizeof(HOSTEXECCALLBACKDATA), progress);
798 Assert(uContextID > 0);
799
800 VBOXHGCMSVCPARM paParms[15];
801 int i = 0;
802 paParms[i++].setUInt32(uContextID);
803 paParms[i++].setPointer((void*)Utf8Command.raw(), (uint32_t)strlen(Utf8Command.raw()) + 1);
804 paParms[i++].setUInt32(aFlags);
805 paParms[i++].setUInt32(uNumArgs);
806 paParms[i++].setPointer((void*)pszArgs, cbArgs);
807 paParms[i++].setUInt32(uNumEnv);
808 paParms[i++].setUInt32(cbEnv);
809 paParms[i++].setPointer((void*)pvEnv, cbEnv);
810 paParms[i++].setPointer((void*)Utf8StdIn.raw(), (uint32_t)strlen(Utf8StdIn.raw()) + 1);
811 paParms[i++].setPointer((void*)Utf8StdOut.raw(), (uint32_t)strlen(Utf8StdOut.raw()) + 1);
812 paParms[i++].setPointer((void*)Utf8StdErr.raw(), (uint32_t)strlen(Utf8StdErr.raw()) + 1);
813 paParms[i++].setPointer((void*)Utf8UserName.raw(), (uint32_t)strlen(Utf8UserName.raw()) + 1);
814 paParms[i++].setPointer((void*)Utf8Password.raw(), (uint32_t)strlen(Utf8Password.raw()) + 1);
815 paParms[i++].setUInt32(aTimeoutMS);
816
817 /* Make sure mParent is valid, so set a read lock in this scope. */
818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
819
820 /* Forward the information to the VMM device. */
821 AssertPtr(mParent);
822 VMMDev *vmmDev = mParent->getVMMDev();
823 if (vmmDev)
824 {
825 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
826 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_CMD,
827 i, paParms);
828 }
829 else
830 vrc = VERR_INVALID_VM_HANDLE;
831 RTMemFree(pvEnv);
832 }
833 RTStrFree(pszArgs);
834 }
835 if (RT_SUCCESS(vrc))
836 {
837 LogFlowFunc(("Waiting for HGCM callback (timeout=%ldms) ...\n", aTimeoutMS));
838
839 /*
840 * Wait for the HGCM low level callback until the process
841 * has been started (or something went wrong). This is necessary to
842 * get the PID.
843 */
844 CallbackListIter it = getCtrlCallbackContextByID(uContextID);
845 if (it != mCallbackList.end())
846 {
847 uint64_t u64Started = RTTimeMilliTS();
848 while (!it->bCalled)
849 {
850 unsigned cMsWait;
851 if (aTimeoutMS == RT_INDEFINITE_WAIT)
852 cMsWait = 10;
853 else
854 {
855 uint64_t cMsElapsed = RTTimeMilliTS() - u64Started;
856 if (cMsElapsed >= aTimeoutMS)
857 break; /* Timed out. */
858 cMsWait = RT_MIN(10, aTimeoutMS - (uint32_t)cMsElapsed);
859 }
860 RTThreadSleep(cMsWait);
861 }
862 }
863
864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
865
866 /* Did we get some status? */
867 PHOSTEXECCALLBACKDATA pData = (HOSTEXECCALLBACKDATA*)it->pvData;
868 Assert(it->cbData == sizeof(HOSTEXECCALLBACKDATA));
869 AssertPtr(pData);
870 if (it->bCalled)
871 {
872 switch (pData->u32Status)
873 {
874 case PROC_STS_STARTED:
875 /* Process is (still) running; get PID. */
876 *aPID = pData->u32PID;
877 break;
878
879 /* In any other case the process either already
880 * terminated or something else went wrong, so no PID ... */
881 case PROC_STS_TEN: /* Terminated normally. */
882 case PROC_STS_TEA: /* Terminated abnormally. */
883 case PROC_STS_TES: /* Terminated through signal. */
884 case PROC_STS_TOK:
885 case PROC_STS_TOA:
886 case PROC_STS_DWN:
887 /*
888 * Process (already) ended, but we want to get the
889 * PID anyway to retrieve the output in a later call.
890 */
891 *aPID = pData->u32PID;
892 break;
893
894 case PROC_STS_ERROR:
895 vrc = pData->u32Flags; /* u32Flags member contains IPRT error code. */
896 break;
897
898 default:
899 vrc = VERR_INVALID_PARAMETER; /* Unknown status, should never happen! */
900 break;
901 }
902 }
903 else /* If callback not called within time ... well, that's a timeout! */
904 vrc = VERR_TIMEOUT;
905
906 alock.release();
907
908 /*
909 * Do *not* remove the callback yet - we might wait with the IProgress object on something
910 * else (like end of process) ...
911 */
912 if (RT_FAILURE(vrc))
913 {
914 if (vrc == VERR_FILE_NOT_FOUND) /* This is the most likely error. */
915 {
916 rc = setError(VBOX_E_IPRT_ERROR,
917 tr("The file '%s' was not found on guest"), Utf8Command.raw());
918 }
919 else if (vrc == VERR_BAD_EXE_FORMAT)
920 {
921 rc = setError(VBOX_E_IPRT_ERROR,
922 tr("The file '%s' is not an executable format on guest"), Utf8Command.raw());
923 }
924 else if (vrc == VERR_LOGON_FAILURE)
925 {
926 rc = setError(VBOX_E_IPRT_ERROR,
927 tr("The specified user '%s' was not able to logon on guest"), Utf8UserName.raw());
928 }
929 else if (vrc == VERR_TIMEOUT)
930 {
931 rc = setError(VBOX_E_IPRT_ERROR,
932 tr("The guest did not respond within time (%ums)"), aTimeoutMS);
933 }
934 else if (vrc == VERR_INVALID_PARAMETER)
935 {
936 rc = setError(VBOX_E_IPRT_ERROR,
937 tr("The guest reported an unknown process status (%u)"), pData->u32Status);
938 }
939 else
940 {
941 rc = setError(E_UNEXPECTED,
942 tr("The service call failed with error %Rrc"), vrc);
943 }
944 }
945 else
946 {
947 /* Return the progress to the caller. */
948 progress.queryInterfaceTo(aProgress);
949 }
950 }
951 else
952 {
953 if (vrc == VERR_INVALID_VM_HANDLE)
954 {
955 rc = setError(VBOX_E_VM_ERROR,
956 tr("VMM device is not available (is the VM running?)"));
957 }
958 else if (vrc == VERR_TIMEOUT)
959 {
960 rc = setError(VBOX_E_VM_ERROR,
961 tr("The guest execution service is not ready"));
962 }
963 else /* HGCM call went wrong. */
964 {
965 rc = setError(E_UNEXPECTED,
966 tr("The HGCM call failed with error %Rrc"), vrc);
967 }
968 }
969
970 for (unsigned i = 0; i < uNumArgs; i++)
971 RTMemFree(papszArgv[i]);
972 RTMemFree(papszArgv);
973 }
974 }
975 catch (std::bad_alloc &)
976 {
977 rc = E_OUTOFMEMORY;
978 }
979 return rc;
980#endif /* VBOX_WITH_GUEST_CONTROL */
981}
982
983STDMETHODIMP Guest::GetProcessOutput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, ULONG64 aSize, ComSafeArrayOut(BYTE, aData))
984{
985#ifndef VBOX_WITH_GUEST_CONTROL
986 ReturnComNotImplemented();
987#else /* VBOX_WITH_GUEST_CONTROL */
988 using namespace guestControl;
989
990 HRESULT rc = S_OK;
991
992 try
993 {
994 /* Adjust timeout */
995 if (aTimeoutMS == 0)
996 aTimeoutMS = UINT32_MAX;
997
998 /* Search for existing PID. */
999 PHOSTEXECOUTCALLBACKDATA pData = (HOSTEXECOUTCALLBACKDATA*)RTMemAlloc(sizeof(HOSTEXECOUTCALLBACKDATA));
1000 AssertReturn(pData, VBOX_E_IPRT_ERROR);
1001 uint32_t uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT,
1002 pData, sizeof(HOSTEXECOUTCALLBACKDATA), NULL /* pProgress */);
1003 Assert(uContextID > 0);
1004
1005 size_t cbData = (size_t)RT_MIN(aSize, _64K);
1006 com::SafeArray<BYTE> outputData(cbData);
1007
1008 VBOXHGCMSVCPARM paParms[5];
1009 int i = 0;
1010 paParms[i++].setUInt32(uContextID);
1011 paParms[i++].setUInt32(aPID);
1012 paParms[i++].setUInt32(aFlags); /** @todo Should represent stdout and/or stderr. */
1013
1014 int vrc = VINF_SUCCESS;
1015
1016 {
1017 /* Make sure mParent is valid, so set the read lock while using. */
1018 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1019
1020 /* Forward the information to the VMM device. */
1021 AssertPtr(mParent);
1022 VMMDev *vmmDev = mParent->getVMMDev();
1023 if (vmmDev)
1024 {
1025 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
1026 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_GET_OUTPUT,
1027 i, paParms);
1028 }
1029 }
1030
1031 if (RT_SUCCESS(vrc))
1032 {
1033 LogFlowFunc(("Waiting for HGCM callback (timeout=%ldms) ...\n", aTimeoutMS));
1034
1035 /*
1036 * Wait for the HGCM low level callback until the process
1037 * has been started (or something went wrong). This is necessary to
1038 * get the PID.
1039 */
1040 CallbackListIter it = getCtrlCallbackContextByID(uContextID);
1041 if (it != mCallbackList.end())
1042 {
1043 uint64_t u64Started = RTTimeMilliTS();
1044 while (!it->bCalled)
1045 {
1046 unsigned cMsWait;
1047 if (aTimeoutMS == RT_INDEFINITE_WAIT)
1048 cMsWait = 10;
1049 else
1050 {
1051 uint64_t cMsElapsed = RTTimeMilliTS() - u64Started;
1052 if (cMsElapsed >= aTimeoutMS)
1053 break; /* timed out */
1054 cMsWait = RT_MIN(10, aTimeoutMS - (uint32_t)cMsElapsed);
1055 }
1056 RTThreadSleep(cMsWait);
1057 }
1058
1059 if (it->bCalled)
1060 {
1061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1062
1063 /* Did we get some output? */
1064 pData = (HOSTEXECOUTCALLBACKDATA*)it->pvData;
1065 Assert(it->cbData == sizeof(HOSTEXECOUTCALLBACKDATA));
1066 AssertPtr(pData);
1067
1068 if (pData->cbData)
1069 {
1070 /* Do we need to resize the array? */
1071 if (pData->cbData > cbData)
1072 outputData.resize(pData->cbData);
1073
1074 /* Fill output in supplied out buffer. */
1075 memcpy(outputData.raw(), pData->pvData, pData->cbData);
1076 outputData.resize(pData->cbData); /* Shrink to fit actual buffer size. */
1077 }
1078 else
1079 vrc = VERR_NO_DATA; /* This is not an error we want to report to COM. */
1080 }
1081 else /* If callback not called within time ... well, that's a timeout! */
1082 vrc = VERR_TIMEOUT;
1083
1084 if (RT_FAILURE(vrc))
1085 {
1086 if (vrc == VERR_NO_DATA)
1087 {
1088 /* This is not an error we want to report to COM. */
1089 }
1090 else if (vrc == VERR_TIMEOUT)
1091 {
1092 rc = setError(VBOX_E_IPRT_ERROR,
1093 tr("The guest did not output within time (%ums)"), aTimeoutMS);
1094 }
1095 else
1096 {
1097 rc = setError(E_UNEXPECTED,
1098 tr("The service call failed with error %Rrc"), vrc);
1099 }
1100 }
1101 }
1102 else
1103 rc = setError(VBOX_E_IPRT_ERROR,
1104 tr("Process (PID %u) not found!"), aPID);
1105 }
1106 else
1107 rc = setError(E_UNEXPECTED,
1108 tr("The HGCM call failed with error %Rrc"), vrc);
1109
1110 /* If something failed (or there simply was no data, indicated by VERR_NO_DATA,
1111 * we return an empty array so that the frontend knows when to give up. */
1112 if (RT_FAILURE(vrc) || FAILED(rc))
1113 outputData.resize(0);
1114 outputData.detachTo(ComSafeArrayOutArg(aData));
1115 }
1116 catch (std::bad_alloc &)
1117 {
1118 rc = E_OUTOFMEMORY;
1119 }
1120 return rc;
1121#endif
1122}
1123
1124STDMETHODIMP Guest::GetProcessStatus(ULONG aPID, ULONG *aExitCode, ULONG *aFlags, ULONG *aStatus)
1125{
1126#ifndef VBOX_WITH_GUEST_CONTROL
1127 ReturnComNotImplemented();
1128#else /* VBOX_WITH_GUEST_CONTROL */
1129 using namespace guestControl;
1130
1131 HRESULT rc = S_OK;
1132
1133 try
1134 {
1135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1136
1137 GuestProcessIterConst it;
1138 for (it = mGuestProcessList.begin(); it != mGuestProcessList.end(); it++)
1139 {
1140 if (it->mPID == aPID)
1141 break;
1142 }
1143
1144 if (it != mGuestProcessList.end())
1145 {
1146 *aExitCode = it->mExitCode;
1147 *aFlags = it->mFlags;
1148 *aStatus = it->mStatus;
1149 }
1150 else
1151 rc = setError(VBOX_E_IPRT_ERROR,
1152 tr("Process (PID %u) not found!"), aPID);
1153 }
1154 catch (std::bad_alloc &)
1155 {
1156 rc = E_OUTOFMEMORY;
1157 }
1158 return rc;
1159#endif
1160}
1161
1162// public methods only for internal purposes
1163/////////////////////////////////////////////////////////////////////////////
1164
1165void Guest::setAdditionsVersion(Bstr aVersion, VBOXOSTYPE aOsType)
1166{
1167 AutoCaller autoCaller(this);
1168 AssertComRCReturnVoid (autoCaller.rc());
1169
1170 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1171
1172 mData.mAdditionsVersion = aVersion;
1173 mData.mAdditionsActive = !aVersion.isEmpty();
1174 /* Older Additions didn't have this finer grained capability bit,
1175 * so enable it by default. Newer Additions will disable it immediately
1176 * if relevant. */
1177 mData.mSupportsGraphics = mData.mAdditionsActive;
1178
1179 mData.mOSTypeId = Global::OSTypeId (aOsType);
1180}
1181
1182void Guest::setSupportsSeamless (BOOL aSupportsSeamless)
1183{
1184 AutoCaller autoCaller(this);
1185 AssertComRCReturnVoid (autoCaller.rc());
1186
1187 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1188
1189 mData.mSupportsSeamless = aSupportsSeamless;
1190}
1191
1192void Guest::setSupportsGraphics (BOOL aSupportsGraphics)
1193{
1194 AutoCaller autoCaller(this);
1195 AssertComRCReturnVoid (autoCaller.rc());
1196
1197 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1198
1199 mData.mSupportsGraphics = aSupportsGraphics;
1200}
1201/* 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