boinc/samples/vboxwrapper/vbox_mscom_impl.cpp

2765 lines
87 KiB
C++

// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2010-2012 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
#ifdef _MSC_VER
#pragma warning(disable: 4706)
#endif
#ifdef _VIRTUALBOX_IMPORT_FUNCTIONS_
const char *MachineStateToName(MachineState State)
{
switch (State)
{
case MachineState_PoweredOff:
return "poweroff";
case MachineState_Saved:
return "saved";
case MachineState_Aborted:
return "aborted";
case MachineState_Teleported:
return "teleported";
case MachineState_Running:
return "running";
case MachineState_Paused:
return "paused";
case MachineState_Stuck:
return "gurumeditation";
case MachineState_LiveSnapshotting:
return "livesnapshotting";
case MachineState_Teleporting:
return "teleporting";
case MachineState_Starting:
return "starting";
case MachineState_Stopping:
return "stopping";
case MachineState_Saving:
return "saving";
case MachineState_Restoring:
return "restoring";
case MachineState_TeleportingPausedVM:
return "teleportingpausedvm";
case MachineState_TeleportingIn:
return "teleportingin";
case MachineState_RestoringSnapshot:
return "restoringsnapshot";
case MachineState_DeletingSnapshot:
return "deletingsnapshot";
case MachineState_DeletingSnapshotOnline:
return "deletingsnapshotlive";
case MachineState_DeletingSnapshotPaused:
return "deletingsnapshotlivepaused";
case MachineState_SettingUp:
return "settingup";
default:
break;
}
return "unknown";
}
// Helper function to print MSCOM exception information set on the current
// thread after a failed MSCOM method call. This function will also print
// extended VirtualBox error info if it is available.
//
#define CHECK_ERROR(rc) \
retval = virtualbox_check_error(rc, __FUNCTION__, __FILE__, __LINE__)
int virtualbox_check_error(HRESULT rc, char* szFunction, char* szFile, int iLine) {
HRESULT hr;
CComPtr<IErrorInfo> pErrorInfo;
CComBSTR strSource;
CComBSTR strDescription;
if (FAILED(rc)) {
vboxlog_msg("Error 0x%x in %s (%s:%d)", rc, szFunction, szFile, iLine);
hr = ::GetErrorInfo(0, &pErrorInfo);
if (SUCCEEDED(hr) && pErrorInfo) {
hr = pErrorInfo->GetSource(&strSource);
if (SUCCEEDED(hr) && strSource) {
vboxlog_msg("Error Source : %S", strSource);
}
hr = pErrorInfo->GetDescription(&strDescription);
if (SUCCEEDED(hr) && strDescription) {
vboxlog_msg("Error Description: %S", strDescription);
}
} else {
vboxlog_msg("Error: Getting Error Info! hr = 0x%x", hr);
}
}
return rc;
}
void __stdcall virtualbox_com_raise_error(HRESULT rc, IErrorInfo* perrinfo) {
HRESULT hr;
CComPtr<IErrorInfo> pErrorInfo;
CComBSTR strSource;
CComBSTR strDescription;
pErrorInfo.Attach(perrinfo);
vboxlog_msg("COM Error 0x%x", rc);
hr = pErrorInfo->GetSource(&strSource);
if (SUCCEEDED(hr) && strSource) {
vboxlog_msg("Error Source : %S", strSource);
}
hr = pErrorInfo->GetDescription(&strDescription);
if (SUCCEEDED(hr) && strDescription) {
vboxlog_msg("Error Description: %S", strDescription);
}
DebugBreak();
}
// We want to recurisively walk the snapshot tree so that we can get the most recent children first.
// We also want to skip whatever the most current snapshot is.
//
void TraverseSnapshots(std::string& current_snapshot_id, std::vector<std::string>& snapshots, ISnapshot* pSnapshot) {
HRESULT rc;
SAFEARRAY* pSnapshots = NULL;
CComSafeArray<LPDISPATCH> aSnapshots;
CComBSTR tmp;
ULONG lCount;
std::string snapshot_id;
// Check to see if we have any children
//
#ifdef _VIRTUALBOX60_
rc = pSnapshot->get_ChildrenCount(&lCount);
#else
rc = pSnapshot->GetChildrenCount(&lCount);
#endif
if (SUCCEEDED(rc) && lCount) {
rc = pSnapshot->get_Children(&pSnapshots);
if (SUCCEEDED(rc)) {
aSnapshots.Attach(pSnapshots);
if (aSnapshots.GetCount() > 0) {
for (int i = 0; i < (int)aSnapshots.GetCount(); i++) {
TraverseSnapshots(current_snapshot_id, snapshots, (ISnapshot*)(LPDISPATCH)aSnapshots[i]);
}
}
}
}
// Check to see if we are the most recent snapshot.
// if not, add the snapshot id to the list of snapshots to be deleted.
//
pSnapshot->get_Id(&tmp);
if (SUCCEEDED(rc)) {
snapshot_id = CW2A(tmp);
if (current_snapshot_id == snapshot_id) {
return;
} else {
snapshots.push_back(snapshot_id);
}
}
}
// We want to recurisively walk the medium tree so that we can get the most recent children first.
//
void TraverseMediums(std::vector<CComPtr<IMedium>>& mediums, IMedium* pMedium) {
HRESULT rc;
SAFEARRAY* pMediums = NULL;
CComSafeArray<LPDISPATCH> aMediums;
// Check to see if we have any children
//
rc = pMedium->get_Children(&pMediums);
if (SUCCEEDED(rc)) {
aMediums.Attach(pMediums);
if (aMediums.GetCount() > 0) {
for (int i = 0; i < (int)aMediums.GetCount(); i++) {
TraverseMediums(mediums, (IMedium*)(LPDISPATCH)aMediums[i]);
}
}
}
mediums.push_back(CComPtr<IMedium>(pMedium));
}
VBOX_VM::VBOX_VM() {
// Initialize COM Exception Trap
//
_set_com_error_handler(virtualbox_com_raise_error);
m_pPrivate = new VBOX_PRIV();
}
VBOX_VM::~VBOX_VM() {
if (m_pPrivate) {
delete m_pPrivate;
m_pPrivate = NULL;
}
}
int VBOX_VM::initialize() {
int rc = BOINC_SUCCESS;
string old_path;
string new_path;
APP_INIT_DATA aid;
bool force_sandbox = false;
boinc_get_init_data_p(&aid);
get_install_directory(virtualbox_install_directory);
get_scratch_directory(virtualbox_scratch_directory);
// Prep the environment so we can execute the vboxmanage application
//
// TODO: Fix for non-Windows environments if we ever find another platform
// where vboxmanage is not already in the search path
if (!virtualbox_install_directory.empty())
{
old_path = getenv("PATH");
new_path = virtualbox_install_directory + ";" + old_path;
if (!SetEnvironmentVariable("PATH", const_cast<char*>(new_path.c_str()))) {
vboxlog_msg("Failed to modify the search path");
}
}
// Determine the VirtualBox home directory. Overwrite as needed.
//
if (getenv("VBOX_USER_HOME")) {
virtualbox_home_directory = getenv("VBOX_USER_HOME");
} else {
// If the override environment variable isn't specified then
// it is based of the current users HOME directory.
virtualbox_home_directory = getenv("USERPROFILE");
virtualbox_home_directory += "/.VirtualBox";
}
// Set the location in which the VirtualBox Configuration files can be
// stored for this instance.
if (aid.using_sandbox || force_sandbox) {
virtualbox_home_directory = aid.project_dir;
virtualbox_home_directory += "/../virtualbox";
if (!boinc_file_exists(virtualbox_home_directory.c_str())) {
boinc_mkdir(virtualbox_home_directory.c_str());
}
if (!SetEnvironmentVariable("VBOX_USER_HOME", const_cast<char*>(virtualbox_home_directory.c_str()))) {
vboxlog_msg("Failed to modify the search path");
}
}
// Launch vboxsvc manually so that the DCOM subsystem won't be able too. Our version
// will have permission and direction to write its state information to the BOINC
// data directory.
//
launch_vboxsvc();
// Instantiate the VirtualBox root object.
rc = m_pPrivate->m_pVirtualBox.CreateInstance(CLSID_VirtualBox);
if (FAILED(rc)) {
vboxlog_msg("Error creating VirtualBox instance! rc = 0x%x", rc);
return rc;
}
// Create the session object.
rc = m_pPrivate->m_pSession.CreateInstance(CLSID_Session);
if (FAILED(rc)) {
vboxlog_msg("Error creating Session instance! rc = 0x%x", rc);
return rc;
}
rc = get_version_information(virtualbox_version_raw, virtualbox_version_display);
if (rc) return rc;
// Get the guest addition information
get_guest_additions(virtualbox_guest_additions);
return rc;
}
int VBOX_VM::create_vm() {
int retval = ERR_EXEC;
HRESULT rc;
char buf[256];
APP_INIT_DATA aid;
CComPtr<IMachine> pMachineRO;
CComPtr<IMachine> pMachine;
CComPtr<ISession> pSession;
CComPtr<IBIOSSettings> pBIOSSettings;
CComPtr<INetworkAdapter> pNetworkAdapter;
CComPtr<INATEngine> pNATEngine;
CComPtr<IUSBController> pUSBContoller;
CComPtr<ISerialPort> pSerialPort1;
CComPtr<ISerialPort> pSerialPort2;
CComPtr<IParallelPort> pParallelPort1;
CComPtr<IParallelPort> pParallelPort2;
CComPtr<IAudioAdapter> pAudioAdapter;
CComPtr<IStorageController> pDiskController;
CComPtr<IStorageController> pFloppyController;
CComPtr<IBandwidthControl> pBandwidthControl;
CComPtr<IVRDEServer> pVRDEServer;
bool disable_acceleration = false;
string virtual_machine_slot_directory;
string default_interface;
boinc_get_init_data_p(&aid);
get_slot_directory(virtual_machine_slot_directory);
rc = pSession.CoCreateInstance(CLSID_Session);
if (CHECK_ERROR(rc)) goto CLEANUP;
vboxlog_msg("Create VM. (%s, slot#%d)", vm_master_name.c_str(), aid.slot);
// Reset VM name in case it was changed while deregistering a stale VM
//
vm_name = vm_master_name;
// Fixup chipset and drive controller information for known configurations
//
if (enable_isocontextualization) {
if ("PIIX4" == vm_disk_controller_model) {
vboxlog_msg("Updating drive controller type and model for desired configuration.");
vm_disk_controller_type = "sata";
vm_disk_controller_model = "IntelAHCI";
}
}
// Start the VM creation process
//
rc = m_pPrivate->m_pVirtualBox->CreateMachine(
CComBSTR(string(virtual_machine_slot_directory + "\\" + vm_name + "\\" + vm_name + ".vbox").c_str()),
CComBSTR(vm_name.c_str()),
NULL,
CComBSTR(os_name.c_str()),
CComBSTR(""),
&pMachineRO
);
if (CHECK_ERROR(rc)) goto CLEANUP;
// Register the VM. Note that this call also saves the VM config
// to disk. It is also possible to save the VM settings but not
// register the VM.
//
// Also note that due to current VirtualBox limitations, the machine
// must be registered *before* we can attach hard disks to it.
//
rc = m_pPrivate->m_pVirtualBox->RegisterMachine(pMachineRO);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pMachineRO->LockMachine(pSession, LockType_Write);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pSession->get_Machine(&pMachine);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pMachine->get_BIOSSettings(&pBIOSSettings);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pMachine->get_BandwidthControl(&pBandwidthControl);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pMachine->get_VRDEServer(&pVRDEServer);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pMachine->GetNetworkAdapter(0, &pNetworkAdapter);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pNetworkAdapter->get_NATEngine(&pNATEngine);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pMachine->get_AudioAdapter(&pAudioAdapter);
if (CHECK_ERROR(rc)) goto CLEANUP;
// Set some properties
//
pMachine->put_Description(CComBSTR(vm_master_description.c_str()));
// Tweak the VM's Memory Size
//
vboxlog_msg("Setting Memory Size for VM. (%dMB)", (int)memory_size_mb);
rc = pMachine->put_MemorySize((int)(memory_size_mb));
if (CHECK_ERROR(rc)) goto CLEANUP;
// Tweak the VM's CPU Count
//
vboxlog_msg("Setting CPU Count for VM. (%s)", vm_cpu_count.c_str());
rc = pMachine->put_CPUCount((int)atoi(vm_cpu_count.c_str()));
if (CHECK_ERROR(rc)) goto CLEANUP;
// Tweak the VM's Chipset Options
//
vboxlog_msg("Setting Chipset Options for VM.");
rc = pBIOSSettings->put_ACPIEnabled(TRUE);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pBIOSSettings->put_IOAPICEnabled(TRUE);
if (CHECK_ERROR(rc)) goto CLEANUP;
// Tweak the VM's Boot Options
//
vboxlog_msg("Setting Boot Options for VM.");
rc = pMachine->SetBootOrder(boot_iso ? 2 : 1, DeviceType_HardDisk);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pMachine->SetBootOrder(boot_iso ? 1 : 2, DeviceType_DVD);
if (CHECK_ERROR(rc)) goto CLEANUP;
pMachine->SetBootOrder(3, DeviceType_Null);
pMachine->SetBootOrder(4, DeviceType_Null);
// Tweak the VM's Network Configuration
//
if (enable_network) {
vboxlog_msg("Enabling VM Network Access.");
rc = pNetworkAdapter->put_Enabled(TRUE);
if (CHECK_ERROR(rc)) goto CLEANUP;
} else {
vboxlog_msg("Disabling VM Network Access.");
rc = pNetworkAdapter->put_Enabled(FALSE);
if (CHECK_ERROR(rc)) goto CLEANUP;
}
if (network_bridged_mode) {
vboxlog_msg("Setting Network Configuration for Bridged Mode.");
rc = pNetworkAdapter->put_AttachmentType(NetworkAttachmentType_Bridged);
if (CHECK_ERROR(rc)) goto CLEANUP;
get_default_network_interface(default_interface);
vboxlog_msg("Setting Bridged Interface. (%s)", default_interface.c_str());
rc = pNetworkAdapter->put_BridgedInterface(CComBSTR(CA2W(default_interface.c_str())));
if (CHECK_ERROR(rc)) goto CLEANUP;
} else {
vboxlog_msg("Setting Network Configuration for NAT.");
rc = pNetworkAdapter->put_AttachmentType(NetworkAttachmentType_NAT);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pNATEngine->put_DNSProxy(TRUE);
if (CHECK_ERROR(rc)) goto CLEANUP;
}
// Tweak the VM's USB Configuration
//
vboxlog_msg("Disabling USB Support for VM.");
#ifdef _VIRTUALBOX42_
rc = pMachine->get_USBController(&pUSBContoller);
if (SUCCEEDED(rc)) {
pUSBContoller->put_Enabled(FALSE);
}
#else
ULONG lOHCICtrls = 0;
rc = pMachine->GetUSBControllerCountByType(USBControllerType_OHCI, &lOHCICtrls);
if (SUCCEEDED(rc) && lOHCICtrls) {
pMachine->RemoveUSBController(CComBSTR("OHCI"));
}
#endif
// Tweak the VM's COM Port Support
//
vboxlog_msg("Disabling COM Port Support for VM.");
rc = pMachine->GetSerialPort(0, &pSerialPort1);
if (SUCCEEDED(rc)) {
pSerialPort1->put_Enabled(FALSE);
}
rc = pMachine->GetSerialPort(1, &pSerialPort2);
if (SUCCEEDED(rc)) {
pSerialPort2->put_Enabled(FALSE);
}
// Tweak the VM's LPT Port Support
//
vboxlog_msg("Disabling LPT Port Support for VM.");
rc = pMachine->GetParallelPort(0, &pParallelPort1);
if (SUCCEEDED(rc)) {
pParallelPort1->put_Enabled(FALSE);
}
rc = pMachine->GetParallelPort(1, &pParallelPort2);
if (SUCCEEDED(rc)) {
pParallelPort2->put_Enabled(FALSE);
}
// Tweak the VM's Audio Support
//
vboxlog_msg("Disabling Audio Support for VM.");
pAudioAdapter->put_Enabled(FALSE);
// Tweak the VM's Clipboard Support
//
vboxlog_msg("Disabling Clipboard Support for VM.");
pMachine->put_ClipboardMode(ClipboardMode_Disabled);
// Tweak the VM's Drag & Drop Support
//
vboxlog_msg("Disabling Drag and Drop Support for VM.");
#if defined(_VIRTUALBOX42_) || defined (_VIRTUALBOX43_)
pMachine->put_DragAndDropMode(DragAndDropMode_Disabled);
#else
pMachine->put_DnDMode(DnDMode_Disabled);
#endif
// Check to see if the processor supports hardware acceleration for virtualization
// If it doesn't, disable the use of it in VirtualBox. Multi-core jobs require hardware
// acceleration and actually override this setting.
//
if (!strstr(aid.host_info.p_features, "vmx") && !strstr(aid.host_info.p_features, "svm")) {
vboxlog_msg("Hardware acceleration CPU extensions not detected. Disabling VirtualBox hardware acceleration support.");
disable_acceleration = true;
}
if (strstr(aid.host_info.p_features, "hypervisor")) {
vboxlog_msg("Running under Hypervisor. Disabling VirtualBox hardware acceleration support.");
disable_acceleration = true;
}
if (is_boinc_client_version_newer(aid, 7, 2, 16)) {
if (aid.vm_extensions_disabled) {
vboxlog_msg("Hardware acceleration failed with previous execution. Disabling VirtualBox hardware acceleration support.");
disable_acceleration = true;
}
} else {
if (vm_cpu_count == "1") {
// Keep this around for older clients. Removing this for older clients might
// lead to a machine that will only return crashed VM reports.
vboxlog_msg("Legacy fallback configuration detected. Disabling VirtualBox hardware acceleration support.");
vboxlog_msg("NOTE: Upgrading to BOINC 7.2.16 or better may re-enable hardware acceleration.");
disable_acceleration = true;
}
}
// Only allow disabling of hardware acceleration on 32-bit VM types, 64-bit VM types require it.
//
if (os_name.find("_64") == std::string::npos) {
if (disable_acceleration) {
vboxlog_msg("Disabling hardware acceleration support for virtualization.");
rc = pMachine->SetHWVirtExProperty(HWVirtExPropertyType_Enabled, FALSE);
if (CHECK_ERROR(rc)) goto CLEANUP;
}
} else if (os_name.find("_64") != std::string::npos) {
if (disable_acceleration) {
vboxlog_msg("ERROR: Invalid configuration. VM type requires acceleration but the current configuration cannot support it.");
retval = ERR_INVALID_PARAM;
goto CLEANUP;
}
}
// Add storage controller to VM
// See: http://www.virtualbox.org/manual/ch08.html#vboxmanage-storagectl
// See: http://www.virtualbox.org/manual/ch05.html#iocaching
//
vboxlog_msg("Adding storage controller(s) to VM.");
if (0 == stricmp(vm_disk_controller_type.c_str(), "ide")) {
rc = pMachine->AddStorageController(CComBSTR("Hard Disk Controller"), StorageBus_IDE, &pDiskController);
if (CHECK_ERROR(rc)) goto CLEANUP;
}
if (0 == stricmp(vm_disk_controller_type.c_str(), "sata")) {
rc = pMachine->AddStorageController(CComBSTR("Hard Disk Controller"), StorageBus_SATA, &pDiskController);
if (CHECK_ERROR(rc)) goto CLEANUP;
pDiskController->put_UseHostIOCache(FALSE);
pDiskController->put_PortCount(3);
}
if (0 == stricmp(vm_disk_controller_type.c_str(), "sas")) {
rc = pMachine->AddStorageController(CComBSTR("Hard Disk Controller"), StorageBus_SAS, &pDiskController);
if (CHECK_ERROR(rc)) goto CLEANUP;
pDiskController->put_UseHostIOCache(FALSE);
}
if (0 == stricmp(vm_disk_controller_type.c_str(), "scsi")) {
rc = pMachine->AddStorageController(CComBSTR("Hard Disk Controller"), StorageBus_SCSI, &pDiskController);
if (CHECK_ERROR(rc)) goto CLEANUP;
pDiskController->put_UseHostIOCache(FALSE);
}
if (0 == stricmp(vm_disk_controller_model.c_str(), "lsilogic")) {
pDiskController->put_ControllerType(StorageControllerType_LsiLogic);
} else if (0 == stricmp(vm_disk_controller_model.c_str(), "buslogic")) {
pDiskController->put_ControllerType(StorageControllerType_BusLogic);
} else if (0 == stricmp(vm_disk_controller_model.c_str(), "intelahci")) {
pDiskController->put_ControllerType(StorageControllerType_IntelAhci);
} else if (0 == stricmp(vm_disk_controller_model.c_str(), "piix3")) {
pDiskController->put_ControllerType(StorageControllerType_PIIX3);
} else if (0 == stricmp(vm_disk_controller_model.c_str(), "piix4")) {
pDiskController->put_ControllerType(StorageControllerType_PIIX4);
} else if (0 == stricmp(vm_disk_controller_model.c_str(), "ich6")) {
pDiskController->put_ControllerType(StorageControllerType_ICH6);
} else if (0 == stricmp(vm_disk_controller_model.c_str(), "i82078")) {
pDiskController->put_ControllerType(StorageControllerType_I82078);
} else if (0 == stricmp(vm_disk_controller_model.c_str(), "lsilogicsas")) {
pDiskController->put_ControllerType(StorageControllerType_LsiLogicSas);
}
// Add storage controller for a floppy device if desired
//
if (enable_floppyio) {
rc = pMachine->AddStorageController(CComBSTR("Floppy Controller"), StorageBus_Floppy, &pFloppyController);
if (CHECK_ERROR(rc)) goto CLEANUP;
}
if (enable_isocontextualization) {
// Add virtual ISO 9660 disk drive to VM
//
vboxlog_msg("Adding virtual ISO 9660 disk drive to VM. (%s)", iso_image_filename.c_str());
CComPtr<IMedium> pISOImage;
rc = m_pPrivate->m_pVirtualBox->OpenMedium(
CComBSTR(string(virtual_machine_slot_directory + "\\" + iso_image_filename).c_str()),
DeviceType_DVD,
AccessMode_ReadOnly,
TRUE,
&pISOImage
);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pMachine->AttachDevice(
CComBSTR("Hard Disk Controller"),
0,
0,
DeviceType_DVD,
pISOImage
);
if (CHECK_ERROR(rc)) goto CLEANUP;
// Add guest additions to the VM
//
if (virtualbox_guest_additions.size()) {
vboxlog_msg("Adding VirtualBox Guest Additions to VM.");
CComPtr<IMedium> pGuestAdditionsImage;
rc = m_pPrivate->m_pVirtualBox->OpenMedium(
CComBSTR(virtualbox_guest_additions.c_str()),
DeviceType_DVD,
AccessMode_ReadOnly,
FALSE,
&pGuestAdditionsImage
);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pMachine->AttachDevice(
CComBSTR("Hard Disk Controller"),
2,
0,
DeviceType_DVD,
pGuestAdditionsImage
);
if (CHECK_ERROR(rc)) goto CLEANUP;
}
// Add a virtual cache disk drive to VM
//
if (enable_cache_disk){
vboxlog_msg("Adding virtual cache disk drive to VM. (%s)", cache_disk_filename.c_str());
CComPtr<IMedium> pCacheImage;
rc = m_pPrivate->m_pVirtualBox->OpenMedium(
CComBSTR(string(virtual_machine_slot_directory + "\\" + cache_disk_filename).c_str()),
DeviceType_HardDisk,
AccessMode_ReadWrite,
TRUE,
&pCacheImage
);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pMachine->AttachDevice(
CComBSTR("Hard Disk Controller"),
1,
0,
DeviceType_HardDisk,
pCacheImage
);
if (CHECK_ERROR(rc)) goto CLEANUP;
}
} else {
// Adding virtual hard drive to VM
//
vboxlog_msg("Adding virtual disk drive to VM. (%s)", image_filename.c_str());
CComPtr<IMedium> pDiskImage;
rc = m_pPrivate->m_pVirtualBox->OpenMedium(
CComBSTR(string(virtual_machine_slot_directory + "\\" + image_filename).c_str()),
DeviceType_HardDisk,
AccessMode_ReadWrite,
TRUE,
&pDiskImage
);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pMachine->AttachDevice(
CComBSTR("Hard Disk Controller"),
0,
0,
DeviceType_HardDisk,
pDiskImage
);
if (CHECK_ERROR(rc)) goto CLEANUP;
// Add guest additions to the VM
//
if (virtualbox_guest_additions.size()) {
vboxlog_msg("Adding VirtualBox Guest Additions to VM.");
CComPtr<IMedium> pGuestAdditionsImage;
rc = m_pPrivate->m_pVirtualBox->OpenMedium(
CComBSTR(virtualbox_guest_additions.c_str()),
DeviceType_DVD,
AccessMode_ReadOnly,
FALSE,
&pGuestAdditionsImage
);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pMachine->AttachDevice(
CComBSTR("Hard Disk Controller"),
1,
0,
DeviceType_DVD,
pGuestAdditionsImage
);
if (CHECK_ERROR(rc)) goto CLEANUP;
}
}
// Adding virtual floppy disk drive to VM
//
if (enable_floppyio) {
// Put in place the FloppyIO abstraction
//
// NOTE: This creates the floppy.img file at runtime for use by the VM.
//
pFloppy = new FloppyIONS::FloppyIO(floppy_image_filename.c_str());
if (!pFloppy->ready()) {
vboxlog_msg("Creating virtual floppy image failed.");
vboxlog_msg("Error Code '%d' Error Message '%s'", pFloppy->error, pFloppy->errorStr.c_str());
retval = ERR_FWRITE;
goto CLEANUP;
}
vboxlog_msg("Adding virtual floppy disk drive to VM.");
CComPtr<IMedium> pFloppyImage;
rc = m_pPrivate->m_pVirtualBox->OpenMedium(
CComBSTR(string(virtual_machine_slot_directory + "\\" + floppy_image_filename).c_str()),
DeviceType_Floppy,
AccessMode_ReadWrite,
TRUE,
&pFloppyImage
);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pMachine->AttachDevice(
CComBSTR("Floppy Controller"),
0,
0,
DeviceType_Floppy,
pFloppyImage
);
if (CHECK_ERROR(rc)) goto CLEANUP;
}
// Add network bandwidth throttle group
//
vboxlog_msg("Adding network bandwidth throttle group to VM. (Defaulting to 1024GB)");
rc = pBandwidthControl->CreateBandwidthGroup(
CComBSTR(string(vm_name + "_net").c_str()),
BandwidthGroupType_Network,
(LONG64)1024*1024*1024*1024
);
if (CHECK_ERROR(rc)) goto CLEANUP;
// Configure port forwarding
//
if (enable_network) {
if (pf_guest_port) {
VBOX_PORT_FORWARD pf;
pf.guest_port = pf_guest_port;
pf.host_port = pf_host_port;
if (!pf_host_port) {
retval = boinc_get_port(false, pf.host_port);
if (retval) return retval;
pf_host_port = pf.host_port;
}
port_forwards.push_back(pf);
}
for (unsigned int i=0; i<port_forwards.size(); i++) {
VBOX_PORT_FORWARD& pf = port_forwards[i];
vboxlog_msg("forwarding host port %d to guest port %d", pf.host_port, pf.guest_port);
// Add new firewall rule
//
rc = pNATEngine->AddRedirect(
CComBSTR(""),
NATProtocol_TCP,
pf.is_remote?CComBSTR(""):CComBSTR("127.0.0.1"),
pf.host_port,
CComBSTR(""),
pf.guest_port
);
if (CHECK_ERROR(rc)) goto CLEANUP;
}
}
// If the VM wants to enable remote desktop for the VM do it here
//
if (enable_remotedesktop) {
vboxlog_msg("Enabling remote desktop for VM.");
if (!is_extpack_installed()) {
vboxlog_msg("Required extension pack not installed, remote desktop not enabled.");
} else {
retval = boinc_get_port(false, rd_host_port);
if (retval) goto CLEANUP;
sprintf(buf, "%d", rd_host_port);
pVRDEServer->put_Enabled(TRUE);
pVRDEServer->put_VRDEExtPack(CComBSTR(""));
pVRDEServer->put_AuthLibrary(CComBSTR(""));
pVRDEServer->put_AuthType(AuthType_Null);
pVRDEServer->SetVRDEProperty(CComBSTR("TCP/Ports"), CComBSTR(buf));
}
}
// Enable the shared folders if a shared folder is specified.
//
if (enable_shared_directory) {
vboxlog_msg("Enabling shared directory for VM.");
rc = pMachine->CreateSharedFolder(
CComBSTR("shared"),
CComBSTR(string(virtual_machine_slot_directory + "\\shared").c_str()),
TRUE,
TRUE
#ifdef _VIRTUALBOX60_
,
CComBSTR("/")
#endif
);
if (CHECK_ERROR(rc)) goto CLEANUP;
}
// Enable the scratch folder if a scratch folder is specified.
//
if (enable_scratch_directory) {
vboxlog_msg("Enabling scratch shared directory for VM.");
rc = pMachine->CreateSharedFolder(
CComBSTR("scratch"),
CComBSTR(virtualbox_scratch_directory.c_str()),
TRUE,
TRUE
#ifdef _VIRTUALBOX60_
,
CComBSTR("/")
#endif
);
if (CHECK_ERROR(rc)) goto CLEANUP;
}
CLEANUP:
if (pMachine) {
pMachine->SaveSettings();
}
if (pSession) {
pSession->UnlockMachine();
}
return retval;
}
int VBOX_VM::register_vm() {
int retval = ERR_EXEC;
HRESULT rc;
string virtual_machine_slot_directory;
APP_INIT_DATA aid;
CComPtr<IMachine> pMachine;
boinc_get_init_data_p(&aid);
get_slot_directory(virtual_machine_slot_directory);
vboxlog_msg("Register VM. (%s, slot#%d)", vm_master_name.c_str(), aid.slot);
// Reset VM name in case it was changed while deregistering a stale VM
//
vm_name = vm_master_name;
rc = m_pPrivate->m_pVirtualBox->OpenMachine(
CComBSTR(string(virtual_machine_slot_directory + "\\" + vm_name + "\\" + vm_name + ".vbox").c_str()),
&pMachine
);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = m_pPrivate->m_pVirtualBox->RegisterMachine(pMachine);
if (CHECK_ERROR(rc)) goto CLEANUP;
CLEANUP:
return retval;
}
int VBOX_VM::deregister_vm(bool delete_media) {
int retval = ERR_EXEC;
HRESULT rc;
SAFEARRAY* pHardDisks = NULL;
SAFEARRAY* pEmptyHardDisks = NULL;
SAFEARRAY* pMediumAttachments = NULL;
CComSafeArray<LPDISPATCH> aMediumAttachments;
CComSafeArray<LPDISPATCH> aHardDisks;
CComPtr<ISession> pSession;
CComPtr<IConsole> pConsole;
CComPtr<IMachine> pMachineRO;
CComPtr<IMachine> pMachine;
CComPtr<IProgress> pProgress;
CComPtr<IBandwidthControl> pBandwidthControl;
CComPtr<ISnapshot> pRootSnapshot;
std::vector<CComPtr<IMedium>> mediums;
std::vector<std::string> snapshots;
DeviceType device_type;
LONG lDevice;
LONG lPort;
string virtual_machine_slot_directory;
APP_INIT_DATA aid;
boinc_get_init_data_p(&aid);
get_slot_directory(virtual_machine_slot_directory);
vboxlog_msg("Deregistering VM. (%s, slot#%d)", vm_name.c_str(), aid.slot);
rc = m_pPrivate->m_pVirtualBox->FindMachine(CComBSTR(vm_name.c_str()), &pMachineRO);
if (SUCCEEDED(rc)) {
if (delete_media) {
rc = pSession.CoCreateInstance(CLSID_Session);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pMachineRO->LockMachine(pSession, LockType_Write);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pSession->get_Console(&pConsole);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pSession->get_Machine(&pMachine);
if (CHECK_ERROR(rc)) goto CLEANUP;
// Delete snapshots
//
rc = pMachine->FindSnapshot(CComBSTR(""), &pRootSnapshot);
if (SUCCEEDED(rc) && pRootSnapshot) {
string s = string("");
TraverseSnapshots(s, snapshots, pRootSnapshot);
}
if (snapshots.size()) {
for (size_t i = 0; i < snapshots.size(); i++) {
CComPtr<IProgress> pProgress;
#if defined(_VIRTUALBOX42_) || defined(_VIRTUALBOX43_)
rc = pConsole->DeleteSnapshot(CComBSTR(snapshots[i].c_str()), &pProgress);
#else
rc = pMachine->DeleteSnapshot(CComBSTR(snapshots[i].c_str()), &pProgress);
#endif
if (SUCCEEDED(rc)) {
pProgress->WaitForCompletion(-1);
} else {
CHECK_ERROR(rc);
}
}
}
// Close Hard Disk, DVD, and floppy mediums
//
vboxlog_msg("Removing virtual disk drive(s) from VM.");
rc = pMachine->get_MediumAttachments(&pMediumAttachments);
if (SUCCEEDED(rc)) {
aMediumAttachments.Attach(pMediumAttachments);
for (int i = 0; i < (int)aMediumAttachments.GetCount(); i++) {
CComPtr<IMediumAttachment> pMediumAttachment((IMediumAttachment*)(LPDISPATCH)aMediumAttachments[i]);
rc = pMediumAttachment->get_Type(&device_type);
if (SUCCEEDED(rc) &&
((DeviceType_HardDisk == device_type) ||
(DeviceType_DVD == device_type) ||
(DeviceType_Floppy == device_type))
) {
CComPtr<IMedium> pMedium;
CComBSTR strController;
if ((DeviceType_HardDisk == device_type) || (DeviceType_DVD == device_type)) {
strController = "Hard Disk Controller";
} else {
strController = "Floppy Controller";
}
pMediumAttachment->get_Device(&lDevice);
pMediumAttachment->get_Port(&lPort);
pMediumAttachment->get_Medium(&pMedium);
// If the device in question is a DVD/CD-ROM drive, the medium may have been ejected.
// If so, pMedium will be NULL.
if (pMedium) {
mediums.push_back(CComPtr<IMedium>(pMedium));
}
rc = pMachine->DetachDevice(strController, lPort, lDevice);
CHECK_ERROR(rc);
}
}
}
// Delete network bandwidth throttle group
//
vboxlog_msg("Removing network bandwidth throttle group from VM.");
rc = pMachine->get_BandwidthControl(&pBandwidthControl);
if (SUCCEEDED(rc)) {
pBandwidthControl->DeleteBandwidthGroup(CComBSTR(string(vm_name + "_net").c_str()));
}
// Delete its storage controller(s)
//
vboxlog_msg("Removing storage controller(s) from VM.");
pMachine->RemoveStorageController(CComBSTR("Hard Disk Controller"));
if (enable_floppyio) {
pMachine->RemoveStorageController(CComBSTR("Floppy Controller"));
}
// Save the VM Settings so the state is stored
//
rc = pMachine->SaveSettings();
CHECK_ERROR(rc);
// Now it should be safe to close down the mediums we detached.
//
for (int i = 0; i < (int)mediums.size(); i++) {
mediums[i]->Close();
}
// Now free the session lock
//
rc = pSession->UnlockMachine();
CHECK_ERROR(rc);
}
// Next, delete VM
//
vboxlog_msg("Removing VM from VirtualBox.");
rc = pMachineRO->Unregister(CleanupMode_Full, &pHardDisks);
if (SUCCEEDED(rc)) {
// We only want to close(remove from media registry) the hard disks
// instead of deleting them, order them by most recent image first
// and then walk back to the root.
//
aHardDisks.Attach(pHardDisks);
for (int i = 0; i < (int)aHardDisks.GetCount(); i++) {
CComPtr<IMedium> pMedium((IMedium*)(LPDISPATCH)aHardDisks[i]);
TraverseMediums(mediums, pMedium);
}
for (int i = 0; i < (int)mediums.size(); i++) {
CComPtr<IMedium> pMedium(mediums[i]);
pMedium->Close();
}
#ifdef _VIRTUALBOX42_
pMachineRO->Delete(pEmptyHardDisks, &pProgress);
if (SUCCEEDED(rc)) {
pProgress->WaitForCompletion(-1);
} else {
CHECK_ERROR(rc);
}
#else
pMachineRO->DeleteConfig(pEmptyHardDisks, &pProgress);
if (SUCCEEDED(rc)) {
pProgress->WaitForCompletion(-1);
} else {
CHECK_ERROR(rc);
}
#endif
} else {
CHECK_ERROR(rc);
}
}
CLEANUP:
return 0;
}
int VBOX_VM::deregister_stale_vm() {
HRESULT rc;
SAFEARRAY* pHardDisks = NULL;
SAFEARRAY* pISOImages = NULL;
SAFEARRAY* pCacheDisks = NULL;
SAFEARRAY* pMachines = NULL;
SAFEARRAY* pISOMachines = NULL;
SAFEARRAY* pCacheMachines = NULL;
CComSafeArray<LPDISPATCH> aHardDisks;
CComSafeArray<LPDISPATCH> aISOImages;
CComSafeArray<LPDISPATCH> aCacheDisks;
CComSafeArray<BSTR> aMachines;
CComSafeArray<BSTR> aISOMachines;
CComSafeArray<BSTR> aCacheMachines;
CComPtr<IMedium> pHardDisk;
CComPtr<IMedium> pISOImage;
CComPtr<IMedium> pCacheDisk;
CComPtr<IMachine> pMachine;
CComBSTR strLocation;
CComBSTR strMachineId;
string virtual_machine_root_dir;
string hdd_image_location;
string iso_image_location;
string cache_image_location;
get_slot_directory(virtual_machine_root_dir);
hdd_image_location = string(virtual_machine_root_dir + "\\" + image_filename);
rc = m_pPrivate->m_pVirtualBox->get_HardDisks(&pHardDisks);
if (SUCCEEDED(rc)) {
aHardDisks.Attach(pHardDisks);
for (int i = 0; i < (int)aHardDisks.GetCount(); i++) {
pHardDisk = aHardDisks[i];
pHardDisk->get_Location(&strLocation);
if (0 == stricmp(hdd_image_location.c_str(), CW2A(strLocation))) {
// Disk found
//
rc = pHardDisk->get_MachineIds(&pMachines);
if (SUCCEEDED(rc) && pMachines) {
aMachines.Attach(pMachines);
// Delete all registered VMs attached to this disk image
//
for (int j = 0; j < (int)aMachines.GetCount(); j++) {
strMachineId = aMachines[j];
vm_name = CW2A(strMachineId);
deregister_vm(false);
}
} else {
// Disk is in the Media Registry but now currently attached
// to a VM. Close it.
pHardDisk->Close();
}
}
}
}
if (enable_isocontextualization) {
iso_image_location = string(virtual_machine_root_dir + "\\" + iso_image_filename);
rc = m_pPrivate->m_pVirtualBox->get_DVDImages(&pISOImages);
if (SUCCEEDED(rc)) {
aISOImages.Attach(pISOImages);
for (int i = 0; i < (int)aISOImages.GetCount(); i++) {
pISOImage = aISOImages[i];
pISOImage->get_Location(&strLocation);
if (0 == stricmp(iso_image_location.c_str(), CW2A(strLocation))) {
// Image found
//
rc = pISOImage->get_MachineIds(&pISOMachines);
if (SUCCEEDED(rc) && pISOMachines) {
aISOMachines.Attach(pISOMachines);
// Delete all registered VMs attached to this disk image
//
for (int j = 0; j < (int)aISOMachines.GetCount(); j++) {
strMachineId = aISOMachines[j];
vm_name = CW2A(strMachineId);
deregister_vm(false);
}
} else {
// Disk is in the Media Registry but now currently attached
// to a VM. Close it.
pISOImage->Close();
}
}
}
}
}
if (enable_isocontextualization && enable_cache_disk) {
cache_image_location = string(virtual_machine_root_dir + "\\" + cache_disk_filename);
rc = m_pPrivate->m_pVirtualBox->get_HardDisks(&pCacheDisks);
if (SUCCEEDED(rc)) {
aCacheDisks.Attach(pCacheDisks);
for (int i = 0; i < (int)aCacheDisks.GetCount(); i++) {
pCacheDisk = aCacheDisks[i];
pCacheDisk->get_Location(&strLocation);
if (0 == stricmp(cache_image_location.c_str(), CW2A(strLocation))) {
// Disk found
//
rc = pCacheDisk->get_MachineIds(&pCacheMachines);
if (SUCCEEDED(rc) && pCacheMachines) {
aCacheMachines.Attach(pCacheMachines);
// Delete all registered VMs attached to this disk image
//
for (int j = 0; j < (int)aCacheMachines.GetCount(); j++) {
strMachineId = aCacheMachines[j];
vm_name = CW2A(strMachineId);
deregister_vm(false);
}
} else {
// Disk is in the Media Registry but now currently attached
// to a VM. Close it.
pCacheDisk->Close();
}
}
}
}
}
return 0;
}
int VBOX_VM::poll(bool log_state) {
int retval = ERR_EXEC;
APP_INIT_DATA aid;
string command;
string output;
string::iterator iter;
string vmstate;
static string vmstate_old = "poweredoff";
//size_t vmstate_start;
//size_t vmstate_end;
boinc_get_init_data_p(&aid);
//
// Is our environment still sane?
//
#ifdef _WIN32
if (aid.using_sandbox && vboxsvc_pid_handle && !process_exists(vboxsvc_pid_handle)) {
vboxlog_msg("Status Report: vboxsvc.exe is no longer running.");
}
if (started_successfully && vm_pid_handle && !process_exists(vm_pid_handle)) {
vboxlog_msg("Status Report: virtualbox.exe/vboxheadless.exe is no longer running.");
}
#else
if (started_successfully && vm_pid && !process_exists(vm_pid)) {
vboxlog_msg("Status Report: virtualbox/vboxheadless is no longer running.");
}
#endif
//
// What state is the VM in?
//
vmstate =read_vm_log();
if (vmstate != "Error in parsing the log file") {
// VirtualBox Documentation suggests that that a VM is running when its
// machine state is between MachineState_FirstOnline and MachineState_LastOnline
// which as of this writing is 5 and 17.
//
// VboxManage's source shows more than that though:
// see: http://www.virtualbox.org/browser/trunk/src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp
//
// So for now, go with what VboxManage is reporting.
//
if (vmstate == "running") {
online = true;
saving = false;
restoring = false;
suspended = false;
crashed = false;
}
else if (vmstate == "paused") {
online = true;
saving = false;
restoring = false;
suspended = true;
crashed = false;
}
else if (vmstate == "saved") {
online = false;
saving = false;
restoring = false;
suspended = true;
crashed = false;
}
else if (vmstate == "starting") {
online = true;
saving = false;
restoring = false;
suspended = false;
crashed = false;
}
else if (vmstate == "stopping") {
online = true;
saving = false;
restoring = false;
suspended = false;
crashed = false;
}
else if (vmstate == "saving") {
online = true;
saving = true;
restoring = false;
suspended = false;
crashed = false;
}
else if (vmstate == "restoring") {
online = true;
saving = false;
restoring = true;
suspended = false;
crashed = false;
}
else if (vmstate == "livesnapshotting") {
online = true;
saving = false;
restoring = false;
suspended = false;
crashed = false;
}
else if (vmstate == "deletingsnapshotonline") {
online = true;
saving = false;
restoring = false;
suspended = false;
crashed = false;
}
else if (vmstate == "deletingsnapshotpaused") {
online = true;
saving = false;
restoring = false;
suspended = false;
crashed = false;
}
else if (vmstate == "aborted") {
online = false;
saving = false;
restoring = false;
suspended = false;
crashed = true;
}
else if (vmstate == "gurumeditation") {
online = false;
saving = false;
restoring = false;
suspended = false;
crashed = true;
}
else {
online = false;
saving = false;
restoring = false;
suspended = false;
crashed = false;
if (log_state) {
vboxlog_msg("VM is no longer is a running state. It is in '%s'.", vmstate.c_str());
}
}
if (log_state && (vmstate_old != vmstate)) {
vboxlog_msg("VM state change detected. (old = '%s', new = '%s')", vmstate_old.c_str(), vmstate.c_str());
vmstate_old = vmstate;
}
retval = BOINC_SUCCESS;
}
else
{
vboxlog_msg("Error in parsing the log file");
}
return retval;
}
int VBOX_VM::poll2(bool log_state) {
int retval = ERR_EXEC;
APP_INIT_DATA aid;
HRESULT rc;
CComPtr<IMachine> pMachine;
MachineState vmstate;
static MachineState vmstate_old = MachineState_PoweredOff;
boinc_get_init_data_p(&aid);
//
// Is our environment still sane?
//
if (aid.using_sandbox && vboxsvc_pid_handle && !process_exists(vboxsvc_pid_handle)) {
vboxlog_msg("Status Report: vboxsvc.exe is no longer running.");
}
if (started_successfully && vm_pid_handle && !process_exists(vm_pid_handle)) {
vboxlog_msg("Status Report: virtualbox.exe/vboxheadless.exe is no longer running.");
}
//
// What state is the VM in?
//
rc = m_pPrivate->m_pVirtualBox->FindMachine(CComBSTR(vm_master_name.c_str()), &pMachine);
if (SUCCEEDED(rc) && pMachine) {
rc = pMachine->get_State(&vmstate);
if (SUCCEEDED(rc)) {
// VirtualBox Documentation suggests that that a VM is running when its
// machine state is between MachineState_FirstOnline and MachineState_LastOnline
// which as of this writing is 5 and 17.
//
// VboxManage's source shows more than that though:
// see: http://www.virtualbox.org/browser/trunk/src/VBox/Frontends/VBoxManage/VBoxManageInfo.cpp
//
// So for now, go with what VboxManage is reporting.
//
switch(vmstate)
{
case MachineState_Running:
online = true;
saving = false;
restoring = false;
suspended = false;
crashed = false;
break;
case MachineState_Paused:
online = true;
saving = false;
restoring = false;
suspended = true;
crashed = false;
break;
case MachineState_Starting:
online = true;
saving = false;
restoring = false;
suspended = false;
crashed = false;
break;
case MachineState_Stopping:
online = true;
saving = false;
restoring = false;
suspended = false;
crashed = false;
break;
case MachineState_Saving:
online = true;
saving = true;
restoring = false;
suspended = false;
crashed = false;
break;
case MachineState_Restoring:
online = true;
saving = false;
restoring = true;
suspended = false;
crashed = false;
break;
case MachineState_LiveSnapshotting:
online = true;
saving = false;
restoring = false;
suspended = false;
crashed = false;
break;
case MachineState_DeletingSnapshotOnline:
online = true;
saving = false;
restoring = false;
suspended = false;
crashed = false;
break;
case MachineState_DeletingSnapshotPaused:
online = true;
saving = false;
restoring = false;
suspended = false;
crashed = false;
break;
case MachineState_Aborted:
online = false;
saving = false;
restoring = false;
suspended = false;
crashed = true;
break;
case MachineState_Stuck:
online = false;
saving = false;
restoring = false;
suspended = false;
crashed = true;
break;
default:
online = false;
saving = false;
restoring = false;
suspended = false;
crashed = false;
if (log_state) {
vboxlog_msg("VM is no longer is a running state. It is in '%s'.", MachineStateToName(vmstate));
}
break;
}
if (log_state && (vmstate_old != vmstate)) {
vboxlog_msg(
"VM state change detected. (old = '%s', new = '%s')",
MachineStateToName(vmstate_old),
MachineStateToName(vmstate)
);
vmstate_old = vmstate;
}
retval = BOINC_SUCCESS;
}
}
//
// Grab a snapshot of the latest log file. Avoids multiple queries across several
// functions.
//
get_vm_log(vm_log);
//
// Dump any new VM Guest Log entries
//
dump_vmguestlog_entries();
return retval;
}
int VBOX_VM::start() {
int retval = ERR_EXEC;
HRESULT rc;
CComBSTR session_type;
CComPtr<IMachine> pMachineRO;
CComPtr<IProgress> pProgress;
APP_INIT_DATA aid;
long bCompleted = 0;
double timeout;
boinc_get_init_data_p(&aid);
vboxlog_msg("Starting VM. (%s, slot#%d)", vm_name.c_str(), aid.slot);
if (!headless) {
session_type = _T("gui");
} else {
session_type = _T("headless");
}
rc = m_pPrivate->m_pVirtualBox->FindMachine(CComBSTR(vm_master_name.c_str()), &pMachineRO);
if (SUCCEEDED(rc)) {
// Start a VM session
rc = pMachineRO->LaunchVMProcess(m_pPrivate->m_pSession, session_type, NULL, &pProgress);
if (CHECK_ERROR(rc)) goto CLEANUP;
// Wait until VM is running.
rc = pProgress->WaitForCompletion(-1);
if (CHECK_ERROR(rc)) goto CLEANUP;
pProgress->get_Completed(&bCompleted);
if (bCompleted) {
// We should now own what goes on with the VM.
//
pMachineRO->LockMachine(m_pPrivate->m_pSession, LockType_Write);
m_pPrivate->m_pSession->get_Machine(&m_pPrivate->m_pMachine);
rc = m_pPrivate->m_pMachine->get_SessionPID((ULONG*)&vm_pid);
if (CHECK_ERROR(rc)) goto CLEANUP;
vm_pid_handle = OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION,
FALSE,
vm_pid
);
// Make sure we are in a running state before proceeding
//
timeout = dtime() + 300;
do {
poll(false);
if (online) break;
boinc_sleep(1.0);
} while (timeout >= dtime());
vboxlog_msg("Successfully started VM. (PID = '%d')", vm_pid);
started_successfully = true;
retval = BOINC_SUCCESS;
} else {
vboxlog_msg("VM failed to start.");
}
}
CLEANUP:
return retval;
}
int VBOX_VM::stop() {
int retval = ERR_EXEC;
HRESULT rc;
double timeout;
CComPtr<IProgress> pProgress;
vboxlog_msg("Stopping VM.");
if (online) {
#if defined(_VIRTUALBOX42_) || defined(_VIRTUALBOX43_)
CComPtr<IConsole> pConsole;
// Get console object.
rc = m_pPrivate->m_pSession->get_Console(&pConsole);
if (CHECK_ERROR(rc)) goto CLEANUP;
// Save the state of the machine.
rc = pConsole->SaveState(&pProgress);
if (CHECK_ERROR(rc)) goto CLEANUP;
#else
rc = m_pPrivate->m_pMachine->SaveState(&pProgress);
if (CHECK_ERROR(rc)) goto CLEANUP;
#endif
// Wait until VM is powered down.
rc = pProgress->WaitForCompletion(-1);
if (CHECK_ERROR(rc)) goto CLEANUP;
// Wait for up to 5 minutes for the VM to switch states. A system
// under load can take a while. Since the poll function can wait for up
// to 45 seconds to execute a command we need to make this time based instead
// of iteration based.
timeout = dtime() + 300;
do {
poll(false);
if (!online && !saving) break;
boinc_sleep(1.0);
} while (timeout >= dtime());
if (!online) {
vboxlog_msg("Successfully stopped VM.");
retval = BOINC_SUCCESS;
} else {
vboxlog_msg("VM did not stop when requested.");
// Attempt to terminate the VM
retval = kill_program(vm_pid);
if (retval) {
vboxlog_msg("VM was NOT successfully terminated.");
} else {
vboxlog_msg("VM was successfully terminated.");
}
}
m_pPrivate->m_pSession->UnlockMachine();
boinc_sleep(5.0);
}
CLEANUP:
return retval;
}
int VBOX_VM::poweroff() {
int retval = ERR_EXEC;
HRESULT rc;
double timeout;
CComPtr<IConsole> pConsole;
CComPtr<IProgress> pProgress;
vboxlog_msg("Powering off VM.");
if (online) {
// Get console object.
rc = m_pPrivate->m_pSession->get_Console(&pConsole);
if (CHECK_ERROR(rc)) goto CLEANUP;
// Power down the VM as quickly as possible.
rc = pConsole->PowerDown(&pProgress);
if (CHECK_ERROR(rc)) goto CLEANUP;
// Wait until VM is powered down.
rc = pProgress->WaitForCompletion(-1);
if (CHECK_ERROR(rc)) goto CLEANUP;
// Wait for up to 5 minutes for the VM to switch states. A system
// under load can take a while. Since the poll function can wait for up
// to 45 seconds to execute a command we need to make this time based instead
// of iteration based.
timeout = dtime() + 300;
do {
poll(false);
if (!online && !saving) break;
boinc_sleep(1.0);
} while (timeout >= dtime());
if (!online) {
vboxlog_msg("Successfully stopped VM.");
retval = BOINC_SUCCESS;
} else {
vboxlog_msg("VM did not power off when requested.");
// Attempt to terminate the VM
retval = kill_program(vm_pid);
if (retval) {
vboxlog_msg("VM was NOT successfully terminated.");
} else {
vboxlog_msg("VM was successfully terminated.");
}
}
m_pPrivate->m_pSession->UnlockMachine();
boinc_sleep(5.0);
}
CLEANUP:
return retval;
}
int VBOX_VM::pause() {
int retval = ERR_EXEC;
HRESULT rc;
CComPtr<IConsole> pConsole;
// Restore the process priority back to the default process priority
// to speed up the last minute maintenance tasks before the VirtualBox
// VM goes to sleep
//
reset_vm_process_priority();
// Get console object.
rc = m_pPrivate->m_pSession->get_Console(&pConsole);
if (CHECK_ERROR(rc)) goto CLEANUP;
// Pause the machine.
if (pConsole) {
rc = pConsole->Pause();
if (CHECK_ERROR(rc)) goto CLEANUP;
retval = BOINC_SUCCESS;
}
CLEANUP:
return retval;
}
int VBOX_VM::resume() {
int retval = ERR_EXEC;
HRESULT rc;
CComPtr<IConsole> pConsole;
// Set the process priority back to the lowest level before resuming
// execution
//
lower_vm_process_priority();
// Get console object.
rc = m_pPrivate->m_pSession->get_Console(&pConsole);
if (CHECK_ERROR(rc)) goto CLEANUP;
// Resume the machine.
if (pConsole) {
rc = pConsole->Resume();
if (CHECK_ERROR(rc)) goto CLEANUP;
retval = BOINC_SUCCESS;
}
CLEANUP:
return retval;
}
int VBOX_VM::capture_screenshot() {
if (enable_screenshots_on_error) {
#if defined(_VIRTUALBOX50_) || defined(_VIRTUALBOX51_)
int retval = ERR_EXEC;
ULONG width, height, bpp;
LONG xOrigin, yOrigin;
GuestMonitorStatus monitorStatus;
string virtual_machine_slot_directory;
string screenshot_location;
HRESULT rc;
FILE* f = NULL;
SAFEARRAY* pScreenshot = NULL;
CComSafeArray<BYTE> aScreenshot;
CComPtr<IConsole> pConsole;
CComPtr<IDisplay> pDisplay;
CComPtr<IKeyboard> pKeyboard;
get_slot_directory(virtual_machine_slot_directory);
vboxlog_msg("Capturing screenshot.");
rc = m_pPrivate->m_pSession->get_Console(&pConsole);
if (CHECK_ERROR(rc)) {
} else {
rc = pConsole->get_Display(&pDisplay);
if (CHECK_ERROR(rc)) {
} else {
// Due to a recently fixed bug in VirtualBox we are going to attempt to prevent receiving garbage
// by waking up the console. We'll attempt to virtually tap the 'spacebar'.
rc = pConsole->get_Keyboard(&pKeyboard);
if (CHECK_ERROR(rc)) {
} else {
pKeyboard->PutScancode(0x39);
boinc_sleep(1);
}
rc = pDisplay->GetScreenResolution(0, &width, &height, &bpp, &xOrigin, &yOrigin, &monitorStatus);
if (CHECK_ERROR(rc)) {
} else {
vboxlog_msg("Retrieving screenshot from VirtualBox.");
rc = pDisplay->TakeScreenShotToArray(0, width, height, BitmapFormat_PNG, &pScreenshot);
if (SUCCEEDED(rc)) {
aScreenshot.Attach(pScreenshot);
vboxlog_msg("Writing screenshot to disk.");
screenshot_location = virtual_machine_slot_directory;
screenshot_location += "/";
screenshot_location += SCREENSHOT_FILENAME;
f = fopen(screenshot_location.c_str(), "wb");
if (f) {
fwrite(aScreenshot.GetSafeArrayPtr(), sizeof(BYTE), aScreenshot.GetCount(), f);
fclose(f);
} else {
vboxlog_msg("Failed to write screenshot to disk.");
}
} else {
vboxlog_msg("Failed to retrieving screenshot from VirtualBox.");
}
}
}
}
vboxlog_msg("Screenshot completed.");
#endif
}
return 0;
}
int VBOX_VM::create_snapshot(double elapsed_time) {
int retval = ERR_EXEC;
char buf[256];
HRESULT rc;
CComPtr<IConsole> pConsole;
CComPtr<IProgress> pProgress;
if (disable_automatic_checkpoints) return BOINC_SUCCESS;
vboxlog_msg("Creating new snapshot for VM.");
// Pause VM - Try and avoid the live snapshot and trigger an online
// snapshot instead.
pause();
// Create new snapshot
sprintf(buf, "%d", (int)elapsed_time);
#if defined(_VIRTUALBOX42_) || defined(_VIRTUALBOX43_)
rc = m_pPrivate->m_pSession->get_Console(&pConsole);
if (CHECK_ERROR(rc)) {
} else {
rc = pConsole->TakeSnapshot(CComBSTR(string(string("boinc_") + buf).c_str()), CComBSTR(""), &pProgress);
if (CHECK_ERROR(rc)) {
} else {
rc = pProgress->WaitForCompletion(-1);
if (CHECK_ERROR(rc)) {
}
}
}
#else
CComBSTR strUUID;
rc = m_pPrivate->m_pMachine->TakeSnapshot(CComBSTR(string(string("boinc_") + buf).c_str()), CComBSTR(""), true, &strUUID, &pProgress);
if (CHECK_ERROR(rc)) {
} else {
rc = pProgress->WaitForCompletion(-1);
if (CHECK_ERROR(rc)) {
}
}
#endif
// Resume VM
resume();
// Set the suspended flag back to false before deleting the stale
// snapshot
poll(false);
// Delete stale snapshot(s), if one exists
cleanup_snapshots(false);
if (BOINC_SUCCESS == retval) {
vboxlog_msg("Checkpoint completed.");
}
return retval;
}
int VBOX_VM::cleanup_snapshots(bool delete_active) {
int retval = ERR_EXEC;
HRESULT rc;
CComPtr<IConsole> pConsole;
CComPtr<ISnapshot> pCurrentSnapshot;
CComPtr<ISnapshot> pRootSnapshot;
CComBSTR tmp;
std::string current_snapshot_id;
std::vector<std::string> snapshots;
rc = m_pPrivate->m_pSession->get_Console(&pConsole);
if (CHECK_ERROR(rc)) goto CLEANUP;
// Get the current snapshot, if we do not need to delete the active snapshot
//
if (!delete_active) {
rc = m_pPrivate->m_pMachine->get_CurrentSnapshot(&pCurrentSnapshot);
if (SUCCEEDED(rc) && pCurrentSnapshot) {
rc = pCurrentSnapshot->get_Id(&tmp);
if (SUCCEEDED(rc)) {
current_snapshot_id = CW2A(tmp);
}
}
}
// Get the root snapshot and traverse the tree
//
rc = m_pPrivate->m_pMachine->FindSnapshot(CComBSTR(""), &pRootSnapshot);
if (SUCCEEDED(rc) && pRootSnapshot) {
TraverseSnapshots(current_snapshot_id, snapshots, pRootSnapshot);
}
// Delete stale snapshots
//
if (snapshots.size()) {
for (size_t i = 0; i < snapshots.size(); i++) {
CComPtr<IProgress> pProgress;
vboxlog_msg("Deleting stale snapshot.");
#if defined(_VIRTUALBOX42_) || defined(_VIRTUALBOX43_)
rc = pConsole->DeleteSnapshot(CComBSTR(snapshots[i].c_str()), &pProgress);
#else
rc = m_pPrivate->m_pMachine->DeleteSnapshot(CComBSTR(snapshots[i].c_str()), &pProgress);
#endif
if (SUCCEEDED(rc)) {
pProgress->WaitForCompletion(-1);
} else {
CHECK_ERROR(rc);
}
}
}
retval = BOINC_SUCCESS;
CLEANUP:
return retval;
}
int VBOX_VM::restore_snapshot() {
int retval = ERR_EXEC;
HRESULT rc;
CComPtr<ISession> pSession;
CComPtr<IMachine> pMachineRO;
CComPtr<IMachine> pMachine;
CComPtr<IConsole> pConsole;
CComPtr<ISnapshot> pSnapshot;
CComPtr<IProgress> pProgress;
if (disable_automatic_checkpoints) return BOINC_SUCCESS;
rc = m_pPrivate->m_pVirtualBox->FindMachine(CComBSTR(vm_name.c_str()), &pMachineRO);
if (SUCCEEDED(rc)) {
rc = pSession.CoCreateInstance(CLSID_Session);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pMachineRO->LockMachine(pSession, LockType_Write);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pSession->get_Machine(&pMachine);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pSession->get_Console(&pConsole);
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pMachine->get_CurrentSnapshot(&pSnapshot);
if (SUCCEEDED(rc)) {
vboxlog_msg("Restore from previously saved snapshot.");
#if defined(_VIRTUALBOX42_) || defined(_VIRTUALBOX43_)
rc = pConsole->RestoreSnapshot(pSnapshot, &pProgress);
#else
rc = pMachine->RestoreSnapshot(pSnapshot, &pProgress);
#endif
if (CHECK_ERROR(rc)) goto CLEANUP;
rc = pProgress->WaitForCompletion(-1);
if (CHECK_ERROR(rc)) goto CLEANUP;
vboxlog_msg("Restore completed.");
}
retval = BOINC_SUCCESS;
}
CLEANUP:
if (pMachine) {
pMachine->SaveSettings();
}
if (pSession) {
pSession->UnlockMachine();
}
return retval;
}
void VBOX_VM::dump_hypervisor_status_reports() {
SIZE_T ulMinimumWorkingSetSize;
SIZE_T ulMaximumWorkingSetSize;
if (
GetProcessWorkingSetSize(
vboxsvc_pid_handle,
&ulMinimumWorkingSetSize,
&ulMaximumWorkingSetSize)
) {
vboxlog_msg(
"Status Report (VirtualBox VboxSvc.exe): Minimum WSS: '%dKB', Maximum WSS: '%dKB'",
ulMinimumWorkingSetSize/1024,
ulMaximumWorkingSetSize/1024
);
}
if (
GetProcessWorkingSetSize(
vm_pid_handle,
&ulMinimumWorkingSetSize,
&ulMaximumWorkingSetSize)
) {
vboxlog_msg(
"Status Report (VirtualBox Vboxheadless.exe/VirtualBox.exe): Minimum WSS: '%dKB', Maximum WSS: '%dKB'",
ulMinimumWorkingSetSize/1024,
ulMaximumWorkingSetSize/1024
);
}
}
int VBOX_VM::is_registered() {
int retval = ERR_NOT_FOUND;
HRESULT rc;
CComPtr<IMachine> pMachine;
rc = m_pPrivate->m_pVirtualBox->FindMachine(CComBSTR(vm_master_name.c_str()), &pMachine);
if (VBOX_E_OBJECT_NOT_FOUND != rc) {
retval = BOINC_SUCCESS;
}
return retval;
}
bool VBOX_VM::is_system_ready(std::string& ) {
return true;
}
bool VBOX_VM::is_disk_image_registered() {
HRESULT rc;
SAFEARRAY* pHardDisks = NULL;
SAFEARRAY* pISOImages = NULL;
SAFEARRAY* pCacheDisks = NULL;
CComSafeArray<LPDISPATCH> aHardDisks;
CComSafeArray<LPDISPATCH> aISOImages;
CComSafeArray<LPDISPATCH> aCacheDisks;
CComBSTR tmp;
IMedium* pHardDisk;
IMedium* pISOImage;
IMedium* pCacheDisk;
string virtual_machine_root_dir;
string hdd_image_location;
string iso_image_location;
string cache_image_location;
get_slot_directory(virtual_machine_root_dir);
hdd_image_location = string(virtual_machine_root_dir + "\\" + image_filename);
rc = m_pPrivate->m_pVirtualBox->get_HardDisks(&pHardDisks);
if (SUCCEEDED(rc)) {
aHardDisks.Attach(pHardDisks);
for (int i = 0; i < (int)aHardDisks.GetCount(); i++) {
pHardDisk = (IMedium*)(LPDISPATCH)aHardDisks[i];
pHardDisk->get_Location(&tmp);
if (0 == stricmp(hdd_image_location.c_str(), CW2A(tmp))) {
return true;
}
}
}
if (enable_isocontextualization) {
iso_image_location = string(virtual_machine_root_dir + "\\" + iso_image_filename);
rc = m_pPrivate->m_pVirtualBox->get_DVDImages(&pISOImages);
if (SUCCEEDED(rc)) {
aISOImages.Attach(pISOImages);
for (int i = 0; i < (int)aISOImages.GetCount(); i++) {
pISOImage = (IMedium*)(LPDISPATCH)aISOImages[i];
pISOImage->get_Location(&tmp);
if (0 == stricmp(iso_image_location.c_str(), CW2A(tmp))) {
return true;
}
}
}
}
if (enable_isocontextualization && enable_cache_disk) {
cache_image_location = string(virtual_machine_root_dir + "\\" + cache_disk_filename);
rc = m_pPrivate->m_pVirtualBox->get_HardDisks(&pCacheDisks);
if (SUCCEEDED(rc)) {
aCacheDisks.Attach(pHardDisks);
for (int i = 0; i < (int)aCacheDisks.GetCount(); i++) {
pCacheDisk = (IMedium*)(LPDISPATCH)aCacheDisks[i];
pCacheDisk->get_Location(&tmp);
if (0 == stricmp(cache_image_location.c_str(), CW2A(tmp))) {
return true;
}
}
}
}
return false;
}
bool VBOX_VM::is_extpack_installed() {
CComPtr<IExtPackManager> pExtPackManager;
CComPtr<IExtPack> pExtPack;
long bUsable = 0;
HRESULT rc;
rc = m_pPrivate->m_pVirtualBox->get_ExtensionPackManager(&pExtPackManager);
if (SUCCEEDED(rc)) {
rc = pExtPackManager->IsExtPackUsable(CComBSTR("Oracle VM VirtualBox Extension Pack"), &bUsable);
if (SUCCEEDED(rc)) {
if (bUsable) {
return true;
}
}
}
return false;
}
int VBOX_VM::get_install_directory(string& install_directory ) {
LONG lReturnValue;
HKEY hkSetupHive;
LPTSTR lpszRegistryValue = NULL;
DWORD dwSize = 0;
// change the current directory to the boinc data directory if it exists
lReturnValue = RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
_T("SOFTWARE\\Oracle\\VirtualBox"),
0,
KEY_READ,
&hkSetupHive
);
if (lReturnValue == ERROR_SUCCESS) {
// How large does our buffer need to be?
lReturnValue = RegQueryValueEx(
hkSetupHive,
_T("InstallDir"),
NULL,
NULL,
NULL,
&dwSize
);
if (lReturnValue != ERROR_FILE_NOT_FOUND) {
// Allocate the buffer space.
lpszRegistryValue = (LPTSTR) malloc(dwSize);
(*lpszRegistryValue) = NULL;
// Now get the data
lReturnValue = RegQueryValueEx(
hkSetupHive,
_T("InstallDir"),
NULL,
NULL,
(LPBYTE)lpszRegistryValue,
&dwSize
);
install_directory = lpszRegistryValue;
}
}
if (hkSetupHive) RegCloseKey(hkSetupHive);
if (lpszRegistryValue) free(lpszRegistryValue);
if (install_directory.empty()) {
return ERR_FREAD;
}
return BOINC_SUCCESS;
}
int VBOX_VM::get_version_information(string& version_raw, string& version_display) {
LONG lReturnValue;
HKEY hkSetupHive;
LPSTR lpszRegistryValue = NULL;
DWORD dwSize = 0;
char buf[256];
// change the current directory to the boinc data directory if it exists
lReturnValue = RegOpenKeyExA(
HKEY_LOCAL_MACHINE,
"SOFTWARE\\Oracle\\VirtualBox",
0,
KEY_READ,
&hkSetupHive
);
if (lReturnValue == ERROR_SUCCESS) {
// How large does our buffer need to be?
lReturnValue = RegQueryValueExA(
hkSetupHive,
"VersionExt",
NULL,
NULL,
NULL,
&dwSize
);
if (lReturnValue != ERROR_FILE_NOT_FOUND) {
// Allocate the buffer space.
lpszRegistryValue = (LPTSTR) malloc(dwSize);
(*lpszRegistryValue) = NULL;
// Now get the data
lReturnValue = RegQueryValueExA(
hkSetupHive,
"VersionExt",
NULL,
NULL,
(LPBYTE)lpszRegistryValue,
&dwSize
);
version_raw = lpszRegistryValue;
snprintf(
buf, sizeof(buf),
"VirtualBox COM Interface (Version: %s)",
lpszRegistryValue
);
version_display = buf;
}
}
if (hkSetupHive) RegCloseKey(hkSetupHive);
if (lpszRegistryValue) free(lpszRegistryValue);
if (version_raw.empty()) {
version_raw = "Unknown";
version_display = "VirtualBox COM Interface (Version: Unknown)";
}
return BOINC_SUCCESS;
}
int VBOX_VM::get_guest_additions(string& guest_additions) {
int retval = ERR_NOT_FOUND;
HRESULT rc;
CComPtr<ISystemProperties> properties;
CComBSTR tmp;
rc = m_pPrivate->m_pVirtualBox->get_SystemProperties(&properties);
if (SUCCEEDED(rc)) {
rc = properties->get_DefaultAdditionsISO(&tmp);
if (SUCCEEDED(rc)) {
guest_additions = CW2A(tmp);
if (!boinc_file_exists(guest_additions.c_str())) {
guest_additions.clear();
} else {
retval = BOINC_SUCCESS;
}
}
}
return retval;
}
int VBOX_VM::get_default_network_interface(string& iface) {
int retval = ERR_EXEC;
HRESULT rc;
SAFEARRAY* pNICS = NULL;
CComSafeArray<LPDISPATCH> aNICS;
CComPtr<IHost> pHost;
CComBSTR tmp;
IHostNetworkInterface* pNIC;
rc = m_pPrivate->m_pVirtualBox->get_Host(&pHost);
if (SUCCEEDED(rc)) {
rc = pHost->FindHostNetworkInterfacesOfType(HostNetworkInterfaceType_Bridged, &pNICS);
if (SUCCEEDED(rc)) {
// Automatically clean up array after use
aNICS.Attach(pNICS);
// We only need the 'default' nic, which is usally the first one.
pNIC = (IHostNetworkInterface*)((LPDISPATCH)aNICS[0]);
// Get the name for future use
rc = pNIC->get_Name(&tmp);
if (SUCCEEDED(rc)) {
iface = CW2A(tmp);
retval = BOINC_SUCCESS;
}
}
}
return retval;
}
int VBOX_VM::get_vm_network_bytes_sent(double& sent) {
int retval = ERR_EXEC;
HRESULT rc;
CComPtr<IConsole> pConsole;
CComPtr<IMachineDebugger> pDebugger;
CComBSTR strPattern("/Devices/*/TransmitBytes");
CComBSTR strOutput;
string output;
string counter_value;
size_t counter_start;
size_t counter_end;
// Get console object.
rc = m_pPrivate->m_pSession->get_Console(&pConsole);
if (CHECK_ERROR(rc)) goto CLEANUP;
// Get debugger object
rc = pConsole->get_Debugger(&pDebugger);
if (SUCCEEDED(rc)) {
rc = pDebugger->GetStats(strPattern, false, &strOutput);
if (SUCCEEDED(rc)) {
output = CW2A(strOutput);
// Output should look like this:
// <?xml version="1.0" encoding="UTF-8" standalone="no"?>
// <Statistics>
// <Counter c="397229" unit="bytes" name="/Devices/PCNet0/TransmitBytes"/>
// <Counter c="256" unit="bytes" name="/Devices/PCNet1/TransmitBytes"/>
// </Statistics>
// add up the counter(s)
//
sent = 0;
counter_start = output.find("c=\"");
while (counter_start != string::npos) {
counter_start += 3;
counter_end = output.find("\"", counter_start);
counter_value = output.substr(counter_start, counter_end - counter_start);
sent += atof(counter_value.c_str());
counter_start = output.find("c=\"", counter_start);
}
retval = BOINC_SUCCESS;
}
}
CLEANUP:
return retval;
}
int VBOX_VM::get_vm_network_bytes_received(double& received) {
int retval = ERR_EXEC;
HRESULT rc;
CComPtr<IConsole> pConsole;
CComPtr<IMachineDebugger> pDebugger;
CComBSTR strPattern("/Devices/*/ReceiveBytes");
CComBSTR strOutput;
string output;
string counter_value;
size_t counter_start;
size_t counter_end;
// Get console object.
rc = m_pPrivate->m_pSession->get_Console(&pConsole);
if (CHECK_ERROR(rc)) goto CLEANUP;
// Get debugger object
rc = pConsole->get_Debugger(&pDebugger);
if (SUCCEEDED(rc)) {
rc = pDebugger->GetStats(strPattern, false, &strOutput);
if (SUCCEEDED(rc)) {
output = CW2A(strOutput);
// Output should look like this:
// <?xml version="1.0" encoding="UTF-8" standalone="no"?>
// <Statistics>
// <Counter c="9423150" unit="bytes" name="/Devices/PCNet0/ReceiveBytes"/>
// <Counter c="256" unit="bytes" name="/Devices/PCNet1/ReceiveBytes"/>
// </Statistics>
// add up the counter(s)
//
received = 0;
counter_start = output.find("c=\"");
while (counter_start != string::npos) {
counter_start += 3;
counter_end = output.find("\"", counter_start);
counter_value = output.substr(counter_start, counter_end - counter_start);
received += atof(counter_value.c_str());
counter_start = output.find("c=\"", counter_start);
}
retval = BOINC_SUCCESS;
}
}
CLEANUP:
return retval;
}
int VBOX_VM::get_vm_process_id() {
return vm_pid;
}
int VBOX_VM::get_vm_exit_code(unsigned long& exit_code) {
if (vm_pid_handle) {
GetExitCodeProcess(vm_pid_handle, &exit_code);
}
return 0;
}
double VBOX_VM::get_vm_cpu_time() {
double x = process_tree_cpu_time(vm_pid);
if (x > current_cpu_time) {
current_cpu_time = x;
}
return current_cpu_time;
}
// Enable the network adapter if a network connection is required.
// NOTE: Network access should never be allowed if the code running in a
// shared directory or the VM image itself is NOT signed. Doing so
// opens up the network behind the company firewall to attack.
//
// Imagine a doomsday scenario where a project has been compromised and
// an unsigned executable/VM image has been tampered with. Volunteer
// downloads compromised code and executes it on a company machine.
// Now the compromised VM starts attacking other machines on the company
// network. The company firewall cannot help because the attacking
// machine is already behind the company firewall.
//
int VBOX_VM::set_network_access(bool enabled) {
int retval = ERR_EXEC;
HRESULT rc;
CComPtr<INetworkAdapter> pNetworkAdapter;
network_suspended = !enabled;
if (enabled) {
vboxlog_msg("Enabling network access for VM.");
rc = m_pPrivate->m_pMachine->GetNetworkAdapter(0, &pNetworkAdapter);
if (CHECK_ERROR(rc)) {
} else {
rc = pNetworkAdapter->put_Enabled(TRUE);
if (SUCCEEDED(rc)) {
retval = BOINC_SUCCESS;
}
}
} else {
vboxlog_msg("Disabling network access for VM.");
rc = m_pPrivate->m_pMachine->GetNetworkAdapter(0, &pNetworkAdapter);
if (CHECK_ERROR(rc)) {
} else {
rc = pNetworkAdapter->put_Enabled(FALSE);
if (SUCCEEDED(rc)) {
retval = BOINC_SUCCESS;
}
}
}
return retval;
}
int VBOX_VM::set_cpu_usage(int percentage) {
vboxlog_msg("Setting CPU throttle for VM. (%d%%)", percentage);
m_pPrivate->m_pMachine->put_CPUExecutionCap(percentage);
return BOINC_SUCCESS;
}
int VBOX_VM::set_network_usage(int kilobytes) {
int retval = ERR_EXEC;
HRESULT rc;
CComPtr<IBandwidthControl> pBandwidthControl;
CComPtr<IBandwidthGroup> pBandwidthGroup;
rc = m_pPrivate->m_pMachine->get_BandwidthControl(&pBandwidthControl);
if (SUCCEEDED(rc)) {
rc = pBandwidthControl->GetBandwidthGroup(CComBSTR(string(vm_name + "_net").c_str()), &pBandwidthGroup);
if (SUCCEEDED(rc)) {
if (kilobytes == 0) {
vboxlog_msg("Setting network throttle for VM. (1024GB)");
rc = pBandwidthGroup->put_MaxBytesPerSec((LONG64)1024*1024*1024*1024);
} else {
vboxlog_msg("Setting network throttle for VM. (%dKB)", kilobytes);
rc = pBandwidthGroup->put_MaxBytesPerSec((LONG64)kilobytes*1024);
}
if (CHECK_ERROR(rc)) {
} else {
retval = BOINC_SUCCESS;
}
}
}
return retval;
}
void VBOX_VM::lower_vm_process_priority() {
if (vm_pid_handle) {
SetPriorityClass(vm_pid_handle, BELOW_NORMAL_PRIORITY_CLASS);
}
}
void VBOX_VM::reset_vm_process_priority() {
if (vm_pid_handle) {
SetPriorityClass(vm_pid_handle, NORMAL_PRIORITY_CLASS);
}
}
// Launch VboxSVC.exe before going any further. if we don't, it'll be launched by
// svchost.exe with its environment block which will not contain the reference
// to VBOX_USER_HOME which is required for running in the BOINC account-based
// sandbox on Windows.
int VBOX_VM::launch_vboxsvc() {
APP_INIT_DATA aid;
PROC_MAP pm;
PROCINFO p;
string command;
int retval = ERR_EXEC;
char buf[256];
STARTUPINFO si;
PROCESS_INFORMATION pi;
int pidVboxSvc = 0;
HANDLE hVboxSvc = NULL;
memset(&si, 0, sizeof(si));
memset(&pi, 0, sizeof(pi));
boinc_get_init_data_p(&aid);
if (aid.using_sandbox) {
if (!vboxsvc_pid_handle || !process_exists(vboxsvc_pid_handle)) {
if (vboxsvc_pid_handle) CloseHandle(vboxsvc_pid_handle);
procinfo_setup(pm);
for (PROC_MAP::iterator i = pm.begin(); i != pm.end(); ++i) {
p = i->second;
// We are only looking for vboxsvc
if (0 != stricmp(p.command, "vboxsvc.exe")) continue;
// Store process id for later use
pidVboxSvc = p.id;
// Is this the vboxsvc for the current user?
// Non-service install it would be the current username
// Service install it would be boinc_project
hVboxSvc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, p.id);
if (hVboxSvc) break;
}
if (pidVboxSvc && hVboxSvc) {
vboxlog_msg("Status Report: Detected vboxsvc.exe. (PID = '%d')", pidVboxSvc);
vboxsvc_pid = pidVboxSvc;
vboxsvc_pid_handle = hVboxSvc;
retval = BOINC_SUCCESS;
} else {
si.cb = sizeof(STARTUPINFO);
si.dwFlags |= STARTF_FORCEOFFFEEDBACK | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
command = "\"VBoxSVC.exe\" --logrotate 1";
// Execute command
if (!CreateProcess(
NULL,
(LPTSTR)command.c_str(),
NULL,
NULL,
TRUE,
CREATE_NO_WINDOW,
NULL,
virtualbox_home_directory.c_str(),
&si,
&pi
)) {
vboxlog_msg(
"Status Report: Launching vboxsvc.exe failed!."
);
vboxlog_msg(
" Error: %s (%d)",
windows_format_error_string(GetLastError(), buf, sizeof(buf)),
GetLastError()
);
}
if (pi.hThread) CloseHandle(pi.hThread);
if (pi.hProcess) {
vboxlog_msg("Status Report: Launching vboxsvc.exe. (PID = '%d')", pi.dwProcessId);
vboxsvc_pid = pi.dwProcessId;
vboxsvc_pid_handle = pi.hProcess;
retval = BOINC_SUCCESS;
}
}
}
}
return retval;
}
// Launch the VM.
int VBOX_VM::launch_vboxvm() {
char cmdline[1024];
char* argv[5];
int argc;
std::string output;
int retval = ERR_EXEC;
char buf[256];
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR sd;
HANDLE hReadPipe = NULL, hWritePipe = NULL;
void* pBuf = NULL;
DWORD dwCount = 0;
unsigned long ulExitCode = 0;
unsigned long ulExitTimeout = 0;
memset(&si, 0, sizeof(si));
memset(&pi, 0, sizeof(pi));
memset(&sa, 0, sizeof(sa));
memset(&sd, 0, sizeof(sd));
// Construct the command line parameters
//
if (headless) {
argv[0] = const_cast<char*>("VboxHeadless.exe");
} else {
argv[0] = const_cast<char*>("VirtualBox.exe");
}
argv[1] = const_cast<char*>("--startvm");
argv[2] = const_cast<char*>(vm_name.c_str());
if (headless) {
argv[3] = const_cast<char*>("--vrde config");
} else {
argv[3] = const_cast<char*>("--no-startvm-errormsgbox");
}
argv[4] = NULL;
argc = 4;
strcpy(cmdline, "");
for (int i=0; i<argc; i++) {
strcat(cmdline, argv[i]);
if (i<argc-1) {
strcat(cmdline, " ");
}
}
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, true, NULL, false);
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = &sd;
if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, NULL)) {
vboxlog_msg("CreatePipe failed! (%d).", GetLastError());
goto CLEANUP;
}
SetHandleInformation(hReadPipe, HANDLE_FLAG_INHERIT, 0);
si.cb = sizeof(STARTUPINFO);
si.dwFlags |= STARTF_FORCEOFFFEEDBACK | STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.wShowWindow = SW_HIDE;
si.hStdOutput = hWritePipe;
si.hStdError = hWritePipe;
si.hStdInput = NULL;
// Execute command
if (!CreateProcess(
NULL,
cmdline,
NULL,
NULL,
TRUE,
CREATE_NO_WINDOW,
NULL,
NULL,
&si,
&pi
)) {
vboxlog_msg(
"Status Report: Launching virtualbox.exe/vboxheadless.exe failed!."
);
vboxlog_msg(
" Error: %s (%d)",
windows_format_error_string(GetLastError(), buf, sizeof(buf)),
GetLastError()
);
goto CLEANUP;
}
while(1) {
GetExitCodeProcess(pi.hProcess, &ulExitCode);
// Copy stdout/stderr to output buffer, handle in the loop so that we can
// copy the pipe as it is populated and prevent the child process from blocking
// in case the output is bigger than pipe buffer.
PeekNamedPipe(hReadPipe, NULL, NULL, NULL, &dwCount, NULL);
if (dwCount) {
pBuf = malloc(dwCount+1);
memset(pBuf, 0, dwCount+1);
if (ReadFile(hReadPipe, pBuf, dwCount, &dwCount, NULL)) {
output += (char*)pBuf;
}
free(pBuf);
}
if ((ulExitCode != STILL_ACTIVE) || (ulExitTimeout >= 1000)) break;
Sleep(250);
ulExitTimeout += 250;
}
if (ulExitCode != STILL_ACTIVE) {
vboxlog_msg(
"Status Report: Virtualbox.exe/Vboxheadless.exe exited prematurely!."
);
vboxlog_msg(
" Exit Code: %d",
ulExitCode
);
vboxlog_msg(
" Output: %s",
output.c_str()
);
}
if (pi.hProcess && (ulExitCode == STILL_ACTIVE)) {
vm_pid = pi.dwProcessId;
vm_pid_handle = pi.hProcess;
retval = BOINC_SUCCESS;
}
CLEANUP:
if (pi.hThread) CloseHandle(pi.hThread);
if (hReadPipe) CloseHandle(hReadPipe);
if (hWritePipe) CloseHandle(hWritePipe);
return retval;
}
#endif