VirtualBox

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

Last change on this file since 33464 was 33464, checked in by vboxsync, 15 years ago

*: Fixes for incorrect RTStrAPrintf usage (it does NOT return an IPRT status code) and the odd bugs nearby.

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

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