VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestSessionImplTasks.cpp@ 43168

Last change on this file since 43168 was 43162, checked in by vboxsync, 12 years ago

Main/Guest Control 2.0: Cleanup, separated guest error handling.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 57.9 KB
Line 
1
2/* $Id: GuestSessionImplTasks.cpp 43162 2012-09-04 13:53:59Z vboxsync $ */
3/** @file
4 * VirtualBox Main - XXX.
5 */
6
7/*
8 * Copyright (C) 2012 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19
20/*******************************************************************************
21* Header Files *
22*******************************************************************************/
23#include "GuestImpl.h"
24#include "GuestSessionImpl.h"
25#include "GuestCtrlImplPrivate.h"
26
27#include "Global.h"
28#include "AutoCaller.h"
29#include "ConsoleImpl.h"
30#include "MachineImpl.h"
31#include "ProgressImpl.h"
32
33#include <memory> /* For auto_ptr. */
34
35#include <iprt/env.h>
36#include <iprt/file.h> /* For CopyTo/From. */
37
38#ifdef LOG_GROUP
39 #undef LOG_GROUP
40#endif
41#define LOG_GROUP LOG_GROUP_GUEST_CONTROL
42#include <VBox/log.h>
43
44
45/*******************************************************************************
46* Defines *
47*******************************************************************************/
48
49/**
50 * Update file flags.
51 */
52#define UPDATEFILE_FLAG_NONE 0
53/** Copy over the file from host to the
54 * guest. */
55#define UPDATEFILE_FLAG_COPY_FROM_ISO RT_BIT(0)
56/** Execute file on the guest after it has
57 * been successfully transfered. */
58#define UPDATEFILE_FLAG_EXECUTE RT_BIT(7)
59/** File is optional, does not have to be
60 * existent on the .ISO. */
61#define UPDATEFILE_FLAG_OPTIONAL RT_BIT(8)
62
63
64// session task classes
65/////////////////////////////////////////////////////////////////////////////
66
67GuestSessionTask::GuestSessionTask(GuestSession *pSession)
68{
69 mSession = pSession;
70}
71
72GuestSessionTask::~GuestSessionTask(void)
73{
74}
75
76int GuestSessionTask::getGuestProperty(const ComObjPtr<Guest> &pGuest,
77 const Utf8Str &strPath, Utf8Str &strValue)
78{
79 ComObjPtr<Console> pConsole = pGuest->getConsole();
80 const ComPtr<IMachine> pMachine = pConsole->machine();
81
82 Assert(!pMachine.isNull());
83 Bstr strTemp, strFlags;
84 LONG64 i64Timestamp;
85 HRESULT hr = pMachine->GetGuestProperty(Bstr(strPath).raw(),
86 strTemp.asOutParam(),
87 &i64Timestamp, strFlags.asOutParam());
88 if (SUCCEEDED(hr))
89 {
90 strValue = strTemp;
91 return VINF_SUCCESS;
92 }
93 return VERR_NOT_FOUND;
94}
95
96int GuestSessionTask::setProgress(ULONG uPercent)
97{
98 if (mProgress.isNull()) /* Progress is optional. */
99 return VINF_SUCCESS;
100
101 BOOL fCanceled;
102 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
103 && fCanceled)
104 return VERR_CANCELLED;
105 BOOL fCompleted;
106 if ( SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
107 && fCompleted)
108 {
109 AssertMsgFailed(("Setting value of an already completed progress\n"));
110 return VINF_SUCCESS;
111 }
112 HRESULT hr = mProgress->SetCurrentOperationProgress(uPercent);
113 if (FAILED(hr))
114 return VERR_COM_UNEXPECTED;
115
116 return VINF_SUCCESS;
117}
118
119int GuestSessionTask::setProgressSuccess(void)
120{
121 if (mProgress.isNull()) /* Progress is optional. */
122 return VINF_SUCCESS;
123
124 BOOL fCanceled;
125 BOOL fCompleted;
126 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
127 && !fCanceled
128 && SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
129 && !fCompleted)
130 {
131 HRESULT hr = mProgress->notifyComplete(S_OK);
132 if (FAILED(hr))
133 return VERR_COM_UNEXPECTED; /** @todo Find a better rc. */
134 }
135
136 return VINF_SUCCESS;
137}
138
139HRESULT GuestSessionTask::setProgressErrorMsg(HRESULT hr, const Utf8Str &strMsg)
140{
141 if (mProgress.isNull()) /* Progress is optional. */
142 return hr; /* Return original rc. */
143
144 BOOL fCanceled;
145 BOOL fCompleted;
146 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
147 && !fCanceled
148 && SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
149 && !fCompleted)
150 {
151 HRESULT hr2 = mProgress->notifyComplete(hr,
152 COM_IIDOF(IGuestSession),
153 GuestSession::getStaticComponentName(),
154 strMsg.c_str());
155 if (FAILED(hr2))
156 return hr2;
157 }
158 return hr; /* Return original rc. */
159}
160
161SessionTaskCopyTo::SessionTaskCopyTo(GuestSession *pSession,
162 const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags)
163 : GuestSessionTask(pSession),
164 mSource(strSource),
165 mSourceFile(NULL),
166 mSourceOffset(0),
167 mSourceSize(0),
168 mDest(strDest)
169{
170 mCopyFileFlags = uFlags;
171}
172
173/** @todo Merge this and the above call and let the above call do the open/close file handling so that the
174 * inner code only has to deal with file handles. No time now ... */
175SessionTaskCopyTo::SessionTaskCopyTo(GuestSession *pSession,
176 PRTFILE pSourceFile, size_t cbSourceOffset, uint64_t cbSourceSize,
177 const Utf8Str &strDest, uint32_t uFlags)
178 : GuestSessionTask(pSession)
179{
180 mSourceFile = pSourceFile;
181 mSourceOffset = cbSourceOffset;
182 mSourceSize = cbSourceSize;
183 mDest = strDest;
184 mCopyFileFlags = uFlags;
185}
186
187SessionTaskCopyTo::~SessionTaskCopyTo(void)
188{
189
190}
191
192int SessionTaskCopyTo::Run(void)
193{
194 LogFlowThisFuncEnter();
195
196 ComObjPtr<GuestSession> pSession = mSession;
197 Assert(!pSession.isNull());
198
199 AutoCaller autoCaller(pSession);
200 if (FAILED(autoCaller.rc())) return autoCaller.rc();
201
202 if (mCopyFileFlags)
203 {
204 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
205 Utf8StrFmt(GuestSession::tr("Copy flags (%#x) not implemented yet"),
206 mCopyFileFlags));
207 return VERR_INVALID_PARAMETER;
208 }
209
210 int rc;
211
212 RTFILE fileLocal;
213 PRTFILE pFile = &fileLocal;
214
215 if (!mSourceFile)
216 {
217 /* Does our source file exist? */
218 if (!RTFileExists(mSource.c_str()))
219 {
220 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
221 Utf8StrFmt(GuestSession::tr("Source file \"%s\" does not exist or is not a file"),
222 mSource.c_str()));
223 }
224 else
225 {
226 rc = RTFileOpen(pFile, mSource.c_str(),
227 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
228 if (RT_FAILURE(rc))
229 {
230 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
231 Utf8StrFmt(GuestSession::tr("Could not open source file \"%s\" for reading: %Rrc"),
232 mSource.c_str(), rc));
233 }
234 else
235 {
236 rc = RTFileGetSize(*pFile, &mSourceSize);
237 if (RT_FAILURE(rc))
238 {
239 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
240 Utf8StrFmt(GuestSession::tr("Could not query file size of \"%s\": %Rrc"),
241 mSource.c_str(), rc));
242 }
243 }
244 }
245 }
246 else
247 {
248 pFile = mSourceFile;
249 /* Size + offset are optional. */
250 }
251
252 GuestProcessStartupInfo procInfo;
253 procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_CAT);
254 procInfo.mFlags = ProcessCreateFlag_Hidden;
255
256 /* Set arguments.*/
257 procInfo.mArguments.push_back(Utf8StrFmt("--output=%s", mDest.c_str())); /** @todo Do we need path conversion? */
258
259 /* Startup process. */
260 ComObjPtr<GuestProcess> pProcess; int guestRc;
261 rc = pSession->processCreateExInteral(procInfo, pProcess);
262 if (RT_SUCCESS(rc))
263 rc = pProcess->startProcess(&guestRc);
264 if (RT_FAILURE(rc))
265 {
266 switch (rc)
267 {
268 case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
269 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
270 GuestProcess::guestErrorToString(guestRc));
271 break;
272
273 default:
274 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
275 Utf8StrFmt(GuestSession::tr("Error while creating guest process for copying file \"%s\" from guest to host: %Rrc"),
276 mSource.c_str(), rc));
277 break;
278 }
279 }
280
281 if (RT_SUCCESS(rc))
282 {
283 ProcessWaitResult_T waitRes;
284 BYTE byBuf[_64K];
285
286 BOOL fCanceled = FALSE;
287 uint64_t cbWrittenTotal = 0;
288 uint64_t cbToRead = mSourceSize;
289
290 for (;;)
291 {
292 int guestRc;
293 rc = pProcess->waitFor(ProcessWaitForFlag_StdIn,
294 30 * 1000 /* Timeout */, waitRes, &guestRc);
295 if ( RT_FAILURE(rc)
296 || ( waitRes != ProcessWaitResult_StdIn
297 && waitRes != ProcessWaitResult_WaitFlagNotSupported))
298 {
299 break;
300 }
301
302 /* If the guest does not support waiting for stdin, we now yield in
303 * order to reduce the CPU load due to busy waiting. */
304 if (waitRes == ProcessWaitResult_WaitFlagNotSupported)
305 RTThreadSleep(1); /* Optional, don't check rc. */
306
307 size_t cbRead = 0;
308 if (mSourceSize) /* If we have nothing to write, take a shortcut. */
309 {
310 /** @todo Not very efficient, but works for now. */
311 rc = RTFileSeek(*pFile, mSourceOffset + cbWrittenTotal,
312 RTFILE_SEEK_BEGIN, NULL /* poffActual */);
313 if (RT_SUCCESS(rc))
314 {
315 rc = RTFileRead(*pFile, (uint8_t*)byBuf,
316 RT_MIN(cbToRead, sizeof(byBuf)), &cbRead);
317 /*
318 * Some other error occured? There might be a chance that RTFileRead
319 * could not resolve/map the native error code to an IPRT code, so just
320 * print a generic error.
321 */
322 if (RT_FAILURE(rc))
323 {
324 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
325 Utf8StrFmt(GuestSession::tr("Could not read from file \"%s\" (%Rrc)"),
326 mSource.c_str(), rc));
327 break;
328 }
329 }
330 else
331 {
332 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
333 Utf8StrFmt(GuestSession::tr("Seeking file \"%s\" to offset %RU64 failed: %Rrc"),
334 mSource.c_str(), cbWrittenTotal, rc));
335 break;
336 }
337 }
338
339 uint32_t fFlags = ProcessInputFlag_None;
340
341 /* Did we reach the end of the content we want to transfer (last chunk)? */
342 if ( (cbRead < sizeof(byBuf))
343 /* Did we reach the last block which is exactly _64K? */
344 || (cbToRead - cbRead == 0)
345 /* ... or does the user want to cancel? */
346 || ( !mProgress.isNull()
347 && SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
348 && fCanceled)
349 )
350 {
351 LogFlowThisFunc(("Writing last chunk cbRead=%RU64\n", cbRead));
352 fFlags |= ProcessInputFlag_EndOfFile;
353 }
354
355 uint32_t cbWritten;
356 Assert(sizeof(byBuf) >= cbRead);
357 rc = pProcess->writeData(0 /* StdIn */, fFlags,
358 byBuf, cbRead,
359 30 * 1000 /* Timeout */, &cbWritten, &guestRc);
360 if (RT_FAILURE(rc))
361 {
362 switch (rc)
363 {
364 case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
365 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
366 GuestProcess::guestErrorToString(guestRc));
367 break;
368
369 default:
370 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
371 Utf8StrFmt(GuestSession::tr("Writing to file \"%s\" (offset %RU64) failed: %Rrc"),
372 mDest.c_str(), cbWrittenTotal, rc));
373 break;
374 }
375
376 break;
377 }
378
379 /* Only subtract bytes reported written by the guest. */
380 Assert(cbToRead >= cbWritten);
381 cbToRead -= cbWritten;
382
383 /* Update total bytes written to the guest. */
384 cbWrittenTotal += cbWritten;
385 Assert(cbWrittenTotal <= mSourceSize);
386
387 LogFlowThisFunc(("rc=%Rrc, cbWritten=%RU32, cbToRead=%RU64, cbWrittenTotal=%RU64, cbFileSize=%RU64\n",
388 rc, cbWritten, cbToRead, cbWrittenTotal, mSourceSize));
389
390 /* Did the user cancel the operation above? */
391 if (fCanceled)
392 break;
393
394 /* Update the progress.
395 * Watch out for division by zero. */
396 mSourceSize > 0
397 ? rc = setProgress((ULONG)(cbWrittenTotal * 100 / mSourceSize))
398 : rc = setProgress(100);
399 if (RT_FAILURE(rc))
400 break;
401
402 /* End of file reached? */
403 if (!cbToRead)
404 break;
405 } /* for */
406
407 LogFlowThisFunc(("Copy loop ended with rc=%Rrc\n" ,rc));
408
409 if ( !fCanceled
410 || RT_SUCCESS(rc))
411 {
412 /*
413 * Even if we succeeded until here make sure to check whether we really transfered
414 * everything.
415 */
416 if ( mSourceSize > 0
417 && cbWrittenTotal == 0)
418 {
419 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
420 * to the destination -> access denied. */
421 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
422 Utf8StrFmt(GuestSession::tr("Access denied when copying file \"%s\" to \"%s\""),
423 mSource.c_str(), mDest.c_str()));
424 rc = VERR_GENERAL_FAILURE; /* Fudge. */
425 }
426 else if (cbWrittenTotal < mSourceSize)
427 {
428 /* If we did not copy all let the user know. */
429 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
430 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" failed (%RU64/%RU64 bytes transfered)"),
431 mSource.c_str(), cbWrittenTotal, mSourceSize));
432 rc = VERR_GENERAL_FAILURE; /* Fudge. */
433 }
434 else
435 {
436 rc = pProcess->waitFor(ProcessWaitForFlag_Terminate,
437 30 * 1000 /* Timeout */, waitRes, &guestRc);
438 if ( RT_FAILURE(rc)
439 || waitRes != ProcessWaitResult_Terminate)
440 {
441 if (RT_FAILURE(rc))
442 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
443 Utf8StrFmt(GuestSession::tr("Waiting on termination for copying file \"%s\" failed: %Rrc"),
444 mSource.c_str(), rc));
445 else
446 {
447 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
448 Utf8StrFmt(GuestSession::tr("Waiting on termination for copying file \"%s\" failed with wait result %ld"),
449 mSource.c_str(), waitRes));
450 rc = VERR_GENERAL_FAILURE; /* Fudge. */
451 }
452 }
453
454 if (RT_SUCCESS(rc))
455 {
456 ProcessStatus_T procStatus;
457 LONG exitCode;
458 if ( ( SUCCEEDED(pProcess->COMGETTER(Status(&procStatus)))
459 && procStatus != ProcessStatus_TerminatedNormally)
460 || ( SUCCEEDED(pProcess->COMGETTER(ExitCode(&exitCode)))
461 && exitCode != 0)
462 )
463 {
464 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
465 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" failed with status %ld, exit code %ld"),
466 mSource.c_str(), procStatus, exitCode)); /**@todo Add stringify methods! */
467 rc = VERR_GENERAL_FAILURE; /* Fudge. */
468 }
469 }
470
471 if (RT_SUCCESS(rc))
472 rc = setProgressSuccess();
473 }
474 }
475
476 if (!pProcess.isNull())
477 pProcess->uninit();
478 } /* processCreateExInteral */
479
480 if (!mSourceFile) /* Only close locally opened files. */
481 RTFileClose(*pFile);
482
483 LogFlowFuncLeaveRC(rc);
484 return rc;
485}
486
487int SessionTaskCopyTo::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
488{
489 LogFlowThisFunc(("strDesc=%s, strSource=%s, strDest=%s, mCopyFileFlags=%x\n",
490 strDesc.c_str(), mSource.c_str(), mDest.c_str(), mCopyFileFlags));
491
492 mDesc = strDesc;
493 mProgress = pProgress;
494
495 int rc = RTThreadCreate(NULL, SessionTaskCopyTo::taskThread, this,
496 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
497 "gctlCpyTo");
498 LogFlowFuncLeaveRC(rc);
499 return rc;
500}
501
502/* static */
503int SessionTaskCopyTo::taskThread(RTTHREAD Thread, void *pvUser)
504{
505 std::auto_ptr<SessionTaskCopyTo> task(static_cast<SessionTaskCopyTo*>(pvUser));
506 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
507
508 LogFlowFunc(("pTask=%p\n", task.get()));
509 return task->Run();
510}
511
512SessionTaskCopyFrom::SessionTaskCopyFrom(GuestSession *pSession,
513 const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags)
514 : GuestSessionTask(pSession)
515{
516 mSource = strSource;
517 mDest = strDest;
518 mFlags = uFlags;
519}
520
521SessionTaskCopyFrom::~SessionTaskCopyFrom(void)
522{
523
524}
525
526int SessionTaskCopyFrom::Run(void)
527{
528 LogFlowThisFuncEnter();
529
530 ComObjPtr<GuestSession> pSession = mSession;
531 Assert(!pSession.isNull());
532
533 AutoCaller autoCaller(pSession);
534 if (FAILED(autoCaller.rc())) return autoCaller.rc();
535
536 /*
537 * Note: There will be races between querying file size + reading the guest file's
538 * content because we currently *do not* lock down the guest file when doing the
539 * actual operations.
540 ** @todo Implement guest file locking!
541 */
542 GuestFsObjData objData; int guestRc;
543 int rc = pSession->fileQueryInfoInternal(Utf8Str(mSource), objData, &guestRc);
544 if (RT_FAILURE(rc))
545 {
546 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
547 Utf8StrFmt(GuestSession::tr("Querying guest file information for \"%s\" failed: %Rrc"),
548 mSource.c_str(), rc));
549 }
550 else if (objData.mType != FsObjType_File) /* Only single files are supported at the moment. */
551 {
552 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
553 Utf8StrFmt(GuestSession::tr("Object \"%s\" on the guest is not a file"), mSource.c_str()));
554 }
555
556 if (RT_SUCCESS(rc))
557 {
558 RTFILE fileDest;
559 rc = RTFileOpen(&fileDest, mDest.c_str(),
560 RTFILE_O_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE); /** @todo Use the correct open modes! */
561 if (RT_FAILURE(rc))
562 {
563 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
564 Utf8StrFmt(GuestSession::tr("Error opening destination file \"%s\": %Rrc"),
565 mDest.c_str(), rc));
566 }
567 else
568 {
569 GuestProcessStartupInfo procInfo;
570 procInfo.mName = Utf8StrFmt(GuestSession::tr("Copying file \"%s\" from guest to the host to \"%s\" (%RI64 bytes)"),
571 mSource.c_str(), mDest.c_str(), objData.mObjectSize);
572 procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_CAT);
573 procInfo.mFlags = ProcessCreateFlag_Hidden | ProcessCreateFlag_WaitForStdOut;
574
575 /* Set arguments.*/
576 procInfo.mArguments.push_back(mSource); /* Which file to output? */
577
578 /* Startup process. */
579 ComObjPtr<GuestProcess> pProcess; int guestRc;
580 rc = pSession->processCreateExInteral(procInfo, pProcess);
581 if (RT_SUCCESS(rc))
582 rc = pProcess->startProcess(&guestRc);
583 if (RT_FAILURE(rc))
584 {
585 switch (rc)
586 {
587 case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
588 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
589 GuestProcess::guestErrorToString(guestRc));
590 break;
591
592 default:
593 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
594 Utf8StrFmt(GuestSession::tr("Error while creating guest process for copying file \"%s\" from guest to host: %Rrc"),
595 mSource.c_str(), rc));
596 break;
597 }
598 }
599 else
600 {
601 ProcessWaitResult_T waitRes;
602 BYTE byBuf[_64K];
603
604 BOOL fCanceled = FALSE;
605 uint64_t cbWrittenTotal = 0;
606 uint64_t cbToRead = objData.mObjectSize;
607
608 int guestRc;
609
610 for (;;)
611 {
612 rc = pProcess->waitFor(ProcessWaitForFlag_StdOut,
613 30 * 1000 /* Timeout */, waitRes, &guestRc);
614 if (RT_FAILURE(rc))
615 {
616 switch (rc)
617 {
618 case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
619 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
620 GuestProcess::guestErrorToString(guestRc));
621 break;
622
623 default:
624 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
625 Utf8StrFmt(GuestSession::tr("Error while creating guest process for copying file \"%s\" from guest to host: %Rrc"),
626 mSource.c_str(), rc));
627 break;
628 }
629
630 break;
631 }
632
633 if ( waitRes == ProcessWaitResult_StdOut
634 || waitRes == ProcessWaitResult_WaitFlagNotSupported)
635 {
636 /* If the guest does not support waiting for stdin, we now yield in
637 * order to reduce the CPU load due to busy waiting. */
638 if (waitRes == ProcessWaitResult_WaitFlagNotSupported)
639 RTThreadSleep(1); /* Optional, don't check rc. */
640
641 size_t cbRead; int guestRc;
642 rc = pProcess->readData(OUTPUT_HANDLE_ID_STDOUT, sizeof(byBuf),
643 30 * 1000 /* Timeout */, byBuf, sizeof(byBuf),
644 &cbRead, &guestRc);
645 if (RT_FAILURE(rc))
646 {
647 switch (rc)
648 {
649 case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
650 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
651 GuestProcess::guestErrorToString(guestRc));
652 break;
653
654 default:
655 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
656 Utf8StrFmt(GuestSession::tr("Reading from file \"%s\" (offset %RU64) failed: %Rrc"),
657 mSource.c_str(), cbWrittenTotal, rc));
658 break;
659 }
660
661 break;
662 }
663
664 if (cbRead)
665 {
666 rc = RTFileWrite(fileDest, byBuf, cbRead, NULL /* No partial writes */);
667 if (RT_FAILURE(rc))
668 {
669 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
670 Utf8StrFmt(GuestSession::tr("Error writing to file \"%s\" (%RU64 bytes left): %Rrc"),
671 mDest.c_str(), cbToRead, rc));
672 break;
673 }
674
675 /* Only subtract bytes reported written by the guest. */
676 Assert(cbToRead >= cbRead);
677 cbToRead -= cbRead;
678
679 /* Update total bytes written to the guest. */
680 cbWrittenTotal += cbRead;
681 Assert(cbWrittenTotal <= (uint64_t)objData.mObjectSize);
682
683 /* Did the user cancel the operation above? */
684 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
685 && fCanceled)
686 break;
687
688 rc = setProgress((ULONG)(cbWrittenTotal / ((uint64_t)objData.mObjectSize / 100.0)));
689 if (RT_FAILURE(rc))
690 break;
691 }
692 }
693 else
694 {
695 break;
696 }
697
698 } /* for */
699
700 LogFlowThisFunc(("rc=%Rrc, guestrc=%Rrc, waitRes=%ld, cbWrittenTotal=%RU64, cbSize=%RI64, cbToRead=%RU64\n",
701 rc, guestRc, waitRes, cbWrittenTotal, objData.mObjectSize, cbToRead));
702
703 if ( !fCanceled
704 || RT_SUCCESS(rc))
705 {
706 /*
707 * Even if we succeeded until here make sure to check whether we really transfered
708 * everything.
709 */
710 if ( objData.mObjectSize > 0
711 && cbWrittenTotal == 0)
712 {
713 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
714 * to the destination -> access denied. */
715 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
716 Utf8StrFmt(GuestSession::tr("Access denied when copying file \"%s\" to \"%s\""),
717 mSource.c_str(), mDest.c_str()));
718 rc = VERR_GENERAL_FAILURE; /* Fudge. */
719 }
720 else if (cbWrittenTotal < (uint64_t)objData.mObjectSize)
721 {
722 /* If we did not copy all let the user know. */
723 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
724 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" failed (%RU64/%RI64 bytes transfered)"),
725 mSource.c_str(), cbWrittenTotal, objData.mObjectSize));
726 rc = VERR_GENERAL_FAILURE; /* Fudge. */
727 }
728 else
729 {
730 ProcessStatus_T procStatus;
731 LONG exitCode;
732 if ( ( SUCCEEDED(pProcess->COMGETTER(Status(&procStatus)))
733 && procStatus != ProcessStatus_TerminatedNormally)
734 || ( SUCCEEDED(pProcess->COMGETTER(ExitCode(&exitCode)))
735 && exitCode != 0)
736 )
737 {
738 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
739 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" failed with status %ld, exit code %d"),
740 mSource.c_str(), procStatus, exitCode)); /**@todo Add stringify methods! */
741 rc = VERR_GENERAL_FAILURE; /* Fudge. */
742 }
743 else /* Yay, success! */
744 rc = setProgressSuccess();
745 }
746 }
747
748 if (!pProcess.isNull())
749 pProcess->uninit();
750 }
751
752 RTFileClose(fileDest);
753 }
754 }
755
756 LogFlowFuncLeaveRC(rc);
757 return rc;
758}
759
760int SessionTaskCopyFrom::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
761{
762 LogFlowThisFunc(("strDesc=%s, strSource=%s, strDest=%s, uFlags=%x\n",
763 strDesc.c_str(), mSource.c_str(), mDest.c_str(), mFlags));
764
765 mDesc = strDesc;
766 mProgress = pProgress;
767
768 int rc = RTThreadCreate(NULL, SessionTaskCopyFrom::taskThread, this,
769 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
770 "gctlCpyFrom");
771 LogFlowFuncLeaveRC(rc);
772 return rc;
773}
774
775/* static */
776int SessionTaskCopyFrom::taskThread(RTTHREAD Thread, void *pvUser)
777{
778 std::auto_ptr<SessionTaskCopyFrom> task(static_cast<SessionTaskCopyFrom*>(pvUser));
779 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
780
781 LogFlowFunc(("pTask=%p\n", task.get()));
782 return task->Run();
783}
784
785SessionTaskUpdateAdditions::SessionTaskUpdateAdditions(GuestSession *pSession,
786 const Utf8Str &strSource, uint32_t uFlags)
787 : GuestSessionTask(pSession)
788{
789 mSource = strSource;
790 mFlags = uFlags;
791}
792
793SessionTaskUpdateAdditions::~SessionTaskUpdateAdditions(void)
794{
795
796}
797
798int SessionTaskUpdateAdditions::copyFileToGuest(GuestSession *pSession, PRTISOFSFILE pISO,
799 Utf8Str const &strFileSource, const Utf8Str &strFileDest,
800 bool fOptional, uint32_t *pcbSize)
801{
802 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
803 AssertPtrReturn(pISO, VERR_INVALID_POINTER);
804 /* pcbSize is optional. */
805
806 uint32_t cbOffset;
807 size_t cbSize;
808
809 int rc = RTIsoFsGetFileInfo(pISO, strFileSource.c_str(), &cbOffset, &cbSize);
810 if (RT_FAILURE(rc))
811 {
812 if (fOptional)
813 return VINF_SUCCESS;
814
815 return rc;
816 }
817
818 Assert(cbOffset);
819 Assert(cbSize);
820 rc = RTFileSeek(pISO->file, cbOffset, RTFILE_SEEK_BEGIN, NULL);
821
822 /* Copy over the Guest Additions file to the guest. */
823 if (RT_SUCCESS(rc))
824 {
825 LogFlowThisFunc(("Copying Guest Additions installer file \"%s\" to \"%s\" on guest ...\n",
826 strFileSource.c_str(), strFileDest.c_str()));
827
828 if (RT_SUCCESS(rc))
829 {
830 SessionTaskCopyTo *pTask = new SessionTaskCopyTo(pSession /* GuestSession */,
831 &pISO->file, cbOffset, cbSize,
832 strFileDest, CopyFileFlag_None);
833 AssertPtrReturn(pTask, VERR_NO_MEMORY);
834
835 ComObjPtr<Progress> pProgressCopyTo;
836 rc = pSession->startTaskAsync(Utf8StrFmt(GuestSession::tr("Copying Guest Additions installer file \"%s\" to \"%s\" on guest"),
837 mSource.c_str(), strFileDest.c_str()),
838 pTask, pProgressCopyTo);
839 if (RT_SUCCESS(rc))
840 {
841 BOOL fCanceled = FALSE;
842 HRESULT hr = pProgressCopyTo->WaitForCompletion(-1);
843 if ( SUCCEEDED(pProgressCopyTo->COMGETTER(Canceled)(&fCanceled))
844 && fCanceled)
845 {
846 rc = VERR_GENERAL_FAILURE; /* Fudge. */
847 }
848 else if (FAILED(hr))
849 {
850 Assert(FAILED(hr));
851 rc = VERR_GENERAL_FAILURE; /* Fudge. */
852 }
853 }
854 }
855 }
856
857 /** @todo Note: Since there is no file locking involved at the moment, there can be modifications
858 * between finished copying, the verification and the actual execution. */
859
860 /* Determine where the installer image ended up and if it has the correct size. */
861 if (RT_SUCCESS(rc))
862 {
863 LogFlowThisFunc(("Verifying Guest Additions installer file \"%s\" ...\n",
864 strFileDest.c_str()));
865
866 GuestFsObjData objData;
867 int64_t cbSizeOnGuest; int guestRc;
868 rc = pSession->fileQuerySizeInternal(strFileDest, &cbSizeOnGuest, &guestRc);
869 if ( RT_SUCCESS(rc)
870 && cbSize == (uint64_t)cbSizeOnGuest)
871 {
872 LogFlowThisFunc(("Guest Additions installer file \"%s\" successfully verified\n",
873 strFileDest.c_str()));
874 }
875 else
876 {
877 if (RT_SUCCESS(rc)) /* Size does not match. */
878 {
879 LogFlowThisFunc(("Size of Guest Additions installer file \"%s\" does not match: %RI64bytes copied, %RU64bytes expected\n",
880 strFileDest.c_str(), cbSizeOnGuest, cbSize));
881 rc = VERR_BROKEN_PIPE; /** @todo Find a better error. */
882 }
883 else
884 {
885 switch (rc)
886 {
887 case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
888 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
889 GuestProcess::guestErrorToString(guestRc));
890 break;
891
892 default:
893 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
894 Utf8StrFmt(GuestSession::tr("Error while querying size for file \"%s\": %Rrc"),
895 strFileDest.c_str(), rc));
896 break;
897 }
898 }
899 }
900
901 if (RT_SUCCESS(rc))
902 {
903 if (pcbSize)
904 *pcbSize = cbSizeOnGuest;
905 }
906 }
907
908 return rc;
909}
910
911int SessionTaskUpdateAdditions::runFileOnGuest(GuestSession *pSession, GuestProcessStartupInfo &procInfo)
912{
913 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
914
915 LogRel(("Running %s ...\n", procInfo.mName.c_str()));
916
917 LONG exitCode;
918 GuestProcessTool procTool; int guestRc;
919 int vrc = procTool.Init(pSession, procInfo, false /* Async */, &guestRc);
920 if (RT_SUCCESS(vrc))
921 {
922 if (RT_SUCCESS(guestRc))
923 vrc = procTool.Wait(GUESTPROCESSTOOL_FLAG_NONE, &guestRc);
924 if (RT_SUCCESS(vrc))
925 vrc = procTool.TerminatedOk(&exitCode);
926 }
927
928 if (RT_FAILURE(vrc))
929 {
930 switch (vrc)
931 {
932 case VERR_NOT_EQUAL: /** @todo Special guest control rc needed! */
933 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
934 Utf8StrFmt(GuestSession::tr("Running update file \"%s\" on guest terminated with exit code %ld"),
935 procInfo.mCommand.c_str(), exitCode));
936 break;
937
938 case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
939 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
940 GuestProcess::guestErrorToString(vrc));
941 break;
942
943 default:
944 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
945 Utf8StrFmt(GuestSession::tr("Error while running update file \"%s\" on guest: %Rrc"),
946 procInfo.mCommand.c_str(), vrc));
947 break;
948 }
949 }
950
951 return vrc;
952}
953
954int SessionTaskUpdateAdditions::Run(void)
955{
956 LogFlowThisFuncEnter();
957
958 ComObjPtr<GuestSession> pSession = mSession;
959 Assert(!pSession.isNull());
960
961 AutoCaller autoCaller(pSession);
962 if (FAILED(autoCaller.rc())) return autoCaller.rc();
963
964 int rc = setProgress(10);
965 if (RT_FAILURE(rc))
966 return rc;
967
968 HRESULT hr = S_OK;
969
970 LogRel(("Automatic update of Guest Additions started, using \"%s\"\n", mSource.c_str()));
971
972 ComObjPtr<Guest> pGuest(mSession->getParent());
973#if 0
974 /*
975 * Wait for the guest being ready within 30 seconds.
976 */
977 AdditionsRunLevelType_T addsRunLevel;
978 uint64_t tsStart = RTTimeSystemMilliTS();
979 while ( SUCCEEDED(hr = pGuest->COMGETTER(AdditionsRunLevel)(&addsRunLevel))
980 && ( addsRunLevel != AdditionsRunLevelType_Userland
981 && addsRunLevel != AdditionsRunLevelType_Desktop))
982 {
983 if ((RTTimeSystemMilliTS() - tsStart) > 30 * 1000)
984 {
985 rc = VERR_TIMEOUT;
986 break;
987 }
988
989 RTThreadSleep(100); /* Wait a bit. */
990 }
991
992 if (FAILED(hr)) rc = VERR_TIMEOUT;
993 if (rc == VERR_TIMEOUT)
994 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
995 Utf8StrFmt(GuestSession::tr("Guest Additions were not ready within time, giving up")));
996#else
997 /*
998 * For use with the GUI we don't want to wait, just return so that the manual .ISO mounting
999 * can continue.
1000 */
1001 AdditionsRunLevelType_T addsRunLevel;
1002 if ( FAILED(hr = pGuest->COMGETTER(AdditionsRunLevel)(&addsRunLevel))
1003 || ( addsRunLevel != AdditionsRunLevelType_Userland
1004 && addsRunLevel != AdditionsRunLevelType_Desktop))
1005 {
1006 if (addsRunLevel == AdditionsRunLevelType_System)
1007 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1008 Utf8StrFmt(GuestSession::tr("Guest Additions are installed but not fully loaded yet, aborting automatic update")));
1009 else
1010 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1011 Utf8StrFmt(GuestSession::tr("Guest Additions not installed or ready, aborting automatic update")));
1012 rc = VERR_NOT_SUPPORTED;
1013 }
1014#endif
1015
1016 if (RT_SUCCESS(rc))
1017 {
1018 /*
1019 * Determine if we are able to update automatically. This only works
1020 * if there are recent Guest Additions installed already.
1021 */
1022 Utf8Str strAddsVer;
1023 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/Version", strAddsVer);
1024 if ( RT_SUCCESS(rc)
1025 && RTStrVersionCompare(strAddsVer.c_str(), "4.1") < 0)
1026 {
1027 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1028 Utf8StrFmt(GuestSession::tr("Guest has too old Guest Additions (%s) installed for automatic updating, please update manually"),
1029 strAddsVer.c_str()));
1030 rc = VERR_NOT_SUPPORTED;
1031 }
1032 }
1033
1034 Utf8Str strOSVer;
1035 eOSType osType;
1036 if (RT_SUCCESS(rc))
1037 {
1038 /*
1039 * Determine guest OS type and the required installer image.
1040 */
1041 Utf8Str strOSType;
1042 rc = getGuestProperty(pGuest, "/VirtualBox/GuestInfo/OS/Product", strOSType);
1043 if (RT_SUCCESS(rc))
1044 {
1045 if ( strOSType.contains("Microsoft", Utf8Str::CaseInsensitive)
1046 || strOSType.contains("Windows", Utf8Str::CaseInsensitive))
1047 {
1048 osType = eOSType_Windows;
1049
1050 /*
1051 * Determine guest OS version.
1052 */
1053 rc = getGuestProperty(pGuest, "/VirtualBox/GuestInfo/OS/Release", strOSVer);
1054 if (RT_FAILURE(rc))
1055 {
1056 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1057 Utf8StrFmt(GuestSession::tr("Unable to detected guest OS version, please update manually")));
1058 rc = VERR_NOT_SUPPORTED;
1059 }
1060
1061 /* Because Windows 2000 + XP and is bitching with WHQL popups even if we have signed drivers we
1062 * can't do automated updates here. */
1063 /* Windows XP 64-bit (5.2) is a Windows 2003 Server actually, so skip this here. */
1064 if ( RT_SUCCESS(rc)
1065 && ( strOSVer.startsWith("5.0") /* Exclude the build number. */
1066 || strOSVer.startsWith("5.1")) /* Exclude the build number. */
1067 )
1068 {
1069 /* If we don't have AdditionsUpdateFlag_WaitForUpdateStartOnly set we can't continue
1070 * because the Windows Guest Additions installer will fail because of WHQL popups. If the
1071 * flag is set this update routine ends successfully as soon as the installer was started
1072 * (and the user has to deal with it in the guest). */
1073 if (!(mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly))
1074 {
1075 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1076 Utf8StrFmt(GuestSession::tr("Windows 2000 and XP are not supported for automatic updating due to WHQL interaction, please update manually")));
1077 rc = VERR_NOT_SUPPORTED;
1078 }
1079 }
1080 }
1081 else if (strOSType.contains("Solaris", Utf8Str::CaseInsensitive))
1082 {
1083 osType = eOSType_Solaris;
1084 }
1085 else /* Everything else hopefully means Linux :-). */
1086 osType = eOSType_Linux;
1087
1088#if 1 /* Only Windows is supported (and tested) at the moment. */
1089 if (osType != eOSType_Windows)
1090 {
1091 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1092 Utf8StrFmt(GuestSession::tr("Detected guest OS (%s) does not support automatic Guest Additions updating, please update manually"),
1093 strOSType.c_str()));
1094 rc = VERR_NOT_SUPPORTED;
1095 }
1096#endif
1097 }
1098 }
1099
1100 RTISOFSFILE iso;
1101 if (RT_SUCCESS(rc))
1102 {
1103 /*
1104 * Try to open the .ISO file to extract all needed files.
1105 */
1106 rc = RTIsoFsOpen(&iso, mSource.c_str());
1107 if (RT_FAILURE(rc))
1108 {
1109 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1110 Utf8StrFmt(GuestSession::tr("Unable to open Guest Additions .ISO file \"%s\": %Rrc"),
1111 mSource.c_str(), rc));
1112 }
1113 else
1114 {
1115 /* Set default installation directories. */
1116 Utf8Str strUpdateDir = "/tmp/";
1117 if (osType == eOSType_Windows)
1118 strUpdateDir = "C:\\Temp\\";
1119
1120 rc = setProgress(5);
1121
1122 /* Try looking up the Guest Additions installation directory. */
1123 if (RT_SUCCESS(rc))
1124 {
1125 /* Try getting the installed Guest Additions version to know whether we
1126 * can install our temporary Guest Addition data into the original installation
1127 * directory.
1128 *
1129 * Because versions prior to 4.2 had bugs wrt spaces in paths we have to choose
1130 * a different location then.
1131 */
1132 bool fUseInstallDir = false;
1133
1134 Utf8Str strAddsVer;
1135 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/Version", strAddsVer);
1136 if ( RT_SUCCESS(rc)
1137 && RTStrVersionCompare(strAddsVer.c_str(), "4.2r80329") > 0)
1138 {
1139 fUseInstallDir = true;
1140 }
1141
1142 if (fUseInstallDir)
1143 {
1144 if (RT_SUCCESS(rc))
1145 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/InstallDir", strUpdateDir);
1146 if (RT_SUCCESS(rc))
1147 {
1148 if (osType == eOSType_Windows)
1149 {
1150 strUpdateDir.findReplace('/', '\\');
1151 strUpdateDir.append("\\Update\\");
1152 }
1153 else
1154 strUpdateDir.append("/update/");
1155 }
1156 }
1157 }
1158
1159 if (RT_SUCCESS(rc))
1160 LogRel(("Guest Additions update directory is: %s\n",
1161 strUpdateDir.c_str()));
1162
1163 /* Create the installation directory. */
1164 int guestRc;
1165 rc = pSession->directoryCreateInternal(strUpdateDir,
1166 755 /* Mode */, DirectoryCreateFlag_Parents, &guestRc);
1167 if (RT_FAILURE(rc))
1168 {
1169 switch (rc)
1170 {
1171 case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
1172 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1173 GuestProcess::guestErrorToString(guestRc));
1174 break;
1175
1176 default:
1177 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1178 Utf8StrFmt(GuestSession::tr("Error creating installation directory \"%s\" on the guest: %Rrc"),
1179 strUpdateDir.c_str(), rc));
1180 break;
1181 }
1182 }
1183 if (RT_SUCCESS(rc))
1184 rc = setProgress(10);
1185
1186 if (RT_SUCCESS(rc))
1187 {
1188 /* Prepare the file(s) we want to copy over to the guest and
1189 * (maybe) want to run. */
1190 switch (osType)
1191 {
1192 case eOSType_Windows:
1193 {
1194 /* Do we need to install our certificates? We do this for W2K and up. */
1195 bool fInstallCert = false;
1196
1197 /* Only Windows 2000 and up need certificates to be installed. */
1198 if (RTStrVersionCompare(strOSVer.c_str(), "5.0") >= 0)
1199 {
1200 fInstallCert = true;
1201 LogRel(("Certificates for auto updating WHQL drivers will be installed\n"));
1202 }
1203 else
1204 LogRel(("Skipping installation of certificates for WHQL drivers\n"));
1205
1206 if (fInstallCert)
1207 {
1208 /* Our certificate. */
1209 mFiles.push_back(InstallerFile("CERT/ORACLE_VBOX.CER",
1210 strUpdateDir + "oracle-vbox.cer",
1211 UPDATEFILE_FLAG_COPY_FROM_ISO | UPDATEFILE_FLAG_OPTIONAL));
1212 /* Our certificate installation utility. */
1213 /* First pass: Copy over the file + execute it to remove any existing
1214 * VBox certificates. */
1215 GuestProcessStartupInfo siCertUtilRem;
1216 siCertUtilRem.mName = "VirtualBox Certificate Utility, removing old VirtualBox certificates";
1217 siCertUtilRem.mArguments.push_back(Utf8Str("remove-trusted-publisher"));
1218 siCertUtilRem.mArguments.push_back(Utf8Str("--root")); /* Add root certificate as well. */
1219 siCertUtilRem.mArguments.push_back(Utf8Str(strUpdateDir + "oracle-vbox.cer"));
1220 siCertUtilRem.mArguments.push_back(Utf8Str(strUpdateDir + "oracle-vbox.cer"));
1221 mFiles.push_back(InstallerFile("CERT/VBOXCERTUTIL.EXE",
1222 strUpdateDir + "VBoxCertUtil.exe",
1223 UPDATEFILE_FLAG_COPY_FROM_ISO | UPDATEFILE_FLAG_EXECUTE | UPDATEFILE_FLAG_OPTIONAL,
1224 siCertUtilRem));
1225 /* Second pass: Only execute (but don't copy) again, this time installng the
1226 * recent certificates just copied over. */
1227 GuestProcessStartupInfo siCertUtilAdd;
1228 siCertUtilAdd.mName = "VirtualBox Certificate Utility, installing VirtualBox certificates";
1229 siCertUtilAdd.mArguments.push_back(Utf8Str("add-trusted-publisher"));
1230 siCertUtilAdd.mArguments.push_back(Utf8Str("--root")); /* Add root certificate as well. */
1231 siCertUtilAdd.mArguments.push_back(Utf8Str(strUpdateDir + "oracle-vbox.cer"));
1232 siCertUtilAdd.mArguments.push_back(Utf8Str(strUpdateDir + "oracle-vbox.cer"));
1233 mFiles.push_back(InstallerFile("CERT/VBOXCERTUTIL.EXE",
1234 strUpdateDir + "VBoxCertUtil.exe",
1235 UPDATEFILE_FLAG_EXECUTE | UPDATEFILE_FLAG_OPTIONAL,
1236 siCertUtilAdd));
1237 }
1238 /* The installers in different flavors, as we don't know (and can't assume)
1239 * the guest's bitness. */
1240 mFiles.push_back(InstallerFile("VBOXWINDOWSADDITIONS_X86.EXE",
1241 strUpdateDir + "VBoxWindowsAdditions-x86.exe",
1242 UPDATEFILE_FLAG_COPY_FROM_ISO));
1243 mFiles.push_back(InstallerFile("VBOXWINDOWSADDITIONS_AMD64.EXE",
1244 strUpdateDir + "VBoxWindowsAdditions-amd64.exe",
1245 UPDATEFILE_FLAG_COPY_FROM_ISO));
1246 /* The stub loader which decides which flavor to run. */
1247 GuestProcessStartupInfo siInstaller;
1248 siInstaller.mName = "VirtualBox Windows Guest Additions Installer";
1249 /* Set a running timeout of 5 minutes -- the Windows Guest Additions
1250 * setup can take quite a while, so be on the safe side. */
1251 siInstaller.mTimeoutMS = 5 * 60 * 1000;
1252 siInstaller.mArguments.push_back(Utf8Str("/S")); /* We want to install in silent mode. */
1253 siInstaller.mArguments.push_back(Utf8Str("/l")); /* ... and logging enabled. */
1254 /* Don't quit VBoxService during upgrade because it still is used for this
1255 * piece of code we're in right now (that is, here!) ... */
1256 siInstaller.mArguments.push_back(Utf8Str("/no_vboxservice_exit"));
1257 /* Tell the installer to report its current installation status
1258 * using a running VBoxTray instance via balloon messages in the
1259 * Windows taskbar. */
1260 siInstaller.mArguments.push_back(Utf8Str("/post_installstatus"));
1261 /* If the caller does not want to wait for out guest update process to end,
1262 * complete the progress object now so that the caller can do other work. */
1263 if (mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly)
1264 siInstaller.mFlags |= ProcessCreateFlag_WaitForProcessStartOnly;
1265 mFiles.push_back(InstallerFile("VBOXWINDOWSADDITIONS.EXE",
1266 strUpdateDir + "VBoxWindowsAdditions.exe",
1267 UPDATEFILE_FLAG_COPY_FROM_ISO | UPDATEFILE_FLAG_EXECUTE, siInstaller));
1268 break;
1269 }
1270 case eOSType_Linux:
1271 /** @todo Add Linux support. */
1272 break;
1273 case eOSType_Solaris:
1274 /** @todo Add Solaris support. */
1275 break;
1276 default:
1277 AssertReleaseMsgFailed(("Unsupported guest type: %d\n", osType));
1278 break;
1279 }
1280 }
1281
1282 if (RT_SUCCESS(rc))
1283 {
1284 /* We want to spend 40% total for all copying operations. So roughly
1285 * calculate the specific percentage step of each copied file. */
1286 uint8_t uOffset = 20; /* Start at 20%. */
1287 uint8_t uStep = 40 / mFiles.size();
1288
1289 LogRel(("Copying over Guest Additions update files to the guest ...\n"));
1290
1291 std::vector<InstallerFile>::const_iterator itFiles = mFiles.begin();
1292 while (itFiles != mFiles.end())
1293 {
1294 if (itFiles->fFlags & UPDATEFILE_FLAG_COPY_FROM_ISO)
1295 {
1296 bool fOptional = false;
1297 if (itFiles->fFlags & UPDATEFILE_FLAG_OPTIONAL)
1298 fOptional = true;
1299 rc = copyFileToGuest(pSession, &iso, itFiles->strSource, itFiles->strDest,
1300 fOptional, NULL /* cbSize */);
1301 if (RT_FAILURE(rc))
1302 {
1303 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1304 Utf8StrFmt(GuestSession::tr("Error while copying file \"%s\" to \"%s\" on the guest: %Rrc"),
1305 itFiles->strSource.c_str(), itFiles->strDest.c_str(), rc));
1306 break;
1307 }
1308 }
1309
1310 rc = setProgress(uOffset);
1311 if (RT_FAILURE(rc))
1312 break;
1313 uOffset += uStep;
1314
1315 itFiles++;
1316 }
1317 }
1318
1319 /* Done copying, close .ISO file. */
1320 RTIsoFsClose(&iso);
1321
1322 if (RT_SUCCESS(rc))
1323 {
1324 /* We want to spend 35% total for all copying operations. So roughly
1325 * calculate the specific percentage step of each copied file. */
1326 uint8_t uOffset = 60; /* Start at 60%. */
1327 uint8_t uStep = 35 / mFiles.size();
1328
1329 LogRel(("Executing Guest Additions update files ...\n"));
1330
1331 std::vector<InstallerFile>::iterator itFiles = mFiles.begin();
1332 while (itFiles != mFiles.end())
1333 {
1334 if (itFiles->fFlags & UPDATEFILE_FLAG_EXECUTE)
1335 {
1336 rc = runFileOnGuest(pSession, itFiles->mProcInfo);
1337 if (RT_FAILURE(rc))
1338 break;
1339 }
1340
1341 rc = setProgress(uOffset);
1342 if (RT_FAILURE(rc))
1343 break;
1344 uOffset += uStep;
1345
1346 itFiles++;
1347 }
1348 }
1349
1350 if (RT_SUCCESS(rc))
1351 {
1352 LogRel(("Automatic update of Guest Additions succeeded\n"));
1353 rc = setProgressSuccess();
1354 }
1355 }
1356 }
1357
1358 if (RT_FAILURE(rc))
1359 {
1360 if (rc == VERR_CANCELLED)
1361 {
1362 LogRel(("Automatic update of Guest Additions was canceled\n"));
1363
1364 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1365 Utf8StrFmt(GuestSession::tr("Installation was canceled")));
1366 }
1367 else
1368 {
1369 Utf8Str strError = Utf8StrFmt("No further error information available (%Rrc)", rc);
1370 if (!mProgress.isNull()) /* Progress object is optional. */
1371 {
1372 ComPtr<IVirtualBoxErrorInfo> pError;
1373 hr = mProgress->COMGETTER(ErrorInfo)(pError.asOutParam());
1374 Assert(!pError.isNull());
1375 if (SUCCEEDED(hr))
1376 {
1377 Bstr strVal;
1378 hr = pError->COMGETTER(Text)(strVal.asOutParam());
1379 if ( SUCCEEDED(hr)
1380 && strVal.isNotEmpty())
1381 strError = strVal;
1382 }
1383 }
1384
1385 LogRel(("Automatic update of Guest Additions failed: %s\n", strError.c_str()));
1386 }
1387
1388 LogRel(("Please install Guest Additions manually\n"));
1389 }
1390
1391 LogFlowFuncLeaveRC(rc);
1392 return rc;
1393}
1394
1395int SessionTaskUpdateAdditions::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
1396{
1397 LogFlowThisFunc(("strDesc=%s, strSource=%s, uFlags=%x\n",
1398 strDesc.c_str(), mSource.c_str(), mFlags));
1399
1400 mDesc = strDesc;
1401 mProgress = pProgress;
1402
1403 int rc = RTThreadCreate(NULL, SessionTaskUpdateAdditions::taskThread, this,
1404 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
1405 "gctlUpGA");
1406 LogFlowFuncLeaveRC(rc);
1407 return rc;
1408}
1409
1410/* static */
1411int SessionTaskUpdateAdditions::taskThread(RTTHREAD Thread, void *pvUser)
1412{
1413 std::auto_ptr<SessionTaskUpdateAdditions> task(static_cast<SessionTaskUpdateAdditions*>(pvUser));
1414 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
1415
1416 LogFlowFunc(("pTask=%p\n", task.get()));
1417 return task->Run();
1418}
1419
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