VirtualBox

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

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

Warning.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 57.8 KB
Line 
1
2/* $Id: GuestSessionImplTasks.cpp 43200 2012-09-05 12:47:46Z 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(vrc));
939 break;
940
941 default:
942 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
943 Utf8StrFmt(GuestSession::tr("Error while running update file \"%s\" on guest: %Rrc"),
944 procInfo.mCommand.c_str(), vrc));
945 break;
946 }
947 }
948
949 return vrc;
950}
951
952int SessionTaskUpdateAdditions::Run(void)
953{
954 LogFlowThisFuncEnter();
955
956 ComObjPtr<GuestSession> pSession = mSession;
957 Assert(!pSession.isNull());
958
959 AutoCaller autoCaller(pSession);
960 if (FAILED(autoCaller.rc())) return autoCaller.rc();
961
962 int rc = setProgress(10);
963 if (RT_FAILURE(rc))
964 return rc;
965
966 HRESULT hr = S_OK;
967
968 LogRel(("Automatic update of Guest Additions started, using \"%s\"\n", mSource.c_str()));
969
970 ComObjPtr<Guest> pGuest(mSession->getParent());
971#if 0
972 /*
973 * Wait for the guest being ready within 30 seconds.
974 */
975 AdditionsRunLevelType_T addsRunLevel;
976 uint64_t tsStart = RTTimeSystemMilliTS();
977 while ( SUCCEEDED(hr = pGuest->COMGETTER(AdditionsRunLevel)(&addsRunLevel))
978 && ( addsRunLevel != AdditionsRunLevelType_Userland
979 && addsRunLevel != AdditionsRunLevelType_Desktop))
980 {
981 if ((RTTimeSystemMilliTS() - tsStart) > 30 * 1000)
982 {
983 rc = VERR_TIMEOUT;
984 break;
985 }
986
987 RTThreadSleep(100); /* Wait a bit. */
988 }
989
990 if (FAILED(hr)) rc = VERR_TIMEOUT;
991 if (rc == VERR_TIMEOUT)
992 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
993 Utf8StrFmt(GuestSession::tr("Guest Additions were not ready within time, giving up")));
994#else
995 /*
996 * For use with the GUI we don't want to wait, just return so that the manual .ISO mounting
997 * can continue.
998 */
999 AdditionsRunLevelType_T addsRunLevel;
1000 if ( FAILED(hr = pGuest->COMGETTER(AdditionsRunLevel)(&addsRunLevel))
1001 || ( addsRunLevel != AdditionsRunLevelType_Userland
1002 && addsRunLevel != AdditionsRunLevelType_Desktop))
1003 {
1004 if (addsRunLevel == AdditionsRunLevelType_System)
1005 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1006 Utf8StrFmt(GuestSession::tr("Guest Additions are installed but not fully loaded yet, aborting automatic update")));
1007 else
1008 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1009 Utf8StrFmt(GuestSession::tr("Guest Additions not installed or ready, aborting automatic update")));
1010 rc = VERR_NOT_SUPPORTED;
1011 }
1012#endif
1013
1014 if (RT_SUCCESS(rc))
1015 {
1016 /*
1017 * Determine if we are able to update automatically. This only works
1018 * if there are recent Guest Additions installed already.
1019 */
1020 Utf8Str strAddsVer;
1021 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/Version", strAddsVer);
1022 if ( RT_SUCCESS(rc)
1023 && RTStrVersionCompare(strAddsVer.c_str(), "4.1") < 0)
1024 {
1025 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1026 Utf8StrFmt(GuestSession::tr("Guest has too old Guest Additions (%s) installed for automatic updating, please update manually"),
1027 strAddsVer.c_str()));
1028 rc = VERR_NOT_SUPPORTED;
1029 }
1030 }
1031
1032 Utf8Str strOSVer;
1033 eOSType osType;
1034 if (RT_SUCCESS(rc))
1035 {
1036 /*
1037 * Determine guest OS type and the required installer image.
1038 */
1039 Utf8Str strOSType;
1040 rc = getGuestProperty(pGuest, "/VirtualBox/GuestInfo/OS/Product", strOSType);
1041 if (RT_SUCCESS(rc))
1042 {
1043 if ( strOSType.contains("Microsoft", Utf8Str::CaseInsensitive)
1044 || strOSType.contains("Windows", Utf8Str::CaseInsensitive))
1045 {
1046 osType = eOSType_Windows;
1047
1048 /*
1049 * Determine guest OS version.
1050 */
1051 rc = getGuestProperty(pGuest, "/VirtualBox/GuestInfo/OS/Release", strOSVer);
1052 if (RT_FAILURE(rc))
1053 {
1054 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1055 Utf8StrFmt(GuestSession::tr("Unable to detected guest OS version, please update manually")));
1056 rc = VERR_NOT_SUPPORTED;
1057 }
1058
1059 /* Because Windows 2000 + XP and is bitching with WHQL popups even if we have signed drivers we
1060 * can't do automated updates here. */
1061 /* Windows XP 64-bit (5.2) is a Windows 2003 Server actually, so skip this here. */
1062 if ( RT_SUCCESS(rc)
1063 && ( strOSVer.startsWith("5.0") /* Exclude the build number. */
1064 || strOSVer.startsWith("5.1")) /* Exclude the build number. */
1065 )
1066 {
1067 /* If we don't have AdditionsUpdateFlag_WaitForUpdateStartOnly set we can't continue
1068 * because the Windows Guest Additions installer will fail because of WHQL popups. If the
1069 * flag is set this update routine ends successfully as soon as the installer was started
1070 * (and the user has to deal with it in the guest). */
1071 if (!(mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly))
1072 {
1073 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1074 Utf8StrFmt(GuestSession::tr("Windows 2000 and XP are not supported for automatic updating due to WHQL interaction, please update manually")));
1075 rc = VERR_NOT_SUPPORTED;
1076 }
1077 }
1078 }
1079 else if (strOSType.contains("Solaris", Utf8Str::CaseInsensitive))
1080 {
1081 osType = eOSType_Solaris;
1082 }
1083 else /* Everything else hopefully means Linux :-). */
1084 osType = eOSType_Linux;
1085
1086#if 1 /* Only Windows is supported (and tested) at the moment. */
1087 if (osType != eOSType_Windows)
1088 {
1089 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1090 Utf8StrFmt(GuestSession::tr("Detected guest OS (%s) does not support automatic Guest Additions updating, please update manually"),
1091 strOSType.c_str()));
1092 rc = VERR_NOT_SUPPORTED;
1093 }
1094#endif
1095 }
1096 }
1097
1098 RTISOFSFILE iso;
1099 if (RT_SUCCESS(rc))
1100 {
1101 /*
1102 * Try to open the .ISO file to extract all needed files.
1103 */
1104 rc = RTIsoFsOpen(&iso, mSource.c_str());
1105 if (RT_FAILURE(rc))
1106 {
1107 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1108 Utf8StrFmt(GuestSession::tr("Unable to open Guest Additions .ISO file \"%s\": %Rrc"),
1109 mSource.c_str(), rc));
1110 }
1111 else
1112 {
1113 /* Set default installation directories. */
1114 Utf8Str strUpdateDir = "/tmp/";
1115 if (osType == eOSType_Windows)
1116 strUpdateDir = "C:\\Temp\\";
1117
1118 rc = setProgress(5);
1119
1120 /* Try looking up the Guest Additions installation directory. */
1121 if (RT_SUCCESS(rc))
1122 {
1123 /* Try getting the installed Guest Additions version to know whether we
1124 * can install our temporary Guest Addition data into the original installation
1125 * directory.
1126 *
1127 * Because versions prior to 4.2 had bugs wrt spaces in paths we have to choose
1128 * a different location then.
1129 */
1130 bool fUseInstallDir = false;
1131
1132 Utf8Str strAddsVer;
1133 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/Version", strAddsVer);
1134 if ( RT_SUCCESS(rc)
1135 && RTStrVersionCompare(strAddsVer.c_str(), "4.2r80329") > 0)
1136 {
1137 fUseInstallDir = true;
1138 }
1139
1140 if (fUseInstallDir)
1141 {
1142 if (RT_SUCCESS(rc))
1143 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/InstallDir", strUpdateDir);
1144 if (RT_SUCCESS(rc))
1145 {
1146 if (osType == eOSType_Windows)
1147 {
1148 strUpdateDir.findReplace('/', '\\');
1149 strUpdateDir.append("\\Update\\");
1150 }
1151 else
1152 strUpdateDir.append("/update/");
1153 }
1154 }
1155 }
1156
1157 if (RT_SUCCESS(rc))
1158 LogRel(("Guest Additions update directory is: %s\n",
1159 strUpdateDir.c_str()));
1160
1161 /* Create the installation directory. */
1162 int guestRc;
1163 rc = pSession->directoryCreateInternal(strUpdateDir,
1164 755 /* Mode */, DirectoryCreateFlag_Parents, &guestRc);
1165 if (RT_FAILURE(rc))
1166 {
1167 switch (rc)
1168 {
1169 case VERR_GENERAL_FAILURE: /** @todo Special guest control rc needed! */
1170 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1171 GuestProcess::guestErrorToString(guestRc));
1172 break;
1173
1174 default:
1175 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1176 Utf8StrFmt(GuestSession::tr("Error creating installation directory \"%s\" on the guest: %Rrc"),
1177 strUpdateDir.c_str(), rc));
1178 break;
1179 }
1180 }
1181 if (RT_SUCCESS(rc))
1182 rc = setProgress(10);
1183
1184 if (RT_SUCCESS(rc))
1185 {
1186 /* Prepare the file(s) we want to copy over to the guest and
1187 * (maybe) want to run. */
1188 switch (osType)
1189 {
1190 case eOSType_Windows:
1191 {
1192 /* Do we need to install our certificates? We do this for W2K and up. */
1193 bool fInstallCert = false;
1194
1195 /* Only Windows 2000 and up need certificates to be installed. */
1196 if (RTStrVersionCompare(strOSVer.c_str(), "5.0") >= 0)
1197 {
1198 fInstallCert = true;
1199 LogRel(("Certificates for auto updating WHQL drivers will be installed\n"));
1200 }
1201 else
1202 LogRel(("Skipping installation of certificates for WHQL drivers\n"));
1203
1204 if (fInstallCert)
1205 {
1206 /* Our certificate. */
1207 mFiles.push_back(InstallerFile("CERT/ORACLE_VBOX.CER",
1208 strUpdateDir + "oracle-vbox.cer",
1209 UPDATEFILE_FLAG_COPY_FROM_ISO | UPDATEFILE_FLAG_OPTIONAL));
1210 /* Our certificate installation utility. */
1211 /* First pass: Copy over the file + execute it to remove any existing
1212 * VBox certificates. */
1213 GuestProcessStartupInfo siCertUtilRem;
1214 siCertUtilRem.mName = "VirtualBox Certificate Utility, removing old VirtualBox certificates";
1215 siCertUtilRem.mArguments.push_back(Utf8Str("remove-trusted-publisher"));
1216 siCertUtilRem.mArguments.push_back(Utf8Str("--root")); /* Add root certificate as well. */
1217 siCertUtilRem.mArguments.push_back(Utf8Str(strUpdateDir + "oracle-vbox.cer"));
1218 siCertUtilRem.mArguments.push_back(Utf8Str(strUpdateDir + "oracle-vbox.cer"));
1219 mFiles.push_back(InstallerFile("CERT/VBOXCERTUTIL.EXE",
1220 strUpdateDir + "VBoxCertUtil.exe",
1221 UPDATEFILE_FLAG_COPY_FROM_ISO | UPDATEFILE_FLAG_EXECUTE | UPDATEFILE_FLAG_OPTIONAL,
1222 siCertUtilRem));
1223 /* Second pass: Only execute (but don't copy) again, this time installng the
1224 * recent certificates just copied over. */
1225 GuestProcessStartupInfo siCertUtilAdd;
1226 siCertUtilAdd.mName = "VirtualBox Certificate Utility, installing VirtualBox certificates";
1227 siCertUtilAdd.mArguments.push_back(Utf8Str("add-trusted-publisher"));
1228 siCertUtilAdd.mArguments.push_back(Utf8Str("--root")); /* Add root certificate as well. */
1229 siCertUtilAdd.mArguments.push_back(Utf8Str(strUpdateDir + "oracle-vbox.cer"));
1230 siCertUtilAdd.mArguments.push_back(Utf8Str(strUpdateDir + "oracle-vbox.cer"));
1231 mFiles.push_back(InstallerFile("CERT/VBOXCERTUTIL.EXE",
1232 strUpdateDir + "VBoxCertUtil.exe",
1233 UPDATEFILE_FLAG_EXECUTE | UPDATEFILE_FLAG_OPTIONAL,
1234 siCertUtilAdd));
1235 }
1236 /* The installers in different flavors, as we don't know (and can't assume)
1237 * the guest's bitness. */
1238 mFiles.push_back(InstallerFile("VBOXWINDOWSADDITIONS_X86.EXE",
1239 strUpdateDir + "VBoxWindowsAdditions-x86.exe",
1240 UPDATEFILE_FLAG_COPY_FROM_ISO));
1241 mFiles.push_back(InstallerFile("VBOXWINDOWSADDITIONS_AMD64.EXE",
1242 strUpdateDir + "VBoxWindowsAdditions-amd64.exe",
1243 UPDATEFILE_FLAG_COPY_FROM_ISO));
1244 /* The stub loader which decides which flavor to run. */
1245 GuestProcessStartupInfo siInstaller;
1246 siInstaller.mName = "VirtualBox Windows Guest Additions Installer";
1247 /* Set a running timeout of 5 minutes -- the Windows Guest Additions
1248 * setup can take quite a while, so be on the safe side. */
1249 siInstaller.mTimeoutMS = 5 * 60 * 1000;
1250 siInstaller.mArguments.push_back(Utf8Str("/S")); /* We want to install in silent mode. */
1251 siInstaller.mArguments.push_back(Utf8Str("/l")); /* ... and logging enabled. */
1252 /* Don't quit VBoxService during upgrade because it still is used for this
1253 * piece of code we're in right now (that is, here!) ... */
1254 siInstaller.mArguments.push_back(Utf8Str("/no_vboxservice_exit"));
1255 /* Tell the installer to report its current installation status
1256 * using a running VBoxTray instance via balloon messages in the
1257 * Windows taskbar. */
1258 siInstaller.mArguments.push_back(Utf8Str("/post_installstatus"));
1259 /* If the caller does not want to wait for out guest update process to end,
1260 * complete the progress object now so that the caller can do other work. */
1261 if (mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly)
1262 siInstaller.mFlags |= ProcessCreateFlag_WaitForProcessStartOnly;
1263 mFiles.push_back(InstallerFile("VBOXWINDOWSADDITIONS.EXE",
1264 strUpdateDir + "VBoxWindowsAdditions.exe",
1265 UPDATEFILE_FLAG_COPY_FROM_ISO | UPDATEFILE_FLAG_EXECUTE, siInstaller));
1266 break;
1267 }
1268 case eOSType_Linux:
1269 /** @todo Add Linux support. */
1270 break;
1271 case eOSType_Solaris:
1272 /** @todo Add Solaris support. */
1273 break;
1274 default:
1275 AssertReleaseMsgFailed(("Unsupported guest type: %d\n", osType));
1276 break;
1277 }
1278 }
1279
1280 if (RT_SUCCESS(rc))
1281 {
1282 /* We want to spend 40% total for all copying operations. So roughly
1283 * calculate the specific percentage step of each copied file. */
1284 uint8_t uOffset = 20; /* Start at 20%. */
1285 uint8_t uStep = 40 / mFiles.size();
1286
1287 LogRel(("Copying over Guest Additions update files to the guest ...\n"));
1288
1289 std::vector<InstallerFile>::const_iterator itFiles = mFiles.begin();
1290 while (itFiles != mFiles.end())
1291 {
1292 if (itFiles->fFlags & UPDATEFILE_FLAG_COPY_FROM_ISO)
1293 {
1294 bool fOptional = false;
1295 if (itFiles->fFlags & UPDATEFILE_FLAG_OPTIONAL)
1296 fOptional = true;
1297 rc = copyFileToGuest(pSession, &iso, itFiles->strSource, itFiles->strDest,
1298 fOptional, NULL /* cbSize */);
1299 if (RT_FAILURE(rc))
1300 {
1301 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1302 Utf8StrFmt(GuestSession::tr("Error while copying file \"%s\" to \"%s\" on the guest: %Rrc"),
1303 itFiles->strSource.c_str(), itFiles->strDest.c_str(), rc));
1304 break;
1305 }
1306 }
1307
1308 rc = setProgress(uOffset);
1309 if (RT_FAILURE(rc))
1310 break;
1311 uOffset += uStep;
1312
1313 itFiles++;
1314 }
1315 }
1316
1317 /* Done copying, close .ISO file. */
1318 RTIsoFsClose(&iso);
1319
1320 if (RT_SUCCESS(rc))
1321 {
1322 /* We want to spend 35% total for all copying operations. So roughly
1323 * calculate the specific percentage step of each copied file. */
1324 uint8_t uOffset = 60; /* Start at 60%. */
1325 uint8_t uStep = 35 / mFiles.size();
1326
1327 LogRel(("Executing Guest Additions update files ...\n"));
1328
1329 std::vector<InstallerFile>::iterator itFiles = mFiles.begin();
1330 while (itFiles != mFiles.end())
1331 {
1332 if (itFiles->fFlags & UPDATEFILE_FLAG_EXECUTE)
1333 {
1334 rc = runFileOnGuest(pSession, itFiles->mProcInfo);
1335 if (RT_FAILURE(rc))
1336 break;
1337 }
1338
1339 rc = setProgress(uOffset);
1340 if (RT_FAILURE(rc))
1341 break;
1342 uOffset += uStep;
1343
1344 itFiles++;
1345 }
1346 }
1347
1348 if (RT_SUCCESS(rc))
1349 {
1350 LogRel(("Automatic update of Guest Additions succeeded\n"));
1351 rc = setProgressSuccess();
1352 }
1353 }
1354 }
1355
1356 if (RT_FAILURE(rc))
1357 {
1358 if (rc == VERR_CANCELLED)
1359 {
1360 LogRel(("Automatic update of Guest Additions was canceled\n"));
1361
1362 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1363 Utf8StrFmt(GuestSession::tr("Installation was canceled")));
1364 }
1365 else
1366 {
1367 Utf8Str strError = Utf8StrFmt("No further error information available (%Rrc)", rc);
1368 if (!mProgress.isNull()) /* Progress object is optional. */
1369 {
1370 ComPtr<IVirtualBoxErrorInfo> pError;
1371 hr = mProgress->COMGETTER(ErrorInfo)(pError.asOutParam());
1372 Assert(!pError.isNull());
1373 if (SUCCEEDED(hr))
1374 {
1375 Bstr strVal;
1376 hr = pError->COMGETTER(Text)(strVal.asOutParam());
1377 if ( SUCCEEDED(hr)
1378 && strVal.isNotEmpty())
1379 strError = strVal;
1380 }
1381 }
1382
1383 LogRel(("Automatic update of Guest Additions failed: %s\n", strError.c_str()));
1384 }
1385
1386 LogRel(("Please install Guest Additions manually\n"));
1387 }
1388
1389 LogFlowFuncLeaveRC(rc);
1390 return rc;
1391}
1392
1393int SessionTaskUpdateAdditions::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
1394{
1395 LogFlowThisFunc(("strDesc=%s, strSource=%s, uFlags=%x\n",
1396 strDesc.c_str(), mSource.c_str(), mFlags));
1397
1398 mDesc = strDesc;
1399 mProgress = pProgress;
1400
1401 int rc = RTThreadCreate(NULL, SessionTaskUpdateAdditions::taskThread, this,
1402 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
1403 "gctlUpGA");
1404 LogFlowFuncLeaveRC(rc);
1405 return rc;
1406}
1407
1408/* static */
1409int SessionTaskUpdateAdditions::taskThread(RTTHREAD Thread, void *pvUser)
1410{
1411 std::auto_ptr<SessionTaskUpdateAdditions> task(static_cast<SessionTaskUpdateAdditions*>(pvUser));
1412 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
1413
1414 LogFlowFunc(("pTask=%p\n", task.get()));
1415 return task->Run();
1416}
1417
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