VirtualBox

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

最後變更 在這個檔案從33289是 33253,由 vboxsync 提交於 14 年 前

build fix (some EOF header define clash)

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 83.8 KB
 
1/* $Id: GuestImpl.cpp 33253 2010-10-20 10:46:11Z 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.alldomusa.eu.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 switch (u32Function)
582 {
583 case GUEST_DISCONNECTED:
584 {
585 //LogFlowFunc(("GUEST_DISCONNECTED\n"));
586
587 PCALLBACKDATACLIENTDISCONNECTED pCBData = reinterpret_cast<PCALLBACKDATACLIENTDISCONNECTED>(pvParms);
588 AssertPtr(pCBData);
589 AssertReturn(sizeof(CALLBACKDATACLIENTDISCONNECTED) == cbParms, VERR_INVALID_PARAMETER);
590 AssertReturn(CALLBACKDATAMAGICCLIENTDISCONNECTED == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
591
592 rc = pGuest->notifyCtrlClientDisconnected(u32Function, pCBData);
593 break;
594 }
595
596 case GUEST_EXEC_SEND_STATUS:
597 {
598 //LogFlowFunc(("GUEST_EXEC_SEND_STATUS\n"));
599
600 PCALLBACKDATAEXECSTATUS pCBData = reinterpret_cast<PCALLBACKDATAEXECSTATUS>(pvParms);
601 AssertPtr(pCBData);
602 AssertReturn(sizeof(CALLBACKDATAEXECSTATUS) == cbParms, VERR_INVALID_PARAMETER);
603 AssertReturn(CALLBACKDATAMAGICEXECSTATUS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
604
605 rc = pGuest->notifyCtrlExecStatus(u32Function, pCBData);
606 break;
607 }
608
609 case GUEST_EXEC_SEND_OUTPUT:
610 {
611 //LogFlowFunc(("GUEST_EXEC_SEND_OUTPUT\n"));
612
613 PCALLBACKDATAEXECOUT pCBData = reinterpret_cast<PCALLBACKDATAEXECOUT>(pvParms);
614 AssertPtr(pCBData);
615 AssertReturn(sizeof(CALLBACKDATAEXECOUT) == cbParms, VERR_INVALID_PARAMETER);
616 AssertReturn(CALLBACKDATAMAGICEXECOUT == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
617
618 rc = pGuest->notifyCtrlExecOut(u32Function, pCBData);
619 break;
620 }
621
622 case GUEST_EXEC_SEND_INPUT_STATUS:
623 {
624 //LogFlowFunc(("GUEST_EXEC_SEND_INPUT_STATUS\n"));
625
626 PCALLBACKDATAEXECINSTATUS pCBData = reinterpret_cast<PCALLBACKDATAEXECINSTATUS>(pvParms);
627 AssertPtr(pCBData);
628 AssertReturn(sizeof(CALLBACKDATAEXECINSTATUS) == cbParms, VERR_INVALID_PARAMETER);
629 AssertReturn(CALLBACKDATAMAGICEXECINSTATUS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
630
631 rc = pGuest->notifyCtrlExecInStatus(u32Function, pCBData);
632 break;
633 }
634
635 default:
636 AssertMsgFailed(("Unknown guest control notification received, u32Function=%u", u32Function));
637 rc = VERR_INVALID_PARAMETER;
638 break;
639 }
640 return rc;
641}
642
643/* Function for handling the execution start/termination notification. */
644int Guest::notifyCtrlExecStatus(uint32_t u32Function,
645 PCALLBACKDATAEXECSTATUS pData)
646{
647 int vrc = VINF_SUCCESS;
648
649 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
650
651 AssertPtr(pData);
652 CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
653
654 /* Callback can be called several times. */
655 if (it != mCallbackMap.end())
656 {
657 PCALLBACKDATAEXECSTATUS pCBData = (PCALLBACKDATAEXECSTATUS)it->second.pvData;
658 AssertPtr(pCBData);
659
660 pCBData->u32PID = pData->u32PID;
661 pCBData->u32Status = pData->u32Status;
662 pCBData->u32Flags = pData->u32Flags;
663 /** @todo Copy void* buffer contents! */
664
665 Utf8Str errMsg;
666
667 /* Was progress canceled before? */
668 BOOL fCanceled;
669 ComAssert(!it->second.pProgress.isNull());
670 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled))
671 && !fCanceled)
672 {
673 /* Do progress handling. */
674 HRESULT hr;
675 switch (pData->u32Status)
676 {
677 case PROC_STS_STARTED:
678 LogRel(("Guest process (PID %u) started\n", pCBData->u32PID)); /** @todo Add process name */
679 hr = it->second.pProgress->SetNextOperation(Bstr(tr("Waiting for process to exit ...")).raw(), 1 /* Weight */);
680 AssertComRC(hr);
681 break;
682
683 case PROC_STS_TEN: /* Terminated normally. */
684 LogRel(("Guest process (PID %u) exited normally\n", pCBData->u32PID)); /** @todo Add process name */
685 if (!it->second.pProgress->getCompleted())
686 {
687 hr = it->second.pProgress->notifyComplete(S_OK);
688 AssertComRC(hr);
689
690 LogFlowFunc(("Proccess (CID=%u, status=%u) terminated successfully\n",
691 pData->hdr.u32ContextID, pData->u32Status));
692 }
693 break;
694
695 case PROC_STS_TEA: /* Terminated abnormally. */
696 LogRel(("Guest process (PID %u) terminated abnormally with exit code = %u\n",
697 pCBData->u32PID, pCBData->u32Flags)); /** @todo Add process name */
698 errMsg = Utf8StrFmt(Guest::tr("Process terminated abnormally with status '%u'"),
699 pCBData->u32Flags);
700 break;
701
702 case PROC_STS_TES: /* Terminated through signal. */
703 LogRel(("Guest process (PID %u) terminated through signal with exit code = %u\n",
704 pCBData->u32PID, pCBData->u32Flags)); /** @todo Add process name */
705 errMsg = Utf8StrFmt(Guest::tr("Process terminated via signal with status '%u'"),
706 pCBData->u32Flags);
707 break;
708
709 case PROC_STS_TOK:
710 LogRel(("Guest process (PID %u) timed out and was killed\n", pCBData->u32PID)); /** @todo Add process name */
711 errMsg = Utf8StrFmt(Guest::tr("Process timed out and was killed"));
712 break;
713
714 case PROC_STS_TOA:
715 LogRel(("Guest process (PID %u) timed out and could not be killed\n", pCBData->u32PID)); /** @todo Add process name */
716 errMsg = Utf8StrFmt(Guest::tr("Process timed out and could not be killed"));
717 break;
718
719 case PROC_STS_DWN:
720 LogRel(("Guest process (PID %u) killed because system is shutting down\n", pCBData->u32PID)); /** @todo Add process name */
721 /*
722 * If u32Flags has ExecuteProcessFlag_IgnoreOrphanedProcesses set, we don't report an error to
723 * our progress object. This is helpful for waiters which rely on the success of our progress object
724 * even if the executed process was killed because the system/VBoxService is shutting down.
725 *
726 * In this case u32Flags contains the actual execution flags reached in via Guest::ExecuteProcess().
727 */
728 if (pData->u32Flags & ExecuteProcessFlag_IgnoreOrphanedProcesses)
729 {
730 if (!it->second.pProgress->getCompleted())
731 {
732 hr = it->second.pProgress->notifyComplete(S_OK);
733 AssertComRC(hr);
734 }
735 }
736 else
737 {
738 errMsg = Utf8StrFmt(Guest::tr("Process killed because system is shutting down"));
739 }
740 break;
741
742 case PROC_STS_ERROR:
743 LogRel(("Guest process (PID %u) could not be started because of rc=%Rrc\n",
744 pCBData->u32PID, pCBData->u32Flags)); /** @todo Add process name */
745 errMsg = Utf8StrFmt(Guest::tr("Process execution failed with rc=%Rrc"), pCBData->u32Flags);
746 break;
747
748 default:
749 vrc = VERR_INVALID_PARAMETER;
750 break;
751 }
752
753 /* Handle process map. */
754 /** @todo What happens on/deal with PID reuse? */
755 /** @todo How to deal with multiple updates at once? */
756 if (pCBData->u32PID > 0)
757 {
758 GuestProcessMapIter it_proc = getProcessByPID(pCBData->u32PID);
759 if (it_proc == mGuestProcessMap.end())
760 {
761 /* Not found, add to map. */
762 GuestProcess newProcess;
763 newProcess.mStatus = pCBData->u32Status;
764 newProcess.mExitCode = pCBData->u32Flags; /* Contains exit code. */
765 newProcess.mFlags = 0;
766
767 mGuestProcessMap[pCBData->u32PID] = newProcess;
768 }
769 else /* Update map. */
770 {
771 it_proc->second.mStatus = pCBData->u32Status;
772 it_proc->second.mExitCode = pCBData->u32Flags; /* Contains exit code. */
773 it_proc->second.mFlags = 0;
774 }
775 }
776 }
777 else
778 errMsg = Utf8StrFmt(Guest::tr("Process execution canceled"));
779
780 if (!it->second.pProgress->getCompleted())
781 {
782 if ( errMsg.length()
783 || fCanceled) /* If canceled we have to report E_FAIL! */
784 {
785 /* Destroy all callbacks which are still waiting on something
786 * which is related to the current PID. */
787 CallbackMapIter it2;
788 for (it2 = mCallbackMap.begin(); it2 != mCallbackMap.end(); it2++)
789 {
790 switch (it2->second.mType)
791 {
792 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_START:
793 break;
794
795 /* When waiting for process output while the process is destroyed,
796 * make sure we also destroy the actual waiting operation (internal progress object)
797 * in order to not block the caller. */
798 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT:
799 {
800 PCALLBACKDATAEXECOUT pItData = (CALLBACKDATAEXECOUT*)it2->second.pvData;
801 AssertPtr(pItData);
802 if (pItData->u32PID == pCBData->u32PID)
803 destroyCtrlCallbackContext(it2);
804 break;
805 }
806
807 default:
808 AssertMsgFailed(("Unknown callback type %d\n", it2->second.mType));
809 break;
810 }
811 }
812
813 HRESULT hr2 = it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,
814 COM_IIDOF(IGuest),
815 Guest::getStaticComponentName(),
816 "%s", errMsg.c_str());
817 AssertComRC(hr2);
818 LogFlowFunc(("Process (CID=%u, status=%u) reported error: %s\n",
819 pData->hdr.u32ContextID, pData->u32Status, errMsg.c_str()));
820 }
821 }
822 }
823 else
824 LogFlowFunc(("Unexpected callback (magic=%u, CID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));
825 LogFlowFunc(("Returned with rc=%Rrc\n", vrc));
826 return vrc;
827}
828
829/* Function for handling the execution output notification. */
830int Guest::notifyCtrlExecOut(uint32_t u32Function,
831 PCALLBACKDATAEXECOUT pData)
832{
833 int rc = VINF_SUCCESS;
834
835 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
836
837 AssertPtr(pData);
838 CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
839 if (it != mCallbackMap.end())
840 {
841 PCALLBACKDATAEXECOUT pCBData = (PCALLBACKDATAEXECOUT)it->second.pvData;
842 AssertPtr(pCBData);
843
844 pCBData->u32PID = pData->u32PID;
845 pCBData->u32HandleId = pData->u32HandleId;
846 pCBData->u32Flags = pData->u32Flags;
847
848 /* Make sure we really got something! */
849 if ( pData->cbData
850 && pData->pvData)
851 {
852 /* Allocate data buffer and copy it */
853 pCBData->pvData = RTMemAlloc(pData->cbData);
854 pCBData->cbData = pData->cbData;
855
856 AssertReturn(pCBData->pvData, VERR_NO_MEMORY);
857 memcpy(pCBData->pvData, pData->pvData, pData->cbData);
858 }
859 else
860 {
861 pCBData->pvData = NULL;
862 pCBData->cbData = 0;
863 }
864
865 /* Was progress canceled before? */
866 BOOL fCanceled;
867 ComAssert(!it->second.pProgress.isNull());
868 if (SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled)) && fCanceled)
869 {
870 it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,
871 COM_IIDOF(IGuest),
872 Guest::getStaticComponentName(),
873 Guest::tr("The output operation was canceled"));
874 }
875 else
876 {
877 BOOL fCompleted;
878 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))
879 && !fCompleted)
880 {
881 /* If we previously got completed notification, don't trigger again. */
882 it->second.pProgress->notifyComplete(S_OK);
883 }
884 }
885 }
886 else
887 LogFlowFunc(("Unexpected callback (magic=%u, CID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));
888 return rc;
889}
890
891/* Function for handling the execution input status notification. */
892int Guest::notifyCtrlExecInStatus(uint32_t u32Function,
893 PCALLBACKDATAEXECINSTATUS pData)
894{
895 int rc = VINF_SUCCESS;
896
897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
898
899 AssertPtr(pData);
900 CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
901 if (it != mCallbackMap.end())
902 {
903 PCALLBACKDATAEXECINSTATUS pCBData = (PCALLBACKDATAEXECINSTATUS)it->second.pvData;
904 AssertPtr(pCBData);
905
906 /* Save bytes processed. */
907 pCBData->cbProcessed = pData->cbProcessed;
908
909 /* Was progress canceled before? */
910 BOOL fCanceled;
911 ComAssert(!it->second.pProgress.isNull());
912 if (SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled)) && fCanceled)
913 {
914 it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,
915 COM_IIDOF(IGuest),
916 Guest::getStaticComponentName(),
917 Guest::tr("The input operation was canceled"));
918 }
919 else
920 {
921 BOOL fCompleted;
922 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))
923 && !fCompleted)
924 {
925 /* If we previously got completed notification, don't trigger again. */
926 it->second.pProgress->notifyComplete(S_OK);
927 }
928 }
929 }
930 else
931 LogFlowFunc(("Unexpected callback (magic=%u, CID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));
932 return rc;
933}
934
935int Guest::notifyCtrlClientDisconnected(uint32_t u32Function,
936 PCALLBACKDATACLIENTDISCONNECTED pData)
937{
938 int rc = VINF_SUCCESS;
939
940 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
941 CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
942 if (it != mCallbackMap.end())
943 {
944 LogFlowFunc(("Client with CID=%u disconnected\n", it->first));
945 destroyCtrlCallbackContext(it);
946 }
947 return rc;
948}
949
950Guest::CallbackMapIter Guest::getCtrlCallbackContextByID(uint32_t u32ContextID)
951{
952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
953 return mCallbackMap.find(u32ContextID);
954}
955
956Guest::GuestProcessMapIter Guest::getProcessByPID(uint32_t u32PID)
957{
958 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
959 return mGuestProcessMap.find(u32PID);
960}
961
962/* No locking here; */
963void Guest::destroyCtrlCallbackContext(Guest::CallbackMapIter it)
964{
965 LogFlowFunc(("Destroying callback with CID=%u ...\n", it->first));
966
967 if (it->second.pvData)
968 {
969 RTMemFree(it->second.pvData);
970 it->second.pvData = NULL;
971 it->second.cbData = 0;
972 }
973
974 /* Notify outstanding waits for progress ... */
975 if ( it->second.pProgress
976 && !it->second.pProgress.isNull())
977 {
978 LogFlowFunc(("Handling progress for CID=%u ...\n", it->first));
979
980 /*
981 * Assume we didn't complete to make sure we clean up even if the
982 * following call fails.
983 */
984 BOOL fCompleted = FALSE;
985 it->second.pProgress->COMGETTER(Completed)(&fCompleted);
986 if (!fCompleted)
987 {
988 LogFlowFunc(("Progress of CID=%u *not* completed, cancelling ...\n", it->first));
989
990 /* Only cancel if not canceled before! */
991 BOOL fCanceled;
992 if (SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled)) && !fCanceled)
993 it->second.pProgress->Cancel();
994
995 /*
996 * To get waitForCompletion completed (unblocked) we have to notify it if necessary (only
997 * cancle won't work!). This could happen if the client thread (e.g. VBoxService, thread of a spawned process)
998 * is disconnecting without having the chance to sending a status message before, so we
999 * have to abort here to make sure the host never hangs/gets stuck while waiting for the
1000 * progress object to become signalled.
1001 */
1002 it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,
1003 COM_IIDOF(IGuest),
1004 Guest::getStaticComponentName(),
1005 Guest::tr("The operation was canceled because client is shutting down"));
1006 }
1007 /*
1008 * Do *not* NULL pProgress here, because waiting function like executeProcess()
1009 * will still rely on this object for checking whether they have to give up!
1010 */
1011 }
1012}
1013
1014/* Adds a callback with a user provided data block and an optional progress object
1015 * to the callback map. A callback is identified by a unique context ID which is used
1016 * to identify a callback from the guest side. */
1017uint32_t Guest::addCtrlCallbackContext(eVBoxGuestCtrlCallbackType enmType, void *pvData, uint32_t cbData, Progress *pProgress)
1018{
1019 AssertPtr(pProgress);
1020
1021 /** @todo Put this stuff into a constructor! */
1022 CallbackContext context;
1023 context.mType = enmType;
1024 context.pvData = pvData;
1025 context.cbData = cbData;
1026 context.pProgress = pProgress;
1027
1028 /* Create a new context ID and assign it. */
1029 CallbackMapIter it;
1030 uint32_t uNewContext = 0;
1031 do
1032 {
1033 /* Create a new context ID ... */
1034 uNewContext = ASMAtomicIncU32(&mNextContextID);
1035 if (uNewContext == UINT32_MAX)
1036 ASMAtomicUoWriteU32(&mNextContextID, 1000);
1037 /* Is the context ID already used? */
1038 it = getCtrlCallbackContextByID(uNewContext);
1039 } while(it != mCallbackMap.end());
1040
1041 uint32_t nCallbacks = 0;
1042 if ( it == mCallbackMap.end()
1043 && uNewContext > 0)
1044 {
1045 /* We apparently got an unused context ID, let's use it! */
1046 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1047 mCallbackMap[uNewContext] = context;
1048 nCallbacks = mCallbackMap.size();
1049 }
1050 else
1051 {
1052 /* Should never happen ... */
1053 {
1054 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1055 nCallbacks = mCallbackMap.size();
1056 }
1057 AssertReleaseMsg(uNewContext, ("No free context ID found! uNewContext=%u, nCallbacks=%u", uNewContext, nCallbacks));
1058 }
1059
1060#if 0
1061 if (nCallbacks > 256) /* Don't let the container size get too big! */
1062 {
1063 Guest::CallbackListIter it = mCallbackList.begin();
1064 destroyCtrlCallbackContext(it);
1065 {
1066 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1067 mCallbackList.erase(it);
1068 }
1069 }
1070#endif
1071 return uNewContext;
1072}
1073#endif /* VBOX_WITH_GUEST_CONTROL */
1074
1075STDMETHODIMP Guest::ExecuteProcess(IN_BSTR aCommand, ULONG aFlags,
1076 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
1077 IN_BSTR aUserName, IN_BSTR aPassword,
1078 ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress)
1079{
1080/** @todo r=bird: Eventually we should clean up all the timeout parameters
1081 * in the API and have the same way of specifying infinite waits! */
1082#ifndef VBOX_WITH_GUEST_CONTROL
1083 ReturnComNotImplemented();
1084#else /* VBOX_WITH_GUEST_CONTROL */
1085 using namespace guestControl;
1086
1087 CheckComArgStrNotEmptyOrNull(aCommand);
1088 CheckComArgOutPointerValid(aPID);
1089 CheckComArgOutPointerValid(aProgress);
1090
1091 /* Do not allow anonymous executions (with system rights). */
1092 if (RT_UNLIKELY((aUserName) == NULL || *(aUserName) == '\0'))
1093 return setError(E_INVALIDARG, tr("No user name specified"));
1094
1095 AutoCaller autoCaller(this);
1096 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1097
1098 /* Validate flags. */
1099 if (aFlags)
1100 {
1101 if (!(aFlags & ExecuteProcessFlag_IgnoreOrphanedProcesses))
1102 return E_INVALIDARG;
1103 }
1104
1105 HRESULT rc = S_OK;
1106
1107 try
1108 {
1109 /*
1110 * Create progress object. Note that this is a multi operation
1111 * object to perform the following steps:
1112 * - Operation 1 (0): Create/start process.
1113 * - Operation 2 (1): Wait for process to exit.
1114 * If this progress completed successfully (S_OK), the process
1115 * started and exited normally. In any other case an error/exception
1116 * occured.
1117 */
1118 ComObjPtr <Progress> progress;
1119 rc = progress.createObject();
1120 if (SUCCEEDED(rc))
1121 {
1122 rc = progress->init(static_cast<IGuest*>(this),
1123 Bstr(tr("Executing process")).raw(),
1124 TRUE,
1125 2, /* Number of operations. */
1126 Bstr(tr("Starting process ...")).raw()); /* Description of first stage. */
1127 }
1128 if (FAILED(rc)) return rc;
1129
1130 /*
1131 * Prepare process execution.
1132 */
1133 int vrc = VINF_SUCCESS;
1134 Utf8Str Utf8Command(aCommand);
1135
1136 /* Adjust timeout */
1137 if (aTimeoutMS == 0)
1138 aTimeoutMS = UINT32_MAX;
1139
1140 /* Prepare arguments. */
1141 char **papszArgv = NULL;
1142 uint32_t uNumArgs = 0;
1143 if (aArguments > 0)
1144 {
1145 com::SafeArray<IN_BSTR> args(ComSafeArrayInArg(aArguments));
1146 uNumArgs = args.size();
1147 papszArgv = (char**)RTMemAlloc(sizeof(char*) * (uNumArgs + 1));
1148 AssertReturn(papszArgv, E_OUTOFMEMORY);
1149 for (unsigned i = 0; RT_SUCCESS(vrc) && i < uNumArgs; i++)
1150 vrc = RTUtf16ToUtf8(args[i], &papszArgv[i]);
1151 papszArgv[uNumArgs] = NULL;
1152 }
1153
1154 Utf8Str Utf8UserName(aUserName);
1155 Utf8Str Utf8Password(aPassword);
1156 if (RT_SUCCESS(vrc))
1157 {
1158 uint32_t uContextID = 0;
1159
1160 char *pszArgs = NULL;
1161 if (uNumArgs > 0)
1162 vrc = RTGetOptArgvToString(&pszArgs, papszArgv, 0);
1163 if (RT_SUCCESS(vrc))
1164 {
1165 uint32_t cbArgs = pszArgs ? strlen(pszArgs) + 1 : 0; /* Include terminating zero. */
1166
1167 /* Prepare environment. */
1168 void *pvEnv = NULL;
1169 uint32_t uNumEnv = 0;
1170 uint32_t cbEnv = 0;
1171 if (aEnvironment > 0)
1172 {
1173 com::SafeArray<IN_BSTR> env(ComSafeArrayInArg(aEnvironment));
1174
1175 for (unsigned i = 0; i < env.size(); i++)
1176 {
1177 vrc = prepareExecuteEnv(Utf8Str(env[i]).c_str(), &pvEnv, &cbEnv, &uNumEnv);
1178 if (RT_FAILURE(vrc))
1179 break;
1180 }
1181 }
1182
1183 LogRel(("Executing guest process \"%s\" as user \"%s\" ...\n",
1184 Utf8Command.c_str(), Utf8UserName.c_str()));
1185
1186 if (RT_SUCCESS(vrc))
1187 {
1188 PCALLBACKDATAEXECSTATUS pData = (PCALLBACKDATAEXECSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECSTATUS));
1189 AssertReturn(pData, VBOX_E_IPRT_ERROR);
1190 RT_ZERO(*pData);
1191 uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_START,
1192 pData, sizeof(CALLBACKDATAEXECSTATUS), progress);
1193 Assert(uContextID > 0);
1194
1195 VBOXHGCMSVCPARM paParms[15];
1196 int i = 0;
1197 paParms[i++].setUInt32(uContextID);
1198 paParms[i++].setPointer((void*)Utf8Command.c_str(), (uint32_t)Utf8Command.length() + 1);
1199 paParms[i++].setUInt32(aFlags);
1200 paParms[i++].setUInt32(uNumArgs);
1201 paParms[i++].setPointer((void*)pszArgs, cbArgs);
1202 paParms[i++].setUInt32(uNumEnv);
1203 paParms[i++].setUInt32(cbEnv);
1204 paParms[i++].setPointer((void*)pvEnv, cbEnv);
1205 paParms[i++].setPointer((void*)Utf8UserName.c_str(), (uint32_t)Utf8UserName.length() + 1);
1206 paParms[i++].setPointer((void*)Utf8Password.c_str(), (uint32_t)Utf8Password.length() + 1);
1207 paParms[i++].setUInt32(aTimeoutMS);
1208
1209 VMMDev *vmmDev;
1210 {
1211 /* Make sure mParent is valid, so set the read lock while using.
1212 * Do not keep this lock while doing the actual call, because in the meanwhile
1213 * another thread could request a write lock which would be a bad idea ... */
1214 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1215
1216 /* Forward the information to the VMM device. */
1217 AssertPtr(mParent);
1218 vmmDev = mParent->getVMMDev();
1219 }
1220
1221 if (vmmDev)
1222 {
1223 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
1224 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_CMD,
1225 i, paParms);
1226 }
1227 else
1228 vrc = VERR_INVALID_VM_HANDLE;
1229 RTMemFree(pvEnv);
1230 }
1231 RTStrFree(pszArgs);
1232 }
1233 if (RT_SUCCESS(vrc))
1234 {
1235 LogFlowFunc(("Waiting for HGCM callback (timeout=%ldms) ...\n", aTimeoutMS));
1236
1237 /*
1238 * Wait for the HGCM low level callback until the process
1239 * has been started (or something went wrong). This is necessary to
1240 * get the PID.
1241 */
1242 CallbackMapIter it = getCtrlCallbackContextByID(uContextID);
1243 BOOL fCanceled = FALSE;
1244 if (it != mCallbackMap.end())
1245 {
1246 ComAssert(!it->second.pProgress.isNull());
1247
1248 /*
1249 * Wait for the first stage (=0) to complete (that is starting the process).
1250 */
1251 PCALLBACKDATAEXECSTATUS pData = NULL;
1252 rc = it->second.pProgress->WaitForOperationCompletion(0, aTimeoutMS);
1253 if (SUCCEEDED(rc))
1254 {
1255 /* Was the operation canceled by one of the parties? */
1256 rc = it->second.pProgress->COMGETTER(Canceled)(&fCanceled);
1257 if (FAILED(rc)) throw rc;
1258
1259 if (!fCanceled)
1260 {
1261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1262
1263 pData = (PCALLBACKDATAEXECSTATUS)it->second.pvData;
1264 Assert(it->second.cbData == sizeof(CALLBACKDATAEXECSTATUS));
1265 AssertPtr(pData);
1266
1267 /* Did we get some status? */
1268 switch (pData->u32Status)
1269 {
1270 case PROC_STS_STARTED:
1271 /* Process is (still) running; get PID. */
1272 *aPID = pData->u32PID;
1273 break;
1274
1275 /* In any other case the process either already
1276 * terminated or something else went wrong, so no PID ... */
1277 case PROC_STS_TEN: /* Terminated normally. */
1278 case PROC_STS_TEA: /* Terminated abnormally. */
1279 case PROC_STS_TES: /* Terminated through signal. */
1280 case PROC_STS_TOK:
1281 case PROC_STS_TOA:
1282 case PROC_STS_DWN:
1283 /*
1284 * Process (already) ended, but we want to get the
1285 * PID anyway to retrieve the output in a later call.
1286 */
1287 *aPID = pData->u32PID;
1288 break;
1289
1290 case PROC_STS_ERROR:
1291 vrc = pData->u32Flags; /* u32Flags member contains IPRT error code. */
1292 break;
1293
1294 case PROC_STS_UNDEFINED:
1295 vrc = VERR_TIMEOUT; /* Operation did not complete within time. */
1296 break;
1297
1298 default:
1299 vrc = VERR_INVALID_PARAMETER; /* Unknown status, should never happen! */
1300 break;
1301 }
1302 }
1303 else /* Operation was canceled. */
1304 vrc = VERR_CANCELLED;
1305 }
1306 else /* Operation did not complete within time. */
1307 vrc = VERR_TIMEOUT;
1308
1309 /*
1310 * Do *not* remove the callback yet - we might wait with the IProgress object on something
1311 * else (like end of process) ...
1312 */
1313 if (RT_FAILURE(vrc))
1314 {
1315 if (vrc == VERR_FILE_NOT_FOUND) /* This is the most likely error. */
1316 rc = setError(VBOX_E_IPRT_ERROR,
1317 tr("The file '%s' was not found on guest"), Utf8Command.c_str());
1318 else if (vrc == VERR_PATH_NOT_FOUND)
1319 rc = setError(VBOX_E_IPRT_ERROR,
1320 tr("The path to file '%s' was not found on guest"), Utf8Command.c_str());
1321 else if (vrc == VERR_BAD_EXE_FORMAT)
1322 rc = setError(VBOX_E_IPRT_ERROR,
1323 tr("The file '%s' is not an executable format on guest"), Utf8Command.c_str());
1324 else if (vrc == VERR_AUTHENTICATION_FAILURE)
1325 rc = setError(VBOX_E_IPRT_ERROR,
1326 tr("The specified user '%s' was not able to logon on guest"), Utf8UserName.c_str());
1327 else if (vrc == VERR_TIMEOUT)
1328 rc = setError(VBOX_E_IPRT_ERROR,
1329 tr("The guest did not respond within time (%ums)"), aTimeoutMS);
1330 else if (vrc == VERR_CANCELLED)
1331 rc = setError(VBOX_E_IPRT_ERROR,
1332 tr("The execution operation was canceled"));
1333 else if (vrc == VERR_PERMISSION_DENIED)
1334 rc = setError(VBOX_E_IPRT_ERROR,
1335 tr("Invalid user/password credentials"));
1336 else
1337 {
1338 if (pData && pData->u32Status == PROC_STS_ERROR)
1339 rc = setError(VBOX_E_IPRT_ERROR,
1340 tr("Process could not be started: %Rrc"), pData->u32Flags);
1341 else
1342 rc = setError(E_UNEXPECTED,
1343 tr("The service call failed with error %Rrc"), vrc);
1344 }
1345 }
1346 else /* Execution went fine. */
1347 {
1348 /* Return the progress to the caller. */
1349 progress.queryInterfaceTo(aProgress);
1350 }
1351 }
1352 else /* Callback context not found; should never happen! */
1353 AssertMsg(it != mCallbackMap.end(), ("Callback context with ID %u not found!", uContextID));
1354 }
1355 else /* HGCM related error codes .*/
1356 {
1357 if (vrc == VERR_INVALID_VM_HANDLE)
1358 rc = setError(VBOX_E_VM_ERROR,
1359 tr("VMM device is not available (is the VM running?)"));
1360 else if (vrc == VERR_TIMEOUT)
1361 rc = setError(VBOX_E_VM_ERROR,
1362 tr("The guest execution service is not ready"));
1363 else if (vrc == VERR_HGCM_SERVICE_NOT_FOUND)
1364 rc = setError(VBOX_E_VM_ERROR,
1365 tr("The guest execution service is not available"));
1366 else /* HGCM call went wrong. */
1367 rc = setError(E_UNEXPECTED,
1368 tr("The HGCM call failed with error %Rrc"), vrc);
1369 }
1370
1371 for (unsigned i = 0; i < uNumArgs; i++)
1372 RTMemFree(papszArgv[i]);
1373 RTMemFree(papszArgv);
1374 }
1375
1376 if (RT_FAILURE(vrc))
1377 LogRel(("Executing guest process \"%s\" as user \"%s\" failed with %Rrc\n",
1378 Utf8Command.c_str(), Utf8UserName.c_str(), vrc));
1379 }
1380 catch (std::bad_alloc &)
1381 {
1382 rc = E_OUTOFMEMORY;
1383 }
1384 return rc;
1385#endif /* VBOX_WITH_GUEST_CONTROL */
1386}
1387
1388STDMETHODIMP Guest::SetProcessInput(ULONG aPID, ULONG aFlags, ComSafeArrayIn(BYTE, aData), ULONG *aBytesWritten)
1389{
1390#ifndef VBOX_WITH_GUEST_CONTROL
1391 ReturnComNotImplemented();
1392#else /* VBOX_WITH_GUEST_CONTROL */
1393 using namespace guestControl;
1394
1395 CheckComArgExpr(aPID, aPID > 0);
1396 CheckComArgOutPointerValid(aBytesWritten);
1397
1398 /* Validate flags. */
1399 if (aFlags)
1400 {
1401 if (!(aFlags & ProcessInputFlag_EndOfFile))
1402 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
1403 }
1404
1405 AutoCaller autoCaller(this);
1406 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1407
1408 HRESULT rc = S_OK;
1409
1410 try
1411 {
1412 /* Init. */
1413 *aBytesWritten = 0;
1414
1415 /* Search for existing PID. */
1416 GuestProcessMapIterConst itProc = getProcessByPID(aPID);
1417 if (itProc != mGuestProcessMap.end())
1418 {
1419 /* PID exists; check if process is still running. */
1420 if (itProc->second.mStatus != PROC_STS_STARTED)
1421 {
1422 rc = setError(VBOX_E_IPRT_ERROR,
1423 tr("Process (PID %u) does not run anymore! Status: %ld, Flags: %u, Exit Code: %u"),
1424 aPID, itProc->second.mStatus, itProc->second.mFlags, itProc->second.mExitCode);
1425 }
1426 }
1427 else
1428 rc = setError(VBOX_E_IPRT_ERROR,
1429 tr("Process (PID %u) not found!"), aPID);
1430
1431 if (SUCCEEDED(rc))
1432 {
1433 /*
1434 * Create progress object.
1435 * This progress object, compared to the one in executeProgress() above
1436 * is only local and is used to determine whether the operation finished
1437 * or got canceled.
1438 */
1439 ComObjPtr <Progress> progress;
1440 rc = progress.createObject();
1441 if (SUCCEEDED(rc))
1442 {
1443 rc = progress->init(static_cast<IGuest*>(this),
1444 Bstr(tr("Setting input for process")).raw(),
1445 TRUE /* Cancelable */);
1446 }
1447 if (FAILED(rc)) return rc;
1448
1449 PCALLBACKDATAEXECINSTATUS pData = (PCALLBACKDATAEXECINSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECINSTATUS));
1450 AssertReturn(pData, VBOX_E_IPRT_ERROR);
1451 RT_ZERO(*pData);
1452 /* Save PID + output flags for later use. */
1453 pData->u32PID = aPID;
1454 pData->u32Flags = aFlags;
1455 /* Add job to callback contexts. */
1456 uint32_t uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_INPUT_STATUS,
1457 pData, sizeof(CALLBACKDATAEXECINSTATUS), progress);
1458 Assert(uContextID > 0);
1459
1460 com::SafeArray<BYTE> sfaData(ComSafeArrayInArg(aData));
1461 uint32_t cbSize = sfaData.size();
1462
1463 VBOXHGCMSVCPARM paParms[6];
1464 int i = 0;
1465 paParms[i++].setUInt32(uContextID);
1466 paParms[i++].setUInt32(aPID);
1467 paParms[i++].setUInt32(aFlags);
1468 paParms[i++].setPointer(sfaData.raw(), cbSize);
1469 paParms[i++].setUInt32(cbSize);
1470
1471 int vrc = VINF_SUCCESS;
1472
1473 {
1474 VMMDev *vmmDev;
1475 {
1476 /* Make sure mParent is valid, so set the read lock while using.
1477 * Do not keep this lock while doing the actual call, because in the meanwhile
1478 * another thread could request a write lock which would be a bad idea ... */
1479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1480
1481 /* Forward the information to the VMM device. */
1482 AssertPtr(mParent);
1483 vmmDev = mParent->getVMMDev();
1484 }
1485
1486 if (vmmDev)
1487 {
1488 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
1489 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_SET_INPUT,
1490 i, paParms);
1491 }
1492 }
1493
1494 if (RT_SUCCESS(vrc))
1495 {
1496 LogFlowFunc(("Waiting for HGCM callback ...\n"));
1497
1498 /*
1499 * Wait for the HGCM low level callback until the process
1500 * has been started (or something went wrong). This is necessary to
1501 * get the PID.
1502 */
1503 CallbackMapIter it = getCtrlCallbackContextByID(uContextID);
1504 BOOL fCanceled = FALSE;
1505 if (it != mCallbackMap.end())
1506 {
1507 ComAssert(!it->second.pProgress.isNull());
1508
1509 /* Wait until operation completed. */
1510 rc = it->second.pProgress->WaitForCompletion(UINT32_MAX /* Wait forever */);
1511 if (FAILED(rc)) throw rc;
1512
1513 /* Was the operation canceled by one of the parties? */
1514 rc = it->second.pProgress->COMGETTER(Canceled)(&fCanceled);
1515 if (FAILED(rc)) throw rc;
1516
1517 if (!fCanceled)
1518 {
1519 BOOL fCompleted;
1520 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))
1521 && fCompleted)
1522 {
1523 PCALLBACKDATAEXECINSTATUS pData = (PCALLBACKDATAEXECINSTATUS)it->second.pvData;
1524 Assert(it->second.cbData == sizeof(CALLBACKDATAEXECINSTATUS));
1525 AssertPtr(pData);
1526
1527 *aBytesWritten = pData->cbProcessed;
1528 }
1529 }
1530 else /* Operation was canceled. */
1531 vrc = VERR_CANCELLED;
1532
1533 if (RT_FAILURE(vrc))
1534 {
1535 if (vrc == VERR_CANCELLED)
1536 {
1537 rc = setError(VBOX_E_IPRT_ERROR,
1538 tr("The input operation was canceled"));
1539 }
1540 else
1541 {
1542 rc = setError(E_UNEXPECTED,
1543 tr("The service call failed with error %Rrc"), vrc);
1544 }
1545 }
1546
1547 {
1548 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1549 /*
1550 * Destroy locally used progress object.
1551 */
1552 destroyCtrlCallbackContext(it);
1553 }
1554
1555 /* Remove callback context (not used anymore). */
1556 mCallbackMap.erase(it);
1557 }
1558 else /* PID lookup failed. */
1559 rc = setError(VBOX_E_IPRT_ERROR,
1560 tr("Process (PID %u) not found!"), aPID);
1561 }
1562 else /* HGCM operation failed. */
1563 rc = setError(E_UNEXPECTED,
1564 tr("The HGCM call failed with error %Rrc"), vrc);
1565
1566 /* Cleanup. */
1567 progress->uninit();
1568 progress.setNull();
1569 }
1570 }
1571 catch (std::bad_alloc &)
1572 {
1573 rc = E_OUTOFMEMORY;
1574 }
1575 return rc;
1576#endif
1577}
1578
1579STDMETHODIMP Guest::GetProcessOutput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
1580{
1581/** @todo r=bird: Eventually we should clean up all the timeout parameters
1582 * in the API and have the same way of specifying infinite waits! */
1583#ifndef VBOX_WITH_GUEST_CONTROL
1584 ReturnComNotImplemented();
1585#else /* VBOX_WITH_GUEST_CONTROL */
1586 using namespace guestControl;
1587
1588 CheckComArgExpr(aPID, aPID > 0);
1589 if (aSize < 0)
1590 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
1591 if (aFlags != 0) /* Flags are not supported at the moment. */
1592 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
1593
1594 AutoCaller autoCaller(this);
1595 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1596
1597 HRESULT rc = S_OK;
1598
1599 try
1600 {
1601 /*
1602 * Create progress object.
1603 * This progress object, compared to the one in executeProgress() above
1604 * is only local and is used to determine whether the operation finished
1605 * or got canceled.
1606 */
1607 ComObjPtr <Progress> progress;
1608 rc = progress.createObject();
1609 if (SUCCEEDED(rc))
1610 {
1611 rc = progress->init(static_cast<IGuest*>(this),
1612 Bstr(tr("Getting output of process")).raw(),
1613 TRUE);
1614 }
1615 if (FAILED(rc)) return rc;
1616
1617 /* Adjust timeout */
1618 if (aTimeoutMS == 0)
1619 aTimeoutMS = UINT32_MAX;
1620
1621 /* Search for existing PID. */
1622 PCALLBACKDATAEXECOUT pData = (CALLBACKDATAEXECOUT*)RTMemAlloc(sizeof(CALLBACKDATAEXECOUT));
1623 AssertReturn(pData, VBOX_E_IPRT_ERROR);
1624 RT_ZERO(*pData);
1625 /* Save PID + output flags for later use. */
1626 pData->u32PID = aPID;
1627 pData->u32Flags = aFlags;
1628 /* Add job to callback contexts. */
1629 uint32_t uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT,
1630 pData, sizeof(CALLBACKDATAEXECOUT), progress);
1631 Assert(uContextID > 0);
1632
1633 size_t cbData = (size_t)RT_MIN(aSize, _64K);
1634 com::SafeArray<BYTE> outputData(cbData);
1635
1636 VBOXHGCMSVCPARM paParms[5];
1637 int i = 0;
1638 paParms[i++].setUInt32(uContextID);
1639 paParms[i++].setUInt32(aPID);
1640 paParms[i++].setUInt32(aFlags); /** @todo Should represent stdout and/or stderr. */
1641
1642 int vrc = VINF_SUCCESS;
1643
1644 {
1645 VMMDev *vmmDev;
1646 {
1647 /* Make sure mParent is valid, so set the read lock while using.
1648 * Do not keep this lock while doing the actual call, because in the meanwhile
1649 * another thread could request a write lock which would be a bad idea ... */
1650 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1651
1652 /* Forward the information to the VMM device. */
1653 AssertPtr(mParent);
1654 vmmDev = mParent->getVMMDev();
1655 }
1656
1657 if (vmmDev)
1658 {
1659 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
1660 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_GET_OUTPUT,
1661 i, paParms);
1662 }
1663 }
1664
1665 if (RT_SUCCESS(vrc))
1666 {
1667 LogFlowFunc(("Waiting for HGCM callback (timeout=%ldms) ...\n", aTimeoutMS));
1668
1669 /*
1670 * Wait for the HGCM low level callback until the process
1671 * has been started (or something went wrong). This is necessary to
1672 * get the PID.
1673 */
1674 CallbackMapIter it = getCtrlCallbackContextByID(uContextID);
1675 BOOL fCanceled = FALSE;
1676 if (it != mCallbackMap.end())
1677 {
1678 ComAssert(!it->second.pProgress.isNull());
1679
1680 /* Wait until operation completed. */
1681 rc = it->second.pProgress->WaitForCompletion(aTimeoutMS);
1682 if (FAILED(rc)) throw rc;
1683
1684 /* Was the operation canceled by one of the parties? */
1685 rc = it->second.pProgress->COMGETTER(Canceled)(&fCanceled);
1686 if (FAILED(rc)) throw rc;
1687
1688 if (!fCanceled)
1689 {
1690 BOOL fCompleted;
1691 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))
1692 && fCompleted)
1693 {
1694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1695
1696 /* Did we get some output? */
1697 pData = (PCALLBACKDATAEXECOUT)it->second.pvData;
1698 Assert(it->second.cbData == sizeof(CALLBACKDATAEXECOUT));
1699 AssertPtr(pData);
1700
1701 if (pData->cbData)
1702 {
1703 /* Do we need to resize the array? */
1704 if (pData->cbData > cbData)
1705 outputData.resize(pData->cbData);
1706
1707 /* Fill output in supplied out buffer. */
1708 memcpy(outputData.raw(), pData->pvData, pData->cbData);
1709 outputData.resize(pData->cbData); /* Shrink to fit actual buffer size. */
1710 }
1711 else
1712 vrc = VERR_NO_DATA; /* This is not an error we want to report to COM. */
1713 }
1714 else /* If callback not called within time ... well, that's a timeout! */
1715 vrc = VERR_TIMEOUT;
1716 }
1717 else /* Operation was canceled. */
1718 {
1719 vrc = VERR_CANCELLED;
1720 }
1721
1722 if (RT_FAILURE(vrc))
1723 {
1724 if (vrc == VERR_NO_DATA)
1725 {
1726 /* This is not an error we want to report to COM. */
1727 rc = S_OK;
1728 }
1729 else if (vrc == VERR_TIMEOUT)
1730 {
1731 rc = setError(VBOX_E_IPRT_ERROR,
1732 tr("The guest did not output within time (%ums)"), aTimeoutMS);
1733 }
1734 else if (vrc == VERR_CANCELLED)
1735 {
1736 rc = setError(VBOX_E_IPRT_ERROR,
1737 tr("The output operation was canceled"));
1738 }
1739 else
1740 {
1741 rc = setError(E_UNEXPECTED,
1742 tr("The service call failed with error %Rrc"), vrc);
1743 }
1744 }
1745
1746 {
1747 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1748 /*
1749 * Destroy locally used progress object.
1750 */
1751 destroyCtrlCallbackContext(it);
1752 }
1753
1754 /* Remove callback context (not used anymore). */
1755 mCallbackMap.erase(it);
1756 }
1757 else /* PID lookup failed. */
1758 rc = setError(VBOX_E_IPRT_ERROR,
1759 tr("Process (PID %u) not found!"), aPID);
1760 }
1761 else /* HGCM operation failed. */
1762 rc = setError(E_UNEXPECTED,
1763 tr("The HGCM call failed with error %Rrc"), vrc);
1764
1765 /* Cleanup. */
1766 progress->uninit();
1767 progress.setNull();
1768
1769 /* If something failed (or there simply was no data, indicated by VERR_NO_DATA,
1770 * we return an empty array so that the frontend knows when to give up. */
1771 if (RT_FAILURE(vrc) || FAILED(rc))
1772 outputData.resize(0);
1773 outputData.detachTo(ComSafeArrayOutArg(aData));
1774 }
1775 catch (std::bad_alloc &)
1776 {
1777 rc = E_OUTOFMEMORY;
1778 }
1779 return rc;
1780#endif
1781}
1782
1783STDMETHODIMP Guest::GetProcessStatus(ULONG aPID, ULONG *aExitCode, ULONG *aFlags, ULONG *aStatus)
1784{
1785#ifndef VBOX_WITH_GUEST_CONTROL
1786 ReturnComNotImplemented();
1787#else /* VBOX_WITH_GUEST_CONTROL */
1788 using namespace guestControl;
1789
1790 AutoCaller autoCaller(this);
1791 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1792
1793 HRESULT rc = S_OK;
1794
1795 try
1796 {
1797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1798
1799 GuestProcessMapIterConst it = getProcessByPID(aPID);
1800 if (it != mGuestProcessMap.end())
1801 {
1802 *aExitCode = it->second.mExitCode;
1803 *aFlags = it->second.mFlags;
1804 *aStatus = it->second.mStatus;
1805 }
1806 else
1807 rc = setError(VBOX_E_IPRT_ERROR,
1808 tr("Process (PID %u) not found!"), aPID);
1809 }
1810 catch (std::bad_alloc &)
1811 {
1812 rc = E_OUTOFMEMORY;
1813 }
1814 return rc;
1815#endif
1816}
1817
1818#ifdef VBOX_WITH_COPYTOGUEST
1819int Guest::directoryEntryAppend(const char *pszPath, PRTLISTNODE pList)
1820{
1821 using namespace guestControl;
1822
1823 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1824 AssertPtrReturn(pList, VERR_INVALID_POINTER);
1825
1826 LogFlowFunc(("Appending to pList=%p: %s\n", pList, pszPath));
1827
1828 VBoxGuestDirEntry *pNode = (VBoxGuestDirEntry*)RTMemAlloc(sizeof(VBoxGuestDirEntry));
1829 if (pNode == NULL)
1830 return VERR_NO_MEMORY;
1831
1832 pNode->pszPath = NULL;
1833 if (RT_SUCCESS(RTStrAAppend(&pNode->pszPath, pszPath)))
1834 {
1835 pNode->Node.pPrev = NULL;
1836 pNode->Node.pNext = NULL;
1837 RTListAppend(pList, &pNode->Node);
1838 return VINF_SUCCESS;
1839 }
1840 return VERR_NO_MEMORY;
1841}
1842
1843int Guest::directoryRead(const char *pszDirectory, const char *pszFilter,
1844 ULONG uFlags, ULONG *pcObjects, PRTLISTNODE pList)
1845{
1846 using namespace guestControl;
1847
1848 AssertPtrReturn(pszDirectory, VERR_INVALID_POINTER);
1849 /* Filter is optional. */
1850 AssertPtrReturn(pcObjects, VERR_INVALID_POINTER);
1851 AssertPtrReturn(pList, VERR_INVALID_POINTER);
1852
1853 LogFlowFunc(("Reading directory: %s, filter: %s\n",
1854 pszDirectory, pszFilter ? pszFilter : "<None>"));
1855
1856 PRTDIR pDir = NULL;
1857 int rc = RTDirOpenFiltered(&pDir, pszDirectory,
1858#ifdef RT_OS_WINDOWS
1859 RTDIRFILTER_WINNT);
1860#else
1861 RTDIRFILTER_UNIX);
1862#endif
1863 char *pszDirectoryStrip = RTStrDup(pszDirectory);
1864 if (!pszDirectoryStrip)
1865 rc = VERR_NO_MEMORY;
1866
1867 if (RT_SUCCESS(rc))
1868 {
1869 RTPathStripFilename(pszDirectoryStrip);
1870 for (;;)
1871 {
1872 RTDIRENTRY DirEntry;
1873 rc = RTDirRead(pDir, &DirEntry, NULL);
1874 if (RT_FAILURE(rc))
1875 {
1876 if (rc == VERR_NO_MORE_FILES)
1877 rc = VINF_SUCCESS;
1878 break;
1879 }
1880 switch (DirEntry.enmType)
1881 {
1882 case RTDIRENTRYTYPE_DIRECTORY:
1883 /* Skip "." and ".." entrires. */
1884 if ( !strcmp(DirEntry.szName, ".")
1885 || !strcmp(DirEntry.szName, ".."))
1886 {
1887 break;
1888 }
1889 if (uFlags & CopyFileFlag_Recursive)
1890 rc = directoryRead(DirEntry.szName, pszFilter,
1891 uFlags, pcObjects, pList);
1892 break;
1893
1894 case RTDIRENTRYTYPE_FILE:
1895 {
1896 char *pszFile;
1897 if (RTStrAPrintf(&pszFile, "%s%c%s",
1898 pszDirectoryStrip, RTPATH_SLASH, DirEntry.szName))
1899 {
1900 rc = directoryEntryAppend(pszFile, pList);
1901 if (RT_SUCCESS(rc))
1902 *pcObjects = *pcObjects + 1;
1903 RTStrFree(pszFile);
1904 }
1905 break;
1906 }
1907
1908 case RTDIRENTRYTYPE_SYMLINK:
1909 if ( (uFlags & CopyFileFlag_Recursive)
1910 && (uFlags & CopyFileFlag_FollowLinks))
1911 {
1912 rc = directoryRead(DirEntry.szName, pszFilter,
1913 uFlags, pcObjects, pList);
1914 }
1915 break;
1916
1917 default:
1918 break;
1919 }
1920 if (RT_FAILURE(rc))
1921 break;
1922 }
1923 RTStrFree(pszDirectoryStrip);
1924 }
1925
1926 if (pDir)
1927 RTDirClose(pDir);
1928 return rc;
1929}
1930#endif
1931
1932STDMETHODIMP Guest::CopyToGuest(IN_BSTR aSource, IN_BSTR aDest, ULONG aFlags,
1933 IProgress **aProgress)
1934{
1935#ifndef VBOX_WITH_GUEST_CONTROL
1936 ReturnComNotImplemented();
1937#else /* VBOX_WITH_GUEST_CONTROL */
1938#ifndef VBOX_WITH_COPYTOGUEST
1939 ReturnComNotImplemented();
1940#else
1941 using namespace guestControl;
1942
1943 CheckComArgStrNotEmptyOrNull(aSource);
1944 CheckComArgStrNotEmptyOrNull(aDest);
1945 CheckComArgOutPointerValid(aProgress);
1946
1947 AutoCaller autoCaller(this);
1948 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1949
1950 /* Validate flags. */
1951 if (aFlags)
1952 {
1953 if ( !(aFlags & CopyFileFlag_Recursive)
1954 && !(aFlags & CopyFileFlag_Update)
1955 && !(aFlags & CopyFileFlag_FollowLinks))
1956 {
1957 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
1958 }
1959 }
1960
1961 HRESULT rc = S_OK;
1962
1963 try
1964 {
1965 ULONG cObjectsToCopy = 0;
1966 RTLISTNODE listEntries;
1967
1968 Utf8Str Utf8Source(aSource);
1969 Utf8Str Utf8Dest(aDest);
1970
1971 char *pszSourceAbs = RTPathAbsDup(Utf8Source.c_str());
1972 if (!pszSourceAbs)
1973 {
1974 rc = setError(VBOX_E_IPRT_ERROR,
1975 tr("Could not determine absolute path of \"%s\""), Utf8Source.c_str());
1976 }
1977 else
1978 {
1979 if (RTDirExists(pszSourceAbs))
1980 {
1981 size_t cch = strlen(pszSourceAbs);
1982 if ( cch > 1
1983 && !RTPATH_IS_SLASH(pszSourceAbs[cch - 1])
1984 && !RTPATH_IS_SLASH(pszSourceAbs[cch - 2]))
1985 {
1986 int vrc = RTStrAAppend(&pszSourceAbs, RTPATH_SLASH_STR);
1987 if (RT_FAILURE(vrc))
1988 rc = setError(VBOX_E_IPRT_ERROR,
1989 tr("Failed to extend source path, rc=%Rrc\n"), vrc);
1990 }
1991 }
1992
1993 if (SUCCEEDED(rc))
1994 {
1995 LogRel(("Copying \"%s\" to guest into \"%s\" ...\n",
1996 pszSourceAbs, Utf8Dest.c_str()));
1997
1998 /*
1999 * Count objects to copy and build file list.
2000 */
2001 RTListInit(&listEntries);
2002 int vrc = directoryRead(pszSourceAbs, NULL /* Filter */,
2003 aFlags, &cObjectsToCopy, &listEntries);
2004 if (RT_FAILURE(vrc))
2005 {
2006 if (vrc != VERR_FILE_NOT_FOUND) /* If no files found, this is no error! */
2007 rc = setError(VBOX_E_IPRT_ERROR,
2008 tr("Error reading source directory \"%s\", rc=%Rrc"),
2009 pszSourceAbs, vrc);
2010 }
2011 }
2012 RTStrFree(pszSourceAbs);
2013 }
2014
2015 if (SUCCEEDED(rc))
2016 {
2017 if (cObjectsToCopy) /* Do we have some objects to copy? */
2018 {
2019 /*
2020 * Create progress object. Note that this is a multi operation
2021 * object to perform an operation per file object we just gathered
2022 * in our list above.
2023 */
2024 ComObjPtr <Progress> progress;
2025 rc = progress.createObject();
2026 if (SUCCEEDED(rc))
2027 {
2028 rc = progress->init(static_cast<IGuest*>(this),
2029 Bstr(tr("Copying to guest")).raw(),
2030 TRUE,
2031 cObjectsToCopy, /* Number of operations. */
2032 Bstr(tr("Copying ...")).raw()); /* Description of first stage. */
2033 }
2034 if (FAILED(rc)) return rc;
2035#if 0
2036 if (SUCCEEDED(rc))
2037 {
2038 PCALLBACKDATAEXECSTATUS pData = (PCALLBACKDATAEXECSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECSTATUS));
2039 AssertReturn(pData, VBOX_E_IPRT_ERROR);
2040 RT_ZERO(*pData);
2041 uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_START,
2042 pData, sizeof(CALLBACKDATAEXECSTATUS), progress);
2043 Assert(uContextID > 0);
2044
2045 VBOXHGCMSVCPARM paParms[15];
2046 int i = 0;
2047 paParms[i++].setUInt32(uContextID);
2048 paParms[i++].setPointer((void*)Utf8Command.c_str(), (uint32_t)Utf8Command.length() + 1);
2049 paParms[i++].setUInt32(aFlags);
2050 paParms[i++].setUInt32(uNumArgs);
2051 paParms[i++].setPointer((void*)pszArgs, cbArgs);
2052 paParms[i++].setUInt32(uNumEnv);
2053 paParms[i++].setUInt32(cbEnv);
2054 paParms[i++].setPointer((void*)pvEnv, cbEnv);
2055 paParms[i++].setPointer((void*)Utf8UserName.c_str(), (uint32_t)Utf8UserName.length() + 1);
2056 paParms[i++].setPointer((void*)Utf8Password.c_str(), (uint32_t)Utf8Password.length() + 1);
2057 paParms[i++].setUInt32(aTimeoutMS);
2058
2059 VMMDev *vmmDev;
2060 {
2061 /* Make sure mParent is valid, so set the read lock while using.
2062 * Do not keep this lock while doing the actual call, because in the meanwhile
2063 * another thread could request a write lock which would be a bad idea ... */
2064 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2065
2066 /* Forward the information to the VMM device. */
2067 AssertPtr(mParent);
2068 vmmDev = mParent->getVMMDev();
2069 }
2070
2071 if (vmmDev)
2072 {
2073 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
2074 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_CMD,
2075 i, paParms);
2076 }
2077 else
2078 vrc = VERR_INVALID_VM_HANDLE;
2079 }
2080#endif
2081 /* Destroy list. */
2082 VBoxGuestDirEntry *pNode = RTListNodeGetFirst(&listEntries, VBoxGuestDirEntry, Node);
2083 while (pNode)
2084 {
2085 VBoxGuestDirEntry *pNext = RTListNodeGetNext(&pNode->Node, VBoxGuestDirEntry, Node);
2086 bool fLast = RTListNodeIsLast(&listEntries, &pNode->Node);
2087
2088 if (pNode->pszPath)
2089 RTStrFree(pNode->pszPath);
2090 RTListNodeRemove(&pNode->Node);
2091 RTMemFree(pNode);
2092
2093 if (fLast)
2094 break;
2095
2096 pNode = pNext;
2097 }
2098 }
2099 }
2100 }
2101 catch (std::bad_alloc &)
2102 {
2103 rc = E_OUTOFMEMORY;
2104 }
2105 return rc;
2106#endif /* VBOX_WITH_COPYTOGUEST */
2107#endif /* VBOX_WITH_GUEST_CONTROL */
2108}
2109
2110// public methods only for internal purposes
2111/////////////////////////////////////////////////////////////////////////////
2112
2113/**
2114 * Sets the general Guest Additions information like
2115 * API (interface) version and OS type. Gets called by
2116 * vmmdevUpdateGuestInfo.
2117 *
2118 * @param aInterfaceVersion
2119 * @param aOsType
2120 */
2121void Guest::setAdditionsInfo(Bstr aInterfaceVersion, VBOXOSTYPE aOsType)
2122{
2123 AutoCaller autoCaller(this);
2124 AssertComRCReturnVoid (autoCaller.rc());
2125
2126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2127
2128 /*
2129 * Note: The Guest Additions API (interface) version is deprecated
2130 * and will not be used anymore! We might need it to at least report
2131 * something as version number if *really* ancient Guest Additions are
2132 * installed (without the guest version + revision properties having set).
2133 */
2134 mData.mInterfaceVersion = aInterfaceVersion;
2135
2136 /*
2137 * Older Additions rely on the Additions API version whether they
2138 * are assumed to be active or not. Since newer Additions do report
2139 * the Additions version *before* calling this function (by calling
2140 * VMMDevReportGuestInfo2, VMMDevReportGuestStatus, VMMDevReportGuestInfo,
2141 * in that order) we can tell apart old and new Additions here. Old
2142 * Additions never would set VMMDevReportGuestInfo2 (which set mData.mAdditionsVersion)
2143 * so they just rely on the aInterfaceVersion string (which gets set by
2144 * VMMDevReportGuestInfo).
2145 *
2146 * So only mark the Additions as being active (run level = system) when we
2147 * don't have the Additions version set.
2148 */
2149 if (mData.mAdditionsVersion.isEmpty())
2150 {
2151 if (aInterfaceVersion.isEmpty())
2152 mData.mAdditionsRunLevel = AdditionsRunLevelType_None;
2153 else
2154 mData.mAdditionsRunLevel = AdditionsRunLevelType_System;
2155 }
2156
2157 /*
2158 * Older Additions didn't have this finer grained capability bit,
2159 * so enable it by default. Newer Additions will not enable this here
2160 * and use the setSupportedFeatures function instead.
2161 */
2162 mData.mSupportsGraphics = mData.mAdditionsRunLevel > AdditionsRunLevelType_None;
2163
2164 /*
2165 * Note! There is a race going on between setting mAdditionsRunLevel and
2166 * mSupportsGraphics here and disabling/enabling it later according to
2167 * its real status when using new(er) Guest Additions.
2168 */
2169 mData.mOSTypeId = Global::OSTypeId (aOsType);
2170}
2171
2172/**
2173 * Sets the Guest Additions version information details.
2174 * Gets called by vmmdevUpdateGuestInfo2.
2175 *
2176 * @param aAdditionsVersion
2177 * @param aVersionName
2178 */
2179void Guest::setAdditionsInfo2(Bstr aAdditionsVersion, Bstr aVersionName, Bstr aRevision)
2180{
2181 AutoCaller autoCaller(this);
2182 AssertComRCReturnVoid (autoCaller.rc());
2183
2184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2185
2186 if (!aVersionName.isEmpty())
2187 /*
2188 * aVersionName could be "x.y.z_BETA1_FOOBAR", so append revision manually to
2189 * become "x.y.z_BETA1_FOOBARr12345".
2190 */
2191 mData.mAdditionsVersion = BstrFmt("%ls r%ls", aVersionName.raw(), aRevision.raw());
2192 else /* aAdditionsVersion is in x.y.zr12345 format. */
2193 mData.mAdditionsVersion = aAdditionsVersion;
2194}
2195
2196/**
2197 * Sets the status of a certain Guest Additions facility.
2198 * Gets called by vmmdevUpdateGuestStatus.
2199 *
2200 * @param Facility
2201 * @param Status
2202 * @param ulFlags
2203 */
2204void Guest::setAdditionsStatus(VBoxGuestStatusFacility Facility, VBoxGuestStatusCurrent Status, ULONG ulFlags)
2205{
2206 AutoCaller autoCaller(this);
2207 AssertComRCReturnVoid (autoCaller.rc());
2208
2209 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2210
2211 uint32_t uCurFacility = Facility + (Status == VBoxGuestStatusCurrent_Active ? 0 : -1);
2212
2213 /* First check for disabled status. */
2214 if ( Facility < VBoxGuestStatusFacility_VBoxGuestDriver
2215 || ( Facility == VBoxGuestStatusFacility_All
2216 && ( Status == VBoxGuestStatusCurrent_Inactive
2217 || Status == VBoxGuestStatusCurrent_Disabled
2218 )
2219 )
2220 )
2221 {
2222 mData.mAdditionsRunLevel = AdditionsRunLevelType_None;
2223 }
2224 else if (uCurFacility >= VBoxGuestStatusFacility_VBoxTray)
2225 {
2226 mData.mAdditionsRunLevel = AdditionsRunLevelType_Desktop;
2227 }
2228 else if (uCurFacility >= VBoxGuestStatusFacility_VBoxService)
2229 {
2230 mData.mAdditionsRunLevel = AdditionsRunLevelType_Userland;
2231 }
2232 else if (uCurFacility >= VBoxGuestStatusFacility_VBoxGuestDriver)
2233 {
2234 mData.mAdditionsRunLevel = AdditionsRunLevelType_System;
2235 }
2236 else /* Should never happen! */
2237 AssertMsgFailed(("Invalid facility status/run level detected! uCurFacility=%ld\n", uCurFacility));
2238}
2239
2240/**
2241 * Sets the supported features (and whether they are active or not).
2242 *
2243 * @param fCaps Guest capability bit mask (VMMDEV_GUEST_SUPPORTS_XXX).
2244 * @param fActive No idea what this is supposed to be, it's always 0 and
2245 * not references by this method.
2246 */
2247void Guest::setSupportedFeatures(uint32_t fCaps, uint32_t fActive)
2248{
2249 AutoCaller autoCaller(this);
2250 AssertComRCReturnVoid (autoCaller.rc());
2251
2252 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2253
2254 mData.mSupportsSeamless = (fCaps & VMMDEV_GUEST_SUPPORTS_SEAMLESS);
2255 /** @todo Add VMMDEV_GUEST_SUPPORTS_GUEST_HOST_WINDOW_MAPPING */
2256 mData.mSupportsGraphics = (fCaps & VMMDEV_GUEST_SUPPORTS_GRAPHICS);
2257}
2258/* vi: set tabstop=4 shiftwidth=4 expandtab: */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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