VirtualBox

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

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

Guest Control: Provide canceling overall task progress as well.

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

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