VirtualBox

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

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

Guest Control/Main: Update on local process list and IGuest::GetProcessStatus().

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