VirtualBox

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

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

Comment.

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