VirtualBox

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

Last change on this file since 36701 was 36531, checked in by vboxsync, 14 years ago

Main/GuestCtrl: Take empty files into account when checking for write result.

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