VirtualBox

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

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

Guest Control 2.0: Update.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.8 KB
Line 
1
2/* $Id: GuestSessionImplTasks.cpp 42810 2012-08-14 14:45:11Z vboxsync $ */
3/** @file
4 * VirtualBox Main - XXX.
5 */
6
7/*
8 * Copyright (C) 2012 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18
19
20/*******************************************************************************
21* Header Files *
22*******************************************************************************/
23#include "GuestImpl.h"
24#include "GuestSessionImpl.h"
25#include "GuestCtrlImplPrivate.h"
26
27#include "Global.h"
28#include "AutoCaller.h"
29#include "ProgressImpl.h"
30
31#include <memory> /* For auto_ptr. */
32
33#include <iprt/env.h>
34#include <iprt/file.h> /* For CopyTo/From. */
35
36
37/*
38 * If the following define is enabled the Guest Additions update code also
39 * checks for locations which were not supposed to happen due to not resolving
40 * environment variables in VBoxService toolbox arguments. So a file copied
41 * to "%TEMP%\foo.exe" became "C:\Windows\EMPfoo.exe".
42 */
43#define VBOX_SERVICE_ENVARG_BUG
44
45// session task classes
46/////////////////////////////////////////////////////////////////////////////
47
48GuestSessionTask::GuestSessionTask(GuestSession *pSession)
49{
50 mSession = pSession;
51}
52
53GuestSessionTask::~GuestSessionTask(void)
54{
55}
56
57int GuestSessionTask::setProgress(ULONG uPercent)
58{
59 if (mProgress.isNull()) /* Progress is optional. */
60 return VINF_SUCCESS;
61
62 BOOL fCanceled;
63 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
64 && fCanceled)
65 return VERR_CANCELLED;
66 BOOL fCompleted;
67 if ( SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
68 && !fCompleted)
69 return VINF_SUCCESS;
70 HRESULT hr = mProgress->SetCurrentOperationProgress(uPercent);
71 if (FAILED(hr))
72 return VERR_COM_UNEXPECTED;
73
74 return VINF_SUCCESS;
75}
76
77int GuestSessionTask::setProgressSuccess(void)
78{
79 if (mProgress.isNull()) /* Progress is optional. */
80 return VINF_SUCCESS;
81
82 BOOL fCanceled;
83 BOOL fCompleted;
84 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
85 && !fCanceled
86 && SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
87 && !fCompleted)
88 {
89 HRESULT hr = mProgress->notifyComplete(S_OK);
90 if (FAILED(hr))
91 return VERR_COM_UNEXPECTED; /** @todo Find a better rc. */
92 }
93
94 return VINF_SUCCESS;
95}
96
97HRESULT GuestSessionTask::setProgressErrorMsg(HRESULT hr, const Utf8Str &strMsg)
98{
99 if (mProgress.isNull()) /* Progress is optional. */
100 return hr; /* Return original rc. */
101
102 BOOL fCanceled;
103 BOOL fCompleted;
104 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
105 && !fCanceled
106 && SUCCEEDED(mProgress->COMGETTER(Completed(&fCompleted)))
107 && !fCompleted)
108 {
109 HRESULT hr2 = mProgress->notifyComplete(hr,
110 COM_IIDOF(IGuestSession),
111 GuestSession::getStaticComponentName(),
112 strMsg.c_str());
113 if (FAILED(hr2))
114 return hr2;
115 }
116 return hr; /* Return original rc. */
117}
118
119SessionTaskCopyTo::SessionTaskCopyTo(GuestSession *pSession,
120 const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags)
121 : mSource(strSource),
122 mDest(strDest),
123 mSourceFile(NULL),
124 mSourceOffset(0),
125 mSourceSize(0),
126 GuestSessionTask(pSession)
127{
128 mCopyFileFlags = uFlags;
129}
130
131/** @todo Merge this and the above call and let the above call do the open/close file handling so that the
132 * inner code only has to deal with file handles. No time now ... */
133SessionTaskCopyTo::SessionTaskCopyTo(GuestSession *pSession,
134 PRTFILE pSourceFile, size_t cbSourceOffset, uint64_t cbSourceSize,
135 const Utf8Str &strDest, uint32_t uFlags)
136 : GuestSessionTask(pSession)
137{
138 mSourceFile = pSourceFile;
139 mSourceOffset = cbSourceOffset;
140 mSourceSize = cbSourceSize;
141 mDest = strDest;
142 mCopyFileFlags = uFlags;
143}
144
145SessionTaskCopyTo::~SessionTaskCopyTo(void)
146{
147
148}
149
150int SessionTaskCopyTo::Run(void)
151{
152 LogFlowThisFuncEnter();
153
154 ComObjPtr<GuestSession> pSession = mSession;
155 Assert(!pSession.isNull());
156
157 AutoCaller autoCaller(pSession);
158 if (FAILED(autoCaller.rc())) return autoCaller.rc();
159
160 if (mCopyFileFlags)
161 {
162 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
163 Utf8StrFmt(GuestSession::tr("Copy flags (%#x) not implemented yet"),
164 mCopyFileFlags));
165 return VERR_INVALID_PARAMETER;
166 }
167
168 int rc;
169
170 RTFILE fileLocal;
171 PRTFILE pFile = &fileLocal;
172
173 if (!mSourceFile)
174 {
175 /* Does our source file exist? */
176 if (!RTFileExists(mSource.c_str()))
177 {
178 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
179 Utf8StrFmt(GuestSession::tr("Source file \"%s\" does not exist or is not a file"),
180 mSource.c_str()));
181 }
182 else
183 {
184 rc = RTFileOpen(pFile, mSource.c_str(),
185 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
186 if (RT_FAILURE(rc))
187 {
188 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
189 Utf8StrFmt(GuestSession::tr("Could not open source file \"%s\" for reading: %Rrc"),
190 mSource.c_str(), rc));
191 }
192 else
193 {
194 rc = RTFileGetSize(*pFile, &mSourceSize);
195 if (RT_FAILURE(rc))
196 {
197 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
198 Utf8StrFmt(GuestSession::tr("Could not query file size of \"%s\": %Rrc"),
199 mSource.c_str(), rc));
200 }
201 }
202 }
203 }
204 else
205 {
206 pFile = mSourceFile;
207 /* Size + offset are optional. */
208 }
209
210 GuestProcessStartupInfo procInfo;
211 procInfo.mName = Utf8StrFmt(GuestSession::tr("Copying file \"%s\" to the guest to \"%s\" (%RU64 bytes)"),
212 mSource.c_str(), mDest.c_str(), mSourceSize);
213 procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_CAT);
214 procInfo.mFlags = ProcessCreateFlag_Hidden;
215
216 /* Set arguments.*/
217 procInfo.mArguments.push_back(Utf8StrFmt("--output=%s", mDest.c_str())); /** @todo Do we need path conversion? */
218
219 /* Startup process. */
220 ComObjPtr<GuestProcess> pProcess;
221 rc = pSession->processCreateExInteral(procInfo, pProcess);
222 if (RT_SUCCESS(rc))
223 rc = pProcess->startProcess();
224 if (RT_FAILURE(rc))
225 {
226 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
227 Utf8StrFmt(GuestSession::tr("Unable to start guest process: %Rrc"), rc));
228 }
229 else
230 {
231 GuestProcessWaitResult waitRes;
232 BYTE byBuf[_64K];
233
234 BOOL fCanceled = FALSE;
235 uint64_t cbWrittenTotal = 0;
236 uint64_t cbToRead = mSourceSize;
237
238 for (;;)
239 {
240 rc = pProcess->waitFor(ProcessWaitForFlag_StdIn,
241 30 * 1000 /* Timeout */, waitRes);
242 if ( RT_FAILURE(rc)
243 || ( waitRes.mResult != ProcessWaitResult_StdIn
244 && waitRes.mResult != ProcessWaitResult_WaitFlagNotSupported))
245 {
246 break;
247 }
248
249 /* If the guest does not support waiting for stdin, we now yield in
250 * order to reduce the CPU load due to busy waiting. */
251 if (waitRes.mResult == ProcessWaitResult_WaitFlagNotSupported)
252 RTThreadYield(); /* Optional, don't check rc. */
253
254 size_t cbRead = 0;
255 if (mSourceSize) /* If we have nothing to write, take a shortcut. */
256 {
257 /** @todo Not very efficient, but works for now. */
258 rc = RTFileSeek(*pFile, mSourceOffset + cbWrittenTotal,
259 RTFILE_SEEK_BEGIN, NULL /* poffActual */);
260 if (RT_SUCCESS(rc))
261 {
262 rc = RTFileRead(*pFile, (uint8_t*)byBuf,
263 RT_MIN(cbToRead, sizeof(byBuf)), &cbRead);
264 /*
265 * Some other error occured? There might be a chance that RTFileRead
266 * could not resolve/map the native error code to an IPRT code, so just
267 * print a generic error.
268 */
269 if (RT_FAILURE(rc))
270 {
271 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
272 Utf8StrFmt(GuestSession::tr("Could not read from file \"%s\" (%Rrc)"),
273 mSource.c_str(), rc));
274 break;
275 }
276 }
277 else
278 {
279 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
280 Utf8StrFmt(GuestSession::tr("Seeking file \"%s\" offset %RU64 failed: %Rrc"),
281 mSource.c_str(), cbWrittenTotal, rc));
282 break;
283 }
284 }
285
286 uint32_t fFlags = ProcessInputFlag_None;
287
288 /* Did we reach the end of the content we want to transfer (last chunk)? */
289 if ( (cbRead < sizeof(byBuf))
290 /* Did we reach the last block which is exactly _64K? */
291 || (cbToRead - cbRead == 0)
292 /* ... or does the user want to cancel? */
293 || ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
294 && fCanceled)
295 )
296 {
297 fFlags |= ProcessInputFlag_EndOfFile;
298 }
299
300 uint32_t cbWritten;
301 Assert(sizeof(byBuf) >= cbRead);
302 rc = pProcess->writeData(0 /* StdIn */, fFlags,
303 byBuf, cbRead,
304 30 * 1000 /* Timeout */, &cbWritten);
305 if (RT_FAILURE(rc))
306 {
307 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
308 Utf8StrFmt(GuestSession::tr("Writing to file \"%s\" (offset %RU64) failed: %Rrc"),
309 mDest.c_str(), cbWrittenTotal, rc));
310 break;
311 }
312
313 LogFlowThisFunc(("cbWritten=%RU32, cbToRead=%RU64, cbWrittenTotal=%RU64, cbFileSize=%RU64\n",
314 cbWritten, cbToRead - cbWritten, cbWrittenTotal + cbWritten, mSourceSize));
315
316 /* Only subtract bytes reported written by the guest. */
317 Assert(cbToRead >= cbWritten);
318 cbToRead -= cbWritten;
319
320 /* Update total bytes written to the guest. */
321 cbWrittenTotal += cbWritten;
322 Assert(cbWrittenTotal <= mSourceSize);
323
324 /* Did the user cancel the operation above? */
325 if (fCanceled)
326 break;
327
328 /* Update the progress.
329 * Watch out for division by zero. */
330 mSourceSize > 0
331 ? rc = setProgress((ULONG)(cbWrittenTotal * 100 / mSourceSize))
332 : rc = setProgress(100);
333 if (RT_FAILURE(rc))
334 break;
335
336 /* End of file reached? */
337 if (!cbToRead)
338 break;
339 } /* for */
340
341 if ( !fCanceled
342 || RT_SUCCESS(rc))
343 {
344 /*
345 * Even if we succeeded until here make sure to check whether we really transfered
346 * everything.
347 */
348 if ( mSourceSize > 0
349 && cbWrittenTotal == 0)
350 {
351 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
352 * to the destination -> access denied. */
353 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
354 Utf8StrFmt(GuestSession::tr("Access denied when copying file \"%s\" to \"%s\""),
355 mSource.c_str(), mDest.c_str()));
356 }
357 else if (cbWrittenTotal < mSourceSize)
358 {
359 /* If we did not copy all let the user know. */
360 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
361 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" failed (%RU64/%RU64 bytes transfered)"),
362 mSource.c_str(), cbWrittenTotal, mSourceSize));
363 }
364 else
365 {
366 rc = pProcess->waitFor(ProcessWaitForFlag_Terminate,
367 30 * 1000 /* Timeout */, waitRes);
368 if ( RT_FAILURE(rc)
369 || waitRes.mResult != ProcessWaitResult_Terminate)
370 {
371 if (RT_FAILURE(rc))
372 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
373 Utf8StrFmt(GuestSession::tr("Waiting on termination for copying file \"%s\" failed: %Rrc"),
374 mSource.c_str(), rc));
375 else
376 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
377 Utf8StrFmt(GuestSession::tr("Waiting on termination for copying file \"%s\" failed with wait result %ld"),
378 mSource.c_str(), waitRes.mResult));
379 }
380
381 if (RT_SUCCESS(rc))
382 {
383 ProcessStatus_T procStatus;
384 LONG exitCode;
385 if ( ( SUCCEEDED(pProcess->COMGETTER(Status(&procStatus)))
386 && procStatus != ProcessStatus_TerminatedNormally)
387 || ( SUCCEEDED(pProcess->COMGETTER(ExitCode(&exitCode)))
388 && exitCode != 0)
389 )
390 {
391 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
392 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" failed with status %ld, exit code %ld"),
393 mSource.c_str(), procStatus, exitCode)); /**@todo Add stringify methods! */
394 }
395 }
396
397 if (RT_SUCCESS(rc))
398 rc = setProgressSuccess();
399 }
400 }
401
402 pProcess->close();
403 } /* processCreateExInteral */
404
405 if (!mSourceFile) /* Only close locally opened files. */
406 RTFileClose(*pFile);
407
408 LogFlowFuncLeaveRC(rc);
409 return rc;
410}
411
412int SessionTaskCopyTo::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
413{
414 LogFlowThisFunc(("strDesc=%s, strSource=%s, strDest=%s, mCopyFileFlags=%x\n",
415 strDesc.c_str(), mSource.c_str(), mDest.c_str(), mCopyFileFlags));
416
417 mDesc = strDesc;
418 mProgress = pProgress;
419
420 int rc = RTThreadCreate(NULL, SessionTaskCopyTo::taskThread, this,
421 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
422 "gctlCpyTo");
423 LogFlowFuncLeaveRC(rc);
424 return rc;
425}
426
427/* static */
428int SessionTaskCopyTo::taskThread(RTTHREAD Thread, void *pvUser)
429{
430 std::auto_ptr<SessionTaskCopyTo> task(static_cast<SessionTaskCopyTo*>(pvUser));
431 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
432
433 LogFlowFunc(("pTask=%p\n", task.get()));
434 return task->Run();
435}
436
437SessionTaskCopyFrom::SessionTaskCopyFrom(GuestSession *pSession,
438 const Utf8Str &strSource, const Utf8Str &strDest, uint32_t uFlags)
439 : GuestSessionTask(pSession)
440{
441 mSource = strSource;
442 mDest = strDest;
443 mFlags = uFlags;
444}
445
446SessionTaskCopyFrom::~SessionTaskCopyFrom(void)
447{
448
449}
450
451int SessionTaskCopyFrom::Run(void)
452{
453 LogFlowThisFuncEnter();
454
455 ComObjPtr<GuestSession> pSession = mSession;
456 Assert(!pSession.isNull());
457
458 AutoCaller autoCaller(pSession);
459 if (FAILED(autoCaller.rc())) return autoCaller.rc();
460
461 /*
462 * Note: There will be races between querying file size + reading the guest file's
463 * content because we currently *do not* lock down the guest file when doing the
464 * actual operations.
465 ** @todo Implement guest file locking!
466 */
467 GuestFsObjData objData;
468 int rc = pSession->fileQueryInfoInternal(Utf8Str(mSource), objData);
469 if (RT_FAILURE(rc))
470 {
471 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
472 Utf8StrFmt(GuestSession::tr("Querying guest file information for \"%s\" failed: %Rrc"),
473 mSource.c_str(), rc));
474 }
475 else if (objData.mType != FsObjType_File) /* Only single files are supported at the moment. */
476 {
477 rc = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
478 Utf8StrFmt(GuestSession::tr("Object \"%s\" on the guest is not a file"), mSource.c_str()));
479 }
480
481 if (RT_SUCCESS(rc))
482 {
483 RTFILE fileDest;
484 rc = RTFileOpen(&fileDest, mDest.c_str(),
485 RTFILE_O_WRITE | RTFILE_O_OPEN_CREATE | RTFILE_O_DENY_WRITE); /** @todo Use the correct open modes! */
486 if (RT_FAILURE(rc))
487 {
488 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
489 Utf8StrFmt(GuestSession::tr("Error opening destination file \"%s\": %Rrc"),
490 mDest.c_str(), rc));
491 }
492 else
493 {
494 GuestProcessStartupInfo procInfo;
495 procInfo.mName = Utf8StrFmt(GuestSession::tr("Copying file \"%s\" from guest to the host to \"%s\" (%RI64 bytes)"),
496 mSource.c_str(), mDest.c_str(), objData.mObjectSize);
497 procInfo.mCommand = Utf8Str(VBOXSERVICE_TOOL_CAT);
498 procInfo.mFlags = ProcessCreateFlag_Hidden | ProcessCreateFlag_WaitForStdOut;
499
500 /* Set arguments.*/
501 procInfo.mArguments.push_back(mSource); /* Which file to output? */
502
503 /* Startup process. */
504 ComObjPtr<GuestProcess> pProcess;
505 rc = pSession->processCreateExInteral(procInfo, pProcess);
506 if (RT_SUCCESS(rc))
507 rc = pProcess->startProcess();
508 if (RT_FAILURE(rc))
509 {
510 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
511 Utf8StrFmt(GuestSession::tr("Unable to start guest process for copying data from guest to host: %Rrc"), rc));
512 }
513 else
514 {
515 GuestProcessWaitResult waitRes;
516 BYTE byBuf[_64K];
517
518 BOOL fCanceled = FALSE;
519 uint64_t cbWrittenTotal = 0;
520 uint64_t cbToRead = objData.mObjectSize;
521
522 for (;;)
523 {
524 rc = pProcess->waitFor(ProcessWaitForFlag_StdOut,
525 30 * 1000 /* Timeout */, waitRes);
526 if ( waitRes.mResult == ProcessWaitResult_StdOut
527 || waitRes.mResult == ProcessWaitResult_WaitFlagNotSupported)
528 {
529 /* If the guest does not support waiting for stdin, we now yield in
530 * order to reduce the CPU load due to busy waiting. */
531 if (waitRes.mResult == ProcessWaitResult_WaitFlagNotSupported)
532 RTThreadYield(); /* Optional, don't check rc. */
533
534 size_t cbRead;
535 rc = pProcess->readData(OUTPUT_HANDLE_ID_STDOUT, sizeof(byBuf),
536 30 * 1000 /* Timeout */, byBuf, sizeof(byBuf),
537 &cbRead);
538 if (RT_FAILURE(rc))
539 {
540 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
541 Utf8StrFmt(GuestSession::tr("Reading from file \"%s\" (offset %RU64) failed: %Rrc"),
542 mSource.c_str(), cbWrittenTotal, rc));
543 break;
544 }
545
546 if (cbRead)
547 {
548 rc = RTFileWrite(fileDest, byBuf, cbRead, NULL /* No partial writes */);
549 if (RT_FAILURE(rc))
550 {
551 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
552 Utf8StrFmt(GuestSession::tr("Error writing to file \"%s\" (%RU64 bytes left): %Rrc"),
553 mDest.c_str(), cbToRead, rc));
554 break;
555 }
556
557 /* Only subtract bytes reported written by the guest. */
558 Assert(cbToRead >= cbRead);
559 cbToRead -= cbRead;
560
561 /* Update total bytes written to the guest. */
562 cbWrittenTotal += cbRead;
563 Assert(cbWrittenTotal <= (uint64_t)objData.mObjectSize);
564
565 /* Did the user cancel the operation above? */
566 if ( SUCCEEDED(mProgress->COMGETTER(Canceled(&fCanceled)))
567 && fCanceled)
568 break;
569
570 rc = setProgress((ULONG)(cbWrittenTotal / ((uint64_t)objData.mObjectSize / 100.0)));
571 if (RT_FAILURE(rc))
572 break;
573 }
574 }
575 else if ( RT_FAILURE(rc)
576 || waitRes.mResult == ProcessWaitResult_Terminate
577 || waitRes.mResult == ProcessWaitResult_Error
578 || waitRes.mResult == ProcessWaitResult_Timeout)
579 {
580 if (RT_FAILURE(waitRes.mRC))
581 rc = waitRes.mRC;
582 break;
583 }
584 } /* for */
585
586 LogFlowThisFunc(("rc=%Rrc, cbWrittenTotal=%RU64, cbSize=%RI64, cbToRead=%RU64\n",
587 rc, cbWrittenTotal, objData.mObjectSize, cbToRead));
588
589 if ( !fCanceled
590 || RT_SUCCESS(rc))
591 {
592 /*
593 * Even if we succeeded until here make sure to check whether we really transfered
594 * everything.
595 */
596 if ( objData.mObjectSize > 0
597 && cbWrittenTotal == 0)
598 {
599 /* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
600 * to the destination -> access denied. */
601 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
602 Utf8StrFmt(GuestSession::tr("Access denied when copying file \"%s\" to \"%s\""),
603 mSource.c_str(), mDest.c_str()));
604 rc = VERR_GENERAL_FAILURE; /* Fudge. */
605 }
606 else if (cbWrittenTotal < (uint64_t)objData.mObjectSize)
607 {
608 /* If we did not copy all let the user know. */
609 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
610 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" failed (%RU64/%RI64 bytes transfered)"),
611 mSource.c_str(), cbWrittenTotal, objData.mObjectSize));
612 rc = VERR_GENERAL_FAILURE; /* Fudge. */
613 }
614 else
615 {
616 ProcessStatus_T procStatus;
617 LONG exitCode;
618 if ( ( SUCCEEDED(pProcess->COMGETTER(Status(&procStatus)))
619 && procStatus != ProcessStatus_TerminatedNormally)
620 || ( SUCCEEDED(pProcess->COMGETTER(ExitCode(&exitCode)))
621 && exitCode != 0)
622 )
623 {
624 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
625 Utf8StrFmt(GuestSession::tr("Copying file \"%s\" failed with status %ld, exit code %d"),
626 mSource.c_str(), procStatus, exitCode)); /**@todo Add stringify methods! */
627 rc = VERR_GENERAL_FAILURE; /* Fudge. */
628 }
629 else /* Yay, success! */
630 rc = setProgressSuccess();
631 }
632 }
633
634 pProcess->close();
635 }
636
637 RTFileClose(fileDest);
638 }
639 }
640
641 LogFlowFuncLeaveRC(rc);
642 return rc;
643}
644
645int SessionTaskCopyFrom::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
646{
647 LogFlowThisFunc(("strDesc=%s, strSource=%s, strDest=%s, uFlags=%x\n",
648 strDesc.c_str(), mSource.c_str(), mDest.c_str(), mFlags));
649
650 mDesc = strDesc;
651 mProgress = pProgress;
652
653 int rc = RTThreadCreate(NULL, SessionTaskCopyFrom::taskThread, this,
654 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
655 "gctlCpyFrom");
656 LogFlowFuncLeaveRC(rc);
657 return rc;
658}
659
660/* static */
661int SessionTaskCopyFrom::taskThread(RTTHREAD Thread, void *pvUser)
662{
663 std::auto_ptr<SessionTaskCopyFrom> task(static_cast<SessionTaskCopyFrom*>(pvUser));
664 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
665
666 LogFlowFunc(("pTask=%p\n", task.get()));
667 return task->Run();
668}
669
670SessionTaskUpdateAdditions::SessionTaskUpdateAdditions(GuestSession *pSession,
671 const Utf8Str &strSource, uint32_t uFlags)
672 : GuestSessionTask(pSession)
673{
674 mSource = strSource;
675 mFlags = uFlags;
676}
677
678SessionTaskUpdateAdditions::~SessionTaskUpdateAdditions(void)
679{
680
681}
682
683int SessionTaskUpdateAdditions::copyFileToGuest(GuestSession *pSession, PRTISOFSFILE pISO,
684 Utf8Str const &strFileSource, const Utf8Str &strFileDest,
685 bool fOptional, uint32_t *pcbSize)
686{
687 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
688 AssertPtrReturn(pISO, VERR_INVALID_POINTER);
689 /* pcbSize is optional. */
690
691 uint32_t cbOffset;
692 size_t cbSize;
693
694 int rc = RTIsoFsGetFileInfo(pISO, strFileSource.c_str(), &cbOffset, &cbSize);
695 if (RT_FAILURE(rc))
696 {
697 if (fOptional)
698 return VINF_SUCCESS;
699
700 return rc;
701 }
702
703 Assert(cbOffset);
704 Assert(cbSize);
705 rc = RTFileSeek(pISO->file, cbOffset, RTFILE_SEEK_BEGIN, NULL);
706
707 /* Copy over the Guest Additions file to the guest. */
708 if (RT_SUCCESS(rc))
709 {
710 LogRel(("Copying Guest Additions installer file \"%s\" to \"%s\" on guest ...\n",
711 strFileSource.c_str(), strFileDest.c_str()));
712
713 if (RT_SUCCESS(rc))
714 {
715 SessionTaskCopyTo *pTask = new SessionTaskCopyTo(pSession /* GuestSession */,
716 &pISO->file, cbOffset, cbSize,
717 strFileDest, CopyFileFlag_None);
718 AssertPtrReturn(pTask, VERR_NO_MEMORY);
719
720 ComObjPtr<Progress> pProgressCopyTo;
721 rc = pSession->startTaskAsync(Utf8StrFmt(GuestSession::tr("Copying Guest Additions installer file \"%s\" to \"%s\" on guest"),
722 mSource.c_str(), strFileDest.c_str()),
723 pTask, pProgressCopyTo);
724 if (RT_SUCCESS(rc))
725 {
726 BOOL fCanceled = FALSE;
727 HRESULT hr = pProgressCopyTo->WaitForCompletion(-1);
728 if ( SUCCEEDED(pProgressCopyTo->COMGETTER(Canceled)(&fCanceled))
729 && fCanceled)
730 {
731 rc = VERR_GENERAL_FAILURE; /* Fudge. */
732 }
733 else if (FAILED(hr))
734 {
735 Assert(FAILED(hr));
736 rc = VERR_GENERAL_FAILURE; /* Fudge. */
737 }
738 }
739 }
740 }
741
742 /** @todo Note: Since there is no file locking involved at the moment, there can be modifications
743 * between finished copying, the verification and the actual execution. */
744
745 /* Determine where the installer image ended up and if it has the correct size. */
746 if (RT_SUCCESS(rc))
747 {
748 LogRel(("Verifying Guest Additions installer file \"%s\" ...\n", strFileDest.c_str()));
749
750 GuestFsObjData objData;
751 int64_t cbSizeOnGuest;
752 rc = pSession->fileQuerySizeInternal(strFileDest, &cbSizeOnGuest);
753#ifdef VBOX_SERVICE_ENVARG_BUG
754 if (RT_FAILURE(rc))
755 {
756 /* Ugly hack: Because older Guest Additions have problems with environment variable
757 expansion in parameters we have to check an alternative location on Windows.
758 So check for "%TEMP%\" being "C:\\Windows\\system32\\EMP" actually. */
759 if (strFileDest.startsWith("%TEMP%\\", RTCString::CaseSensitive))
760 {
761 Utf8Str strFileDestBug = "C:\\Windows\\system32\\EMP" + strFileDest.substr(sizeof("%TEMP%\\") - sizeof(char));
762 rc = pSession->fileQuerySizeInternal(strFileDestBug, &cbSizeOnGuest);
763 }
764 }
765#endif
766 if ( RT_SUCCESS(rc)
767 && cbSize == (uint64_t)cbSizeOnGuest)
768 {
769 LogRel(("Guest Additions installer file \"%s\" successfully verified\n",
770 strFileDest.c_str()));
771 }
772 else
773 {
774 if (RT_SUCCESS(rc)) /* Size does not match. */
775 rc = VERR_BROKEN_PIPE; /** @todo FInd a better error. */
776 }
777
778 if (RT_SUCCESS(rc))
779 {
780 if (pcbSize)
781 *pcbSize = cbSizeOnGuest;
782 }
783 }
784
785 return rc;
786}
787
788int SessionTaskUpdateAdditions::runFile(GuestSession *pSession, GuestProcessStartupInfo &procInfo)
789{
790 AssertPtrReturn(pSession, VERR_INVALID_POINTER);
791
792#ifdef VBOX_SERVICE_ENVARG_BUG
793 GuestFsObjData objData;
794 int rc = pSession->fileQueryInfoInternal(procInfo.mCommand, objData);
795 if (RT_FAILURE(rc))
796 procInfo.mCommand = "C:\\Windows\\system32\\EMP" + procInfo.mCommand.substr(sizeof("%TEMP%\\") - sizeof(char));
797#endif
798
799 ComObjPtr<GuestProcess> pProcess;
800 rc = pSession->processCreateExInteral(procInfo, pProcess);
801 if (RT_SUCCESS(rc))
802 rc = pProcess->startProcess();
803
804 if (RT_SUCCESS(rc))
805 {
806 LogRel(("Running %s ...\n", procInfo.mName.c_str()));
807
808 GuestProcessWaitResult waitRes;
809 rc = pProcess->waitFor(ProcessWaitForFlag_Terminate,
810 10 * 60 * 1000 /* 10 mins Timeout */, waitRes);
811 if (waitRes.mResult == ProcessWaitResult_Terminate)
812 {
813 ProcessStatus_T procStatus;
814 LONG exitCode;
815 if ( ( SUCCEEDED(pProcess->COMGETTER(Status(&procStatus)))
816 && procStatus != ProcessStatus_TerminatedNormally)
817 || ( SUCCEEDED(pProcess->COMGETTER(ExitCode(&exitCode)))
818 && exitCode != 0)
819 )
820 {
821 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
822 Utf8StrFmt(GuestSession::tr("Running %s failed with status %ld, exit code %ld"),
823 procInfo.mName.c_str(), procStatus, exitCode));
824 rc = VERR_GENERAL_FAILURE; /* Fudge. */
825 }
826 else /* Yay, success! */
827 {
828 LogRel(("%s successfully completed\n", procInfo.mName.c_str()));
829 }
830 }
831 else
832 {
833 if (RT_FAILURE(rc))
834 setProgressErrorMsg(VBOX_E_IPRT_ERROR,
835 Utf8StrFmt(GuestSession::tr("Error while waiting running %s: %Rrc"),
836 procInfo.mName.c_str(), rc));
837 else
838 {
839 setProgressErrorMsg(VBOX_E_IPRT_ERROR, pProcess->errorMsg());
840 rc = VERR_GENERAL_FAILURE; /* Fudge. */
841 }
842 }
843 }
844
845 if (!pProcess.isNull())
846 pProcess->close();
847
848 return rc;
849}
850
851int SessionTaskUpdateAdditions::Run(void)
852{
853 LogFlowThisFuncEnter();
854
855 ComObjPtr<GuestSession> pSession = mSession;
856 Assert(!pSession.isNull());
857
858 AutoCaller autoCaller(pSession);
859 if (FAILED(autoCaller.rc())) return autoCaller.rc();
860
861 int rc = setProgress(10);
862 if (RT_FAILURE(rc))
863 return rc;
864
865 HRESULT hr = S_OK;
866
867 LogRel(("Automatic update of Guest Additions started, using \"%s\"\n", mSource.c_str()));
868
869 /*
870 * Determine guest OS type and the required installer image.
871 * At the moment only Windows guests are supported.
872 */
873 Utf8Str strInstallerImage;
874
875 ComObjPtr<Guest> pGuest(mSession->getParent());
876 Bstr osTypeId;
877 if ( SUCCEEDED(pGuest->COMGETTER(OSTypeId(osTypeId.asOutParam())))
878 && !osTypeId.isEmpty())
879 {
880 Utf8Str osTypeIdUtf8(osTypeId); /* Needed for .contains(). */
881 if ( osTypeIdUtf8.contains("Microsoft", Utf8Str::CaseInsensitive)
882 || osTypeIdUtf8.contains("Windows", Utf8Str::CaseInsensitive))
883 {
884 if (osTypeIdUtf8.contains("64", Utf8Str::CaseInsensitive))
885 strInstallerImage = "VBOXWINDOWSADDITIONS_AMD64.EXE";
886 else
887 strInstallerImage = "VBOXWINDOWSADDITIONS_X86.EXE";
888 /* Since the installers are located in the root directory,
889 * no further path processing needs to be done (yet). */
890 }
891 else /* Everything else is not supported (yet). */
892 {
893 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
894 Utf8StrFmt(GuestSession::tr("Detected guest OS (%s) does not support automatic Guest Additions updating, please update manually"),
895 osTypeIdUtf8.c_str()));
896 rc = VERR_GENERAL_FAILURE; /* Fudge. */
897 }
898 }
899 else
900 {
901 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
902 Utf8StrFmt(GuestSession::tr("Could not detected guest OS type/version, please update manually")));
903 rc = VERR_GENERAL_FAILURE; /* Fudge. */
904 }
905
906 RTISOFSFILE iso;
907 if (RT_SUCCESS(rc))
908 {
909 Assert(!strInstallerImage.isEmpty());
910
911 /*
912 * Try to open the .ISO file and locate the specified installer.
913 */
914 rc = RTIsoFsOpen(&iso, mSource.c_str());
915 if (RT_FAILURE(rc))
916 {
917 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
918 Utf8StrFmt(GuestSession::tr("Unable to open Guest Additions .ISO file \"%s\": %Rrc"),
919 mSource.c_str(), rc));
920 }
921 else
922 {
923 rc = setProgress(5);
924
925 /** @todo Add support for non-Windows as well! */
926 Utf8Str strInstallerDest = "%TEMP%\\VBoxWindowsAdditions.exe";
927 bool fInstallCertificates = false;
928
929 if (RT_SUCCESS(rc))
930 {
931 /*
932 * Copy over main installer to the guest.
933 */
934 rc = copyFileToGuest(pSession, &iso, strInstallerImage, strInstallerDest,
935 false /* File is not optional */, NULL /* cbSize */);
936 if (RT_SUCCESS(rc))
937 rc = setProgress(20);
938
939 /*
940 * Install needed certificates for the WHQL crap.
941 ** @todo Only for Windows!
942 */
943 if (RT_SUCCESS(rc))
944 {
945 rc = copyFileToGuest(pSession, &iso, "CERT/ORACLE_VBOX.CER", "%TEMP%\\oracle-vbox.cer",
946 true /* File is optional */, NULL /* cbSize */);
947 if (RT_SUCCESS(rc))
948 {
949 rc = setProgress(30);
950 if (RT_SUCCESS(rc))
951 {
952 rc = copyFileToGuest(pSession, &iso, "CERT/VBOXCERTUTIL.EXE", "%TEMP%\\VBoxCertUtil.exe",
953 true /* File is optional */, NULL /* cbSize */);
954 if (RT_SUCCESS(rc))
955 {
956 fInstallCertificates = true;
957 rc = setProgress(40);
958 }
959 else
960 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
961 Utf8StrFmt(GuestSession::tr("Error while copying certificate installation tool to the guest: %Rrc"), rc));
962 }
963 }
964 else
965 hr = setProgressErrorMsg(VBOX_E_IPRT_ERROR,
966 Utf8StrFmt(GuestSession::tr("Error while copying certificate to the guest: %Rrc"), rc));
967 }
968 }
969
970 /*
971 * Run VBoxCertUtil.exe to install the Oracle certificate.
972 */
973 if ( RT_SUCCESS(rc)
974 && fInstallCertificates)
975 {
976 LogRel(("Installing certificates on the guest ...\n"));
977
978 GuestProcessStartupInfo procInfo;
979 procInfo.mName = Utf8StrFmt(GuestSession::tr("VirtualBox Guest Additions Certificate Utility"));
980 procInfo.mCommand = Utf8Str("%TEMP%\\VBoxCertUtil.exe");
981 procInfo.mFlags = ProcessCreateFlag_Hidden;
982
983 /* Construct arguments. */
984 /** @todo Remove hardcoded paths. */
985 procInfo.mArguments.push_back(Utf8Str("add-trusted-publisher"));
986 /* Ugly hack: Because older Guest Additions have problems with environment variable
987 expansion in parameters we have to check an alternative location on Windows.
988 So check for "%TEMP%\VBoxWindowsAdditions.exe" in a screwed up way. */
989#ifdef VBOX_SERVICE_ENVARG_BUG
990 GuestFsObjData objData;
991 rc = pSession->fileQueryInfoInternal("%TEMP%\\oracle-vbox.cer", objData);
992 if (RT_SUCCESS(rc))
993#endif
994 procInfo.mArguments.push_back(Utf8Str("%TEMP%\\oracle-vbox.cer"));
995#ifdef VBOX_SERVICE_ENVARG_BUG
996 else
997 procInfo.mArguments.push_back(Utf8Str("C:\\Windows\\system32\\EMPoracle-vbox.cer"));
998#endif
999 /* Overwrite rc in any case. */
1000 rc = runFile(pSession, procInfo);
1001 }
1002
1003 if (RT_SUCCESS(rc))
1004 rc = setProgress(60);
1005
1006 if (RT_SUCCESS(rc))
1007 {
1008 LogRel(("Updating Guest Additions ...\n"));
1009
1010 GuestProcessStartupInfo procInfo;
1011 procInfo.mName = Utf8StrFmt(GuestSession::tr("VirtualBox Guest Additions Setup"));
1012 procInfo.mCommand = Utf8Str(strInstallerDest);
1013 procInfo.mFlags = ProcessCreateFlag_Hidden;
1014 /* If the caller does not want to wait for out guest update process to end,
1015 * complete the progress object now so that the caller can do other work. */
1016 if (mFlags & AdditionsUpdateFlag_WaitForUpdateStartOnly)
1017 procInfo.mFlags |= ProcessCreateFlag_WaitForProcessStartOnly;
1018
1019 /* Construct arguments. */
1020 procInfo.mArguments.push_back(Utf8Str("/S")); /* We want to install in silent mode. */
1021 procInfo.mArguments.push_back(Utf8Str("/l")); /* ... and logging enabled. */
1022 /* Don't quit VBoxService during upgrade because it still is used for this
1023 * piece of code we're in right now (that is, here!) ... */
1024 procInfo.mArguments.push_back(Utf8Str("/no_vboxservice_exit"));
1025 /* Tell the installer to report its current installation status
1026 * using a running VBoxTray instance via balloon messages in the
1027 * Windows taskbar. */
1028 procInfo.mArguments.push_back(Utf8Str("/post_installstatus"));
1029
1030 rc = runFile(pSession, procInfo);
1031 if (RT_SUCCESS(rc))
1032 hr = setProgressSuccess();
1033 }
1034 RTIsoFsClose(&iso);
1035 }
1036 }
1037
1038 LogFlowFuncLeaveRC(rc);
1039 return rc;
1040}
1041
1042int SessionTaskUpdateAdditions::RunAsync(const Utf8Str &strDesc, ComObjPtr<Progress> &pProgress)
1043{
1044 LogFlowThisFunc(("strDesc=%s, strSource=%s, uFlags=%x\n",
1045 strDesc.c_str(), mSource.c_str(), mFlags));
1046
1047 mDesc = strDesc;
1048 mProgress = pProgress;
1049
1050 int rc = RTThreadCreate(NULL, SessionTaskUpdateAdditions::taskThread, this,
1051 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, 0,
1052 "gctlUpGA");
1053 LogFlowFuncLeaveRC(rc);
1054 return rc;
1055}
1056
1057/* static */
1058int SessionTaskUpdateAdditions::taskThread(RTTHREAD Thread, void *pvUser)
1059{
1060 std::auto_ptr<SessionTaskUpdateAdditions> task(static_cast<SessionTaskUpdateAdditions*>(pvUser));
1061 AssertReturn(task.get(), VERR_GENERAL_FAILURE);
1062
1063 LogFlowFunc(("pTask=%p\n", task.get()));
1064 return task->Run();
1065}
1066
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