VirtualBox

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

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

GuestCtrl: Update of copy from guest (work in progress).

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