VirtualBox

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

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

Main/GuestSessionImpl: warnings.

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