VirtualBox

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

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

Check for valid pointers.

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