VirtualBox

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

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

GuestCtrl: Added support for getting separated stdout/stderr output.

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