VirtualBox

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

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