VirtualBox

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

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

Guest Control: Updates on guest path/environment resolving, ignore/skip not implemented guests when doing automatic Guest Additions update.

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