VirtualBox

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

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

Comment.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 104.4 KB
Line 
1/* $Id: GuestCtrlImpl.cpp 35540 2011-01-13 15:36:00Z 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 {
2126 /* No data within specified timeout available. Use a special
2127 * error so that we can gently handle that case a bit below. */
2128 vrc = VERR_NO_DATA;
2129 }
2130 }
2131 else /* If callback not called within time ... well, that's a timeout! */
2132 vrc = VERR_TIMEOUT;
2133 }
2134 else /* Operation was canceled. */
2135 {
2136 vrc = VERR_CANCELLED;
2137 }
2138
2139 if (RT_FAILURE(vrc))
2140 {
2141 if (vrc == VERR_NO_DATA)
2142 {
2143 /* If there was no output data then this is no error we want
2144 * to report to COM. The caller just gets back a size of 0 (zero). */
2145 rc = S_OK;
2146 }
2147 else if (vrc == VERR_TIMEOUT)
2148 {
2149 rc = setError(VBOX_E_IPRT_ERROR,
2150 tr("The guest did not output within time (%ums)"), aTimeoutMS);
2151 }
2152 else if (vrc == VERR_CANCELLED)
2153 {
2154 rc = setError(VBOX_E_IPRT_ERROR,
2155 tr("The output operation was canceled"));
2156 }
2157 else
2158 {
2159 rc = setError(E_UNEXPECTED,
2160 tr("The service call failed with error %Rrc"), vrc);
2161 }
2162 }
2163
2164 {
2165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2166 /* Destroy locally used progress object. */
2167 destroyCtrlCallbackContext(it);
2168 }
2169 }
2170 else /* PID lookup failed. */
2171 rc = setError(VBOX_E_IPRT_ERROR,
2172 tr("Process (PID %u) not found!"), aPID);
2173 }
2174 else /* HGCM operation failed. */
2175 rc = setError(E_UNEXPECTED,
2176 tr("The HGCM call failed with error %Rrc"), vrc);
2177
2178 /* Cleanup. */
2179 progress->uninit();
2180 progress.setNull();
2181
2182 /* If something failed (or there simply was no data, indicated by VERR_NO_DATA,
2183 * we return an empty array so that the frontend knows when to give up. */
2184 if (RT_FAILURE(vrc) || FAILED(rc))
2185 outputData.resize(0);
2186 outputData.detachTo(ComSafeArrayOutArg(aData));
2187 }
2188 catch (std::bad_alloc &)
2189 {
2190 rc = E_OUTOFMEMORY;
2191 }
2192 return rc;
2193#endif
2194}
2195
2196STDMETHODIMP Guest::GetProcessStatus(ULONG aPID, ULONG *aExitCode, ULONG *aFlags, ULONG *aStatus)
2197{
2198#ifndef VBOX_WITH_GUEST_CONTROL
2199 ReturnComNotImplemented();
2200#else /* VBOX_WITH_GUEST_CONTROL */
2201 using namespace guestControl;
2202
2203 AutoCaller autoCaller(this);
2204 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2205
2206 HRESULT rc = S_OK;
2207
2208 try
2209 {
2210 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2211
2212 GuestProcessMapIterConst it = getProcessByPID(aPID);
2213 if (it != mGuestProcessMap.end())
2214 {
2215 *aExitCode = it->second.mExitCode;
2216 *aFlags = it->second.mFlags;
2217 *aStatus = it->second.mStatus;
2218 }
2219 else
2220 rc = setError(VBOX_E_IPRT_ERROR,
2221 tr("Process (PID %u) not found!"), aPID);
2222 }
2223 catch (std::bad_alloc &)
2224 {
2225 rc = E_OUTOFMEMORY;
2226 }
2227 return rc;
2228#endif
2229}
2230
2231STDMETHODIMP Guest::CopyToGuest(IN_BSTR aSource, IN_BSTR aDest,
2232 IN_BSTR aUserName, IN_BSTR aPassword,
2233 ULONG aFlags, IProgress **aProgress)
2234{
2235#ifndef VBOX_WITH_GUEST_CONTROL
2236 ReturnComNotImplemented();
2237#else /* VBOX_WITH_GUEST_CONTROL */
2238 CheckComArgStrNotEmptyOrNull(aSource);
2239 CheckComArgStrNotEmptyOrNull(aDest);
2240 CheckComArgStrNotEmptyOrNull(aUserName);
2241 CheckComArgStrNotEmptyOrNull(aPassword);
2242 CheckComArgOutPointerValid(aProgress);
2243
2244 AutoCaller autoCaller(this);
2245 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2246
2247 /* Validate flags. */
2248 if (aFlags != CopyFileFlag_None)
2249 {
2250 if ( !(aFlags & CopyFileFlag_Recursive)
2251 && !(aFlags & CopyFileFlag_Update)
2252 && !(aFlags & CopyFileFlag_FollowLinks))
2253 {
2254 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
2255 }
2256 }
2257
2258 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2259
2260 HRESULT rc = S_OK;
2261
2262 ComObjPtr<Progress> progress;
2263 try
2264 {
2265 /* Create the progress object. */
2266 progress.createObject();
2267
2268 rc = progress->init(static_cast<IGuest*>(this),
2269 Bstr(tr("Copying file")).raw(),
2270 TRUE /* aCancelable */);
2271 if (FAILED(rc)) throw rc;
2272
2273 /* Initialize our worker task. */
2274 TaskGuest *pTask = new TaskGuest(TaskGuest::CopyFile, this, progress);
2275 AssertPtr(pTask);
2276 std::auto_ptr<TaskGuest> task(pTask);
2277
2278 /* Assign data - aSource is the source file on the host,
2279 * aDest reflects the full path on the guest. */
2280 task->strSource = (Utf8Str(aSource));
2281 task->strDest = (Utf8Str(aDest));
2282 task->strUserName = (Utf8Str(aUserName));
2283 task->strPassword = (Utf8Str(aPassword));
2284 task->uFlags = aFlags;
2285
2286 rc = task->startThread();
2287 if (FAILED(rc)) throw rc;
2288
2289 /* Don't destruct on success. */
2290 task.release();
2291 }
2292 catch (HRESULT aRC)
2293 {
2294 rc = aRC;
2295 }
2296
2297 if (SUCCEEDED(rc))
2298 {
2299 /* Return progress to the caller. */
2300 progress.queryInterfaceTo(aProgress);
2301 }
2302 return rc;
2303#endif /* VBOX_WITH_GUEST_CONTROL */
2304}
2305
2306STDMETHODIMP Guest::CreateDirectory(IN_BSTR aDirectory,
2307 IN_BSTR aUserName, IN_BSTR aPassword,
2308 ULONG aMode, ULONG aFlags,
2309 IProgress **aProgress)
2310{
2311#ifndef VBOX_WITH_GUEST_CONTROL
2312 ReturnComNotImplemented();
2313#else /* VBOX_WITH_GUEST_CONTROL */
2314 using namespace guestControl;
2315
2316 CheckComArgStrNotEmptyOrNull(aDirectory);
2317
2318 /* Do not allow anonymous executions (with system rights). */
2319 if (RT_UNLIKELY((aUserName) == NULL || *(aUserName) == '\0'))
2320 return setError(E_INVALIDARG, tr("No user name specified"));
2321
2322 LogRel(("Creating guest directory \"%s\" as user \"%s\" ...\n",
2323 Utf8Str(aDirectory).c_str(), Utf8Str(aUserName).c_str()));
2324
2325 return createDirectoryInternal(aDirectory,
2326 aUserName, aPassword,
2327 aMode, aFlags, aProgress, NULL /* rc */);
2328#endif
2329}
2330
2331HRESULT Guest::createDirectoryInternal(IN_BSTR aDirectory,
2332 IN_BSTR aUserName, IN_BSTR aPassword,
2333 ULONG aMode, ULONG aFlags,
2334 IProgress **aProgress, int *pRC)
2335{
2336#ifndef VBOX_WITH_GUEST_CONTROL
2337 ReturnComNotImplemented();
2338#else /* VBOX_WITH_GUEST_CONTROL */
2339 using namespace guestControl;
2340
2341 CheckComArgStrNotEmptyOrNull(aDirectory);
2342 CheckComArgOutPointerValid(aProgress);
2343
2344 AutoCaller autoCaller(this);
2345 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2346
2347 /* Validate flags. */
2348 if (aFlags != CreateDirectoryFlag_None)
2349 {
2350 if (!(aFlags & CreateDirectoryFlag_Parents))
2351 {
2352 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
2353 }
2354 }
2355
2356 /**
2357 * @todo We return a progress object because we maybe later want to
2358 * process more than one directory (or somewhat lengthly operations)
2359 * that require having a progress object provided to the caller.
2360 */
2361
2362 HRESULT rc = S_OK;
2363 try
2364 {
2365 Utf8Str Utf8Directory(aDirectory);
2366 Utf8Str Utf8UserName(aUserName);
2367 Utf8Str Utf8Password(aPassword);
2368
2369 com::SafeArray<IN_BSTR> args;
2370 com::SafeArray<IN_BSTR> env;
2371
2372 /*
2373 * Prepare tool command line.
2374 */
2375 args.push_back(Bstr(VBOXSERVICE_TOOL_MKDIR).raw()); /* The actual (internal) tool to use (as argv[0]). */
2376 if (aFlags & CreateDirectoryFlag_Parents)
2377 args.push_back(Bstr("--parents").raw()); /* We also want to create the parent directories. */
2378 if (aMode > 0)
2379 {
2380 args.push_back(Bstr("--mode").raw()); /* Set the creation mode. */
2381
2382 char szMode[16];
2383 RTStrPrintf(szMode, sizeof(szMode), "%o", aMode);
2384 args.push_back(Bstr(szMode).raw());
2385 }
2386 args.push_back(Bstr(Utf8Directory).raw()); /* The directory we want to create. */
2387
2388 /*
2389 * Execute guest process.
2390 */
2391 ComPtr<IProgress> progressExec;
2392 ULONG uPID;
2393 if (SUCCEEDED(rc))
2394 {
2395 rc = ExecuteProcess(Bstr(VBOXSERVICE_TOOL_MKDIR).raw(),
2396 ExecuteProcessFlag_Hidden,
2397 ComSafeArrayAsInParam(args),
2398 ComSafeArrayAsInParam(env),
2399 Bstr(Utf8UserName).raw(),
2400 Bstr(Utf8Password).raw(),
2401 5 * 1000 /* Wait 5s for getting the process started. */,
2402 &uPID, progressExec.asOutParam());
2403 }
2404
2405 if (SUCCEEDED(rc))
2406 {
2407 /* Wait for process to exit ... */
2408 BOOL fCompleted = FALSE;
2409 BOOL fCanceled = FALSE;
2410
2411 while ( SUCCEEDED(progressExec->COMGETTER(Completed(&fCompleted)))
2412 && !fCompleted)
2413 {
2414 /* Progress canceled by Main API? */
2415 if ( SUCCEEDED(progressExec->COMGETTER(Canceled(&fCanceled)))
2416 && fCanceled)
2417 {
2418 break;
2419 }
2420 }
2421
2422 ComObjPtr<Progress> progressCreate;
2423 rc = progressCreate.createObject();
2424 if (SUCCEEDED(rc))
2425 {
2426 rc = progressCreate->init(static_cast<IGuest*>(this),
2427 Bstr(tr("Creating directory")).raw(),
2428 TRUE);
2429 }
2430 if (FAILED(rc)) return rc;
2431
2432 if (fCompleted)
2433 {
2434 ULONG uRetStatus, uRetExitCode, uRetFlags;
2435 if (SUCCEEDED(rc))
2436 {
2437 rc = GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &uRetStatus);
2438 if (SUCCEEDED(rc) && uRetExitCode != 0)
2439 {
2440 rc = setError(VBOX_E_IPRT_ERROR,
2441 tr("Error while creating directory"));
2442 }
2443 }
2444 }
2445 else if (fCanceled)
2446 rc = setError(VBOX_E_IPRT_ERROR,
2447 tr("Directory creation was aborted"));
2448 else
2449 AssertReleaseMsgFailed(("Directory creation neither completed nor canceled!?"));
2450
2451 if (SUCCEEDED(rc))
2452 progressCreate->notifyComplete(S_OK);
2453 else
2454 progressCreate->notifyComplete(VBOX_E_IPRT_ERROR,
2455 COM_IIDOF(IGuest),
2456 Guest::getStaticComponentName(),
2457 Guest::tr("Error while executing creation command"));
2458
2459 /* Return the progress to the caller. */
2460 progressCreate.queryInterfaceTo(aProgress);
2461 }
2462 }
2463 catch (std::bad_alloc &)
2464 {
2465 rc = E_OUTOFMEMORY;
2466 }
2467 return rc;
2468#endif /* VBOX_WITH_GUEST_CONTROL */
2469}
2470
2471STDMETHODIMP Guest::UpdateGuestAdditions(IN_BSTR aSource, ULONG aFlags, IProgress **aProgress)
2472{
2473#ifndef VBOX_WITH_GUEST_CONTROL
2474 ReturnComNotImplemented();
2475#else /* VBOX_WITH_GUEST_CONTROL */
2476 CheckComArgStrNotEmptyOrNull(aSource);
2477 CheckComArgOutPointerValid(aProgress);
2478
2479 AutoCaller autoCaller(this);
2480 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2481
2482 /* Validate flags. */
2483 if (aFlags)
2484 {
2485 if (!(aFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly))
2486 return setError(E_INVALIDARG, tr("Unknown flags (%#x)"), aFlags);
2487 }
2488
2489 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2490
2491 HRESULT rc = S_OK;
2492
2493 ComObjPtr<Progress> progress;
2494 try
2495 {
2496 /* Create the progress object. */
2497 progress.createObject();
2498
2499 rc = progress->init(static_cast<IGuest*>(this),
2500 Bstr(tr("Updating Guest Additions")).raw(),
2501 TRUE /* aCancelable */);
2502 if (FAILED(rc)) throw rc;
2503
2504 /* Initialize our worker task. */
2505 TaskGuest *pTask = new TaskGuest(TaskGuest::UpdateGuestAdditions, this, progress);
2506 AssertPtr(pTask);
2507 std::auto_ptr<TaskGuest> task(pTask);
2508
2509 /* Assign data - in that case aSource is the full path
2510 * to the Guest Additions .ISO we want to mount. */
2511 task->strSource = (Utf8Str(aSource));
2512 task->uFlags = aFlags;
2513
2514 rc = task->startThread();
2515 if (FAILED(rc)) throw rc;
2516
2517 /* Don't destruct on success. */
2518 task.release();
2519 }
2520 catch (HRESULT aRC)
2521 {
2522 rc = aRC;
2523 }
2524
2525 if (SUCCEEDED(rc))
2526 {
2527 /* Return progress to the caller. */
2528 progress.queryInterfaceTo(aProgress);
2529 }
2530 return rc;
2531#endif /* VBOX_WITH_GUEST_CONTROL */
2532}
2533
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