VirtualBox

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

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

src/*: more spelling fixes (logging), thanks Timeless!

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