VirtualBox

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

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

GuestCtrl/Execute: Added NoProfile flag for executing processes without loading the user's profile.

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