VirtualBox

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

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

Main: Moved Guest Control code to own file for better separation.

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

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