VirtualBox

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

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

GuestControl/Main: Wait 10s for setting process input.

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