VirtualBox

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

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

GuestCtrl: Infrastructure changes for handling and executing dedicated guest sessions and protocol versioning (untested, work in progress).

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