GuestCtrlImplTasks.cpp revision 5f2b03bf7695dabd71222dba123532a3f76828c1
/* $Id: */
/** @file
* VirtualBox Guest Control - Threaded operations (tasks).
*/
/*
* Copyright (C) 2011 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#include <memory>
#include "GuestImpl.h"
#include "GuestCtrlImplPrivate.h"
#include "Global.h"
#include "ConsoleImpl.h"
#include "ProgressImpl.h"
#include "VMMDev.h"
#include "AutoCaller.h"
#include "Logging.h"
#ifdef VBOX_WITH_GUEST_CONTROL
#endif
{
}
{
}
int GuestTask::startThread(void)
{
"GuestTask");
}
/* static */
{
{
#ifdef VBOX_WITH_GUEST_CONTROL
case TaskType_CopyFileToGuest:
{
break;
}
{
break;
}
{
break;
}
#endif
default:
break;
}
return VINF_SUCCESS;
}
/* static */
{
if ( pTask
{
if (fCanceled)
return -1;
}
return VINF_SUCCESS;
}
/* static */
{
&& !fCanceled
&& !fCompleted)
{
}
return S_OK;
}
/* static */
const char *pszText, ...)
{
&& !fCanceled
&& !fCompleted)
{
va);
return hr2;
}
return S_OK;
}
/* static */
{
}
#ifdef VBOX_WITH_GUEST_CONTROL
{
AutoCaller autoCaller(this);
/*
* Do *not* take a write lock here since we don't (and won't)
* touch any class-specific data (of IGuest) here - only the member functions
* which get called here can do that.
*/
try
{
/* Does our source file exist? */
{
}
else
{
if (RT_FAILURE(vrc))
{
}
else
{
if (RT_FAILURE(vrc))
{
}
else
{
/*
* Prepare tool command line.
*/
char szOutput[RTPATH_MAX];
{
/*
* Normalize path slashes, based on the detected guest.
*/
{
/* We have a Windows guest. */
}
else /* ... or something which isn't from Redmond ... */
{
}
}
else
{
LogRel(("Copying file \"%s\" to guest \"%s\" (%u bytes) ...\n",
/*
* Okay, since we gathered all stuff we need until now to start the
* actual copying, start the guest part now.
*/
}
{
&& !fCompleted)
{
if (cbSize) /* If we have nothing to read, take a shortcut. */
{
/** @todo Not very efficient, but works for now. */
if (RT_SUCCESS(vrc))
{
/*
* Some other error occured? There might be a chance that RTFileRead
* print a generic error.
*/
if (RT_FAILURE(vrc))
{
break;
}
}
else
{
break;
}
}
/* Resize buffer to reflect amount we just have read.
* Size 0 is allowed! */
/* Did we reach the end of the content we want to transfer (last chunk)? */
/* Did we reach the last block which is exactly _64K? */
/* ... or does the user want to cancel? */
&& fCanceled)
)
{
}
ULONG cbBytesWritten = 0;
0 /* Infinite timeout */,
{
break;
}
/* Only subtract bytes reported written by the guest. */
/* End of file reached? */
if (cbToRead == 0)
break;
/* Did the user cancel the operation above? */
if (fCanceled)
break;
/* Progress canceled by Main API? */
&& fCanceled)
{
break;
}
}
{
/*
* If we got here this means the started process either was completed,
* canceled or we simply got all stuff transferred.
*/
{
}
else
{
if (RT_SUCCESS(vrc))
{
{
}
}
}
}
{
if (fCanceled)
{
/*
* In order to make the progress object to behave nicely, we also have to
* notify the object with a complete event when it's canceled.
*/
}
else
{
/*
* Even if we succeeded until here make sure to check whether we really transfered
* everything.
*/
if ( cbSize > 0
&& cbTransferedTotal == 0)
{
/* If nothing was transfered but the file size was > 0 then "vbox_cat" wasn't able to write
* to the destination -> access denied. */
}
else if (cbTransferedTotal < cbSize)
{
/* If we did not copy all let the user know. */
}
else /* Yay, all went fine! */
}
}
}
}
}
}
}
{
}
/* Clean up */
return VINF_SUCCESS;
}
{
AutoCaller autoCaller(this);
/*
* Do *not* take a write lock here since we don't (and won't)
* touch any class-specific data (of IGuest) here - only the member functions
* which get called here can do that.
*/
try
{
/* Does our source file exist? */
&fFileExists);
{
if (!fFileExists)
}
else
/* Query file size to make an estimate for our progress object. */
{
&lFileSize);
{
/*
* Prepare tool command line.
*/
char szSource[RTPATH_MAX];
{
/*
* Normalize path slashes, based on the detected guest.
*/
{
/* We have a Windows guest. */
}
else /* ... or something which isn't from Redmond ... */
{
}
}
else
}
{
LogRel(("Copying file \"%s\" to host \"%s\" (%u bytes) ...\n",
/*
* Okay, since we gathered all stuff we need until now to start the
* actual copying, start the guest part now.
*/
}
{
if (RT_FAILURE(vrc))
else
{
/* Note: Using size_t here is possible because the file size is
* stored as 32-bit value in the ISO 9660 file system. */
size_t cbTransfered = 0;
&& !fCompleted)
{
0 /* No timeout. */,
{
if (aOutputData.size())
{
if (RT_FAILURE(vrc))
{
break;
}
}
/* Nothing read this time; try next round. */
}
else
{
break;
}
}
{
if ( cbTransfered
{
/*
* Only bitch about an unexpected end of a file when there already
* was data read from that file. If this was the very first read we can
* be (almost) sure that this file is not meant to be read by the specified user.
*/
}
}
}
}
}
}
{
}
/* Clean up */
return VINF_SUCCESS;
}
{
AutoCaller autoCaller(this);
/*
* Do *not* take a write lock here since we don't (and won't)
* touch any class-specific data (of IGuest) here - only the member functions
* which get called here can do that.
*/
try
{
/*
* Determine guest OS type and the required installer image.
* At the moment only Windows guests are supported.
*/
{
{
installerImage = "VBOXWINDOWSADDITIONS_AMD64.EXE";
else
installerImage = "VBOXWINDOWSADDITIONS_X86.EXE";
/* Since the installers are located in the root directory,
* no further path processing needs to be done (yet). */
}
else /* Everything else is not supported (yet). */
Guest::tr("Detected guest OS (%s) does not support automatic Guest Additions updating, please update manually"),
osTypeIdUtf8.c_str());
}
else
/*
* Try to open the .ISO file and locate the specified installer.
*/
if (RT_FAILURE(vrc))
{
}
else
{
if ( RT_SUCCESS(vrc)
&& cbOffset
&& cbLength)
{
if (RT_FAILURE(vrc))
}
else
{
switch (vrc)
{
case VERR_FILE_NOT_FOUND:
break;
default:
Guest::tr("An unknown error (%Rrc) occured while retrieving information of setup file on installation medium \"%s\""),
break;
}
}
/* Specify the ouput path on the guest side. */
if (RT_SUCCESS(vrc))
{
/* Okay, we're ready to start our copy routine on the guest! */
/* Prepare command line args. */
{
/*
* Start built-in "vbox_cat" tool (inside VBoxService) to
*/
5 * 1000 /* Wait 5s for getting the process started. */,
{
/* Errors which return VBOX_E_NOT_SUPPORTED can be safely skipped by the caller
* to silently fall back to "normal" (old) .ISO mounting. */
/* Due to a very limited COM error range we use vrc for a more detailed error
* lookup to figure out what went wrong. */
switch (vrc)
{
/* Guest execution service is not (yet) ready. This basically means that either VBoxService
* is not running (yet) or that the Guest Additions are too old (because VBoxService does not
* support the guest execution feature in this version). */
case VERR_NOT_FOUND:
LogRel(("Guest Additions seem not to be installed yet\n"));
break;
/* Getting back a VERR_INVALID_PARAMETER indicates that the installed Guest Additions are supporting the guest
* execution but not the built-in "vbox_cat" tool of VBoxService (< 4.0). */
case VERR_INVALID_PARAMETER:
LogRel(("Guest Additions are installed but don't supported automatic updating\n"));
break;
case VERR_TIMEOUT:
LogRel(("Guest was unable to start copying the Guest Additions setup within time\n"));
break;
default:
break;
}
}
else
{
LogRel(("Copying Guest Additions installer \"%s\" to \"%s\" on guest ...\n",
/* Wait for process to exit ... */
&& !fCompleted)
{
/* cbLength contains remaining bytes of our installer file
* opened above to read. */
if (cbToRead)
{
if ( cbRead
&& RT_SUCCESS(vrc))
{
/* Resize buffer to reflect amount we just have read. */
if (cbRead > 0)
/* Did we reach the end of the content we want to transfer (last chunk)? */
/* Did we reach the last block which is exactly _64K? */
/* ... or does the user want to cancel? */
&& fCanceled)
)
{
}
/* Transfer the current chunk ... */
#ifdef DEBUG_andy
#endif
10 * 1000 /* Wait 10s for getting the input data transfered. */,
{
break;
}
/* If task was canceled above also cancel the process execution. */
if (fCanceled)
progressCat->Cancel();
#ifdef DEBUG_andy
#endif
}
else if (RT_FAILURE(vrc))
{
Guest::tr("Error while reading setup file \"%s\" (To read: %u, Size: %u) from installation medium (%Rrc)"),
}
}
/* Internal progress canceled? */
&& fCanceled)
{
break;
}
}
}
}
}
RTIsoFsClose(&iso);
&& !fCanceled
)
)
{
/*
* Installer was transferred successfully, so let's start it
* (with system rights).
*/
LogRel(("Preparing to execute Guest Additions update ...\n"));
/* Prepare command line args for installer. */
/** @todo Only Windows! */
installerArgs.push_back(Bstr(strInstallerPath).raw()); /* The actual (internal) installer image (as argv[0]). */
/* Note that starting at Windows Vista the lovely session 0 separation applies:
* of VBoxService (system rights!) we're not able to show any UI. */
/* Don't quit VBoxService during upgrade because it still is used for this
* piece of code we're in right now (that is, here!) ... */
/* Tell the installer to report its current installation status
* using a running VBoxTray instance via balloon messages in the
* Windows taskbar. */
/*
* Start the just copied over installer with system rights
* in silent mode on the guest. Don't use the hidden flag since there
* may be pop ups the user has to process.
*/
10 * 1000 /* Wait 10s for getting the process started */,
{
LogRel(("Guest Additions update is running ...\n"));
/* If the caller does not want to wait for out guest update process to end,
* complete the progress object now so that the caller can do other work. */
{
}
else
/* Wait until the Guest Additions installer finishes ... */
&& !fCompleted)
{
&& fCanceled)
{
break;
}
/* Progress canceled by Main API? */
&& fCanceled)
{
break;
}
RTThreadSleep(100);
}
{
if (fCompleted)
{
if (uRetExitCode == 0)
{
LogRel(("Guest Additions update successful!\n"));
&& !fCompleted)
{
}
}
else
{
LogRel(("Guest Additions update failed (Exit code=%u, Status=%u, Flags=%u)\n",
}
}
&& fCanceled)
{
LogRel(("Guest Additions update was canceled\n"));
Guest::tr("Guest Additions update was canceled by the guest with exit code=%u (status=%u, flags=%u)"),
}
else
{
LogRel(("Guest Additions update was canceled by the user\n"));
}
}
else
}
else
}
}
}
{
}
/* Clean up */
return VINF_SUCCESS;
}
#endif