VirtualBox

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

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

Main/GuestSessionImplTasks: More code for updating Guest Additions.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.3 KB
Line 
1
2/* $Id: GuestSessionImplTasks.cpp 43001 2012-08-27 15:42:00Z 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 RT_BIT(0)
53/** Copy over the file from host to the
54 * guest. */
55#define UPDATEFILE_FLAG_COPY_FROM_ISO RT_BIT(1)
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.mName = Utf8StrFmt(GuestSession::tr("Copying file \"%s\" to the guest to \"%s\" (%RU64 bytes)"),
254 mSource.c_str(), mDest.c_str(), mSourceSize);
255 procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_CAT);
256 procInfo.mFlags = ProcessCreateFlag_Hidden;
257
258 /* Set arguments.*/
259 procInfo.mArguments.push_back(Utf8StrFmt("--output=%s", mDest.c_str())); /** @todo Do we need path conversion? */
260
261 /* Startup process. */
262 ComObjPtr<GuestProcess> pProcess;
263 rc = pSession->processCreateExInteral(procInfo, pProcess);
264 if (RT_SUCCESS(rc))
265 rc = pProcess->startProcess();
266 if (RT_FAILURE(rc))
267 {
268 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
269 Utf8StrFmt(GuestSession::tr("Unable to start guest process: %Rrc"), rc));
270 }
271 else
272 {
273 GuestProcessWaitResult waitRes;
274 BYTE byBuf[_64K];
275
276 BOOL fCanceled = FALSE;
277 uint64_t cbWrittenTotal = 0;
278 uint64_t cbToRead = mSourceSize;
279
280 for (;;)
281 {
282 rc = pProcess->waitFor(ProcessWaitForFlag_StdIn,
283 30 * 1000 /* Timeout */, waitRes);
284 if ( RT_FAILURE(rc)
285 || ( waitRes.mResult != ProcessWaitResult_StdIn
286 && waitRes.mResult != ProcessWaitResult_WaitFlagNotSupported))
287 {
288 break;
289 }
290
291 /* If the guest does not support waiting for stdin, we now yield in
292 * order to reduce the CPU load due to busy waiting. */
293 if (waitRes.mResult == ProcessWaitResult_WaitFlagNotSupported)
294 RTThreadYield(); /* Optional, don't check rc. */
295
296 size_t cbRead = 0;
297 if (mSourceSize) /* If we have nothing to write, take a shortcut. */
298 {
299 /** @todo Not very efficient, but works for now. */
300 rc = RTFileSeek(*pFile, mSourceOffset + cbWrittenTotal,
301 RTFILE_SEEK_BEGIN, NULL /* poffActual */);
302 if (RT_SUCCESS(rc))
303 {
304 rc = RTFileRead(*pFile, (uint8_t*)byBuf,
305 RT_MIN(cbToRead, sizeof(byBuf)), &cbRead);
306 /*
307 * Some other error occured? There might be a chance that RTFileRead
308 * could not resolve/map the native error code to an IPRT code, so just
309 * print a generic error.
310 */
311 if (RT_FAILURE(rc))
312 {
313 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
314 Utf8StrFmt(GuestSession::tr("Could not read from file \"%s\" (%Rrc)"),
315 mSource.c_str(), rc));
316 break;
317 }
318 }
319 else
320 {
321 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
322 Utf8StrFmt(GuestSession::tr("Seeking file \"%s\" offset %RU64 failed: %Rrc"),
323 mSource.c_str(), cbWrittenTotal, rc));
324 break;
325 }
326 }
327
328 uint32_t fFlags = ProcessInputFlag_None;
329
330 /* Did we reach the end of the content we want to transfer (last chunk)? */
331 if ( (cbRead < sizeof(byBuf))
332 /* Did we reach the last block which is exactly _64K? */
333 || (cbToRead - cbRead == 0)
334 /* ... or does the user want to cancel? */
335 || ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
336 && fCanceled)
337 )
338 {
339 fFlags |= ProcessInputFlag_EndOfFile;
340 }
341
342 uint32_t cbWritten;
343 Assert(sizeof(byBuf) >= cbRead);
344 rc = pProcess->writeData(0 /* StdIn */, fFlags,
345 byBuf, cbRead,
346 30 * 1000 /* Timeout */, &cbWritten);
347 if (RT_FAILURE(rc))
348 {
349 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
350 Utf8StrFmt(GuestSession::tr("Writing to file \"%s\" (offset %RU64) failed: %Rrc"),
351 mDest.c_str(), cbWrittenTotal, rc));
352 break;
353 }
354
355 LogFlowThisFunc(("cbWritten=%RU32, cbToRead=%RU64, cbWrittenTotal=%RU64, cbFileSize=%RU64\n",
356 cbWritten, cbToRead - cbWritten, cbWrittenTotal + cbWritten, mSourceSize));
357
358 /* Only subtract bytes reported written by the guest. */
359 Assert(cbToRead >= cbWritten);
360 cbToRead -= cbWritten;
361
362 /* Update total bytes written to the guest. */
363 cbWrittenTotal += cbWritten;
364 Assert(cbWrittenTotal <= mSourceSize);
365
366 /* Did the user cancel the operation above? */
367 if (fCanceled)
368 break;
369
370 /* Update the progress.
371 * Watch out for division by zero. */
372 mSourceSize > 0
373 ? rc = setProgress((ULONG)(cbWrittenTotal * 100 / mSourceSize))
374 : rc = setProgress(100);
375 if (RT_FAILURE(rc))
376 break;
377
378 /* End of file reached? */
379 if (!cbToRead)
380 break;
381 } /* for */
382
383 if ( !fCanceled
384 || RT_SUCCESS(rc))
385 {
386 /*
387 * Even if we succeeded until here make sure to check whether we really transfered
388 * everything.
389 */
390 if ( mSourceSize > 0
391 && cbWrittenTotal == 0)
392 {
393 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
394 * to the destination -> access denied. */
395 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
396 Utf8StrFmt(GuestSession::tr("Access denied when copying file \"%s\" to \"%s\""),
397 mSource.c_str(), mDest.c_str()));
398 }
399 else if (cbWrittenTotal < mSourceSize)
400 {
401 /* If we did not copy all let the user know. */
402 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
403 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" failed (%RU64/%RU64 bytes transfered)"),
404 mSource.c_str(), cbWrittenTotal, mSourceSize));
405 }
406 else
407 {
408 rc = pProcess->waitFor(ProcessWaitForFlag_Terminate,
409 30 * 1000 /* Timeout */, waitRes);
410 if ( RT_FAILURE(rc)
411 || waitRes.mResult != ProcessWaitResult_Terminate)
412 {
413 if (RT_FAILURE(rc))
414 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
415 Utf8StrFmt(GuestSession::tr("Waiting on termination for copying file \"%s\" failed: %Rrc"),
416 mSource.c_str(), rc));
417 else
418 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
419 Utf8StrFmt(GuestSession::tr("Waiting on termination for copying file \"%s\" failed with wait result %ld"),
420 mSource.c_str(), waitRes.mResult));
421 }
422
423 if (RT_SUCCESS(rc))
424 {
425 ProcessStatus_T procStatus;
426 LONG exitCode;
427 if ( ( SUCCEEDED(pProcess->COMGETTER(Status(&procStatus)))
428 && procStatus != ProcessStatus_TerminatedNormally)
429 || ( SUCCEEDED(pProcess->COMGETTER(ExitCode(&exitCode)))
430 && exitCode != 0)
431 )
432 {
433 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
434 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" failed with status %ld, exit code %ld"),
435 mSource.c_str(), procStatus, exitCode)); /**@todo Add stringify methods! */
436 }
437 }
438
439 if (RT_SUCCESS(rc))
440 rc = setProgressSuccess();
441 }
442 }
443
444 if (!pProcess.isNull())
445 pProcess->uninit();
446 } /* processCreateExInteral */
447
448 if (!mSourceFile) /* Only close locally opened files. */
449 RTFileClose(*pFile);
450
451 LogFlowFuncLeaveRC(rc);
452 return rc;
453}
454
455int SessionTaskCopyTo::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
456{
457 LogFlowThisFunc(("strDesc=%s, strSource=%s, strDest=%s, mCopyFileFlags=%x\n",
458 strDesc.c_str(), mSource.c_str(), mDest.c_str(), mCopyFileFlags));
459
460 mDesc = strDesc;
461 mProgress = pProgress;
462
463 int rc = RTThreadCreate(NULL, SessionTaskCopyTo::taskThread, this,
464 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
465 "gctlCpyTo");
466 LogFlowFuncLeaveRC(rc);
467 return rc;
468}
469
470/* static */
471int SessionTaskCopyTo::taskThread(RTTHREAD Thread, void *pvUser)
472{
473 std::auto_ptr<SessionTaskCopyTo> task(static_cast<SessionTaskCopyTo*>(pvUser));
474 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
475
476 LogFlowFunc(("pTask=%p\n", task.get()));
477 return task->Run();
478}
479
480SessionTaskCopyFrom::SessionTaskCopyFrom(GuestSession *pSession,
481 const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags)
482 : GuestSessionTask(pSession)
483{
484 mSource = strSource;
485 mDest = strDest;
486 mFlags = uFlags;
487}
488
489SessionTaskCopyFrom::~SessionTaskCopyFrom(void)
490{
491
492}
493
494int SessionTaskCopyFrom::Run(void)
495{
496 LogFlowThisFuncEnter();
497
498 ComObjPtr<GuestSession> pSession = mSession;
499 Assert(!pSession.isNull());
500
501 AutoCaller autoCaller(pSession);
502 if (FAILED(autoCaller.rc())) return autoCaller.rc();
503
504 /*
505 * Note: There will be races between querying file size + reading the guest file's
506 * content because we currently *do not* lock down the guest file when doing the
507 * actual operations.
508 ** @todo Implement guest file locking!
509 */
510 GuestFsObjData objData;
511 int rc = pSession->fileQueryInfoInternal(Utf8Str(mSource), objData);
512 if (RT_FAILURE(rc))
513 {
514 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
515 Utf8StrFmt(GuestSession::tr("Querying guest file information for \"%s\" failed: %Rrc"),
516 mSource.c_str(), rc));
517 }
518 else if (objData.mType != FsObjType_File) /* Only single files are supported at the moment. */
519 {
520 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
521 Utf8StrFmt(GuestSession::tr("Object \"%s\" on the guest is not a file"), mSource.c_str()));
522 }
523
524 if (RT_SUCCESS(rc))
525 {
526 RTFILE fileDest;
527 rc = RTFileOpen(&fileDest, mDest.c_str(),
528 RTFILE_O_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE); /** @todo Use the correct open modes! */
529 if (RT_FAILURE(rc))
530 {
531 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
532 Utf8StrFmt(GuestSession::tr("Error opening destination file \"%s\": %Rrc"),
533 mDest.c_str(), rc));
534 }
535 else
536 {
537 GuestProcessStartupInfo procInfo;
538 procInfo.mName = Utf8StrFmt(GuestSession::tr("Copying file \"%s\" from guest to the host to \"%s\" (%RI64 bytes)"),
539 mSource.c_str(), mDest.c_str(), objData.mObjectSize);
540 procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_CAT);
541 procInfo.mFlags = ProcessCreateFlag_Hidden | ProcessCreateFlag_WaitForStdOut;
542
543 /* Set arguments.*/
544 procInfo.mArguments.push_back(mSource); /* Which file to output? */
545
546 /* Startup process. */
547 ComObjPtr<GuestProcess> pProcess;
548 rc = pSession->processCreateExInteral(procInfo, pProcess);
549 if (RT_SUCCESS(rc))
550 rc = pProcess->startProcess();
551 if (RT_FAILURE(rc))
552 {
553 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
554 Utf8StrFmt(GuestSession::tr("Unable to start guest process for copying data from guest to host: %Rrc"), rc));
555 }
556 else
557 {
558 GuestProcessWaitResult waitRes;
559 BYTE byBuf[_64K];
560
561 BOOL fCanceled = FALSE;
562 uint64_t cbWrittenTotal = 0;
563 uint64_t cbToRead = objData.mObjectSize;
564
565 for (;;)
566 {
567 rc = pProcess->waitFor(ProcessWaitForFlag_StdOut,
568 30 * 1000 /* Timeout */, waitRes);
569 if ( waitRes.mResult == ProcessWaitResult_StdOut
570 || waitRes.mResult == ProcessWaitResult_WaitFlagNotSupported)
571 {
572 /* If the guest does not support waiting for stdin, we now yield in
573 * order to reduce the CPU load due to busy waiting. */
574 if (waitRes.mResult == ProcessWaitResult_WaitFlagNotSupported)
575 RTThreadYield(); /* Optional, don't check rc. */
576
577 size_t cbRead;
578 rc = pProcess->readData(OUTPUT_HANDLE_ID_STDOUT, sizeof(byBuf),
579 30 * 1000 /* Timeout */, byBuf, sizeof(byBuf),
580 &cbRead);
581 if (RT_FAILURE(rc))
582 {
583 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
584 Utf8StrFmt(GuestSession::tr("Reading from file \"%s\" (offset %RU64) failed: %Rrc"),
585 mSource.c_str(), cbWrittenTotal, rc));
586 break;
587 }
588
589 if (cbRead)
590 {
591 rc = RTFileWrite(fileDest, byBuf, cbRead, NULL /* No partial writes */);
592 if (RT_FAILURE(rc))
593 {
594 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
595 Utf8StrFmt(GuestSession::tr("Error writing to file \"%s\" (%RU64 bytes left): %Rrc"),
596 mDest.c_str(), cbToRead, rc));
597 break;
598 }
599
600 /* Only subtract bytes reported written by the guest. */
601 Assert(cbToRead >= cbRead);
602 cbToRead -= cbRead;
603
604 /* Update total bytes written to the guest. */
605 cbWrittenTotal += cbRead;
606 Assert(cbWrittenTotal <= (uint64_t)objData.mObjectSize);
607
608 /* Did the user cancel the operation above? */
609 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
610 && fCanceled)
611 break;
612
613 rc = setProgress((ULONG)(cbWrittenTotal / ((uint64_t)objData.mObjectSize / 100.0)));
614 if (RT_FAILURE(rc))
615 break;
616 }
617 }
618 else if ( RT_FAILURE(rc)
619 || waitRes.mResult == ProcessWaitResult_Terminate
620 || waitRes.mResult == ProcessWaitResult_Error
621 || waitRes.mResult == ProcessWaitResult_Timeout)
622 {
623 if (RT_FAILURE(waitRes.mRC))
624 rc = waitRes.mRC;
625 break;
626 }
627 } /* for */
628
629 LogFlowThisFunc(("rc=%Rrc, cbWrittenTotal=%RU64, cbSize=%RI64, cbToRead=%RU64\n",
630 rc, cbWrittenTotal, objData.mObjectSize, cbToRead));
631
632 if ( !fCanceled
633 || RT_SUCCESS(rc))
634 {
635 /*
636 * Even if we succeeded until here make sure to check whether we really transfered
637 * everything.
638 */
639 if ( objData.mObjectSize > 0
640 && cbWrittenTotal == 0)
641 {
642 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
643 * to the destination -> access denied. */
644 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
645 Utf8StrFmt(GuestSession::tr("Access denied when copying file \"%s\" to \"%s\""),
646 mSource.c_str(), mDest.c_str()));
647 rc = VERR_GENERAL_FAILURE; /* Fudge. */
648 }
649 else if (cbWrittenTotal < (uint64_t)objData.mObjectSize)
650 {
651 /* If we did not copy all let the user know. */
652 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
653 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" failed (%RU64/%RI64 bytes transfered)"),
654 mSource.c_str(), cbWrittenTotal, objData.mObjectSize));
655 rc = VERR_GENERAL_FAILURE; /* Fudge. */
656 }
657 else
658 {
659 ProcessStatus_T procStatus;
660 LONG exitCode;
661 if ( ( SUCCEEDED(pProcess->COMGETTER(Status(&procStatus)))
662 && procStatus != ProcessStatus_TerminatedNormally)
663 || ( SUCCEEDED(pProcess->COMGETTER(ExitCode(&exitCode)))
664 && exitCode != 0)
665 )
666 {
667 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
668 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" failed with status %ld, exit code %d"),
669 mSource.c_str(), procStatus, exitCode)); /**@todo Add stringify methods! */
670 rc = VERR_GENERAL_FAILURE; /* Fudge. */
671 }
672 else /* Yay, success! */
673 rc = setProgressSuccess();
674 }
675 }
676
677 if (!pProcess.isNull())
678 pProcess->uninit();
679 }
680
681 RTFileClose(fileDest);
682 }
683 }
684
685 LogFlowFuncLeaveRC(rc);
686 return rc;
687}
688
689int SessionTaskCopyFrom::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
690{
691 LogFlowThisFunc(("strDesc=%s, strSource=%s, strDest=%s, uFlags=%x\n",
692 strDesc.c_str(), mSource.c_str(), mDest.c_str(), mFlags));
693
694 mDesc = strDesc;
695 mProgress = pProgress;
696
697 int rc = RTThreadCreate(NULL, SessionTaskCopyFrom::taskThread, this,
698 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
699 "gctlCpyFrom");
700 LogFlowFuncLeaveRC(rc);
701 return rc;
702}
703
704/* static */
705int SessionTaskCopyFrom::taskThread(RTTHREAD Thread, void *pvUser)
706{
707 std::auto_ptr<SessionTaskCopyFrom> task(static_cast<SessionTaskCopyFrom*>(pvUser));
708 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
709
710 LogFlowFunc(("pTask=%p\n", task.get()));
711 return task->Run();
712}
713
714SessionTaskUpdateAdditions::SessionTaskUpdateAdditions(GuestSession *pSession,
715 const Utf8Str &strSource, uint32_t uFlags)
716 : GuestSessionTask(pSession)
717{
718 mSource = strSource;
719 mFlags = uFlags;
720}
721
722SessionTaskUpdateAdditions::~SessionTaskUpdateAdditions(void)
723{
724
725}
726
727int SessionTaskUpdateAdditions::copyFileToGuest(GuestSession *pSession, PRTISOFSFILE pISO,
728 Utf8Str const &strFileSource, const Utf8Str &strFileDest,
729 bool fOptional, uint32_t *pcbSize)
730{
731 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
732 AssertPtrReturn(pISO, VERR_INVALID_POINTER);
733 /* pcbSize is optional. */
734
735 uint32_t cbOffset;
736 size_t cbSize;
737
738 int rc = RTIsoFsGetFileInfo(pISO, strFileSource.c_str(), &cbOffset, &cbSize);
739 if (RT_FAILURE(rc))
740 {
741 if (fOptional)
742 return VINF_SUCCESS;
743
744 return rc;
745 }
746
747 Assert(cbOffset);
748 Assert(cbSize);
749 rc = RTFileSeek(pISO->file, cbOffset, RTFILE_SEEK_BEGIN, NULL);
750
751 /* Copy over the Guest Additions file to the guest. */
752 if (RT_SUCCESS(rc))
753 {
754 LogFlowThisFunc(("Copying Guest Additions installer file \"%s\" to \"%s\" on guest ...\n",
755 strFileSource.c_str(), strFileDest.c_str()));
756
757 if (RT_SUCCESS(rc))
758 {
759 SessionTaskCopyTo *pTask = new SessionTaskCopyTo(pSession /* GuestSession */,
760 &pISO->file, cbOffset, cbSize,
761 strFileDest, CopyFileFlag_None);
762 AssertPtrReturn(pTask, VERR_NO_MEMORY);
763
764 ComObjPtr<Progress> pProgressCopyTo;
765 rc = pSession->startTaskAsync(Utf8StrFmt(GuestSession::tr("Copying Guest Additions installer file \"%s\" to \"%s\" on guest"),
766 mSource.c_str(), strFileDest.c_str()),
767 pTask, pProgressCopyTo);
768 if (RT_SUCCESS(rc))
769 {
770 BOOL fCanceled = FALSE;
771 HRESULT hr = pProgressCopyTo->WaitForCompletion(-1);
772 if ( SUCCEEDED(pProgressCopyTo->COMGETTER(Canceled)(&fCanceled))
773 && fCanceled)
774 {
775 rc = VERR_GENERAL_FAILURE; /* Fudge. */
776 }
777 else if (FAILED(hr))
778 {
779 Assert(FAILED(hr));
780 rc = VERR_GENERAL_FAILURE; /* Fudge. */
781 }
782 }
783 }
784 }
785
786 /** @todo Note: Since there is no file locking involved at the moment, there can be modifications
787 * between finished copying, the verification and the actual execution. */
788
789 /* Determine where the installer image ended up and if it has the correct size. */
790 if (RT_SUCCESS(rc))
791 {
792 LogFlowThisFunc(("Verifying Guest Additions installer file \"%s\" ...\n",
793 strFileDest.c_str()));
794
795 GuestFsObjData objData;
796 int64_t cbSizeOnGuest;
797 rc = pSession->fileQuerySizeInternal(strFileDest, &cbSizeOnGuest);
798 if ( RT_SUCCESS(rc)
799 && cbSize == (uint64_t)cbSizeOnGuest)
800 {
801 LogFlowThisFunc(("Guest Additions installer file \"%s\" successfully verified\n",
802 strFileDest.c_str()));
803 }
804 else
805 {
806 if (RT_SUCCESS(rc)) /* Size does not match. */
807 {
808 LogFlowThisFunc(("Size of Guest Additions installer file \"%s\" does not match: %RI64bytes copied, %RU64bytes expected\n",
809 strFileDest.c_str(), cbSizeOnGuest, cbSize));
810 rc = VERR_BROKEN_PIPE; /** @todo Find a better error. */
811 }
812 else
813 LogFlowThisFunc(("Error copying Guest Additions installer file \"%s\": %Rrc\n",
814 strFileDest.c_str(), rc));
815 }
816
817 if (RT_SUCCESS(rc))
818 {
819 if (pcbSize)
820 *pcbSize = cbSizeOnGuest;
821 }
822 }
823
824 return rc;
825}
826
827int SessionTaskUpdateAdditions::runFileOnGuest(GuestSession *pSession, GuestProcessStartupInfo &procInfo)
828{
829 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
830
831 ComObjPtr<GuestProcess> pProcess;
832 int rc = pSession->processCreateExInteral(procInfo, pProcess);
833 if (RT_SUCCESS(rc))
834 rc = pProcess->startProcess();
835
836 if (RT_SUCCESS(rc))
837 {
838 LogRel(("Running %s ...\n", procInfo.mName.c_str()));
839
840 GuestProcessWaitResult waitRes;
841 rc = pProcess->waitFor(ProcessWaitForFlag_Terminate,
842 10 * 60 * 1000 /* 10 mins Timeout */, waitRes);
843 if (waitRes.mResult == ProcessWaitResult_Terminate)
844 {
845 ProcessStatus_T procStatus;
846 LONG exitCode;
847 if ( ( SUCCEEDED(pProcess->COMGETTER(Status(&procStatus)))
848 && procStatus != ProcessStatus_TerminatedNormally)
849 || ( SUCCEEDED(pProcess->COMGETTER(ExitCode(&exitCode)))
850 && exitCode != 0)
851 )
852 {
853 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
854 Utf8StrFmt(GuestSession::tr("Running %s failed with status %ld, exit code %ld"),
855 procInfo.mName.c_str(), procStatus, exitCode));
856 rc = VERR_GENERAL_FAILURE; /* Fudge. */
857 }
858 else /* Yay, success! */
859 {
860 LogFlowThisFunc(("%s successfully completed\n", procInfo.mName.c_str()));
861 }
862 }
863 else
864 {
865 if (RT_FAILURE(rc))
866 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
867 Utf8StrFmt(GuestSession::tr("Error while waiting running %s: %Rrc"),
868 procInfo.mName.c_str(), rc));
869 else
870 {
871 setProgressErrorMsg(VBOX_E_IPRT_ERROR, pProcess->errorMsg());
872 rc = VERR_GENERAL_FAILURE; /* Fudge. */
873 }
874 }
875 }
876
877 if (!pProcess.isNull())
878 pProcess->uninit();
879
880 return rc;
881}
882
883int SessionTaskUpdateAdditions::Run(void)
884{
885 LogFlowThisFuncEnter();
886
887 ComObjPtr<GuestSession> pSession = mSession;
888 Assert(!pSession.isNull());
889
890 AutoCaller autoCaller(pSession);
891 if (FAILED(autoCaller.rc())) return autoCaller.rc();
892
893 int rc = setProgress(10);
894 if (RT_FAILURE(rc))
895 return rc;
896
897 HRESULT hr = S_OK;
898
899 LogRel(("Automatic update of Guest Additions started, using \"%s\"\n", mSource.c_str()));
900
901 ComObjPtr<Guest> pGuest(mSession->getParent());
902 /*
903 * Wait for the guest being ready within 30 seconds.
904 */
905 AdditionsRunLevelType_T addsRunLevel;
906 uint64_t tsStart = RTTimeSystemMilliTS();
907 while ( SUCCEEDED(hr = pGuest->COMGETTER(AdditionsRunLevel)(&addsRunLevel))
908 && ( addsRunLevel != AdditionsRunLevelType_Userland
909 && addsRunLevel != AdditionsRunLevelType_Desktop))
910 {
911 if ((RTTimeSystemMilliTS() - tsStart) > 30 * 1000)
912 {
913 rc = VERR_TIMEOUT;
914 break;
915 }
916
917 RTThreadSleep(100); /* Wait a bit. */
918 }
919
920 if (FAILED(hr)) rc = VERR_TIMEOUT;
921 if (rc == VERR_TIMEOUT)
922 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
923 Utf8StrFmt(GuestSession::tr("Guest Additions were not ready within time, giving up")));
924
925 eOSType eOSType;
926 if (RT_SUCCESS(rc))
927 {
928 /*
929 * Determine if we are able to update automatically. This only works
930 * if there are recent Guest Additions installed already.
931 */
932 Utf8Str strAddsVer;
933 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/Version", strAddsVer);
934 if ( RT_SUCCESS(rc)
935 && RTStrVersionCompare(strAddsVer.c_str(), "4.1") < 0)
936 {
937 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
938 Utf8StrFmt(GuestSession::tr("Guest has too old Guest Additions (%s) installed for automatic updating, please update manually"),
939 strAddsVer.c_str()));
940 rc = VERR_NOT_SUPPORTED;
941 }
942
943 /*
944 * Determine guest OS type and the required installer image.
945 */
946 Utf8Str strOSType;
947 rc = getGuestProperty(pGuest, "/VirtualBox/GuestInfo/OS/Product", strOSType);
948 if (RT_SUCCESS(rc))
949 {
950 if ( strOSType.contains("Microsoft", Utf8Str::CaseInsensitive)
951 || strOSType.contains("Windows", Utf8Str::CaseInsensitive))
952 {
953 eOSType = eOSType_Windows;
954 }
955 else if (strOSType.contains("Solaris", Utf8Str::CaseInsensitive))
956 {
957 eOSType = eOSType_Solaris;
958 }
959 else /* Everything else hopefully means Linux :-). */
960 eOSType = eOSType_Linux;
961
962#if 1 /* Only Windows is supported (and tested) at the moment. */
963 if (eOSType != eOSType_Windows)
964 {
965 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
966 Utf8StrFmt(GuestSession::tr("Detected guest OS (%s) does not support automatic Guest Additions updating, please update manually"),
967 strOSType.c_str()));
968 rc = VERR_NOT_SUPPORTED;
969 }
970#endif
971 }
972 }
973
974 RTISOFSFILE iso;
975 if (RT_SUCCESS(rc))
976 {
977 /*
978 * Try to open the .ISO file to extract all needed files.
979 */
980 rc = RTIsoFsOpen(&iso, mSource.c_str());
981 if (RT_FAILURE(rc))
982 {
983 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
984 Utf8StrFmt(GuestSession::tr("Unable to open Guest Additions .ISO file \"%s\": %Rrc"),
985 mSource.c_str(), rc));
986 }
987 else
988 {
989 /* Set default installation directories. */
990 Utf8Str strUpdateDir = "/tmp/";
991 if (eOSType == eOSType_Windows)
992 strUpdateDir = "C:\\Temp\\";
993
994 rc = setProgress(5);
995
996 /* Try looking up the Guest Additions installation directory. */
997 if (RT_SUCCESS(rc))
998 {
999 /* Try getting the installed Guest Additions version to know whether we
1000 * can install our temporary Guest Addition data into the original installation
1001 * directory.
1002 *
1003 * Because versions prior to 4.2 had bugs wrt spaces in paths we have to choose
1004 * a different location then.
1005 */
1006 bool fUseInstallDir = false;
1007
1008 Utf8Str strAddsVer;
1009 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/Version", strAddsVer);
1010 if ( RT_SUCCESS(rc)
1011 && RTStrVersionCompare(strAddsVer.c_str(), "4.2") >= 0)
1012 {
1013 fUseInstallDir = true;
1014 }
1015
1016 if (fUseInstallDir)
1017 {
1018 if (RT_SUCCESS(rc))
1019 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/InstallDir", strUpdateDir);
1020 if (RT_SUCCESS(rc))
1021 {
1022 if (eOSType == eOSType_Windows)
1023 {
1024 strUpdateDir.findReplace('/', '\\');
1025 strUpdateDir.append("\\Update\\");
1026 }
1027 else
1028 strUpdateDir.append("/update/");
1029 }
1030 }
1031 }
1032
1033 if (RT_SUCCESS(rc))
1034 LogRel(("Guest Additions update directory is: %s\n",
1035 strUpdateDir.c_str()));
1036
1037 /* Create the installation directory. */
1038 rc = pSession->directoryCreateInternal(strUpdateDir,
1039 755 /* Mode */, DirectoryCreateFlag_Parents);
1040 if (RT_FAILURE(rc))
1041 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1042 Utf8StrFmt(GuestSession::tr("Error creating installation directory \"%s\" on the guest: %Rrc"),
1043 strUpdateDir.c_str(), rc));
1044 if (RT_SUCCESS(rc))
1045 rc = setProgress(10);
1046
1047 if (RT_SUCCESS(rc))
1048 {
1049 /* Prepare the file(s) we want to copy over to the guest and
1050 * (maybe) want to run. */
1051 switch (eOSType)
1052 {
1053 case eOSType_Windows:
1054 {
1055 /* Do we need to install our certificates? We do this for W2K and up. */
1056 bool fInstallCert = false;
1057
1058 Utf8Str strOSVer;
1059 rc = getGuestProperty(pGuest, "/VirtualBox/GuestInfo/OS/Release", strOSVer);
1060 if ( RT_SUCCESS(rc)
1061 && RTStrVersionCompare(strOSVer.c_str(), "5.0") >= 0) /* Only W2K an up. */
1062 {
1063 fInstallCert = true;
1064 LogRel(("Certificates for auto updating WHQL drivers will be installed\n"));
1065 }
1066 else if (RT_FAILURE(rc))
1067 {
1068 /* Unknown (or unhandled) Windows OS. */
1069 fInstallCert = true;
1070 LogRel(("Unknown guest Windows version detected (%s), installing certificates for WHQL drivers\n",
1071 strOSVer.c_str()));
1072 }
1073 else
1074 LogRel(("Skipping installation of certificates for WHQL drivers\n"));
1075
1076 if (fInstallCert)
1077 {
1078 /* Our certificate. */
1079 mFiles.push_back(InstallerFile("CERT/ORACLE_VBOX.CER",
1080 strUpdateDir + "oracle-vbox.cer",
1081 UPDATEFILE_FLAG_COPY_FROM_ISO | UPDATEFILE_FLAG_OPTIONAL));
1082 /* Our certificate installation utility. */
1083 /* First pass: Copy over the file + execute it to remove any existing
1084 * VBox certificates. */
1085 GuestProcessStartupInfo siCertUtilRem;
1086 siCertUtilRem.mName = "VirtualBox Certificate Utility";
1087 siCertUtilRem.mArguments.push_back(Utf8Str("remove-trusted-publisher"));
1088 siCertUtilRem.mArguments.push_back(Utf8Str("--root")); /* Add root certificate as well. */
1089 siCertUtilRem.mArguments.push_back(Utf8Str(strUpdateDir + "oracle-vbox.cer"));
1090 siCertUtilRem.mArguments.push_back(Utf8Str(strUpdateDir + "oracle-vbox.cer"));
1091 mFiles.push_back(InstallerFile("CERT/VBOXCERTUTIL.EXE",
1092 strUpdateDir + "VBoxCertUtil.exe",
1093 UPDATEFILE_FLAG_COPY_FROM_ISO | UPDATEFILE_FLAG_EXECUTE | UPDATEFILE_FLAG_OPTIONAL,
1094 siCertUtilRem));
1095 /* Second pass: Only execute (but don't copy) again, this time installng the
1096 * recent certificates just copied over. */
1097 GuestProcessStartupInfo siCertUtilAdd;
1098 siCertUtilAdd.mName = "VirtualBox Certificate Utility";
1099 siCertUtilAdd.mArguments.push_back(Utf8Str("add-trusted-publisher"));
1100 siCertUtilAdd.mArguments.push_back(Utf8Str("--root")); /* Add root certificate as well. */
1101 siCertUtilAdd.mArguments.push_back(Utf8Str(strUpdateDir + "oracle-vbox.cer"));
1102 siCertUtilAdd.mArguments.push_back(Utf8Str(strUpdateDir + "oracle-vbox.cer"));
1103 mFiles.push_back(InstallerFile("CERT/VBOXCERTUTIL.EXE",
1104 strUpdateDir + "VBoxCertUtil.exe",
1105 UPDATEFILE_FLAG_EXECUTE | UPDATEFILE_FLAG_OPTIONAL,
1106 siCertUtilAdd));
1107 }
1108 /* The installers in different flavors, as we don't know (and can't assume)
1109 * the guest's bitness. */
1110 mFiles.push_back(InstallerFile("VBOXWINDOWSADDITIONS_X86.EXE",
1111 strUpdateDir + "VBoxWindowsAdditions-x86.exe",
1112 UPDATEFILE_FLAG_COPY_FROM_ISO));
1113 mFiles.push_back(InstallerFile("VBOXWINDOWSADDITIONS_AMD64.EXE",
1114 strUpdateDir + "VBoxWindowsAdditions-amd64.exe",
1115 UPDATEFILE_FLAG_COPY_FROM_ISO));
1116 /* The stub loader which decides which flavor to run. */
1117 GuestProcessStartupInfo siInstaller;
1118 siInstaller.mName = "VirtualBox Windows Guest Additions Installer";
1119 siInstaller.mArguments.push_back(Utf8Str("/S")); /* We want to install in silent mode. */
1120 siInstaller.mArguments.push_back(Utf8Str("/l")); /* ... and logging enabled. */
1121 /* Don't quit VBoxService during upgrade because it still is used for this
1122 * piece of code we're in right now (that is, here!) ... */
1123 siInstaller.mArguments.push_back(Utf8Str("/no_vboxservice_exit"));
1124 /* Tell the installer to report its current installation status
1125 * using a running VBoxTray instance via balloon messages in the
1126 * Windows taskbar. */
1127 siInstaller.mArguments.push_back(Utf8Str("/post_installstatus"));
1128 /* If the caller does not want to wait for out guest update process to end,
1129 * complete the progress object now so that the caller can do other work. */
1130 if (mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly)
1131 siInstaller.mFlags |= ProcessCreateFlag_WaitForProcessStartOnly;
1132 mFiles.push_back(InstallerFile("VBOXWINDOWSADDITIONS.EXE",
1133 strUpdateDir + "VBoxWindowsAdditions.exe",
1134 UPDATEFILE_FLAG_COPY_FROM_ISO | UPDATEFILE_FLAG_EXECUTE, siInstaller));
1135 break;
1136 }
1137 case eOSType_Linux:
1138 /** @todo Add Linux support. */
1139 break;
1140 case eOSType_Solaris:
1141 /** @todo Add Solaris support. */
1142 break;
1143 default:
1144 AssertReleaseMsgFailed(("Unsupported guest type: %d\n", eOSType));
1145 break;
1146 }
1147 }
1148
1149 if (RT_SUCCESS(rc))
1150 {
1151 /* We want to spend 40% total for all copying operations. So roughly
1152 * calculate the specific percentage step of each copied file. */
1153 uint8_t uOffset = 20; /* Start at 20%. */
1154 uint8_t uStep = 40 / mFiles.size();
1155
1156 LogRel(("Copying over Guest Additions update files to the guest ...\n"));
1157
1158 std::vector<InstallerFile>::const_iterator itFiles = mFiles.begin();
1159 while (itFiles != mFiles.end())
1160 {
1161 if (itFiles->fFlags & UPDATEFILE_FLAG_COPY_FROM_ISO)
1162 {
1163 bool fOptional = false;
1164 if (itFiles->fFlags & UPDATEFILE_FLAG_OPTIONAL)
1165 fOptional = true;
1166 rc = copyFileToGuest(pSession, &iso, itFiles->strSource, itFiles->strDest,
1167 fOptional, NULL /* cbSize */);
1168 if (RT_FAILURE(rc))
1169 {
1170 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1171 Utf8StrFmt(GuestSession::tr("Error while copying file \"%s\" to \"%s\" on the guest: %Rrc"),
1172 itFiles->strSource.c_str(), itFiles->strDest.c_str(), rc));
1173 break;
1174 }
1175 }
1176
1177 rc = setProgress(uOffset);
1178 if (RT_FAILURE(rc))
1179 break;
1180 uOffset += uStep;
1181
1182 itFiles++;
1183 }
1184 }
1185
1186 /* Done copying, close .ISO file. */
1187 RTIsoFsClose(&iso);
1188
1189 if (RT_SUCCESS(rc))
1190 {
1191 /* We want to spend 35% total for all copying operations. So roughly
1192 * calculate the specific percentage step of each copied file. */
1193 uint8_t uOffset = 60; /* Start at 60%. */
1194 uint8_t uStep = 35 / mFiles.size();
1195
1196 LogRel(("Executing Guest Additions update files ...\n"));
1197
1198 std::vector<InstallerFile>::iterator itFiles = mFiles.begin();
1199 while (itFiles != mFiles.end())
1200 {
1201 if (itFiles->fFlags & UPDATEFILE_FLAG_EXECUTE)
1202 {
1203 rc = runFileOnGuest(pSession, itFiles->mProcInfo);
1204 if (RT_FAILURE(rc))
1205 {
1206 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1207 Utf8StrFmt(GuestSession::tr("Error while running installer file \"%s\" on the guest: %Rrc"),
1208 itFiles->strDest.c_str(), rc));
1209 break;
1210 }
1211 }
1212
1213 rc = setProgress(uOffset);
1214 if (RT_FAILURE(rc))
1215 break;
1216 uOffset += uStep;
1217
1218 itFiles++;
1219 }
1220 }
1221
1222 if (RT_SUCCESS(rc))
1223 {
1224 LogRel(("Automatic update of Guest Additions succeeded\n"));
1225 rc = setProgressSuccess();
1226 }
1227 else if (rc == VERR_CANCELLED)
1228 {
1229 LogRel(("Automatic update of Guest Additions canceled\n"));
1230 }
1231 else
1232 {
1233 Utf8Str strError = Utf8StrFmt("No further error information available (%Rrc)", rc);
1234 if (!mProgress.isNull()) /* Progress object is optional. */
1235 {
1236 ComPtr<IVirtualBoxErrorInfo> pError;
1237 hr = mProgress->COMGETTER(ErrorInfo)(pError.asOutParam());
1238 Assert(!pError.isNull());
1239 if (SUCCEEDED(hr))
1240 {
1241 Bstr strVal;
1242 hr = pError->COMGETTER(Text)(strVal.asOutParam());
1243 if ( SUCCEEDED(hr)
1244 && strVal.isNotEmpty())
1245 strError = strVal;
1246 }
1247 }
1248
1249 LogRel(("Automatic update of Guest Additions failed: %s\n", strError.c_str()));
1250 LogRel(("Please install Guest Additions manually\n"));
1251 }
1252 }
1253 }
1254
1255 LogFlowFuncLeaveRC(rc);
1256 return rc;
1257}
1258
1259int SessionTaskUpdateAdditions::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
1260{
1261 LogFlowThisFunc(("strDesc=%s, strSource=%s, uFlags=%x\n",
1262 strDesc.c_str(), mSource.c_str(), mFlags));
1263
1264 mDesc = strDesc;
1265 mProgress = pProgress;
1266
1267 int rc = RTThreadCreate(NULL, SessionTaskUpdateAdditions::taskThread, this,
1268 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
1269 "gctlUpGA");
1270 LogFlowFuncLeaveRC(rc);
1271 return rc;
1272}
1273
1274/* static */
1275int SessionTaskUpdateAdditions::taskThread(RTTHREAD Thread, void *pvUser)
1276{
1277 std::auto_ptr<SessionTaskUpdateAdditions> task(static_cast<SessionTaskUpdateAdditions*>(pvUser));
1278 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
1279
1280 LogFlowFunc(("pTask=%p\n", task.get()));
1281 return task->Run();
1282}
1283
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