VirtualBox

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

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

Guest Copy/VBoxManage+Main: Added very first bits (not enabled by default yet, untested).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 82.6 KB
Line 
1/* $Id: GuestImpl.cpp 32866 2010-10-01 14:41:42Z 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/dir.h>
36#include <iprt/getopt.h>
37#include <iprt/list.h>
38#include <iprt/path.h>
39#include <VBox/pgm.h>
40
41// defines
42/////////////////////////////////////////////////////////////////////////////
43
44// constructor / destructor
45/////////////////////////////////////////////////////////////////////////////
46
47DEFINE_EMPTY_CTOR_DTOR (Guest)
48
49HRESULT Guest::FinalConstruct()
50{
51 return S_OK;
52}
53
54void Guest::FinalRelease()
55{
56 uninit ();
57}
58
59// public methods only for internal purposes
60/////////////////////////////////////////////////////////////////////////////
61
62/**
63 * Initializes the guest object.
64 */
65HRESULT Guest::init (Console *aParent)
66{
67 LogFlowThisFunc(("aParent=%p\n", aParent));
68
69 ComAssertRet(aParent, E_INVALIDARG);
70
71 /* Enclose the state transition NotReady->InInit->Ready */
72 AutoInitSpan autoInitSpan(this);
73 AssertReturn(autoInitSpan.isOk(), E_FAIL);
74
75 unconst(mParent) = aParent;
76
77 /* Confirm a successful initialization when it's the case */
78 autoInitSpan.setSucceeded();
79
80 ULONG aMemoryBalloonSize;
81 HRESULT ret = mParent->machine()->COMGETTER(MemoryBalloonSize)(&aMemoryBalloonSize);
82 if (ret == S_OK)
83 mMemoryBalloonSize = aMemoryBalloonSize;
84 else
85 mMemoryBalloonSize = 0; /* Default is no ballooning */
86
87 BOOL fPageFusionEnabled;
88 ret = mParent->machine()->COMGETTER(PageFusionEnabled)(&fPageFusionEnabled);
89 if (ret == S_OK)
90 mfPageFusionEnabled = fPageFusionEnabled;
91 else
92 mfPageFusionEnabled = false; /* Default is no page fusion*/
93
94 mStatUpdateInterval = 0; /* Default is not to report guest statistics at all */
95
96 /* Clear statistics. */
97 for (unsigned i = 0 ; i < GUESTSTATTYPE_MAX; i++)
98 mCurrentGuestStat[i] = 0;
99
100#ifdef VBOX_WITH_GUEST_CONTROL
101 /* Init the context ID counter at 1000. */
102 mNextContextID = 1000;
103#endif
104
105 return S_OK;
106}
107
108/**
109 * Uninitializes the instance and sets the ready flag to FALSE.
110 * Called either from FinalRelease() or by the parent when it gets destroyed.
111 */
112void Guest::uninit()
113{
114 LogFlowThisFunc(("\n"));
115
116#ifdef VBOX_WITH_GUEST_CONTROL
117 /* Scope write lock as much as possible. */
118 {
119 /*
120 * Cleanup must be done *before* AutoUninitSpan to cancel all
121 * all outstanding waits in API functions (which hold AutoCaller
122 * ref counts).
123 */
124 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
125
126 /* Clean up callback data. */
127 CallbackMapIter it;
128 for (it = mCallbackMap.begin(); it != mCallbackMap.end(); it++)
129 destroyCtrlCallbackContext(it);
130
131 /* Clear process map. */
132 mGuestProcessMap.clear();
133 }
134#endif
135
136 /* Enclose the state transition Ready->InUninit->NotReady */
137 AutoUninitSpan autoUninitSpan(this);
138 if (autoUninitSpan.uninitDone())
139 return;
140
141 unconst(mParent) = NULL;
142}
143
144// IGuest properties
145/////////////////////////////////////////////////////////////////////////////
146
147STDMETHODIMP Guest::COMGETTER(OSTypeId) (BSTR *aOSTypeId)
148{
149 CheckComArgOutPointerValid(aOSTypeId);
150
151 AutoCaller autoCaller(this);
152 if (FAILED(autoCaller.rc())) return autoCaller.rc();
153
154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
155
156 // redirect the call to IMachine if no additions are installed
157 if (mData.mAdditionsVersion.isEmpty())
158 return mParent->machine()->COMGETTER(OSTypeId)(aOSTypeId);
159
160 mData.mOSTypeId.cloneTo(aOSTypeId);
161
162 return S_OK;
163}
164
165STDMETHODIMP Guest::COMGETTER(AdditionsRunLevel) (AdditionsRunLevelType_T *aRunLevel)
166{
167 AutoCaller autoCaller(this);
168 if (FAILED(autoCaller.rc())) return autoCaller.rc();
169
170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
171
172 *aRunLevel = mData.mAdditionsRunLevel;
173
174 return S_OK;
175}
176
177STDMETHODIMP Guest::COMGETTER(AdditionsVersion) (BSTR *aAdditionsVersion)
178{
179 CheckComArgOutPointerValid(aAdditionsVersion);
180
181 AutoCaller autoCaller(this);
182 if (FAILED(autoCaller.rc())) return autoCaller.rc();
183
184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
185
186 HRESULT hr = S_OK;
187 if ( mData.mAdditionsVersion.isEmpty()
188 /* Only try alternative way if GA are active! */
189 && mData.mAdditionsRunLevel > AdditionsRunLevelType_None)
190 {
191 /*
192 * If we got back an empty string from GetAdditionsVersion() we either
193 * really don't have the Guest Additions version yet or the guest is running
194 * older Guest Additions (< 3.2.0) which don't provide VMMDevReq_ReportGuestInfo2,
195 * so get the version + revision from the (hopefully) provided guest properties
196 * instead.
197 */
198 Bstr addVersion;
199 LONG64 u64Timestamp;
200 Bstr flags;
201 hr = mParent->machine()->GetGuestProperty(Bstr("/VirtualBox/GuestAdd/Version").raw(),
202 addVersion.asOutParam(), &u64Timestamp, flags.asOutParam());
203 if (hr == S_OK)
204 {
205 Bstr addRevision;
206 hr = mParent->machine()->GetGuestProperty(Bstr("/VirtualBox/GuestAdd/Revision").raw(),
207 addRevision.asOutParam(), &u64Timestamp, flags.asOutParam());
208 if ( hr == S_OK
209 && !addVersion.isEmpty()
210 && !addRevision.isEmpty())
211 {
212 /* Some Guest Additions versions had interchanged version + revision values,
213 * so check if the version value at least has a dot to identify it and change
214 * both values to reflect the right content. */
215 if (!Utf8Str(addVersion).contains("."))
216 {
217 Bstr addTemp = addVersion;
218 addVersion = addRevision;
219 addRevision = addTemp;
220 }
221
222 Bstr additionsVersion = BstrFmt("%ls r%ls",
223 addVersion.raw(), addRevision.raw());
224 additionsVersion.cloneTo(aAdditionsVersion);
225 }
226 /** @todo r=bird: else: Should not return failure! */
227 }
228 else
229 {
230 /* If getting the version + revision above fails or they simply aren't there
231 * because of *really* old Guest Additions we only can report the interface
232 * version to at least have something. */
233 mData.mInterfaceVersion.cloneTo(aAdditionsVersion);
234 /** @todo r=bird: hr is still indicating failure! */
235 }
236 }
237 else
238 mData.mAdditionsVersion.cloneTo(aAdditionsVersion);
239
240 return hr;
241}
242
243STDMETHODIMP Guest::COMGETTER(SupportsSeamless) (BOOL *aSupportsSeamless)
244{
245 CheckComArgOutPointerValid(aSupportsSeamless);
246
247 AutoCaller autoCaller(this);
248 if (FAILED(autoCaller.rc())) return autoCaller.rc();
249
250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
251
252 *aSupportsSeamless = mData.mSupportsSeamless;
253
254 return S_OK;
255}
256
257STDMETHODIMP Guest::COMGETTER(SupportsGraphics) (BOOL *aSupportsGraphics)
258{
259 CheckComArgOutPointerValid(aSupportsGraphics);
260
261 AutoCaller autoCaller(this);
262 if (FAILED(autoCaller.rc())) return autoCaller.rc();
263
264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
265
266 *aSupportsGraphics = mData.mSupportsGraphics;
267
268 return S_OK;
269}
270
271BOOL Guest::isPageFusionEnabled()
272{
273 AutoCaller autoCaller(this);
274 if (FAILED(autoCaller.rc())) return false;
275
276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
277
278 return mfPageFusionEnabled;
279}
280
281STDMETHODIMP Guest::COMGETTER(MemoryBalloonSize) (ULONG *aMemoryBalloonSize)
282{
283 CheckComArgOutPointerValid(aMemoryBalloonSize);
284
285 AutoCaller autoCaller(this);
286 if (FAILED(autoCaller.rc())) return autoCaller.rc();
287
288 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
289
290 *aMemoryBalloonSize = mMemoryBalloonSize;
291
292 return S_OK;
293}
294
295STDMETHODIMP Guest::COMSETTER(MemoryBalloonSize) (ULONG aMemoryBalloonSize)
296{
297 AutoCaller autoCaller(this);
298 if (FAILED(autoCaller.rc())) return autoCaller.rc();
299
300 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
301
302 /* We must be 100% sure that IMachine::COMSETTER(MemoryBalloonSize)
303 * does not call us back in any way! */
304 HRESULT ret = mParent->machine()->COMSETTER(MemoryBalloonSize)(aMemoryBalloonSize);
305 if (ret == S_OK)
306 {
307 mMemoryBalloonSize = aMemoryBalloonSize;
308 /* forward the information to the VMM device */
309 VMMDev *pVMMDev = mParent->getVMMDev();
310 /* MUST release all locks before calling VMM device as its critsect
311 * has higher lock order than anything in Main. */
312 alock.release();
313 if (pVMMDev)
314 {
315 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
316 if (pVMMDevPort)
317 pVMMDevPort->pfnSetMemoryBalloon(pVMMDevPort, aMemoryBalloonSize);
318 }
319 }
320
321 return ret;
322}
323
324STDMETHODIMP Guest::COMGETTER(StatisticsUpdateInterval)(ULONG *aUpdateInterval)
325{
326 CheckComArgOutPointerValid(aUpdateInterval);
327
328 AutoCaller autoCaller(this);
329 if (FAILED(autoCaller.rc())) return autoCaller.rc();
330
331 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
332
333 *aUpdateInterval = mStatUpdateInterval;
334 return S_OK;
335}
336
337STDMETHODIMP Guest::COMSETTER(StatisticsUpdateInterval)(ULONG aUpdateInterval)
338{
339 AutoCaller autoCaller(this);
340 if (FAILED(autoCaller.rc())) return autoCaller.rc();
341
342 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
343
344 mStatUpdateInterval = aUpdateInterval;
345 /* forward the information to the VMM device */
346 VMMDev *pVMMDev = mParent->getVMMDev();
347 /* MUST release all locks before calling VMM device as its critsect
348 * has higher lock order than anything in Main. */
349 alock.release();
350 if (pVMMDev)
351 {
352 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
353 if (pVMMDevPort)
354 pVMMDevPort->pfnSetStatisticsInterval(pVMMDevPort, aUpdateInterval);
355 }
356
357 return S_OK;
358}
359
360STDMETHODIMP Guest::InternalGetStatistics(ULONG *aCpuUser, ULONG *aCpuKernel, ULONG *aCpuIdle,
361 ULONG *aMemTotal, ULONG *aMemFree, ULONG *aMemBalloon, ULONG *aMemShared,
362 ULONG *aMemCache, ULONG *aPageTotal,
363 ULONG *aMemAllocTotal, ULONG *aMemFreeTotal, ULONG *aMemBalloonTotal, ULONG *aMemSharedTotal)
364{
365 CheckComArgOutPointerValid(aCpuUser);
366 CheckComArgOutPointerValid(aCpuKernel);
367 CheckComArgOutPointerValid(aCpuIdle);
368 CheckComArgOutPointerValid(aMemTotal);
369 CheckComArgOutPointerValid(aMemFree);
370 CheckComArgOutPointerValid(aMemBalloon);
371 CheckComArgOutPointerValid(aMemShared);
372 CheckComArgOutPointerValid(aMemCache);
373 CheckComArgOutPointerValid(aPageTotal);
374 CheckComArgOutPointerValid(aMemAllocTotal);
375 CheckComArgOutPointerValid(aMemFreeTotal);
376 CheckComArgOutPointerValid(aMemBalloonTotal);
377 CheckComArgOutPointerValid(aMemSharedTotal);
378
379 AutoCaller autoCaller(this);
380 if (FAILED(autoCaller.rc())) return autoCaller.rc();
381
382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
383
384 *aCpuUser = mCurrentGuestStat[GUESTSTATTYPE_CPUUSER];
385 *aCpuKernel = mCurrentGuestStat[GUESTSTATTYPE_CPUKERNEL];
386 *aCpuIdle = mCurrentGuestStat[GUESTSTATTYPE_CPUIDLE];
387 *aMemTotal = mCurrentGuestStat[GUESTSTATTYPE_MEMTOTAL] * (_4K/_1K); /* page (4K) -> 1KB units */
388 *aMemFree = mCurrentGuestStat[GUESTSTATTYPE_MEMFREE] * (_4K/_1K); /* page (4K) -> 1KB units */
389 *aMemBalloon = mCurrentGuestStat[GUESTSTATTYPE_MEMBALLOON] * (_4K/_1K); /* page (4K) -> 1KB units */
390 *aMemCache = mCurrentGuestStat[GUESTSTATTYPE_MEMCACHE] * (_4K/_1K); /* page (4K) -> 1KB units */
391 *aPageTotal = mCurrentGuestStat[GUESTSTATTYPE_PAGETOTAL] * (_4K/_1K); /* page (4K) -> 1KB units */
392
393 /* MUST release all locks before calling any PGM statistics queries,
394 * as they are executed by EMT and that might deadlock us by VMM device
395 * activity which waits for the Guest object lock. */
396 alock.release();
397 Console::SafeVMPtr pVM (mParent);
398 if (pVM.isOk())
399 {
400 uint64_t uFreeTotal, uAllocTotal, uBalloonedTotal, uSharedTotal;
401 *aMemFreeTotal = 0;
402 int rc = PGMR3QueryVMMMemoryStats(pVM.raw(), &uAllocTotal, &uFreeTotal, &uBalloonedTotal, &uSharedTotal);
403 AssertRC(rc);
404 if (rc == VINF_SUCCESS)
405 {
406 *aMemAllocTotal = (ULONG)(uAllocTotal / _1K); /* bytes -> KB */
407 *aMemFreeTotal = (ULONG)(uFreeTotal / _1K);
408 *aMemBalloonTotal = (ULONG)(uBalloonedTotal / _1K);
409 *aMemSharedTotal = (ULONG)(uSharedTotal / _1K);
410 }
411
412 /* Query the missing per-VM memory statistics. */
413 *aMemShared = 0;
414 uint64_t uTotalMem, uPrivateMem, uSharedMem, uZeroMem;
415 rc = PGMR3QueryMemoryStats(pVM.raw(), &uTotalMem, &uPrivateMem, &uSharedMem, &uZeroMem);
416 if (rc == VINF_SUCCESS)
417 {
418 *aMemShared = (ULONG)(uSharedMem / _1K);
419 }
420 }
421 else
422 {
423 *aMemFreeTotal = 0;
424 *aMemShared = 0;
425 }
426
427 return S_OK;
428}
429
430HRESULT Guest::setStatistic(ULONG aCpuId, GUESTSTATTYPE enmType, ULONG aVal)
431{
432 AutoCaller autoCaller(this);
433 if (FAILED(autoCaller.rc())) return autoCaller.rc();
434
435 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
436
437 if (enmType >= GUESTSTATTYPE_MAX)
438 return E_INVALIDARG;
439
440 mCurrentGuestStat[enmType] = aVal;
441 return S_OK;
442}
443
444STDMETHODIMP Guest::GetAdditionsStatus(AdditionsRunLevelType_T aLevel, BOOL *aActive)
445{
446 AutoCaller autoCaller(this);
447 if (FAILED(autoCaller.rc())) return autoCaller.rc();
448
449 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
450
451 HRESULT rc = S_OK;
452 switch (aLevel)
453 {
454 case AdditionsRunLevelType_System:
455 *aActive = (mData.mAdditionsRunLevel > AdditionsRunLevelType_None);
456 break;
457
458 case AdditionsRunLevelType_Userland:
459 *aActive = (mData.mAdditionsRunLevel >= AdditionsRunLevelType_Userland);
460 break;
461
462 case AdditionsRunLevelType_Desktop:
463 *aActive = (mData.mAdditionsRunLevel >= AdditionsRunLevelType_Desktop);
464 break;
465
466 default:
467 rc = setError(VBOX_E_NOT_SUPPORTED,
468 tr("Invalid status level defined: %u"), aLevel);
469 break;
470 }
471
472 return rc;
473}
474
475STDMETHODIMP Guest::SetCredentials(IN_BSTR aUserName, IN_BSTR aPassword,
476 IN_BSTR aDomain, BOOL aAllowInteractiveLogon)
477{
478 AutoCaller autoCaller(this);
479 if (FAILED(autoCaller.rc())) return autoCaller.rc();
480
481 /* forward the information to the VMM device */
482 VMMDev *pVMMDev = mParent->getVMMDev();
483 if (pVMMDev)
484 {
485 PPDMIVMMDEVPORT pVMMDevPort = pVMMDev->getVMMDevPort();
486 if (pVMMDevPort)
487 {
488 uint32_t u32Flags = VMMDEV_SETCREDENTIALS_GUESTLOGON;
489 if (!aAllowInteractiveLogon)
490 u32Flags = VMMDEV_SETCREDENTIALS_NOLOCALLOGON;
491
492 pVMMDevPort->pfnSetCredentials(pVMMDevPort,
493 Utf8Str(aUserName).c_str(),
494 Utf8Str(aPassword).c_str(),
495 Utf8Str(aDomain).c_str(),
496 u32Flags);
497 return S_OK;
498 }
499 }
500
501 return setError(VBOX_E_VM_ERROR,
502 tr("VMM device is not available (is the VM running?)"));
503}
504
505#ifdef VBOX_WITH_GUEST_CONTROL
506/**
507 * Appends environment variables to the environment block. Each var=value pair is separated
508 * by NULL (\0) sequence. The whole block will be stored in one blob and disassembled on the
509 * guest side later to fit into the HGCM param structure.
510 *
511 * @returns VBox status code.
512 *
513 * @todo
514 *
515 */
516int Guest::prepareExecuteEnv(const char *pszEnv, void **ppvList, uint32_t *pcbList, uint32_t *pcEnv)
517{
518 int rc = VINF_SUCCESS;
519 uint32_t cbLen = strlen(pszEnv);
520 if (*ppvList)
521 {
522 uint32_t cbNewLen = *pcbList + cbLen + 1; /* Include zero termination. */
523 char *pvTmp = (char*)RTMemRealloc(*ppvList, cbNewLen);
524 if (NULL == pvTmp)
525 {
526 rc = VERR_NO_MEMORY;
527 }
528 else
529 {
530 memcpy(pvTmp + *pcbList, pszEnv, cbLen);
531 pvTmp[cbNewLen - 1] = '\0'; /* Add zero termination. */
532 *ppvList = (void**)pvTmp;
533 }
534 }
535 else
536 {
537 char *pcTmp;
538 if (RTStrAPrintf(&pcTmp, "%s", pszEnv) > 0)
539 {
540 *ppvList = (void**)pcTmp;
541 /* Reset counters. */
542 *pcEnv = 0;
543 *pcbList = 0;
544 }
545 }
546 if (RT_SUCCESS(rc))
547 {
548 *pcbList += cbLen + 1; /* Include zero termination. */
549 *pcEnv += 1; /* Increase env pairs count. */
550 }
551 return rc;
552}
553
554/**
555 * Static callback function for receiving updates on guest control commands
556 * from the guest. Acts as a dispatcher for the actual class instance.
557 *
558 * @returns VBox status code.
559 *
560 * @todo
561 *
562 */
563DECLCALLBACK(int) Guest::doGuestCtrlNotification(void *pvExtension,
564 uint32_t u32Function,
565 void *pvParms,
566 uint32_t cbParms)
567{
568 using namespace guestControl;
569
570 /*
571 * No locking, as this is purely a notification which does not make any
572 * changes to the object state.
573 */
574#ifdef DEBUG_andy
575 LogFlowFunc(("pvExtension = %p, u32Function = %d, pvParms = %p, cbParms = %d\n",
576 pvExtension, u32Function, pvParms, cbParms));
577#endif
578 ComObjPtr<Guest> pGuest = reinterpret_cast<Guest *>(pvExtension);
579
580 int rc = VINF_SUCCESS;
581 if (u32Function == GUEST_DISCONNECTED)
582 {
583 //LogFlowFunc(("GUEST_DISCONNECTED\n"));
584
585 PCALLBACKDATACLIENTDISCONNECTED pCBData = reinterpret_cast<PCALLBACKDATACLIENTDISCONNECTED>(pvParms);
586 AssertPtr(pCBData);
587 AssertReturn(sizeof(CALLBACKDATACLIENTDISCONNECTED) == cbParms, VERR_INVALID_PARAMETER);
588 AssertReturn(CALLBACKDATAMAGICCLIENTDISCONNECTED == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
589
590 rc = pGuest->notifyCtrlClientDisconnected(u32Function, pCBData);
591 }
592 else if (u32Function == GUEST_EXEC_SEND_STATUS)
593 {
594 //LogFlowFunc(("GUEST_EXEC_SEND_STATUS\n"));
595
596 PCALLBACKDATAEXECSTATUS pCBData = reinterpret_cast<PCALLBACKDATAEXECSTATUS>(pvParms);
597 AssertPtr(pCBData);
598 AssertReturn(sizeof(CALLBACKDATAEXECSTATUS) == cbParms, VERR_INVALID_PARAMETER);
599 AssertReturn(CALLBACKDATAMAGICEXECSTATUS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
600
601 rc = pGuest->notifyCtrlExecStatus(u32Function, pCBData);
602 }
603 else if (u32Function == GUEST_EXEC_SEND_OUTPUT)
604 {
605 //LogFlowFunc(("GUEST_EXEC_SEND_OUTPUT\n"));
606
607 PCALLBACKDATAEXECOUT pCBData = reinterpret_cast<PCALLBACKDATAEXECOUT>(pvParms);
608 AssertPtr(pCBData);
609 AssertReturn(sizeof(CALLBACKDATAEXECOUT) == cbParms, VERR_INVALID_PARAMETER);
610 AssertReturn(CALLBACKDATAMAGICEXECOUT == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
611
612 rc = pGuest->notifyCtrlExecOut(u32Function, pCBData);
613 }
614 else
615 rc = VERR_NOT_SUPPORTED;
616 return rc;
617}
618
619/* Function for handling the execution start/termination notification. */
620int Guest::notifyCtrlExecStatus(uint32_t u32Function,
621 PCALLBACKDATAEXECSTATUS pData)
622{
623 int vrc = VINF_SUCCESS;
624
625 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
626
627 AssertPtr(pData);
628 CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
629
630 /* Callback can be called several times. */
631 if (it != mCallbackMap.end())
632 {
633 PCALLBACKDATAEXECSTATUS pCBData = (PCALLBACKDATAEXECSTATUS)it->second.pvData;
634 AssertPtr(pCBData);
635
636 pCBData->u32PID = pData->u32PID;
637 pCBData->u32Status = pData->u32Status;
638 pCBData->u32Flags = pData->u32Flags;
639 /** @todo Copy void* buffer contents! */
640
641 Utf8Str errMsg;
642
643 /* Was progress canceled before? */
644 BOOL fCanceled;
645 ComAssert(!it->second.pProgress.isNull());
646 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled))
647 && !fCanceled)
648 {
649 /* Do progress handling. */
650 HRESULT hr;
651 switch (pData->u32Status)
652 {
653 case PROC_STS_STARTED:
654 LogRel(("Guest process (PID %u) started\n", pCBData->u32PID)); /** @todo Add process name */
655 hr = it->second.pProgress->SetNextOperation(Bstr(tr("Waiting for process to exit ...")).raw(), 1 /* Weight */);
656 AssertComRC(hr);
657 break;
658
659 case PROC_STS_TEN: /* Terminated normally. */
660 LogRel(("Guest process (PID %u) exited normally\n", pCBData->u32PID)); /** @todo Add process name */
661 hr = it->second.pProgress->notifyComplete(S_OK);
662 AssertComRC(hr);
663 LogFlowFunc(("Proccess (context ID=%u, status=%u) terminated successfully\n",
664 pData->hdr.u32ContextID, pData->u32Status));
665 break;
666
667 case PROC_STS_TEA: /* Terminated abnormally. */
668 LogRel(("Guest process (PID %u) terminated abnormally with exit code = %u\n",
669 pCBData->u32PID, pCBData->u32Flags)); /** @todo Add process name */
670 errMsg = Utf8StrFmt(Guest::tr("Process terminated abnormally with status '%u'"),
671 pCBData->u32Flags);
672 break;
673
674 case PROC_STS_TES: /* Terminated through signal. */
675 LogRel(("Guest process (PID %u) terminated through signal with exit code = %u\n",
676 pCBData->u32PID, pCBData->u32Flags)); /** @todo Add process name */
677 errMsg = Utf8StrFmt(Guest::tr("Process terminated via signal with status '%u'"),
678 pCBData->u32Flags);
679 break;
680
681 case PROC_STS_TOK:
682 LogRel(("Guest process (PID %u) timed out and was killed\n", pCBData->u32PID)); /** @todo Add process name */
683 errMsg = Utf8StrFmt(Guest::tr("Process timed out and was killed"));
684 break;
685
686 case PROC_STS_TOA:
687 LogRel(("Guest process (PID %u) timed out and could not be killed\n", pCBData->u32PID)); /** @todo Add process name */
688 errMsg = Utf8StrFmt(Guest::tr("Process timed out and could not be killed"));
689 break;
690
691 case PROC_STS_DWN:
692 LogRel(("Guest process (PID %u) killed because system is shutting down\n", pCBData->u32PID)); /** @todo Add process name */
693 /*
694 * If u32Flags has ExecuteProcessFlag_IgnoreOrphanedProcesses set, we don't report an error to
695 * our progress object. This is helpful for waiters which rely on the success of our progress object
696 * even if the executed process was killed because the system/VBoxService is shutting down.
697 *
698 * In this case u32Flags contains the actual execution flags reached in via Guest::ExecuteProcess().
699 */
700 if (pData->u32Flags & ExecuteProcessFlag_IgnoreOrphanedProcesses)
701 {
702 hr = it->second.pProgress->notifyComplete(S_OK);
703 AssertComRC(hr);
704 }
705 else
706 {
707 errMsg = Utf8StrFmt(Guest::tr("Process killed because system is shutting down"));
708 }
709 break;
710
711 case PROC_STS_ERROR:
712 LogRel(("Guest process (PID %u) could not be started because of rc=%Rrc\n",
713 pCBData->u32PID, pCBData->u32Flags)); /** @todo Add process name */
714 errMsg = Utf8StrFmt(Guest::tr("Process execution failed with rc=%Rrc"), pCBData->u32Flags);
715 break;
716
717 default:
718 vrc = VERR_INVALID_PARAMETER;
719 break;
720 }
721
722 /* Handle process map. */
723 /** @todo What happens on/deal with PID reuse? */
724 /** @todo How to deal with multiple updates at once? */
725 if (pCBData->u32PID > 0)
726 {
727 GuestProcessMapIter it_proc = getProcessByPID(pCBData->u32PID);
728 if (it_proc == mGuestProcessMap.end())
729 {
730 /* Not found, add to map. */
731 GuestProcess newProcess;
732 newProcess.mStatus = pCBData->u32Status;
733 newProcess.mExitCode = pCBData->u32Flags; /* Contains exit code. */
734 newProcess.mFlags = 0;
735
736 mGuestProcessMap[pCBData->u32PID] = newProcess;
737 }
738 else /* Update map. */
739 {
740 it_proc->second.mStatus = pCBData->u32Status;
741 it_proc->second.mExitCode = pCBData->u32Flags; /* Contains exit code. */
742 it_proc->second.mFlags = 0;
743 }
744 }
745 }
746 else
747 errMsg = Utf8StrFmt(Guest::tr("Process execution canceled"));
748
749 if (!it->second.pProgress->getCompleted())
750 {
751 if ( errMsg.length()
752 || fCanceled) /* If canceled we have to report E_FAIL! */
753 {
754 /* Destroy all callbacks which are still waiting on something
755 * which is related to the current PID. */
756 CallbackMapIter it2;
757 for (it2 = mCallbackMap.begin(); it2 != mCallbackMap.end(); it2++)
758 {
759 switch (it2->second.mType)
760 {
761 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_START:
762 break;
763
764 /* When waiting for process output while the process is destroyed,
765 * make sure we also destroy the actual waiting operation (internal progress object)
766 * in order to not block the caller. */
767 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT:
768 {
769 PCALLBACKDATAEXECOUT pItData = (CALLBACKDATAEXECOUT*)it2->second.pvData;
770 AssertPtr(pItData);
771 if (pItData->u32PID == pCBData->u32PID)
772 destroyCtrlCallbackContext(it2);
773 break;
774 }
775
776 default:
777 AssertMsgFailed(("unknown callback type %d\n", it2->second.mType));
778 break;
779 }
780 }
781
782 HRESULT hr2 = it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,
783 COM_IIDOF(IGuest),
784 Guest::getStaticComponentName(),
785 "%s", errMsg.c_str());
786 AssertComRC(hr2);
787 LogFlowFunc(("Process (context ID=%u, status=%u) reported error: %s\n",
788 pData->hdr.u32ContextID, pData->u32Status, errMsg.c_str()));
789 }
790 }
791 }
792 else
793 LogFlowFunc(("Unexpected callback (magic=%u, context ID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));
794 LogFlowFunc(("Returned with rc=%Rrc\n", vrc));
795 return vrc;
796}
797
798/* Function for handling the execution output notification. */
799int Guest::notifyCtrlExecOut(uint32_t u32Function,
800 PCALLBACKDATAEXECOUT pData)
801{
802 int rc = VINF_SUCCESS;
803
804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
805
806 AssertPtr(pData);
807 CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
808 if (it != mCallbackMap.end())
809 {
810 PCALLBACKDATAEXECOUT pCBData = (CALLBACKDATAEXECOUT*)it->second.pvData;
811 AssertPtr(pCBData);
812
813 pCBData->u32PID = pData->u32PID;
814 pCBData->u32HandleId = pData->u32HandleId;
815 pCBData->u32Flags = pData->u32Flags;
816
817 /* Make sure we really got something! */
818 if ( pData->cbData
819 && pData->pvData)
820 {
821 /* Allocate data buffer and copy it */
822 pCBData->pvData = RTMemAlloc(pData->cbData);
823 pCBData->cbData = pData->cbData;
824
825 AssertReturn(pCBData->pvData, VERR_NO_MEMORY);
826 memcpy(pCBData->pvData, pData->pvData, pData->cbData);
827 }
828 else
829 {
830 pCBData->pvData = NULL;
831 pCBData->cbData = 0;
832 }
833
834 /* Was progress canceled before? */
835 BOOL fCanceled;
836 ComAssert(!it->second.pProgress.isNull());
837 if (SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled)) && fCanceled)
838 {
839 it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,
840 COM_IIDOF(IGuest),
841 Guest::getStaticComponentName(),
842 Guest::tr("The output operation was canceled"));
843 }
844 else
845 it->second.pProgress->notifyComplete(S_OK);
846 }
847 else
848 LogFlowFunc(("Unexpected callback (magic=%u, context ID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));
849 return rc;
850}
851
852int Guest::notifyCtrlClientDisconnected(uint32_t u32Function,
853 PCALLBACKDATACLIENTDISCONNECTED pData)
854{
855 int rc = VINF_SUCCESS;
856
857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
858 CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
859 if (it != mCallbackMap.end())
860 {
861 LogFlowFunc(("Client with context ID=%u disconnected\n", it->first));
862 destroyCtrlCallbackContext(it);
863 }
864 return rc;
865}
866
867Guest::CallbackMapIter Guest::getCtrlCallbackContextByID(uint32_t u32ContextID)
868{
869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
870 return mCallbackMap.find(u32ContextID);
871}
872
873Guest::GuestProcessMapIter Guest::getProcessByPID(uint32_t u32PID)
874{
875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
876 return mGuestProcessMap.find(u32PID);
877}
878
879/* No locking here; */
880void Guest::destroyCtrlCallbackContext(Guest::CallbackMapIter it)
881{
882 LogFlowFunc(("Destroying callback with CID=%u ...\n", it->first));
883
884 if (it->second.pvData)
885 {
886 RTMemFree(it->second.pvData);
887 it->second.pvData = NULL;
888 it->second.cbData = 0;
889 }
890
891 /* Notify outstanding waits for progress ... */
892 if ( it->second.pProgress
893 && !it->second.pProgress.isNull())
894 {
895 LogFlowFunc(("Handling progress for CID=%u ...\n", it->first));
896
897 /*
898 * Assume we didn't complete to make sure we clean up even if the
899 * following call fails.
900 */
901 BOOL fCompleted = FALSE;
902 it->second.pProgress->COMGETTER(Completed)(&fCompleted);
903 if (!fCompleted)
904 {
905 LogFlowFunc(("Progress of CID=%u *not* completed, cancelling ...\n", it->first));
906
907 /* Only cancel if not canceled before! */
908 BOOL fCanceled;
909 if (SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled)) && !fCanceled)
910 it->second.pProgress->Cancel();
911
912 /*
913 * To get waitForCompletion completed (unblocked) we have to notify it if necessary (only
914 * cancle won't work!). This could happen if the client thread (e.g. VBoxService, thread of a spawned process)
915 * is disconnecting without having the chance to sending a status message before, so we
916 * have to abort here to make sure the host never hangs/gets stuck while waiting for the
917 * progress object to become signalled.
918 */
919 it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,
920 COM_IIDOF(IGuest),
921 Guest::getStaticComponentName(),
922 Guest::tr("The operation was canceled because client is shutting down"));
923 }
924 /*
925 * Do *not* NULL pProgress here, because waiting function like executeProcess()
926 * will still rely on this object for checking whether they have to give up!
927 */
928 }
929}
930
931/* Adds a callback with a user provided data block and an optional progress object
932 * to the callback map. A callback is identified by a unique context ID which is used
933 * to identify a callback from the guest side. */
934uint32_t Guest::addCtrlCallbackContext(eVBoxGuestCtrlCallbackType enmType, void *pvData, uint32_t cbData, Progress *pProgress)
935{
936 AssertPtr(pProgress);
937
938 /** @todo Put this stuff into a constructor! */
939 CallbackContext context;
940 context.mType = enmType;
941 context.pvData = pvData;
942 context.cbData = cbData;
943 context.pProgress = pProgress;
944
945 /* Create a new context ID and assign it. */
946 CallbackMapIter it;
947 uint32_t uNewContext = 0;
948 do
949 {
950 /* Create a new context ID ... */
951 uNewContext = ASMAtomicIncU32(&mNextContextID);
952 if (uNewContext == UINT32_MAX)
953 ASMAtomicUoWriteU32(&mNextContextID, 1000);
954 /* Is the context ID already used? */
955 it = getCtrlCallbackContextByID(uNewContext);
956 } while(it != mCallbackMap.end());
957
958 uint32_t nCallbacks = 0;
959 if ( it == mCallbackMap.end()
960 && uNewContext > 0)
961 {
962 /* We apparently got an unused context ID, let's use it! */
963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
964 mCallbackMap[uNewContext] = context;
965 nCallbacks = mCallbackMap.size();
966 }
967 else
968 {
969 /* Should never happen ... */
970 {
971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
972 nCallbacks = mCallbackMap.size();
973 }
974 AssertReleaseMsg(uNewContext, ("No free context ID found! uNewContext=%u, nCallbacks=%u", uNewContext, nCallbacks));
975 }
976
977#if 0
978 if (nCallbacks > 256) /* Don't let the container size get too big! */
979 {
980 Guest::CallbackListIter it = mCallbackList.begin();
981 destroyCtrlCallbackContext(it);
982 {
983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
984 mCallbackList.erase(it);
985 }
986 }
987#endif
988 return uNewContext;
989}
990#endif /* VBOX_WITH_GUEST_CONTROL */
991
992STDMETHODIMP Guest::ExecuteProcess(IN_BSTR aCommand, ULONG aFlags,
993 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
994 IN_BSTR aUserName, IN_BSTR aPassword,
995 ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress)
996{
997/** @todo r=bird: Eventually we should clean up all the timeout parameters
998 * in the API and have the same way of specifying infinite waits! */
999#ifndef VBOX_WITH_GUEST_CONTROL
1000 ReturnComNotImplemented();
1001#else /* VBOX_WITH_GUEST_CONTROL */
1002 using namespace guestControl;
1003
1004 CheckComArgStrNotEmptyOrNull(aCommand);
1005 CheckComArgOutPointerValid(aPID);
1006 CheckComArgOutPointerValid(aProgress);
1007
1008 /* Do not allow anonymous executions (with system rights). */
1009 if (RT_UNLIKELY((aUserName) == NULL || *(aUserName) == '\0'))
1010 return setError(E_INVALIDARG, tr("No user name specified"));
1011
1012 AutoCaller autoCaller(this);
1013 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1014
1015 /* Validate flags. */
1016 if (aFlags)
1017 {
1018 if (!(aFlags & ExecuteProcessFlag_IgnoreOrphanedProcesses))
1019 return E_INVALIDARG;
1020 }
1021
1022 HRESULT rc = S_OK;
1023
1024 try
1025 {
1026 /*
1027 * Create progress object. Note that this is a multi operation
1028 * object to perform the following steps:
1029 * - Operation 1 (0): Create/start process.
1030 * - Operation 2 (1): Wait for process to exit.
1031 * If this progress completed successfully (S_OK), the process
1032 * started and exited normally. In any other case an error/exception
1033 * occured.
1034 */
1035 ComObjPtr <Progress> progress;
1036 rc = progress.createObject();
1037 if (SUCCEEDED(rc))
1038 {
1039 rc = progress->init(static_cast<IGuest*>(this),
1040 Bstr(tr("Executing process")).raw(),
1041 TRUE,
1042 2, /* Number of operations. */
1043 Bstr(tr("Starting process ...")).raw()); /* Description of first stage. */
1044 }
1045 if (FAILED(rc)) return rc;
1046
1047 /*
1048 * Prepare process execution.
1049 */
1050 int vrc = VINF_SUCCESS;
1051 Utf8Str Utf8Command(aCommand);
1052
1053 /* Adjust timeout */
1054 if (aTimeoutMS == 0)
1055 aTimeoutMS = UINT32_MAX;
1056
1057 /* Prepare arguments. */
1058 char **papszArgv = NULL;
1059 uint32_t uNumArgs = 0;
1060 if (aArguments > 0)
1061 {
1062 com::SafeArray<IN_BSTR> args(ComSafeArrayInArg(aArguments));
1063 uNumArgs = args.size();
1064 papszArgv = (char**)RTMemAlloc(sizeof(char*) * (uNumArgs + 1));
1065 AssertReturn(papszArgv, E_OUTOFMEMORY);
1066 for (unsigned i = 0; RT_SUCCESS(vrc) && i < uNumArgs; i++)
1067 vrc = RTUtf16ToUtf8(args[i], &papszArgv[i]);
1068 papszArgv[uNumArgs] = NULL;
1069 }
1070
1071 Utf8Str Utf8UserName(aUserName);
1072 Utf8Str Utf8Password(aPassword);
1073 if (RT_SUCCESS(vrc))
1074 {
1075 uint32_t uContextID = 0;
1076
1077 char *pszArgs = NULL;
1078 if (uNumArgs > 0)
1079 vrc = RTGetOptArgvToString(&pszArgs, papszArgv, 0);
1080 if (RT_SUCCESS(vrc))
1081 {
1082 uint32_t cbArgs = pszArgs ? strlen(pszArgs) + 1 : 0; /* Include terminating zero. */
1083
1084 /* Prepare environment. */
1085 void *pvEnv = NULL;
1086 uint32_t uNumEnv = 0;
1087 uint32_t cbEnv = 0;
1088 if (aEnvironment > 0)
1089 {
1090 com::SafeArray<IN_BSTR> env(ComSafeArrayInArg(aEnvironment));
1091
1092 for (unsigned i = 0; i < env.size(); i++)
1093 {
1094 vrc = prepareExecuteEnv(Utf8Str(env[i]).c_str(), &pvEnv, &cbEnv, &uNumEnv);
1095 if (RT_FAILURE(vrc))
1096 break;
1097 }
1098 }
1099
1100 LogRel(("Executing guest process \"%s\" as user \"%s\" ...\n",
1101 Utf8Command.c_str(), Utf8UserName.c_str()));
1102
1103 if (RT_SUCCESS(vrc))
1104 {
1105 PCALLBACKDATAEXECSTATUS pData = (PCALLBACKDATAEXECSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECSTATUS));
1106 AssertReturn(pData, VBOX_E_IPRT_ERROR);
1107 RT_ZERO(*pData);
1108 uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_START,
1109 pData, sizeof(CALLBACKDATAEXECSTATUS), progress);
1110 Assert(uContextID > 0);
1111
1112 VBOXHGCMSVCPARM paParms[15];
1113 int i = 0;
1114 paParms[i++].setUInt32(uContextID);
1115 paParms[i++].setPointer((void*)Utf8Command.c_str(), (uint32_t)Utf8Command.length() + 1);
1116 paParms[i++].setUInt32(aFlags);
1117 paParms[i++].setUInt32(uNumArgs);
1118 paParms[i++].setPointer((void*)pszArgs, cbArgs);
1119 paParms[i++].setUInt32(uNumEnv);
1120 paParms[i++].setUInt32(cbEnv);
1121 paParms[i++].setPointer((void*)pvEnv, cbEnv);
1122 paParms[i++].setPointer((void*)Utf8UserName.c_str(), (uint32_t)Utf8UserName.length() + 1);
1123 paParms[i++].setPointer((void*)Utf8Password.c_str(), (uint32_t)Utf8Password.length() + 1);
1124 paParms[i++].setUInt32(aTimeoutMS);
1125
1126 VMMDev *vmmDev;
1127 {
1128 /* Make sure mParent is valid, so set the read lock while using.
1129 * Do not keep this lock while doing the actual call, because in the meanwhile
1130 * another thread could request a write lock which would be a bad idea ... */
1131 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1132
1133 /* Forward the information to the VMM device. */
1134 AssertPtr(mParent);
1135 vmmDev = mParent->getVMMDev();
1136 }
1137
1138 if (vmmDev)
1139 {
1140 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
1141 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_CMD,
1142 i, paParms);
1143 }
1144 else
1145 vrc = VERR_INVALID_VM_HANDLE;
1146 RTMemFree(pvEnv);
1147 }
1148 RTStrFree(pszArgs);
1149 }
1150 if (RT_SUCCESS(vrc))
1151 {
1152 LogFlowFunc(("Waiting for HGCM callback (timeout=%ldms) ...\n", aTimeoutMS));
1153
1154 /*
1155 * Wait for the HGCM low level callback until the process
1156 * has been started (or something went wrong). This is necessary to
1157 * get the PID.
1158 */
1159 CallbackMapIter it = getCtrlCallbackContextByID(uContextID);
1160 BOOL fCanceled = FALSE;
1161 if (it != mCallbackMap.end())
1162 {
1163 ComAssert(!it->second.pProgress.isNull());
1164
1165 /*
1166 * Wait for the first stage (=0) to complete (that is starting the process).
1167 */
1168 PCALLBACKDATAEXECSTATUS pData = NULL;
1169 rc = it->second.pProgress->WaitForOperationCompletion(0, aTimeoutMS);
1170 if (SUCCEEDED(rc))
1171 {
1172 /* Was the operation canceled by one of the parties? */
1173 rc = it->second.pProgress->COMGETTER(Canceled)(&fCanceled);
1174 if (FAILED(rc)) throw rc;
1175
1176 if (!fCanceled)
1177 {
1178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1179
1180 pData = (PCALLBACKDATAEXECSTATUS)it->second.pvData;
1181 Assert(it->second.cbData == sizeof(CALLBACKDATAEXECSTATUS));
1182 AssertPtr(pData);
1183
1184 /* Did we get some status? */
1185 switch (pData->u32Status)
1186 {
1187 case PROC_STS_STARTED:
1188 /* Process is (still) running; get PID. */
1189 *aPID = pData->u32PID;
1190 break;
1191
1192 /* In any other case the process either already
1193 * terminated or something else went wrong, so no PID ... */
1194 case PROC_STS_TEN: /* Terminated normally. */
1195 case PROC_STS_TEA: /* Terminated abnormally. */
1196 case PROC_STS_TES: /* Terminated through signal. */
1197 case PROC_STS_TOK:
1198 case PROC_STS_TOA:
1199 case PROC_STS_DWN:
1200 /*
1201 * Process (already) ended, but we want to get the
1202 * PID anyway to retrieve the output in a later call.
1203 */
1204 *aPID = pData->u32PID;
1205 break;
1206
1207 case PROC_STS_ERROR:
1208 vrc = pData->u32Flags; /* u32Flags member contains IPRT error code. */
1209 break;
1210
1211 case PROC_STS_UNDEFINED:
1212 vrc = VERR_TIMEOUT; /* Operation did not complete within time. */
1213 break;
1214
1215 default:
1216 vrc = VERR_INVALID_PARAMETER; /* Unknown status, should never happen! */
1217 break;
1218 }
1219 }
1220 else /* Operation was canceled. */
1221 vrc = VERR_CANCELLED;
1222 }
1223 else /* Operation did not complete within time. */
1224 vrc = VERR_TIMEOUT;
1225
1226 /*
1227 * Do *not* remove the callback yet - we might wait with the IProgress object on something
1228 * else (like end of process) ...
1229 */
1230 if (RT_FAILURE(vrc))
1231 {
1232 if (vrc == VERR_FILE_NOT_FOUND) /* This is the most likely error. */
1233 rc = setError(VBOX_E_IPRT_ERROR,
1234 tr("The file '%s' was not found on guest"), Utf8Command.c_str());
1235 else if (vrc == VERR_PATH_NOT_FOUND)
1236 rc = setError(VBOX_E_IPRT_ERROR,
1237 tr("The path to file '%s' was not found on guest"), Utf8Command.c_str());
1238 else if (vrc == VERR_BAD_EXE_FORMAT)
1239 rc = setError(VBOX_E_IPRT_ERROR,
1240 tr("The file '%s' is not an executable format on guest"), Utf8Command.c_str());
1241 else if (vrc == VERR_AUTHENTICATION_FAILURE)
1242 rc = setError(VBOX_E_IPRT_ERROR,
1243 tr("The specified user '%s' was not able to logon on guest"), Utf8UserName.c_str());
1244 else if (vrc == VERR_TIMEOUT)
1245 rc = setError(VBOX_E_IPRT_ERROR,
1246 tr("The guest did not respond within time (%ums)"), aTimeoutMS);
1247 else if (vrc == VERR_CANCELLED)
1248 rc = setError(VBOX_E_IPRT_ERROR,
1249 tr("The execution operation was canceled"));
1250 else if (vrc == VERR_PERMISSION_DENIED)
1251 rc = setError(VBOX_E_IPRT_ERROR,
1252 tr("Invalid user/password credentials"));
1253 else
1254 {
1255 if (pData && pData->u32Status == PROC_STS_ERROR)
1256 rc = setError(VBOX_E_IPRT_ERROR,
1257 tr("Process could not be started: %Rrc"), pData->u32Flags);
1258 else
1259 rc = setError(E_UNEXPECTED,
1260 tr("The service call failed with error %Rrc"), vrc);
1261 }
1262 }
1263 else /* Execution went fine. */
1264 {
1265 /* Return the progress to the caller. */
1266 progress.queryInterfaceTo(aProgress);
1267 }
1268 }
1269 else /* Callback context not found; should never happen! */
1270 AssertMsg(it != mCallbackMap.end(), ("Callback context with ID %u not found!", uContextID));
1271 }
1272 else /* HGCM related error codes .*/
1273 {
1274 if (vrc == VERR_INVALID_VM_HANDLE)
1275 rc = setError(VBOX_E_VM_ERROR,
1276 tr("VMM device is not available (is the VM running?)"));
1277 else if (vrc == VERR_TIMEOUT)
1278 rc = setError(VBOX_E_VM_ERROR,
1279 tr("The guest execution service is not ready"));
1280 else if (vrc == VERR_HGCM_SERVICE_NOT_FOUND)
1281 rc = setError(VBOX_E_VM_ERROR,
1282 tr("The guest execution service is not available"));
1283 else /* HGCM call went wrong. */
1284 rc = setError(E_UNEXPECTED,
1285 tr("The HGCM call failed with error %Rrc"), vrc);
1286 }
1287
1288 for (unsigned i = 0; i < uNumArgs; i++)
1289 RTMemFree(papszArgv[i]);
1290 RTMemFree(papszArgv);
1291 }
1292
1293 if (RT_FAILURE(vrc))
1294 LogRel(("Executing guest process \"%s\" as user \"%s\" failed with %Rrc\n",
1295 Utf8Command.c_str(), Utf8UserName.c_str(), vrc));
1296 }
1297 catch (std::bad_alloc &)
1298 {
1299 rc = E_OUTOFMEMORY;
1300 }
1301 return rc;
1302#endif /* VBOX_WITH_GUEST_CONTROL */
1303}
1304
1305STDMETHODIMP Guest::GetProcessOutput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
1306{
1307/** @todo r=bird: Eventually we should clean up all the timeout parameters
1308 * in the API and have the same way of specifying infinite waits! */
1309#ifndef VBOX_WITH_GUEST_CONTROL
1310 ReturnComNotImplemented();
1311#else /* VBOX_WITH_GUEST_CONTROL */
1312 using namespace guestControl;
1313
1314 CheckComArgExpr(aPID, aPID > 0);
1315 if (aSize < 0)
1316 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
1317 if (aFlags != 0) /* Flags are not supported at the moment. */
1318 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
1319
1320 AutoCaller autoCaller(this);
1321 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1322
1323 HRESULT rc = S_OK;
1324
1325 try
1326 {
1327 /*
1328 * Create progress object.
1329 * This progress object, compared to the one in executeProgress() above
1330 * is only local and is used to determine whether the operation finished
1331 * or got canceled.
1332 */
1333 ComObjPtr <Progress> progress;
1334 rc = progress.createObject();
1335 if (SUCCEEDED(rc))
1336 {
1337 rc = progress->init(static_cast<IGuest*>(this),
1338 Bstr(tr("Getting output of process")).raw(),
1339 TRUE);
1340 }
1341 if (FAILED(rc)) return rc;
1342
1343 /* Adjust timeout */
1344 if (aTimeoutMS == 0)
1345 aTimeoutMS = UINT32_MAX;
1346
1347 /* Search for existing PID. */
1348 PCALLBACKDATAEXECOUT pData = (CALLBACKDATAEXECOUT*)RTMemAlloc(sizeof(CALLBACKDATAEXECOUT));
1349 AssertReturn(pData, VBOX_E_IPRT_ERROR);
1350 RT_ZERO(*pData);
1351 /* Save PID + output flags for later use. */
1352 pData->u32PID = aPID;
1353 pData->u32Flags = aFlags;
1354 /* Add job to callback contexts. */
1355 uint32_t uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT,
1356 pData, sizeof(CALLBACKDATAEXECOUT), progress);
1357 Assert(uContextID > 0);
1358
1359 size_t cbData = (size_t)RT_MIN(aSize, _64K);
1360 com::SafeArray<BYTE> outputData(cbData);
1361
1362 VBOXHGCMSVCPARM paParms[5];
1363 int i = 0;
1364 paParms[i++].setUInt32(uContextID);
1365 paParms[i++].setUInt32(aPID);
1366 paParms[i++].setUInt32(aFlags); /** @todo Should represent stdout and/or stderr. */
1367
1368 int vrc = VINF_SUCCESS;
1369
1370 {
1371 VMMDev *vmmDev;
1372 {
1373 /* Make sure mParent is valid, so set the read lock while using.
1374 * Do not keep this lock while doing the actual call, because in the meanwhile
1375 * another thread could request a write lock which would be a bad idea ... */
1376 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1377
1378 /* Forward the information to the VMM device. */
1379 AssertPtr(mParent);
1380 vmmDev = mParent->getVMMDev();
1381 }
1382
1383 if (vmmDev)
1384 {
1385 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
1386 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_GET_OUTPUT,
1387 i, paParms);
1388 }
1389 }
1390
1391 if (RT_SUCCESS(vrc))
1392 {
1393 LogFlowFunc(("Waiting for HGCM callback (timeout=%ldms) ...\n", aTimeoutMS));
1394
1395 /*
1396 * Wait for the HGCM low level callback until the process
1397 * has been started (or something went wrong). This is necessary to
1398 * get the PID.
1399 */
1400 CallbackMapIter it = getCtrlCallbackContextByID(uContextID);
1401 BOOL fCanceled = FALSE;
1402 if (it != mCallbackMap.end())
1403 {
1404 ComAssert(!it->second.pProgress.isNull());
1405
1406 /* Wait until operation completed. */
1407 rc = it->second.pProgress->WaitForCompletion(aTimeoutMS);
1408 if (FAILED(rc)) throw rc;
1409
1410 /* Was the operation canceled by one of the parties? */
1411 rc = it->second.pProgress->COMGETTER(Canceled)(&fCanceled);
1412 if (FAILED(rc)) throw rc;
1413
1414 if (!fCanceled)
1415 {
1416 BOOL fCompleted;
1417 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))
1418 && fCompleted)
1419 {
1420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1421
1422 /* Did we get some output? */
1423 pData = (PCALLBACKDATAEXECOUT)it->second.pvData;
1424 Assert(it->second.cbData == sizeof(CALLBACKDATAEXECOUT));
1425 AssertPtr(pData);
1426
1427 if (pData->cbData)
1428 {
1429 /* Do we need to resize the array? */
1430 if (pData->cbData > cbData)
1431 outputData.resize(pData->cbData);
1432
1433 /* Fill output in supplied out buffer. */
1434 memcpy(outputData.raw(), pData->pvData, pData->cbData);
1435 outputData.resize(pData->cbData); /* Shrink to fit actual buffer size. */
1436 }
1437 else
1438 vrc = VERR_NO_DATA; /* This is not an error we want to report to COM. */
1439 }
1440 else /* If callback not called within time ... well, that's a timeout! */
1441 vrc = VERR_TIMEOUT;
1442 }
1443 else /* Operation was canceled. */
1444 {
1445 vrc = VERR_CANCELLED;
1446 }
1447
1448 if (RT_FAILURE(vrc))
1449 {
1450 if (vrc == VERR_NO_DATA)
1451 {
1452 /* This is not an error we want to report to COM. */
1453 rc = S_OK;
1454 }
1455 else if (vrc == VERR_TIMEOUT)
1456 {
1457 rc = setError(VBOX_E_IPRT_ERROR,
1458 tr("The guest did not output within time (%ums)"), aTimeoutMS);
1459 }
1460 else if (vrc == VERR_CANCELLED)
1461 {
1462 rc = setError(VBOX_E_IPRT_ERROR,
1463 tr("The output operation was canceled"));
1464 }
1465 else
1466 {
1467 rc = setError(E_UNEXPECTED,
1468 tr("The service call failed with error %Rrc"), vrc);
1469 }
1470 }
1471
1472 {
1473 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1474 /*
1475 * Destroy locally used progress object.
1476 */
1477 destroyCtrlCallbackContext(it);
1478 }
1479
1480 /* Remove callback context (not used anymore). */
1481 mCallbackMap.erase(it);
1482 }
1483 else /* PID lookup failed. */
1484 rc = setError(VBOX_E_IPRT_ERROR,
1485 tr("Process (PID %u) not found!"), aPID);
1486 }
1487 else /* HGCM operation failed. */
1488 rc = setError(E_UNEXPECTED,
1489 tr("The HGCM call failed with error %Rrc"), vrc);
1490
1491 /* Cleanup. */
1492 progress->uninit();
1493 progress.setNull();
1494
1495 /* If something failed (or there simply was no data, indicated by VERR_NO_DATA,
1496 * we return an empty array so that the frontend knows when to give up. */
1497 if (RT_FAILURE(vrc) || FAILED(rc))
1498 outputData.resize(0);
1499 outputData.detachTo(ComSafeArrayOutArg(aData));
1500 }
1501 catch (std::bad_alloc &)
1502 {
1503 rc = E_OUTOFMEMORY;
1504 }
1505 return rc;
1506#endif
1507}
1508
1509STDMETHODIMP Guest::GetProcessStatus(ULONG aPID, ULONG *aExitCode, ULONG *aFlags, ULONG *aStatus)
1510{
1511#ifndef VBOX_WITH_GUEST_CONTROL
1512 ReturnComNotImplemented();
1513#else /* VBOX_WITH_GUEST_CONTROL */
1514 using namespace guestControl;
1515
1516 AutoCaller autoCaller(this);
1517 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1518
1519 HRESULT rc = S_OK;
1520
1521 try
1522 {
1523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1524
1525 GuestProcessMapIterConst it = getProcessByPID(aPID);
1526 if (it != mGuestProcessMap.end())
1527 {
1528 *aExitCode = it->second.mExitCode;
1529 *aFlags = it->second.mFlags;
1530 *aStatus = it->second.mStatus;
1531 }
1532 else
1533 rc = setError(VBOX_E_IPRT_ERROR,
1534 tr("Process (PID %u) not found!"), aPID);
1535 }
1536 catch (std::bad_alloc &)
1537 {
1538 rc = E_OUTOFMEMORY;
1539 }
1540 return rc;
1541#endif
1542}
1543
1544#ifdef VBOX_WITH_COPYTOGUEST
1545int Guest::directoryEntryAppend(const char *pszPath, PRTLISTNODE pList)
1546{
1547 using namespace guestControl;
1548
1549 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1550 AssertPtrReturn(pList, VERR_INVALID_POINTER);
1551
1552 LogFlowFunc(("Appending to pList=%p: %s\n", pList, pszPath));
1553
1554 DirEntry *pNode = (DirEntry*)RTMemAlloc(sizeof(DirEntry));
1555 if (pNode == NULL)
1556 return VERR_NO_MEMORY;
1557
1558 if (RT_SUCCESS(RTStrAAppend(&pNode->pszPath, pszPath)))
1559 {
1560 pNode->Node.pPrev = NULL;
1561 pNode->Node.pNext = NULL;
1562 RTListAppend(pList, &pNode->Node);
1563 return VINF_SUCCESS;
1564 }
1565 return VERR_NO_MEMORY;
1566}
1567
1568int Guest::directoryRead(const char *pszDirectory, const char *pszFilter,
1569 ULONG uFlags, ULONG *pcObjects, PRTLISTNODE pList)
1570{
1571 using namespace guestControl;
1572
1573 AssertPtrReturn(pszDirectory, VERR_INVALID_POINTER);
1574 /* Filter is optional. */
1575 AssertPtrReturn(pcObjects, VERR_INVALID_POINTER);
1576 AssertPtrReturn(pList, VERR_INVALID_POINTER);
1577
1578 LogFlowFunc(("Reading directory: %s, filter: %s\n",
1579 pszDirectory, pszFilter ? pszFilter : "<None>"));
1580
1581 char *pszDirWithFilter = NULL;
1582 PRTDIR pDir;
1583 int rc = RTStrAAppend(&pszDirWithFilter, pszDirectory);
1584 if (RT_SUCCESS(rc))
1585 {
1586 if (pszFilter)
1587 {
1588 RTStrAAppend(&pszDirWithFilter, pszDirectory);
1589 rc = RTDirOpenFiltered(&pDir, pszDirectory, RTDIRFILTER_WINNT);
1590 }
1591 else
1592 rc = RTDirOpen(&pDir, pszDirectory);
1593 }
1594
1595 if (RT_SUCCESS(rc))
1596 {
1597 rc = directoryEntryAppend(pszDirectory, pList);
1598 if (RT_SUCCESS(rc))
1599 {
1600 *pcObjects = *pcObjects + 1;
1601 for (;;)
1602 {
1603 RTDIRENTRY DirEntry;
1604 rc = RTDirRead(pDir, &DirEntry, NULL);
1605 if (RT_FAILURE(rc))
1606 break;
1607 switch (DirEntry.enmType)
1608 {
1609 case RTDIRENTRYTYPE_DIRECTORY:
1610 if (uFlags & CopyFileFlag_Recursive)
1611 rc = directoryRead(DirEntry.szName, pszFilter,
1612 uFlags, pcObjects, pList);
1613 break;
1614
1615 case RTDIRENTRYTYPE_FILE:
1616 {
1617 char *pszFile;
1618 if (RTStrAPrintf(&pszFile, "%s/%s",
1619 pszDirectory, DirEntry.szName))
1620 {
1621 rc = directoryEntryAppend(pszFile, pList);
1622 if (RT_SUCCESS(rc))
1623 *pcObjects = *pcObjects + 1;
1624 RTStrFree(pszFile);
1625 }
1626 break;
1627 }
1628
1629 case RTDIRENTRYTYPE_SYMLINK:
1630 if ( (uFlags & CopyFileFlag_Recursive)
1631 && (uFlags & CopyFileFlag_FollowLinks))
1632 {
1633 rc = directoryRead(DirEntry.szName, pszFilter,
1634 uFlags, pcObjects, pList);
1635 }
1636 break;
1637
1638 default:
1639 break;
1640 }
1641 if (RT_FAILURE(rc))
1642 break;
1643 }
1644 }
1645 RTDirClose(pDir);
1646 }
1647 if (pszDirWithFilter)
1648 RTStrFree(pszDirWithFilter);
1649 return rc;
1650}
1651#endif
1652
1653STDMETHODIMP Guest::CopyToGuest(IN_BSTR aSource, IN_BSTR aDest, ULONG aFlags,
1654 IProgress **aProgress)
1655{
1656#ifndef VBOX_WITH_GUEST_CONTROL
1657 ReturnComNotImplemented();
1658#else /* VBOX_WITH_GUEST_CONTROL */
1659#ifndef VBOX_WITH_COPYTOGUEST
1660 ReturnComNotImplemented();
1661#else
1662 using namespace guestControl;
1663
1664 CheckComArgStrNotEmptyOrNull(aSource);
1665 CheckComArgStrNotEmptyOrNull(aDest);
1666 CheckComArgOutPointerValid(aProgress);
1667
1668 AutoCaller autoCaller(this);
1669 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1670
1671 /* Validate flags. */
1672 if (aFlags)
1673 {
1674 if ( !(aFlags & CopyFileFlag_Recursive)
1675 && !(aFlags & CopyFileFlag_Update)
1676 && !(aFlags & CopyFileFlag_FollowLinks))
1677 return E_INVALIDARG;
1678 }
1679
1680 HRESULT rc = S_OK;
1681
1682 try
1683 {
1684 char szSourceAbs[RTPATH_MAX];
1685 Utf8Str Utf8Dest(aDest);
1686
1687 int vrc = RTPathAbs(Utf8Str(aSource).c_str(),
1688 szSourceAbs, sizeof(szSourceAbs));
1689 if (RT_SUCCESS(vrc))
1690 {
1691 LogRel(("Copying \"%s\" to guest into \"%s\" ...\n",
1692 szSourceAbs, Utf8Dest.c_str()));
1693 }
1694 else
1695 rc = setError(VBOX_E_IPRT_ERROR,
1696 tr("Could not determine absolute path! rc=%Rrc"), vrc);
1697
1698 ULONG cObjectsToCopy = 0;
1699 RTLISTNODE listEntries;
1700 if (SUCCEEDED(rc))
1701 {
1702 /*
1703 * Count objects to copy.
1704 * This is needed for have multi operation progress object.
1705 * Examples:
1706 * D:\ -> D:\*
1707 * E:\asdf\qwer\ -> E:\asdf\qwer\*
1708 * C:\temp.txt
1709 * C:\Foo\bar\*.txt
1710 * /home/foo/bar/movie.avi
1711 */
1712 char *pszFileName = RTPathFilename(szSourceAbs);
1713 RTListInit(&listEntries);
1714
1715 vrc = directoryRead(szSourceAbs, pszFileName /* Filter */,
1716 aFlags, &cObjectsToCopy, &listEntries);
1717 if (RT_FAILURE(vrc))
1718 rc = setError(VBOX_E_IPRT_ERROR,
1719 tr("Could not open source directory! rc=%Rrc"), vrc);
1720 }
1721
1722 if (SUCCEEDED(rc))
1723 {
1724 /*
1725 * Create progress object. Note that this is a multi operation
1726 * object to perform an operation per the following steps:
1727 * - Operation 1 (0): Create/start process.
1728 * - Operation 2 (1): Wait for process to exit.
1729 * If this progress completed successfully (S_OK), the process
1730 * started and exited normally. In any other case an error/exception
1731 * occured.
1732 */
1733 ComObjPtr <Progress> progress;
1734 rc = progress.createObject();
1735 if (SUCCEEDED(rc))
1736 {
1737 rc = progress->init(static_cast<IGuest*>(this),
1738 Bstr(tr("Executing process")).raw(),
1739 TRUE,
1740 cObjectsToCopy, /* Number of operations. */
1741 Bstr(tr("Starting process ...")).raw()); /* Description of first stage. */
1742 }
1743 if (FAILED(rc)) return rc;
1744 }
1745
1746 /* Destroy list. */
1747 DirEntry *pNode = RTListNodeGetFirst(&listEntries, DirEntry, Node);
1748 while (pNode)
1749 {
1750 DirEntry *pNext = RTListNodeGetNext(&pNode->Node, DirEntry, Node);
1751 bool fLast = RTListNodeIsLast(&listEntries, &pNode->Node);
1752
1753 if (pNode->pszPath)
1754 RTStrFree(pNode->pszPath);
1755 RTListNodeRemove(&pNode->Node);
1756 RTMemFree(pNode);
1757
1758 if (fLast)
1759 break;
1760
1761 pNode = pNext;
1762 }
1763 }
1764
1765#if 0
1766
1767 /*
1768 * Prepare process execution.
1769 */
1770 int vrc = VINF_SUCCESS;
1771 Utf8Str Utf8Command(aCommand);
1772
1773 /* Adjust timeout */
1774 if (aTimeoutMS == 0)
1775 aTimeoutMS = UINT32_MAX;
1776
1777 /* Prepare arguments. */
1778 char **papszArgv = NULL;
1779 uint32_t uNumArgs = 0;
1780 if (aArguments > 0)
1781 {
1782 com::SafeArray<IN_BSTR> args(ComSafeArrayInArg(aArguments));
1783 uNumArgs = args.size();
1784 papszArgv = (char**)RTMemAlloc(sizeof(char*) * (uNumArgs + 1));
1785 AssertReturn(papszArgv, E_OUTOFMEMORY);
1786 for (unsigned i = 0; RT_SUCCESS(vrc) && i < uNumArgs; i++)
1787 vrc = RTUtf16ToUtf8(args[i], &papszArgv[i]);
1788 papszArgv[uNumArgs] = NULL;
1789 }
1790
1791 Utf8Str Utf8UserName(aUserName);
1792 Utf8Str Utf8Password(aPassword);
1793 if (RT_SUCCESS(vrc))
1794 {
1795 uint32_t uContextID = 0;
1796
1797 char *pszArgs = NULL;
1798 if (uNumArgs > 0)
1799 vrc = RTGetOptArgvToString(&pszArgs, papszArgv, 0);
1800 if (RT_SUCCESS(vrc))
1801 {
1802 uint32_t cbArgs = pszArgs ? strlen(pszArgs) + 1 : 0; /* Include terminating zero. */
1803
1804 /* Prepare environment. */
1805 void *pvEnv = NULL;
1806 uint32_t uNumEnv = 0;
1807 uint32_t cbEnv = 0;
1808 if (aEnvironment > 0)
1809 {
1810 com::SafeArray<IN_BSTR> env(ComSafeArrayInArg(aEnvironment));
1811
1812 for (unsigned i = 0; i < env.size(); i++)
1813 {
1814 vrc = prepareExecuteEnv(Utf8Str(env[i]).c_str(), &pvEnv, &cbEnv, &uNumEnv);
1815 if (RT_FAILURE(vrc))
1816 break;
1817 }
1818 }
1819
1820 LogRel(("Executing guest process \"%s\" as user \"%s\" ...\n",
1821 Utf8Command.c_str(), Utf8UserName.c_str()));
1822
1823 if (RT_SUCCESS(vrc))
1824 {
1825 PCALLBACKDATAEXECSTATUS pData = (PCALLBACKDATAEXECSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECSTATUS));
1826 AssertReturn(pData, VBOX_E_IPRT_ERROR);
1827 RT_ZERO(*pData);
1828 uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_START,
1829 pData, sizeof(CALLBACKDATAEXECSTATUS), progress);
1830 Assert(uContextID > 0);
1831
1832 VBOXHGCMSVCPARM paParms[15];
1833 int i = 0;
1834 paParms[i++].setUInt32(uContextID);
1835 paParms[i++].setPointer((void*)Utf8Command.c_str(), (uint32_t)Utf8Command.length() + 1);
1836 paParms[i++].setUInt32(aFlags);
1837 paParms[i++].setUInt32(uNumArgs);
1838 paParms[i++].setPointer((void*)pszArgs, cbArgs);
1839 paParms[i++].setUInt32(uNumEnv);
1840 paParms[i++].setUInt32(cbEnv);
1841 paParms[i++].setPointer((void*)pvEnv, cbEnv);
1842 paParms[i++].setPointer((void*)Utf8UserName.c_str(), (uint32_t)Utf8UserName.length() + 1);
1843 paParms[i++].setPointer((void*)Utf8Password.c_str(), (uint32_t)Utf8Password.length() + 1);
1844 paParms[i++].setUInt32(aTimeoutMS);
1845
1846 VMMDev *vmmDev;
1847 {
1848 /* Make sure mParent is valid, so set the read lock while using.
1849 * Do not keep this lock while doing the actual call, because in the meanwhile
1850 * another thread could request a write lock which would be a bad idea ... */
1851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1852
1853 /* Forward the information to the VMM device. */
1854 AssertPtr(mParent);
1855 vmmDev = mParent->getVMMDev();
1856 }
1857
1858 if (vmmDev)
1859 {
1860 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
1861 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_CMD,
1862 i, paParms);
1863 }
1864 else
1865 vrc = VERR_INVALID_VM_HANDLE;
1866 RTMemFree(pvEnv);
1867 }
1868 RTStrFree(pszArgs);
1869 }
1870 if (RT_SUCCESS(vrc))
1871 {
1872 LogFlowFunc(("Waiting for HGCM callback (timeout=%ldms) ...\n", aTimeoutMS));
1873
1874 /*
1875 * Wait for the HGCM low level callback until the process
1876 * has been started (or something went wrong). This is necessary to
1877 * get the PID.
1878 */
1879 CallbackMapIter it = getCtrlCallbackContextByID(uContextID);
1880 BOOL fCanceled = FALSE;
1881 if (it != mCallbackMap.end())
1882 {
1883 ComAssert(!it->second.pProgress.isNull());
1884
1885 /*
1886 * Wait for the first stage (=0) to complete (that is starting the process).
1887 */
1888 PCALLBACKDATAEXECSTATUS pData = NULL;
1889 rc = it->second.pProgress->WaitForOperationCompletion(0, aTimeoutMS);
1890 if (SUCCEEDED(rc))
1891 {
1892 /* Was the operation canceled by one of the parties? */
1893 rc = it->second.pProgress->COMGETTER(Canceled)(&fCanceled);
1894 if (FAILED(rc)) throw rc;
1895
1896 if (!fCanceled)
1897 {
1898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1899
1900 pData = (PCALLBACKDATAEXECSTATUS)it->second.pvData;
1901 Assert(it->second.cbData == sizeof(CALLBACKDATAEXECSTATUS));
1902 AssertPtr(pData);
1903
1904 /* Did we get some status? */
1905 switch (pData->u32Status)
1906 {
1907 case PROC_STS_STARTED:
1908 /* Process is (still) running; get PID. */
1909 *aPID = pData->u32PID;
1910 break;
1911
1912 /* In any other case the process either already
1913 * terminated or something else went wrong, so no PID ... */
1914 case PROC_STS_TEN: /* Terminated normally. */
1915 case PROC_STS_TEA: /* Terminated abnormally. */
1916 case PROC_STS_TES: /* Terminated through signal. */
1917 case PROC_STS_TOK:
1918 case PROC_STS_TOA:
1919 case PROC_STS_DWN:
1920 /*
1921 * Process (already) ended, but we want to get the
1922 * PID anyway to retrieve the output in a later call.
1923 */
1924 *aPID = pData->u32PID;
1925 break;
1926
1927 case PROC_STS_ERROR:
1928 vrc = pData->u32Flags; /* u32Flags member contains IPRT error code. */
1929 break;
1930
1931 case PROC_STS_UNDEFINED:
1932 vrc = VERR_TIMEOUT; /* Operation did not complete within time. */
1933 break;
1934
1935 default:
1936 vrc = VERR_INVALID_PARAMETER; /* Unknown status, should never happen! */
1937 break;
1938 }
1939 }
1940 else /* Operation was canceled. */
1941 vrc = VERR_CANCELLED;
1942 }
1943 else /* Operation did not complete within time. */
1944 vrc = VERR_TIMEOUT;
1945
1946 /*
1947 * Do *not* remove the callback yet - we might wait with the IProgress object on something
1948 * else (like end of process) ...
1949 */
1950 if (RT_FAILURE(vrc))
1951 {
1952 if (vrc == VERR_FILE_NOT_FOUND) /* This is the most likely error. */
1953 rc = setError(VBOX_E_IPRT_ERROR,
1954 tr("The file '%s' was not found on guest"), Utf8Command.c_str());
1955 else if (vrc == VERR_PATH_NOT_FOUND)
1956 rc = setError(VBOX_E_IPRT_ERROR,
1957 tr("The path to file '%s' was not found on guest"), Utf8Command.c_str());
1958 else if (vrc == VERR_BAD_EXE_FORMAT)
1959 rc = setError(VBOX_E_IPRT_ERROR,
1960 tr("The file '%s' is not an executable format on guest"), Utf8Command.c_str());
1961 else if (vrc == VERR_AUTHENTICATION_FAILURE)
1962 rc = setError(VBOX_E_IPRT_ERROR,
1963 tr("The specified user '%s' was not able to logon on guest"), Utf8UserName.c_str());
1964 else if (vrc == VERR_TIMEOUT)
1965 rc = setError(VBOX_E_IPRT_ERROR,
1966 tr("The guest did not respond within time (%ums)"), aTimeoutMS);
1967 else if (vrc == VERR_CANCELLED)
1968 rc = setError(VBOX_E_IPRT_ERROR,
1969 tr("The execution operation was canceled"));
1970 else if (vrc == VERR_PERMISSION_DENIED)
1971 rc = setError(VBOX_E_IPRT_ERROR,
1972 tr("Invalid user/password credentials"));
1973 else
1974 {
1975 if (pData && pData->u32Status == PROC_STS_ERROR)
1976 rc = setError(VBOX_E_IPRT_ERROR,
1977 tr("Process could not be started: %Rrc"), pData->u32Flags);
1978 else
1979 rc = setError(E_UNEXPECTED,
1980 tr("The service call failed with error %Rrc"), vrc);
1981 }
1982 }
1983 else /* Execution went fine. */
1984 {
1985 /* Return the progress to the caller. */
1986 progress.queryInterfaceTo(aProgress);
1987 }
1988 }
1989 else /* Callback context not found; should never happen! */
1990 AssertMsg(it != mCallbackMap.end(), ("Callback context with ID %u not found!", uContextID));
1991 }
1992 else /* HGCM related error codes .*/
1993 {
1994 if (vrc == VERR_INVALID_VM_HANDLE)
1995 rc = setError(VBOX_E_VM_ERROR,
1996 tr("VMM device is not available (is the VM running?)"));
1997 else if (vrc == VERR_TIMEOUT)
1998 rc = setError(VBOX_E_VM_ERROR,
1999 tr("The guest execution service is not ready"));
2000 else if (vrc == VERR_HGCM_SERVICE_NOT_FOUND)
2001 rc = setError(VBOX_E_VM_ERROR,
2002 tr("The guest execution service is not available"));
2003 else /* HGCM call went wrong. */
2004 rc = setError(E_UNEXPECTED,
2005 tr("The HGCM call failed with error %Rrc"), vrc);
2006 }
2007
2008 for (unsigned i = 0; i < uNumArgs; i++)
2009 RTMemFree(papszArgv[i]);
2010 RTMemFree(papszArgv);
2011 }
2012
2013 if (RT_FAILURE(vrc))
2014 LogRel(("Executing guest process \"%s\" as user \"%s\" failed with %Rrc\n",
2015 Utf8Command.c_str(), Utf8UserName.c_str(), vrc));
2016#endif
2017 catch (std::bad_alloc &)
2018 {
2019 rc = E_OUTOFMEMORY;
2020 }
2021 return rc;
2022#endif /* VBOX_WITH_COPYTOGUEST */
2023#endif /* VBOX_WITH_GUEST_CONTROL */
2024}
2025
2026// public methods only for internal purposes
2027/////////////////////////////////////////////////////////////////////////////
2028
2029/**
2030 * Sets the general Guest Additions information like
2031 * API (interface) version and OS type. Gets called by
2032 * vmmdevUpdateGuestInfo.
2033 *
2034 * @param aInterfaceVersion
2035 * @param aOsType
2036 */
2037void Guest::setAdditionsInfo(Bstr aInterfaceVersion, VBOXOSTYPE aOsType)
2038{
2039 AutoCaller autoCaller(this);
2040 AssertComRCReturnVoid (autoCaller.rc());
2041
2042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2043
2044 /*
2045 * Note: The Guest Additions API (interface) version is deprecated
2046 * and will not be used anymore! We might need it to at least report
2047 * something as version number if *really* ancient Guest Additions are
2048 * installed (without the guest version + revision properties having set).
2049 */
2050 mData.mInterfaceVersion = aInterfaceVersion;
2051
2052 /*
2053 * Older Additions rely on the Additions API version whether they
2054 * are assumed to be active or not. Since newer Additions do report
2055 * the Additions version *before* calling this function (by calling
2056 * VMMDevReportGuestInfo2, VMMDevReportGuestStatus, VMMDevReportGuestInfo,
2057 * in that order) we can tell apart old and new Additions here. Old
2058 * Additions never would set VMMDevReportGuestInfo2 (which set mData.mAdditionsVersion)
2059 * so they just rely on the aInterfaceVersion string (which gets set by
2060 * VMMDevReportGuestInfo).
2061 *
2062 * So only mark the Additions as being active (run level = system) when we
2063 * don't have the Additions version set.
2064 */
2065 if (mData.mAdditionsVersion.isEmpty())
2066 {
2067 if (aInterfaceVersion.isEmpty())
2068 mData.mAdditionsRunLevel = AdditionsRunLevelType_None;
2069 else
2070 mData.mAdditionsRunLevel = AdditionsRunLevelType_System;
2071 }
2072
2073 /*
2074 * Older Additions didn't have this finer grained capability bit,
2075 * so enable it by default. Newer Additions will not enable this here
2076 * and use the setSupportedFeatures function instead.
2077 */
2078 mData.mSupportsGraphics = mData.mAdditionsRunLevel > AdditionsRunLevelType_None;
2079
2080 /*
2081 * Note! There is a race going on between setting mAdditionsRunLevel and
2082 * mSupportsGraphics here and disabling/enabling it later according to
2083 * its real status when using new(er) Guest Additions.
2084 */
2085 mData.mOSTypeId = Global::OSTypeId (aOsType);
2086}
2087
2088/**
2089 * Sets the Guest Additions version information details.
2090 * Gets called by vmmdevUpdateGuestInfo2.
2091 *
2092 * @param aAdditionsVersion
2093 * @param aVersionName
2094 */
2095void Guest::setAdditionsInfo2(Bstr aAdditionsVersion, Bstr aVersionName, Bstr aRevision)
2096{
2097 AutoCaller autoCaller(this);
2098 AssertComRCReturnVoid (autoCaller.rc());
2099
2100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2101
2102 if (!aVersionName.isEmpty())
2103 /*
2104 * aVersionName could be "x.y.z_BETA1_FOOBAR", so append revision manually to
2105 * become "x.y.z_BETA1_FOOBARr12345".
2106 */
2107 mData.mAdditionsVersion = BstrFmt("%ls r%ls", aVersionName.raw(), aRevision.raw());
2108 else /* aAdditionsVersion is in x.y.zr12345 format. */
2109 mData.mAdditionsVersion = aAdditionsVersion;
2110}
2111
2112/**
2113 * Sets the status of a certain Guest Additions facility.
2114 * Gets called by vmmdevUpdateGuestStatus.
2115 *
2116 * @param Facility
2117 * @param Status
2118 * @param ulFlags
2119 */
2120void Guest::setAdditionsStatus(VBoxGuestStatusFacility Facility, VBoxGuestStatusCurrent Status, ULONG ulFlags)
2121{
2122 AutoCaller autoCaller(this);
2123 AssertComRCReturnVoid (autoCaller.rc());
2124
2125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2126
2127 uint32_t uCurFacility = Facility + (Status == VBoxGuestStatusCurrent_Active ? 0 : -1);
2128
2129 /* First check for disabled status. */
2130 if ( Facility < VBoxGuestStatusFacility_VBoxGuestDriver
2131 || ( Facility == VBoxGuestStatusFacility_All
2132 && ( Status == VBoxGuestStatusCurrent_Inactive
2133 || Status == VBoxGuestStatusCurrent_Disabled
2134 )
2135 )
2136 )
2137 {
2138 mData.mAdditionsRunLevel = AdditionsRunLevelType_None;
2139 }
2140 else if (uCurFacility >= VBoxGuestStatusFacility_VBoxTray)
2141 {
2142 mData.mAdditionsRunLevel = AdditionsRunLevelType_Desktop;
2143 }
2144 else if (uCurFacility >= VBoxGuestStatusFacility_VBoxService)
2145 {
2146 mData.mAdditionsRunLevel = AdditionsRunLevelType_Userland;
2147 }
2148 else if (uCurFacility >= VBoxGuestStatusFacility_VBoxGuestDriver)
2149 {
2150 mData.mAdditionsRunLevel = AdditionsRunLevelType_System;
2151 }
2152 else /* Should never happen! */
2153 AssertMsgFailed(("Invalid facility status/run level detected! uCurFacility=%ld\n", uCurFacility));
2154}
2155
2156/**
2157 * Sets the supported features (and whether they are active or not).
2158 *
2159 * @param fCaps Guest capability bit mask (VMMDEV_GUEST_SUPPORTS_XXX).
2160 * @param fActive No idea what this is supposed to be, it's always 0 and
2161 * not references by this method.
2162 */
2163void Guest::setSupportedFeatures(uint32_t fCaps, uint32_t fActive)
2164{
2165 AutoCaller autoCaller(this);
2166 AssertComRCReturnVoid (autoCaller.rc());
2167
2168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2169
2170 mData.mSupportsSeamless = (fCaps & VMMDEV_GUEST_SUPPORTS_SEAMLESS);
2171 /** @todo Add VMMDEV_GUEST_SUPPORTS_GUEST_HOST_WINDOW_MAPPING */
2172 mData.mSupportsGraphics = (fCaps & VMMDEV_GUEST_SUPPORTS_GRAPHICS);
2173}
2174/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use