VirtualBox

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

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

Main/IGuest::updateGuestAdditions: Introduced additional flags parameter for later use.

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