VirtualBox

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

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

Logging.

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

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