VirtualBox

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

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

GuestImpl.cpp: r=bird: Follow-up to r64502 adding two todos.

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