VirtualBox

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

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

Adjusted flags checking.

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