VirtualBox

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

Last change on this file since 58521 was 58521, checked in by vboxsync, 10 years ago

pr7179. GuestSession, GuestProcess, GuestControl classes have been modified.

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