VirtualBox

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

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

Automatic Guest Additions update: Support/graceful handling for old(er) Guest Additions.

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

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