VirtualBox

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

Last change on this file since 33223 was 33139, checked in by vboxsync, 14 years ago

Warnings.

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

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