VirtualBox

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

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

Main/GuestCtrl: Bugfixes:

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette