VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestCtrlImpl.cpp@ 35456

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

Build fix.

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