VirtualBox

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

Last change on this file since 31890 was 31746, checked in by vboxsync, 14 years ago

Main: Added input validations missing in r64842.

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

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