VirtualBox

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

Last change on this file since 63165 was 63152, checked in by vboxsync, 8 years ago

GuestSessionImplTasks.cpp: Attempt to fix two bogus procStatus+exitCode checks pointed out by MSC (exitCode could be used uninitialized). Logic also failed to handle procStatus other than TerminatedNormally, and would mistake that as a success.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 67.4 KB
Line 
1/* $Id: GuestSessionImplTasks.cpp 63152 2016-08-08 11:37:26Z 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 = VERR_IPE_UNINITIALIZED_STATUS;
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 /** @todo This code sequence is duplicated in CopyFrom... */
563 ProcessStatus_T procStatus = ProcessStatus_TerminatedAbnormally;
564 HRESULT hrc = pProcess->COMGETTER(Status(&procStatus));
565 if (!SUCCEEDED(hrc))
566 procStatus = ProcessStatus_TerminatedAbnormally;
567
568 LONG exitCode = 42424242;
569 hrc = pProcess->COMGETTER(ExitCode(&exitCode));
570 if (!SUCCEEDED(hrc))
571 exitCode = 42424242;
572
573 if ( procStatus != ProcessStatus_TerminatedNormally
574 || exitCode != 0)
575 {
576 LogFlowThisFunc(("procStatus=%d, exitCode=%d\n", procStatus, exitCode));
577 if (procStatus == ProcessStatus_TerminatedNormally)
578 rc = GuestProcessTool::i_exitCodeToRc(procInfo, exitCode);
579 else
580 rc = VERR_GENERAL_FAILURE;
581 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
582 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" to guest failed: %Rrc"),
583 mSource.c_str(), rc));
584 }
585 /*
586 * Even if we succeeded until here make sure to check whether we really transfered
587 * everything.
588 */
589 else if ( mSourceSize > 0
590 && cbWrittenTotal == 0)
591 {
592 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
593 * to the destination -> access denied. */
594 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
595 Utf8StrFmt(GuestSession::tr("Access denied when copying file \"%s\" to guest \"%s\""),
596 mSource.c_str(), mDest.c_str()));
597 rc = VERR_ACCESS_DENIED;
598 }
599 else if (cbWrittenTotal < mSourceSize)
600 {
601 /* If we did not copy all let the user know. */
602 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
603 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" to guest failed (%RU64/%RU64 bytes transfered)"),
604 mSource.c_str(), cbWrittenTotal, mSourceSize));
605 rc = VERR_INTERRUPTED;
606 }
607 else
608 rc = setProgressSuccess();
609 }
610 } /* processCreateExInteral */
611
612 if (!mSourceFile) /* Only close locally opened files. */
613 RTFileClose(*pFile);
614
615 LogFlowFuncLeaveRC(rc);
616 return rc;
617}
618
619int SessionTaskCopyTo::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
620{
621 LogFlowThisFunc(("strDesc=%s, strSource=%s, strDest=%s, mCopyFileFlags=%x\n",
622 strDesc.c_str(), mSource.c_str(), mDest.c_str(), mCopyFileFlags));
623
624 mDesc = strDesc;
625 mProgress = pProgress;
626
627 int rc = RTThreadCreate(NULL, SessionTaskCopyTo::taskThread, this,
628 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
629 "gctlCpyTo");
630 LogFlowFuncLeaveRC(rc);
631 return rc;
632}
633
634/* static */
635DECLCALLBACK(int) SessionTaskCopyTo::taskThread(RTTHREAD Thread, void *pvUser)
636{
637 SessionTaskCopyTo* task = static_cast<SessionTaskCopyTo*>(pvUser);
638 AssertReturn(task, VERR_GENERAL_FAILURE);
639
640 LogFlowFunc(("pTask=%p\n", task));
641
642 return task->Run();
643}
644
645SessionTaskCopyFrom::SessionTaskCopyFrom(GuestSession *pSession,
646 const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags)
647 : GuestSessionTask(pSession)
648{
649 mSource = strSource;
650 mDest = strDest;
651 mFlags = uFlags;
652 m_strTaskName = "gctlCpyFrom";
653}
654
655SessionTaskCopyFrom::~SessionTaskCopyFrom(void)
656{
657
658}
659
660int SessionTaskCopyFrom::Run(void)
661{
662 LogFlowThisFuncEnter();
663
664 ComObjPtr<GuestSession> pSession = mSession;
665 Assert(!pSession.isNull());
666
667 AutoCaller autoCaller(pSession);
668 if (FAILED(autoCaller.rc())) return autoCaller.rc();
669
670 RTMSINTERVAL msTimeout = 30 * 1000; /** @todo 30s timeout for all actions. Make this configurable? */
671
672 /*
673 * Note: There will be races between querying file size + reading the guest file's
674 * content because we currently *do not* lock down the guest file when doing the
675 * actual operations.
676 ** @todo Use the IGuestFile API for locking down the file on the guest!
677 */
678 GuestFsObjData objData; int guestRc;
679 int rc = pSession->i_fileQueryInfoInternal(Utf8Str(mSource), false /*fFollowSymlinks*/, objData, &guestRc);
680 if (RT_FAILURE(rc))
681 {
682 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
683 Utf8StrFmt(GuestSession::tr("Querying guest file information for \"%s\" failed: %Rrc"),
684 mSource.c_str(), rc));
685 }
686 else if (objData.mType != FsObjType_File) /* Only single files are supported at the moment. */
687 {
688 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
689 Utf8StrFmt(GuestSession::tr("Object \"%s\" on the guest is not a file"), mSource.c_str()));
690 rc = VERR_NOT_A_FILE;
691 }
692
693 if (RT_SUCCESS(rc))
694 {
695 RTFILE fileDest;
696 rc = RTFileOpen(&fileDest, mDest.c_str(),
697 RTFILE_O_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE); /** @todo Use the correct open modes! */
698 if (RT_FAILURE(rc))
699 {
700 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
701 Utf8StrFmt(GuestSession::tr("Opening/creating destination file on host \"%s\" failed: %Rrc"),
702 mDest.c_str(), rc));
703 }
704 else
705 {
706 GuestProcessStartupInfo procInfo;
707 procInfo.mName = Utf8StrFmt(GuestSession::tr("Copying file \"%s\" from guest to the host to \"%s\" (%RI64 bytes)"),
708 mSource.c_str(), mDest.c_str(), objData.mObjectSize);
709 procInfo.mExecutable = Utf8Str(VBOXSERVICE_TOOL_CAT);
710 procInfo.mFlags = ProcessCreateFlag_Hidden | ProcessCreateFlag_WaitForStdOut;
711
712 /* Set arguments.*/
713 procInfo.mArguments.push_back(procInfo.mExecutable); /* Set argv0. */
714 procInfo.mArguments.push_back(mSource); /* Which file to output? */
715
716 /* Startup process. */
717 ComObjPtr<GuestProcess> pProcess;
718 rc = pSession->i_processCreateExInternal(procInfo, pProcess);
719 if (RT_SUCCESS(rc))
720 rc = pProcess->i_startProcess(msTimeout, &guestRc);
721 if (RT_FAILURE(rc))
722 {
723 switch (rc)
724 {
725 case VERR_GSTCTL_GUEST_ERROR:
726 setProgressErrorMsg(VBOX_E_IPRT_ERROR, GuestProcess::i_guestErrorToString(guestRc));
727 break;
728
729 default:
730 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
731 Utf8StrFmt(GuestSession::tr(
732 "Error while creating guest process for copying file \"%s\" from guest to host: %Rrc"),
733 mSource.c_str(), rc));
734 break;
735 }
736 }
737 else
738 {
739 ProcessWaitResult_T waitRes;
740 BYTE byBuf[_64K];
741
742 BOOL fCanceled = FALSE;
743 uint64_t cbWrittenTotal = 0;
744 uint64_t cbToRead = objData.mObjectSize;
745
746 for (;;)
747 {
748 rc = pProcess->i_waitFor(ProcessWaitForFlag_StdOut, msTimeout, waitRes, &guestRc);
749 if (RT_FAILURE(rc))
750 {
751 switch (rc)
752 {
753 case VERR_GSTCTL_GUEST_ERROR:
754 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
755 GuestProcess::i_guestErrorToString(guestRc));
756 break;
757
758 default:
759 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
760 Utf8StrFmt(GuestSession::tr("Error while creating guest process for copying file \"%s\" from guest to host: %Rrc"),
761 mSource.c_str(), rc));
762 break;
763 }
764
765 break;
766 }
767
768 if ( waitRes == ProcessWaitResult_StdOut
769 || waitRes == ProcessWaitResult_WaitFlagNotSupported)
770 {
771 /* If the guest does not support waiting for stdin, we now yield in
772 * order to reduce the CPU load due to busy waiting. */
773 if (waitRes == ProcessWaitResult_WaitFlagNotSupported)
774 RTThreadYield(); /* Optional, don't check rc. */
775
776 uint32_t cbRead = 0; /* readData can return with VWRN_GSTCTL_OBJECTSTATE_CHANGED. */
777 rc = pProcess->i_readData(OUTPUT_HANDLE_ID_STDOUT, sizeof(byBuf),
778 msTimeout, byBuf, sizeof(byBuf),
779 &cbRead, &guestRc);
780 if (RT_FAILURE(rc))
781 {
782 switch (rc)
783 {
784 case VERR_GSTCTL_GUEST_ERROR:
785 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
786 GuestProcess::i_guestErrorToString(guestRc));
787 break;
788
789 default:
790 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
791 Utf8StrFmt(GuestSession::tr("Reading from guest file \"%s\" (offset %RU64) failed: %Rrc"),
792 mSource.c_str(), cbWrittenTotal, rc));
793 break;
794 }
795
796 break;
797 }
798
799 if (cbRead)
800 {
801 rc = RTFileWrite(fileDest, byBuf, cbRead, NULL /* No partial writes */);
802 if (RT_FAILURE(rc))
803 {
804 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
805 Utf8StrFmt(GuestSession::tr("Writing to host file \"%s\" (%RU64 bytes left) failed: %Rrc"),
806 mDest.c_str(), cbToRead, rc));
807 break;
808 }
809
810 /* Only subtract bytes reported written by the guest. */
811 Assert(cbToRead >= cbRead);
812 cbToRead -= cbRead;
813
814 /* Update total bytes written to the guest. */
815 cbWrittenTotal += cbRead;
816 Assert(cbWrittenTotal <= (uint64_t)objData.mObjectSize);
817
818 /* Did the user cancel the operation above? */
819 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
820 && fCanceled)
821 break;
822
823 rc = setProgress((ULONG)(cbWrittenTotal / ((uint64_t)objData.mObjectSize / 100.0)));
824 if (RT_FAILURE(rc))
825 break;
826 }
827 }
828 else
829 {
830 break;
831 }
832
833 } /* for */
834
835 LogFlowThisFunc(("rc=%Rrc, guestrc=%Rrc, waitRes=%ld, cbWrittenTotal=%RU64, cbSize=%RI64, cbToRead=%RU64\n",
836 rc, guestRc, waitRes, cbWrittenTotal, objData.mObjectSize, cbToRead));
837
838 /*
839 * Wait on termination of guest process until it completed all operations.
840 */
841 if ( !fCanceled
842 || RT_SUCCESS(rc))
843 {
844 rc = pProcess->i_waitFor(ProcessWaitForFlag_Terminate, msTimeout, waitRes, &guestRc);
845 if ( RT_FAILURE(rc)
846 || waitRes != ProcessWaitResult_Terminate)
847 {
848 if (RT_FAILURE(rc))
849 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
850 Utf8StrFmt(
851 GuestSession::tr("Waiting on termination for copying file \"%s\" from guest failed: %Rrc"),
852 mSource.c_str(), rc));
853 else
854 {
855 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
856 Utf8StrFmt(GuestSession::tr(
857 "Waiting on termination for copying file \"%s\" from guest failed with wait result %ld"),
858 mSource.c_str(), waitRes));
859 rc = VERR_GENERAL_FAILURE; /* Fudge. */
860 }
861 }
862 }
863
864 if (RT_SUCCESS(rc))
865 {
866 /** @todo this code sequence is duplicated in CopyTo */
867 ProcessStatus_T procStatus = ProcessStatus_TerminatedAbnormally;
868 HRESULT hrc = pProcess->COMGETTER(Status(&procStatus));
869 if (!SUCCEEDED(hrc))
870 procStatus = ProcessStatus_TerminatedAbnormally;
871
872 LONG exitCode = 42424242;
873 hrc = pProcess->COMGETTER(ExitCode(&exitCode));
874 if (!SUCCEEDED(hrc))
875 exitCode = 42424242;
876
877 if ( procStatus != ProcessStatus_TerminatedNormally
878 || exitCode != 0)
879 {
880 LogFlowThisFunc(("procStatus=%d, exitCode=%d\n", procStatus, exitCode));
881 if (procStatus == ProcessStatus_TerminatedNormally)
882 rc = GuestProcessTool::i_exitCodeToRc(procInfo, exitCode);
883 else
884 rc = VERR_GENERAL_FAILURE;
885 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
886 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" to host failed: %Rrc"),
887 mSource.c_str(), rc));
888 }
889 /*
890 * Even if we succeeded until here make sure to check whether we really transfered
891 * everything.
892 */
893 else if ( objData.mObjectSize > 0
894 && cbWrittenTotal == 0)
895 {
896 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
897 * to the destination -> access denied. */
898 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
899 Utf8StrFmt(GuestSession::tr("Writing guest file \"%s\" to host to \"%s\" failed: Access denied"),
900 mSource.c_str(), mDest.c_str()));
901 rc = VERR_ACCESS_DENIED;
902 }
903 else if (cbWrittenTotal < (uint64_t)objData.mObjectSize)
904 {
905 /* If we did not copy all let the user know. */
906 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
907 Utf8StrFmt(GuestSession::tr("Copying guest file \"%s\" to host to \"%s\" failed (%RU64/%RI64 bytes transfered)"),
908 mSource.c_str(), mDest.c_str(), cbWrittenTotal, objData.mObjectSize));
909 rc = VERR_INTERRUPTED;
910 }
911 else
912 rc = setProgressSuccess();
913 }
914 }
915
916 RTFileClose(fileDest);
917 }
918 }
919
920 LogFlowFuncLeaveRC(rc);
921 return rc;
922}
923
924int SessionTaskCopyFrom::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
925{
926 LogFlowThisFunc(("strDesc=%s, strSource=%s, strDest=%s, uFlags=%x\n",
927 strDesc.c_str(), mSource.c_str(), mDest.c_str(), mFlags));
928
929 mDesc = strDesc;
930 mProgress = pProgress;
931
932 int rc = RTThreadCreate(NULL, SessionTaskCopyFrom::taskThread, this,
933 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
934 "gctlCpyFrom");
935 LogFlowFuncLeaveRC(rc);
936 return rc;
937}
938
939/* static */
940DECLCALLBACK(int) SessionTaskCopyFrom::taskThread(RTTHREAD Thread, void *pvUser)
941{
942 SessionTaskCopyFrom* task = static_cast<SessionTaskCopyFrom*>(pvUser);
943 AssertReturn(task, VERR_GENERAL_FAILURE);
944
945 LogFlowFunc(("pTask=%p\n", task));
946
947 return task->Run();
948}
949
950SessionTaskUpdateAdditions::SessionTaskUpdateAdditions(GuestSession *pSession,
951 const Utf8Str &strSource,
952 const ProcessArguments &aArguments,
953 uint32_t uFlags)
954 : GuestSessionTask(pSession)
955{
956 mSource = strSource;
957 mArguments = aArguments;
958 mFlags = uFlags;
959 m_strTaskName = "gctlUpGA";
960}
961
962SessionTaskUpdateAdditions::~SessionTaskUpdateAdditions(void)
963{
964
965}
966
967int SessionTaskUpdateAdditions::i_addProcessArguments(ProcessArguments &aArgumentsDest,
968 const ProcessArguments &aArgumentsSource)
969{
970 int rc = VINF_SUCCESS;
971
972 try
973 {
974 /* Filter out arguments which already are in the destination to
975 * not end up having them specified twice. Not the fastest method on the
976 * planet but does the job. */
977 ProcessArguments::const_iterator itSource = aArgumentsSource.begin();
978 while (itSource != aArgumentsSource.end())
979 {
980 bool fFound = false;
981 ProcessArguments::iterator itDest = aArgumentsDest.begin();
982 while (itDest != aArgumentsDest.end())
983 {
984 if ((*itDest).equalsIgnoreCase((*itSource)))
985 {
986 fFound = true;
987 break;
988 }
989 ++itDest;
990 }
991
992 if (!fFound)
993 aArgumentsDest.push_back((*itSource));
994
995 ++itSource;
996 }
997 }
998 catch(std::bad_alloc &)
999 {
1000 return VERR_NO_MEMORY;
1001 }
1002
1003 return rc;
1004}
1005
1006int SessionTaskUpdateAdditions::i_copyFileToGuest(GuestSession *pSession, PRTISOFSFILE pISO,
1007 Utf8Str const &strFileSource, const Utf8Str &strFileDest,
1008 bool fOptional, uint32_t *pcbSize)
1009{
1010 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1011 AssertPtrReturn(pISO, VERR_INVALID_POINTER);
1012 /* pcbSize is optional. */
1013
1014 uint32_t cbOffset;
1015 size_t cbSize;
1016
1017 int rc = RTIsoFsGetFileInfo(pISO, strFileSource.c_str(), &cbOffset, &cbSize);
1018 if (RT_FAILURE(rc))
1019 {
1020 if (fOptional)
1021 return VINF_SUCCESS;
1022
1023 return rc;
1024 }
1025
1026 Assert(cbOffset);
1027 Assert(cbSize);
1028 rc = RTFileSeek(pISO->file, cbOffset, RTFILE_SEEK_BEGIN, NULL);
1029
1030 HRESULT hr = S_OK;
1031 /* Copy over the Guest Additions file to the guest. */
1032 if (RT_SUCCESS(rc))
1033 {
1034 LogRel(("Copying Guest Additions installer file \"%s\" to \"%s\" on guest ...\n",
1035 strFileSource.c_str(), strFileDest.c_str()));
1036
1037 if (RT_SUCCESS(rc))
1038 {
1039 SessionTaskCopyTo *pTask = NULL;
1040 ComObjPtr<Progress> pProgressCopyTo;
1041 try
1042 {
1043 try
1044 {
1045 pTask = new SessionTaskCopyTo(pSession /* GuestSession */,
1046 &pISO->file, cbOffset, cbSize,
1047 strFileDest, FileCopyFlag_None);
1048 }
1049 catch(...)
1050 {
1051 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1052 GuestSession::tr("Failed to create SessionTaskCopyTo object "));
1053 throw;
1054 }
1055
1056 hr = pTask->Init(Utf8StrFmt(GuestSession::tr("Copying Guest Additions installer file \"%s\" to \"%s\" on guest"),
1057 mSource.c_str(), strFileDest.c_str()));
1058 if (FAILED(hr))
1059 {
1060 delete pTask;
1061 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1062 GuestSession::tr("Creating progress object for SessionTaskCopyTo object failed"));
1063 throw hr;
1064 }
1065
1066 hr = pTask->createThread(NULL, RTTHREADTYPE_MAIN_HEAVY_WORKER);
1067
1068 if (SUCCEEDED(hr))
1069 {
1070 /* Return progress to the caller. */
1071 pProgressCopyTo = pTask->GetProgressObject();
1072 }
1073 else
1074 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1075 GuestSession::tr("Starting thread for updating additions failed "));
1076 }
1077 catch(std::bad_alloc &)
1078 {
1079 hr = E_OUTOFMEMORY;
1080 }
1081 catch(HRESULT eHR)
1082 {
1083 hr = eHR;
1084 LogFlowThisFunc(("Exception was caught in the function \n"));
1085 }
1086
1087 if (SUCCEEDED(hr))
1088 {
1089 BOOL fCanceled = FALSE;
1090 hr = pProgressCopyTo->WaitForCompletion(-1);
1091 if ( SUCCEEDED(pProgressCopyTo->COMGETTER(Canceled)(&fCanceled))
1092 && fCanceled)
1093 {
1094 rc = VERR_GENERAL_FAILURE; /* Fudge. */
1095 }
1096 else if (FAILED(hr))
1097 {
1098 Assert(FAILED(hr));
1099 rc = VERR_GENERAL_FAILURE; /* Fudge. */
1100 }
1101 }
1102 }
1103 }
1104
1105 /** @todo Note: Since there is no file locking involved at the moment, there can be modifications
1106 * between finished copying, the verification and the actual execution. */
1107
1108 /* Determine where the installer image ended up and if it has the correct size. */
1109 if (RT_SUCCESS(rc))
1110 {
1111 LogRel(("Verifying Guest Additions installer file \"%s\" ...\n",
1112 strFileDest.c_str()));
1113
1114 GuestFsObjData objData;
1115 int64_t cbSizeOnGuest; int guestRc;
1116 rc = pSession->i_fileQuerySizeInternal(strFileDest, false /*fFollowSymlinks*/, &cbSizeOnGuest, &guestRc);
1117 if ( RT_SUCCESS(rc)
1118 && cbSize == (uint64_t)cbSizeOnGuest)
1119 {
1120 LogFlowThisFunc(("Guest Additions installer file \"%s\" successfully verified\n",
1121 strFileDest.c_str()));
1122 }
1123 else
1124 {
1125 if (RT_SUCCESS(rc)) /* Size does not match. */
1126 {
1127 LogRel(("Size of Guest Additions installer file \"%s\" does not match: %RI64 bytes copied, %RU64 bytes expected\n",
1128 strFileDest.c_str(), cbSizeOnGuest, cbSize));
1129 rc = VERR_BROKEN_PIPE; /** @todo Find a better error. */
1130 }
1131 else
1132 {
1133 switch (rc)
1134 {
1135 case VERR_GSTCTL_GUEST_ERROR:
1136 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1137 GuestProcess::i_guestErrorToString(guestRc));
1138 break;
1139
1140 default:
1141 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1142 Utf8StrFmt(GuestSession::tr("Error while querying size for file \"%s\": %Rrc"),
1143 strFileDest.c_str(), rc));
1144 break;
1145 }
1146 }
1147 }
1148
1149 if (RT_SUCCESS(rc))
1150 {
1151 if (pcbSize)
1152 *pcbSize = (uint32_t)cbSizeOnGuest;
1153 }
1154 }
1155
1156 return rc;
1157}
1158
1159int SessionTaskUpdateAdditions::i_runFileOnGuest(GuestSession *pSession, GuestProcessStartupInfo &procInfo)
1160{
1161 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
1162
1163 LogRel(("Running %s ...\n", procInfo.mName.c_str()));
1164
1165 GuestProcessTool procTool; int guestRc;
1166 int vrc = procTool.Init(pSession, procInfo, false /* Async */, &guestRc);
1167 if (RT_SUCCESS(vrc))
1168 {
1169 if (RT_SUCCESS(guestRc))
1170 vrc = procTool.i_wait(GUESTPROCESSTOOL_FLAG_NONE, &guestRc);
1171 if (RT_SUCCESS(vrc))
1172 vrc = procTool.i_terminatedOk();
1173 }
1174
1175 if (RT_FAILURE(vrc))
1176 {
1177 switch (vrc)
1178 {
1179 case VWRN_GSTCTL_PROCESS_EXIT_CODE:
1180 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1181 Utf8StrFmt(GuestSession::tr("Running update file \"%s\" on guest failed: %Rrc"),
1182 procInfo.mExecutable.c_str(), procTool.i_getRc()));
1183 break;
1184
1185 case VERR_GSTCTL_GUEST_ERROR:
1186 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1187 GuestProcess::i_guestErrorToString(guestRc));
1188 break;
1189
1190 case VERR_INVALID_STATE: /** @todo Special guest control rc needed! */
1191 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1192 Utf8StrFmt(GuestSession::tr("Update file \"%s\" reported invalid running state"),
1193 procInfo.mExecutable.c_str()));
1194 break;
1195
1196 default:
1197 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1198 Utf8StrFmt(GuestSession::tr("Error while running update file \"%s\" on guest: %Rrc"),
1199 procInfo.mExecutable.c_str(), vrc));
1200 break;
1201 }
1202 }
1203
1204 return vrc;
1205}
1206
1207int SessionTaskUpdateAdditions::Run(void)
1208{
1209 LogFlowThisFuncEnter();
1210
1211 ComObjPtr<GuestSession> pSession = mSession;
1212 Assert(!pSession.isNull());
1213
1214 AutoCaller autoCaller(pSession);
1215 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1216
1217 int rc = setProgress(10);
1218 if (RT_FAILURE(rc))
1219 return rc;
1220
1221 HRESULT hr = S_OK;
1222
1223 LogRel(("Automatic update of Guest Additions started, using \"%s\"\n", mSource.c_str()));
1224
1225 ComObjPtr<Guest> pGuest(mSession->i_getParent());
1226#if 0
1227 /*
1228 * Wait for the guest being ready within 30 seconds.
1229 */
1230 AdditionsRunLevelType_T addsRunLevel;
1231 uint64_t tsStart = RTTimeSystemMilliTS();
1232 while ( SUCCEEDED(hr = pGuest->COMGETTER(AdditionsRunLevel)(&addsRunLevel))
1233 && ( addsRunLevel != AdditionsRunLevelType_Userland
1234 && addsRunLevel != AdditionsRunLevelType_Desktop))
1235 {
1236 if ((RTTimeSystemMilliTS() - tsStart) > 30 * 1000)
1237 {
1238 rc = VERR_TIMEOUT;
1239 break;
1240 }
1241
1242 RTThreadSleep(100); /* Wait a bit. */
1243 }
1244
1245 if (FAILED(hr)) rc = VERR_TIMEOUT;
1246 if (rc == VERR_TIMEOUT)
1247 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1248 Utf8StrFmt(GuestSession::tr("Guest Additions were not ready within time, giving up")));
1249#else
1250 /*
1251 * For use with the GUI we don't want to wait, just return so that the manual .ISO mounting
1252 * can continue.
1253 */
1254 AdditionsRunLevelType_T addsRunLevel;
1255 if ( FAILED(hr = pGuest->COMGETTER(AdditionsRunLevel)(&addsRunLevel))
1256 || ( addsRunLevel != AdditionsRunLevelType_Userland
1257 && addsRunLevel != AdditionsRunLevelType_Desktop))
1258 {
1259 if (addsRunLevel == AdditionsRunLevelType_System)
1260 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1261 Utf8StrFmt(GuestSession::tr("Guest Additions are installed but not fully loaded yet, aborting automatic update")));
1262 else
1263 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1264 Utf8StrFmt(GuestSession::tr("Guest Additions not installed or ready, aborting automatic update")));
1265 rc = VERR_NOT_SUPPORTED;
1266 }
1267#endif
1268
1269 if (RT_SUCCESS(rc))
1270 {
1271 /*
1272 * Determine if we are able to update automatically. This only works
1273 * if there are recent Guest Additions installed already.
1274 */
1275 Utf8Str strAddsVer;
1276 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/Version", strAddsVer);
1277 if ( RT_SUCCESS(rc)
1278 && RTStrVersionCompare(strAddsVer.c_str(), "4.1") < 0)
1279 {
1280 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1281 Utf8StrFmt(GuestSession::tr("Guest has too old Guest Additions (%s) installed for automatic updating, please update manually"),
1282 strAddsVer.c_str()));
1283 rc = VERR_NOT_SUPPORTED;
1284 }
1285 }
1286
1287 Utf8Str strOSVer;
1288 eOSType osType = eOSType_Unknown;
1289 if (RT_SUCCESS(rc))
1290 {
1291 /*
1292 * Determine guest OS type and the required installer image.
1293 */
1294 Utf8Str strOSType;
1295 rc = getGuestProperty(pGuest, "/VirtualBox/GuestInfo/OS/Product", strOSType);
1296 if (RT_SUCCESS(rc))
1297 {
1298 if ( strOSType.contains("Microsoft", Utf8Str::CaseInsensitive)
1299 || strOSType.contains("Windows", Utf8Str::CaseInsensitive))
1300 {
1301 osType = eOSType_Windows;
1302
1303 /*
1304 * Determine guest OS version.
1305 */
1306 rc = getGuestProperty(pGuest, "/VirtualBox/GuestInfo/OS/Release", strOSVer);
1307 if (RT_FAILURE(rc))
1308 {
1309 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1310 Utf8StrFmt(GuestSession::tr("Unable to detected guest OS version, please update manually")));
1311 rc = VERR_NOT_SUPPORTED;
1312 }
1313
1314 /* Because Windows 2000 + XP and is bitching with WHQL popups even if we have signed drivers we
1315 * can't do automated updates here. */
1316 /* Windows XP 64-bit (5.2) is a Windows 2003 Server actually, so skip this here. */
1317 if ( RT_SUCCESS(rc)
1318 && RTStrVersionCompare(strOSVer.c_str(), "5.0") >= 0)
1319 {
1320 if ( strOSVer.startsWith("5.0") /* Exclude the build number. */
1321 || strOSVer.startsWith("5.1") /* Exclude the build number. */)
1322 {
1323 /* If we don't have AdditionsUpdateFlag_WaitForUpdateStartOnly set we can't continue
1324 * because the Windows Guest Additions installer will fail because of WHQL popups. If the
1325 * flag is set this update routine ends successfully as soon as the installer was started
1326 * (and the user has to deal with it in the guest). */
1327 if (!(mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly))
1328 {
1329 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1330 Utf8StrFmt(GuestSession::tr("Windows 2000 and XP are not supported for automatic updating due to WHQL interaction, please update manually")));
1331 rc = VERR_NOT_SUPPORTED;
1332 }
1333 }
1334 }
1335 else
1336 {
1337 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1338 Utf8StrFmt(GuestSession::tr("%s (%s) not supported for automatic updating, please update manually"),
1339 strOSType.c_str(), strOSVer.c_str()));
1340 rc = VERR_NOT_SUPPORTED;
1341 }
1342 }
1343 else if (strOSType.contains("Solaris", Utf8Str::CaseInsensitive))
1344 {
1345 osType = eOSType_Solaris;
1346 }
1347 else /* Everything else hopefully means Linux :-). */
1348 osType = eOSType_Linux;
1349
1350#if 1 /* Only Windows is supported (and tested) at the moment. */
1351 if ( RT_SUCCESS(rc)
1352 && osType != eOSType_Windows)
1353 {
1354 hr = setProgressErrorMsg(VBOX_E_NOT_SUPPORTED,
1355 Utf8StrFmt(GuestSession::tr("Detected guest OS (%s) does not support automatic Guest Additions updating, please update manually"),
1356 strOSType.c_str()));
1357 rc = VERR_NOT_SUPPORTED;
1358 }
1359#endif
1360 }
1361 }
1362
1363 RTISOFSFILE iso;
1364 if (RT_SUCCESS(rc))
1365 {
1366 /*
1367 * Try to open the .ISO file to extract all needed files.
1368 */
1369 rc = RTIsoFsOpen(&iso, mSource.c_str());
1370 if (RT_FAILURE(rc))
1371 {
1372 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1373 Utf8StrFmt(GuestSession::tr("Unable to open Guest Additions .ISO file \"%s\": %Rrc"),
1374 mSource.c_str(), rc));
1375 }
1376 else
1377 {
1378 /* Set default installation directories. */
1379 Utf8Str strUpdateDir = "/tmp/";
1380 if (osType == eOSType_Windows)
1381 strUpdateDir = "C:\\Temp\\";
1382
1383 rc = setProgress(5);
1384
1385 /* Try looking up the Guest Additions installation directory. */
1386 if (RT_SUCCESS(rc))
1387 {
1388 /* Try getting the installed Guest Additions version to know whether we
1389 * can install our temporary Guest Addition data into the original installation
1390 * directory.
1391 *
1392 * Because versions prior to 4.2 had bugs wrt spaces in paths we have to choose
1393 * a different location then.
1394 */
1395 bool fUseInstallDir = false;
1396
1397 Utf8Str strAddsVer;
1398 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/Version", strAddsVer);
1399 if ( RT_SUCCESS(rc)
1400 && RTStrVersionCompare(strAddsVer.c_str(), "4.2r80329") > 0)
1401 {
1402 fUseInstallDir = true;
1403 }
1404
1405 if (fUseInstallDir)
1406 {
1407 if (RT_SUCCESS(rc))
1408 rc = getGuestProperty(pGuest, "/VirtualBox/GuestAdd/InstallDir", strUpdateDir);
1409 if (RT_SUCCESS(rc))
1410 {
1411 if (osType == eOSType_Windows)
1412 {
1413 strUpdateDir.findReplace('/', '\\');
1414 strUpdateDir.append("\\Update\\");
1415 }
1416 else
1417 strUpdateDir.append("/update/");
1418 }
1419 }
1420 }
1421
1422 if (RT_SUCCESS(rc))
1423 LogRel(("Guest Additions update directory is: %s\n",
1424 strUpdateDir.c_str()));
1425
1426 /* Create the installation directory. */
1427 int guestRc;
1428 rc = pSession->i_directoryCreateInternal(strUpdateDir,
1429 755 /* Mode */, DirectoryCreateFlag_Parents, &guestRc);
1430 if (RT_FAILURE(rc))
1431 {
1432 switch (rc)
1433 {
1434 case VERR_GSTCTL_GUEST_ERROR:
1435 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1436 GuestProcess::i_guestErrorToString(guestRc));
1437 break;
1438
1439 default:
1440 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1441 Utf8StrFmt(GuestSession::tr("Error creating installation directory \"%s\" on the guest: %Rrc"),
1442 strUpdateDir.c_str(), rc));
1443 break;
1444 }
1445 }
1446 if (RT_SUCCESS(rc))
1447 rc = setProgress(10);
1448
1449 if (RT_SUCCESS(rc))
1450 {
1451 /* Prepare the file(s) we want to copy over to the guest and
1452 * (maybe) want to run. */
1453 switch (osType)
1454 {
1455 case eOSType_Windows:
1456 {
1457 /* Do we need to install our certificates? We do this for W2K and up. */
1458 bool fInstallCert = false;
1459
1460 /* Only Windows 2000 and up need certificates to be installed. */
1461 if (RTStrVersionCompare(strOSVer.c_str(), "5.0") >= 0)
1462 {
1463 fInstallCert = true;
1464 LogRel(("Certificates for auto updating WHQL drivers will be installed\n"));
1465 }
1466 else
1467 LogRel(("Skipping installation of certificates for WHQL drivers\n"));
1468
1469 if (fInstallCert)
1470 {
1471 /* Our certificate. */
1472 mFiles.push_back(InstallerFile("CERT/ORACLE_VBOX.CER",
1473 strUpdateDir + "oracle-vbox.cer",
1474 UPDATEFILE_FLAG_COPY_FROM_ISO | UPDATEFILE_FLAG_OPTIONAL));
1475 /* Our certificate installation utility. */
1476 /* First pass: Copy over the file + execute it to remove any existing
1477 * VBox certificates. */
1478 GuestProcessStartupInfo siCertUtilRem;
1479 siCertUtilRem.mName = "VirtualBox Certificate Utility, removing old VirtualBox certificates";
1480 siCertUtilRem.mArguments.push_back(Utf8Str("remove-trusted-publisher"));
1481 siCertUtilRem.mArguments.push_back(Utf8Str("--root")); /* Add root certificate as well. */
1482 siCertUtilRem.mArguments.push_back(Utf8Str(strUpdateDir + "oracle-vbox.cer"));
1483 siCertUtilRem.mArguments.push_back(Utf8Str(strUpdateDir + "oracle-vbox.cer"));
1484 mFiles.push_back(InstallerFile("CERT/VBOXCERTUTIL.EXE",
1485 strUpdateDir + "VBoxCertUtil.exe",
1486 UPDATEFILE_FLAG_COPY_FROM_ISO | UPDATEFILE_FLAG_EXECUTE |
1487 UPDATEFILE_FLAG_OPTIONAL,
1488 siCertUtilRem));
1489 /* Second pass: Only execute (but don't copy) again, this time installng the
1490 * recent certificates just copied over. */
1491 GuestProcessStartupInfo siCertUtilAdd;
1492 siCertUtilAdd.mName = "VirtualBox Certificate Utility, installing VirtualBox certificates";
1493 siCertUtilAdd.mArguments.push_back(Utf8Str("add-trusted-publisher"));
1494 siCertUtilAdd.mArguments.push_back(Utf8Str("--root")); /* Add root certificate as well. */
1495 siCertUtilAdd.mArguments.push_back(Utf8Str(strUpdateDir + "oracle-vbox.cer"));
1496 siCertUtilAdd.mArguments.push_back(Utf8Str(strUpdateDir + "oracle-vbox.cer"));
1497 mFiles.push_back(InstallerFile("CERT/VBOXCERTUTIL.EXE",
1498 strUpdateDir + "VBoxCertUtil.exe",
1499 UPDATEFILE_FLAG_EXECUTE | UPDATEFILE_FLAG_OPTIONAL,
1500 siCertUtilAdd));
1501 }
1502 /* The installers in different flavors, as we don't know (and can't assume)
1503 * the guest's bitness. */
1504 mFiles.push_back(InstallerFile("VBOXWINDOWSADDITIONS_X86.EXE",
1505 strUpdateDir + "VBoxWindowsAdditions-x86.exe",
1506 UPDATEFILE_FLAG_COPY_FROM_ISO));
1507 mFiles.push_back(InstallerFile("VBOXWINDOWSADDITIONS_AMD64.EXE",
1508 strUpdateDir + "VBoxWindowsAdditions-amd64.exe",
1509 UPDATEFILE_FLAG_COPY_FROM_ISO));
1510 /* The stub loader which decides which flavor to run. */
1511 GuestProcessStartupInfo siInstaller;
1512 siInstaller.mName = "VirtualBox Windows Guest Additions Installer";
1513 /* Set a running timeout of 5 minutes -- the Windows Guest Additions
1514 * setup can take quite a while, so be on the safe side. */
1515 siInstaller.mTimeoutMS = 5 * 60 * 1000;
1516 siInstaller.mArguments.push_back(Utf8Str("/S")); /* We want to install in silent mode. */
1517 siInstaller.mArguments.push_back(Utf8Str("/l")); /* ... and logging enabled. */
1518 /* Don't quit VBoxService during upgrade because it still is used for this
1519 * piece of code we're in right now (that is, here!) ... */
1520 siInstaller.mArguments.push_back(Utf8Str("/no_vboxservice_exit"));
1521 /* Tell the installer to report its current installation status
1522 * using a running VBoxTray instance via balloon messages in the
1523 * Windows taskbar. */
1524 siInstaller.mArguments.push_back(Utf8Str("/post_installstatus"));
1525 /* Add optional installer command line arguments from the API to the
1526 * installer's startup info. */
1527 rc = i_addProcessArguments(siInstaller.mArguments, mArguments);
1528 AssertRC(rc);
1529 /* If the caller does not want to wait for out guest update process to end,
1530 * complete the progress object now so that the caller can do other work. */
1531 if (mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly)
1532 siInstaller.mFlags |= ProcessCreateFlag_WaitForProcessStartOnly;
1533 mFiles.push_back(InstallerFile("VBOXWINDOWSADDITIONS.EXE",
1534 strUpdateDir + "VBoxWindowsAdditions.exe",
1535 UPDATEFILE_FLAG_COPY_FROM_ISO | UPDATEFILE_FLAG_EXECUTE, siInstaller));
1536 break;
1537 }
1538 case eOSType_Linux:
1539 /** @todo Add Linux support. */
1540 break;
1541 case eOSType_Solaris:
1542 /** @todo Add Solaris support. */
1543 break;
1544 default:
1545 AssertReleaseMsgFailed(("Unsupported guest type: %d\n", osType));
1546 break;
1547 }
1548 }
1549
1550 if (RT_SUCCESS(rc))
1551 {
1552 /* We want to spend 40% total for all copying operations. So roughly
1553 * calculate the specific percentage step of each copied file. */
1554 uint8_t uOffset = 20; /* Start at 20%. */
1555 uint8_t uStep = 40 / (uint8_t)mFiles.size(); Assert(mFiles.size() <= 10);
1556
1557 LogRel(("Copying over Guest Additions update files to the guest ...\n"));
1558
1559 std::vector<InstallerFile>::const_iterator itFiles = mFiles.begin();
1560 while (itFiles != mFiles.end())
1561 {
1562 if (itFiles->fFlags & UPDATEFILE_FLAG_COPY_FROM_ISO)
1563 {
1564 bool fOptional = false;
1565 if (itFiles->fFlags & UPDATEFILE_FLAG_OPTIONAL)
1566 fOptional = true;
1567 rc = i_copyFileToGuest(pSession, &iso, itFiles->strSource, itFiles->strDest,
1568 fOptional, NULL /* cbSize */);
1569 if (RT_FAILURE(rc))
1570 {
1571 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1572 Utf8StrFmt(GuestSession::tr("Error while copying file \"%s\" to \"%s\" on the guest: %Rrc"),
1573 itFiles->strSource.c_str(), itFiles->strDest.c_str(), rc));
1574 break;
1575 }
1576 }
1577
1578 rc = setProgress(uOffset);
1579 if (RT_FAILURE(rc))
1580 break;
1581 uOffset += uStep;
1582
1583 ++itFiles;
1584 }
1585 }
1586
1587 /* Done copying, close .ISO file. */
1588 RTIsoFsClose(&iso);
1589
1590 if (RT_SUCCESS(rc))
1591 {
1592 /* We want to spend 35% total for all copying operations. So roughly
1593 * calculate the specific percentage step of each copied file. */
1594 uint8_t uOffset = 60; /* Start at 60%. */
1595 uint8_t uStep = 35 / (uint8_t)mFiles.size(); Assert(mFiles.size() <= 10);
1596
1597 LogRel(("Executing Guest Additions update files ...\n"));
1598
1599 std::vector<InstallerFile>::iterator itFiles = mFiles.begin();
1600 while (itFiles != mFiles.end())
1601 {
1602 if (itFiles->fFlags & UPDATEFILE_FLAG_EXECUTE)
1603 {
1604 rc = i_runFileOnGuest(pSession, itFiles->mProcInfo);
1605 if (RT_FAILURE(rc))
1606 break;
1607 }
1608
1609 rc = setProgress(uOffset);
1610 if (RT_FAILURE(rc))
1611 break;
1612 uOffset += uStep;
1613
1614 ++itFiles;
1615 }
1616 }
1617
1618 if (RT_SUCCESS(rc))
1619 {
1620 LogRel(("Automatic update of Guest Additions succeeded\n"));
1621 rc = setProgressSuccess();
1622 }
1623 }
1624 }
1625
1626 if (RT_FAILURE(rc))
1627 {
1628 if (rc == VERR_CANCELLED)
1629 {
1630 LogRel(("Automatic update of Guest Additions was canceled\n"));
1631
1632 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
1633 Utf8StrFmt(GuestSession::tr("Installation was canceled")));
1634 }
1635 else
1636 {
1637 Utf8Str strError = Utf8StrFmt("No further error information available (%Rrc)", rc);
1638 if (!mProgress.isNull()) /* Progress object is optional. */
1639 {
1640 com::ProgressErrorInfo errorInfo(mProgress);
1641 if ( errorInfo.isFullAvailable()
1642 || errorInfo.isBasicAvailable())
1643 {
1644 strError = errorInfo.getText();
1645 }
1646 }
1647
1648 LogRel(("Automatic update of Guest Additions failed: %s (%Rhrc)\n",
1649 strError.c_str(), hr));
1650 }
1651
1652 LogRel(("Please install Guest Additions manually\n"));
1653 }
1654
1655 /** @todo Clean up copied / left over installation files. */
1656
1657 LogFlowFuncLeaveRC(rc);
1658 return rc;
1659}
1660
1661int SessionTaskUpdateAdditions::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
1662{
1663 LogFlowThisFunc(("strDesc=%s, strSource=%s, uFlags=%x\n",
1664 strDesc.c_str(), mSource.c_str(), mFlags));
1665
1666 mDesc = strDesc;
1667 mProgress = pProgress;
1668
1669 int rc = RTThreadCreate(NULL, SessionTaskUpdateAdditions::taskThread, this,
1670 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
1671 "gctlUpGA");
1672 LogFlowFuncLeaveRC(rc);
1673 return rc;
1674}
1675
1676/* static */
1677DECLCALLBACK(int) SessionTaskUpdateAdditions::taskThread(RTTHREAD Thread, void *pvUser)
1678{
1679 SessionTaskUpdateAdditions* task = static_cast<SessionTaskUpdateAdditions*>(pvUser);
1680 AssertReturn(task, VERR_GENERAL_FAILURE);
1681
1682 LogFlowFunc(("pTask=%p\n", task));
1683
1684 return task->Run();
1685}
1686
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