VirtualBox

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

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

Automatic Guest Additions update/Main: Removed not needed release logging, pass more error information, added abort handling while executing Guest Additions setup.

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

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