VirtualBox

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

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

Main: use settings struct for machine user data; remove iprt::MiniString::raw() and change all occurences to c_str()

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 58.5 KB
Line 
1/* $Id: GuestImpl.cpp 31539 2010-08-10 15:40:18Z 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).c_str(),
469 Utf8Str(aPassword).c_str(),
470 Utf8Str(aDomain).c_str(),
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]).c_str(), &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.c_str(), Utf8UserName.c_str()));
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.c_str(), (uint32_t)Utf8Command.length() + 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.c_str(), (uint32_t)Utf8UserName.length() + 1);
1051 paParms[i++].setPointer((void*)Utf8Password.c_str(), (uint32_t)Utf8Password.length() + 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 rc = setError(VBOX_E_IPRT_ERROR,
1162 tr("The file '%s' was not found on guest"), Utf8Command.c_str());
1163 else if (vrc == VERR_PATH_NOT_FOUND)
1164 rc = setError(VBOX_E_IPRT_ERROR,
1165 tr("The path to file '%s' was not found on guest"), Utf8Command.c_str());
1166 else if (vrc == VERR_BAD_EXE_FORMAT)
1167 rc = setError(VBOX_E_IPRT_ERROR,
1168 tr("The file '%s' is not an executable format on guest"), Utf8Command.c_str());
1169 else if (vrc == VERR_AUTHENTICATION_FAILURE)
1170 rc = setError(VBOX_E_IPRT_ERROR,
1171 tr("The specified user '%s' was not able to logon on guest"), Utf8UserName.c_str());
1172 else if (vrc == VERR_TIMEOUT)
1173 rc = setError(VBOX_E_IPRT_ERROR,
1174 tr("The guest did not respond within time (%ums)"), aTimeoutMS);
1175 else if (vrc == VERR_CANCELLED)
1176 rc = setError(VBOX_E_IPRT_ERROR,
1177 tr("The execution operation was canceled"));
1178 else if (vrc == VERR_PERMISSION_DENIED)
1179 rc = setError(VBOX_E_IPRT_ERROR,
1180 tr("Invalid user/password credentials"));
1181 else
1182 {
1183 if (pData && pData->u32Status == PROC_STS_ERROR)
1184 rc = setError(VBOX_E_IPRT_ERROR,
1185 tr("Process could not be started: %Rrc"), pData->u32Flags);
1186 else
1187 rc = setError(E_UNEXPECTED,
1188 tr("The service call failed with error %Rrc"), vrc);
1189 }
1190 }
1191 else /* Execution went fine. */
1192 {
1193 /* Return the progress to the caller. */
1194 progress.queryInterfaceTo(aProgress);
1195 }
1196 }
1197 else /* Callback context not found; should never happen! */
1198 AssertMsg(it != mCallbackMap.end(), ("Callback context with ID %u not found!", uContextID));
1199 }
1200 else /* HGCM related error codes .*/
1201 {
1202 if (vrc == VERR_INVALID_VM_HANDLE)
1203 rc = setError(VBOX_E_VM_ERROR,
1204 tr("VMM device is not available (is the VM running?)"));
1205 else if (vrc == VERR_TIMEOUT)
1206 rc = setError(VBOX_E_VM_ERROR,
1207 tr("The guest execution service is not ready"));
1208 else if (vrc == VERR_HGCM_SERVICE_NOT_FOUND)
1209 rc = setError(VBOX_E_VM_ERROR,
1210 tr("The guest execution service is not available"));
1211 else /* HGCM call went wrong. */
1212 rc = setError(E_UNEXPECTED,
1213 tr("The HGCM call failed with error %Rrc"), vrc);
1214 }
1215
1216 for (unsigned i = 0; i < uNumArgs; i++)
1217 RTMemFree(papszArgv[i]);
1218 RTMemFree(papszArgv);
1219 }
1220
1221 if (RT_FAILURE(vrc))
1222 LogRel(("Executing guest process \"%s\" as user \"%s\" failed with %Rrc\n",
1223 Utf8Command.c_str(), Utf8UserName.c_str(), vrc));
1224 }
1225 catch (std::bad_alloc &)
1226 {
1227 rc = E_OUTOFMEMORY;
1228 }
1229 return rc;
1230#endif /* VBOX_WITH_GUEST_CONTROL */
1231}
1232
1233STDMETHODIMP Guest::GetProcessOutput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, ULONG64 aSize, ComSafeArrayOut(BYTE, aData))
1234{
1235/** @todo r=bird: Eventually we should clean up all the timeout parameters
1236 * in the API and have the same way of specifying infinite waits! */
1237#ifndef VBOX_WITH_GUEST_CONTROL
1238 ReturnComNotImplemented();
1239#else /* VBOX_WITH_GUEST_CONTROL */
1240 using namespace guestControl;
1241
1242 CheckComArgExpr(aPID, aPID > 0);
1243
1244 if (aFlags != 0) /* Flags are not supported at the moment. */
1245 return E_INVALIDARG;
1246
1247 AutoCaller autoCaller(this);
1248 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1249
1250 HRESULT rc = S_OK;
1251
1252 try
1253 {
1254 /*
1255 * Create progress object.
1256 * This progress object, compared to the one in executeProgress() above,
1257 * is only local and is used to determine whether the operation finished
1258 * or got canceled.
1259 */
1260 ComObjPtr <Progress> progress;
1261 rc = progress.createObject();
1262 if (SUCCEEDED(rc))
1263 {
1264 rc = progress->init(static_cast<IGuest*>(this),
1265 BstrFmt(tr("Getting output of process")),
1266 TRUE);
1267 }
1268 if (FAILED(rc)) return rc;
1269
1270 /* Adjust timeout */
1271 if (aTimeoutMS == 0)
1272 aTimeoutMS = UINT32_MAX;
1273
1274 /* Search for existing PID. */
1275 PCALLBACKDATAEXECOUT pData = (CALLBACKDATAEXECOUT*)RTMemAlloc(sizeof(CALLBACKDATAEXECOUT));
1276 AssertReturn(pData, VBOX_E_IPRT_ERROR);
1277 RT_ZERO(*pData);
1278 uint32_t uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT,
1279 pData, sizeof(CALLBACKDATAEXECOUT), progress);
1280 Assert(uContextID > 0);
1281
1282 size_t cbData = (size_t)RT_MIN(aSize, _64K);
1283 com::SafeArray<BYTE> outputData(cbData);
1284
1285 VBOXHGCMSVCPARM paParms[5];
1286 int i = 0;
1287 paParms[i++].setUInt32(uContextID);
1288 paParms[i++].setUInt32(aPID);
1289 paParms[i++].setUInt32(aFlags); /** @todo Should represent stdout and/or stderr. */
1290
1291 int vrc = VINF_SUCCESS;
1292
1293 {
1294 VMMDev *vmmDev;
1295 {
1296 /* Make sure mParent is valid, so set the read lock while using.
1297 * Do not keep this lock while doing the actual call, because in the meanwhile
1298 * another thread could request a write lock which would be a bad idea ... */
1299 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1300
1301 /* Forward the information to the VMM device. */
1302 AssertPtr(mParent);
1303 vmmDev = mParent->getVMMDev();
1304 }
1305
1306 if (vmmDev)
1307 {
1308 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
1309 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_GET_OUTPUT,
1310 i, paParms);
1311 }
1312 }
1313
1314 if (RT_SUCCESS(vrc))
1315 {
1316 LogFlowFunc(("Waiting for HGCM callback (timeout=%ldms) ...\n", aTimeoutMS));
1317
1318 /*
1319 * Wait for the HGCM low level callback until the process
1320 * has been started (or something went wrong). This is necessary to
1321 * get the PID.
1322 */
1323 CallbackMapIter it = getCtrlCallbackContextByID(uContextID);
1324 BOOL fCanceled = FALSE;
1325 if (it != mCallbackMap.end())
1326 {
1327 ComAssert(!it->second.pProgress.isNull());
1328
1329 /* Wait until operation completed. */
1330 rc = it->second.pProgress->WaitForCompletion(aTimeoutMS);
1331 if (FAILED(rc)) throw rc;
1332
1333 /* Was the operation canceled by one of the parties? */
1334 rc = it->second.pProgress->COMGETTER(Canceled)(&fCanceled);
1335 if (FAILED(rc)) throw rc;
1336
1337 if (!fCanceled)
1338 {
1339 BOOL fCompleted;
1340 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))
1341 && fCompleted)
1342 {
1343 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1344
1345 /* Did we get some output? */
1346 pData = (PCALLBACKDATAEXECOUT)it->second.pvData;
1347 Assert(it->second.cbData == sizeof(CALLBACKDATAEXECOUT));
1348 AssertPtr(pData);
1349
1350 if (pData->cbData)
1351 {
1352 /* Do we need to resize the array? */
1353 if (pData->cbData > cbData)
1354 outputData.resize(pData->cbData);
1355
1356 /* Fill output in supplied out buffer. */
1357 memcpy(outputData.raw(), pData->pvData, pData->cbData);
1358 outputData.resize(pData->cbData); /* Shrink to fit actual buffer size. */
1359 }
1360 else
1361 vrc = VERR_NO_DATA; /* This is not an error we want to report to COM. */
1362 }
1363 else /* If callback not called within time ... well, that's a timeout! */
1364 vrc = VERR_TIMEOUT;
1365 }
1366 else /* Operation was canceled. */
1367 {
1368 vrc = VERR_CANCELLED;
1369 }
1370
1371 if (RT_FAILURE(vrc))
1372 {
1373 if (vrc == VERR_NO_DATA)
1374 {
1375 /* This is not an error we want to report to COM. */
1376 rc = S_OK;
1377 }
1378 else if (vrc == VERR_TIMEOUT)
1379 {
1380 rc = setError(VBOX_E_IPRT_ERROR,
1381 tr("The guest did not output within time (%ums)"), aTimeoutMS);
1382 }
1383 else if (vrc == VERR_CANCELLED)
1384 {
1385 rc = setError(VBOX_E_IPRT_ERROR,
1386 tr("The output operation was canceled"));
1387 }
1388 else
1389 {
1390 rc = setError(E_UNEXPECTED,
1391 tr("The service call failed with error %Rrc"), vrc);
1392 }
1393 }
1394
1395 {
1396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1397 /*
1398 * Destroy locally used progress object.
1399 */
1400 destroyCtrlCallbackContext(it);
1401 }
1402
1403 /* Remove callback context (not used anymore). */
1404 mCallbackMap.erase(it);
1405 }
1406 else /* PID lookup failed. */
1407 rc = setError(VBOX_E_IPRT_ERROR,
1408 tr("Process (PID %u) not found!"), aPID);
1409 }
1410 else /* HGCM operation failed. */
1411 rc = setError(E_UNEXPECTED,
1412 tr("The HGCM call failed with error %Rrc"), vrc);
1413
1414 /* Cleanup. */
1415 progress->uninit();
1416 progress.setNull();
1417
1418 /* If something failed (or there simply was no data, indicated by VERR_NO_DATA,
1419 * we return an empty array so that the frontend knows when to give up. */
1420 if (RT_FAILURE(vrc) || FAILED(rc))
1421 outputData.resize(0);
1422 outputData.detachTo(ComSafeArrayOutArg(aData));
1423 }
1424 catch (std::bad_alloc &)
1425 {
1426 rc = E_OUTOFMEMORY;
1427 }
1428 return rc;
1429#endif
1430}
1431
1432STDMETHODIMP Guest::GetProcessStatus(ULONG aPID, ULONG *aExitCode, ULONG *aFlags, ULONG *aStatus)
1433{
1434#ifndef VBOX_WITH_GUEST_CONTROL
1435 ReturnComNotImplemented();
1436#else /* VBOX_WITH_GUEST_CONTROL */
1437 using namespace guestControl;
1438
1439 AutoCaller autoCaller(this);
1440 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1441
1442 HRESULT rc = S_OK;
1443
1444 try
1445 {
1446 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1447
1448 GuestProcessMapIterConst it = getProcessByPID(aPID);
1449 if (it != mGuestProcessMap.end())
1450 {
1451 *aExitCode = it->second.mExitCode;
1452 *aFlags = it->second.mFlags;
1453 *aStatus = it->second.mStatus;
1454 }
1455 else
1456 rc = setError(VBOX_E_IPRT_ERROR,
1457 tr("Process (PID %u) not found!"), aPID);
1458 }
1459 catch (std::bad_alloc &)
1460 {
1461 rc = E_OUTOFMEMORY;
1462 }
1463 return rc;
1464#endif
1465}
1466
1467// public methods only for internal purposes
1468/////////////////////////////////////////////////////////////////////////////
1469
1470/**
1471 * Sets the general Guest Additions information like
1472 * API (interface) version and OS type. Gets called by
1473 * vmmdevUpdateGuestInfo.
1474 *
1475 * @param aInterfaceVersion
1476 * @param aOsType
1477 */
1478void Guest::setAdditionsInfo(Bstr aInterfaceVersion, VBOXOSTYPE aOsType)
1479{
1480 AutoCaller autoCaller(this);
1481 AssertComRCReturnVoid (autoCaller.rc());
1482
1483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1484
1485 /*
1486 * Note: The Guest Additions API (interface) version is deprecated
1487 * and will not be used anymore! We might need it to at least report
1488 * something as version number if *really* ancient Guest Additions are
1489 * installed (without the guest version + revision properties having set).
1490 */
1491 mData.mInterfaceVersion = aInterfaceVersion;
1492
1493 /*
1494 * Older Additions rely on the Additions API version whether they
1495 * are assumed to be active or not. Since newer Additions do report
1496 * the Additions version *before* calling this function (by calling
1497 * VMMDevReportGuestInfo2, VMMDevReportGuestStatus, VMMDevReportGuestInfo,
1498 * in that order) we can tell apart old and new Additions here. Old
1499 * Additions never would set VMMDevReportGuestInfo2 (which set mData.mAdditionsVersion)
1500 * so they just rely on the aInterfaceVersion string (which gets set by
1501 * VMMDevReportGuestInfo).
1502 *
1503 * So only mark the Additions as being active when we don't have the Additions
1504 * version set.
1505 */
1506 if (mData.mAdditionsVersion.isEmpty())
1507 mData.mAdditionsActive = !aInterfaceVersion.isEmpty();
1508 /*
1509 * Older Additions didn't have this finer grained capability bit,
1510 * so enable it by default. Newer Additions will not enable this here
1511 * and use the setSupportedFeatures function instead.
1512 */
1513 mData.mSupportsGraphics = mData.mAdditionsActive;
1514
1515 /*
1516 * Note! There is a race going on between setting mAdditionsActive and
1517 * mSupportsGraphics here and disabling/enabling it later according to
1518 * its real status when using new(er) Guest Additions.
1519 */
1520
1521 mData.mOSTypeId = Global::OSTypeId (aOsType);
1522}
1523
1524/**
1525 * Sets the Guest Additions version information details.
1526 * Gets called by vmmdevUpdateGuestInfo2.
1527 *
1528 * @param aAdditionsVersion
1529 * @param aVersionName
1530 */
1531void Guest::setAdditionsInfo2(Bstr aAdditionsVersion, Bstr aVersionName, Bstr aRevision)
1532{
1533 AutoCaller autoCaller(this);
1534 AssertComRCReturnVoid (autoCaller.rc());
1535
1536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1537
1538 if (!aVersionName.isEmpty())
1539 /*
1540 * aVersionName could be "x.y.z_BETA1_FOOBAR", so append revision manually to
1541 * become "x.y.z_BETA1_FOOBARr12345".
1542 */
1543 mData.mAdditionsVersion = BstrFmt("%ls r%ls", aVersionName.raw(), aRevision.raw());
1544 else /* aAdditionsVersion is in x.y.zr12345 format. */
1545 mData.mAdditionsVersion = aAdditionsVersion;
1546}
1547
1548/**
1549 * Sets the status of a certain Guest Additions facility.
1550 * Gets called by vmmdevUpdateGuestStatus.
1551 *
1552 * @param Facility
1553 * @param Status
1554 * @param ulFlags
1555 */
1556void Guest::setAdditionsStatus(VBoxGuestStatusFacility Facility, VBoxGuestStatusCurrent Status, ULONG ulFlags)
1557{
1558 AutoCaller autoCaller(this);
1559 AssertComRCReturnVoid (autoCaller.rc());
1560
1561 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1562
1563 /*
1564 * Only mark Guest Additions as active when VBoxService started up.
1565 */
1566 mData.mAdditionsActive = ( Facility == VBoxGuestStatusFacility_VBoxService
1567 && Status == VBoxGuestStatusCurrent_Active) ? TRUE : FALSE;
1568}
1569
1570/**
1571 * Sets the supported features (and whether they are active or not).
1572 *
1573 * @param ulCaps
1574 * @param ulActive
1575 */
1576void Guest::setSupportedFeatures(ULONG64 ulCaps, ULONG64 ulActive)
1577{
1578 AutoCaller autoCaller(this);
1579 AssertComRCReturnVoid (autoCaller.rc());
1580
1581 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1582
1583 mData.mSupportsSeamless = (ulCaps & VMMDEV_GUEST_SUPPORTS_SEAMLESS);
1584 /** @todo Add VMMDEV_GUEST_SUPPORTS_GUEST_HOST_WINDOW_MAPPING */
1585 mData.mSupportsGraphics = (ulCaps & VMMDEV_GUEST_SUPPORTS_GRAPHICS);
1586}
1587/* vi: set tabstop=4 shiftwidth=4 expandtab: */
1588
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