VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestCtrlImplTasks.cpp@ 38235

Last change on this file since 38235 was 38235, checked in by vboxsync, 13 years ago

GuestCtrl: Update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 46.1 KB
Line 
1/* $Id: */
2/** @file
3 * VirtualBox Guest Control - Threaded operations (tasks).
4 */
5
6/*
7 * Copyright (C) 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
35#include <iprt/file.h>
36#include <iprt/isofs.h>
37#include <iprt/list.h>
38#include <iprt/path.h>
39
40GuestTask::GuestTask(TaskType aTaskType, Guest *aThat, Progress *aProgress)
41 : taskType(aTaskType),
42 pGuest(aThat),
43 progress(aProgress),
44 rc(S_OK)
45{
46
47}
48
49GuestTask::~GuestTask()
50{
51
52}
53
54int GuestTask::startThread()
55{
56 return RTThreadCreate(NULL, GuestTask::taskThread, this,
57 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
58 "GuestTask");
59}
60
61/* static */
62DECLCALLBACK(int) GuestTask::taskThread(RTTHREAD /* aThread */, void *pvUser)
63{
64 std::auto_ptr<GuestTask> task(static_cast<GuestTask*>(pvUser));
65 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
66
67 Guest *pGuest = task->pGuest;
68
69 LogFlowFuncEnter();
70 LogFlowFunc(("Guest %p\n", pGuest));
71
72 HRESULT rc = S_OK;
73
74 switch (task->taskType)
75 {
76#ifdef VBOX_WITH_GUEST_CONTROL
77 case TaskType_CopyFileToGuest:
78 {
79 rc = pGuest->taskCopyFileToGuest(task.get());
80 break;
81 }
82 case TaskType_CopyFileFromGuest:
83 {
84 rc = pGuest->taskCopyFileFromGuest(task.get());
85 break;
86 }
87 case TaskType_UpdateGuestAdditions:
88 {
89 rc = pGuest->taskUpdateGuestAdditions(task.get());
90 break;
91 }
92#endif
93 default:
94 AssertMsgFailed(("Invalid task type %u specified!\n", task->taskType));
95 break;
96 }
97
98 LogFlowFunc(("rc=%Rhrc\n", rc));
99 LogFlowFuncLeave();
100
101 return VINF_SUCCESS;
102}
103
104/* static */
105int GuestTask::uploadProgress(unsigned uPercent, void *pvUser)
106{
107 GuestTask *pTask = *(GuestTask**)pvUser;
108
109 if ( pTask
110 && !pTask->progress.isNull())
111 {
112 BOOL fCanceled;
113 pTask->progress->COMGETTER(Canceled)(&fCanceled);
114 if (fCanceled)
115 return -1;
116 pTask->progress->SetCurrentOperationProgress(uPercent);
117 }
118 return VINF_SUCCESS;
119}
120
121/* static */
122HRESULT GuestTask::setProgressErrorInfo(HRESULT hr, ComObjPtr<Progress> pProgress,
123 const char *pszText, ...)
124{
125 BOOL fCanceled;
126 BOOL fCompleted;
127 if ( SUCCEEDED(pProgress->COMGETTER(Canceled(&fCanceled)))
128 && !fCanceled
129 && SUCCEEDED(pProgress->COMGETTER(Completed(&fCompleted)))
130 && !fCompleted)
131 {
132 va_list va;
133 va_start(va, pszText);
134 HRESULT hr2 = pProgress->notifyCompleteV(hr,
135 COM_IIDOF(IGuest),
136 Guest::getStaticComponentName(),
137 pszText,
138 va);
139 va_end(va);
140 if (hr2 == S_OK) /* If unable to retrieve error, return input error. */
141 hr2 = hr;
142 return hr2;
143 }
144 return S_OK;
145}
146
147/* static */
148HRESULT GuestTask::setProgressErrorInfo(HRESULT hr,
149 ComObjPtr<Progress> pProgress, ComObjPtr<Guest> pGuest)
150{
151 return setProgressErrorInfo(hr, pProgress,
152 Utf8Str(com::ErrorInfo((IGuest*)pGuest, COM_IIDOF(IGuest)).getText()).c_str());
153}
154
155#ifdef VBOX_WITH_GUEST_CONTROL
156HRESULT Guest::taskCopyFileToGuest(GuestTask *aTask)
157{
158 LogFlowFuncEnter();
159
160 AutoCaller autoCaller(this);
161 if (FAILED(autoCaller.rc())) return autoCaller.rc();
162
163 /*
164 * Do *not* take a write lock here since we don't (and won't)
165 * touch any class-specific data (of IGuest) here - only the member functions
166 * which get called here can do that.
167 */
168
169 HRESULT rc = S_OK;
170
171 try
172 {
173 Guest *pGuest = aTask->pGuest;
174 AssertPtr(pGuest);
175
176 /* Does our source file exist? */
177 if (!RTFileExists(aTask->strSource.c_str()))
178 {
179 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
180 Guest::tr("Source file \"%s\" does not exist, or is not a file"),
181 aTask->strSource.c_str());
182 }
183 else
184 {
185 RTFILE fileSource;
186 int vrc = RTFileOpen(&fileSource, aTask->strSource.c_str(),
187 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
188 if (RT_FAILURE(vrc))
189 {
190 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
191 Guest::tr("Could not open source file \"%s\" for reading (%Rrc)"),
192 aTask->strSource.c_str(), vrc);
193 }
194 else
195 {
196 uint64_t cbSize;
197 vrc = RTFileGetSize(fileSource, &cbSize);
198 if (RT_FAILURE(vrc))
199 {
200 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
201 Guest::tr("Could not query file size of \"%s\" (%Rrc)"),
202 aTask->strSource.c_str(), vrc);
203 }
204 else
205 {
206 com::SafeArray<IN_BSTR> args;
207 com::SafeArray<IN_BSTR> env;
208
209 /*
210 * Prepare tool command line.
211 */
212 char szOutput[RTPATH_MAX];
213 if (RTStrPrintf(szOutput, sizeof(szOutput), "--output=%s", aTask->strDest.c_str()) <= sizeof(szOutput) - 1)
214 {
215 /*
216 * Normalize path slashes, based on the detected guest.
217 */
218 Utf8Str osType = mData.mOSTypeId;
219 if ( osType.contains("Microsoft", Utf8Str::CaseInsensitive)
220 || osType.contains("Windows", Utf8Str::CaseInsensitive))
221 {
222 /* We have a Windows guest. */
223 RTPathChangeToDosSlashes(szOutput, true /* Force conversion. */);
224 }
225 else /* ... or something which isn't from Redmond ... */
226 {
227 RTPathChangeToUnixSlashes(szOutput, true /* Force conversion. */);
228 }
229
230 args.push_back(Bstr(szOutput).raw()); /* We want to write a file ... */
231 }
232 else
233 {
234 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
235 Guest::tr("Error preparing command line"));
236 }
237
238 ComPtr<IProgress> execProgress;
239 ULONG uPID;
240 if (SUCCEEDED(rc))
241 {
242 LogRel(("Copying file \"%s\" to guest \"%s\" (%u bytes) ...\n",
243 aTask->strSource.c_str(), aTask->strDest.c_str(), cbSize));
244 /*
245 * Okay, since we gathered all stuff we need until now to start the
246 * actual copying, start the guest part now.
247 */
248 rc = pGuest->ExecuteProcess(Bstr(VBOXSERVICE_TOOL_CAT).raw(),
249 ExecuteProcessFlag_Hidden
250 | ExecuteProcessFlag_WaitForProcessStartOnly,
251 ComSafeArrayAsInParam(args),
252 ComSafeArrayAsInParam(env),
253 Bstr(aTask->strUserName).raw(),
254 Bstr(aTask->strPassword).raw(),
255 5 * 1000 /* Wait 5s for getting the process started. */,
256 &uPID, execProgress.asOutParam());
257 if (FAILED(rc))
258 rc = GuestTask::setProgressErrorInfo(rc, aTask->progress, pGuest);
259 }
260
261 if (SUCCEEDED(rc))
262 {
263 BOOL fCompleted = FALSE;
264 BOOL fCanceled = FALSE;
265
266 size_t cbToRead = cbSize;
267 size_t cbTransfered = 0;
268 size_t cbRead;
269 SafeArray<BYTE> aInputData(_64K);
270 while ( SUCCEEDED(execProgress->COMGETTER(Completed(&fCompleted)))
271 && !fCompleted)
272 {
273 if (!cbToRead)
274 cbRead = 0;
275 else
276 {
277 vrc = RTFileRead(fileSource, (uint8_t*)aInputData.raw(),
278 RT_MIN(cbToRead, _64K), &cbRead);
279 /*
280 * Some other error occured? There might be a chance that RTFileRead
281 * could not resolve/map the native error code to an IPRT code, so just
282 * print a generic error.
283 */
284 if (RT_FAILURE(vrc))
285 {
286 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
287 Guest::tr("Could not read from file \"%s\" (%Rrc)"),
288 aTask->strSource.c_str(), vrc);
289 break;
290 }
291 }
292
293 /* Resize buffer to reflect amount we just have read.
294 * Size 0 is allowed! */
295 aInputData.resize(cbRead);
296
297 ULONG uFlags = ProcessInputFlag_None;
298 /* Did we reach the end of the content we want to transfer (last chunk)? */
299 if ( (cbRead < _64K)
300 /* Did we reach the last block which is exactly _64K? */
301 || (cbToRead - cbRead == 0)
302 /* ... or does the user want to cancel? */
303 || ( SUCCEEDED(aTask->progress->COMGETTER(Canceled(&fCanceled)))
304 && fCanceled)
305 )
306 {
307 uFlags |= ProcessInputFlag_EndOfFile;
308 }
309
310 /* Transfer the current chunk ... */
311 ULONG uBytesWritten;
312 rc = pGuest->SetProcessInput(uPID, uFlags,
313 10 * 1000 /* Wait 10s for getting the input data transfered. */,
314 ComSafeArrayAsInParam(aInputData), &uBytesWritten);
315 if (FAILED(rc))
316 {
317 rc = GuestTask::setProgressErrorInfo(rc, aTask->progress, pGuest);
318 break;
319 }
320
321 Assert(cbRead <= cbToRead);
322 Assert(cbToRead >= cbRead);
323 cbToRead -= cbRead;
324
325 cbTransfered += uBytesWritten;
326 Assert(cbTransfered <= cbSize);
327 aTask->progress->SetCurrentOperationProgress(cbTransfered / (cbSize / 100.0));
328
329 /* End of file reached? */
330 if (cbToRead == 0)
331 break;
332
333 /* Did the user cancel the operation above? */
334 if (fCanceled)
335 break;
336
337 /* Progress canceled by Main API? */
338 if ( SUCCEEDED(execProgress->COMGETTER(Canceled(&fCanceled)))
339 && fCanceled)
340 {
341 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
342 Guest::tr("Copy operation of file \"%s\" was canceled on guest side"),
343 aTask->strSource.c_str());
344 break;
345 }
346 }
347
348 if (SUCCEEDED(rc))
349 {
350 /*
351 * If we got here this means the started process either was completed,
352 * canceled or we simply got all stuff transferred.
353 */
354 ExecuteProcessStatus_T retStatus;
355 ULONG uRetExitCode;
356 rc = pGuest->waitForProcessStatusChange(uPID, &retStatus, &uRetExitCode, 10 * 1000 /* 10s timeout. */);
357 if (FAILED(rc))
358 {
359 rc = GuestTask::setProgressErrorInfo(rc, aTask->progress, pGuest);
360 }
361 else
362 {
363 if ( uRetExitCode != 0
364 || retStatus != ExecuteProcessStatus_TerminatedNormally)
365 {
366 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
367 Guest::tr("Guest reported error %u while copying file \"%s\" to \"%s\""),
368 uRetExitCode, aTask->strSource.c_str(), aTask->strDest.c_str());
369 }
370 }
371 }
372
373 if (SUCCEEDED(rc))
374 {
375 if (fCanceled)
376 {
377 /*
378 * In order to make the progress object to behave nicely, we also have to
379 * notify the object with a complete event when it's canceled.
380 */
381 aTask->progress->notifyComplete(VBOX_E_IPRT_ERROR,
382 COM_IIDOF(IGuest),
383 Guest::getStaticComponentName(),
384 Guest::tr("Copying file \"%s\" canceled"), aTask->strSource.c_str());
385 }
386 else
387 {
388 /*
389 * Even if we succeeded until here make sure to check whether we really transfered
390 * everything.
391 */
392 if ( cbSize > 0
393 && cbTransfered == 0)
394 {
395 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
396 * to the destination -> access denied. */
397 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
398 Guest::tr("Access denied when copying file \"%s\" to \"%s\""),
399 aTask->strSource.c_str(), aTask->strDest.c_str());
400 }
401 else if (cbTransfered < cbSize)
402 {
403 /* If we did not copy all let the user know. */
404 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
405 Guest::tr("Copying file \"%s\" failed (%u/%u bytes transfered)"),
406 aTask->strSource.c_str(), cbTransfered, cbSize);
407 }
408 else /* Yay, all went fine! */
409 aTask->progress->notifyComplete(S_OK);
410 }
411 }
412 }
413 }
414 RTFileClose(fileSource);
415 }
416 }
417 }
418 catch (HRESULT aRC)
419 {
420 rc = aRC;
421 }
422
423 /* Clean up */
424 aTask->rc = rc;
425
426 LogFlowFunc(("rc=%Rhrc\n", rc));
427 LogFlowFuncLeave();
428
429 return VINF_SUCCESS;
430}
431
432HRESULT Guest::taskCopyFileFromGuest(GuestTask *aTask)
433{
434 LogFlowFuncEnter();
435
436 AutoCaller autoCaller(this);
437 if (FAILED(autoCaller.rc())) return autoCaller.rc();
438
439 /*
440 * Do *not* take a write lock here since we don't (and won't)
441 * touch any class-specific data (of IGuest) here - only the member functions
442 * which get called here can do that.
443 */
444
445 HRESULT rc = S_OK;
446
447 try
448 {
449 Guest *pGuest = aTask->pGuest;
450 AssertPtr(pGuest);
451
452 /* Does our source file exist? */
453 BOOL fFileExists;
454 rc = pGuest->FileExists(Bstr(aTask->strSource).raw(),
455 Bstr(aTask->strUserName).raw(), Bstr(aTask->strPassword).raw(),
456 &fFileExists);
457 if (SUCCEEDED(rc))
458 {
459 if (!fFileExists)
460 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
461 Guest::tr("Source file \"%s\" does not exist, or is not a file"),
462 aTask->strSource.c_str());
463 }
464
465 /* Query file size to make an estimate for our progress object. */
466 if (SUCCEEDED(rc))
467 {
468 LONG64 lFileSize;
469 rc = pGuest->FileQuerySize(Bstr(aTask->strSource).raw(),
470 Bstr(aTask->strUserName).raw(), Bstr(aTask->strPassword).raw(),
471 &lFileSize);
472 if (FAILED(rc))
473 rc = GuestTask::setProgressErrorInfo(rc, aTask->progress, pGuest);
474
475 com::SafeArray<IN_BSTR> args;
476 com::SafeArray<IN_BSTR> env;
477
478 if (SUCCEEDED(rc))
479 {
480 /*
481 * Prepare tool command line.
482 */
483 char szSource[RTPATH_MAX];
484 if (RTStrPrintf(szSource, sizeof(szSource), "%s", aTask->strSource.c_str()) <= sizeof(szSource) - 1)
485 {
486 /*
487 * Normalize path slashes, based on the detected guest.
488 */
489 Utf8Str osType = mData.mOSTypeId;
490 if ( osType.contains("Microsoft", Utf8Str::CaseInsensitive)
491 || osType.contains("Windows", Utf8Str::CaseInsensitive))
492 {
493 /* We have a Windows guest. */
494 RTPathChangeToDosSlashes(szSource, true /* Force conversion. */);
495 }
496 else /* ... or something which isn't from Redmond ... */
497 {
498 RTPathChangeToUnixSlashes(szSource, true /* Force conversion. */);
499 }
500
501 args.push_back(Bstr(szSource).raw()); /* Tell our cat tool which file to output. */
502 }
503 else
504 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
505 Guest::tr("Error preparing command line"));
506 }
507
508 ComPtr<IProgress> execProgress;
509 ULONG uPID;
510 if (SUCCEEDED(rc))
511 {
512 LogRel(("Copying file \"%s\" to host \"%s\" (%u bytes) ...\n",
513 aTask->strSource.c_str(), aTask->strDest.c_str(), lFileSize));
514
515 /*
516 * Okay, since we gathered all stuff we need until now to start the
517 * actual copying, start the guest part now.
518 */
519 rc = pGuest->ExecuteProcess(Bstr(VBOXSERVICE_TOOL_CAT).raw(),
520 ExecuteProcessFlag_Hidden,
521 ComSafeArrayAsInParam(args),
522 ComSafeArrayAsInParam(env),
523 Bstr(aTask->strUserName).raw(),
524 Bstr(aTask->strPassword).raw(),
525 5 * 1000 /* Wait 5s for getting the process started. */,
526 &uPID, execProgress.asOutParam());
527 if (FAILED(rc))
528 rc = GuestTask::setProgressErrorInfo(rc, aTask->progress, pGuest);
529 }
530
531 if (SUCCEEDED(rc))
532 {
533 BOOL fCompleted = FALSE;
534 BOOL fCanceled = FALSE;
535
536 RTFILE hFileDest;
537 int vrc = RTFileOpen(&hFileDest, aTask->strDest.c_str(),
538 RTFILE_O_READWRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE);
539 if (RT_FAILURE(vrc))
540 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
541 Guest::tr("Unable to create/open destination file \"%s\", rc=%Rrc"),
542 aTask->strDest.c_str(), vrc);
543 else
544 {
545 size_t cbToRead = lFileSize;
546 size_t cbTransfered = 0;
547 SafeArray<BYTE> aOutputData(_64K);
548 while (SUCCEEDED(execProgress->COMGETTER(Completed(&fCompleted))))
549 {
550 rc = this->GetProcessOutput(uPID, ProcessOutputFlag_None,
551 10 * 1000 /* Timeout in ms */,
552 _64K, ComSafeArrayAsOutParam(aOutputData));
553 if (SUCCEEDED(rc))
554 {
555 if (!aOutputData.size())
556 {
557 if (cbToRead)
558 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
559 Guest::tr("Unexpected end of file \"%s\" (%u bytes left)"),
560 aTask->strSource.c_str(), cbToRead);
561 break;
562 }
563
564 vrc = RTFileWrite(hFileDest, aOutputData.raw(), aOutputData.size(), NULL /* No partial writes */);
565 if (RT_FAILURE(vrc))
566 {
567 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
568 Guest::tr("Error writing to file \"%s\" (%u bytes left), rc=%Rrc"),
569 aTask->strSource.c_str(), cbToRead, vrc);
570 break;
571 }
572
573 cbToRead -= aOutputData.size();
574 cbTransfered += aOutputData.size();
575
576 aTask->progress->SetCurrentOperationProgress(cbTransfered / (lFileSize / 100.0));
577 }
578 else
579 {
580 rc = GuestTask::setProgressErrorInfo(rc, aTask->progress, pGuest);
581 break;
582 }
583 }
584
585 if (SUCCEEDED(rc))
586 aTask->progress->notifyComplete(S_OK);
587
588 RTFileClose(hFileDest);
589 }
590 }
591 }
592 }
593 catch (HRESULT aRC)
594 {
595 rc = aRC;
596 }
597
598 /* Clean up */
599 aTask->rc = rc;
600
601 LogFlowFunc(("rc=%Rhrc\n", rc));
602 LogFlowFuncLeave();
603
604 return VINF_SUCCESS;
605}
606
607HRESULT Guest::taskUpdateGuestAdditions(GuestTask *aTask)
608{
609 LogFlowFuncEnter();
610
611 AutoCaller autoCaller(this);
612 if (FAILED(autoCaller.rc())) return autoCaller.rc();
613
614 /*
615 * Do *not* take a write lock here since we don't (and won't)
616 * touch any class-specific data (of IGuest) here - only the member functions
617 * which get called here can do that.
618 */
619
620 HRESULT rc = S_OK;
621 BOOL fCompleted;
622 BOOL fCanceled;
623
624 try
625 {
626 Guest *pGuest = aTask->pGuest;
627 AssertPtr(pGuest);
628
629 aTask->progress->SetCurrentOperationProgress(10);
630
631 /*
632 * Determine guest OS type and the required installer image.
633 * At the moment only Windows guests are supported.
634 */
635 Utf8Str installerImage;
636 Bstr osTypeId;
637 if ( SUCCEEDED(pGuest->COMGETTER(OSTypeId(osTypeId.asOutParam())))
638 && !osTypeId.isEmpty())
639 {
640 Utf8Str osTypeIdUtf8(osTypeId); /* Needed for .contains(). */
641 if ( osTypeIdUtf8.contains("Microsoft", Utf8Str::CaseInsensitive)
642 || osTypeIdUtf8.contains("Windows", Utf8Str::CaseInsensitive))
643 {
644 if (osTypeIdUtf8.contains("64", Utf8Str::CaseInsensitive))
645 installerImage = "VBOXWINDOWSADDITIONS_AMD64.EXE";
646 else
647 installerImage = "VBOXWINDOWSADDITIONS_X86.EXE";
648 /* Since the installers are located in the root directory,
649 * no further path processing needs to be done (yet). */
650 }
651 else /* Everything else is not supported (yet). */
652 throw GuestTask::setProgressErrorInfo(VBOX_E_NOT_SUPPORTED, aTask->progress,
653 Guest::tr("Detected guest OS (%s) does not support automatic Guest Additions updating, please update manually"),
654 osTypeIdUtf8.c_str());
655 }
656 else
657 throw GuestTask::setProgressErrorInfo(VBOX_E_NOT_SUPPORTED, aTask->progress,
658 Guest::tr("Could not detected guest OS type/version, please update manually"));
659 Assert(!installerImage.isEmpty());
660
661 /*
662 * Try to open the .ISO file and locate the specified installer.
663 */
664 RTISOFSFILE iso;
665 int vrc = RTIsoFsOpen(&iso, aTask->strSource.c_str());
666 if (RT_FAILURE(vrc))
667 {
668 rc = GuestTask::setProgressErrorInfo(VBOX_E_FILE_ERROR, aTask->progress,
669 Guest::tr("Invalid installation medium detected: \"%s\""),
670 aTask->strSource.c_str());
671 }
672 else
673 {
674 uint32_t cbOffset;
675 size_t cbLength;
676 vrc = RTIsoFsGetFileInfo(&iso, installerImage.c_str(), &cbOffset, &cbLength);
677 if ( RT_SUCCESS(vrc)
678 && cbOffset
679 && cbLength)
680 {
681 vrc = RTFileSeek(iso.file, cbOffset, RTFILE_SEEK_BEGIN, NULL);
682 if (RT_FAILURE(vrc))
683 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
684 Guest::tr("Could not seek to setup file on installation medium \"%s\" (%Rrc)"),
685 aTask->strSource.c_str(), vrc);
686 }
687 else
688 {
689 switch (vrc)
690 {
691 case VERR_FILE_NOT_FOUND:
692 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
693 Guest::tr("Setup file was not found on installation medium \"%s\""),
694 aTask->strSource.c_str());
695 break;
696
697 default:
698 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
699 Guest::tr("An unknown error (%Rrc) occured while retrieving information of setup file on installation medium \"%s\""),
700 vrc, aTask->strSource.c_str());
701 break;
702 }
703 }
704
705 /* Specify the ouput path on the guest side. */
706 Utf8Str strInstallerPath = "%TEMP%\\VBoxWindowsAdditions.exe";
707
708 if (RT_SUCCESS(vrc))
709 {
710 /* Okay, we're ready to start our copy routine on the guest! */
711 aTask->progress->SetCurrentOperationProgress(15);
712
713 /* Prepare command line args. */
714 com::SafeArray<IN_BSTR> args;
715 com::SafeArray<IN_BSTR> env;
716
717 args.push_back(Bstr("--output").raw()); /* We want to write a file ... */
718 args.push_back(Bstr(strInstallerPath.c_str()).raw()); /* ... with this path. */
719
720 if (SUCCEEDED(rc))
721 {
722 ComPtr<IProgress> progressCat;
723 ULONG uPID;
724
725 /*
726 * Start built-in "vbox_cat" tool (inside VBoxService) to
727 * copy over/pipe the data into a file on the guest (with
728 * system rights, no username/password specified).
729 */
730 rc = pGuest->executeProcessInternal(Bstr(VBOXSERVICE_TOOL_CAT).raw(),
731 ExecuteProcessFlag_Hidden
732 | ExecuteProcessFlag_WaitForProcessStartOnly,
733 ComSafeArrayAsInParam(args),
734 ComSafeArrayAsInParam(env),
735 Bstr("").raw() /* Username. */,
736 Bstr("").raw() /* Password */,
737 5 * 1000 /* Wait 5s for getting the process started. */,
738 &uPID, progressCat.asOutParam(), &vrc);
739 if (FAILED(rc))
740 {
741 /* Errors which return VBOX_E_NOT_SUPPORTED can be safely skipped by the caller
742 * to silently fall back to "normal" (old) .ISO mounting. */
743
744 /* Due to a very limited COM error range we use vrc for a more detailed error
745 * lookup to figure out what went wrong. */
746 switch (vrc)
747 {
748 /* Guest execution service is not (yet) ready. This basically means that either VBoxService
749 * is not running (yet) or that the Guest Additions are too old (because VBoxService does not
750 * support the guest execution feature in this version). */
751 case VERR_NOT_FOUND:
752 LogRel(("Guest Additions seem not to be installed yet\n"));
753 rc = GuestTask::setProgressErrorInfo(VBOX_E_NOT_SUPPORTED, aTask->progress,
754 Guest::tr("Guest Additions seem not to be installed or are not ready to update yet"));
755 break;
756
757 /* Getting back a VERR_INVALID_PARAMETER indicates that the installed Guest Additions are supporting the guest
758 * execution but not the built-in "vbox_cat" tool of VBoxService (< 4.0). */
759 case VERR_INVALID_PARAMETER:
760 LogRel(("Guest Additions are installed but don't supported automatic updating\n"));
761 rc = GuestTask::setProgressErrorInfo(VBOX_E_NOT_SUPPORTED, aTask->progress,
762 Guest::tr("Installed Guest Additions do not support automatic updating"));
763 break;
764
765 case VERR_TIMEOUT:
766 LogRel(("Guest was unable to start copying the Guest Additions setup within time\n"));
767 rc = GuestTask::setProgressErrorInfo(E_FAIL, aTask->progress,
768 Guest::tr("Guest was unable to start copying the Guest Additions setup within time"));
769 break;
770
771 default:
772 rc = GuestTask::setProgressErrorInfo(E_FAIL, aTask->progress,
773 Guest::tr("Error copying Guest Additions setup file to guest path \"%s\" (%Rrc)"),
774 strInstallerPath.c_str(), vrc);
775 break;
776 }
777 }
778 else
779 {
780 LogRel(("Automatic update of Guest Additions started, using \"%s\"\n", aTask->strSource.c_str()));
781 LogRel(("Copying Guest Additions installer \"%s\" to \"%s\" on guest ...\n",
782 installerImage.c_str(), strInstallerPath.c_str()));
783 aTask->progress->SetCurrentOperationProgress(20);
784
785 /* Wait for process to exit ... */
786 SafeArray<BYTE> aInputData(_64K);
787 while ( SUCCEEDED(progressCat->COMGETTER(Completed(&fCompleted)))
788 && !fCompleted)
789 {
790 size_t cbRead;
791 /* cbLength contains remaining bytes of our installer file
792 * opened above to read. */
793 size_t cbToRead = RT_MIN(cbLength, _64K);
794 if (cbToRead)
795 {
796 vrc = RTFileRead(iso.file, (uint8_t*)aInputData.raw(), cbToRead, &cbRead);
797 if ( cbRead
798 && RT_SUCCESS(vrc))
799 {
800 /* Resize buffer to reflect amount we just have read. */
801 if (cbRead > 0)
802 aInputData.resize(cbRead);
803
804 /* Did we reach the end of the content we want to transfer (last chunk)? */
805 ULONG uFlags = ProcessInputFlag_None;
806 if ( (cbRead < _64K)
807 /* Did we reach the last block which is exactly _64K? */
808 || (cbToRead - cbRead == 0)
809 /* ... or does the user want to cancel? */
810 || ( SUCCEEDED(aTask->progress->COMGETTER(Canceled(&fCanceled)))
811 && fCanceled)
812 )
813 {
814 uFlags |= ProcessInputFlag_EndOfFile;
815 }
816
817 /* Transfer the current chunk ... */
818 #ifdef DEBUG_andy
819 LogRel(("Copying Guest Additions (%u bytes left) ...\n", cbLength));
820 #endif
821 ULONG uBytesWritten;
822 rc = pGuest->SetProcessInput(uPID, uFlags,
823 10 * 1000 /* Wait 10s for getting the input data transfered. */,
824 ComSafeArrayAsInParam(aInputData), &uBytesWritten);
825 if (FAILED(rc))
826 {
827 rc = GuestTask::setProgressErrorInfo(rc, aTask->progress, pGuest);
828 break;
829 }
830
831 /* If task was canceled above also cancel the process execution. */
832 if (fCanceled)
833 progressCat->Cancel();
834
835 #ifdef DEBUG_andy
836 LogRel(("Copying Guest Additions (%u bytes written) ...\n", uBytesWritten));
837 #endif
838 Assert(cbLength >= uBytesWritten);
839 cbLength -= uBytesWritten;
840 }
841 else if (RT_FAILURE(vrc))
842 {
843 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
844 Guest::tr("Error while reading setup file \"%s\" (To read: %u, Size: %u) from installation medium (%Rrc)"),
845 installerImage.c_str(), cbToRead, cbLength, vrc);
846 }
847 }
848
849 /* Internal progress canceled? */
850 if ( SUCCEEDED(progressCat->COMGETTER(Canceled(&fCanceled)))
851 && fCanceled)
852 {
853 aTask->progress->Cancel();
854 break;
855 }
856 }
857 }
858 }
859 }
860 RTIsoFsClose(&iso);
861
862 if ( SUCCEEDED(rc)
863 && ( SUCCEEDED(aTask->progress->COMGETTER(Canceled(&fCanceled)))
864 && !fCanceled
865 )
866 )
867 {
868 /*
869 * Installer was transferred successfully, so let's start it
870 * (with system rights).
871 */
872 LogRel(("Preparing to execute Guest Additions update ...\n"));
873 aTask->progress->SetCurrentOperationProgress(66);
874
875 /* Prepare command line args for installer. */
876 com::SafeArray<IN_BSTR> installerArgs;
877 com::SafeArray<IN_BSTR> installerEnv;
878
879 /** @todo Only Windows! */
880 installerArgs.push_back(Bstr(strInstallerPath).raw()); /* The actual (internal) installer image (as argv[0]). */
881 /* Note that starting at Windows Vista the lovely session 0 separation applies:
882 * This means that if we run an application with the profile/security context
883 * of VBoxService (system rights!) we're not able to show any UI. */
884 installerArgs.push_back(Bstr("/S").raw()); /* We want to install in silent mode. */
885 installerArgs.push_back(Bstr("/l").raw()); /* ... and logging enabled. */
886 /* Don't quit VBoxService during upgrade because it still is used for this
887 * piece of code we're in right now (that is, here!) ... */
888 installerArgs.push_back(Bstr("/no_vboxservice_exit").raw());
889 /* Tell the installer to report its current installation status
890 * using a running VBoxTray instance via balloon messages in the
891 * Windows taskbar. */
892 installerArgs.push_back(Bstr("/post_installstatus").raw());
893
894 /*
895 * Start the just copied over installer with system rights
896 * in silent mode on the guest. Don't use the hidden flag since there
897 * may be pop ups the user has to process.
898 */
899 ComPtr<IProgress> progressInstaller;
900 ULONG uPID;
901 rc = pGuest->executeProcessInternal(Bstr(strInstallerPath).raw(),
902 ExecuteProcessFlag_WaitForProcessStartOnly,
903 ComSafeArrayAsInParam(installerArgs),
904 ComSafeArrayAsInParam(installerEnv),
905 Bstr("").raw() /* Username */,
906 Bstr("").raw() /* Password */,
907 10 * 1000 /* Wait 10s for getting the process started */,
908 &uPID, progressInstaller.asOutParam(), &vrc);
909 if (SUCCEEDED(rc))
910 {
911 LogRel(("Guest Additions update is running ...\n"));
912
913 /* If the caller does not want to wait for out guest update process to end,
914 * complete the progress object now so that the caller can do other work. */
915 if (aTask->uFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly)
916 aTask->progress->notifyComplete(S_OK);
917 else
918 aTask->progress->SetCurrentOperationProgress(70);
919
920 /* Wait until the Guest Additions installer finishes ... */
921 while ( SUCCEEDED(progressInstaller->COMGETTER(Completed(&fCompleted)))
922 && !fCompleted)
923 {
924 if ( SUCCEEDED(aTask->progress->COMGETTER(Canceled(&fCanceled)))
925 && fCanceled)
926 {
927 progressInstaller->Cancel();
928 break;
929 }
930 /* Progress canceled by Main API? */
931 if ( SUCCEEDED(progressInstaller->COMGETTER(Canceled(&fCanceled)))
932 && fCanceled)
933 {
934 break;
935 }
936 RTThreadSleep(100);
937 }
938
939 ExecuteProcessStatus_T retStatus;
940 ULONG uRetExitCode, uRetFlags;
941 rc = pGuest->GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &retStatus);
942 if (SUCCEEDED(rc))
943 {
944 if (fCompleted)
945 {
946 if (uRetExitCode == 0)
947 {
948 LogRel(("Guest Additions update successful!\n"));
949 if ( SUCCEEDED(aTask->progress->COMGETTER(Completed(&fCompleted)))
950 && !fCompleted)
951 aTask->progress->notifyComplete(S_OK);
952 }
953 else
954 {
955 LogRel(("Guest Additions update failed (Exit code=%u, Status=%u, Flags=%u)\n",
956 uRetExitCode, retStatus, uRetFlags));
957 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
958 Guest::tr("Guest Additions update failed with exit code=%u (status=%u, flags=%u)"),
959 uRetExitCode, retStatus, uRetFlags);
960 }
961 }
962 else if ( SUCCEEDED(progressInstaller->COMGETTER(Canceled(&fCanceled)))
963 && fCanceled)
964 {
965 LogRel(("Guest Additions update was canceled\n"));
966 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
967 Guest::tr("Guest Additions update was canceled by the guest with exit code=%u (status=%u, flags=%u)"),
968 uRetExitCode, retStatus, uRetFlags);
969 }
970 else
971 {
972 LogRel(("Guest Additions update was canceled by the user\n"));
973 }
974 }
975 else
976 rc = GuestTask::setProgressErrorInfo(rc, aTask->progress, pGuest);
977 }
978 else
979 rc = GuestTask::setProgressErrorInfo(rc, aTask->progress, pGuest);
980 }
981 }
982 }
983 catch (HRESULT aRC)
984 {
985 rc = aRC;
986 }
987
988 /* Clean up */
989 aTask->rc = rc;
990
991 LogFlowFunc(("rc=%Rhrc\n", rc));
992 LogFlowFuncLeave();
993
994 return VINF_SUCCESS;
995}
996#endif
997
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