VirtualBox

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

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

Guest Control/VBoxManage,Main: Immediate process output, now also cancelable with signal handling.

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