VirtualBox

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

Last change on this file since 31164 was 30903, checked in by vboxsync, 14 years ago

Logging.

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