VirtualBox

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

Last change on this file since 33517 was 33510, checked in by vboxsync, 14 years ago

Guest Copy/Guest Additions: Also don't wait forever when spawning Guest Additions installer.

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