VirtualBox

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

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

GuestSessionImplTasks/Update Additions: More error checks, logging enhancements, disabled using VBoxCertUtil (needs investigation first).

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