diff --git a/samples/vboxwrapper/Makefile b/samples/vboxwrapper/Makefile
index 4762dfcf21..75db5d347b 100644
--- a/samples/vboxwrapper/Makefile
+++ b/samples/vboxwrapper/Makefile
@@ -32,5 +32,8 @@ floppyio.o: floppyio.cpp
vbox.o: vbox.cpp
g++ $(CXXFLAGS) -c vbox.cpp
-vboxwrapper: vboxwrapper.o vbox.o floppyio.o libstdc++.a $(BOINC_LIB_DIR)/libboinc.a $(BOINC_API_DIR)/libboinc_api.a
- g++ $(CXXFLAGS) -o vboxwrapper vboxwrapper.o vbox.o floppyio.o libstdc++.a -pthread -lboinc_api -lboinc
+vbox_unix.o: vbox_unix.cpp
+ g++ $(CXXFLAGS) -c vbox_unix.cpp
+
+vboxwrapper: vboxwrapper.o vbox.o vbox_unix.o floppyio.o libstdc++.a $(BOINC_LIB_DIR)/libboinc.a $(BOINC_API_DIR)/libboinc_api.a
+ g++ $(CXXFLAGS) -o vboxwrapper vboxwrapper.o vbox.o vbox_unix.o floppyio.o libstdc++.a -pthread -lboinc_api -lboinc
diff --git a/samples/vboxwrapper/Makefile_mac b/samples/vboxwrapper/Makefile_mac
index 313ad92e76..bc7f4f3543 100644
--- a/samples/vboxwrapper/Makefile_mac
+++ b/samples/vboxwrapper/Makefile_mac
@@ -24,6 +24,7 @@ CXXFLAGS = $(VARIANTFLAGS) \
OBJ = \
floppyio.o \
vbox.o \
+ vbox_unix.o \
vboxwrapper.o
PROGS = vboxwrapper
@@ -39,5 +40,8 @@ floppyio.o: floppyio.cpp
vbox.o: vbox.cpp
$(CXX) $(CXXFLAGS) -c vbox.cpp
-vboxwrapper: vboxwrapper.o vbox.o floppyio.o $(BOINC_BUILD_DIR)/libboinc.a $(BOINC_BUILD_DIR)/libboinc_api.a
- $(CXX) $(CXXFLAGS) $(LDFLAGS) -o vboxwrapper vboxwrapper.o vbox.o floppyio.o -pthread -lboinc_api -lboinc
+vbox_unix.o: vbox_unix.cpp
+ $(CXX) $(CXXFLAGS) -c vbox_unix.cpp
+
+vboxwrapper: vboxwrapper.o vbox.o vbox_unix.o floppyio.o $(BOINC_BUILD_DIR)/libboinc.a $(BOINC_BUILD_DIR)/libboinc_api.a
+ $(CXX) $(CXXFLAGS) $(LDFLAGS) -o vboxwrapper vboxwrapper.o vbox.o vbox_unix.o floppyio.o -pthread -lboinc_api -lboinc
diff --git a/samples/vboxwrapper/vbox.h b/samples/vboxwrapper/vbox.h
index 06572fcea3..407ca534dc 100644
--- a/samples/vboxwrapper/vbox.h
+++ b/samples/vboxwrapper/vbox.h
@@ -287,6 +287,7 @@ public:
void sanitize_output(std::string& output);
+#ifndef _WIN32
int vbm_popen(
std::string& command, std::string& output, const char* item, bool log_error = true, bool retry_failures = true, unsigned int timeout = 45, bool log_trace = true
);
@@ -299,6 +300,7 @@ public:
void vbm_trace(
std::string& command, std::string& ouput, int retval
);
+#endif
};
#endif
diff --git a/samples/vboxwrapper/vbox_unix.cpp b/samples/vboxwrapper/vbox_unix.cpp
new file mode 100644
index 0000000000..310d43046e
--- /dev/null
+++ b/samples/vboxwrapper/vbox_unix.cpp
@@ -0,0 +1,2228 @@
+// 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 .
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+using std::string;
+
+#if defined(_MSC_VER)
+#define getcwd _getcwd
+#define stricmp _stricmp
+#endif
+
+#include "diagnostics.h"
+#include "filesys.h"
+#include "parse.h"
+#include "str_util.h"
+#include "str_replace.h"
+#include "util.h"
+#include "error_numbers.h"
+#include "procinfo.h"
+#include "network.h"
+#include "boinc_api.h"
+#include "floppyio.h"
+#include "vboxwrapper.h"
+#include "vbox.h"
+
+int VBOX_VM::initialize() {
+ int rc = 0;
+ string old_path;
+ string new_path;
+ string command;
+ string output;
+ APP_INIT_DATA aid;
+ bool force_sandbox = false;
+ char buf[256];
+
+ boinc_get_init_data_p(&aid);
+ get_install_directory(virtualbox_install_directory);
+
+
+ // 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("HOME");
+ virtualbox_home_directory += "/.VirtualBox";
+ }
+
+ // On *nix style systems, VirtualBox expects that there is a home directory specified
+ // by environment variable. When it doesn't exist it attempts to store logging information
+ // in root's home directory. Bad things happen if the process isn't owned by root.
+ //
+ // if the HOME environment variable is missing force VirtualBox to use a directory it
+ // has a reasonable chance of writing log files too.
+ if (NULL == getenv("HOME")) {
+ force_sandbox = true;
+ }
+
+ // 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());
+
+ // putenv does not copy its input buffer, so we must use setenv
+ if (setenv("VBOX_USER_HOME", const_cast(virtualbox_home_directory.c_str()), 1)) {
+ fprintf(
+ stderr,
+ "%s Failed to modify the VBOX_USER_HOME path.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ }
+ }
+
+ rc = get_version_information(virtualbox_version);
+ if (rc) return rc;
+
+ rc = get_guest_additions(virtualbox_guest_additions);
+ if (rc) return rc;
+
+ return 0;
+}
+
+void VBOX_VM::poll(bool log_state) {
+ char buf[256];
+ APP_INIT_DATA aid;
+ string command;
+ string output;
+ string::iterator iter;
+ string vmstate;
+ static string vmstate_old = "poweroff";
+ size_t vmstate_start;
+ size_t vmstate_end;
+
+ boinc_get_init_data_p(&aid);
+
+ //
+ // Is our environment still sane?
+ //
+ if (vm_pid && !process_exists(vm_pid)) {
+ fprintf(
+ stderr,
+ "%s Status Report: virtualbox/vboxheadless is no longer running.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ }
+
+ //
+ // What state is the VM in?
+ //
+
+ command = "showvminfo \"" + vm_name + "\" ";
+ command += "--machinereadable ";
+
+ if (vbm_popen(command, output, "VM state", false, false, 45, false) == 0) {
+ vmstate_start = output.find("VMState=\"");
+ if (vmstate_start != string::npos) {
+ vmstate_start += 9;
+ vmstate_end = output.find("\"", vmstate_start);
+ vmstate = output.substr(vmstate_start, vmstate_end - vmstate_start);
+
+ // 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 == "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 == "deletingsnapshotlive") {
+ online = true;
+ saving = false;
+ restoring = false;
+ suspended = false;
+ crashed = false;
+ } else if (vmstate == "deletingsnapshotlivepaused") {
+ 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) {
+ fprintf(
+ stderr,
+ "%s VM is no longer is a running state. It is in '%s'.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ vmstate.c_str()
+ );
+ }
+ }
+ if (log_state && (vmstate_old != vmstate)) {
+ fprintf(
+ stderr,
+ "%s VM state change detected. (old = '%s', new = '%s')\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ vmstate_old.c_str(),
+ vmstate.c_str()
+ );
+ vmstate_old = vmstate;
+ }
+ }
+ }
+
+ //
+ // 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();
+}
+
+int VBOX_VM::create_vm() {
+ string command;
+ string output;
+ string virtual_machine_slot_directory;
+ string default_interface;
+ APP_INIT_DATA aid;
+ bool disable_acceleration = false;
+ char buf[256];
+ int retval;
+
+ boinc_get_init_data_p(&aid);
+ get_slot_directory(virtual_machine_slot_directory);
+
+
+ // Reset VM name in case it was changed while deregistering a stale VM
+ //
+ vm_name = vm_master_name;
+
+
+ fprintf(
+ stderr,
+ "%s Create VM. (%s, slot#%d) \n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ vm_name.c_str(),
+ aid.slot
+ );
+
+ // Fixup chipset and drive controller information for known configurations
+ //
+ if (enable_isocontextualization) {
+ if ("PIIX4" == vm_disk_controller_model) {
+ fprintf(
+ stderr,
+ "%s Updating drive controller type and model for desired configuration.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ vm_disk_controller_type = "sata";
+ vm_disk_controller_model = "IntelAHCI";
+ }
+ }
+
+ // Create and register the VM
+ //
+ command = "createvm ";
+ command += "--name \"" + vm_name + "\" ";
+ command += "--basefolder \"" + virtual_machine_slot_directory + "\" ";
+ command += "--ostype \"" + os_name + "\" ";
+ command += "--register";
+
+ retval = vbm_popen(command, output, "create");
+ if (retval) return retval;
+
+ // Tweak the VM's Description
+ //
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--description \"" + vm_master_description + "\" ";
+
+ vbm_popen(command, output, "modifydescription", false, false);
+
+ // Tweak the VM's CPU Count
+ //
+ fprintf(
+ stderr,
+ "%s Setting CPU Count for VM. (%s)\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ vm_cpu_count.c_str()
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--cpus " + vm_cpu_count + " ";
+
+ retval = vbm_popen(command, output, "modifycpu");
+ if (retval) return retval;
+
+ // Tweak the VM's Memory Size
+ //
+ fprintf(
+ stderr,
+ "%s Setting Memory Size for VM. (%sMB)\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ vm_memory_size_mb.c_str()
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--memory " + vm_memory_size_mb + " ";
+
+ retval = vbm_popen(command, output, "modifymem");
+ if (retval) return retval;
+
+ // Tweak the VM's Chipset Options
+ //
+ fprintf(
+ stderr,
+ "%s Setting Chipset Options for VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--acpi on ";
+ command += "--ioapic on ";
+
+ retval = vbm_popen(command, output, "modifychipset");
+ if (retval) return retval;
+
+ // Tweak the VM's Boot Options
+ //
+ fprintf(
+ stderr,
+ "%s Setting Boot Options for VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--boot1 disk ";
+ command += "--boot2 dvd ";
+ command += "--boot3 none ";
+ command += "--boot4 none ";
+
+ retval = vbm_popen(command, output, "modifyboot");
+ if (retval) return retval;
+
+ // Tweak the VM's Network Configuration
+ //
+ if (network_bridged_mode) {
+ fprintf(
+ stderr,
+ "%s Setting Network Configuration for Bridged Mode.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--nic1 bridged ";
+ command += "--cableconnected1 off ";
+
+ retval = vbm_popen(command, output, "set bridged mode");
+ if (retval) return retval;
+
+ get_default_network_interface(default_interface);
+ fprintf(
+ stderr,
+ "%s Setting Bridged Interface. (%s)\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ default_interface.c_str()
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--bridgeadapter1 \"";
+ command += default_interface;
+ command += "\" ";
+
+ retval = vbm_popen(command, output, "set bridged interface");
+ if (retval) return retval;
+ } else {
+ fprintf(
+ stderr,
+ "%s Setting Network Configuration for NAT.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--nic1 nat ";
+ command += "--natdnsproxy1 on ";
+ command += "--cableconnected1 off ";
+
+ retval = vbm_popen(command, output, "modifynetwork");
+ if (retval) return retval;
+ }
+
+ // Tweak the VM's USB Configuration
+ //
+ fprintf(
+ stderr,
+ "%s Disabling USB Support for VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--usb off ";
+
+ vbm_popen(command, output, "modifyusb", false, false);
+
+ // Tweak the VM's COM Port Support
+ //
+ fprintf(
+ stderr,
+ "%s Disabling COM Port Support for VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--uart1 off ";
+ command += "--uart2 off ";
+
+ vbm_popen(command, output, "modifycom", false, false);
+
+ // Tweak the VM's LPT Port Support
+ //
+ fprintf(
+ stderr,
+ "%s Disabling LPT Port Support for VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--lpt1 off ";
+ command += "--lpt2 off ";
+
+ vbm_popen(command, output, "modifylpt", false, false);
+
+ // Tweak the VM's Audio Support
+ //
+ fprintf(
+ stderr,
+ "%s Disabling Audio Support for VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--audio none ";
+
+ vbm_popen(command, output, "modifyaudio", false, false);
+
+ // Tweak the VM's Clipboard Support
+ //
+ fprintf(
+ stderr,
+ "%s Disabling Clipboard Support for VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--clipboard disabled ";
+
+ vbm_popen(command, output, "modifyclipboard", false, false);
+
+ // Tweak the VM's Drag & Drop Support
+ //
+ fprintf(
+ stderr,
+ "%s Disabling Drag and Drop Support for VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--draganddrop disabled ";
+
+ vbm_popen(command, output, "modifydragdrop", false, false);
+
+ // 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")) {
+ fprintf(
+ stderr,
+ "%s Hardware acceleration CPU extensions not detected. Disabling VirtualBox hardware acceleration support.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ disable_acceleration = true;
+ }
+ if (strstr(aid.host_info.p_features, "hypervisor")) {
+ fprintf(
+ stderr,
+ "%s Running under Hypervisor. Disabling VirtualBox hardware acceleration support.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ disable_acceleration = true;
+ }
+ if (is_boinc_client_version_newer(aid, 7, 2, 16)) {
+ if (aid.vm_extensions_disabled) {
+ fprintf(
+ stderr,
+ "%s Hardware acceleration failed with previous execution. Disabling VirtualBox hardware acceleration support.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ 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.
+ vboxwrapper_msg_prefix(buf, sizeof(buf));
+ fprintf(
+ stderr,
+ "%s Legacy fallback configuration detected. Disabling VirtualBox hardware acceleration support.\n"
+ "%s NOTE: Upgrading to BOINC 7.2.16 or better may re-enable hardware acceleration.\n",
+ buf,
+ buf
+ );
+ 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) {
+ fprintf(
+ stderr,
+ "%s Disabling hardware acceleration support for virtualization.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--hwvirtex off ";
+
+ retval = vbm_popen(command, output, "VT-x/AMD-V support");
+ if (retval) return retval;
+ }
+ } else if (os_name.find("_64") != std::string::npos) {
+ if (disable_acceleration) {
+ fprintf(
+ stderr,
+ "%s ERROR: Invalid configuration. VM type requires acceleration but the current configuration cannot support it.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ return ERR_INVALID_PARAM;
+ }
+ }
+
+ // Add storage controller to VM
+ // See: http://www.virtualbox.org/manual/ch08.html#vboxmanage-storagectl
+ // See: http://www.virtualbox.org/manual/ch05.html#iocaching
+ //
+ fprintf(
+ stderr,
+ "%s Adding storage controller to VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "storagectl \"" + vm_name + "\" ";
+ command += "--name \"Hard Disk Controller\" ";
+ command += "--add \"" + vm_disk_controller_type + "\" ";
+ command += "--controller \"" + vm_disk_controller_model + "\" ";
+ if (
+ (vm_disk_controller_type == "sata") || (vm_disk_controller_type == "SATA") ||
+ (vm_disk_controller_type == "scsi") || (vm_disk_controller_type == "SCSI") ||
+ (vm_disk_controller_type == "sas") || (vm_disk_controller_type == "SAS")
+ ) {
+ command += "--hostiocache off ";
+ }
+ if ((vm_disk_controller_type == "sata") || (vm_disk_controller_type == "SATA")) {
+ if (is_virtualbox_version_newer(4, 3, 0)) {
+ command += "--portcount 3";
+ } else {
+ command += "--sataportcount 3";
+ }
+ }
+
+ retval = vbm_popen(command, output, "add storage controller (fixed disk)");
+ if (retval) return retval;
+
+ // Add storage controller for a floppy device if desired
+ //
+ if (enable_floppyio) {
+ command = "storagectl \"" + vm_name + "\" ";
+ command += "--name \"Floppy Controller\" ";
+ command += "--add floppy ";
+
+ retval = vbm_popen(command, output, "add storage controller (floppy)");
+ if (retval) return retval;
+ }
+
+ if (enable_isocontextualization) {
+
+ // Add virtual ISO 9660 disk drive to VM
+ //
+ fprintf(
+ stderr,
+ "%s Adding virtual ISO 9660 disk drive to VM. (%s)\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ iso_image_filename.c_str()
+ );
+ command = "storageattach \"" + vm_name + "\" ";
+ command += "--storagectl \"Hard Disk Controller\" ";
+ command += "--port 0 ";
+ command += "--device 0 ";
+ command += "--type dvddrive ";
+ command += "--medium \"" + virtual_machine_slot_directory + "/" + iso_image_filename + "\" ";
+
+ retval = vbm_popen(command, output, "storage attach (ISO 9660 image)");
+ if (retval) return retval;
+
+ // Add guest additions to the VM
+ //
+ fprintf(
+ stderr,
+ "%s Adding VirtualBox Guest Additions to VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "storageattach \"" + vm_name + "\" ";
+ command += "--storagectl \"Hard Disk Controller\" ";
+ command += "--port 2 ";
+ command += "--device 0 ";
+ command += "--type dvddrive ";
+ command += "--medium \"" + virtualbox_guest_additions + "\" ";
+
+ retval = vbm_popen(command, output, "storage attach (guest additions image)");
+ if (retval) return retval;
+
+ // Add a virtual cache disk drive to VM
+ //
+ if (enable_cache_disk){
+ fprintf(
+ stderr,
+ "%s Adding virtual cache disk drive to VM. (%s)\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ cache_disk_filename.c_str()
+ );
+ command = "storageattach \"" + vm_name + "\" ";
+ command += "--storagectl \"Hard Disk Controller\" ";
+ command += "--port 1 ";
+ command += "--device 0 ";
+ command += "--type hdd ";
+ command += "--setuuid \"\" ";
+ command += "--medium \"" + virtual_machine_slot_directory + "/" + cache_disk_filename + "\" ";
+
+ retval = vbm_popen(command, output, "storage attach (cached disk)");
+ if (retval) return retval;
+ }
+
+ } else {
+
+ // Adding virtual hard drive to VM
+ //
+ fprintf(
+ stderr,
+ "%s Adding virtual disk drive to VM. (%s)\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ image_filename.c_str()
+ );
+ command = "storageattach \"" + vm_name + "\" ";
+ command += "--storagectl \"Hard Disk Controller\" ";
+ command += "--port 0 ";
+ command += "--device 0 ";
+ command += "--type hdd ";
+ command += "--setuuid \"\" ";
+ command += "--medium \"" + virtual_machine_slot_directory + "/" + image_filename + "\" ";
+
+ retval = vbm_popen(command, output, "storage attach (fixed disk)");
+ if (retval) return retval;
+
+ // Add guest additions to the VM
+ //
+ fprintf(
+ stderr,
+ "%s Adding VirtualBox Guest Additions to VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "storageattach \"" + vm_name + "\" ";
+ command += "--storagectl \"Hard Disk Controller\" ";
+ command += "--port 1 ";
+ command += "--device 0 ";
+ command += "--type dvddrive ";
+ command += "--medium \"" + virtualbox_guest_additions + "\" ";
+
+ retval = vbm_popen(command, output, "storage attach (guest additions image)");
+ if (retval) return retval;
+
+ }
+
+
+ // Add network bandwidth throttle group
+ //
+ if (is_virtualbox_version_newer(4, 2, 0)) {
+ fprintf(
+ stderr,
+ "%s Adding network bandwidth throttle group to VM. (Defaulting to 1024GB)\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "bandwidthctl \"" + vm_name + "\" ";
+ command += "add \"" + vm_name + "_net\" ";
+ command += "--type network ";
+ command += "--limit 1024G";
+ command += " ";
+
+ retval = vbm_popen(command, output, "network throttle group (add)");
+ if (retval) return retval;
+ }
+
+ // 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 FloppyIO(floppy_image_filename.c_str());
+ if (!pFloppy->ready()) {
+ vboxwrapper_msg_prefix(buf, sizeof(buf));
+ fprintf(
+ stderr,
+ "%s Creating virtual floppy image failed.\n"
+ "%s Error Code '%d' Error Message '%s'\n",
+ buf,
+ buf,
+ pFloppy->error,
+ pFloppy->errorStr.c_str()
+ );
+ return ERR_FWRITE;
+ }
+
+ fprintf(
+ stderr,
+ "%s Adding virtual floppy disk drive to VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "storageattach \"" + vm_name + "\" ";
+ command += "--storagectl \"Floppy Controller\" ";
+ command += "--port 0 ";
+ command += "--device 0 ";
+ command += "--medium \"" + virtual_machine_slot_directory + "/" + floppy_image_filename + "\" ";
+
+ retval = vbm_popen(command, output, "storage attach (floppy disk)");
+ if (retval) return retval;
+
+ }
+
+ // Enable the network adapter if a network connection is required.
+ //
+ if (enable_network) {
+ set_network_access(true);
+
+ // set up port forwarding
+ //
+ if (pf_guest_port) {
+ 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 45) {
+ retval = ERR_TIMEOUT;
+ break;
+ }
+ }
+
+ if (BOINC_SUCCESS == retval) {
+ fprintf(
+ stderr,
+ "%s Successfully started VM. (PID = '%d')\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ vm_pid
+ );
+ } else {
+ fprintf(
+ stderr,
+ "%s VM failed to start.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ }
+
+ return retval;
+}
+
+int VBOX_VM::stop() {
+ string command;
+ string output;
+ double timeout;
+ char buf[256];
+ int retval = 0;
+
+ fprintf(
+ stderr,
+ "%s Stopping VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ if (online) {
+ command = "controlvm \"" + vm_name + "\" savestate";
+ retval = vbm_popen(command, output, "stop VM", true, false);
+
+ // 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.
+ if (!retval) {
+ timeout = dtime() + 300;
+ do {
+ poll(false);
+ if (!online && !saving) break;
+ boinc_sleep(1.0);
+ } while (timeout >= dtime());
+ }
+
+ if (!online) {
+ fprintf(
+ stderr,
+ "%s Successfully stopped VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ retval = BOINC_SUCCESS;
+ } else {
+ fprintf(
+ stderr,
+ "%s VM did not stop when requested.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+
+ // Attempt to terminate the VM
+ retval = kill_program(vm_pid);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s VM was NOT successfully terminated.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ } else {
+ fprintf(
+ stderr,
+ "%s VM was successfully terminated.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ }
+ }
+ }
+
+ return retval;
+}
+
+int VBOX_VM::poweroff() {
+ string command;
+ string output;
+ double timeout;
+ char buf[256];
+ int retval = 0;
+
+ fprintf(
+ stderr,
+ "%s Powering off VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ if (online) {
+ command = "controlvm \"" + vm_name + "\" poweroff";
+ retval = vbm_popen(command, output, "poweroff VM", true, false);
+
+ // 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.
+ if (!retval) {
+ timeout = dtime() + 300;
+ do {
+ poll(false);
+ if (!online && !saving) break;
+ boinc_sleep(1.0);
+ } while (timeout >= dtime());
+ }
+
+ if (!online) {
+ fprintf(
+ stderr,
+ "%s Successfully powered off VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ retval = BOINC_SUCCESS;
+ } else {
+ fprintf(
+ stderr,
+ "%s VM did not power off when requested.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+
+ // Attempt to terminate the VM
+ retval = kill_program(vm_pid);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s VM was NOT successfully terminated.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ } else {
+ fprintf(
+ stderr,
+ "%s VM was successfully terminated.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ }
+ }
+ }
+
+ return retval;
+}
+
+int VBOX_VM::pause() {
+ string command;
+ string output;
+ int retval;
+
+ // 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();
+
+ command = "controlvm \"" + vm_name + "\" pause";
+ retval = vbm_popen(command, output, "pause VM");
+ if (retval) return retval;
+ suspended = true;
+ return 0;
+}
+
+int VBOX_VM::resume() {
+ string command;
+ string output;
+ int retval;
+
+ // Set the process priority back to the lowest level before resuming
+ // execution
+ //
+ lower_vm_process_priority();
+
+ command = "controlvm \"" + vm_name + "\" resume";
+ retval = vbm_popen(command, output, "resume VM");
+ if (retval) return retval;
+ suspended = false;
+ return 0;
+}
+
+int VBOX_VM::create_snapshot(double elapsed_time) {
+ string command;
+ string output;
+ char buf[256];
+ int retval;
+
+ fprintf(
+ stderr,
+ "%s Creating new snapshot for VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+
+ // Pause VM - Try and avoid the live snapshot and trigger an online
+ // snapshot instead.
+ pause();
+
+ // Create new snapshot
+ sprintf(buf, "%d", (int)elapsed_time);
+ command = "snapshot \"" + vm_name + "\" ";
+ command += "take boinc_";
+ command += buf;
+ retval = vbm_popen(command, output, "create new snapshot", true, true, 0);
+ if (retval) return retval;
+
+ // Resume VM
+ resume();
+
+ // Set the suspended flag back to false before deleting the stale
+ // snapshot
+ poll(false);
+
+ // Delete stale snapshot(s), if one exists
+ retval = cleanup_snapshots(false);
+ if (retval) return retval;
+
+ fprintf(
+ stderr,
+ "%s Checkpoint completed.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+
+ return 0;
+}
+
+int VBOX_VM::cleanup_snapshots(bool delete_active) {
+ string command;
+ string output;
+ string snapshotlist;
+ string line;
+ string uuid;
+ size_t eol_pos;
+ size_t eol_prev_pos;
+ size_t uuid_start;
+ size_t uuid_end;
+ char buf[256];
+ int retval;
+
+
+ // Enumerate snapshot(s)
+ command = "snapshot \"" + vm_name + "\" ";
+ command += "list ";
+
+ // Only log the error if we are not attempting to deregister the VM.
+ // delete_active is only set to true when we are deregistering the VM.
+ retval = vbm_popen(command, snapshotlist, "enumerate snapshot(s)", !delete_active, false, 0);
+ if (retval) return retval;
+
+ // Output should look a little like this:
+ // Name: Snapshot 2 (UUID: 1751e9a6-49e7-4dcc-ab23-08428b665ddf)
+ // Name: Snapshot 3 (UUID: 92fa8b35-873a-4197-9d54-7b6b746b2c58)
+ // Name: Snapshot 4 (UUID: c049023a-5132-45d5-987d-a9cfadb09664) *
+ //
+ // Traverse the list from newest to oldest. Otherwise we end up with an error:
+ // VBoxManage.exe: error: Snapshot operation failed
+ // VBoxManage.exe: error: Hard disk 'C:\ProgramData\BOINC\slots\23\vm_image.vdi' has
+ // more than one child hard disk (2)
+ //
+
+ // Prepend a space and line feed to the output since we are going to traverse it backwards
+ snapshotlist = " \n" + snapshotlist;
+
+ eol_prev_pos = snapshotlist.rfind("\n");
+ eol_pos = snapshotlist.rfind("\n", eol_prev_pos - 1);
+ while (eol_pos != string::npos) {
+ line = snapshotlist.substr(eol_pos, eol_prev_pos - eol_pos);
+
+ // Find the previous line to use in the next iteration
+ eol_prev_pos = eol_pos;
+ eol_pos = snapshotlist.rfind("\n", eol_prev_pos - 1);
+
+ // This VM does not yet have any snapshots
+ if (line.find("does not have any snapshots") != string::npos) break;
+
+ // The * signifies that it is the active snapshot and one we do not want to delete
+ if (!delete_active && (line.rfind("*") != string::npos)) continue;
+
+ uuid_start = line.find("(UUID: ");
+ if (uuid_start != string::npos) {
+ // We can parse the virtual machine ID from the line
+ uuid_start += 7;
+ uuid_end = line.find(")", uuid_start);
+ uuid = line.substr(uuid_start, uuid_end - uuid_start);
+
+ fprintf(
+ stderr,
+ "%s Deleting stale snapshot.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+
+ // Delete stale snapshot, if one exists
+ command = "snapshot \"" + vm_name + "\" ";
+ command += "delete \"";
+ command += uuid;
+ command += "\" ";
+
+ // Only log the error if we are not attempting to deregister the VM.
+ // delete_active is only set to true when we are deregistering the VM.
+ retval = vbm_popen(command, output, "delete stale snapshot", !delete_active, false, 0);
+ if (retval) return retval;
+ }
+ }
+
+ return 0;
+}
+
+int VBOX_VM::restore_snapshot() {
+ string command;
+ string output;
+ char buf[256];
+ int retval = BOINC_SUCCESS;
+
+ fprintf(
+ stderr,
+ "%s Restore from previously saved snapshot.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+
+ command = "snapshot \"" + vm_name + "\" ";
+ command += "restorecurrent ";
+ retval = vbm_popen(command, output, "restore current snapshot", true, false, 0);
+ if (retval) return retval;
+
+ fprintf(
+ stderr,
+ "%s Restore completed.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+
+ return retval;
+}
+
+void VBOX_VM::dump_hypervisor_status_reports() {
+}
+
+int VBOX_VM::is_registered() {
+ string command;
+ string output;
+ string needle;
+ char buf[256];
+ int retval;
+
+ command = "showvminfo \"" + vm_master_name + "\" ";
+ command += "--machinereadable ";
+
+ // Look for this string in the output
+ //
+ needle = "name=\"" + vm_master_name + "\"";
+
+ retval = vbm_popen(command, output, "registration detection", false, false);
+
+ // Handle explicit cases first
+ if (ERR_TIMEOUT == retval) {
+ return ERR_TIMEOUT;
+ }
+ if (output.find("VBOX_E_OBJECT_NOT_FOUND") != string::npos) {
+ return ERR_NOT_FOUND;
+ }
+ if (!retval && output.find(needle.c_str()) != string::npos) {
+ return BOINC_SUCCESS;
+ }
+
+ // Something unexpected has happened. Dump diagnostic output.
+ fprintf(
+ stderr,
+ "%s Error in registration for VM: %d\nArguments:\n%s\nOutput:\n%s\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ retval,
+ command.c_str(),
+ output.c_str()
+ );
+
+ return retval;
+}
+
+// Attempt to detect any condition that would prevent VirtualBox from running a VM properly, like:
+// 1. The DCOM service not being started on Windows
+// 2. Vboxmanage not being able to communicate with vboxsvc for some reason
+// 3. VirtualBox driver not loaded for the current Linux kernel.
+//
+// Luckly both of the above conditions can be detected by attempting to detect the host information
+// via vboxmanage and it is cross platform.
+//
+bool VBOX_VM::is_system_ready(std::string& message) {
+ string command;
+ string output;
+ char buf[256];
+ int retval;
+ bool rc = false;
+
+ command = "list hostinfo ";
+ retval = vbm_popen(command, output, "host info");
+ if (BOINC_SUCCESS == retval) {
+ rc = true;
+ }
+
+ if (output.size() == 0) {
+ fprintf(
+ stderr,
+ "%s WARNING: Communication with VM Hypervisor failed. (Possibly Out of Memory).\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ message = "Communication with VM Hypervisor failed. (Possibly Out of Memory).";
+ rc = false;
+ }
+
+ if (output.find("Processor count:") == string::npos) {
+ fprintf(
+ stderr,
+ "%s WARNING: Communication with VM Hypervisor failed.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ message = "Communication with VM Hypervisor failed.";
+ rc = false;
+ }
+
+ if (output.find("WARNING: The vboxdrv kernel module is not loaded.") != string::npos) {
+ vboxwrapper_msg_prefix(buf, sizeof(buf));
+ fprintf(
+ stderr,
+ "%s WARNING: The vboxdrv kernel module is not loaded.\n"
+ "%s WARNING: Please update/recompile VirtualBox kernel drivers.\n",
+ buf,
+ buf
+ );
+ message = "Please update/recompile VirtualBox kernel drivers.";
+ rc = false;
+ }
+
+ return rc;
+}
+
+bool VBOX_VM::is_hdd_registered() {
+ string command;
+ string output;
+ string virtual_machine_root_dir;
+
+ get_slot_directory(virtual_machine_root_dir);
+
+ command = "showhdinfo \"" + virtual_machine_root_dir + "/" + image_filename + "\" ";
+
+ if (vbm_popen(command, output, "hdd registration", false, false) == 0) {
+ if ((output.find("VBOX_E_FILE_ERROR") == string::npos) &&
+ (output.find("VBOX_E_OBJECT_NOT_FOUND") == string::npos) &&
+ (output.find("does not match the value") == string::npos)
+ ) {
+ // Error message not found in text
+ return true;
+ }
+ }
+ return false;
+}
+
+bool VBOX_VM::is_extpack_installed() {
+ string command;
+ string output;
+
+ command = "list extpacks";
+
+ if (vbm_popen(command, output, "extpack detection", false, false) == 0) {
+ if ((output.find("Oracle VM VirtualBox Extension Pack") != string::npos) && (output.find("VBoxVRDP") != string::npos)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+int VBOX_VM::get_install_directory(string& install_directory ) {
+ install_directory = "";
+ return 0;
+}
+
+int VBOX_VM::get_version_information(string& version) {
+ string command;
+ string output;
+ int retval;
+
+ // Record the VirtualBox version information for later use.
+ command = "--version ";
+ retval = vbm_popen(command, output, "version check");
+
+ if (!retval) {
+ // Remove \r or \n from the output spew
+ string::iterator iter = output.begin();
+ while (iter != output.end()) {
+ if (*iter == '\r' || *iter == '\n') {
+ iter = output.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+ version = output;
+ }
+
+ return retval;
+}
+
+int VBOX_VM::get_guest_additions(string& guest_additions) {
+ string command;
+ string output;
+ size_t ga_start;
+ size_t ga_end;
+ int retval;
+
+ // Get the location of where the guest additions are
+ command = "list systemproperties";
+ retval = vbm_popen(command, output, "guest additions");
+
+ // Output should look like this:
+ // API version: 4_3
+ // Minimum guest RAM size: 4 Megabytes
+ // Maximum guest RAM size: 2097152 Megabytes
+ // Minimum video RAM size: 1 Megabytes
+ // Maximum video RAM size: 256 Megabytes
+ // ...
+ // Default Guest Additions ISO: C:\Program Files\Oracle\VirtualBox/VBoxGuestAdditions.iso
+ //
+
+ ga_start = output.find("Default Guest Additions ISO:");
+ if (ga_start == string::npos) {
+ return ERR_NOT_FOUND;
+ }
+ ga_start += strlen("Default Guest Additions ISO:");
+ ga_end = output.find("\n", ga_start);
+ guest_additions = output.substr(ga_start, ga_end - ga_start);
+ strip_whitespace(guest_additions);
+ if (guest_additions.size() <= 0) {
+ return ERR_NOT_FOUND;
+ }
+
+ return retval;
+}
+
+int VBOX_VM::get_default_network_interface(string& iface) {
+ string command;
+ string output;
+ size_t if_start;
+ size_t if_end;
+ int retval;
+
+ // Get the location of where the guest additions are
+ command = "list bridgedifs";
+ retval = vbm_popen(command, output, "default interface");
+
+ // Output should look like this:
+ // Name: Intel(R) Ethernet Connection I217-V
+ // GUID: 4b8796d6-a4ed-4752-8e8e-bf23984fd93c
+ // DHCP: Enabled
+ // IPAddress: 192.168.1.19
+ // NetworkMask: 255.255.255.0
+ // IPV6Address: fe80:0000:0000:0000:31c2:0053:4f50:4e64
+ // IPV6NetworkMaskPrefixLength: 64
+ // HardwareAddress: bc:5f:f4:ba:cc:16
+ // MediumType: Ethernet
+ // Status: Up
+ // VBoxNetworkName: HostInterfaceNetworking-Intel(R) Ethernet Connection I217-V
+
+ if_start = output.find("Name:");
+ if (if_start == string::npos) {
+ return ERR_NOT_FOUND;
+ }
+ if_start += strlen("Name:");
+ if_end = output.find("\n", if_start);
+ iface = output.substr(if_start, if_end - if_start);
+ strip_whitespace(iface);
+ if (iface.size() <= 0) {
+ return ERR_NOT_FOUND;
+ }
+
+ return retval;
+}
+
+int VBOX_VM::get_vm_network_bytes_sent(double& sent) {
+ string command;
+ string output;
+ string counter_value;
+ size_t counter_start;
+ size_t counter_end;
+ int retval;
+
+ command = "debugvm \"" + vm_name + "\" ";
+ command += "statistics --pattern \"/Devices/*/TransmitBytes\" ";
+
+ retval = vbm_popen(command, output, "get bytes sent");
+ if (retval) return retval;
+
+ // Output should look like this:
+ //
+ //
+ //
+ //
+ //
+
+ // 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);
+ }
+ return 0;
+}
+
+int VBOX_VM::get_vm_network_bytes_received(double& received) {
+ string command;
+ string output;
+ string counter_value;
+ size_t counter_start;
+ size_t counter_end;
+ int retval;
+
+ command = "debugvm \"" + vm_name + "\" ";
+ command += "statistics --pattern \"/Devices/*/ReceiveBytes\" ";
+
+ retval = vbm_popen(command, output, "get bytes received");
+ if (retval) return retval;
+
+ // Output should look like this:
+ //
+ //
+ //
+ //
+ //
+
+ // 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);
+ }
+
+ return 0;
+}
+
+int VBOX_VM::get_vm_process_id() {
+ string output;
+ string pid;
+ size_t pid_start;
+ size_t pid_end;
+
+ get_vm_log(output, false);
+
+ // Output should look like this:
+ // VirtualBox 4.1.0 r73009 win.amd64 (Jul 19 2011 13:05:53) release log
+ // 00:00:06.008 Log opened 2011-09-01T23:00:59.829170900Z
+ // 00:00:06.008 OS Product: Windows 7
+ // 00:00:06.009 OS Release: 6.1.7601
+ // 00:00:06.009 OS Service Pack: 1
+ // 00:00:06.015 Host RAM: 4094MB RAM, available: 876MB
+ // 00:00:06.015 Executable: C:\Program Files\Oracle\VirtualBox\VirtualBox.exe
+ // 00:00:06.015 Process ID: 6128
+ // 00:00:06.015 Package type: WINDOWS_64BITS_GENERIC
+ // 00:00:06.015 Installed Extension Packs:
+ // 00:00:06.015 None installed!
+ //
+ pid_start = output.find("Process ID: ");
+ if (pid_start == string::npos) {
+ return ERR_NOT_FOUND;
+ }
+ pid_start += strlen("Process ID: ");
+ pid_end = output.find("\n", pid_start);
+ pid = output.substr(pid_start, pid_end - pid_start);
+ strip_whitespace(pid);
+ if (pid.size() <= 0) {
+ return ERR_NOT_FOUND;
+ }
+
+ vm_pid = atol(pid.c_str());
+
+ return 0;
+}
+
+int VBOX_VM::get_vm_exit_code(unsigned long& exit_code) {
+ int ec = 0;
+ waitpid(vm_pid, &ec, WNOHANG);
+ exit_code = ec;
+ return 0;
+}
+
+// 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) {
+ string command;
+ string output;
+ char buf[256];
+ int retval;
+
+ network_suspended = !enabled;
+
+ if (enabled) {
+ fprintf(
+ stderr,
+ "%s Enabling network access for VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--cableconnected1 on ";
+
+ retval = vbm_popen(command, output, "enable network");
+ if (retval) return retval;
+ } else {
+ fprintf(
+ stderr,
+ "%s Disabling network access for VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--cableconnected1 off ";
+
+ retval = vbm_popen(command, output, "disable network");
+ if (retval) return retval;
+ }
+ return 0;
+}
+
+int VBOX_VM::set_cpu_usage(int percentage) {
+ string command;
+ string output;
+ char buf[256];
+ int retval;
+
+ // the arg to controlvm is percentage
+ //
+ fprintf(
+ stderr,
+ "%s Setting CPU throttle for VM. (%d%%)\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ percentage
+ );
+ sprintf(buf, "%d", percentage);
+ command = "controlvm \"" + vm_name + "\" ";
+ command += "cpuexecutioncap ";
+ command += buf;
+ command += " ";
+
+ retval = vbm_popen(command, output, "CPU throttle");
+ if (retval) return retval;
+ return 0;
+}
+
+int VBOX_VM::set_network_usage(int kilobytes) {
+ string command;
+ string output;
+ char buf[256];
+ int retval;
+
+ // the argument to modifyvm is in KB
+ //
+ if (kilobytes == 0) {
+ fprintf(
+ stderr,
+ "%s Setting network throttle for VM. (1024GB)\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ } else {
+ fprintf(
+ stderr,
+ "%s Setting network throttle for VM. (%dKB)\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ kilobytes
+ );
+ }
+
+ if (is_virtualbox_version_newer(4, 2, 0)) {
+
+ // Update bandwidth group limits
+ //
+ if (kilobytes == 0) {
+ command = "bandwidthctl \"" + vm_name + "\" ";
+ command += "set \"" + vm_name + "_net\" ";
+ command += "--limit 1024G ";
+
+ retval = vbm_popen(command, output, "network throttle (set default value)");
+ if (retval) return retval;
+ } else {
+ sprintf(buf, "%d", kilobytes);
+ command = "bandwidthctl \"" + vm_name + "\" ";
+ command += "set \"" + vm_name + "_net\" ";
+ command += "--limit ";
+ command += buf;
+ command += "K ";
+
+ retval = vbm_popen(command, output, "network throttle (set)");
+ if (retval) return retval;
+ }
+
+ } else {
+
+ sprintf(buf, "%d", kilobytes);
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--nicspeed1 ";
+ command += buf;
+ command += " ";
+
+ retval = vbm_popen(command, output, "network throttle");
+ if (retval) return retval;
+
+ }
+
+ return 0;
+}
+
+void VBOX_VM::lower_vm_process_priority() {
+ char buf[256];
+ if (vm_pid) {
+ setpriority(PRIO_PROCESS, vm_pid, PROCESS_MEDIUM_PRIORITY);
+ fprintf(
+ stderr,
+ "%s Lowering VM Process priority.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ }
+}
+
+void VBOX_VM::reset_vm_process_priority() {
+ char buf[256];
+ if (vm_pid) {
+ setpriority(PRIO_PROCESS, vm_pid, PROCESS_NORMAL_PRIORITY);
+ fprintf(
+ stderr,
+ "%s Restoring VM Process priority.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ }
+}
+
+// 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() {
+ return BOINC_SUCCESS;
+}
+
+// Launch the VM.
+int VBOX_VM::launch_vboxvm() {
+ char buf[256];
+ char cmdline[1024];
+ char* argv[5];
+ int argc;
+ std::string output;
+ int retval = ERR_EXEC;
+
+ // Construct the command line parameters
+ //
+ if (headless) {
+ argv[0] = const_cast("VboxHeadless.exe");
+ } else {
+ argv[0] = const_cast("VirtualBox.exe");
+ }
+ argv[1] = const_cast("--startvm");
+ argv[2] = const_cast(vm_name.c_str());
+ if (headless) {
+ argv[3] = const_cast("--vrde config");
+ } else {
+ argv[3] = const_cast("--no-startvm-errormsgbox");
+ }
+ argv[4] = NULL;
+ argc = 4;
+
+ strcpy(cmdline, "");
+ for (int i=0; i= 5) break;
+
+ retry_count++;
+ boinc_sleep(sleep_interval);
+ }
+ }
+ while (retval);
+
+ // Add all relivent notes to the output string and log errors
+ //
+ if (retval && log_error) {
+ if (!retry_notes.empty()) {
+ output += "\nNotes:\n\n" + retry_notes;
+ }
+
+ fprintf(
+ stderr,
+ "%s Error in %s for VM: %d\nCommand:\n%s\nOutput:\n%s\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ item,
+ retval,
+ command.c_str(),
+ output.c_str()
+ );
+ }
+
+ return retval;
+}
+
+// Execute the vbox manage application and copy the output to the buffer.
+//
+int VBOX_VM::vbm_popen_raw(string& command, string& output, unsigned int timeout) {
+ char buf[256];
+ FILE* fp;
+ size_t errcode_start;
+ size_t errcode_end;
+ string errcode;
+ int retval = BOINC_SUCCESS;
+
+ // Launch vboxsvc in case it was shutdown for being idle
+ launch_vboxsvc();
+
+ // Reset output buffer
+ output.clear();
+
+ // redirect stderr to stdout for the child process so we can trap it with popen.
+ string modified_command = command + " 2>&1";
+
+ // Execute command
+ fp = popen(modified_command.c_str(), "r");
+ if (fp == NULL){
+ fprintf(
+ stderr,
+ "%s vbm_popen popen failed! errno = %d\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ errno
+ );
+ retval = ERR_FOPEN;
+ } else {
+ // Copy output to buffer
+ while (fgets(buf, 256, fp)) {
+ output += buf;
+ }
+
+ // Close stream
+ pclose(fp);
+
+ // Determine the real error code by parsing the output
+ errcode_start = output.find("(0x");
+ if (errcode_start != string::npos) {
+ errcode_start += 1;
+ errcode_end = output.find(")", errcode_start);
+ errcode = output.substr(errcode_start, errcode_end - errcode_start);
+
+ sscanf(errcode.c_str(), "%x", &retval);
+ }
+ }
+
+ // Is this a RPC_S_SERVER_UNAVAILABLE returned by vboxmanage?
+ if (output.find("RPC_S_SERVER_UNAVAILABLE") != string::npos) {
+ retval = RPC_S_SERVER_UNAVAILABLE;
+ }
+
+ return retval;
+}
+
+void VBOX_VM::vbm_replay(std::string& command) {
+ FILE* f = fopen(REPLAYLOG_FILENAME, "a");
+ if (f) {
+ fprintf(f, "%s\n", command.c_str());
+ fclose(f);
+ }
+}
+
+void VBOX_VM::vbm_trace(std::string& command, std::string& output, int retval) {
+ vbm_replay(command);
+
+ char buf[256];
+ FILE* f = fopen(TRACELOG_FILENAME, "a");
+ if (f) {
+ fprintf(
+ f,
+ "%s\nCommand: %s\nExit Code: %d\nOutput:\n%s\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ command.c_str(),
+ retval,
+ output.c_str()
+ );
+ fclose(f);
+ }
+}
+
diff --git a/samples/vboxwrapper/vbox_win.cpp b/samples/vboxwrapper/vbox_win.cpp
new file mode 100644
index 0000000000..b804b0937b
--- /dev/null
+++ b/samples/vboxwrapper/vbox_win.cpp
@@ -0,0 +1,2634 @@
+// 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 .
+
+#include "boinc_win.h"
+#include "win_util.h"
+
+using std::string;
+
+#if defined(_MSC_VER)
+#define getcwd _getcwd
+#define stricmp _stricmp
+#endif
+
+#include "diagnostics.h"
+#include "filesys.h"
+#include "parse.h"
+#include "str_util.h"
+#include "str_replace.h"
+#include "util.h"
+#include "error_numbers.h"
+#include "procinfo.h"
+#include "network.h"
+#include "boinc_api.h"
+#include "mscom/VirtualBox.h"
+#include "floppyio.h"
+#include "vboxwrapper.h"
+#include "vbox.h"
+
+
+static IVirtualBox* g_pVirtualBox = NULL;
+static ISession* g_pSession = NULL;
+static IMachine* g_pMachine = NULL;
+
+
+//
+// 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.
+//
+void virtualbox_dump_error() {
+ HRESULT rc;
+ BSTR strDescription = NULL;
+ char buf[256];
+ IErrorInfo* pErrorInfo;
+
+ rc = GetErrorInfo(0, &pErrorInfo);
+
+ if (FAILED(rc)) {
+ fprintf(
+ stderr,
+ "%s Error: getting error info! rc = 0x%x\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ rc
+ );
+ } else {
+ rc = pErrorInfo->GetDescription(&strDescription);
+ if (FAILED(rc) || !strDescription) {
+ fprintf(
+ stderr,
+ "%s Error: getting error description! rc = 0x%x\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ rc
+ );
+ } else {
+ fprintf(
+ stderr,
+ "%s Error description: %S\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ strDescription
+ );
+ SysFreeString(strDescription);
+ }
+ pErrorInfo->Release();
+ }
+}
+
+int VBOX_VM::initialize() {
+ int rc = BOINC_SUCCESS;
+ string old_path;
+ string new_path;
+ string command;
+ string output;
+ APP_INIT_DATA aid;
+ bool force_sandbox = false;
+ char buf[256];
+
+ boinc_get_init_data_p(&aid);
+ get_install_directory(virtualbox_install_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(new_path.c_str()))) {
+ fprintf(
+ stderr,
+ "%s Failed to modify the search path.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ }
+ }
+
+ // 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(virtualbox_home_directory.c_str()))) {
+ fprintf(
+ stderr,
+ "%s Failed to modify the search path.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ }
+ }
+
+ // Initialize the COM subsystem.
+ CoInitialize(NULL);
+
+ // Instantiate the VirtualBox root object.
+ rc = CoCreateInstance(CLSID_VirtualBox, /* the VirtualBox base object */
+ NULL, /* no aggregation */
+ CLSCTX_LOCAL_SERVER, /* the object lives in a server process on this machine */
+ IID_IVirtualBox, /* IID of the interface */
+ (void**)&g_pVirtualBox);
+ if (!SUCCEEDED(rc))
+ {
+ fprintf(
+ stderr,
+ "%s Error creating VirtualBox instance! rc = 0x%x\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ rc
+ );
+ return rc;
+ }
+
+ // Create the session object.
+ rc = CoCreateInstance(CLSID_Session, /* the Session base object */
+ NULL, /* no aggregation */
+ CLSCTX_INPROC_SERVER, /* the object lives in the current process on this machine */
+ IID_ISession, /* IID of the interface */
+ (void**)&g_pSession);
+ if (!SUCCEEDED(rc))
+ {
+ fprintf(
+ stderr,
+ "%s Error creating Session instance! rc = 0x%x\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ rc
+ );
+ return rc;
+ }
+
+ rc = get_version_information(virtualbox_version);
+ if (rc) return rc;
+
+ rc = get_guest_additions(virtualbox_guest_additions);
+ if (rc) return rc;
+
+ return rc;
+}
+
+void VBOX_VM::poll(bool log_state) {
+ char buf[256];
+ APP_INIT_DATA aid;
+ string command;
+ string output;
+ string::iterator iter;
+ string vmstate;
+ static string vmstate_old = "poweroff";
+ size_t vmstate_start;
+ size_t vmstate_end;
+
+ boinc_get_init_data_p(&aid);
+
+ //
+ // Is our environment still sane?
+ //
+ if (aid.using_sandbox && vboxsvc_pid_handle && !process_exists(vboxsvc_pid_handle)) {
+ fprintf(
+ stderr,
+ "%s Status Report: vboxsvc.exe is no longer running.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ }
+ if (vm_pid_handle && !process_exists(vm_pid_handle)) {
+ fprintf(
+ stderr,
+ "%s Status Report: virtualbox.exe/vboxheadless.exe is no longer running.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ }
+
+ //
+ // What state is the VM in?
+ //
+
+ command = "showvminfo \"" + vm_name + "\" ";
+ command += "--machinereadable ";
+
+ if (vbm_popen(command, output, "VM state", false, false, 45, false) == 0) {
+ vmstate_start = output.find("VMState=\"");
+ if (vmstate_start != string::npos) {
+ vmstate_start += 9;
+ vmstate_end = output.find("\"", vmstate_start);
+ vmstate = output.substr(vmstate_start, vmstate_end - vmstate_start);
+
+ // 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 == "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 == "deletingsnapshotlive") {
+ online = true;
+ saving = false;
+ restoring = false;
+ suspended = false;
+ crashed = false;
+ } else if (vmstate == "deletingsnapshotlivepaused") {
+ 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) {
+ fprintf(
+ stderr,
+ "%s VM is no longer is a running state. It is in '%s'.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ vmstate.c_str()
+ );
+ }
+ }
+ if (log_state && (vmstate_old != vmstate)) {
+ fprintf(
+ stderr,
+ "%s VM state change detected. (old = '%s', new = '%s')\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ vmstate_old.c_str(),
+ vmstate.c_str()
+ );
+ vmstate_old = vmstate;
+ }
+ }
+ }
+
+ //
+ // 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();
+}
+
+int VBOX_VM::create_vm() {
+ string command;
+ string output;
+ string virtual_machine_slot_directory;
+ string default_interface;
+ APP_INIT_DATA aid;
+ bool disable_acceleration = false;
+ char buf[256];
+ int retval;
+
+ boinc_get_init_data_p(&aid);
+ get_slot_directory(virtual_machine_slot_directory);
+
+
+ // Reset VM name in case it was changed while deregistering a stale VM
+ //
+ vm_name = vm_master_name;
+
+
+ fprintf(
+ stderr,
+ "%s Create VM. (%s, slot#%d) \n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ vm_name.c_str(),
+ aid.slot
+ );
+
+ // Fixup chipset and drive controller information for known configurations
+ //
+ if (enable_isocontextualization) {
+ if ("PIIX4" == vm_disk_controller_model) {
+ fprintf(
+ stderr,
+ "%s Updating drive controller type and model for desired configuration.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ vm_disk_controller_type = "sata";
+ vm_disk_controller_model = "IntelAHCI";
+ }
+ }
+
+ // Create and register the VM
+ //
+ command = "createvm ";
+ command += "--name \"" + vm_name + "\" ";
+ command += "--basefolder \"" + virtual_machine_slot_directory + "\" ";
+ command += "--ostype \"" + os_name + "\" ";
+ command += "--register";
+
+ retval = vbm_popen(command, output, "create");
+ if (retval) return retval;
+
+ // Tweak the VM's Description
+ //
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--description \"" + vm_master_description + "\" ";
+
+ vbm_popen(command, output, "modifydescription", false, false);
+
+ // Tweak the VM's CPU Count
+ //
+ fprintf(
+ stderr,
+ "%s Setting CPU Count for VM. (%s)\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ vm_cpu_count.c_str()
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--cpus " + vm_cpu_count + " ";
+
+ retval = vbm_popen(command, output, "modifycpu");
+ if (retval) return retval;
+
+ // Tweak the VM's Memory Size
+ //
+ fprintf(
+ stderr,
+ "%s Setting Memory Size for VM. (%sMB)\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ vm_memory_size_mb.c_str()
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--memory " + vm_memory_size_mb + " ";
+
+ retval = vbm_popen(command, output, "modifymem");
+ if (retval) return retval;
+
+ // Tweak the VM's Chipset Options
+ //
+ fprintf(
+ stderr,
+ "%s Setting Chipset Options for VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--acpi on ";
+ command += "--ioapic on ";
+
+ retval = vbm_popen(command, output, "modifychipset");
+ if (retval) return retval;
+
+ // Tweak the VM's Boot Options
+ //
+ fprintf(
+ stderr,
+ "%s Setting Boot Options for VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--boot1 disk ";
+ command += "--boot2 dvd ";
+ command += "--boot3 none ";
+ command += "--boot4 none ";
+
+ retval = vbm_popen(command, output, "modifyboot");
+ if (retval) return retval;
+
+ // Tweak the VM's Network Configuration
+ //
+ if (network_bridged_mode) {
+ fprintf(
+ stderr,
+ "%s Setting Network Configuration for Bridged Mode.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--nic1 bridged ";
+ command += "--cableconnected1 off ";
+
+ retval = vbm_popen(command, output, "set bridged mode");
+ if (retval) return retval;
+
+ get_default_network_interface(default_interface);
+ fprintf(
+ stderr,
+ "%s Setting Bridged Interface. (%s)\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ default_interface.c_str()
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--bridgeadapter1 \"";
+ command += default_interface;
+ command += "\" ";
+
+ retval = vbm_popen(command, output, "set bridged interface");
+ if (retval) return retval;
+ } else {
+ fprintf(
+ stderr,
+ "%s Setting Network Configuration for NAT.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--nic1 nat ";
+ command += "--natdnsproxy1 on ";
+ command += "--cableconnected1 off ";
+
+ retval = vbm_popen(command, output, "modifynetwork");
+ if (retval) return retval;
+ }
+
+ // Tweak the VM's USB Configuration
+ //
+ fprintf(
+ stderr,
+ "%s Disabling USB Support for VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--usb off ";
+
+ vbm_popen(command, output, "modifyusb", false, false);
+
+ // Tweak the VM's COM Port Support
+ //
+ fprintf(
+ stderr,
+ "%s Disabling COM Port Support for VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--uart1 off ";
+ command += "--uart2 off ";
+
+ vbm_popen(command, output, "modifycom", false, false);
+
+ // Tweak the VM's LPT Port Support
+ //
+ fprintf(
+ stderr,
+ "%s Disabling LPT Port Support for VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--lpt1 off ";
+ command += "--lpt2 off ";
+
+ vbm_popen(command, output, "modifylpt", false, false);
+
+ // Tweak the VM's Audio Support
+ //
+ fprintf(
+ stderr,
+ "%s Disabling Audio Support for VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--audio none ";
+
+ vbm_popen(command, output, "modifyaudio", false, false);
+
+ // Tweak the VM's Clipboard Support
+ //
+ fprintf(
+ stderr,
+ "%s Disabling Clipboard Support for VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--clipboard disabled ";
+
+ vbm_popen(command, output, "modifyclipboard", false, false);
+
+ // Tweak the VM's Drag & Drop Support
+ //
+ fprintf(
+ stderr,
+ "%s Disabling Drag and Drop Support for VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--draganddrop disabled ";
+
+ vbm_popen(command, output, "modifydragdrop", false, false);
+
+ // 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")) {
+ fprintf(
+ stderr,
+ "%s Hardware acceleration CPU extensions not detected. Disabling VirtualBox hardware acceleration support.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ disable_acceleration = true;
+ }
+ if (strstr(aid.host_info.p_features, "hypervisor")) {
+ fprintf(
+ stderr,
+ "%s Running under Hypervisor. Disabling VirtualBox hardware acceleration support.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ disable_acceleration = true;
+ }
+ if (is_boinc_client_version_newer(aid, 7, 2, 16)) {
+ if (aid.vm_extensions_disabled) {
+ fprintf(
+ stderr,
+ "%s Hardware acceleration failed with previous execution. Disabling VirtualBox hardware acceleration support.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ 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.
+ vboxwrapper_msg_prefix(buf, sizeof(buf));
+ fprintf(
+ stderr,
+ "%s Legacy fallback configuration detected. Disabling VirtualBox hardware acceleration support.\n"
+ "%s NOTE: Upgrading to BOINC 7.2.16 or better may re-enable hardware acceleration.\n",
+ buf,
+ buf
+ );
+ 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) {
+ fprintf(
+ stderr,
+ "%s Disabling hardware acceleration support for virtualization.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--hwvirtex off ";
+
+ retval = vbm_popen(command, output, "VT-x/AMD-V support");
+ if (retval) return retval;
+ }
+ } else if (os_name.find("_64") != std::string::npos) {
+ if (disable_acceleration) {
+ fprintf(
+ stderr,
+ "%s ERROR: Invalid configuration. VM type requires acceleration but the current configuration cannot support it.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ return ERR_INVALID_PARAM;
+ }
+ }
+
+ // Add storage controller to VM
+ // See: http://www.virtualbox.org/manual/ch08.html#vboxmanage-storagectl
+ // See: http://www.virtualbox.org/manual/ch05.html#iocaching
+ //
+ fprintf(
+ stderr,
+ "%s Adding storage controller to VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "storagectl \"" + vm_name + "\" ";
+ command += "--name \"Hard Disk Controller\" ";
+ command += "--add \"" + vm_disk_controller_type + "\" ";
+ command += "--controller \"" + vm_disk_controller_model + "\" ";
+ if (
+ (vm_disk_controller_type == "sata") || (vm_disk_controller_type == "SATA") ||
+ (vm_disk_controller_type == "scsi") || (vm_disk_controller_type == "SCSI") ||
+ (vm_disk_controller_type == "sas") || (vm_disk_controller_type == "SAS")
+ ) {
+ command += "--hostiocache off ";
+ }
+ if ((vm_disk_controller_type == "sata") || (vm_disk_controller_type == "SATA")) {
+ if (is_virtualbox_version_newer(4, 3, 0)) {
+ command += "--portcount 3";
+ } else {
+ command += "--sataportcount 3";
+ }
+ }
+
+ retval = vbm_popen(command, output, "add storage controller (fixed disk)");
+ if (retval) return retval;
+
+ // Add storage controller for a floppy device if desired
+ //
+ if (enable_floppyio) {
+ command = "storagectl \"" + vm_name + "\" ";
+ command += "--name \"Floppy Controller\" ";
+ command += "--add floppy ";
+
+ retval = vbm_popen(command, output, "add storage controller (floppy)");
+ if (retval) return retval;
+ }
+
+ if (enable_isocontextualization) {
+
+ // Add virtual ISO 9660 disk drive to VM
+ //
+ fprintf(
+ stderr,
+ "%s Adding virtual ISO 9660 disk drive to VM. (%s)\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ iso_image_filename.c_str()
+ );
+ command = "storageattach \"" + vm_name + "\" ";
+ command += "--storagectl \"Hard Disk Controller\" ";
+ command += "--port 0 ";
+ command += "--device 0 ";
+ command += "--type dvddrive ";
+ command += "--medium \"" + virtual_machine_slot_directory + "/" + iso_image_filename + "\" ";
+
+ retval = vbm_popen(command, output, "storage attach (ISO 9660 image)");
+ if (retval) return retval;
+
+ // Add guest additions to the VM
+ //
+ fprintf(
+ stderr,
+ "%s Adding VirtualBox Guest Additions to VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "storageattach \"" + vm_name + "\" ";
+ command += "--storagectl \"Hard Disk Controller\" ";
+ command += "--port 2 ";
+ command += "--device 0 ";
+ command += "--type dvddrive ";
+ command += "--medium \"" + virtualbox_guest_additions + "\" ";
+
+ retval = vbm_popen(command, output, "storage attach (guest additions image)");
+ if (retval) return retval;
+
+ // Add a virtual cache disk drive to VM
+ //
+ if (enable_cache_disk){
+ fprintf(
+ stderr,
+ "%s Adding virtual cache disk drive to VM. (%s)\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ cache_disk_filename.c_str()
+ );
+ command = "storageattach \"" + vm_name + "\" ";
+ command += "--storagectl \"Hard Disk Controller\" ";
+ command += "--port 1 ";
+ command += "--device 0 ";
+ command += "--type hdd ";
+ command += "--setuuid \"\" ";
+ command += "--medium \"" + virtual_machine_slot_directory + "/" + cache_disk_filename + "\" ";
+
+ retval = vbm_popen(command, output, "storage attach (cached disk)");
+ if (retval) return retval;
+ }
+
+ } else {
+
+ // Adding virtual hard drive to VM
+ //
+ fprintf(
+ stderr,
+ "%s Adding virtual disk drive to VM. (%s)\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ image_filename.c_str()
+ );
+ command = "storageattach \"" + vm_name + "\" ";
+ command += "--storagectl \"Hard Disk Controller\" ";
+ command += "--port 0 ";
+ command += "--device 0 ";
+ command += "--type hdd ";
+ command += "--setuuid \"\" ";
+ command += "--medium \"" + virtual_machine_slot_directory + "/" + image_filename + "\" ";
+
+ retval = vbm_popen(command, output, "storage attach (fixed disk)");
+ if (retval) return retval;
+
+ // Add guest additions to the VM
+ //
+ fprintf(
+ stderr,
+ "%s Adding VirtualBox Guest Additions to VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "storageattach \"" + vm_name + "\" ";
+ command += "--storagectl \"Hard Disk Controller\" ";
+ command += "--port 1 ";
+ command += "--device 0 ";
+ command += "--type dvddrive ";
+ command += "--medium \"" + virtualbox_guest_additions + "\" ";
+
+ retval = vbm_popen(command, output, "storage attach (guest additions image)");
+ if (retval) return retval;
+
+ }
+
+
+ // Add network bandwidth throttle group
+ //
+ if (is_virtualbox_version_newer(4, 2, 0)) {
+ fprintf(
+ stderr,
+ "%s Adding network bandwidth throttle group to VM. (Defaulting to 1024GB)\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "bandwidthctl \"" + vm_name + "\" ";
+ command += "add \"" + vm_name + "_net\" ";
+ command += "--type network ";
+ command += "--limit 1024G";
+ command += " ";
+
+ retval = vbm_popen(command, output, "network throttle group (add)");
+ if (retval) return retval;
+ }
+
+ // 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 FloppyIO(floppy_image_filename.c_str());
+ if (!pFloppy->ready()) {
+ vboxwrapper_msg_prefix(buf, sizeof(buf));
+ fprintf(
+ stderr,
+ "%s Creating virtual floppy image failed.\n"
+ "%s Error Code '%d' Error Message '%s'\n",
+ buf,
+ buf,
+ pFloppy->error,
+ pFloppy->errorStr.c_str()
+ );
+ return ERR_FWRITE;
+ }
+
+ fprintf(
+ stderr,
+ "%s Adding virtual floppy disk drive to VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "storageattach \"" + vm_name + "\" ";
+ command += "--storagectl \"Floppy Controller\" ";
+ command += "--port 0 ";
+ command += "--device 0 ";
+ command += "--medium \"" + virtual_machine_slot_directory + "/" + floppy_image_filename + "\" ";
+
+ retval = vbm_popen(command, output, "storage attach (floppy disk)");
+ if (retval) return retval;
+
+ }
+
+ // Enable the network adapter if a network connection is required.
+ //
+ if (enable_network) {
+ set_network_access(true);
+
+ // set up port forwarding
+ //
+ if (pf_guest_port) {
+ 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; iFindMachine(vm_name, &pMachine);
+ if (SUCCEEDED(rc)) {
+
+ // Start a VM session
+ rc = pMachine->LaunchVMProcess(g_pSession, session_type, NULL, &pProgress);
+ if (FAILED(rc)) {
+ fprintf(
+ stderr,
+ "%s Error could not launch VM process! rc = 0x%x\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ rc
+ );
+ virtualbox_dump_error();
+ retval = rc;
+ goto CLEANUP;
+ }
+
+ // Wait until VM is running.
+ rc = pProgress->WaitForCompletion(-1);
+ if (FAILED(rc)) {
+ fprintf(
+ stderr,
+ "%s Error could not wait for VM start completion! rc = 0x%x\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ rc
+ );
+ virtualbox_dump_error();
+ retval = rc;
+ goto CLEANUP;
+ }
+
+ pProgress->get_Completed(&bCompleted);
+ if (bCompleted)
+
+ if (BOINC_SUCCESS == retval) {
+ fprintf(
+ stderr,
+ "%s Successfully started VM. (PID = '%d')\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ vm_pid
+ );
+ } else {
+ fprintf(
+ stderr,
+ "%s VM failed to start.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ }
+
+ }
+
+CLEANUP:
+ if (session_type) SysFreeString(session_type);
+
+ if (pProgress) {
+ pProgress->Release();
+ pProgress = NULL;
+ }
+
+ if (pMachine) {
+ pMachine->Release();
+ pMachine = NULL;
+ }
+
+ return retval;
+}
+
+int VBOX_VM::start() {
+ char buf[256];
+ int retval;
+
+ fprintf(
+ stderr,
+ "%s Starting VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+
+#ifdef NEW_EXECUTION_PATH
+ retval = launch_vboxvm();
+#else
+ string command;
+ string output;
+ int timeout = 0;
+
+ command = "startvm \"" + vm_name + "\"";
+ if (headless) {
+ command += " --type headless";
+ }
+ retval = vbm_popen(command, output, "start VM", true, false, 0);
+
+ // Get the VM pid as soon as possible
+ while (!retval) {
+ boinc_sleep(1.0);
+ timeout += 1;
+
+ get_vm_process_id();
+
+ if (vm_pid) break;
+
+ if (timeout > 45) {
+ retval = ERR_TIMEOUT;
+ break;
+ }
+ }
+#endif
+
+ if (BOINC_SUCCESS == retval) {
+ fprintf(
+ stderr,
+ "%s Successfully started VM. (PID = '%d')\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ vm_pid
+ );
+ } else {
+ fprintf(
+ stderr,
+ "%s VM failed to start.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ }
+
+ return retval;
+}
+
+int VBOX_VM::stop() {
+ int retval = 0;
+ HRESULT rc;
+ char buf[256];
+ double timeout;
+ IConsole* pConsole = NULL;
+ IProgress* pProgress = NULL;
+
+
+ fprintf(
+ stderr,
+ "%s Stopping VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+
+
+ // Get console object.
+ rc = g_pSession->get_Console(&pConsole);
+ if (FAILED(rc)) {
+ fprintf(
+ stderr,
+ "%s Error retrieving console object! rc = 0x%x\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ rc
+ );
+ virtualbox_dump_error();
+ retval = rc;
+ goto CLEANUP;
+ }
+
+ // Save the state of the machine.
+ rc = pConsole->SaveState(&pProgress);
+ if (FAILED(rc)) {
+ fprintf(
+ stderr,
+ "%s Error could not save the state of the VM! rc = 0x%x\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ rc
+ );
+ virtualbox_dump_error();
+ retval = rc;
+ goto CLEANUP;
+ }
+
+ // Wait until VM is powered down.
+ rc = pProgress->WaitForCompletion(-1);
+ if (FAILED(rc)) {
+ fprintf(
+ stderr,
+ "%s Error could not wait for VM save state completion! rc = 0x%x\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ rc
+ );
+ virtualbox_dump_error();
+ retval = 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.
+ if (!retval) {
+ timeout = dtime() + 300;
+ do {
+ poll(false);
+ if (!online && !saving) break;
+ boinc_sleep(1.0);
+ } while (timeout >= dtime());
+ }
+
+ if (!online) {
+ fprintf(
+ stderr,
+ "%s Successfully stopped VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ retval = BOINC_SUCCESS;
+ } else {
+ fprintf(
+ stderr,
+ "%s VM did not stop when requested.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+
+ // Attempt to terminate the VM
+ retval = kill_program(vm_pid);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s VM was NOT successfully terminated.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ } else {
+ fprintf(
+ stderr,
+ "%s VM was successfully terminated.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ }
+ }
+
+CLEANUP:
+ if (pProgress) {
+ pProgress->Release();
+ pProgress = NULL;
+ }
+ if (pConsole) {
+ pConsole->Release();
+ pConsole = NULL;
+ }
+ if (g_pSession) {
+ g_pSession->UnlockMachine();
+ }
+ return retval;
+}
+
+int VBOX_VM::poweroff() {
+ int retval = 0;
+ HRESULT rc;
+ char buf[256];
+ double timeout;
+ IConsole* pConsole = NULL;
+ IProgress* pProgress = NULL;
+
+
+ fprintf(
+ stderr,
+ "%s Powering off VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+
+ if (online) {
+ // Get console object.
+ rc = g_pSession->get_Console(&pConsole);
+ if (FAILED(rc)) {
+ fprintf(
+ stderr,
+ "%s Error retrieving console object! rc = 0x%x\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ rc
+ );
+ virtualbox_dump_error();
+ retval = rc;
+ goto CLEANUP;
+ }
+
+ // Power down the VM as quickly as possible.
+ rc = pConsole->PowerDown(&pProgress);
+ if (FAILED(rc)) {
+ fprintf(
+ stderr,
+ "%s Error could not save the state of the VM! rc = 0x%x\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ rc
+ );
+ virtualbox_dump_error();
+ retval = rc;
+ goto CLEANUP;
+ }
+
+ // Wait until VM is powered down.
+ rc = pProgress->WaitForCompletion(-1);
+ if (FAILED(rc)) {
+ fprintf(
+ stderr,
+ "%s Error could not wait for VM save state completion! rc = 0x%x\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ rc
+ );
+ virtualbox_dump_error();
+ retval = 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.
+ if (!retval) {
+ timeout = dtime() + 300;
+ do {
+ poll(false);
+ if (!online && !saving) break;
+ boinc_sleep(1.0);
+ } while (timeout >= dtime());
+ }
+
+ if (!online) {
+ fprintf(
+ stderr,
+ "%s Successfully stopped VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ retval = BOINC_SUCCESS;
+ } else {
+ fprintf(
+ stderr,
+ "%s VM did not stop when requested.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+
+ // Attempt to terminate the VM
+ retval = kill_program(vm_pid);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s VM was NOT successfully terminated.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ } else {
+ fprintf(
+ stderr,
+ "%s VM was successfully terminated.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ }
+ }
+ }
+
+CLEANUP:
+ if (pProgress) {
+ pProgress->Release();
+ pProgress = NULL;
+ }
+ if (pConsole) {
+ pConsole->Release();
+ pConsole = NULL;
+ }
+ if (g_pSession) {
+ g_pSession->UnlockMachine();
+ }
+ return retval;
+}
+
+int VBOX_VM::pause() {
+ int retval = 0;
+ HRESULT rc;
+ char buf[256];
+ IConsole* pConsole = NULL;
+
+
+ // 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 = g_pSession->get_Console(&pConsole);
+ if (FAILED(rc))
+ {
+ fprintf(
+ stderr,
+ "%s Error retrieving console object! rc = 0x%x\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ rc
+ );
+ virtualbox_dump_error();
+ retval = rc;
+ goto CLEANUP;
+ }
+
+ // Pause the machine.
+ rc = pConsole->Pause();
+ if (FAILED(rc))
+ {
+ fprintf(
+ stderr,
+ "%s Error could not pause VM! rc = 0x%x\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ rc
+ );
+ virtualbox_dump_error();
+ retval = rc;
+ goto CLEANUP;
+ }
+
+CLEANUP:
+ if (pConsole) {
+ pConsole->Release();
+ pConsole = NULL;
+ }
+ return retval;
+}
+
+int VBOX_VM::resume() {
+ int retval = 0;
+ HRESULT rc;
+ char buf[256];
+ IConsole* pConsole = NULL;
+
+
+ // Set the process priority back to the lowest level before resuming
+ // execution
+ //
+ lower_vm_process_priority();
+
+
+ // Get console object.
+ rc = g_pSession->get_Console(&pConsole);
+ if (FAILED(rc)) {
+ fprintf(
+ stderr,
+ "%s Error retrieving console object! rc = 0x%x\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ rc
+ );
+ virtualbox_dump_error();
+ retval = rc;
+ goto CLEANUP;
+ }
+
+ // Resume the machine.
+ rc = pConsole->Resume();
+ if (FAILED(rc)) {
+ fprintf(
+ stderr,
+ "%s Error could not resume VM! rc = 0x%x\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ rc
+ );
+ virtualbox_dump_error();
+ retval = rc;
+ goto CLEANUP;
+ }
+
+CLEANUP:
+ if (pConsole) {
+ pConsole->Release();
+ pConsole = NULL;
+ }
+ return retval;
+}
+
+
+int VBOX_VM::create_snapshot(double elapsed_time) {
+ string command;
+ string output;
+ char buf[256];
+ int retval;
+
+ fprintf(
+ stderr,
+ "%s Creating new snapshot for VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+
+ // Pause VM - Try and avoid the live snapshot and trigger an online
+ // snapshot instead.
+ pause();
+
+ // Create new snapshot
+ sprintf(buf, "%d", (int)elapsed_time);
+ command = "snapshot \"" + vm_name + "\" ";
+ command += "take boinc_";
+ command += buf;
+ retval = vbm_popen(command, output, "create new snapshot", true, true, 0);
+ if (retval) return retval;
+
+ // Resume VM
+ resume();
+
+ // Set the suspended flag back to false before deleting the stale
+ // snapshot
+ poll(false);
+
+ // Delete stale snapshot(s), if one exists
+ retval = cleanup_snapshots(false);
+ if (retval) return retval;
+
+ fprintf(
+ stderr,
+ "%s Checkpoint completed.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+
+ return 0;
+}
+
+int VBOX_VM::cleanup_snapshots(bool delete_active) {
+ string command;
+ string output;
+ string snapshotlist;
+ string line;
+ string uuid;
+ size_t eol_pos;
+ size_t eol_prev_pos;
+ size_t uuid_start;
+ size_t uuid_end;
+ char buf[256];
+ int retval;
+
+
+ // Enumerate snapshot(s)
+ command = "snapshot \"" + vm_name + "\" ";
+ command += "list ";
+
+ // Only log the error if we are not attempting to deregister the VM.
+ // delete_active is only set to true when we are deregistering the VM.
+ retval = vbm_popen(command, snapshotlist, "enumerate snapshot(s)", !delete_active, false, 0);
+ if (retval) return retval;
+
+ // Output should look a little like this:
+ // Name: Snapshot 2 (UUID: 1751e9a6-49e7-4dcc-ab23-08428b665ddf)
+ // Name: Snapshot 3 (UUID: 92fa8b35-873a-4197-9d54-7b6b746b2c58)
+ // Name: Snapshot 4 (UUID: c049023a-5132-45d5-987d-a9cfadb09664) *
+ //
+ // Traverse the list from newest to oldest. Otherwise we end up with an error:
+ // VBoxManage.exe: error: Snapshot operation failed
+ // VBoxManage.exe: error: Hard disk 'C:\ProgramData\BOINC\slots\23\vm_image.vdi' has
+ // more than one child hard disk (2)
+ //
+
+ // Prepend a space and line feed to the output since we are going to traverse it backwards
+ snapshotlist = " \n" + snapshotlist;
+
+ eol_prev_pos = snapshotlist.rfind("\n");
+ eol_pos = snapshotlist.rfind("\n", eol_prev_pos - 1);
+ while (eol_pos != string::npos) {
+ line = snapshotlist.substr(eol_pos, eol_prev_pos - eol_pos);
+
+ // Find the previous line to use in the next iteration
+ eol_prev_pos = eol_pos;
+ eol_pos = snapshotlist.rfind("\n", eol_prev_pos - 1);
+
+ // This VM does not yet have any snapshots
+ if (line.find("does not have any snapshots") != string::npos) break;
+
+ // The * signifies that it is the active snapshot and one we do not want to delete
+ if (!delete_active && (line.rfind("*") != string::npos)) continue;
+
+ uuid_start = line.find("(UUID: ");
+ if (uuid_start != string::npos) {
+ // We can parse the virtual machine ID from the line
+ uuid_start += 7;
+ uuid_end = line.find(")", uuid_start);
+ uuid = line.substr(uuid_start, uuid_end - uuid_start);
+
+ fprintf(
+ stderr,
+ "%s Deleting stale snapshot.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+
+ // Delete stale snapshot, if one exists
+ command = "snapshot \"" + vm_name + "\" ";
+ command += "delete \"";
+ command += uuid;
+ command += "\" ";
+
+ // Only log the error if we are not attempting to deregister the VM.
+ // delete_active is only set to true when we are deregistering the VM.
+ retval = vbm_popen(command, output, "delete stale snapshot", !delete_active, false, 0);
+ if (retval) return retval;
+ }
+ }
+
+ return 0;
+}
+
+int VBOX_VM::restore_snapshot() {
+ string command;
+ string output;
+ char buf[256];
+ int retval = BOINC_SUCCESS;
+
+ fprintf(
+ stderr,
+ "%s Restore from previously saved snapshot.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+
+ command = "snapshot \"" + vm_name + "\" ";
+ command += "restorecurrent ";
+ retval = vbm_popen(command, output, "restore current snapshot", true, false, 0);
+ if (retval) return retval;
+
+ fprintf(
+ stderr,
+ "%s Restore completed.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+
+ return retval;
+}
+
+void VBOX_VM::dump_hypervisor_status_reports() {
+ char buf[256];
+ SIZE_T ulMinimumWorkingSetSize;
+ SIZE_T ulMaximumWorkingSetSize;
+
+ if (
+ GetProcessWorkingSetSize(
+ vboxsvc_pid_handle,
+ &ulMinimumWorkingSetSize,
+ &ulMaximumWorkingSetSize)
+ ) {
+ fprintf(
+ stderr,
+ "%s Status Report (VirtualBox VboxSvc.exe): Minimum WSS: '%dKB', Maximum WSS: '%dKB'\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ ulMinimumWorkingSetSize/1024,
+ ulMaximumWorkingSetSize/1024
+ );
+ }
+
+ if (
+ GetProcessWorkingSetSize(
+ vm_pid_handle,
+ &ulMinimumWorkingSetSize,
+ &ulMaximumWorkingSetSize)
+ ) {
+ fprintf(
+ stderr,
+ "%s Status Report (VirtualBox Vboxheadless.exe/VirtualBox.exe): Minimum WSS: '%dKB', Maximum WSS: '%dKB'\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ ulMinimumWorkingSetSize/1024,
+ ulMaximumWorkingSetSize/1024
+ );
+ }
+}
+
+int VBOX_VM::is_registered() {
+ int retval = ERR_NOT_FOUND;
+ HRESULT rc;
+ BSTR vm_name;
+ IMachine* pMachine = NULL;
+
+ vm_name = SysAllocString(A2W(vm_master_name).c_str());
+
+ rc = g_pVirtualBox->FindMachine(vm_name, &pMachine);
+ if (VBOX_E_OBJECT_NOT_FOUND != rc) {
+ retval = BOINC_SUCCESS;
+ }
+
+ SysFreeString(vm_name);
+ return retval;
+}
+
+// Attempt to detect any condition that would prevent VirtualBox from running a VM properly, like:
+// 1. The DCOM service not being started on Windows
+// 2. Vboxmanage not being able to communicate with vboxsvc for some reason
+// 3. VirtualBox driver not loaded for the current Linux kernel.
+//
+// Luckly both of the above conditions can be detected by attempting to detect the host information
+// via vboxmanage and it is cross platform.
+//
+bool VBOX_VM::is_system_ready(std::string& message) {
+ string command;
+ string output;
+ char buf[256];
+ int retval;
+ bool rc = false;
+
+ command = "list hostinfo ";
+ retval = vbm_popen(command, output, "host info");
+ if (BOINC_SUCCESS == retval) {
+ rc = true;
+ }
+
+ if (output.size() == 0) {
+ fprintf(
+ stderr,
+ "%s WARNING: Communication with VM Hypervisor failed. (Possibly Out of Memory).\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ message = "Communication with VM Hypervisor failed. (Possibly Out of Memory).";
+ rc = false;
+ }
+
+ if (output.find("Processor count:") == string::npos) {
+ fprintf(
+ stderr,
+ "%s WARNING: Communication with VM Hypervisor failed.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ message = "Communication with VM Hypervisor failed.";
+ rc = false;
+ }
+
+ if (output.find("WARNING: The vboxdrv kernel module is not loaded.") != string::npos) {
+ vboxwrapper_msg_prefix(buf, sizeof(buf));
+ fprintf(
+ stderr,
+ "%s WARNING: The vboxdrv kernel module is not loaded.\n"
+ "%s WARNING: Please update/recompile VirtualBox kernel drivers.\n",
+ buf,
+ buf
+ );
+ message = "Please update/recompile VirtualBox kernel drivers.";
+ rc = false;
+ }
+
+ return rc;
+}
+
+bool VBOX_VM::is_hdd_registered() {
+ string command;
+ string output;
+ string virtual_machine_root_dir;
+
+ get_slot_directory(virtual_machine_root_dir);
+
+ command = "showhdinfo \"" + virtual_machine_root_dir + "/" + image_filename + "\" ";
+
+ if (vbm_popen(command, output, "hdd registration", false, false) == 0) {
+ if ((output.find("VBOX_E_FILE_ERROR") == string::npos) &&
+ (output.find("VBOX_E_OBJECT_NOT_FOUND") == string::npos) &&
+ (output.find("does not match the value") == string::npos)
+ ) {
+ // Error message not found in text
+ return true;
+ }
+ }
+ return false;
+}
+
+bool VBOX_VM::is_extpack_installed() {
+ string command;
+ string output;
+
+ command = "list extpacks";
+
+ if (vbm_popen(command, output, "extpack detection", false, false) == 0) {
+ if ((output.find("Oracle VM VirtualBox Extension Pack") != string::npos) && (output.find("VBoxVRDP") != string::npos)) {
+ 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 1;
+ }
+ return 0;
+}
+
+int VBOX_VM::get_version_information(string& version) {
+ string command;
+ string output;
+ int retval;
+
+ // Record the VirtualBox version information for later use.
+ command = "--version ";
+ retval = vbm_popen(command, output, "version check");
+
+ if (!retval) {
+ // Remove \r or \n from the output spew
+ string::iterator iter = output.begin();
+ while (iter != output.end()) {
+ if (*iter == '\r' || *iter == '\n') {
+ iter = output.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+ version = output;
+ }
+
+ return retval;
+}
+
+int VBOX_VM::get_guest_additions(string& guest_additions) {
+ string command;
+ string output;
+ size_t ga_start;
+ size_t ga_end;
+ int retval;
+
+ // Get the location of where the guest additions are
+ command = "list systemproperties";
+ retval = vbm_popen(command, output, "guest additions");
+
+ // Output should look like this:
+ // API version: 4_3
+ // Minimum guest RAM size: 4 Megabytes
+ // Maximum guest RAM size: 2097152 Megabytes
+ // Minimum video RAM size: 1 Megabytes
+ // Maximum video RAM size: 256 Megabytes
+ // ...
+ // Default Guest Additions ISO: C:\Program Files\Oracle\VirtualBox/VBoxGuestAdditions.iso
+ //
+
+ ga_start = output.find("Default Guest Additions ISO:");
+ if (ga_start == string::npos) {
+ return ERR_NOT_FOUND;
+ }
+ ga_start += strlen("Default Guest Additions ISO:");
+ ga_end = output.find("\n", ga_start);
+ guest_additions = output.substr(ga_start, ga_end - ga_start);
+ strip_whitespace(guest_additions);
+ if (guest_additions.size() <= 0) {
+ return ERR_NOT_FOUND;
+ }
+
+ return retval;
+}
+
+int VBOX_VM::get_default_network_interface(string& iface) {
+ string command;
+ string output;
+ size_t if_start;
+ size_t if_end;
+ int retval;
+
+ // Get the location of where the guest additions are
+ command = "list bridgedifs";
+ retval = vbm_popen(command, output, "default interface");
+
+ // Output should look like this:
+ // Name: Intel(R) Ethernet Connection I217-V
+ // GUID: 4b8796d6-a4ed-4752-8e8e-bf23984fd93c
+ // DHCP: Enabled
+ // IPAddress: 192.168.1.19
+ // NetworkMask: 255.255.255.0
+ // IPV6Address: fe80:0000:0000:0000:31c2:0053:4f50:4e64
+ // IPV6NetworkMaskPrefixLength: 64
+ // HardwareAddress: bc:5f:f4:ba:cc:16
+ // MediumType: Ethernet
+ // Status: Up
+ // VBoxNetworkName: HostInterfaceNetworking-Intel(R) Ethernet Connection I217-V
+
+ if_start = output.find("Name:");
+ if (if_start == string::npos) {
+ return ERR_NOT_FOUND;
+ }
+ if_start += strlen("Name:");
+ if_end = output.find("\n", if_start);
+ iface = output.substr(if_start, if_end - if_start);
+ strip_whitespace(iface);
+ if (iface.size() <= 0) {
+ return ERR_NOT_FOUND;
+ }
+
+ return retval;
+}
+
+int VBOX_VM::get_vm_network_bytes_sent(double& sent) {
+ string command;
+ string output;
+ string counter_value;
+ size_t counter_start;
+ size_t counter_end;
+ int retval;
+
+ command = "debugvm \"" + vm_name + "\" ";
+ command += "statistics --pattern \"/Devices/*/TransmitBytes\" ";
+
+ retval = vbm_popen(command, output, "get bytes sent");
+ if (retval) return retval;
+
+ // Output should look like this:
+ //
+ //
+ //
+ //
+ //
+
+ // 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);
+ }
+ return 0;
+}
+
+int VBOX_VM::get_vm_network_bytes_received(double& received) {
+ string command;
+ string output;
+ string counter_value;
+ size_t counter_start;
+ size_t counter_end;
+ int retval;
+
+ command = "debugvm \"" + vm_name + "\" ";
+ command += "statistics --pattern \"/Devices/*/ReceiveBytes\" ";
+
+ retval = vbm_popen(command, output, "get bytes received");
+ if (retval) return retval;
+
+ // Output should look like this:
+ //
+ //
+ //
+ //
+ //
+
+ // 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);
+ }
+
+ return 0;
+}
+
+int VBOX_VM::get_vm_process_id() {
+ string output;
+ string pid;
+ size_t pid_start;
+ size_t pid_end;
+
+ get_vm_log(output, false);
+
+ // Output should look like this:
+ // VirtualBox 4.1.0 r73009 win.amd64 (Jul 19 2011 13:05:53) release log
+ // 00:00:06.008 Log opened 2011-09-01T23:00:59.829170900Z
+ // 00:00:06.008 OS Product: Windows 7
+ // 00:00:06.009 OS Release: 6.1.7601
+ // 00:00:06.009 OS Service Pack: 1
+ // 00:00:06.015 Host RAM: 4094MB RAM, available: 876MB
+ // 00:00:06.015 Executable: C:\Program Files\Oracle\VirtualBox\VirtualBox.exe
+ // 00:00:06.015 Process ID: 6128
+ // 00:00:06.015 Package type: WINDOWS_64BITS_GENERIC
+ // 00:00:06.015 Installed Extension Packs:
+ // 00:00:06.015 None installed!
+ //
+ pid_start = output.find("Process ID: ");
+ if (pid_start == string::npos) {
+ return ERR_NOT_FOUND;
+ }
+ pid_start += strlen("Process ID: ");
+ pid_end = output.find("\n", pid_start);
+ pid = output.substr(pid_start, pid_end - pid_start);
+ strip_whitespace(pid);
+ if (pid.size() <= 0) {
+ return ERR_NOT_FOUND;
+ }
+
+ vm_pid = atol(pid.c_str());
+
+#ifdef _WIN32
+ vm_pid_handle = OpenProcess(
+ PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION,
+ FALSE,
+ vm_pid
+ );
+#endif
+
+ return 0;
+}
+
+int VBOX_VM::get_vm_exit_code(unsigned long& exit_code) {
+ if (vm_pid_handle) {
+ GetExitCodeProcess(vm_pid_handle, &exit_code);
+ }
+ return 0;
+}
+
+// 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) {
+ string command;
+ string output;
+ char buf[256];
+ int retval;
+
+ network_suspended = !enabled;
+
+ if (enabled) {
+ fprintf(
+ stderr,
+ "%s Enabling network access for VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--cableconnected1 on ";
+
+ retval = vbm_popen(command, output, "enable network");
+ if (retval) return retval;
+ } else {
+ fprintf(
+ stderr,
+ "%s Disabling network access for VM.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--cableconnected1 off ";
+
+ retval = vbm_popen(command, output, "disable network");
+ if (retval) return retval;
+ }
+ return 0;
+}
+
+int VBOX_VM::set_cpu_usage(int percentage) {
+ string command;
+ string output;
+ char buf[256];
+ int retval;
+
+ // the arg to controlvm is percentage
+ //
+ fprintf(
+ stderr,
+ "%s Setting CPU throttle for VM. (%d%%)\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ percentage
+ );
+ sprintf(buf, "%d", percentage);
+ command = "controlvm \"" + vm_name + "\" ";
+ command += "cpuexecutioncap ";
+ command += buf;
+ command += " ";
+
+ retval = vbm_popen(command, output, "CPU throttle");
+ if (retval) return retval;
+ return 0;
+}
+
+int VBOX_VM::set_network_usage(int kilobytes) {
+ string command;
+ string output;
+ char buf[256];
+ int retval;
+
+ // the argument to modifyvm is in KB
+ //
+ if (kilobytes == 0) {
+ fprintf(
+ stderr,
+ "%s Setting network throttle for VM. (1024GB)\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ } else {
+ fprintf(
+ stderr,
+ "%s Setting network throttle for VM. (%dKB)\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ kilobytes
+ );
+ }
+
+ if (is_virtualbox_version_newer(4, 2, 0)) {
+
+ // Update bandwidth group limits
+ //
+ if (kilobytes == 0) {
+ command = "bandwidthctl \"" + vm_name + "\" ";
+ command += "set \"" + vm_name + "_net\" ";
+ command += "--limit 1024G ";
+
+ retval = vbm_popen(command, output, "network throttle (set default value)");
+ if (retval) return retval;
+ } else {
+ sprintf(buf, "%d", kilobytes);
+ command = "bandwidthctl \"" + vm_name + "\" ";
+ command += "set \"" + vm_name + "_net\" ";
+ command += "--limit ";
+ command += buf;
+ command += "K ";
+
+ retval = vbm_popen(command, output, "network throttle (set)");
+ if (retval) return retval;
+ }
+
+ } else {
+
+ sprintf(buf, "%d", kilobytes);
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--nicspeed1 ";
+ command += buf;
+ command += " ";
+
+ retval = vbm_popen(command, output, "network throttle");
+ if (retval) return retval;
+
+ }
+
+ return 0;
+}
+
+void VBOX_VM::lower_vm_process_priority() {
+ char buf[256];
+ if (vm_pid_handle) {
+ SetPriorityClass(vm_pid_handle, BELOW_NORMAL_PRIORITY_CLASS);
+ fprintf(
+ stderr,
+ "%s Lowering VM Process priority.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ }
+}
+
+void VBOX_VM::reset_vm_process_priority() {
+ char buf[256];
+ if (vm_pid_handle) {
+ SetPriorityClass(vm_pid_handle, NORMAL_PRIORITY_CLASS);
+ fprintf(
+ stderr,
+ "%s Restoring VM Process priority.\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf))
+ );
+ }
+}
+
+// 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) {
+
+ fprintf(
+ stderr,
+ "%s Status Report: Detected vboxsvc.exe. (PID = '%d')\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ 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";
+
+ CreateProcess(NULL, (LPTSTR)command.c_str(), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi);
+
+ if (pi.hThread) CloseHandle(pi.hThread);
+ if (pi.hProcess) {
+ fprintf(
+ stderr,
+ "%s Status Report: Launching vboxsvc.exe. (PID = '%d')\n",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ pi.dwProcessId
+ );
+ vboxsvc_pid = pi.dwProcessId;
+ vboxsvc_pid_handle = pi.hProcess;
+ retval = BOINC_SUCCESS;
+ } else {
+ fprintf(
+ stderr,
+ "%s Status Report: Launching vboxsvc.exe failed!.\n"
+ " Error: %s",
+ vboxwrapper_msg_prefix(buf, sizeof(buf)),
+ windows_format_error_string(GetLastError(), buf, sizeof(buf))
+ );
+ }
+ }
+ }
+ }
+
+ return retval;
+}
+
+// Launch the VM.
+int VBOX_VM::launch_vboxvm() {
+ char buf[256];
+ char cmdline[1024];
+ char* argv[5];
+ int argc;
+ std::string output;
+ int retval = ERR_EXEC;
+ char error_msg[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("VboxHeadless.exe");
+ } else {
+ argv[0] = const_cast("VirtualBox.exe");
+ }
+ argv[1] = const_cast("--startvm");
+ argv[2] = const_cast(vm_name.c_str());
+ if (headless) {
+ argv[3] = const_cast("--vrde config");
+ } else {
+ argv[3] = const_cast("--no-startvm-errormsgbox");
+ }
+ argv[4] = NULL;
+ argc = 4;
+
+ strcpy(cmdline, "");
+ for (int i=0; i= 1000)) break;
+
+ Sleep(250);
+ ulExitTimeout += 250;
+ }
+
+ if (ulExitCode != STILL_ACTIVE) {
+ vboxwrapper_msg_prefix(buf, sizeof(buf));
+ fprintf(
+ stderr,
+ "%s Status Report: Virtualbox.exe/Vboxheadless.exe exited prematurely!.\n"
+ "%s Exit Code: %d\n"
+ "%s Output:\n"
+ "%s\n",
+ buf,
+ buf,
+ ulExitCode,
+ buf,
+ 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;
+}
diff --git a/win_build/vboxwrapper.vcxproj b/win_build/vboxwrapper.vcxproj
index b6609b746a..ae801e7901 100644
--- a/win_build/vboxwrapper.vcxproj
+++ b/win_build/vboxwrapper.vcxproj
@@ -290,6 +290,7 @@
+