VirtualBox

source: vbox/trunk/src/VBox/Main/GuestCtrlImpl.cpp@ 35346

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

VMM reorg: Moving the public include files from include/VBox to include/VBox/vmm.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 99.8 KB
 
1/* $Id: GuestCtrlImpl.cpp 35346 2010-12-27 16:13:13Z vboxsync $ */
2/** @file
3 * VirtualBox COM class implementation: Guest
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include "GuestImpl.h"
19
20#include "Global.h"
21#include "ConsoleImpl.h"
22#include "ProgressImpl.h"
23#include "VMMDev.h"
24
25#include "AutoCaller.h"
26#include "Logging.h"
27
28#include <VBox/VMMDev.h>
29#ifdef VBOX_WITH_GUEST_CONTROL
30# include <VBox/com/array.h>
31# include <VBox/com/ErrorInfo.h>
32#endif
33#include <iprt/cpp/utils.h>
34#include <iprt/file.h>
35#include <iprt/getopt.h>
36#include <iprt/isofs.h>
37#include <iprt/list.h>
38#include <iprt/path.h>
39#include <VBox/vmm/pgm.h>
40
41#include <memory>
42
43struct Guest::TaskGuest
44{
45 enum TaskType
46 {
47 /** Copies a file to the guest. */
48 CopyFile = 50,
49
50 /** Update Guest Additions by directly copying the required installer
51 * off the .ISO file, transfer it to the guest and execute the installer
52 * with system privileges. */
53 UpdateGuestAdditions = 100
54 };
55
56 TaskGuest(TaskType aTaskType, Guest *aThat, Progress *aProgress)
57 : taskType(aTaskType),
58 pGuest(aThat),
59 progress(aProgress),
60 rc(S_OK)
61 {}
62 ~TaskGuest() {}
63
64 int startThread();
65 static int taskThread(RTTHREAD aThread, void *pvUser);
66 static int uploadProgress(unsigned uPercent, void *pvUser);
67
68 static HRESULT setProgressErrorInfo(HRESULT hr, ComObjPtr<Progress> pProgress, const char * pszText, ...);
69 static HRESULT setProgressErrorInfo(HRESULT hr, ComObjPtr<Progress> pProgress, ComObjPtr<Guest> pGuest);
70
71 TaskType taskType;
72 Guest *pGuest;
73 ComObjPtr<Progress> progress;
74 HRESULT rc;
75
76 /* Task data. */
77 Utf8Str strSource;
78 Utf8Str strDest;
79 Utf8Str strUserName;
80 Utf8Str strPassword;
81 ULONG uFlags;
82};
83
84int Guest::TaskGuest::startThread()
85{
86 int vrc = RTThreadCreate(NULL, Guest::TaskGuest::taskThread, this,
87 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
88 "Guest::Task");
89
90 if (RT_FAILURE(vrc))
91 return Guest::setErrorStatic(E_FAIL, Utf8StrFmt("Could not create taskThreadGuest (%Rrc)\n", vrc));
92
93 return vrc;
94}
95
96/* static */
97DECLCALLBACK(int) Guest::TaskGuest::taskThread(RTTHREAD /* aThread */, void *pvUser)
98{
99 std::auto_ptr<TaskGuest> task(static_cast<TaskGuest*>(pvUser));
100 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
101
102 Guest *pGuest = task->pGuest;
103
104 LogFlowFuncEnter();
105 LogFlowFunc(("Guest %p\n", pGuest));
106
107 HRESULT rc = S_OK;
108
109 switch (task->taskType)
110 {
111#ifdef VBOX_WITH_GUEST_CONTROL
112 case TaskGuest::CopyFile:
113 {
114 rc = pGuest->taskCopyFile(task.get());
115 break;
116 }
117 case TaskGuest::UpdateGuestAdditions:
118 {
119 rc = pGuest->taskUpdateGuestAdditions(task.get());
120 break;
121 }
122#endif
123 default:
124 AssertMsgFailed(("Invalid task type %u specified!\n", task->taskType));
125 break;
126 }
127
128 LogFlowFunc(("rc=%Rhrc\n", rc));
129 LogFlowFuncLeave();
130
131 return VINF_SUCCESS;
132}
133
134/* static */
135int Guest::TaskGuest::uploadProgress(unsigned uPercent, void *pvUser)
136{
137 Guest::TaskGuest *pTask = *(Guest::TaskGuest**)pvUser;
138
139 if (pTask &&
140 !pTask->progress.isNull())
141 {
142 BOOL fCanceled;
143 pTask->progress->COMGETTER(Canceled)(&fCanceled);
144 if (fCanceled)
145 return -1;
146 pTask->progress->SetCurrentOperationProgress(uPercent);
147 }
148 return VINF_SUCCESS;
149}
150
151/* static */
152HRESULT Guest::TaskGuest::setProgressErrorInfo(HRESULT hr, ComObjPtr<Progress> pProgress, const char *pszText, ...)
153{
154 BOOL fCanceled;
155 BOOL fCompleted;
156 if ( SUCCEEDED(pProgress->COMGETTER(Canceled(&fCanceled)))
157 && !fCanceled
158 && SUCCEEDED(pProgress->COMGETTER(Completed(&fCompleted)))
159 && !fCompleted)
160 {
161 va_list va;
162 va_start(va, pszText);
163 HRESULT hr2 = pProgress->notifyCompleteV(hr,
164 COM_IIDOF(IGuest),
165 Guest::getStaticComponentName(),
166 pszText,
167 va);
168 va_end(va);
169 if (hr2 == S_OK) /* If unable to retrieve error, return input error. */
170 hr2 = hr;
171 return hr2;
172 }
173 return S_OK;
174}
175
176/* static */
177HRESULT Guest::TaskGuest::setProgressErrorInfo(HRESULT hr, ComObjPtr<Progress> pProgress, ComObjPtr<Guest> pGuest)
178{
179 return setProgressErrorInfo(hr, pProgress,
180 Utf8Str(com::ErrorInfo((IGuest*)pGuest, COM_IIDOF(IGuest)).getText()).c_str());
181}
182
183#ifdef VBOX_WITH_GUEST_CONTROL
184HRESULT Guest::taskCopyFile(TaskGuest *aTask)
185{
186 LogFlowFuncEnter();
187
188 AutoCaller autoCaller(this);
189 if (FAILED(autoCaller.rc())) return autoCaller.rc();
190
191 /*
192 * Do *not* take a write lock here since we don't (and won't)
193 * touch any class-specific data (of IGuest) here - only the member functions
194 * which get called here can do that.
195 */
196
197 HRESULT rc = S_OK;
198
199 try
200 {
201 Guest *pGuest = aTask->pGuest;
202 AssertPtr(pGuest);
203
204 /* Does our source file exist? */
205 if (!RTFileExists(aTask->strSource.c_str()))
206 {
207 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
208 Guest::tr("Source file \"%s\" does not exist"),
209 aTask->strSource.c_str());
210 }
211 else
212 {
213 RTFILE fileSource;
214 int vrc = RTFileOpen(&fileSource, aTask->strSource.c_str(),
215 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
216 if (RT_FAILURE(vrc))
217 {
218 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
219 Guest::tr("Could not open source file \"%s\" for reading (%Rrc)"),
220 aTask->strSource.c_str(), vrc);
221 }
222 else
223 {
224 uint64_t cbSize;
225 vrc = RTFileGetSize(fileSource, &cbSize);
226 if (RT_FAILURE(vrc))
227 {
228 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
229 Guest::tr("Could not query file size of \"%s\" (%Rrc)"),
230 aTask->strSource.c_str(), vrc);
231 }
232 else
233 {
234 com::SafeArray<IN_BSTR> args;
235 com::SafeArray<IN_BSTR> env;
236
237 /*
238 * Prepare tool command line.
239 */
240 char szOutput[RTPATH_MAX];
241 if (RTStrPrintf(szOutput, sizeof(szOutput), "--output=%s", aTask->strDest.c_str()) <= sizeof(szOutput) - 1)
242 {
243 /*
244 * Normalize path slashes, based on the detected guest.
245 */
246 Utf8Str osType = mData.mOSTypeId;
247 if ( osType.contains("Microsoft", Utf8Str::CaseInsensitive)
248 || osType.contains("Windows", Utf8Str::CaseInsensitive))
249 {
250 /* We have a Windows guest. */
251 RTPathChangeToDosSlashes(szOutput, true /* Force conversion. */);
252 }
253 else /* ... or something which isn't from Redmond ... */
254 {
255 RTPathChangeToUnixSlashes(szOutput, true /* Force conversion. */);
256 }
257
258 args.push_back(Bstr(VBOXSERVICE_TOOL_CAT).raw()); /* The actual (internal) tool to use (as argv[0]). */
259 args.push_back(Bstr(szOutput).raw()); /* We want to write a file ... */
260 }
261 else
262 {
263 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
264 Guest::tr("Error preparing command line"));
265 }
266
267 ComPtr<IProgress> execProgress;
268 ULONG uPID;
269 if (SUCCEEDED(rc))
270 {
271 LogRel(("Copying file \"%s\" to guest \"%s\" (%u bytes) ...\n",
272 aTask->strSource.c_str(), aTask->strDest.c_str(), cbSize));
273 /*
274 * Okay, since we gathered all stuff we need until now to start the
275 * actual copying, start the guest part now.
276 */
277 rc = pGuest->ExecuteProcess(Bstr(VBOXSERVICE_TOOL_CAT).raw(),
278 ExecuteProcessFlag_Hidden
279 | ExecuteProcessFlag_WaitForProcessStartOnly,
280 ComSafeArrayAsInParam(args),
281 ComSafeArrayAsInParam(env),
282 Bstr(aTask->strUserName).raw(),
283 Bstr(aTask->strPassword).raw(),
284 5 * 1000 /* Wait 5s for getting the process started. */,
285 &uPID, execProgress.asOutParam());
286 if (FAILED(rc))
287 rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);
288 }
289
290 if (SUCCEEDED(rc))
291 {
292 BOOL fCompleted = FALSE;
293 BOOL fCanceled = FALSE;
294
295 size_t cbToRead = cbSize;
296 size_t cbTransfered = 0;
297 size_t cbRead;
298 SafeArray<BYTE> aInputData(_1M);
299 while ( SUCCEEDED(execProgress->COMGETTER(Completed(&fCompleted)))
300 && !fCompleted)
301 {
302 if (!cbToRead)
303 cbRead = 0;
304 else
305 {
306 vrc = RTFileRead(fileSource, (uint8_t*)aInputData.raw(),
307 RT_MIN(cbToRead, _1M), &cbRead);
308 /*
309 * Some other error occured? There might be a chance that RTFileRead
310 * could not resolve/map the native error code to an IPRT code, so just
311 * print a generic error.
312 */
313 if (RT_FAILURE(vrc))
314 {
315 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
316 Guest::tr("Could not read from file \"%s\" (%Rrc)"),
317 aTask->strSource.c_str(), vrc);
318 break;
319 }
320 }
321
322 /* Resize buffer to reflect amount we just have read.
323 * Size 0 is allowed! */
324 aInputData.resize(cbRead);
325
326 ULONG uFlags = ProcessInputFlag_None;
327 /* Did we reach the end of the content we want to transfer (last chunk)? */
328 if ( (cbRead < _1M)
329 /* ... or does the user want to cancel? */
330 || ( SUCCEEDED(aTask->progress->COMGETTER(Canceled(&fCanceled)))
331 && fCanceled)
332 )
333 {
334 uFlags |= ProcessInputFlag_EndOfFile;
335 }
336
337 /* Transfer the current chunk ... */
338 ULONG uBytesWritten;
339 rc = pGuest->SetProcessInput(uPID, uFlags,
340 10 * 1000 /* Wait 10s for getting the input data transfered. */,
341 ComSafeArrayAsInParam(aInputData), &uBytesWritten);
342 if (FAILED(rc))
343 {
344 rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);
345 break;
346 }
347
348 Assert(cbRead <= cbToRead);
349 Assert(cbToRead >= cbRead);
350 cbToRead -= cbRead;
351
352 cbTransfered += uBytesWritten;
353 Assert(cbTransfered <= cbSize);
354 aTask->progress->SetCurrentOperationProgress(cbTransfered / (cbSize / 100.0));
355
356 /* End of file reached? */
357 if (cbToRead == 0)
358 break;
359
360 /* Progress canceled by Main API? */
361 if ( SUCCEEDED(execProgress->COMGETTER(Canceled(&fCanceled)))
362 && fCanceled)
363 {
364 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
365 Guest::tr("Copy operation of file \"%s\" was canceled on guest side"),
366 aTask->strSource.c_str());
367 break;
368 }
369 }
370
371 if (SUCCEEDED(rc))
372 aTask->progress->notifyComplete(S_OK);
373 }
374 }
375 RTFileClose(fileSource);
376 }
377 }
378 }
379 catch (HRESULT aRC)
380 {
381 rc = aRC;
382 }
383
384 /* Clean up */
385 aTask->rc = rc;
386
387 LogFlowFunc(("rc=%Rhrc\n", rc));
388 LogFlowFuncLeave();
389
390 return VINF_SUCCESS;
391}
392
393HRESULT Guest::taskUpdateGuestAdditions(TaskGuest *aTask)
394{
395 LogFlowFuncEnter();
396
397 AutoCaller autoCaller(this);
398 if (FAILED(autoCaller.rc())) return autoCaller.rc();
399
400 /*
401 * Do *not* take a write lock here since we don't (and won't)
402 * touch any class-specific data (of IGuest) here - only the member functions
403 * which get called here can do that.
404 */
405
406 HRESULT rc = S_OK;
407 BOOL fCompleted;
408 BOOL fCanceled;
409
410 try
411 {
412 Guest *pGuest = aTask->pGuest;
413 AssertPtr(pGuest);
414
415 aTask->progress->SetCurrentOperationProgress(10);
416
417 /*
418 * Determine guest OS type and the required installer image.
419 * At the moment only Windows guests are supported.
420 */
421 Utf8Str installerImage;
422 Bstr osTypeId;
423 if ( SUCCEEDED(pGuest->COMGETTER(OSTypeId(osTypeId.asOutParam())))
424 && !osTypeId.isEmpty())
425 {
426 Utf8Str osTypeIdUtf8(osTypeId); /* Needed for .contains(). */
427 if ( osTypeIdUtf8.contains("Microsoft", Utf8Str::CaseInsensitive)
428 || osTypeIdUtf8.contains("Windows", Utf8Str::CaseInsensitive))
429 {
430 if (osTypeIdUtf8.contains("64", Utf8Str::CaseInsensitive))
431 installerImage = "VBOXWINDOWSADDITIONS_AMD64.EXE";
432 else
433 installerImage = "VBOXWINDOWSADDITIONS_X86.EXE";
434 /* Since the installers are located in the root directory,
435 * no further path processing needs to be done (yet). */
436 }
437 else /* Everything else is not supported (yet). */
438 throw TaskGuest::setProgressErrorInfo(VBOX_E_NOT_SUPPORTED, aTask->progress,
439 Guest::tr("Detected guest OS (%s) does not support automatic Guest Additions updating, please update manually"),
440 osTypeIdUtf8.c_str());
441 }
442 else
443 throw TaskGuest::setProgressErrorInfo(VBOX_E_NOT_SUPPORTED, aTask->progress,
444 Guest::tr("Could not detected guest OS type/version, please update manually"));
445 Assert(!installerImage.isEmpty());
446
447 /*
448 * Try to open the .ISO file and locate the specified installer.
449 */
450 RTISOFSFILE iso;
451 int vrc = RTIsoFsOpen(&iso, aTask->strSource.c_str());
452 if (RT_FAILURE(vrc))
453 {
454 rc = TaskGuest::setProgressErrorInfo(VBOX_E_FILE_ERROR, aTask->progress,
455 Guest::tr("Invalid installation medium detected: \"%s\""),
456 aTask->strSource.c_str());
457 }
458 else
459 {
460 uint32_t cbOffset;
461 size_t cbLength;
462 vrc = RTIsoFsGetFileInfo(&iso, installerImage.c_str(), &cbOffset, &cbLength);
463 if ( RT_SUCCESS(vrc)
464 && cbOffset
465 && cbLength)
466 {
467 vrc = RTFileSeek(iso.file, cbOffset, RTFILE_SEEK_BEGIN, NULL);
468 if (RT_FAILURE(vrc))
469 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
470 Guest::tr("Could not seek to setup file on installation medium \"%s\" (%Rrc)"),
471 aTask->strSource.c_str(), vrc);
472 }
473 else
474 {
475 switch (vrc)
476 {
477 case VERR_FILE_NOT_FOUND:
478 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
479 Guest::tr("Setup file was not found on installation medium \"%s\""),
480 aTask->strSource.c_str());
481 break;
482
483 default:
484 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
485 Guest::tr("An unknown error (%Rrc) occured while retrieving information of setup file on installation medium \"%s\""),
486 vrc, aTask->strSource.c_str());
487 break;
488 }
489 }
490
491 /* Specify the ouput path on the guest side. */
492 Utf8Str strInstallerPath = "%TEMP%\\VBoxWindowsAdditions.exe";
493
494 if (RT_SUCCESS(vrc))
495 {
496 /* Okay, we're ready to start our copy routine on the guest! */
497 aTask->progress->SetCurrentOperationProgress(15);
498
499 /* Prepare command line args. */
500 com::SafeArray<IN_BSTR> args;
501 com::SafeArray<IN_BSTR> env;
502
503 args.push_back(Bstr(VBOXSERVICE_TOOL_CAT).raw()); /* The actual (internal) tool to use (as argv[0]). */
504 args.push_back(Bstr("--output").raw()); /* We want to write a file ... */
505 args.push_back(Bstr(strInstallerPath.c_str()).raw()); /* ... with this path. */
506
507 if (SUCCEEDED(rc))
508 {
509 ComPtr<IProgress> progressCat;
510 ULONG uPID;
511
512 /*
513 * Start built-in "vbox_cat" tool (inside VBoxService) to
514 * copy over/pipe the data into a file on the guest (with
515 * system rights, no username/password specified).
516 */
517 rc = pGuest->executeProcessInternal(Bstr(VBOXSERVICE_TOOL_CAT).raw(),
518 ExecuteProcessFlag_Hidden
519 | ExecuteProcessFlag_WaitForProcessStartOnly,
520 ComSafeArrayAsInParam(args),
521 ComSafeArrayAsInParam(env),
522 Bstr("").raw() /* Username. */,
523 Bstr("").raw() /* Password */,
524 5 * 1000 /* Wait 5s for getting the process started. */,
525 &uPID, progressCat.asOutParam(), &vrc);
526 if (FAILED(rc))
527 {
528 /* Errors which return VBOX_E_NOT_SUPPORTED can be safely skipped by the caller
529 * to silently fall back to "normal" (old) .ISO mounting. */
530
531 /* Due to a very limited COM error range we use vrc for a more detailed error
532 * lookup to figure out what went wrong. */
533 switch (vrc)
534 {
535 /* Guest execution service is not (yet) ready. This basically means that either VBoxService
536 * is not running (yet) or that the Guest Additions are too old (because VBoxService does not
537 * support the guest execution feature in this version). */
538 case VERR_NOT_FOUND:
539 LogRel(("Guest Additions seem not to be installed yet\n"));
540 rc = TaskGuest::setProgressErrorInfo(VBOX_E_NOT_SUPPORTED, aTask->progress,
541 Guest::tr("Guest Additions seem not to be installed or are not ready to update yet"));
542 break;
543
544 /* Getting back a VERR_INVALID_PARAMETER indicates that the installed Guest Additions are supporting the guest
545 * execution but not the built-in "vbox_cat" tool of VBoxService (< 4.0). */
546 case VERR_INVALID_PARAMETER:
547 LogRel(("Guest Additions are installed but don't supported automatic updating\n"));
548 rc = TaskGuest::setProgressErrorInfo(VBOX_E_NOT_SUPPORTED, aTask->progress,
549 Guest::tr("Installed Guest Additions do not support automatic updating"));
550 break;
551
552 default:
553 rc = TaskGuest::setProgressErrorInfo(E_FAIL, aTask->progress,
554 Guest::tr("Error copying Guest Additions setup file to guest path \"%s\" (%Rrc)"),
555 strInstallerPath.c_str(), vrc);
556 break;
557 }
558 }
559 else
560 {
561 LogRel(("Automatic update of Guest Additions started, using \"%s\"\n", aTask->strSource.c_str()));
562 LogRel(("Copying Guest Additions installer \"%s\" to \"%s\" on guest ...\n",
563 installerImage.c_str(), strInstallerPath.c_str()));
564 aTask->progress->SetCurrentOperationProgress(20);
565
566 /* Wait for process to exit ... */
567 SafeArray<BYTE> aInputData(_1M);
568 while ( SUCCEEDED(progressCat->COMGETTER(Completed(&fCompleted)))
569 && !fCompleted)
570 {
571 size_t cbRead;
572 /* cbLength contains remaining bytes of our installer file
573 * opened above to read. */
574 size_t cbToRead = RT_MIN(cbLength, _1M);
575 if (cbToRead)
576 {
577 vrc = RTFileRead(iso.file, (uint8_t*)aInputData.raw(), cbToRead, &cbRead);
578 if ( cbRead
579 && RT_SUCCESS(vrc))
580 {
581 /* Resize buffer to reflect amount we just have read. */
582 if (cbRead > 0)
583 aInputData.resize(cbRead);
584
585 /* Did we reach the end of the content we want to transfer (last chunk)? */
586 ULONG uFlags = ProcessInputFlag_None;
587 if ( (cbRead < _1M)
588 /* ... or does the user want to cancel? */
589 || ( SUCCEEDED(aTask->progress->COMGETTER(Canceled(&fCanceled)))
590 && fCanceled)
591 )
592 {
593 uFlags |= ProcessInputFlag_EndOfFile;
594 }
595
596 /* Transfer the current chunk ... */
597 #ifdef DEBUG_andy
598 LogRel(("Copying Guest Additions (%u bytes left) ...\n", cbLength));
599 #endif
600 ULONG uBytesWritten;
601 rc = pGuest->SetProcessInput(uPID, uFlags,
602 10 * 1000 /* Wait 10s for getting the input data transfered. */,
603 ComSafeArrayAsInParam(aInputData), &uBytesWritten);
604 if (FAILED(rc))
605 {
606 rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);
607 break;
608 }
609
610 /* If task was canceled above also cancel the process execution. */
611 if (fCanceled)
612 progressCat->Cancel();
613
614 #ifdef DEBUG_andy
615 LogRel(("Copying Guest Additions (%u bytes written) ...\n", uBytesWritten));
616 #endif
617 Assert(cbLength >= uBytesWritten);
618 cbLength -= uBytesWritten;
619 }
620 else if (RT_FAILURE(vrc))
621 {
622 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
623 Guest::tr("Error while reading setup file \"%s\" (To read: %u, Size: %u) from installation medium (%Rrc)"),
624 installerImage.c_str(), cbToRead, cbLength, vrc);
625 }
626 }
627
628 /* Internal progress canceled? */
629 if ( SUCCEEDED(progressCat->COMGETTER(Canceled(&fCanceled)))
630 && fCanceled)
631 {
632 aTask->progress->Cancel();
633 break;
634 }
635 }
636 }
637 }
638 }
639 RTIsoFsClose(&iso);
640
641 if ( SUCCEEDED(rc)
642 && ( SUCCEEDED(aTask->progress->COMGETTER(Canceled(&fCanceled)))
643 && !fCanceled
644 )
645 )
646 {
647 /*
648 * Installer was transferred successfully, so let's start it
649 * (with system rights).
650 */
651 LogRel(("Preparing to execute Guest Additions update ...\n"));
652 aTask->progress->SetCurrentOperationProgress(66);
653
654 /* Prepare command line args for installer. */
655 com::SafeArray<IN_BSTR> installerArgs;
656 com::SafeArray<IN_BSTR> installerEnv;
657
658 /** @todo Only Windows! */
659 installerArgs.push_back(Bstr(strInstallerPath).raw()); /* The actual (internal) installer image (as argv[0]). */
660 /* Note that starting at Windows Vista the lovely session 0 separation applies:
661 * This means that if we run an application with the profile/security context
662 * of VBoxService (system rights!) we're not able to show any UI. */
663 installerArgs.push_back(Bstr("/S").raw()); /* We want to install in silent mode. */
664 installerArgs.push_back(Bstr("/l").raw()); /* ... and logging enabled. */
665 /* Don't quit VBoxService during upgrade because it still is used for this
666 * piece of code we're in right now (that is, here!) ... */
667 installerArgs.push_back(Bstr("/no_vboxservice_exit").raw());
668 /* Tell the installer to report its current installation status
669 * using a running VBoxTray instance via balloon messages in the
670 * Windows taskbar. */
671 installerArgs.push_back(Bstr("/post_installstatus").raw());
672
673 /*
674 * Start the just copied over installer with system rights
675 * in silent mode on the guest. Don't use the hidden flag since there
676 * may be pop ups the user has to process.
677 */
678 ComPtr<IProgress> progressInstaller;
679 ULONG uPID;
680 rc = pGuest->executeProcessInternal(Bstr(strInstallerPath).raw(),
681 ExecuteProcessFlag_WaitForProcessStartOnly,
682 ComSafeArrayAsInParam(installerArgs),
683 ComSafeArrayAsInParam(installerEnv),
684 Bstr("").raw() /* Username */,
685 Bstr("").raw() /* Password */,
686 10 * 1000 /* Wait 10s for getting the process started */,
687 &uPID, progressInstaller.asOutParam(), &vrc);
688 if (SUCCEEDED(rc))
689 {
690 LogRel(("Guest Additions update is running ...\n"));
691
692 /* If the caller does not want to wait for out guest update process to end,
693 * complete the progress object now so that the caller can do other work. */
694 if (aTask->uFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly)
695 aTask->progress->notifyComplete(S_OK);
696 else
697 aTask->progress->SetCurrentOperationProgress(70);
698
699 /* Wait until the Guest Additions installer finishes ... */
700 while ( SUCCEEDED(progressInstaller->COMGETTER(Completed(&fCompleted)))
701 && !fCompleted)
702 {
703 if ( SUCCEEDED(aTask->progress->COMGETTER(Canceled(&fCanceled)))
704 && fCanceled)
705 {
706 progressInstaller->Cancel();
707 break;
708 }
709 /* Progress canceled by Main API? */
710 if ( SUCCEEDED(progressInstaller->COMGETTER(Canceled(&fCanceled)))
711 && fCanceled)
712 {
713 break;
714 }
715 RTThreadSleep(100);
716 }
717
718 ULONG uRetStatus, uRetExitCode, uRetFlags;
719 rc = pGuest->GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &uRetStatus);
720 if (SUCCEEDED(rc))
721 {
722 if (fCompleted)
723 {
724 if (uRetExitCode == 0)
725 {
726 LogRel(("Guest Additions update successful!\n"));
727 if ( SUCCEEDED(aTask->progress->COMGETTER(Completed(&fCompleted)))
728 && !fCompleted)
729 aTask->progress->notifyComplete(S_OK);
730 }
731 else
732 {
733 LogRel(("Guest Additions update failed (Exit code=%u, Status=%u, Flags=%u)\n",
734 uRetExitCode, uRetStatus, uRetFlags));
735 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
736 Guest::tr("Guest Additions update failed with exit code=%u (status=%u, flags=%u)"),
737 uRetExitCode, uRetStatus, uRetFlags);
738 }
739 }
740 else if ( SUCCEEDED(progressInstaller->COMGETTER(Canceled(&fCanceled)))
741 && fCanceled)
742 {
743 LogRel(("Guest Additions update was canceled\n"));
744 rc = TaskGuest::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
745 Guest::tr("Guest Additions update was canceled by the guest with exit code=%u (status=%u, flags=%u)"),
746 uRetExitCode, uRetStatus, uRetFlags);
747 }
748 else
749 {
750 LogRel(("Guest Additions update was canceled by the user\n"));
751 }
752 }
753 else
754 rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);
755 }
756 else
757 rc = TaskGuest::setProgressErrorInfo(rc, aTask->progress, pGuest);
758 }
759 }
760 }
761 catch (HRESULT aRC)
762 {
763 rc = aRC;
764 }
765
766 /* Clean up */
767 aTask->rc = rc;
768
769 LogFlowFunc(("rc=%Rhrc\n", rc));
770 LogFlowFuncLeave();
771
772 return VINF_SUCCESS;
773}
774#endif
775
776// public methods only for internal purposes
777/////////////////////////////////////////////////////////////////////////////
778
779#ifdef VBOX_WITH_GUEST_CONTROL
780/**
781 * Appends environment variables to the environment block.
782 *
783 * Each var=value pair is separated by the null character ('\\0'). The whole
784 * block will be stored in one blob and disassembled on the guest side later to
785 * fit into the HGCM param structure.
786 *
787 * @returns VBox status code.
788 *
789 * @param pszEnvVar The environment variable=value to append to the
790 * environment block.
791 * @param ppvList This is actually a pointer to a char pointer
792 * variable which keeps track of the environment block
793 * that we're constructing.
794 * @param pcbList Pointer to the variable holding the current size of
795 * the environment block. (List is a misnomer, go
796 * ahead a be confused.)
797 * @param pcEnvVars Pointer to the variable holding count of variables
798 * stored in the environment block.
799 */
800int Guest::prepareExecuteEnv(const char *pszEnv, void **ppvList, uint32_t *pcbList, uint32_t *pcEnvVars)
801{
802 int rc = VINF_SUCCESS;
803 uint32_t cchEnv = strlen(pszEnv); Assert(cchEnv >= 2);
804 if (*ppvList)
805 {
806 uint32_t cbNewLen = *pcbList + cchEnv + 1; /* Include zero termination. */
807 char *pvTmp = (char *)RTMemRealloc(*ppvList, cbNewLen);
808 if (pvTmp == NULL)
809 rc = VERR_NO_MEMORY;
810 else
811 {
812 memcpy(pvTmp + *pcbList, pszEnv, cchEnv);
813 pvTmp[cbNewLen - 1] = '\0'; /* Add zero termination. */
814 *ppvList = (void **)pvTmp;
815 }
816 }
817 else
818 {
819 char *pszTmp;
820 if (RTStrAPrintf(&pszTmp, "%s", pszEnv) >= 0)
821 {
822 *ppvList = (void **)pszTmp;
823 /* Reset counters. */
824 *pcEnvVars = 0;
825 *pcbList = 0;
826 }
827 }
828 if (RT_SUCCESS(rc))
829 {
830 *pcbList += cchEnv + 1; /* Include zero termination. */
831 *pcEnvVars += 1; /* Increase env variable count. */
832 }
833 return rc;
834}
835
836/**
837 * Static callback function for receiving updates on guest control commands
838 * from the guest. Acts as a dispatcher for the actual class instance.
839 *
840 * @returns VBox status code.
841 *
842 * @todo
843 *
844 */
845DECLCALLBACK(int) Guest::doGuestCtrlNotification(void *pvExtension,
846 uint32_t u32Function,
847 void *pvParms,
848 uint32_t cbParms)
849{
850 using namespace guestControl;
851
852 /*
853 * No locking, as this is purely a notification which does not make any
854 * changes to the object state.
855 */
856#ifdef DEBUG_andy
857 LogFlowFunc(("pvExtension = %p, u32Function = %d, pvParms = %p, cbParms = %d\n",
858 pvExtension, u32Function, pvParms, cbParms));
859#endif
860 ComObjPtr<Guest> pGuest = reinterpret_cast<Guest *>(pvExtension);
861
862 int rc = VINF_SUCCESS;
863 switch (u32Function)
864 {
865 case GUEST_DISCONNECTED:
866 {
867 //LogFlowFunc(("GUEST_DISCONNECTED\n"));
868
869 PCALLBACKDATACLIENTDISCONNECTED pCBData = reinterpret_cast<PCALLBACKDATACLIENTDISCONNECTED>(pvParms);
870 AssertPtr(pCBData);
871 AssertReturn(sizeof(CALLBACKDATACLIENTDISCONNECTED) == cbParms, VERR_INVALID_PARAMETER);
872 AssertReturn(CALLBACKDATAMAGICCLIENTDISCONNECTED == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
873
874 rc = pGuest->notifyCtrlClientDisconnected(u32Function, pCBData);
875 break;
876 }
877
878 case GUEST_EXEC_SEND_STATUS:
879 {
880 //LogFlowFunc(("GUEST_EXEC_SEND_STATUS\n"));
881
882 PCALLBACKDATAEXECSTATUS pCBData = reinterpret_cast<PCALLBACKDATAEXECSTATUS>(pvParms);
883 AssertPtr(pCBData);
884 AssertReturn(sizeof(CALLBACKDATAEXECSTATUS) == cbParms, VERR_INVALID_PARAMETER);
885 AssertReturn(CALLBACKDATAMAGICEXECSTATUS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
886
887 rc = pGuest->notifyCtrlExecStatus(u32Function, pCBData);
888 break;
889 }
890
891 case GUEST_EXEC_SEND_OUTPUT:
892 {
893 //LogFlowFunc(("GUEST_EXEC_SEND_OUTPUT\n"));
894
895 PCALLBACKDATAEXECOUT pCBData = reinterpret_cast<PCALLBACKDATAEXECOUT>(pvParms);
896 AssertPtr(pCBData);
897 AssertReturn(sizeof(CALLBACKDATAEXECOUT) == cbParms, VERR_INVALID_PARAMETER);
898 AssertReturn(CALLBACKDATAMAGICEXECOUT == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
899
900 rc = pGuest->notifyCtrlExecOut(u32Function, pCBData);
901 break;
902 }
903
904 case GUEST_EXEC_SEND_INPUT_STATUS:
905 {
906 //LogFlowFunc(("GUEST_EXEC_SEND_INPUT_STATUS\n"));
907
908 PCALLBACKDATAEXECINSTATUS pCBData = reinterpret_cast<PCALLBACKDATAEXECINSTATUS>(pvParms);
909 AssertPtr(pCBData);
910 AssertReturn(sizeof(CALLBACKDATAEXECINSTATUS) == cbParms, VERR_INVALID_PARAMETER);
911 AssertReturn(CALLBACKDATAMAGICEXECINSTATUS == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
912
913 rc = pGuest->notifyCtrlExecInStatus(u32Function, pCBData);
914 break;
915 }
916
917 default:
918 AssertMsgFailed(("Unknown guest control notification received, u32Function=%u\n", u32Function));
919 rc = VERR_INVALID_PARAMETER;
920 break;
921 }
922 return rc;
923}
924
925/* Function for handling the execution start/termination notification. */
926int Guest::notifyCtrlExecStatus(uint32_t u32Function,
927 PCALLBACKDATAEXECSTATUS pData)
928{
929 int vrc = VINF_SUCCESS;
930
931 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
932
933 AssertPtr(pData);
934 CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
935
936 /* Callback can be called several times. */
937 if (it != mCallbackMap.end())
938 {
939 PCALLBACKDATAEXECSTATUS pCBData = (PCALLBACKDATAEXECSTATUS)it->second.pvData;
940 AssertPtr(pCBData);
941
942 pCBData->u32PID = pData->u32PID;
943 pCBData->u32Status = pData->u32Status;
944 pCBData->u32Flags = pData->u32Flags;
945 /** @todo Copy void* buffer contents! */
946
947 Utf8Str errMsg;
948
949 /* Was progress canceled before? */
950 BOOL fCanceled;
951 ComAssert(!it->second.pProgress.isNull());
952 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled))
953 && !fCanceled)
954 {
955 /* Do progress handling. */
956 HRESULT hr;
957 switch (pData->u32Status)
958 {
959 case PROC_STS_STARTED:
960 LogRel(("Guest process (PID %u) started\n", pCBData->u32PID)); /** @todo Add process name */
961 hr = it->second.pProgress->SetNextOperation(Bstr(tr("Waiting for process to exit ...")).raw(), 1 /* Weight */);
962 AssertComRC(hr);
963 break;
964
965 case PROC_STS_TEN: /* Terminated normally. */
966 LogRel(("Guest process (PID %u) exited normally\n", pCBData->u32PID)); /** @todo Add process name */
967 if (!it->second.pProgress->getCompleted())
968 {
969 hr = it->second.pProgress->notifyComplete(S_OK);
970 AssertComRC(hr);
971
972 LogFlowFunc(("Process (CID=%u, status=%u) terminated successfully\n",
973 pData->hdr.u32ContextID, pData->u32Status));
974 }
975 break;
976
977 case PROC_STS_TEA: /* Terminated abnormally. */
978 LogRel(("Guest process (PID %u) terminated abnormally with exit code = %u\n",
979 pCBData->u32PID, pCBData->u32Flags)); /** @todo Add process name */
980 errMsg = Utf8StrFmt(Guest::tr("Process terminated abnormally with status '%u'"),
981 pCBData->u32Flags);
982 break;
983
984 case PROC_STS_TES: /* Terminated through signal. */
985 LogRel(("Guest process (PID %u) terminated through signal with exit code = %u\n",
986 pCBData->u32PID, pCBData->u32Flags)); /** @todo Add process name */
987 errMsg = Utf8StrFmt(Guest::tr("Process terminated via signal with status '%u'"),
988 pCBData->u32Flags);
989 break;
990
991 case PROC_STS_TOK:
992 LogRel(("Guest process (PID %u) timed out and was killed\n", pCBData->u32PID)); /** @todo Add process name */
993 errMsg = Utf8StrFmt(Guest::tr("Process timed out and was killed"));
994 break;
995
996 case PROC_STS_TOA:
997 LogRel(("Guest process (PID %u) timed out and could not be killed\n", pCBData->u32PID)); /** @todo Add process name */
998 errMsg = Utf8StrFmt(Guest::tr("Process timed out and could not be killed"));
999 break;
1000
1001 case PROC_STS_DWN:
1002 LogRel(("Guest process (PID %u) killed because system is shutting down\n", pCBData->u32PID)); /** @todo Add process name */
1003 /*
1004 * If u32Flags has ExecuteProcessFlag_IgnoreOrphanedProcesses set, we don't report an error to
1005 * our progress object. This is helpful for waiters which rely on the success of our progress object
1006 * even if the executed process was killed because the system/VBoxService is shutting down.
1007 *
1008 * In this case u32Flags contains the actual execution flags reached in via Guest::ExecuteProcess().
1009 */
1010 if (pData->u32Flags & ExecuteProcessFlag_IgnoreOrphanedProcesses)
1011 {
1012 if (!it->second.pProgress->getCompleted())
1013 {
1014 hr = it->second.pProgress->notifyComplete(S_OK);
1015 AssertComRC(hr);
1016 }
1017 }
1018 else
1019 {
1020 errMsg = Utf8StrFmt(Guest::tr("Process killed because system is shutting down"));
1021 }
1022 break;
1023
1024 case PROC_STS_ERROR:
1025 LogRel(("Guest process (PID %u) could not be started because of rc=%Rrc\n",
1026 pCBData->u32PID, pCBData->u32Flags)); /** @todo Add process name */
1027 errMsg = Utf8StrFmt(Guest::tr("Process execution failed with rc=%Rrc"), pCBData->u32Flags);
1028 break;
1029
1030 default:
1031 vrc = VERR_INVALID_PARAMETER;
1032 break;
1033 }
1034
1035 /* Handle process map. */
1036 /** @todo What happens on/deal with PID reuse? */
1037 /** @todo How to deal with multiple updates at once? */
1038 if (pCBData->u32PID > 0)
1039 {
1040 GuestProcessMapIter it_proc = getProcessByPID(pCBData->u32PID);
1041 if (it_proc == mGuestProcessMap.end())
1042 {
1043 /* Not found, add to map. */
1044 GuestProcess newProcess;
1045 newProcess.mStatus = pCBData->u32Status;
1046 newProcess.mExitCode = pCBData->u32Flags; /* Contains exit code. */
1047 newProcess.mFlags = 0;
1048
1049 mGuestProcessMap[pCBData->u32PID] = newProcess;
1050 }
1051 else /* Update map. */
1052 {
1053 it_proc->second.mStatus = pCBData->u32Status;
1054 it_proc->second.mExitCode = pCBData->u32Flags; /* Contains exit code. */
1055 it_proc->second.mFlags = 0;
1056 }
1057 }
1058 }
1059 else
1060 errMsg = Utf8StrFmt(Guest::tr("Process execution canceled"));
1061
1062 if (!it->second.pProgress->getCompleted())
1063 {
1064 if ( errMsg.length()
1065 || fCanceled) /* If canceled we have to report E_FAIL! */
1066 {
1067 /* Destroy all callbacks which are still waiting on something
1068 * which is related to the current PID. */
1069 CallbackMapIter it2;
1070 for (it2 = mCallbackMap.begin(); it2 != mCallbackMap.end(); it2++)
1071 {
1072 switch (it2->second.mType)
1073 {
1074 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_START:
1075 break;
1076
1077 /* When waiting for process output while the process is destroyed,
1078 * make sure we also destroy the actual waiting operation (internal progress object)
1079 * in order to not block the caller. */
1080 case VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT:
1081 {
1082 PCALLBACKDATAEXECOUT pItData = (CALLBACKDATAEXECOUT*)it2->second.pvData;
1083 AssertPtr(pItData);
1084 if (pItData->u32PID == pCBData->u32PID)
1085 destroyCtrlCallbackContext(it2);
1086 break;
1087 }
1088
1089 default:
1090 AssertMsgFailed(("Unknown callback type %d\n", it2->second.mType));
1091 break;
1092 }
1093 }
1094
1095 HRESULT hr2 = it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,
1096 COM_IIDOF(IGuest),
1097 Guest::getStaticComponentName(),
1098 "%s", errMsg.c_str());
1099 AssertComRC(hr2);
1100 LogFlowFunc(("Process (CID=%u, status=%u) reported error: %s\n",
1101 pData->hdr.u32ContextID, pData->u32Status, errMsg.c_str()));
1102 }
1103 }
1104 }
1105 else
1106 LogFlowFunc(("Unexpected callback (magic=%u, CID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));
1107 LogFlowFunc(("Returned with rc=%Rrc\n", vrc));
1108 return vrc;
1109}
1110
1111/* Function for handling the execution output notification. */
1112int Guest::notifyCtrlExecOut(uint32_t u32Function,
1113 PCALLBACKDATAEXECOUT pData)
1114{
1115 int rc = VINF_SUCCESS;
1116
1117 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1118
1119 AssertPtr(pData);
1120 CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
1121 if (it != mCallbackMap.end())
1122 {
1123 PCALLBACKDATAEXECOUT pCBData = (PCALLBACKDATAEXECOUT)it->second.pvData;
1124 AssertPtr(pCBData);
1125
1126 pCBData->u32PID = pData->u32PID;
1127 pCBData->u32HandleId = pData->u32HandleId;
1128 pCBData->u32Flags = pData->u32Flags;
1129
1130 /* Make sure we really got something! */
1131 if ( pData->cbData
1132 && pData->pvData)
1133 {
1134 /* Allocate data buffer and copy it */
1135 pCBData->pvData = RTMemAlloc(pData->cbData);
1136 pCBData->cbData = pData->cbData;
1137
1138 AssertReturn(pCBData->pvData, VERR_NO_MEMORY);
1139 memcpy(pCBData->pvData, pData->pvData, pData->cbData);
1140 }
1141 else
1142 {
1143 pCBData->pvData = NULL;
1144 pCBData->cbData = 0;
1145 }
1146
1147 /* Was progress canceled before? */
1148 BOOL fCanceled;
1149 ComAssert(!it->second.pProgress.isNull());
1150 if (SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled)) && fCanceled)
1151 {
1152 it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,
1153 COM_IIDOF(IGuest),
1154 Guest::getStaticComponentName(),
1155 Guest::tr("The output operation was canceled"));
1156 }
1157 else
1158 {
1159 BOOL fCompleted;
1160 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))
1161 && !fCompleted)
1162 {
1163 /* If we previously got completed notification, don't trigger again. */
1164 it->second.pProgress->notifyComplete(S_OK);
1165 }
1166 }
1167 }
1168 else
1169 LogFlowFunc(("Unexpected callback (magic=%u, CID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));
1170 return rc;
1171}
1172
1173/* Function for handling the execution input status notification. */
1174int Guest::notifyCtrlExecInStatus(uint32_t u32Function,
1175 PCALLBACKDATAEXECINSTATUS pData)
1176{
1177 int rc = VINF_SUCCESS;
1178
1179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1180
1181 AssertPtr(pData);
1182 CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
1183 if (it != mCallbackMap.end())
1184 {
1185 PCALLBACKDATAEXECINSTATUS pCBData = (PCALLBACKDATAEXECINSTATUS)it->second.pvData;
1186 AssertPtr(pCBData);
1187
1188 /* Save bytes processed. */
1189 pCBData->cbProcessed = pData->cbProcessed;
1190
1191 /* Was progress canceled before? */
1192 BOOL fCanceled;
1193 ComAssert(!it->second.pProgress.isNull());
1194 if (SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled)) && fCanceled)
1195 {
1196 it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,
1197 COM_IIDOF(IGuest),
1198 Guest::getStaticComponentName(),
1199 Guest::tr("The input operation was canceled"));
1200 }
1201 else
1202 {
1203 BOOL fCompleted;
1204 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))
1205 && !fCompleted)
1206 {
1207 /* If we previously got completed notification, don't trigger again. */
1208 it->second.pProgress->notifyComplete(S_OK);
1209 }
1210 }
1211 }
1212 else
1213 LogFlowFunc(("Unexpected callback (magic=%u, CID=%u) arrived\n", pData->hdr.u32Magic, pData->hdr.u32ContextID));
1214 return rc;
1215}
1216
1217int Guest::notifyCtrlClientDisconnected(uint32_t u32Function,
1218 PCALLBACKDATACLIENTDISCONNECTED pData)
1219{
1220 int rc = VINF_SUCCESS;
1221
1222 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1223 CallbackMapIter it = getCtrlCallbackContextByID(pData->hdr.u32ContextID);
1224 if (it != mCallbackMap.end())
1225 {
1226 LogFlowFunc(("Client with CID=%u disconnected\n", it->first));
1227 destroyCtrlCallbackContext(it);
1228 }
1229 return rc;
1230}
1231
1232Guest::CallbackMapIter Guest::getCtrlCallbackContextByID(uint32_t u32ContextID)
1233{
1234 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1235 return mCallbackMap.find(u32ContextID);
1236}
1237
1238Guest::GuestProcessMapIter Guest::getProcessByPID(uint32_t u32PID)
1239{
1240 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1241 return mGuestProcessMap.find(u32PID);
1242}
1243
1244/* No locking here; */
1245void Guest::destroyCtrlCallbackContext(Guest::CallbackMapIter it)
1246{
1247 LogFlowFunc(("Destroying callback with CID=%u ...\n", it->first));
1248
1249 if (it->second.pvData)
1250 {
1251 RTMemFree(it->second.pvData);
1252 it->second.pvData = NULL;
1253 it->second.cbData = 0;
1254 }
1255
1256 /* Notify outstanding waits for progress ... */
1257 if ( it->second.pProgress
1258 && !it->second.pProgress.isNull())
1259 {
1260 LogFlowFunc(("Handling progress for CID=%u ...\n", it->first));
1261
1262 /*
1263 * Assume we didn't complete to make sure we clean up even if the
1264 * following call fails.
1265 */
1266 BOOL fCompleted = FALSE;
1267 it->second.pProgress->COMGETTER(Completed)(&fCompleted);
1268 if (!fCompleted)
1269 {
1270 LogFlowFunc(("Progress of CID=%u *not* completed, cancelling ...\n", it->first));
1271
1272 /* Only cancel if not canceled before! */
1273 BOOL fCanceled;
1274 if (SUCCEEDED(it->second.pProgress->COMGETTER(Canceled)(&fCanceled)) && !fCanceled)
1275 it->second.pProgress->Cancel();
1276
1277 /*
1278 * To get waitForCompletion completed (unblocked) we have to notify it if necessary (only
1279 * cancel won't work!). This could happen if the client thread (e.g. VBoxService, thread of a spawned process)
1280 * is disconnecting without having the chance to sending a status message before, so we
1281 * have to abort here to make sure the host never hangs/gets stuck while waiting for the
1282 * progress object to become signalled.
1283 */
1284 it->second.pProgress->notifyComplete(VBOX_E_IPRT_ERROR,
1285 COM_IIDOF(IGuest),
1286 Guest::getStaticComponentName(),
1287 Guest::tr("The operation was canceled because client is shutting down"));
1288 }
1289 /*
1290 * Do *not* NULL pProgress here, because waiting function like executeProcess()
1291 * will still rely on this object for checking whether they have to give up!
1292 */
1293 }
1294}
1295
1296/* Adds a callback with a user provided data block and an optional progress object
1297 * to the callback map. A callback is identified by a unique context ID which is used
1298 * to identify a callback from the guest side. */
1299uint32_t Guest::addCtrlCallbackContext(eVBoxGuestCtrlCallbackType enmType, void *pvData, uint32_t cbData, Progress *pProgress)
1300{
1301 AssertPtr(pProgress);
1302
1303 /** @todo Put this stuff into a constructor! */
1304 CallbackContext context;
1305 context.mType = enmType;
1306 context.pvData = pvData;
1307 context.cbData = cbData;
1308 context.pProgress = pProgress;
1309
1310 /* Create a new context ID and assign it. */
1311 CallbackMapIter it;
1312 uint32_t uNewContext = 0;
1313 do
1314 {
1315 /* Create a new context ID ... */
1316 uNewContext = ASMAtomicIncU32(&mNextContextID);
1317 if (uNewContext == UINT32_MAX)
1318 ASMAtomicUoWriteU32(&mNextContextID, 1000);
1319 /* Is the context ID already used? */
1320 it = getCtrlCallbackContextByID(uNewContext);
1321 } while(it != mCallbackMap.end());
1322
1323 uint32_t nCallbacks = 0;
1324 if ( it == mCallbackMap.end()
1325 && uNewContext > 0)
1326 {
1327 /* We apparently got an unused context ID, let's use it! */
1328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1329 mCallbackMap[uNewContext] = context;
1330 nCallbacks = mCallbackMap.size();
1331 }
1332 else
1333 {
1334 /* Should never happen ... */
1335 {
1336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1337 nCallbacks = mCallbackMap.size();
1338 }
1339 AssertReleaseMsg(uNewContext, ("No free context ID found! uNewContext=%u, nCallbacks=%u", uNewContext, nCallbacks));
1340 }
1341
1342#if 0
1343 if (nCallbacks > 256) /* Don't let the container size get too big! */
1344 {
1345 Guest::CallbackListIter it = mCallbackList.begin();
1346 destroyCtrlCallbackContext(it);
1347 {
1348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1349 mCallbackList.erase(it);
1350 }
1351 }
1352#endif
1353 return uNewContext;
1354}
1355#endif /* VBOX_WITH_GUEST_CONTROL */
1356
1357STDMETHODIMP Guest::ExecuteProcess(IN_BSTR aCommand, ULONG aFlags,
1358 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
1359 IN_BSTR aUserName, IN_BSTR aPassword,
1360 ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress)
1361{
1362/** @todo r=bird: Eventually we should clean up all the timeout parameters
1363 * in the API and have the same way of specifying infinite waits! */
1364#ifndef VBOX_WITH_GUEST_CONTROL
1365 ReturnComNotImplemented();
1366#else /* VBOX_WITH_GUEST_CONTROL */
1367 using namespace guestControl;
1368
1369 CheckComArgStrNotEmptyOrNull(aCommand);
1370 CheckComArgOutPointerValid(aPID);
1371 CheckComArgOutPointerValid(aProgress);
1372
1373 /* Do not allow anonymous executions (with system rights). */
1374 if (RT_UNLIKELY((aUserName) == NULL || *(aUserName) == '\0'))
1375 return setError(E_INVALIDARG, tr("No user name specified"));
1376
1377 LogRel(("Executing guest process \"%s\" as user \"%s\" ...\n",
1378 Utf8Str(aCommand).c_str(), Utf8Str(aUserName).c_str()));
1379
1380 return executeProcessInternal(aCommand, aFlags, ComSafeArrayInArg(aArguments),
1381 ComSafeArrayInArg(aEnvironment),
1382 aUserName, aPassword, aTimeoutMS, aPID, aProgress, NULL /* rc */);
1383#endif
1384}
1385
1386HRESULT Guest::executeProcessInternal(IN_BSTR aCommand, ULONG aFlags,
1387 ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
1388 IN_BSTR aUserName, IN_BSTR aPassword,
1389 ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress, int *pRC)
1390{
1391/** @todo r=bird: Eventually we should clean up all the timeout parameters
1392 * in the API and have the same way of specifying infinite waits! */
1393#ifndef VBOX_WITH_GUEST_CONTROL
1394 ReturnComNotImplemented();
1395#else /* VBOX_WITH_GUEST_CONTROL */
1396 using namespace guestControl;
1397
1398 AutoCaller autoCaller(this);
1399 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1400
1401 /* Validate flags. */
1402 if (aFlags != ExecuteProcessFlag_None)
1403 {
1404 if ( !(aFlags & ExecuteProcessFlag_IgnoreOrphanedProcesses)
1405 && !(aFlags & ExecuteProcessFlag_WaitForProcessStartOnly)
1406 && !(aFlags & ExecuteProcessFlag_Hidden))
1407 {
1408 if (pRC)
1409 *pRC = VERR_INVALID_PARAMETER;
1410 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
1411 }
1412 }
1413
1414 HRESULT rc = S_OK;
1415
1416 try
1417 {
1418 /*
1419 * Create progress object. Note that this is a multi operation
1420 * object to perform the following steps:
1421 * - Operation 1 (0): Create/start process.
1422 * - Operation 2 (1): Wait for process to exit.
1423 * If this progress completed successfully (S_OK), the process
1424 * started and exited normally. In any other case an error/exception
1425 * occurred.
1426 */
1427 ComObjPtr <Progress> progress;
1428 rc = progress.createObject();
1429 if (SUCCEEDED(rc))
1430 {
1431 rc = progress->init(static_cast<IGuest*>(this),
1432 Bstr(tr("Executing process")).raw(),
1433 TRUE,
1434 2, /* Number of operations. */
1435 Bstr(tr("Starting process ...")).raw()); /* Description of first stage. */
1436 }
1437 ComAssertComRC(rc);
1438
1439 /*
1440 * Prepare process execution.
1441 */
1442 int vrc = VINF_SUCCESS;
1443 Utf8Str Utf8Command(aCommand);
1444
1445 /* Adjust timeout. If set to 0, we define
1446 * an infinite timeout. */
1447 if (aTimeoutMS == 0)
1448 aTimeoutMS = UINT32_MAX;
1449
1450 /* Prepare arguments. */
1451 char **papszArgv = NULL;
1452 uint32_t uNumArgs = 0;
1453 if (aArguments > 0)
1454 {
1455 com::SafeArray<IN_BSTR> args(ComSafeArrayInArg(aArguments));
1456 uNumArgs = args.size();
1457 papszArgv = (char**)RTMemAlloc(sizeof(char*) * (uNumArgs + 1));
1458 AssertReturn(papszArgv, E_OUTOFMEMORY);
1459 for (unsigned i = 0; RT_SUCCESS(vrc) && i < uNumArgs; i++)
1460 vrc = RTUtf16ToUtf8(args[i], &papszArgv[i]);
1461 papszArgv[uNumArgs] = NULL;
1462 }
1463
1464 Utf8Str Utf8UserName(aUserName);
1465 Utf8Str Utf8Password(aPassword);
1466 if (RT_SUCCESS(vrc))
1467 {
1468 uint32_t uContextID = 0;
1469
1470 char *pszArgs = NULL;
1471 if (uNumArgs > 0)
1472 vrc = RTGetOptArgvToString(&pszArgs, papszArgv, 0);
1473 if (RT_SUCCESS(vrc))
1474 {
1475 uint32_t cbArgs = pszArgs ? strlen(pszArgs) + 1 : 0; /* Include terminating zero. */
1476
1477 /* Prepare environment. */
1478 void *pvEnv = NULL;
1479 uint32_t uNumEnv = 0;
1480 uint32_t cbEnv = 0;
1481 if (aEnvironment > 0)
1482 {
1483 com::SafeArray<IN_BSTR> env(ComSafeArrayInArg(aEnvironment));
1484
1485 for (unsigned i = 0; i < env.size(); i++)
1486 {
1487 vrc = prepareExecuteEnv(Utf8Str(env[i]).c_str(), &pvEnv, &cbEnv, &uNumEnv);
1488 if (RT_FAILURE(vrc))
1489 break;
1490 }
1491 }
1492
1493 if (RT_SUCCESS(vrc))
1494 {
1495 PCALLBACKDATAEXECSTATUS pData = (PCALLBACKDATAEXECSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECSTATUS));
1496 AssertReturn(pData, VBOX_E_IPRT_ERROR);
1497 RT_ZERO(*pData);
1498 uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_START,
1499 pData, sizeof(CALLBACKDATAEXECSTATUS), progress);
1500 Assert(uContextID > 0);
1501
1502 VBOXHGCMSVCPARM paParms[15];
1503 int i = 0;
1504 paParms[i++].setUInt32(uContextID);
1505 paParms[i++].setPointer((void*)Utf8Command.c_str(), (uint32_t)Utf8Command.length() + 1);
1506 paParms[i++].setUInt32(aFlags);
1507 paParms[i++].setUInt32(uNumArgs);
1508 paParms[i++].setPointer((void*)pszArgs, cbArgs);
1509 paParms[i++].setUInt32(uNumEnv);
1510 paParms[i++].setUInt32(cbEnv);
1511 paParms[i++].setPointer((void*)pvEnv, cbEnv);
1512 paParms[i++].setPointer((void*)Utf8UserName.c_str(), (uint32_t)Utf8UserName.length() + 1);
1513 paParms[i++].setPointer((void*)Utf8Password.c_str(), (uint32_t)Utf8Password.length() + 1);
1514
1515 /*
1516 * If the WaitForProcessStartOnly flag is set, we only want to define and wait for a timeout
1517 * until the process was started - the process itself then gets an infinite timeout for execution.
1518 * This is handy when we want to start a process inside a worker thread within a certain timeout
1519 * but let the started process perform lengthly operations then.
1520 */
1521 if (aFlags & ExecuteProcessFlag_WaitForProcessStartOnly)
1522 paParms[i++].setUInt32(UINT32_MAX /* Infinite timeout */);
1523 else
1524 paParms[i++].setUInt32(aTimeoutMS);
1525
1526 VMMDev *vmmDev;
1527 {
1528 /* Make sure mParent is valid, so set the read lock while using.
1529 * Do not keep this lock while doing the actual call, because in the meanwhile
1530 * another thread could request a write lock which would be a bad idea ... */
1531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1532
1533 /* Forward the information to the VMM device. */
1534 AssertPtr(mParent);
1535 vmmDev = mParent->getVMMDev();
1536 }
1537
1538 if (vmmDev)
1539 {
1540 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
1541 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_CMD,
1542 i, paParms);
1543 }
1544 else
1545 vrc = VERR_INVALID_VM_HANDLE;
1546 RTMemFree(pvEnv);
1547 }
1548 RTStrFree(pszArgs);
1549 }
1550 if (RT_SUCCESS(vrc))
1551 {
1552 LogFlowFunc(("Waiting for HGCM callback (timeout=%ldms) ...\n", aTimeoutMS));
1553
1554 /*
1555 * Wait for the HGCM low level callback until the process
1556 * has been started (or something went wrong). This is necessary to
1557 * get the PID.
1558 */
1559 CallbackMapIter it = getCtrlCallbackContextByID(uContextID);
1560 BOOL fCanceled = FALSE;
1561 if (it != mCallbackMap.end())
1562 {
1563 ComAssert(!it->second.pProgress.isNull());
1564
1565 /*
1566 * Wait for the first stage (=0) to complete (that is starting the process).
1567 */
1568 PCALLBACKDATAEXECSTATUS pData = NULL;
1569 rc = it->second.pProgress->WaitForOperationCompletion(0, aTimeoutMS);
1570 if (SUCCEEDED(rc))
1571 {
1572 /* Was the operation canceled by one of the parties? */
1573 rc = it->second.pProgress->COMGETTER(Canceled)(&fCanceled);
1574 if (FAILED(rc)) throw rc;
1575
1576 if (!fCanceled)
1577 {
1578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1579
1580 pData = (PCALLBACKDATAEXECSTATUS)it->second.pvData;
1581 Assert(it->second.cbData == sizeof(CALLBACKDATAEXECSTATUS));
1582 AssertPtr(pData);
1583
1584 /* Did we get some status? */
1585 switch (pData->u32Status)
1586 {
1587 case PROC_STS_STARTED:
1588 /* Process is (still) running; get PID. */
1589 *aPID = pData->u32PID;
1590 break;
1591
1592 /* In any other case the process either already
1593 * terminated or something else went wrong, so no PID ... */
1594 case PROC_STS_TEN: /* Terminated normally. */
1595 case PROC_STS_TEA: /* Terminated abnormally. */
1596 case PROC_STS_TES: /* Terminated through signal. */
1597 case PROC_STS_TOK:
1598 case PROC_STS_TOA:
1599 case PROC_STS_DWN:
1600 /*
1601 * Process (already) ended, but we want to get the
1602 * PID anyway to retrieve the output in a later call.
1603 */
1604 *aPID = pData->u32PID;
1605 break;
1606
1607 case PROC_STS_ERROR:
1608 vrc = pData->u32Flags; /* u32Flags member contains IPRT error code. */
1609 break;
1610
1611 case PROC_STS_UNDEFINED:
1612 vrc = VERR_TIMEOUT; /* Operation did not complete within time. */
1613 break;
1614
1615 default:
1616 vrc = VERR_INVALID_PARAMETER; /* Unknown status, should never happen! */
1617 break;
1618 }
1619 }
1620 else /* Operation was canceled. */
1621 vrc = VERR_CANCELLED;
1622 }
1623 else /* Operation did not complete within time. */
1624 vrc = VERR_TIMEOUT;
1625
1626 /*
1627 * Do *not* remove the callback yet - we might wait with the IProgress object on something
1628 * else (like end of process) ...
1629 */
1630 if (RT_FAILURE(vrc))
1631 {
1632 if (vrc == VERR_FILE_NOT_FOUND) /* This is the most likely error. */
1633 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1634 tr("The file '%s' was not found on guest"), Utf8Command.c_str());
1635 else if (vrc == VERR_PATH_NOT_FOUND)
1636 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1637 tr("The path to file '%s' was not found on guest"), Utf8Command.c_str());
1638 else if (vrc == VERR_BAD_EXE_FORMAT)
1639 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1640 tr("The file '%s' is not an executable format on guest"), Utf8Command.c_str());
1641 else if (vrc == VERR_AUTHENTICATION_FAILURE)
1642 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1643 tr("The specified user '%s' was not able to logon on guest"), Utf8UserName.c_str());
1644 else if (vrc == VERR_TIMEOUT)
1645 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1646 tr("The guest did not respond within time (%ums)"), aTimeoutMS);
1647 else if (vrc == VERR_CANCELLED)
1648 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1649 tr("The execution operation was canceled"));
1650 else if (vrc == VERR_PERMISSION_DENIED)
1651 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1652 tr("Invalid user/password credentials"));
1653 else
1654 {
1655 if (pData && pData->u32Status == PROC_STS_ERROR)
1656 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1657 tr("Process could not be started: %Rrc"), pData->u32Flags);
1658 else
1659 rc = setErrorNoLog(E_UNEXPECTED,
1660 tr("The service call failed with error %Rrc"), vrc);
1661 }
1662 }
1663 else /* Execution went fine. */
1664 {
1665 /* Return the progress to the caller. */
1666 progress.queryInterfaceTo(aProgress);
1667 }
1668 }
1669 else /* Callback context not found; should never happen! */
1670 AssertMsg(it != mCallbackMap.end(), ("Callback context with ID %u not found!", uContextID));
1671 }
1672 else /* HGCM related error codes .*/
1673 {
1674 if (vrc == VERR_INVALID_VM_HANDLE)
1675 rc = setErrorNoLog(VBOX_E_VM_ERROR,
1676 tr("VMM device is not available (is the VM running?)"));
1677 else if (vrc == VERR_NOT_FOUND)
1678 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1679 tr("The guest execution service is not ready"));
1680 else if (vrc == VERR_HGCM_SERVICE_NOT_FOUND)
1681 rc = setErrorNoLog(VBOX_E_IPRT_ERROR,
1682 tr("The guest execution service is not available"));
1683 else /* HGCM call went wrong. */
1684 rc = setErrorNoLog(E_UNEXPECTED,
1685 tr("The HGCM call failed with error %Rrc"), vrc);
1686 }
1687
1688 for (unsigned i = 0; i < uNumArgs; i++)
1689 RTMemFree(papszArgv[i]);
1690 RTMemFree(papszArgv);
1691 }
1692
1693 if (RT_FAILURE(vrc))
1694 {
1695 if (!pRC) /* Skip logging internal calls. */
1696 LogRel(("Executing guest process \"%s\" as user \"%s\" failed with %Rrc\n",
1697 Utf8Command.c_str(), Utf8UserName.c_str(), vrc));
1698 }
1699
1700 if (pRC)
1701 *pRC = vrc;
1702 }
1703 catch (std::bad_alloc &)
1704 {
1705 rc = E_OUTOFMEMORY;
1706 }
1707 return rc;
1708#endif /* VBOX_WITH_GUEST_CONTROL */
1709}
1710
1711STDMETHODIMP Guest::SetProcessInput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, ComSafeArrayIn(BYTE, aData), ULONG *aBytesWritten)
1712{
1713#ifndef VBOX_WITH_GUEST_CONTROL
1714 ReturnComNotImplemented();
1715#else /* VBOX_WITH_GUEST_CONTROL */
1716 using namespace guestControl;
1717
1718 CheckComArgExpr(aPID, aPID > 0);
1719 CheckComArgOutPointerValid(aBytesWritten);
1720
1721 /* Validate flags. */
1722 if (aFlags)
1723 {
1724 if (!(aFlags & ProcessInputFlag_EndOfFile))
1725 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
1726 }
1727
1728 AutoCaller autoCaller(this);
1729 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1730
1731 HRESULT rc = S_OK;
1732
1733 try
1734 {
1735 /* Init. */
1736 *aBytesWritten = 0;
1737
1738 /* Search for existing PID. */
1739 GuestProcessMapIterConst itProc = getProcessByPID(aPID);
1740 if (itProc != mGuestProcessMap.end())
1741 {
1742 /* PID exists; check if process is still running. */
1743 if (itProc->second.mStatus != PROC_STS_STARTED)
1744 {
1745 rc = setError(VBOX_E_IPRT_ERROR,
1746 tr("Process (PID %u) does not run anymore! Status: %ld, Flags: %u, Exit Code: %u"),
1747 aPID, itProc->second.mStatus, itProc->second.mFlags, itProc->second.mExitCode);
1748 }
1749 }
1750 else
1751 rc = setError(VBOX_E_IPRT_ERROR,
1752 tr("Process (PID %u) not found!"), aPID);
1753
1754 if (SUCCEEDED(rc))
1755 {
1756 /*
1757 * Create progress object.
1758 * This progress object, compared to the one in executeProgress() above
1759 * is only local and is used to determine whether the operation finished
1760 * or got canceled.
1761 */
1762 ComObjPtr <Progress> progress;
1763 rc = progress.createObject();
1764 if (SUCCEEDED(rc))
1765 {
1766 rc = progress->init(static_cast<IGuest*>(this),
1767 Bstr(tr("Setting input for process")).raw(),
1768 TRUE /* Cancelable */);
1769 }
1770 if (FAILED(rc)) return rc;
1771
1772 /* Adjust timeout. */
1773 if (aTimeoutMS == 0)
1774 aTimeoutMS = UINT32_MAX;
1775
1776 PCALLBACKDATAEXECINSTATUS pData = (PCALLBACKDATAEXECINSTATUS)RTMemAlloc(sizeof(CALLBACKDATAEXECINSTATUS));
1777 AssertReturn(pData, VBOX_E_IPRT_ERROR);
1778 RT_ZERO(*pData);
1779 /* Save PID + output flags for later use. */
1780 pData->u32PID = aPID;
1781 pData->u32Flags = aFlags;
1782 /* Add job to callback contexts. */
1783 uint32_t uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_INPUT_STATUS,
1784 pData, sizeof(CALLBACKDATAEXECINSTATUS), progress);
1785 Assert(uContextID > 0);
1786
1787 com::SafeArray<BYTE> sfaData(ComSafeArrayInArg(aData));
1788 uint32_t cbSize = sfaData.size();
1789
1790 VBOXHGCMSVCPARM paParms[6];
1791 int i = 0;
1792 paParms[i++].setUInt32(uContextID);
1793 paParms[i++].setUInt32(aPID);
1794 paParms[i++].setUInt32(aFlags);
1795 paParms[i++].setPointer(sfaData.raw(), cbSize);
1796 paParms[i++].setUInt32(cbSize);
1797
1798 int vrc = VINF_SUCCESS;
1799
1800 {
1801 VMMDev *vmmDev;
1802 {
1803 /* Make sure mParent is valid, so set the read lock while using.
1804 * Do not keep this lock while doing the actual call, because in the meanwhile
1805 * another thread could request a write lock which would be a bad idea ... */
1806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1807
1808 /* Forward the information to the VMM device. */
1809 AssertPtr(mParent);
1810 vmmDev = mParent->getVMMDev();
1811 }
1812
1813 if (vmmDev)
1814 {
1815 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
1816 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_SET_INPUT,
1817 i, paParms);
1818 }
1819 }
1820
1821 if (RT_SUCCESS(vrc))
1822 {
1823 LogFlowFunc(("Waiting for HGCM callback ...\n"));
1824
1825 /*
1826 * Wait for the HGCM low level callback until the process
1827 * has been started (or something went wrong). This is necessary to
1828 * get the PID.
1829 */
1830 CallbackMapIter it = getCtrlCallbackContextByID(uContextID);
1831 BOOL fCanceled = FALSE;
1832 if (it != mCallbackMap.end())
1833 {
1834 ComAssert(!it->second.pProgress.isNull());
1835
1836 /* Wait until operation completed. */
1837 rc = it->second.pProgress->WaitForCompletion(aTimeoutMS);
1838 if (FAILED(rc)) throw rc;
1839
1840 /* Was the operation canceled by one of the parties? */
1841 rc = it->second.pProgress->COMGETTER(Canceled)(&fCanceled);
1842 if (FAILED(rc)) throw rc;
1843
1844 if (!fCanceled)
1845 {
1846 BOOL fCompleted;
1847 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))
1848 && fCompleted)
1849 {
1850 PCALLBACKDATAEXECINSTATUS pStatusData = (PCALLBACKDATAEXECINSTATUS)it->second.pvData;
1851 Assert(it->second.cbData == sizeof(CALLBACKDATAEXECINSTATUS));
1852 AssertPtr(pStatusData);
1853
1854 *aBytesWritten = pStatusData->cbProcessed;
1855 }
1856 }
1857 else /* Operation was canceled. */
1858 vrc = VERR_CANCELLED;
1859
1860 if (RT_FAILURE(vrc))
1861 {
1862 if (vrc == VERR_CANCELLED)
1863 {
1864 rc = setError(VBOX_E_IPRT_ERROR,
1865 tr("The input operation was canceled"));
1866 }
1867 else
1868 {
1869 rc = setError(E_UNEXPECTED,
1870 tr("The service call failed with error %Rrc"), vrc);
1871 }
1872 }
1873
1874 {
1875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1876 /*
1877 * Destroy locally used progress object.
1878 */
1879 destroyCtrlCallbackContext(it);
1880 }
1881
1882 /* Remove callback context (not used anymore). */
1883 mCallbackMap.erase(it);
1884 }
1885 else /* PID lookup failed. */
1886 rc = setError(VBOX_E_IPRT_ERROR,
1887 tr("Process (PID %u) not found!"), aPID);
1888 }
1889 else /* HGCM operation failed. */
1890 rc = setError(E_UNEXPECTED,
1891 tr("The HGCM call failed with error %Rrc"), vrc);
1892
1893 /* Cleanup. */
1894 progress->uninit();
1895 progress.setNull();
1896 }
1897 }
1898 catch (std::bad_alloc &)
1899 {
1900 rc = E_OUTOFMEMORY;
1901 }
1902 return rc;
1903#endif
1904}
1905
1906STDMETHODIMP Guest::GetProcessOutput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
1907{
1908/** @todo r=bird: Eventually we should clean up all the timeout parameters
1909 * in the API and have the same way of specifying infinite waits! */
1910#ifndef VBOX_WITH_GUEST_CONTROL
1911 ReturnComNotImplemented();
1912#else /* VBOX_WITH_GUEST_CONTROL */
1913 using namespace guestControl;
1914
1915 CheckComArgExpr(aPID, aPID > 0);
1916 if (aSize < 0)
1917 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
1918 if (aFlags != 0) /* Flags are not supported at the moment. */
1919 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
1920
1921 AutoCaller autoCaller(this);
1922 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1923
1924 HRESULT rc = S_OK;
1925
1926 try
1927 {
1928 /*
1929 * Create progress object.
1930 * This progress object, compared to the one in executeProgress() above
1931 * is only local and is used to determine whether the operation finished
1932 * or got canceled.
1933 */
1934 ComObjPtr <Progress> progress;
1935 rc = progress.createObject();
1936 if (SUCCEEDED(rc))
1937 {
1938 rc = progress->init(static_cast<IGuest*>(this),
1939 Bstr(tr("Getting output of process")).raw(),
1940 TRUE);
1941 }
1942 if (FAILED(rc)) return rc;
1943
1944 /* Adjust timeout. */
1945 if (aTimeoutMS == 0)
1946 aTimeoutMS = UINT32_MAX;
1947
1948 /* Search for existing PID. */
1949 PCALLBACKDATAEXECOUT pData = (CALLBACKDATAEXECOUT*)RTMemAlloc(sizeof(CALLBACKDATAEXECOUT));
1950 AssertReturn(pData, VBOX_E_IPRT_ERROR);
1951 RT_ZERO(*pData);
1952 /* Save PID + output flags for later use. */
1953 pData->u32PID = aPID;
1954 pData->u32Flags = aFlags;
1955 /* Add job to callback contexts. */
1956 uint32_t uContextID = addCtrlCallbackContext(VBOXGUESTCTRLCALLBACKTYPE_EXEC_OUTPUT,
1957 pData, sizeof(CALLBACKDATAEXECOUT), progress);
1958 Assert(uContextID > 0);
1959
1960 com::SafeArray<BYTE> outputData((size_t)aSize);
1961
1962 VBOXHGCMSVCPARM paParms[5];
1963 int i = 0;
1964 paParms[i++].setUInt32(uContextID);
1965 paParms[i++].setUInt32(aPID);
1966 paParms[i++].setUInt32(aFlags); /** @todo Should represent stdout and/or stderr. */
1967
1968 int vrc = VINF_SUCCESS;
1969
1970 {
1971 VMMDev *vmmDev;
1972 {
1973 /* Make sure mParent is valid, so set the read lock while using.
1974 * Do not keep this lock while doing the actual call, because in the meanwhile
1975 * another thread could request a write lock which would be a bad idea ... */
1976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1977
1978 /* Forward the information to the VMM device. */
1979 AssertPtr(mParent);
1980 vmmDev = mParent->getVMMDev();
1981 }
1982
1983 if (vmmDev)
1984 {
1985 LogFlowFunc(("hgcmHostCall numParms=%d\n", i));
1986 vrc = vmmDev->hgcmHostCall("VBoxGuestControlSvc", HOST_EXEC_GET_OUTPUT,
1987 i, paParms);
1988 }
1989 }
1990
1991 if (RT_SUCCESS(vrc))
1992 {
1993 LogFlowFunc(("Waiting for HGCM callback (timeout=%ldms) ...\n", aTimeoutMS));
1994
1995 /*
1996 * Wait for the HGCM low level callback until the process
1997 * has been started (or something went wrong). This is necessary to
1998 * get the PID.
1999 */
2000 CallbackMapIter it = getCtrlCallbackContextByID(uContextID);
2001 BOOL fCanceled = FALSE;
2002 if (it != mCallbackMap.end())
2003 {
2004 ComAssert(!it->second.pProgress.isNull());
2005
2006 /* Wait until operation completed. */
2007 rc = it->second.pProgress->WaitForCompletion(aTimeoutMS);
2008 if (FAILED(rc)) throw rc;
2009
2010 /* Was the operation canceled by one of the parties? */
2011 rc = it->second.pProgress->COMGETTER(Canceled)(&fCanceled);
2012 if (FAILED(rc)) throw rc;
2013
2014 if (!fCanceled)
2015 {
2016 BOOL fCompleted;
2017 if ( SUCCEEDED(it->second.pProgress->COMGETTER(Completed)(&fCompleted))
2018 && fCompleted)
2019 {
2020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2021
2022 /* Did we get some output? */
2023 pData = (PCALLBACKDATAEXECOUT)it->second.pvData;
2024 Assert(it->second.cbData == sizeof(CALLBACKDATAEXECOUT));
2025 AssertPtr(pData);
2026
2027 if (pData->cbData)
2028 {
2029 /* Do we need to resize the array? */
2030 if (pData->cbData > aSize)
2031 outputData.resize(pData->cbData);
2032
2033 /* Fill output in supplied out buffer. */
2034 memcpy(outputData.raw(), pData->pvData, pData->cbData);
2035 outputData.resize(pData->cbData); /* Shrink to fit actual buffer size. */
2036 }
2037 else
2038 vrc = VERR_NO_DATA; /* This is not an error we want to report to COM. */
2039 }
2040 else /* If callback not called within time ... well, that's a timeout! */
2041 vrc = VERR_TIMEOUT;
2042 }
2043 else /* Operation was canceled. */
2044 {
2045 vrc = VERR_CANCELLED;
2046 }
2047
2048 if (RT_FAILURE(vrc))
2049 {
2050 if (vrc == VERR_NO_DATA)
2051 {
2052 /* This is not an error we want to report to COM. */
2053 rc = S_OK;
2054 }
2055 else if (vrc == VERR_TIMEOUT)
2056 {
2057 rc = setError(VBOX_E_IPRT_ERROR,
2058 tr("The guest did not output within time (%ums)"), aTimeoutMS);
2059 }
2060 else if (vrc == VERR_CANCELLED)
2061 {
2062 rc = setError(VBOX_E_IPRT_ERROR,
2063 tr("The output operation was canceled"));
2064 }
2065 else
2066 {
2067 rc = setError(E_UNEXPECTED,
2068 tr("The service call failed with error %Rrc"), vrc);
2069 }
2070 }
2071
2072 {
2073 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2074 /*
2075 * Destroy locally used progress object.
2076 */
2077 destroyCtrlCallbackContext(it);
2078 }
2079
2080 /* Remove callback context (not used anymore). */
2081 mCallbackMap.erase(it);
2082 }
2083 else /* PID lookup failed. */
2084 rc = setError(VBOX_E_IPRT_ERROR,
2085 tr("Process (PID %u) not found!"), aPID);
2086 }
2087 else /* HGCM operation failed. */
2088 rc = setError(E_UNEXPECTED,
2089 tr("The HGCM call failed with error %Rrc"), vrc);
2090
2091 /* Cleanup. */
2092 progress->uninit();
2093 progress.setNull();
2094
2095 /* If something failed (or there simply was no data, indicated by VERR_NO_DATA,
2096 * we return an empty array so that the frontend knows when to give up. */
2097 if (RT_FAILURE(vrc) || FAILED(rc))
2098 outputData.resize(0);
2099 outputData.detachTo(ComSafeArrayOutArg(aData));
2100 }
2101 catch (std::bad_alloc &)
2102 {
2103 rc = E_OUTOFMEMORY;
2104 }
2105 return rc;
2106#endif
2107}
2108
2109STDMETHODIMP Guest::GetProcessStatus(ULONG aPID, ULONG *aExitCode, ULONG *aFlags, ULONG *aStatus)
2110{
2111#ifndef VBOX_WITH_GUEST_CONTROL
2112 ReturnComNotImplemented();
2113#else /* VBOX_WITH_GUEST_CONTROL */
2114 using namespace guestControl;
2115
2116 AutoCaller autoCaller(this);
2117 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2118
2119 HRESULT rc = S_OK;
2120
2121 try
2122 {
2123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2124
2125 GuestProcessMapIterConst it = getProcessByPID(aPID);
2126 if (it != mGuestProcessMap.end())
2127 {
2128 *aExitCode = it->second.mExitCode;
2129 *aFlags = it->second.mFlags;
2130 *aStatus = it->second.mStatus;
2131 }
2132 else
2133 rc = setError(VBOX_E_IPRT_ERROR,
2134 tr("Process (PID %u) not found!"), aPID);
2135 }
2136 catch (std::bad_alloc &)
2137 {
2138 rc = E_OUTOFMEMORY;
2139 }
2140 return rc;
2141#endif
2142}
2143
2144STDMETHODIMP Guest::CopyToGuest(IN_BSTR aSource, IN_BSTR aDest,
2145 IN_BSTR aUserName, IN_BSTR aPassword,
2146 ULONG aFlags, IProgress **aProgress)
2147{
2148#ifndef VBOX_WITH_GUEST_CONTROL
2149 ReturnComNotImplemented();
2150#else /* VBOX_WITH_GUEST_CONTROL */
2151 CheckComArgStrNotEmptyOrNull(aSource);
2152 CheckComArgStrNotEmptyOrNull(aDest);
2153 CheckComArgStrNotEmptyOrNull(aUserName);
2154 CheckComArgStrNotEmptyOrNull(aPassword);
2155 CheckComArgOutPointerValid(aProgress);
2156
2157 AutoCaller autoCaller(this);
2158 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2159
2160 /* Validate flags. */
2161 if (aFlags != CopyFileFlag_None)
2162 {
2163 if ( !(aFlags & CopyFileFlag_Recursive)
2164 && !(aFlags & CopyFileFlag_Update)
2165 && !(aFlags & CopyFileFlag_FollowLinks))
2166 {
2167 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
2168 }
2169 }
2170
2171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2172
2173 HRESULT rc = S_OK;
2174
2175 ComObjPtr<Progress> progress;
2176 try
2177 {
2178 /* Create the progress object. */
2179 progress.createObject();
2180
2181 rc = progress->init(static_cast<IGuest*>(this),
2182 Bstr(tr("Copying file")).raw(),
2183 TRUE /* aCancelable */);
2184 if (FAILED(rc)) throw rc;
2185
2186 /* Initialize our worker task. */
2187 TaskGuest *pTask = new TaskGuest(TaskGuest::CopyFile, this, progress);
2188 AssertPtr(pTask);
2189 std::auto_ptr<TaskGuest> task(pTask);
2190
2191 /* Assign data - aSource is the source file on the host,
2192 * aDest reflects the full path on the guest. */
2193 task->strSource = (Utf8Str(aSource));
2194 task->strDest = (Utf8Str(aDest));
2195 task->strUserName = (Utf8Str(aUserName));
2196 task->strPassword = (Utf8Str(aPassword));
2197 task->uFlags = aFlags;
2198
2199 rc = task->startThread();
2200 if (FAILED(rc)) throw rc;
2201
2202 /* Don't destruct on success. */
2203 task.release();
2204 }
2205 catch (HRESULT aRC)
2206 {
2207 rc = aRC;
2208 }
2209
2210 if (SUCCEEDED(rc))
2211 {
2212 /* Return progress to the caller. */
2213 progress.queryInterfaceTo(aProgress);
2214 }
2215 return rc;
2216#endif /* VBOX_WITH_GUEST_CONTROL */
2217}
2218
2219STDMETHODIMP Guest::CreateDirectory(IN_BSTR aDirectory,
2220 IN_BSTR aUserName, IN_BSTR aPassword,
2221 ULONG aMode, ULONG aFlags,
2222 IProgress **aProgress)
2223{
2224#ifndef VBOX_WITH_GUEST_CONTROL
2225 ReturnComNotImplemented();
2226#else /* VBOX_WITH_GUEST_CONTROL */
2227 using namespace guestControl;
2228
2229 CheckComArgStrNotEmptyOrNull(aDirectory);
2230
2231 /* Do not allow anonymous executions (with system rights). */
2232 if (RT_UNLIKELY((aUserName) == NULL || *(aUserName) == '\0'))
2233 return setError(E_INVALIDARG, tr("No user name specified"));
2234
2235 LogRel(("Creating guest directory \"%s\" as user \"%s\" ...\n",
2236 Utf8Str(aDirectory).c_str(), Utf8Str(aUserName).c_str()));
2237
2238 return createDirectoryInternal(aDirectory,
2239 aUserName, aPassword,
2240 aMode, aFlags, aProgress, NULL /* rc */);
2241#endif
2242}
2243
2244HRESULT Guest::createDirectoryInternal(IN_BSTR aDirectory,
2245 IN_BSTR aUserName, IN_BSTR aPassword,
2246 ULONG aMode, ULONG aFlags,
2247 IProgress **aProgress, int *pRC)
2248{
2249#ifndef VBOX_WITH_GUEST_CONTROL
2250 ReturnComNotImplemented();
2251#else /* VBOX_WITH_GUEST_CONTROL */
2252 using namespace guestControl;
2253
2254 CheckComArgStrNotEmptyOrNull(aDirectory);
2255 CheckComArgOutPointerValid(aProgress);
2256
2257 AutoCaller autoCaller(this);
2258 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2259
2260 /* Validate flags. */
2261 if (aFlags != CreateDirectoryFlag_None)
2262 {
2263 if (!(aFlags & CreateDirectoryFlag_Parents))
2264 {
2265 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
2266 }
2267 }
2268
2269 /**
2270 * @todo We return a progress object because we maybe later want to
2271 * process more than one directory (or somewhat lengthly operations)
2272 * that require having a progress object provided to the caller.
2273 */
2274
2275 HRESULT rc = S_OK;
2276 try
2277 {
2278 Utf8Str Utf8Directory(aDirectory);
2279 Utf8Str Utf8UserName(aUserName);
2280 Utf8Str Utf8Password(aPassword);
2281
2282 com::SafeArray<IN_BSTR> args;
2283 com::SafeArray<IN_BSTR> env;
2284
2285 /*
2286 * Prepare tool command line.
2287 */
2288 args.push_back(Bstr(VBOXSERVICE_TOOL_MKDIR).raw()); /* The actual (internal) tool to use (as argv[0]). */
2289 if (aFlags & CreateDirectoryFlag_Parents)
2290 args.push_back(Bstr("--parents").raw()); /* We also want to create the parent directories. */
2291 if (aMode > 0)
2292 {
2293 args.push_back(Bstr("--mode").raw()); /* Set the creation mode. */
2294
2295 char szMode[16];
2296 RTStrPrintf(szMode, sizeof(szMode), "%o", aMode);
2297 args.push_back(Bstr(szMode).raw());
2298 }
2299 args.push_back(Bstr(Utf8Directory).raw()); /* The directory we want to create. */
2300
2301 /*
2302 * Execute guest process.
2303 */
2304 ComPtr<IProgress> progressExec;
2305 ULONG uPID;
2306 if (SUCCEEDED(rc))
2307 {
2308 rc = ExecuteProcess(Bstr(VBOXSERVICE_TOOL_MKDIR).raw(),
2309 ExecuteProcessFlag_Hidden,
2310 ComSafeArrayAsInParam(args),
2311 ComSafeArrayAsInParam(env),
2312 Bstr(Utf8UserName).raw(),
2313 Bstr(Utf8Password).raw(),
2314 5 * 1000 /* Wait 5s for getting the process started. */,
2315 &uPID, progressExec.asOutParam());
2316 }
2317
2318 if (SUCCEEDED(rc))
2319 {
2320 /* Wait for process to exit ... */
2321 BOOL fCompleted = FALSE;
2322 BOOL fCanceled = FALSE;
2323
2324 while ( SUCCEEDED(progressExec->COMGETTER(Completed(&fCompleted)))
2325 && !fCompleted)
2326 {
2327 /* Progress canceled by Main API? */
2328 if ( SUCCEEDED(progressExec->COMGETTER(Canceled(&fCanceled)))
2329 && fCanceled)
2330 {
2331 break;
2332 }
2333 }
2334
2335 ComObjPtr<Progress> progressCreate;
2336 rc = progressCreate.createObject();
2337 if (SUCCEEDED(rc))
2338 {
2339 rc = progressCreate->init(static_cast<IGuest*>(this),
2340 Bstr(tr("Creating directory")).raw(),
2341 TRUE);
2342 }
2343 if (FAILED(rc)) return rc;
2344
2345 if (fCompleted)
2346 {
2347 ULONG uRetStatus, uRetExitCode, uRetFlags;
2348 if (SUCCEEDED(rc))
2349 {
2350 rc = GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &uRetStatus);
2351 if (SUCCEEDED(rc) && uRetExitCode != 0)
2352 {
2353 rc = setError(VBOX_E_IPRT_ERROR,
2354 tr("Error while creating directory"));
2355 }
2356 }
2357 }
2358 else if (fCanceled)
2359 rc = setError(VBOX_E_IPRT_ERROR,
2360 tr("Directory creation was aborted"));
2361 else
2362 AssertReleaseMsgFailed(("Directory creation neither completed nor canceled!?"));
2363
2364 if (SUCCEEDED(rc))
2365 progressCreate->notifyComplete(S_OK);
2366 else
2367 progressCreate->notifyComplete(VBOX_E_IPRT_ERROR,
2368 COM_IIDOF(IGuest),
2369 Guest::getStaticComponentName(),
2370 Guest::tr("Error while executing creation command"));
2371
2372 /* Return the progress to the caller. */
2373 progressCreate.queryInterfaceTo(aProgress);
2374 }
2375 }
2376 catch (std::bad_alloc &)
2377 {
2378 rc = E_OUTOFMEMORY;
2379 }
2380 return rc;
2381#endif /* VBOX_WITH_GUEST_CONTROL */
2382}
2383
2384STDMETHODIMP Guest::UpdateGuestAdditions(IN_BSTR aSource, ULONG aFlags, IProgress **aProgress)
2385{
2386#ifndef VBOX_WITH_GUEST_CONTROL
2387 ReturnComNotImplemented();
2388#else /* VBOX_WITH_GUEST_CONTROL */
2389 CheckComArgStrNotEmptyOrNull(aSource);
2390 CheckComArgOutPointerValid(aProgress);
2391
2392 AutoCaller autoCaller(this);
2393 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2394
2395 /* Validate flags. */
2396 if (aFlags)
2397 {
2398 if (!(aFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly))
2399 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
2400 }
2401
2402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2403
2404 HRESULT rc = S_OK;
2405
2406 ComObjPtr<Progress> progress;
2407 try
2408 {
2409 /* Create the progress object. */
2410 progress.createObject();
2411
2412 rc = progress->init(static_cast<IGuest*>(this),
2413 Bstr(tr("Updating Guest Additions")).raw(),
2414 TRUE /* aCancelable */);
2415 if (FAILED(rc)) throw rc;
2416
2417 /* Initialize our worker task. */
2418 TaskGuest *pTask = new TaskGuest(TaskGuest::UpdateGuestAdditions, this, progress);
2419 AssertPtr(pTask);
2420 std::auto_ptr<TaskGuest> task(pTask);
2421
2422 /* Assign data - in that case aSource is the full path
2423 * to the Guest Additions .ISO we want to mount. */
2424 task->strSource = (Utf8Str(aSource));
2425 task->uFlags = aFlags;
2426
2427 rc = task->startThread();
2428 if (FAILED(rc)) throw rc;
2429
2430 /* Don't destruct on success. */
2431 task.release();
2432 }
2433 catch (HRESULT aRC)
2434 {
2435 rc = aRC;
2436 }
2437
2438 if (SUCCEEDED(rc))
2439 {
2440 /* Return progress to the caller. */
2441 progress.queryInterfaceTo(aProgress);
2442 }
2443 return rc;
2444#endif /* VBOX_WITH_GUEST_CONTROL */
2445}
2446
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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