VirtualBox

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

最後變更 在這個檔案從35129是 35129,由 vboxsync 提交於 14 年 前

GuestAdditionsUpdate/Main: Minor logging + waiting time adjustments.

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

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