VirtualBox

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

Last change on this file since 61644 was 60629, checked in by vboxsync, 9 years ago

Build fix.

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

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