VirtualBox

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

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

Main/GuestCtrl: Respect the ProcessCreateFlag_WaitForProcessStartOnly flag.

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