VirtualBox

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

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