VirtualBox

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

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

please use a space between the version and the revision, looks better

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