VirtualBox

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

最後變更 在這個檔案從32855是 32855,由 vboxsync 提交於 14 年 前

warning

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 63.1 KB
 
1/* $Id: GuestImpl.cpp 32855 2010-09-30 18:12:32Z vboxsync $ */
2
3/** @file
4 *
5 * VirtualBox COM class implementation
6 */
7
8/*
9 * Copyright (C) 2006-2010 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.alldomusa.eu.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 /* Confirm a successful initialization when it's the case */
75 autoInitSpan.setSucceeded();
76
77 ULONG aMemoryBalloonSize;
78 HRESULT ret = mParent->machine()->COMGETTER(MemoryBalloonSize)(&aMemoryBalloonSize);
79 if (ret == S_OK)
80 mMemoryBalloonSize = aMemoryBalloonSize;
81 else
82 mMemoryBalloonSize = 0; /* Default is no ballooning */
83
84 BOOL fPageFusionEnabled;
85 ret = mParent->machine()->COMGETTER(PageFusionEnabled)(&fPageFusionEnabled);
86 if (ret == S_OK)
87 mfPageFusionEnabled = fPageFusionEnabled;
88 else
89 mfPageFusionEnabled = false; /* Default is no page fusion*/
90
91 mStatUpdateInterval = 0; /* Default is not to report guest statistics at all */
92
93 /* Clear statistics. */
94 for (unsigned i = 0 ; i < GUESTSTATTYPE_MAX; i++)
95 mCurrentGuestStat[i] = 0;
96
97#ifdef VBOX_WITH_GUEST_CONTROL
98 /* Init the context ID counter at 1000. */
99 mNextContextID = 1000;
100#endif
101
102 return S_OK;
103}
104
105/**
106 * Uninitializes the instance and sets the ready flag to FALSE.
107 * Called either from FinalRelease() or by the parent when it gets destroyed.
108 */
109void Guest::uninit()
110{
111 LogFlowThisFunc(("\n"));
112
113#ifdef VBOX_WITH_GUEST_CONTROL
114 /* Scope write lock as much as possible. */
115 {
116 /*
117 * Cleanup must be done *before* AutoUninitSpan to cancel all
118 * all outstanding waits in API functions (which hold AutoCaller
119 * ref counts).
120 */
121 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
122
123 /* Clean up callback data. */
124 CallbackMapIter it;
125 for (it = mCallbackMap.begin(); it != mCallbackMap.end(); it++)
126 destroyCtrlCallbackContext(it);
127
128 /* Clear process map. */
129 mGuestProcessMap.clear();
130 }
131#endif
132
133 /* Enclose the state transition Ready->InUninit->NotReady */
134 AutoUninitSpan autoUninitSpan(this);
135 if (autoUninitSpan.uninitDone())
136 return;
137
138 unconst(mParent) = NULL;
139}
140
141// IGuest properties
142/////////////////////////////////////////////////////////////////////////////
143
144STDMETHODIMP Guest::COMGETTER(OSTypeId) (BSTR *aOSTypeId)
145{
146 CheckComArgOutPointerValid(aOSTypeId);
147
148 AutoCaller autoCaller(this);
149 if (FAILED(autoCaller.rc())) return autoCaller.rc();
150
151 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
152
153 // redirect the call to IMachine if no additions are installed
154 if (mData.mAdditionsVersion.isEmpty())
155 return mParent->machine()->COMGETTER(OSTypeId)(aOSTypeId);
156
157 mData.mOSTypeId.cloneTo(aOSTypeId);
158
159 return S_OK;
160}
161
162STDMETHODIMP Guest::COMGETTER(AdditionsRunLevel) (AdditionsRunLevelType_T *aRunLevel)
163{
164 AutoCaller autoCaller(this);
165 if (FAILED(autoCaller.rc())) return autoCaller.rc();
166
167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
168
169 *aRunLevel = mData.mAdditionsRunLevel;
170
171 return S_OK;
172}
173
174STDMETHODIMP Guest::COMGETTER(AdditionsVersion) (BSTR *aAdditionsVersion)
175{
176 CheckComArgOutPointerValid(aAdditionsVersion);
177
178 AutoCaller autoCaller(this);
179 if (FAILED(autoCaller.rc())) return autoCaller.rc();
180
181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
182
183 HRESULT hr = S_OK;
184 if ( mData.mAdditionsVersion.isEmpty()
185 /* Only try alternative way if GA are active! */
186 && mData.mAdditionsRunLevel > AdditionsRunLevelType_None)
187 {
188 /*
189 * If we got back an empty string from GetAdditionsVersion() we either
190 * really don't have the Guest Additions version yet or the guest is running
191 * older Guest Additions (< 3.2.0) which don't provide VMMDevReq_ReportGuestInfo2,
192 * so get the version + revision from the (hopefully) provided guest properties
193 * instead.
194 */
195 Bstr addVersion;
196 LONG64 u64Timestamp;
197 Bstr flags;
198 hr = mParent->machine()->GetGuestProperty(Bstr("/VirtualBox/GuestAdd/Version").raw(),
199 addVersion.asOutParam(), &u64Timestamp, flags.asOutParam());
200 if (hr == S_OK)
201 {
202 Bstr addRevision;
203 hr = mParent->machine()->GetGuestProperty(Bstr("/VirtualBox/GuestAdd/Revision").raw(),
204 addRevision.asOutParam(), &u64Timestamp, flags.asOutParam());
205 if ( hr == S_OK
206 && !addVersion.isEmpty()
207 && !addRevision.isEmpty())
208 {
209 /* Some Guest Additions versions had interchanged version + revision values,
210 * so check if the version value at least has a dot to identify it and change
211 * both values to reflect the right content. */
212 if (!Utf8Str(addVersion).contains("."))
213 {
214 Bstr addTemp = addVersion;
215 addVersion = addRevision;
216 addRevision = addTemp;
217 }
218
219 Bstr additionsVersion = BstrFmt("%ls r%ls",
220 addVersion.raw(), addRevision.raw());
221 additionsVersion.cloneTo(aAdditionsVersion);
222 }
223 /** @todo r=bird: else: Should not return failure! */
224 }
225 else
226 {
227 /* If getting the version + revision above fails or they simply aren't there
228 * because of *really* old Guest Additions we only can report the interface
229 * version to at least have something. */
230 mData.mInterfaceVersion.cloneTo(aAdditionsVersion);
231 /** @todo r=bird: hr is still indicating failure! */
232 }
233 }
234 else
235 mData.mAdditionsVersion.cloneTo(aAdditionsVersion);
236
237 return hr;
238}
239
240STDMETHODIMP Guest::COMGETTER(SupportsSeamless) (BOOL *aSupportsSeamless)
241{
242 CheckComArgOutPointerValid(aSupportsSeamless);
243
244 AutoCaller autoCaller(this);
245 if (FAILED(autoCaller.rc())) return autoCaller.rc();
246
247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
248
249 *aSupportsSeamless = mData.mSupportsSeamless;
250
251 return S_OK;
252}
253
254STDMETHODIMP Guest::COMGETTER(SupportsGraphics) (BOOL *aSupportsGraphics)
255{
256 CheckComArgOutPointerValid(aSupportsGraphics);
257
258 AutoCaller autoCaller(this);
259 if (FAILED(autoCaller.rc())) return autoCaller.rc();
260
261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
262
263 *aSupportsGraphics = mData.mSupportsGraphics;
264
265 return S_OK;
266}
267
268BOOL Guest::isPageFusionEnabled()
269{
270 AutoCaller autoCaller(this);
271 if (FAILED(autoCaller.rc())) return false;
272
273 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
274
275 return mfPageFusionEnabled;
276}
277
278STDMETHODIMP Guest::COMGETTER(MemoryBalloonSize) (ULONG *aMemoryBalloonSize)
279{
280 CheckComArgOutPointerValid(aMemoryBalloonSize);
281
282 AutoCaller autoCaller(this);
283 if (FAILED(autoCaller.rc())) return autoCaller.rc();
284
285 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
286
287 *aMemoryBalloonSize = mMemoryBalloonSize;
288
289 return S_OK;
290}
291
292STDMETHODIMP Guest::COMSETTER(MemoryBalloonSize) (ULONG aMemoryBalloonSize)
293{
294 AutoCaller autoCaller(this);
295 if (FAILED(autoCaller.rc())) return autoCaller.rc();
296
297 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
298
299 /* We must be 100% sure that IMachine::COMSETTER(MemoryBalloonSize)
300 * does not call us back in any way! */
301 HRESULT ret = mParent->machine()->COMSETTER(MemoryBalloonSize)(aMemoryBalloonSize);
302 if (ret == S_OK)
303 {
304 mMemoryBalloonSize = aMemoryBalloonSize;
305 /* forward the information to the VMM device */
306 VMMDev *pVMMDev = mParent->getVMMDev();
307 /* MUST release all locks before calling VMM device as its critsect
308 * has higher lock order than anything in Main. */
309 alock.release();
310 if (pVMMDev)
311 {
312 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
313 if (pVMMDevPort)
314 pVMMDevPort->pfnSetMemoryBalloon(pVMMDevPort, aMemoryBalloonSize);
315 }
316 }
317
318 return ret;
319}
320
321STDMETHODIMP Guest::COMGETTER(StatisticsUpdateInterval)(ULONG *aUpdateInterval)
322{
323 CheckComArgOutPointerValid(aUpdateInterval);
324
325 AutoCaller autoCaller(this);
326 if (FAILED(autoCaller.rc())) return autoCaller.rc();
327
328 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
329
330 *aUpdateInterval = mStatUpdateInterval;
331 return S_OK;
332}
333
334STDMETHODIMP Guest::COMSETTER(StatisticsUpdateInterval)(ULONG aUpdateInterval)
335{
336 AutoCaller autoCaller(this);
337 if (FAILED(autoCaller.rc())) return autoCaller.rc();
338
339 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
340
341 mStatUpdateInterval = aUpdateInterval;
342 /* forward the information to the VMM device */
343 VMMDev *pVMMDev = mParent->getVMMDev();
344 /* MUST release all locks before calling VMM device as its critsect
345 * has higher lock order than anything in Main. */
346 alock.release();
347 if (pVMMDev)
348 {
349 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
350 if (pVMMDevPort)
351 pVMMDevPort->pfnSetStatisticsInterval(pVMMDevPort, aUpdateInterval);
352 }
353
354 return S_OK;
355}
356
357STDMETHODIMP Guest::InternalGetStatistics(ULONG *aCpuUser, ULONG *aCpuKernel, ULONG *aCpuIdle,
358 ULONG *aMemTotal, ULONG *aMemFree, ULONG *aMemBalloon, ULONG *aMemShared,
359 ULONG *aMemCache, ULONG *aPageTotal,
360 ULONG *aMemAllocTotal, ULONG *aMemFreeTotal, ULONG *aMemBalloonTotal, ULONG *aMemSharedTotal)
361{
362 CheckComArgOutPointerValid(aCpuUser);
363 CheckComArgOutPointerValid(aCpuKernel);
364 CheckComArgOutPointerValid(aCpuIdle);
365 CheckComArgOutPointerValid(aMemTotal);
366 CheckComArgOutPointerValid(aMemFree);
367 CheckComArgOutPointerValid(aMemBalloon);
368 CheckComArgOutPointerValid(aMemShared);
369 CheckComArgOutPointerValid(aMemCache);
370 CheckComArgOutPointerValid(aPageTotal);
371 CheckComArgOutPointerValid(aMemAllocTotal);
372 CheckComArgOutPointerValid(aMemFreeTotal);
373 CheckComArgOutPointerValid(aMemBalloonTotal);
374 CheckComArgOutPointerValid(aMemSharedTotal);
375
376 AutoCaller autoCaller(this);
377 if (FAILED(autoCaller.rc())) return autoCaller.rc();
378
379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
380
381 *aCpuUser = mCurrentGuestStat[GUESTSTATTYPE_CPUUSER];
382 *aCpuKernel = mCurrentGuestStat[GUESTSTATTYPE_CPUKERNEL];
383 *aCpuIdle = mCurrentGuestStat[GUESTSTATTYPE_CPUIDLE];
384 *aMemTotal = mCurrentGuestStat[GUESTSTATTYPE_MEMTOTAL] * (_4K/_1K); /* page (4K) -> 1KB units */
385 *aMemFree = mCurrentGuestStat[GUESTSTATTYPE_MEMFREE] * (_4K/_1K); /* page (4K) -> 1KB units */
386 *aMemBalloon = mCurrentGuestStat[GUESTSTATTYPE_MEMBALLOON] * (_4K/_1K); /* page (4K) -> 1KB units */
387 *aMemCache = mCurrentGuestStat[GUESTSTATTYPE_MEMCACHE] * (_4K/_1K); /* page (4K) -> 1KB units */
388 *aPageTotal = mCurrentGuestStat[GUESTSTATTYPE_PAGETOTAL] * (_4K/_1K); /* page (4K) -> 1KB units */
389
390 /* MUST release all locks before calling any PGM statistics queries,
391 * as they are executed by EMT and that might deadlock us by VMM device
392 * activity which waits for the Guest object lock. */
393 alock.release();
394 Console::SafeVMPtr pVM (mParent);
395 if (pVM.isOk())
396 {
397 uint64_t uFreeTotal, uAllocTotal, uBalloonedTotal, uSharedTotal;
398 *aMemFreeTotal = 0;
399 int rc = PGMR3QueryVMMMemoryStats(pVM.raw(), &uAllocTotal, &uFreeTotal, &uBalloonedTotal, &uSharedTotal);
400 AssertRC(rc);
401 if (rc == VINF_SUCCESS)
402 {
403 *aMemAllocTotal = (ULONG)(uAllocTotal / _1K); /* bytes -> KB */
404 *aMemFreeTotal = (ULONG)(uFreeTotal / _1K);
405 *aMemBalloonTotal = (ULONG)(uBalloonedTotal / _1K);
406 *aMemSharedTotal = (ULONG)(uSharedTotal / _1K);
407 }
408
409 /* Query the missing per-VM memory statistics. */
410 *aMemShared = 0;
411 uint64_t uTotalMem, uPrivateMem, uSharedMem, uZeroMem;
412 rc = PGMR3QueryMemoryStats(pVM.raw(), &uTotalMem, &uPrivateMem, &uSharedMem, &uZeroMem);
413 if (rc == VINF_SUCCESS)
414 {
415 *aMemShared = (ULONG)(uSharedMem / _1K);
416 }
417 }
418 else
419 {
420 *aMemFreeTotal = 0;
421 *aMemShared = 0;
422 }
423
424 return S_OK;
425}
426
427HRESULT Guest::setStatistic(ULONG aCpuId, GUESTSTATTYPE enmType, ULONG aVal)
428{
429 AutoCaller autoCaller(this);
430 if (FAILED(autoCaller.rc())) return autoCaller.rc();
431
432 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
433
434 if (enmType >= GUESTSTATTYPE_MAX)
435 return E_INVALIDARG;
436
437 mCurrentGuestStat[enmType] = aVal;
438 return S_OK;
439}
440
441STDMETHODIMP Guest::GetAdditionsStatus(AdditionsRunLevelType_T aLevel, BOOL *aActive)
442{
443 AutoCaller autoCaller(this);
444 if (FAILED(autoCaller.rc())) return autoCaller.rc();
445
446 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
447
448 HRESULT rc = S_OK;
449 switch (aLevel)
450 {
451 case AdditionsRunLevelType_System:
452 *aActive = (mData.mAdditionsRunLevel > AdditionsRunLevelType_None);
453 break;
454
455 case AdditionsRunLevelType_Userland:
456 *aActive = (mData.mAdditionsRunLevel >= AdditionsRunLevelType_Userland);
457 break;
458
459 case AdditionsRunLevelType_Desktop:
460 *aActive = (mData.mAdditionsRunLevel >= AdditionsRunLevelType_Desktop);
461 break;
462
463 default:
464 rc = setError(VBOX_E_NOT_SUPPORTED,
465 tr("Invalid status level defined: %u"), aLevel);
466 break;
467 }
468
469 return rc;
470}
471
472STDMETHODIMP Guest::SetCredentials(IN_BSTR aUserName, IN_BSTR aPassword,
473 IN_BSTR aDomain, BOOL aAllowInteractiveLogon)
474{
475 AutoCaller autoCaller(this);
476 if (FAILED(autoCaller.rc())) return autoCaller.rc();
477
478 /* forward the information to the VMM device */
479 VMMDev *pVMMDev = mParent->getVMMDev();
480 if (pVMMDev)
481 {
482 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
483 if (pVMMDevPort)
484 {
485 uint32_t u32Flags = VMMDEV_SETCREDENTIALS_GUESTLOGON;
486 if (!aAllowInteractiveLogon)
487 u32Flags = VMMDEV_SETCREDENTIALS_NOLOCALLOGON;
488
489 pVMMDevPort->pfnSetCredentials(pVMMDevPort,
490 Utf8Str(aUserName).c_str(),
491 Utf8Str(aPassword).c_str(),
492 Utf8Str(aDomain).c_str(),
493 u32Flags);
494 return S_OK;
495 }
496 }
497
498 return setError(VBOX_E_VM_ERROR,
499 tr("VMM device is not available (is the VM running?)"));
500}
501
502#ifdef VBOX_WITH_GUEST_CONTROL
503/**
504 * Appends environment variables to the environment block. Each var=value pair is separated
505 * by NULL (\0) sequence. The whole block will be stored in one blob and disassembled on the
506 * guest side later to fit into the HGCM param structure.
507 *
508 * @returns VBox status code.
509 *
510 * @todo
511 *
512 */
513int Guest::prepareExecuteEnv(const char *pszEnv, void **ppvList, uint32_t *pcbList, uint32_t *pcEnv)
514{
515 int rc = VINF_SUCCESS;
516 uint32_t cbLen = strlen(pszEnv);
517 if (*ppvList)
518 {
519 uint32_t cbNewLen = *pcbList + cbLen + 1; /* Include zero termination. */
520 char *pvTmp = (char*)RTMemRealloc(*ppvList, cbNewLen);
521 if (NULL == pvTmp)
522 {
523 rc = VERR_NO_MEMORY;
524 }
525 else
526 {
527 memcpy(pvTmp + *pcbList, pszEnv, cbLen);
528 pvTmp[cbNewLen - 1] = '\0'; /* Add zero termination. */
529 *ppvList = (void**)pvTmp;
530 }
531 }
532 else
533 {
534 char *pcTmp;
535 if (RTStrAPrintf(&pcTmp, "%s", pszEnv) > 0)
536 {
537 *ppvList = (void**)pcTmp;
538 /* Reset counters. */
539 *pcEnv = 0;
540 *pcbList = 0;
541 }
542 }
543 if (RT_SUCCESS(rc))
544 {
545 *pcbList += cbLen + 1; /* Include zero termination. */
546 *pcEnv += 1; /* Increase env pairs count. */
547 }
548 return rc;
549}
550
551/**
552 * Static callback function for receiving updates on guest control commands
553 * from the guest. Acts as a dispatcher for the actual class instance.
554 *
555 * @returns VBox status code.
556 *
557 * @todo
558 *
559 */
560DECLCALLBACK(int) Guest::doGuestCtrlNotification(void *pvExtension,
561 uint32_t u32Function,
562 void *pvParms,
563 uint32_t cbParms)
564{
565 using namespace guestControl;
566
567 /*
568 * No locking, as this is purely a notification which does not make any
569 * changes to the object state.
570 */
571#ifdef DEBUG_andy
572 LogFlowFunc(("pvExtension = %p, u32Function = %d, pvParms = %p, cbParms = %d\n",
573 pvExtension, u32Function, pvParms, cbParms));
574#endif
575 ComObjPtr<Guest> pGuest = reinterpret_cast<Guest *>(pvExtension);
576
577 int rc = VINF_SUCCESS;
578 if (u32Function == GUEST_DISCONNECTED)
579 {
580 //LogFlowFunc(("GUEST_DISCONNECTED\n"));
581
582 PCALLBACKDATACLIENTDISCONNECTED pCBData = reinterpret_cast<PCALLBACKDATACLIENTDISCONNECTED>(pvParms);
583 AssertPtr(pCBData);
584 AssertReturn(sizeof(CALLBACKDATACLIENTDISCONNECTED) == cbParms, VERR_INVALID_PARAMETER);
585 AssertReturn(CALLBACKDATAMAGICCLIENTDISCONNECTED == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
586
587 rc = pGuest->notifyCtrlClientDisconnected(u32Function, pCBData);
588 }
589 else if (u32Function == GUEST_EXEC_SEND_STATUS)
590 {
591 //LogFlowFunc(("GUEST_EXEC_SEND_STATUS\n"));
592
593 PCALLBACKDATAEXECSTATUS pCBData = reinterpret_cast<PCALLBACKDATAEXECSTATUS>(pvParms);
594 AssertPtr(pCBData);
595 AssertReturn(sizeof(CALLBACKDATAEXECSTATUS) == cbParms, VERR_INVALID_PARAMETER);
596 AssertReturn(CALLBACKDATAMAGICEXECSTATUS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
597
598 rc = pGuest->notifyCtrlExecStatus(u32Function, pCBData);
599 }
600 else if (u32Function == GUEST_EXEC_SEND_OUTPUT)
601 {
602 //LogFlowFunc(("GUEST_EXEC_SEND_OUTPUT\n"));
603
604 PCALLBACKDATAEXECOUT pCBData = reinterpret_cast<PCALLBACKDATAEXECOUT>(pvParms);
605 AssertPtr(pCBData);
606 AssertReturn(sizeof(CALLBACKDATAEXECOUT) == cbParms, VERR_INVALID_PARAMETER);
607 AssertReturn(CALLBACKDATAMAGICEXECOUT == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
608
609 rc = pGuest->notifyCtrlExecOut(u32Function, pCBData);
610 }
611 else
612 rc = VERR_NOT_SUPPORTED;
613 return rc;
614}
615
616/* Function for handling the execution start/termination notification. */
617int Guest::notifyCtrlExecStatus(uint32_t u32Function,
618 PCALLBACKDATAEXECSTATUS pData)
619{
620 int vrc = VINF_SUCCESS;
621
622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
623
624 AssertPtr(pData);
625 CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
626
627 /* Callback can be called several times. */
628 if (it != mCallbackMap.end())
629 {
630 PCALLBACKDATAEXECSTATUS pCBData = (PCALLBACKDATAEXECSTATUS)it->second.pvData;
631 AssertPtr(pCBData);
632
633 pCBData->u32PID = pData->u32PID;
634 pCBData->u32Status = pData->u32Status;
635 pCBData->u32Flags = pData->u32Flags;
636 /** @todo Copy void* buffer contents! */
637
638 Utf8Str errMsg;
639
640 /* Was progress canceled before? */
641 BOOL fCanceled;
642 ComAssert(!it->second.pProgress.isNull());
643 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled))
644 && !fCanceled)
645 {
646 /* Do progress handling. */
647 HRESULT hr;
648 switch (pData->u32Status)
649 {
650 case PROC_STS_STARTED:
651 LogRel(("Guest process (PID %u) started\n", pCBData->u32PID)); /** @todo Add process name */
652 hr = it->second.pProgress->SetNextOperation(Bstr(tr("Waiting for process to exit ...")).raw(), 1 /* Weight */);
653 AssertComRC(hr);
654 break;
655
656 case PROC_STS_TEN: /* Terminated normally. */
657 LogRel(("Guest process (PID %u) exited normally\n", pCBData->u32PID)); /** @todo Add process name */
658 hr = it->second.pProgress->notifyComplete(S_OK);
659 AssertComRC(hr);
660 LogFlowFunc(("Proccess (context ID=%u, status=%u) terminated successfully\n",
661 pData->hdr.u32ContextID, pData->u32Status));
662 break;
663
664 case PROC_STS_TEA: /* Terminated abnormally. */
665 LogRel(("Guest process (PID %u) terminated abnormally with exit code = %u\n",
666 pCBData->u32PID, pCBData->u32Flags)); /** @todo Add process name */
667 errMsg = Utf8StrFmt(Guest::tr("Process terminated abnormally with status '%u'"),
668 pCBData->u32Flags);
669 break;
670
671 case PROC_STS_TES: /* Terminated through signal. */
672 LogRel(("Guest process (PID %u) terminated through signal with exit code = %u\n",
673 pCBData->u32PID, pCBData->u32Flags)); /** @todo Add process name */
674 errMsg = Utf8StrFmt(Guest::tr("Process terminated via signal with status '%u'"),
675 pCBData->u32Flags);
676 break;
677
678 case PROC_STS_TOK:
679 LogRel(("Guest process (PID %u) timed out and was killed\n", pCBData->u32PID)); /** @todo Add process name */
680 errMsg = Utf8StrFmt(Guest::tr("Process timed out and was killed"));
681 break;
682
683 case PROC_STS_TOA:
684 LogRel(("Guest process (PID %u) timed out and could not be killed\n", pCBData->u32PID)); /** @todo Add process name */
685 errMsg = Utf8StrFmt(Guest::tr("Process timed out and could not be killed"));
686 break;
687
688 case PROC_STS_DWN:
689 LogRel(("Guest process (PID %u) killed because system is shutting down\n", pCBData->u32PID)); /** @todo Add process name */
690 /*
691 * If u32Flags has ExecuteProcessFlag_IgnoreOrphanedProcesses set, we don't report an error to
692 * our progress object. This is helpful for waiters which rely on the success of our progress object
693 * even if the executed process was killed because the system/VBoxService is shutting down.
694 *
695 * In this case u32Flags contains the actual execution flags reached in via Guest::ExecuteProcess().
696 */
697 if (pData->u32Flags & ExecuteProcessFlag_IgnoreOrphanedProcesses)
698 {
699 hr = it->second.pProgress->notifyComplete(S_OK);
700 AssertComRC(hr);
701 }
702 else
703 {
704 errMsg = Utf8StrFmt(Guest::tr("Process killed because system is shutting down"));
705 }
706 break;
707
708 case PROC_STS_ERROR:
709 LogRel(("Guest process (PID %u) could not be started because of rc=%Rrc\n",
710 pCBData->u32PID, pCBData->u32Flags)); /** @todo Add process name */
711 errMsg = Utf8StrFmt(Guest::tr("Process execution failed with rc=%Rrc"), pCBData->u32Flags);
712 break;
713
714 default:
715 vrc = VERR_INVALID_PARAMETER;
716 break;
717 }
718
719 /* Handle process map. */
720 /** @todo What happens on/deal with PID reuse? */
721 /** @todo How to deal with multiple updates at once? */
722 if (pCBData->u32PID > 0)
723 {
724 GuestProcessMapIter it_proc = getProcessByPID(pCBData->u32PID);
725 if (it_proc == mGuestProcessMap.end())
726 {
727 /* Not found, add to map. */
728 GuestProcess newProcess;
729 newProcess.mStatus = pCBData->u32Status;
730 newProcess.mExitCode = pCBData->u32Flags; /* Contains exit code. */
731 newProcess.mFlags = 0;
732
733 mGuestProcessMap[pCBData->u32PID] = newProcess;
734 }
735 else /* Update map. */
736 {
737 it_proc->second.mStatus = pCBData->u32Status;
738 it_proc->second.mExitCode = pCBData->u32Flags; /* Contains exit code. */
739 it_proc->second.mFlags = 0;
740 }
741 }
742 }
743 else
744 errMsg = Utf8StrFmt(Guest::tr("Process execution canceled"));
745
746 if (!it->second.pProgress->getCompleted())
747 {
748 if ( errMsg.length()
749 || fCanceled) /* If canceled we have to report E_FAIL! */
750 {
751 /* Destroy all callbacks which are still waiting on something
752 * which is related to the current PID. */
753 CallbackMapIter it2;
754 for (it2 = mCallbackMap.begin(); it2 != mCallbackMap.end(); it2++)
755 {
756 switch (it2->second.mType)
757 {
758 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_START:
759 break;
760
761 /* When waiting for process output while the process is destroyed,
762 * make sure we also destroy the actual waiting operation (internal progress object)
763 * in order to not block the caller. */
764 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT:
765 {
766 PCALLBACKDATAEXECOUT pItData = (CALLBACKDATAEXECOUT*)it2->second.pvData;
767 AssertPtr(pItData);
768 if (pItData->u32PID == pCBData->u32PID)
769 destroyCtrlCallbackContext(it2);
770 break;
771 }
772
773 default:
774 AssertMsgFailed(("unknown callback type %d\n", it2->second.mType));
775 break;
776 }
777 }
778
779 HRESULT hr2 = it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,
780 COM_IIDOF(IGuest),
781 Guest::getStaticComponentName(),
782 "%s", errMsg.c_str());
783 AssertComRC(hr2);
784 LogFlowFunc(("Process (context ID=%u, status=%u) reported error: %s\n",
785 pData->hdr.u32ContextID, pData->u32Status, errMsg.c_str()));
786 }
787 }
788 }
789 else
790 LogFlowFunc(("Unexpected callback (magic=%u, context ID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));
791 LogFlowFunc(("Returned with rc=%Rrc\n", vrc));
792 return vrc;
793}
794
795/* Function for handling the execution output notification. */
796int Guest::notifyCtrlExecOut(uint32_t u32Function,
797 PCALLBACKDATAEXECOUT pData)
798{
799 int rc = VINF_SUCCESS;
800
801 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
802
803 AssertPtr(pData);
804 CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
805 if (it != mCallbackMap.end())
806 {
807 PCALLBACKDATAEXECOUT pCBData = (CALLBACKDATAEXECOUT*)it->second.pvData;
808 AssertPtr(pCBData);
809
810 pCBData->u32PID = pData->u32PID;
811 pCBData->u32HandleId = pData->u32HandleId;
812 pCBData->u32Flags = pData->u32Flags;
813
814 /* Make sure we really got something! */
815 if ( pData->cbData
816 && pData->pvData)
817 {
818 /* Allocate data buffer and copy it */
819 pCBData->pvData = RTMemAlloc(pData->cbData);
820 pCBData->cbData = pData->cbData;
821
822 AssertReturn(pCBData->pvData, VERR_NO_MEMORY);
823 memcpy(pCBData->pvData, pData->pvData, pData->cbData);
824 }
825 else
826 {
827 pCBData->pvData = NULL;
828 pCBData->cbData = 0;
829 }
830
831 /* Was progress canceled before? */
832 BOOL fCanceled;
833 ComAssert(!it->second.pProgress.isNull());
834 if (SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled)) && fCanceled)
835 {
836 it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,
837 COM_IIDOF(IGuest),
838 Guest::getStaticComponentName(),
839 Guest::tr("The output operation was canceled"));
840 }
841 else
842 it->second.pProgress->notifyComplete(S_OK);
843 }
844 else
845 LogFlowFunc(("Unexpected callback (magic=%u, context ID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));
846 return rc;
847}
848
849int Guest::notifyCtrlClientDisconnected(uint32_t u32Function,
850 PCALLBACKDATACLIENTDISCONNECTED pData)
851{
852 int rc = VINF_SUCCESS;
853
854 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
855 CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
856 if (it != mCallbackMap.end())
857 {
858 LogFlowFunc(("Client with context ID=%u disconnected\n", it->first));
859 destroyCtrlCallbackContext(it);
860 }
861 return rc;
862}
863
864Guest::CallbackMapIter Guest::getCtrlCallbackContextByID(uint32_t u32ContextID)
865{
866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
867 return mCallbackMap.find(u32ContextID);
868}
869
870Guest::GuestProcessMapIter Guest::getProcessByPID(uint32_t u32PID)
871{
872 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
873 return mGuestProcessMap.find(u32PID);
874}
875
876/* No locking here; */
877void Guest::destroyCtrlCallbackContext(Guest::CallbackMapIter it)
878{
879 LogFlowFunc(("Destroying callback with CID=%u ...\n", it->first));
880
881 if (it->second.pvData)
882 {
883 RTMemFree(it->second.pvData);
884 it->second.pvData = NULL;
885 it->second.cbData = 0;
886 }
887
888 /* Notify outstanding waits for progress ... */
889 if ( it->second.pProgress
890 && !it->second.pProgress.isNull())
891 {
892 LogFlowFunc(("Handling progress for CID=%u ...\n", it->first));
893
894 /*
895 * Assume we didn't complete to make sure we clean up even if the
896 * following call fails.
897 */
898 BOOL fCompleted = FALSE;
899 it->second.pProgress->COMGETTER(Completed)(&fCompleted);
900 if (!fCompleted)
901 {
902 LogFlowFunc(("Progress of CID=%u *not* completed, cancelling ...\n", it->first));
903
904 /* Only cancel if not canceled before! */
905 BOOL fCanceled;
906 if (SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled)) && !fCanceled)
907 it->second.pProgress->Cancel();
908
909 /*
910 * To get waitForCompletion completed (unblocked) we have to notify it if necessary (only
911 * cancle won't work!). This could happen if the client thread (e.g. VBoxService, thread of a spawned process)
912 * is disconnecting without having the chance to sending a status message before, so we
913 * have to abort here to make sure the host never hangs/gets stuck while waiting for the
914 * progress object to become signalled.
915 */
916 it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,
917 COM_IIDOF(IGuest),
918 Guest::getStaticComponentName(),
919 Guest::tr("The operation was canceled because client is shutting down"));
920 }
921 /*
922 * Do *not* NULL pProgress here, because waiting function like executeProcess()
923 * will still rely on this object for checking whether they have to give up!
924 */
925 }
926}
927
928/* Adds a callback with a user provided data block and an optional progress object
929 * to the callback map. A callback is identified by a unique context ID which is used
930 * to identify a callback from the guest side. */
931uint32_t Guest::addCtrlCallbackContext(eVBoxGuestCtrlCallbackType enmType, void *pvData, uint32_t cbData, Progress *pProgress)
932{
933 AssertPtr(pProgress);
934
935 /** @todo Put this stuff into a constructor! */
936 CallbackContext context;
937 context.mType = enmType;
938 context.pvData = pvData;
939 context.cbData = cbData;
940 context.pProgress = pProgress;
941
942 /* Create a new context ID and assign it. */
943 CallbackMapIter it;
944 uint32_t uNewContext = 0;
945 do
946 {
947 /* Create a new context ID ... */
948 uNewContext = ASMAtomicIncU32(&mNextContextID);
949 if (uNewContext == UINT32_MAX)
950 ASMAtomicUoWriteU32(&mNextContextID, 1000);
951 /* Is the context ID already used? */
952 it = getCtrlCallbackContextByID(uNewContext);
953 } while(it != mCallbackMap.end());
954
955 uint32_t nCallbacks = 0;
956 if ( it == mCallbackMap.end()
957 && uNewContext > 0)
958 {
959 /* We apparently got an unused context ID, let's use it! */
960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
961 mCallbackMap[uNewContext] = context;
962 nCallbacks = mCallbackMap.size();
963 }
964 else
965 {
966 /* Should never happen ... */
967 {
968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
969 nCallbacks = mCallbackMap.size();
970 }
971 AssertReleaseMsg(uNewContext, ("No free context ID found! uNewContext=%u, nCallbacks=%u", uNewContext, nCallbacks));
972 }
973
974#if 0
975 if (nCallbacks > 256) /* Don't let the container size get too big! */
976 {
977 Guest::CallbackListIter it = mCallbackList.begin();
978 destroyCtrlCallbackContext(it);
979 {
980 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
981 mCallbackList.erase(it);
982 }
983 }
984#endif
985 return uNewContext;
986}
987#endif /* VBOX_WITH_GUEST_CONTROL */
988
989STDMETHODIMP Guest::ExecuteProcess(IN_BSTR aCommand, ULONG aFlags,
990 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
991 IN_BSTR aUserName, IN_BSTR aPassword,
992 ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress)
993{
994/** @todo r=bird: Eventually we should clean up all the timeout parameters
995 * in the API and have the same way of specifying infinite waits! */
996#ifndef VBOX_WITH_GUEST_CONTROL
997 ReturnComNotImplemented();
998#else /* VBOX_WITH_GUEST_CONTROL */
999 using namespace guestControl;
1000
1001 CheckComArgStrNotEmptyOrNull(aCommand);
1002 CheckComArgOutPointerValid(aPID);
1003 CheckComArgOutPointerValid(aProgress);
1004
1005 /* Do not allow anonymous executions (with system rights). */
1006 if (RT_UNLIKELY((aUserName) == NULL || *(aUserName) == '\0'))
1007 return setError(E_INVALIDARG, tr("No user name specified"));
1008
1009 AutoCaller autoCaller(this);
1010 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1011
1012 /* Validate flags. */
1013 if (aFlags)
1014 {
1015 if (!(aFlags & ExecuteProcessFlag_IgnoreOrphanedProcesses))
1016 return E_INVALIDARG;
1017 }
1018
1019 HRESULT rc = S_OK;
1020
1021 try
1022 {
1023 /*
1024 * Create progress object. Note that this is a multi operation
1025 * object to perform the following steps:
1026 * - Operation 1 (0): Create/start process.
1027 * - Operation 2 (1): Wait for process to exit.
1028 * If this progress completed successfully (S_OK), the process
1029 * started and exited normally. In any other case an error/exception
1030 * occured.
1031 */
1032 ComObjPtr <Progress> progress;
1033 rc = progress.createObject();
1034 if (SUCCEEDED(rc))
1035 {
1036 rc = progress->init(static_cast<IGuest*>(this),
1037 Bstr(tr("Executing process")).raw(),
1038 TRUE,
1039 2, /* Number of operations. */
1040 Bstr(tr("Starting process ...")).raw()); /* Description of first stage. */
1041 }
1042 if (FAILED(rc)) return rc;
1043
1044 /*
1045 * Prepare process execution.
1046 */
1047 int vrc = VINF_SUCCESS;
1048 Utf8Str Utf8Command(aCommand);
1049
1050 /* Adjust timeout */
1051 if (aTimeoutMS == 0)
1052 aTimeoutMS = UINT32_MAX;
1053
1054 /* Prepare arguments. */
1055 char **papszArgv = NULL;
1056 uint32_t uNumArgs = 0;
1057 if (aArguments > 0)
1058 {
1059 com::SafeArray<IN_BSTR> args(ComSafeArrayInArg(aArguments));
1060 uNumArgs = args.size();
1061 papszArgv = (char**)RTMemAlloc(sizeof(char*) * (uNumArgs + 1));
1062 AssertReturn(papszArgv, E_OUTOFMEMORY);
1063 for (unsigned i = 0; RT_SUCCESS(vrc) && i < uNumArgs; i++)
1064 vrc = RTUtf16ToUtf8(args[i], &papszArgv[i]);
1065 papszArgv[uNumArgs] = NULL;
1066 }
1067
1068 Utf8Str Utf8UserName(aUserName);
1069 Utf8Str Utf8Password(aPassword);
1070 if (RT_SUCCESS(vrc))
1071 {
1072 uint32_t uContextID = 0;
1073
1074 char *pszArgs = NULL;
1075 if (uNumArgs > 0)
1076 vrc = RTGetOptArgvToString(&pszArgs, papszArgv, 0);
1077 if (RT_SUCCESS(vrc))
1078 {
1079 uint32_t cbArgs = pszArgs ? strlen(pszArgs) + 1 : 0; /* Include terminating zero. */
1080
1081 /* Prepare environment. */
1082 void *pvEnv = NULL;
1083 uint32_t uNumEnv = 0;
1084 uint32_t cbEnv = 0;
1085 if (aEnvironment > 0)
1086 {
1087 com::SafeArray<IN_BSTR> env(ComSafeArrayInArg(aEnvironment));
1088
1089 for (unsigned i = 0; i < env.size(); i++)
1090 {
1091 vrc = prepareExecuteEnv(Utf8Str(env[i]).c_str(), &pvEnv, &cbEnv, &uNumEnv);
1092 if (RT_FAILURE(vrc))
1093 break;
1094 }
1095 }
1096
1097 LogRel(("Executing guest process \"%s\" as user \"%s\" ...\n",
1098 Utf8Command.c_str(), Utf8UserName.c_str()));
1099
1100 if (RT_SUCCESS(vrc))
1101 {
1102 PCALLBACKDATAEXECSTATUS pData = (PCALLBACKDATAEXECSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECSTATUS));
1103 AssertReturn(pData, VBOX_E_IPRT_ERROR);
1104 RT_ZERO(*pData);
1105 uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_START,
1106 pData, sizeof(CALLBACKDATAEXECSTATUS), progress);
1107 Assert(uContextID > 0);
1108
1109 VBOXHGCMSVCPARM paParms[15];
1110 int i = 0;
1111 paParms[i++].setUInt32(uContextID);
1112 paParms[i++].setPointer((void*)Utf8Command.c_str(), (uint32_t)Utf8Command.length() + 1);
1113 paParms[i++].setUInt32(aFlags);
1114 paParms[i++].setUInt32(uNumArgs);
1115 paParms[i++].setPointer((void*)pszArgs, cbArgs);
1116 paParms[i++].setUInt32(uNumEnv);
1117 paParms[i++].setUInt32(cbEnv);
1118 paParms[i++].setPointer((void*)pvEnv, cbEnv);
1119 paParms[i++].setPointer((void*)Utf8UserName.c_str(), (uint32_t)Utf8UserName.length() + 1);
1120 paParms[i++].setPointer((void*)Utf8Password.c_str(), (uint32_t)Utf8Password.length() + 1);
1121 paParms[i++].setUInt32(aTimeoutMS);
1122
1123 VMMDev *vmmDev;
1124 {
1125 /* Make sure mParent is valid, so set the read lock while using.
1126 * Do not keep this lock while doing the actual call, because in the meanwhile
1127 * another thread could request a write lock which would be a bad idea ... */
1128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1129
1130 /* Forward the information to the VMM device. */
1131 AssertPtr(mParent);
1132 vmmDev = mParent->getVMMDev();
1133 }
1134
1135 if (vmmDev)
1136 {
1137 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
1138 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_CMD,
1139 i, paParms);
1140 }
1141 else
1142 vrc = VERR_INVALID_VM_HANDLE;
1143 RTMemFree(pvEnv);
1144 }
1145 RTStrFree(pszArgs);
1146 }
1147 if (RT_SUCCESS(vrc))
1148 {
1149 LogFlowFunc(("Waiting for HGCM callback (timeout=%ldms) ...\n", aTimeoutMS));
1150
1151 /*
1152 * Wait for the HGCM low level callback until the process
1153 * has been started (or something went wrong). This is necessary to
1154 * get the PID.
1155 */
1156 CallbackMapIter it = getCtrlCallbackContextByID(uContextID);
1157 BOOL fCanceled = FALSE;
1158 if (it != mCallbackMap.end())
1159 {
1160 ComAssert(!it->second.pProgress.isNull());
1161
1162 /*
1163 * Wait for the first stage (=0) to complete (that is starting the process).
1164 */
1165 PCALLBACKDATAEXECSTATUS pData = NULL;
1166 rc = it->second.pProgress->WaitForOperationCompletion(0, aTimeoutMS);
1167 if (SUCCEEDED(rc))
1168 {
1169 /* Was the operation canceled by one of the parties? */
1170 rc = it->second.pProgress->COMGETTER(Canceled)(&fCanceled);
1171 if (FAILED(rc)) throw rc;
1172
1173 if (!fCanceled)
1174 {
1175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1176
1177 pData = (PCALLBACKDATAEXECSTATUS)it->second.pvData;
1178 Assert(it->second.cbData == sizeof(CALLBACKDATAEXECSTATUS));
1179 AssertPtr(pData);
1180
1181 /* Did we get some status? */
1182 switch (pData->u32Status)
1183 {
1184 case PROC_STS_STARTED:
1185 /* Process is (still) running; get PID. */
1186 *aPID = pData->u32PID;
1187 break;
1188
1189 /* In any other case the process either already
1190 * terminated or something else went wrong, so no PID ... */
1191 case PROC_STS_TEN: /* Terminated normally. */
1192 case PROC_STS_TEA: /* Terminated abnormally. */
1193 case PROC_STS_TES: /* Terminated through signal. */
1194 case PROC_STS_TOK:
1195 case PROC_STS_TOA:
1196 case PROC_STS_DWN:
1197 /*
1198 * Process (already) ended, but we want to get the
1199 * PID anyway to retrieve the output in a later call.
1200 */
1201 *aPID = pData->u32PID;
1202 break;
1203
1204 case PROC_STS_ERROR:
1205 vrc = pData->u32Flags; /* u32Flags member contains IPRT error code. */
1206 break;
1207
1208 case PROC_STS_UNDEFINED:
1209 vrc = VERR_TIMEOUT; /* Operation did not complete within time. */
1210 break;
1211
1212 default:
1213 vrc = VERR_INVALID_PARAMETER; /* Unknown status, should never happen! */
1214 break;
1215 }
1216 }
1217 else /* Operation was canceled. */
1218 vrc = VERR_CANCELLED;
1219 }
1220 else /* Operation did not complete within time. */
1221 vrc = VERR_TIMEOUT;
1222
1223 /*
1224 * Do *not* remove the callback yet - we might wait with the IProgress object on something
1225 * else (like end of process) ...
1226 */
1227 if (RT_FAILURE(vrc))
1228 {
1229 if (vrc == VERR_FILE_NOT_FOUND) /* This is the most likely error. */
1230 rc = setError(VBOX_E_IPRT_ERROR,
1231 tr("The file '%s' was not found on guest"), Utf8Command.c_str());
1232 else if (vrc == VERR_PATH_NOT_FOUND)
1233 rc = setError(VBOX_E_IPRT_ERROR,
1234 tr("The path to file '%s' was not found on guest"), Utf8Command.c_str());
1235 else if (vrc == VERR_BAD_EXE_FORMAT)
1236 rc = setError(VBOX_E_IPRT_ERROR,
1237 tr("The file '%s' is not an executable format on guest"), Utf8Command.c_str());
1238 else if (vrc == VERR_AUTHENTICATION_FAILURE)
1239 rc = setError(VBOX_E_IPRT_ERROR,
1240 tr("The specified user '%s' was not able to logon on guest"), Utf8UserName.c_str());
1241 else if (vrc == VERR_TIMEOUT)
1242 rc = setError(VBOX_E_IPRT_ERROR,
1243 tr("The guest did not respond within time (%ums)"), aTimeoutMS);
1244 else if (vrc == VERR_CANCELLED)
1245 rc = setError(VBOX_E_IPRT_ERROR,
1246 tr("The execution operation was canceled"));
1247 else if (vrc == VERR_PERMISSION_DENIED)
1248 rc = setError(VBOX_E_IPRT_ERROR,
1249 tr("Invalid user/password credentials"));
1250 else
1251 {
1252 if (pData && pData->u32Status == PROC_STS_ERROR)
1253 rc = setError(VBOX_E_IPRT_ERROR,
1254 tr("Process could not be started: %Rrc"), pData->u32Flags);
1255 else
1256 rc = setError(E_UNEXPECTED,
1257 tr("The service call failed with error %Rrc"), vrc);
1258 }
1259 }
1260 else /* Execution went fine. */
1261 {
1262 /* Return the progress to the caller. */
1263 progress.queryInterfaceTo(aProgress);
1264 }
1265 }
1266 else /* Callback context not found; should never happen! */
1267 AssertMsg(it != mCallbackMap.end(), ("Callback context with ID %u not found!", uContextID));
1268 }
1269 else /* HGCM related error codes .*/
1270 {
1271 if (vrc == VERR_INVALID_VM_HANDLE)
1272 rc = setError(VBOX_E_VM_ERROR,
1273 tr("VMM device is not available (is the VM running?)"));
1274 else if (vrc == VERR_TIMEOUT)
1275 rc = setError(VBOX_E_VM_ERROR,
1276 tr("The guest execution service is not ready"));
1277 else if (vrc == VERR_HGCM_SERVICE_NOT_FOUND)
1278 rc = setError(VBOX_E_VM_ERROR,
1279 tr("The guest execution service is not available"));
1280 else /* HGCM call went wrong. */
1281 rc = setError(E_UNEXPECTED,
1282 tr("The HGCM call failed with error %Rrc"), vrc);
1283 }
1284
1285 for (unsigned i = 0; i < uNumArgs; i++)
1286 RTMemFree(papszArgv[i]);
1287 RTMemFree(papszArgv);
1288 }
1289
1290 if (RT_FAILURE(vrc))
1291 LogRel(("Executing guest process \"%s\" as user \"%s\" failed with %Rrc\n",
1292 Utf8Command.c_str(), Utf8UserName.c_str(), vrc));
1293 }
1294 catch (std::bad_alloc &)
1295 {
1296 rc = E_OUTOFMEMORY;
1297 }
1298 return rc;
1299#endif /* VBOX_WITH_GUEST_CONTROL */
1300}
1301
1302STDMETHODIMP Guest::GetProcessOutput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
1303{
1304/** @todo r=bird: Eventually we should clean up all the timeout parameters
1305 * in the API and have the same way of specifying infinite waits! */
1306#ifndef VBOX_WITH_GUEST_CONTROL
1307 ReturnComNotImplemented();
1308#else /* VBOX_WITH_GUEST_CONTROL */
1309 using namespace guestControl;
1310
1311 CheckComArgExpr(aPID, aPID > 0);
1312 if (aSize < 0)
1313 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
1314 if (aFlags != 0) /* Flags are not supported at the moment. */
1315 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
1316
1317 AutoCaller autoCaller(this);
1318 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1319
1320 HRESULT rc = S_OK;
1321
1322 try
1323 {
1324 /*
1325 * Create progress object.
1326 * This progress object, compared to the one in executeProgress() above
1327 * is only local and is used to determine whether the operation finished
1328 * or got canceled.
1329 */
1330 ComObjPtr <Progress> progress;
1331 rc = progress.createObject();
1332 if (SUCCEEDED(rc))
1333 {
1334 rc = progress->init(static_cast<IGuest*>(this),
1335 Bstr(tr("Getting output of process")).raw(),
1336 TRUE);
1337 }
1338 if (FAILED(rc)) return rc;
1339
1340 /* Adjust timeout */
1341 if (aTimeoutMS == 0)
1342 aTimeoutMS = UINT32_MAX;
1343
1344 /* Search for existing PID. */
1345 PCALLBACKDATAEXECOUT pData = (CALLBACKDATAEXECOUT*)RTMemAlloc(sizeof(CALLBACKDATAEXECOUT));
1346 AssertReturn(pData, VBOX_E_IPRT_ERROR);
1347 RT_ZERO(*pData);
1348 /* Save PID + output flags for later use. */
1349 pData->u32PID = aPID;
1350 pData->u32Flags = aFlags;
1351 /* Add job to callback contexts. */
1352 uint32_t uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT,
1353 pData, sizeof(CALLBACKDATAEXECOUT), progress);
1354 Assert(uContextID > 0);
1355
1356 size_t cbData = (size_t)RT_MIN(aSize, _64K);
1357 com::SafeArray<BYTE> outputData(cbData);
1358
1359 VBOXHGCMSVCPARM paParms[5];
1360 int i = 0;
1361 paParms[i++].setUInt32(uContextID);
1362 paParms[i++].setUInt32(aPID);
1363 paParms[i++].setUInt32(aFlags); /** @todo Should represent stdout and/or stderr. */
1364
1365 int vrc = VINF_SUCCESS;
1366
1367 {
1368 VMMDev *vmmDev;
1369 {
1370 /* Make sure mParent is valid, so set the read lock while using.
1371 * Do not keep this lock while doing the actual call, because in the meanwhile
1372 * another thread could request a write lock which would be a bad idea ... */
1373 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1374
1375 /* Forward the information to the VMM device. */
1376 AssertPtr(mParent);
1377 vmmDev = mParent->getVMMDev();
1378 }
1379
1380 if (vmmDev)
1381 {
1382 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
1383 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_GET_OUTPUT,
1384 i, paParms);
1385 }
1386 }
1387
1388 if (RT_SUCCESS(vrc))
1389 {
1390 LogFlowFunc(("Waiting for HGCM callback (timeout=%ldms) ...\n", aTimeoutMS));
1391
1392 /*
1393 * Wait for the HGCM low level callback until the process
1394 * has been started (or something went wrong). This is necessary to
1395 * get the PID.
1396 */
1397 CallbackMapIter it = getCtrlCallbackContextByID(uContextID);
1398 BOOL fCanceled = FALSE;
1399 if (it != mCallbackMap.end())
1400 {
1401 ComAssert(!it->second.pProgress.isNull());
1402
1403 /* Wait until operation completed. */
1404 rc = it->second.pProgress->WaitForCompletion(aTimeoutMS);
1405 if (FAILED(rc)) throw rc;
1406
1407 /* Was the operation canceled by one of the parties? */
1408 rc = it->second.pProgress->COMGETTER(Canceled)(&fCanceled);
1409 if (FAILED(rc)) throw rc;
1410
1411 if (!fCanceled)
1412 {
1413 BOOL fCompleted;
1414 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))
1415 && fCompleted)
1416 {
1417 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1418
1419 /* Did we get some output? */
1420 pData = (PCALLBACKDATAEXECOUT)it->second.pvData;
1421 Assert(it->second.cbData == sizeof(CALLBACKDATAEXECOUT));
1422 AssertPtr(pData);
1423
1424 if (pData->cbData)
1425 {
1426 /* Do we need to resize the array? */
1427 if (pData->cbData > cbData)
1428 outputData.resize(pData->cbData);
1429
1430 /* Fill output in supplied out buffer. */
1431 memcpy(outputData.raw(), pData->pvData, pData->cbData);
1432 outputData.resize(pData->cbData); /* Shrink to fit actual buffer size. */
1433 }
1434 else
1435 vrc = VERR_NO_DATA; /* This is not an error we want to report to COM. */
1436 }
1437 else /* If callback not called within time ... well, that's a timeout! */
1438 vrc = VERR_TIMEOUT;
1439 }
1440 else /* Operation was canceled. */
1441 {
1442 vrc = VERR_CANCELLED;
1443 }
1444
1445 if (RT_FAILURE(vrc))
1446 {
1447 if (vrc == VERR_NO_DATA)
1448 {
1449 /* This is not an error we want to report to COM. */
1450 rc = S_OK;
1451 }
1452 else if (vrc == VERR_TIMEOUT)
1453 {
1454 rc = setError(VBOX_E_IPRT_ERROR,
1455 tr("The guest did not output within time (%ums)"), aTimeoutMS);
1456 }
1457 else if (vrc == VERR_CANCELLED)
1458 {
1459 rc = setError(VBOX_E_IPRT_ERROR,
1460 tr("The output operation was canceled"));
1461 }
1462 else
1463 {
1464 rc = setError(E_UNEXPECTED,
1465 tr("The service call failed with error %Rrc"), vrc);
1466 }
1467 }
1468
1469 {
1470 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1471 /*
1472 * Destroy locally used progress object.
1473 */
1474 destroyCtrlCallbackContext(it);
1475 }
1476
1477 /* Remove callback context (not used anymore). */
1478 mCallbackMap.erase(it);
1479 }
1480 else /* PID lookup failed. */
1481 rc = setError(VBOX_E_IPRT_ERROR,
1482 tr("Process (PID %u) not found!"), aPID);
1483 }
1484 else /* HGCM operation failed. */
1485 rc = setError(E_UNEXPECTED,
1486 tr("The HGCM call failed with error %Rrc"), vrc);
1487
1488 /* Cleanup. */
1489 progress->uninit();
1490 progress.setNull();
1491
1492 /* If something failed (or there simply was no data, indicated by VERR_NO_DATA,
1493 * we return an empty array so that the frontend knows when to give up. */
1494 if (RT_FAILURE(vrc) || FAILED(rc))
1495 outputData.resize(0);
1496 outputData.detachTo(ComSafeArrayOutArg(aData));
1497 }
1498 catch (std::bad_alloc &)
1499 {
1500 rc = E_OUTOFMEMORY;
1501 }
1502 return rc;
1503#endif
1504}
1505
1506STDMETHODIMP Guest::GetProcessStatus(ULONG aPID, ULONG *aExitCode, ULONG *aFlags, ULONG *aStatus)
1507{
1508#ifndef VBOX_WITH_GUEST_CONTROL
1509 ReturnComNotImplemented();
1510#else /* VBOX_WITH_GUEST_CONTROL */
1511 using namespace guestControl;
1512
1513 AutoCaller autoCaller(this);
1514 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1515
1516 HRESULT rc = S_OK;
1517
1518 try
1519 {
1520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1521
1522 GuestProcessMapIterConst it = getProcessByPID(aPID);
1523 if (it != mGuestProcessMap.end())
1524 {
1525 *aExitCode = it->second.mExitCode;
1526 *aFlags = it->second.mFlags;
1527 *aStatus = it->second.mStatus;
1528 }
1529 else
1530 rc = setError(VBOX_E_IPRT_ERROR,
1531 tr("Process (PID %u) not found!"), aPID);
1532 }
1533 catch (std::bad_alloc &)
1534 {
1535 rc = E_OUTOFMEMORY;
1536 }
1537 return rc;
1538#endif
1539}
1540
1541// public methods only for internal purposes
1542/////////////////////////////////////////////////////////////////////////////
1543
1544/**
1545 * Sets the general Guest Additions information like
1546 * API (interface) version and OS type. Gets called by
1547 * vmmdevUpdateGuestInfo.
1548 *
1549 * @param aInterfaceVersion
1550 * @param aOsType
1551 */
1552void Guest::setAdditionsInfo(Bstr aInterfaceVersion, VBOXOSTYPE aOsType)
1553{
1554 AutoCaller autoCaller(this);
1555 AssertComRCReturnVoid (autoCaller.rc());
1556
1557 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1558
1559 /*
1560 * Note: The Guest Additions API (interface) version is deprecated
1561 * and will not be used anymore! We might need it to at least report
1562 * something as version number if *really* ancient Guest Additions are
1563 * installed (without the guest version + revision properties having set).
1564 */
1565 mData.mInterfaceVersion = aInterfaceVersion;
1566
1567 /*
1568 * Older Additions rely on the Additions API version whether they
1569 * are assumed to be active or not. Since newer Additions do report
1570 * the Additions version *before* calling this function (by calling
1571 * VMMDevReportGuestInfo2, VMMDevReportGuestStatus, VMMDevReportGuestInfo,
1572 * in that order) we can tell apart old and new Additions here. Old
1573 * Additions never would set VMMDevReportGuestInfo2 (which set mData.mAdditionsVersion)
1574 * so they just rely on the aInterfaceVersion string (which gets set by
1575 * VMMDevReportGuestInfo).
1576 *
1577 * So only mark the Additions as being active (run level = system) when we
1578 * don't have the Additions version set.
1579 */
1580 if (mData.mAdditionsVersion.isEmpty())
1581 {
1582 if (aInterfaceVersion.isEmpty())
1583 mData.mAdditionsRunLevel = AdditionsRunLevelType_None;
1584 else
1585 mData.mAdditionsRunLevel = AdditionsRunLevelType_System;
1586 }
1587
1588 /*
1589 * Older Additions didn't have this finer grained capability bit,
1590 * so enable it by default. Newer Additions will not enable this here
1591 * and use the setSupportedFeatures function instead.
1592 */
1593 mData.mSupportsGraphics = mData.mAdditionsRunLevel > AdditionsRunLevelType_None;
1594
1595 /*
1596 * Note! There is a race going on between setting mAdditionsRunLevel and
1597 * mSupportsGraphics here and disabling/enabling it later according to
1598 * its real status when using new(er) Guest Additions.
1599 */
1600 mData.mOSTypeId = Global::OSTypeId (aOsType);
1601}
1602
1603/**
1604 * Sets the Guest Additions version information details.
1605 * Gets called by vmmdevUpdateGuestInfo2.
1606 *
1607 * @param aAdditionsVersion
1608 * @param aVersionName
1609 */
1610void Guest::setAdditionsInfo2(Bstr aAdditionsVersion, Bstr aVersionName, Bstr aRevision)
1611{
1612 AutoCaller autoCaller(this);
1613 AssertComRCReturnVoid (autoCaller.rc());
1614
1615 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1616
1617 if (!aVersionName.isEmpty())
1618 /*
1619 * aVersionName could be "x.y.z_BETA1_FOOBAR", so append revision manually to
1620 * become "x.y.z_BETA1_FOOBARr12345".
1621 */
1622 mData.mAdditionsVersion = BstrFmt("%ls r%ls", aVersionName.raw(), aRevision.raw());
1623 else /* aAdditionsVersion is in x.y.zr12345 format. */
1624 mData.mAdditionsVersion = aAdditionsVersion;
1625}
1626
1627/**
1628 * Sets the status of a certain Guest Additions facility.
1629 * Gets called by vmmdevUpdateGuestStatus.
1630 *
1631 * @param Facility
1632 * @param Status
1633 * @param ulFlags
1634 */
1635void Guest::setAdditionsStatus(VBoxGuestStatusFacility Facility, VBoxGuestStatusCurrent Status, ULONG ulFlags)
1636{
1637 AutoCaller autoCaller(this);
1638 AssertComRCReturnVoid (autoCaller.rc());
1639
1640 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1641
1642 uint32_t uCurFacility = Facility + (Status == VBoxGuestStatusCurrent_Active ? 0 : -1);
1643
1644 /* First check for disabled status. */
1645 if ( Facility < VBoxGuestStatusFacility_VBoxGuestDriver
1646 || ( Facility == VBoxGuestStatusFacility_All
1647 && ( Status == VBoxGuestStatusCurrent_Inactive
1648 || Status == VBoxGuestStatusCurrent_Disabled
1649 )
1650 )
1651 )
1652 {
1653 mData.mAdditionsRunLevel = AdditionsRunLevelType_None;
1654 }
1655 else if (uCurFacility >= VBoxGuestStatusFacility_VBoxTray)
1656 {
1657 mData.mAdditionsRunLevel = AdditionsRunLevelType_Desktop;
1658 }
1659 else if (uCurFacility >= VBoxGuestStatusFacility_VBoxService)
1660 {
1661 mData.mAdditionsRunLevel = AdditionsRunLevelType_Userland;
1662 }
1663 else if (uCurFacility >= VBoxGuestStatusFacility_VBoxGuestDriver)
1664 {
1665 mData.mAdditionsRunLevel = AdditionsRunLevelType_System;
1666 }
1667 else /* Should never happen! */
1668 AssertMsgFailed(("Invalid facility status/run level detected! uCurFacility=%ld\n", uCurFacility));
1669}
1670
1671/**
1672 * Sets the supported features (and whether they are active or not).
1673 *
1674 * @param fCaps Guest capability bit mask (VMMDEV_GUEST_SUPPORTS_XXX).
1675 * @param fActive No idea what this is supposed to be, it's always 0 and
1676 * not references by this method.
1677 */
1678void Guest::setSupportedFeatures(uint32_t fCaps, uint32_t fActive)
1679{
1680 AutoCaller autoCaller(this);
1681 AssertComRCReturnVoid (autoCaller.rc());
1682
1683 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1684
1685 mData.mSupportsSeamless = (fCaps & VMMDEV_GUEST_SUPPORTS_SEAMLESS);
1686 /** @todo Add VMMDEV_GUEST_SUPPORTS_GUEST_HOST_WINDOW_MAPPING */
1687 mData.mSupportsGraphics = (fCaps & VMMDEV_GUEST_SUPPORTS_GRAPHICS);
1688}
1689/* vi: set tabstop=4 shiftwidth=4 expandtab: */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette