diff --git a/checkin_notes b/checkin_notes
index 60c8454739..46e44ddc88 100644
--- a/checkin_notes
+++ b/checkin_notes
@@ -5481,3 +5481,10 @@ David 1 Sept 2011
samples/wrapper/
wrapper.cpp
+
+Rom 1 Sept 2011
+ - VboxWrapper: Add a way to extract the VM process id from the VM's
+ log file.
+
+ samples/vboxwrapper/
+ vbox.cpp, .h
diff --git a/samples/vboxwrapper/vbox.cpp b/samples/vboxwrapper/vbox.cpp
index 1adb300400..a1260d2a1e 100644
--- a/samples/vboxwrapper/vbox.cpp
+++ b/samples/vboxwrapper/vbox.cpp
@@ -1,902 +1,957 @@
-// This file is part of BOINC.
-// http://boinc.berkeley.edu
-// Copyright (C) 2010 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 .
-
-#ifdef _WIN32
-#include "boinc_win.h"
-#include "win_util.h"
-#else
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#endif
-
-using std::string;
-
-#if defined(_MSC_VER) || defined(__MINGW32__)
-#define popen _popen
-#define pclose _pclose
-#define getcwd _getcwd
-#define putenv _putenv
-#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 "boinc_api.h"
-#include "vbox.h"
-
-VBOX_VM::VBOX_VM() {
- os_name.clear();
- memory_size_mb.clear();
- image_filename.clear();
- suspended = false;
- network_suspended = false;
- enable_network = false;
- enable_shared_directory = false;
-}
-
-int VBOX_VM::run() {
- int retval;
-
- retval = initialize();
- if (retval) return retval;
-
- if (!is_registered()) {
- if (is_hdd_registered()) {
- // Handle the case where a previous instance of the same projects VM
- // was already initialized for the current slot directory but aborted
- // while the task was suspended and unloaded from memory.
- retval = deregister_stale_vm();
- if (retval) return retval;
- }
- retval = register_vm();
- if (retval) return retval;
- }
-
- retval = startvm();
- if (retval) return retval;
-
- return 0;
-}
-
-void VBOX_VM::poll() {
- return;
-}
-
-void VBOX_VM::cleanup() {
- stop();
- deregister_vm();
-
- // Give time enough for external processes to finish the cleanup process
- boinc_sleep(5.0);
-}
-
-// Execute the vbox manage application and copy the output to the
-// designated buffer.
-//
-int VBOX_VM::vbm_popen(string& arguments, string& output) {
- char buf[256];
- string command;
-
- // Initialize command line
- command = "VBoxManage -q " + arguments;
-
-#ifdef _WIN32
- STARTUPINFO si;
- PROCESS_INFORMATION pi;
- SECURITY_ATTRIBUTES sa;
- SECURITY_DESCRIPTOR sd;
- HANDLE hReadPipe, hWritePipe;
- void* pBuf;
- DWORD dwCount;
- unsigned long ulExitCode = 0;
- int retval = ERR_FOPEN;
-
-
- memset(&si, 0, sizeof(si));
- memset(&pi, 0, sizeof(pi));
- memset(&sa, 0, sizeof(sa));
- memset(&sd, 0, sizeof(sd));
-
-
- InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
- SetSecurityDescriptorDacl(&sd, true, NULL, false);
-
- sa.nLength = sizeof(SECURITY_ATTRIBUTES);
- sa.bInheritHandle = TRUE;
- sa.lpSecurityDescriptor = &sd;
-
-
- if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0)) {
- fprintf(
- stderr,
- "%s CreatePipe failed! (%d).\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- GetLastError()
- );
- goto CLEANUP;
- }
- SetHandleInformation(hReadPipe, HANDLE_FLAG_INHERIT, 0);
-
-
- si.cb = sizeof(STARTUPINFO);
- si.dwFlags |= STARTF_FORCEOFFFEEDBACK | STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
- si.wShowWindow = SW_HIDE;
- si.hStdOutput = hWritePipe;
- si.hStdError = hWritePipe;
- si.hStdInput = NULL;
-
-
- // Execute command
- if (!CreateProcess(NULL, (LPTSTR)command.c_str(), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
- fprintf(
- stderr,
- "%s CreateProcess failed! (%d).\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- GetLastError()
- );
- goto CLEANUP;
- }
-
-
- // Wait until process has completed
- while(1) {
- GetExitCodeProcess(pi.hProcess, &ulExitCode);
- if (ulExitCode != STILL_ACTIVE) break;
- }
-
-
- // Copy stdout/stderr to output buffer
- if (!PeekNamedPipe(hReadPipe, NULL, NULL, NULL, &dwCount, NULL)) {
- fprintf(
- stderr,
- "%s PeekNamedPipe failed! (%d).\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- GetLastError()
- );
- }
-
- if (dwCount) {
- pBuf = malloc(dwCount+1);
- memset(pBuf, 0, dwCount+1);
-
- if (ReadFile(hReadPipe, pBuf, dwCount, &dwCount, NULL)) {
- output += (char*)pBuf;
- }
-
- free(pBuf);
- }
-
-
-CLEANUP:
- if (pi.hThread) CloseHandle(pi.hThread);
- if (pi.hProcess) CloseHandle(pi.hProcess);
- if (hReadPipe) CloseHandle(hReadPipe);
- if (hWritePipe) CloseHandle(hWritePipe);
-
- if ((ulExitCode == 0) && (pi.hProcess)) {
- retval = 0;
- }
-
- return retval;
-
-#else
- FILE* fp;
-
- // Execute command
- fp = popen(command.c_str(), "r");
- if (fp == NULL){
- fprintf(
- stderr,
- "%s vbm_popen popen failed! errno = %d\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- errno
- );
- return ERR_FOPEN;
- }
-
- // Copy output to buffer
- while (fgets(buf, 256, fp)) {
- output += buf;
- }
-
- // Close stream
- pclose(fp);
-
-#endif
-
- return 0;
-}
-
-
-// Returns the current directory in which the executable resides.
-//
-int VBOX_VM::generate_vm_root_dir( string& dir ) {
- char root_dir[256];
-
- getcwd(root_dir, (sizeof(root_dir)*sizeof(char)));
- dir = root_dir;
-
- if (!dir.empty()) {
- return 1;
- }
- return 0;
-}
-
-bool VBOX_VM::is_registered() {
- string command;
- string output;
-
- command = "showvminfo " + vm_name;
-
- if (vbm_popen(command, output) == 0) {
- if (output.find("VBOX_E_OBJECT_NOT_FOUND") != string::npos) {
- return true;
- }
- }
- return false;
-}
-
-
-bool VBOX_VM::is_hdd_registered() {
- string command;
- string output;
- string virtual_machine_root_dir;
-
- generate_vm_root_dir(virtual_machine_root_dir);
-
- command = "showhdinfo \"" + virtual_machine_root_dir + "/" + image_filename + "\" ";
-
- if (vbm_popen(command, output) == 0) {
- if (output.find("VBOX_E_FILE_ERROR") != string::npos) {
- return true;
- }
- }
- return false;
-}
-
-
-bool VBOX_VM::is_running() {
- string command;
- string output;
-
- command = "list runningvms";
-
- if (vbm_popen(command, output) == 0) {
- if (output.find(vm_name) != string::npos) {
- return true;
- }
- }
-
- return false;
-}
-
-int VBOX_VM::get_install_directory(string& virtualbox_install_directory ) {
-#ifdef _WIN32
- 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
- );
-
- virtualbox_install_directory = lpszRegistryValue;
- }
- }
-
- if (hkSetupHive) RegCloseKey(hkSetupHive);
- if (lpszRegistryValue) free(lpszRegistryValue);
-#else
- virtualbox_install_directory = "";
-#endif
- return 0;
-}
-
-
-int VBOX_VM::initialize() {
- string virtualbox_install_directory;
- string virtual_machine_root_dir;
- string old_path;
- string new_path;
- string virtualbox_user_home;
- char buf[256];
-
- get_install_directory(virtualbox_install_directory);
- generate_vm_root_dir(virtual_machine_root_dir);
-
- // Prep the environment so we can execute the vboxmanage application
- if (!virtualbox_install_directory.empty()) {
- old_path = getenv("path");
-
- new_path = "path=";
- new_path += virtualbox_install_directory;
-
- // Path environment variable seperator
-#ifdef _WIN32
- new_path += ";";
-#else
- new_path += ":";
-#endif
-
- new_path += old_path;
-
- if (putenv(const_cast(new_path.c_str()))) {
- fprintf(
- stderr,
- "%s Failed to modify the search path.\n",
- boinc_msg_prefix(buf, sizeof(buf))
- );
- }
-
-
- // Set the location in which the VirtualBox Configuration files can be
- // stored for this instance.
- virtualbox_user_home = "VBOX_USER_HOME=";
- virtualbox_user_home += virtual_machine_root_dir;
- virtualbox_user_home += "/vbox";
-
- if (putenv(const_cast(virtualbox_user_home.c_str()))) {
- fprintf(
- stderr,
- "%s Failed to modify the VBOX_USER_HOME path.\n",
- boinc_msg_prefix(buf, sizeof(buf))
- );
- }
- }
-
- return 0;
-}
-
-
-int VBOX_VM::register_vm() {
- string command;
- string output;
- string virtual_machine_root_dir;
- char buf[256];
- int retval;
-
- generate_vm_root_dir(virtual_machine_root_dir);
-
- fprintf(
- stderr,
- "%s Registering virtual machine. (%s) \n",
- boinc_msg_prefix(buf, sizeof(buf)),
- vm_name.c_str()
- );
-
-
- // Create and register the VM
- //
- command = "createvm ";
- command += "--name \"" + vm_name + "\" ";
- command += "--basefolder \"" + virtual_machine_root_dir + "\" ";
- command += "--ostype \"" + os_name + "\" ";
- command += "--register";
-
- retval = vbm_popen(command, output);
- if (retval) {
- fprintf(
- stderr,
- "%s Error registering virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- retval,
- command.c_str(),
- output.c_str()
- );
- return retval;
- }
-
-
- // Tweak the VM from it's default configuration
- //
- command = "modifyvm \"" + vm_name + "\" ";
- command += "--memory " + memory_size_mb + " ";
- command += "--acpi on ";
- command += "--ioapic on ";
- command += "--boot1 disk ";
- command += "--boot2 none ";
- command += "--boot3 none ";
- command += "--boot4 none ";
- command += "--nic1 nat ";
- command += "--natdnsproxy1 on ";
- command += "--cableconnected1 off ";
-
- retval = vbm_popen(command, output);
- if (retval) {
- fprintf(
- stderr,
- "%s Error modifing virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- retval,
- command.c_str(),
- output.c_str()
- );
- return retval;
- }
-
-
- // Add storage controller to VM
- //
- command = "storagectl \"" + vm_name + "\" ";
- command += "--name \"IDE Controller\" ";
- command += "--add ide ";
- command += "--controller PIIX4 ";
-
- retval = vbm_popen(command, output);
- if (retval) {
- fprintf(
- stderr,
- "%s Error adding storage controller to virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- retval,
- command.c_str(),
- output.c_str()
- );
- return retval;
- }
-
-
- // Adding virtual hard drive to VM
- //
- command = "storageattach \"" + vm_name + "\" ";
- command += "--storagectl \"IDE Controller\" ";
- command += "--port 0 ";
- command += "--device 0 ";
- command += "--type hdd ";
- command += "--medium \"" + virtual_machine_root_dir + "/" + image_filename + "\" ";
-
- retval = vbm_popen(command, output);
- if (retval) {
- fprintf(
- stderr,
- "%s Error adding virtual disk drive to virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- retval,
- command.c_str(),
- output.c_str()
- );
- return retval;
- }
-
- // Enable the network adapter if a network connection is required.
- //
- if (enable_network) {
- set_network_access(true);
- }
-
- // Enable the shared folder if a shared folder is specified.
- //
- if (enable_shared_directory) {
- command = "sharedfolder add \"" + vm_name + "\" ";
- command += "--name \"shared\" ";
- command += "--hostpath \"" + virtual_machine_root_dir + "/shared\"";
-
- retval = vbm_popen(command, output);
- if (retval) {
- fprintf(
- stderr,
- "%s Error enabling shared directory for virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- retval,
- command.c_str(),
- output.c_str()
- );
- return retval;
- }
- }
-
- return 0;
-}
-
-
-int VBOX_VM::deregister_vm() {
- string command;
- string output;
- string virtual_machine_root_dir;
- char buf[256];
- int retval;
-
- generate_vm_root_dir(virtual_machine_root_dir);
-
- fprintf(
- stderr,
- "%s Deregistering virtual machine. (%s)\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- vm_name.c_str()
- );
-
-
- // First step in deregistering a VM is to delete its storage controller
- //
- command = "storagectl \"" + vm_name + "\" ";
- command += "--name \"IDE Controller\" ";
- command += "--remove ";
-
- retval = vbm_popen(command, output);
- if (retval) {
- fprintf(
- stderr,
- "%s Error removing storage controller from virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- retval,
- command.c_str(),
- output.c_str()
- );
- return retval;
- }
-
-
- // Next delete VM
- //
- command = "unregistervm \"" + vm_name + "\" ";
- command += "--delete ";
-
- retval = vbm_popen(command, output);
- if (retval) {
- fprintf(
- stderr,
- "%s Error removing virtual machine from virtual box! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- retval,
- command.c_str(),
- output.c_str()
- );
- return retval;
- }
-
-
- // Lastly delete medium from Virtual Box Media Registry
- //
- command = "closemedium disk \"" + virtual_machine_root_dir + "/" + image_filename + "\" ";
-
- retval = vbm_popen(command, output);
- if (retval) {
- fprintf(
- stderr,
- "%s Error removing virtual hdd from virtual box! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- retval,
- command.c_str(),
- output.c_str()
- );
- return retval;
- }
-
- return 0;
-}
-
-
-int VBOX_VM::deregister_stale_vm() {
- string command;
- string output;
- string virtual_machine_root_dir;
- size_t uuid_location;
- size_t uuid_length;
- char buf[256];
- int retval;
-
- generate_vm_root_dir(virtual_machine_root_dir);
-
- // We need to determine what the name or uuid is of the previous VM which owns
- // this virtual disk
- //
- command = "showhdinfo \"" + virtual_machine_root_dir + "/" + image_filename + "\" ";
-
- retval = vbm_popen(command, output);
- if (retval) {
- fprintf(
- stderr,
- "%s Error retrieving virtual hard disk information from virtual box! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- retval,
- command.c_str(),
- output.c_str()
- );
- return retval;
- }
-
-
- // Output should look a little like this:
- // UUID: c119acaf-636c-41f6-86c9-38e639a31339
- // Accessible: yes
- // Logical size: 10240 MBytes
- // Current size on disk: 0 MBytes
- // Type: normal (base)
- // Storage format: VDI
- // Format variant: dynamic default
- // In use by VMs: test2 (UUID: 000ab2be-1254-4c6a-9fdc-1536a478f601)
- // Location: C:\Users\romw\VirtualBox VMs\test2\test2.vdi
- //
- uuid_location = output.find("(UUID: ");
- if (uuid_location != string::npos) {
- // We can parse the virtual machine ID from the output
-
- uuid_location += 7;
- uuid_length = output.find(")", uuid_location);
-
- vm_name = output.substr(uuid_location, uuid_length);
-
- // Deregister stale VM by UUID
- return deregister_vm();
- } else {
- // Did the user delete the VM in VirtualBox and not the medium? If so,
- // just remove the medium.
- command = "closemedium \"" + virtual_machine_root_dir + "/" + image_filename + "\" ";
-
- retval = vbm_popen(command, output);
- if (retval) {
- fprintf(
- stderr,
- "%s Error removing virtual hdd from virtual box! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- retval,
- command.c_str(),
- output.c_str()
- );
- return retval;
- }
- }
-
- return 0;
-}
-
-
-int VBOX_VM::startvm() {
- string command;
- string output;
- char buf[256];
- int retval;
-
- command = "startvm \"" + vm_name + "\" --type headless";
- retval = vbm_popen(command, output);
- if (retval) {
- fprintf(
- stderr,
- "%s Error starting virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- retval,
- command.c_str(),
- output.c_str()
- );
- return retval;
- }
- return 0;
-}
-
-
-int VBOX_VM::stop() {
- string command;
- string output;
- char buf[256];
- int retval;
-
- command = "controlvm \"" + vm_name + "\" savestate";
- retval = vbm_popen(command, output);
- if (retval) {
- fprintf(
- stderr,
- "%s Error stopping virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- retval,
- command.c_str(),
- output.c_str()
- );
- return retval;
- }
- return 0;
-}
-
-
-int VBOX_VM::pause() {
- string command;
- string output;
- char buf[256];
- int retval;
-
- command = "controlvm \"" + vm_name + "\" pause";
- retval = vbm_popen(command, output);
- if (retval) {
- fprintf(
- stderr,
- "%s Error pausing virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- retval,
- command.c_str(),
- output.c_str()
- );
- return retval;
- }
- suspended = true;
- return 0;
-}
-
-
-int VBOX_VM::resume() {
- string command;
- string output;
- char buf[256];
- int retval;
-
- command = "controlvm \"" + vm_name + "\" resume";
- retval = vbm_popen(command, output);
- if (retval) {
- fprintf(
- stderr,
- "%s Error resuming virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- retval,
- command.c_str(),
- output.c_str()
- );
- return retval;
- }
- suspended = false;
- 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 itself is NOT signed. Doing so opens up
-// the network behind the firewall to attack.
-//
-int VBOX_VM::set_network_access(bool enabled) {
- string command;
- string output;
- char buf[256];
- int retval;
-
- network_suspended = !enabled;
-
- if (enabled) {
- command = "modifyvm \"" + vm_name + "\" ";
- command += "--cableconnected1 on ";
-
- retval = vbm_popen(command, output);
- if (retval) {
- fprintf(
- stderr,
- "%s Error enabling network access for virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- retval,
- command.c_str(),
- output.c_str()
- );
- return retval;
- }
- } else {
- command = "modifyvm \"" + vm_name + "\" ";
- command += "--cableconnected1 off ";
-
- retval = vbm_popen(command, output);
- if (retval) {
- fprintf(
- stderr,
- "%s Error disabling network access for virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- retval,
- command.c_str(),
- output.c_str()
- );
- return retval;
- }
- }
- return 0;
-}
-
-
-int VBOX_VM::set_cpu_usage_fraction(double x) {
- string command;
- string output;
- char buf[256];
- int retval;
-
- // the arg to modifyvm is percentage
- //
- sprintf(buf, "%d", (int)(x*100.));
- command = "modifyvm \"" + vm_name + "\" ";
- command += "--cpuexecutioncap ";
- command += buf;
- command += " ";
-
- retval = vbm_popen(command, output);
- if (retval) {
- fprintf(
- stderr,
- "%s Error setting cpu throttle for virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- retval,
- command.c_str(),
- output.c_str()
- );
- return retval;
- }
- return 0;
-}
-
-
-int VBOX_VM::set_network_max_bytes_sec(double x) {
- string command;
- string output;
- char buf[256];
- int retval;
-
-
- // the argument to modifyvm is in Kbps
- //
- sprintf(buf, "%d", (int)(x*8./1000.));
- command = "modifyvm \"" + vm_name + "\" ";
- command += "--nicspeed1 ";
- command += buf;
- command += " ";
-
- retval = vbm_popen(command, output);
- if (retval) {
- fprintf(
- stderr,
- "%s Error setting network throttle for virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
- boinc_msg_prefix(buf, sizeof(buf)),
- retval,
- command.c_str(),
- output.c_str()
- );
- return retval;
- }
- return 0;
-}
-
+// This file is part of BOINC.
+// http://boinc.berkeley.edu
+// Copyright (C) 2010 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 .
+
+#ifdef _WIN32
+#include "boinc_win.h"
+#include "win_util.h"
+#else
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#endif
+
+using std::string;
+
+#if defined(_MSC_VER) || defined(__MINGW32__)
+#define popen _popen
+#define pclose _pclose
+#define getcwd _getcwd
+#define putenv _putenv
+#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 "boinc_api.h"
+#include "vbox.h"
+
+VBOX_VM::VBOX_VM() {
+ os_name.clear();
+ memory_size_mb.clear();
+ image_filename.clear();
+ suspended = false;
+ network_suspended = false;
+ enable_network = false;
+ enable_shared_directory = false;
+}
+
+int VBOX_VM::run() {
+ int retval;
+
+ retval = initialize();
+ if (retval) return retval;
+
+ if (!is_registered()) {
+ if (is_hdd_registered()) {
+ // Handle the case where a previous instance of the same projects VM
+ // was already initialized for the current slot directory but aborted
+ // while the task was suspended and unloaded from memory.
+ retval = deregister_stale_vm();
+ if (retval) return retval;
+ }
+ retval = register_vm();
+ if (retval) return retval;
+ }
+
+ retval = startvm();
+ if (retval) return retval;
+
+ return 0;
+}
+
+void VBOX_VM::poll() {
+ return;
+}
+
+void VBOX_VM::cleanup() {
+ stop();
+ deregister_vm();
+
+ // Give time enough for external processes to finish the cleanup process
+ boinc_sleep(5.0);
+}
+
+// Execute the vbox manage application and copy the output to the
+// designated buffer.
+//
+int VBOX_VM::vbm_popen(string& arguments, string& output) {
+ char buf[256];
+ string command;
+
+ // Initialize command line
+ command = "VBoxManage -q " + arguments;
+
+#ifdef _WIN32
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ SECURITY_ATTRIBUTES sa;
+ SECURITY_DESCRIPTOR sd;
+ HANDLE hReadPipe, hWritePipe;
+ void* pBuf;
+ DWORD dwCount;
+ unsigned long ulExitCode = 0;
+ int retval = ERR_FOPEN;
+
+
+ memset(&si, 0, sizeof(si));
+ memset(&pi, 0, sizeof(pi));
+ memset(&sa, 0, sizeof(sa));
+ memset(&sd, 0, sizeof(sd));
+
+
+ InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
+ SetSecurityDescriptorDacl(&sd, true, NULL, false);
+
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.bInheritHandle = TRUE;
+ sa.lpSecurityDescriptor = &sd;
+
+
+ if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0)) {
+ fprintf(
+ stderr,
+ "%s CreatePipe failed! (%d).\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ GetLastError()
+ );
+ goto CLEANUP;
+ }
+ SetHandleInformation(hReadPipe, HANDLE_FLAG_INHERIT, 0);
+
+
+ si.cb = sizeof(STARTUPINFO);
+ si.dwFlags |= STARTF_FORCEOFFFEEDBACK | STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
+ si.wShowWindow = SW_HIDE;
+ si.hStdOutput = hWritePipe;
+ si.hStdError = hWritePipe;
+ si.hStdInput = NULL;
+
+
+ // Execute command
+ if (!CreateProcess(NULL, (LPTSTR)command.c_str(), NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi)) {
+ fprintf(
+ stderr,
+ "%s CreateProcess failed! (%d).\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ GetLastError()
+ );
+ goto CLEANUP;
+ }
+
+
+ // Wait until process has completed
+ while(1) {
+ GetExitCodeProcess(pi.hProcess, &ulExitCode);
+ if (ulExitCode != STILL_ACTIVE) break;
+ }
+
+
+ // Copy stdout/stderr to output buffer
+ if (!PeekNamedPipe(hReadPipe, NULL, NULL, NULL, &dwCount, NULL)) {
+ fprintf(
+ stderr,
+ "%s PeekNamedPipe failed! (%d).\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ GetLastError()
+ );
+ }
+
+ if (dwCount) {
+ pBuf = malloc(dwCount+1);
+ memset(pBuf, 0, dwCount+1);
+
+ if (ReadFile(hReadPipe, pBuf, dwCount, &dwCount, NULL)) {
+ output += (char*)pBuf;
+ }
+
+ free(pBuf);
+ }
+
+
+CLEANUP:
+ if (pi.hThread) CloseHandle(pi.hThread);
+ if (pi.hProcess) CloseHandle(pi.hProcess);
+ if (hReadPipe) CloseHandle(hReadPipe);
+ if (hWritePipe) CloseHandle(hWritePipe);
+
+ if ((ulExitCode == 0) && (pi.hProcess)) {
+ retval = 0;
+ }
+
+ return retval;
+
+#else
+ FILE* fp;
+
+ // Execute command
+ fp = popen(command.c_str(), "r");
+ if (fp == NULL){
+ fprintf(
+ stderr,
+ "%s vbm_popen popen failed! errno = %d\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ errno
+ );
+ return ERR_FOPEN;
+ }
+
+ // Copy output to buffer
+ while (fgets(buf, 256, fp)) {
+ output += buf;
+ }
+
+ // Close stream
+ pclose(fp);
+
+#endif
+
+ return 0;
+}
+
+
+// Returns the current directory in which the executable resides.
+//
+int VBOX_VM::generate_vm_root_dir( string& dir ) {
+ char root_dir[256];
+
+ getcwd(root_dir, (sizeof(root_dir)*sizeof(char)));
+ dir = root_dir;
+
+ if (!dir.empty()) {
+ return 1;
+ }
+ return 0;
+}
+
+bool VBOX_VM::is_registered() {
+ string command;
+ string output;
+
+ command = "showvminfo " + vm_name;
+
+ if (vbm_popen(command, output) == 0) {
+ if (output.find("VBOX_E_OBJECT_NOT_FOUND") != string::npos) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool VBOX_VM::is_hdd_registered() {
+ string command;
+ string output;
+ string virtual_machine_root_dir;
+
+ generate_vm_root_dir(virtual_machine_root_dir);
+
+ command = "showhdinfo \"" + virtual_machine_root_dir + "/" + image_filename + "\" ";
+
+ if (vbm_popen(command, output) == 0) {
+ if (output.find("VBOX_E_FILE_ERROR") != string::npos) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool VBOX_VM::is_running() {
+ string command;
+ string output;
+
+ command = "list runningvms";
+
+ if (vbm_popen(command, output) == 0) {
+ if (output.find(vm_name) != string::npos) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int VBOX_VM::get_install_directory(string& virtualbox_install_directory ) {
+#ifdef _WIN32
+ 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
+ );
+
+ virtualbox_install_directory = lpszRegistryValue;
+ }
+ }
+
+ if (hkSetupHive) RegCloseKey(hkSetupHive);
+ if (lpszRegistryValue) free(lpszRegistryValue);
+#else
+ virtualbox_install_directory = "";
+#endif
+ return 0;
+}
+
+
+int VBOX_VM::initialize() {
+ string virtualbox_install_directory;
+ string virtual_machine_root_dir;
+ string old_path;
+ string new_path;
+ string virtualbox_user_home;
+ char buf[256];
+
+ get_install_directory(virtualbox_install_directory);
+ generate_vm_root_dir(virtual_machine_root_dir);
+
+ // Prep the environment so we can execute the vboxmanage application
+ if (!virtualbox_install_directory.empty()) {
+ old_path = getenv("path");
+
+ new_path = "path=";
+ new_path += virtualbox_install_directory;
+
+ // Path environment variable seperator
+#ifdef _WIN32
+ new_path += ";";
+#else
+ new_path += ":";
+#endif
+
+ new_path += old_path;
+
+ if (putenv(const_cast(new_path.c_str()))) {
+ fprintf(
+ stderr,
+ "%s Failed to modify the search path.\n",
+ boinc_msg_prefix(buf, sizeof(buf))
+ );
+ }
+
+
+ // Set the location in which the VirtualBox Configuration files can be
+ // stored for this instance.
+ virtualbox_user_home = "VBOX_USER_HOME=";
+ virtualbox_user_home += virtual_machine_root_dir;
+ virtualbox_user_home += "/vbox";
+
+ if (putenv(const_cast(virtualbox_user_home.c_str()))) {
+ fprintf(
+ stderr,
+ "%s Failed to modify the VBOX_USER_HOME path.\n",
+ boinc_msg_prefix(buf, sizeof(buf))
+ );
+ }
+ }
+
+ return 0;
+}
+
+
+int VBOX_VM::register_vm() {
+ string command;
+ string output;
+ string virtual_machine_root_dir;
+ char buf[256];
+ int retval;
+
+ generate_vm_root_dir(virtual_machine_root_dir);
+
+ fprintf(
+ stderr,
+ "%s Registering virtual machine. (%s) \n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ vm_name.c_str()
+ );
+
+
+ // Create and register the VM
+ //
+ command = "createvm ";
+ command += "--name \"" + vm_name + "\" ";
+ command += "--basefolder \"" + virtual_machine_root_dir + "\" ";
+ command += "--ostype \"" + os_name + "\" ";
+ command += "--register";
+
+ retval = vbm_popen(command, output);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s Error registering virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ retval,
+ command.c_str(),
+ output.c_str()
+ );
+ return retval;
+ }
+
+
+ // Tweak the VM from it's default configuration
+ //
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--memory " + memory_size_mb + " ";
+ command += "--acpi on ";
+ command += "--ioapic on ";
+ command += "--boot1 disk ";
+ command += "--boot2 none ";
+ command += "--boot3 none ";
+ command += "--boot4 none ";
+ command += "--nic1 nat ";
+ command += "--natdnsproxy1 on ";
+ command += "--cableconnected1 off ";
+
+ retval = vbm_popen(command, output);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s Error modifing virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ retval,
+ command.c_str(),
+ output.c_str()
+ );
+ return retval;
+ }
+
+
+ // Add storage controller to VM
+ //
+ command = "storagectl \"" + vm_name + "\" ";
+ command += "--name \"IDE Controller\" ";
+ command += "--add ide ";
+ command += "--controller PIIX4 ";
+
+ retval = vbm_popen(command, output);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s Error adding storage controller to virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ retval,
+ command.c_str(),
+ output.c_str()
+ );
+ return retval;
+ }
+
+
+ // Adding virtual hard drive to VM
+ //
+ command = "storageattach \"" + vm_name + "\" ";
+ command += "--storagectl \"IDE Controller\" ";
+ command += "--port 0 ";
+ command += "--device 0 ";
+ command += "--type hdd ";
+ command += "--medium \"" + virtual_machine_root_dir + "/" + image_filename + "\" ";
+
+ retval = vbm_popen(command, output);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s Error adding virtual disk drive to virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ retval,
+ command.c_str(),
+ output.c_str()
+ );
+ return retval;
+ }
+
+ // Enable the network adapter if a network connection is required.
+ //
+ if (enable_network) {
+ set_network_access(true);
+ }
+
+ // Enable the shared folder if a shared folder is specified.
+ //
+ if (enable_shared_directory) {
+ command = "sharedfolder add \"" + vm_name + "\" ";
+ command += "--name \"shared\" ";
+ command += "--hostpath \"" + virtual_machine_root_dir + "/shared\"";
+
+ retval = vbm_popen(command, output);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s Error enabling shared directory for virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ retval,
+ command.c_str(),
+ output.c_str()
+ );
+ return retval;
+ }
+ }
+
+ return 0;
+}
+
+
+int VBOX_VM::deregister_vm() {
+ string command;
+ string output;
+ string virtual_machine_root_dir;
+ char buf[256];
+ int retval;
+
+ generate_vm_root_dir(virtual_machine_root_dir);
+
+ fprintf(
+ stderr,
+ "%s Deregistering virtual machine. (%s)\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ vm_name.c_str()
+ );
+
+
+ // First step in deregistering a VM is to delete its storage controller
+ //
+ command = "storagectl \"" + vm_name + "\" ";
+ command += "--name \"IDE Controller\" ";
+ command += "--remove ";
+
+ retval = vbm_popen(command, output);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s Error removing storage controller from virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ retval,
+ command.c_str(),
+ output.c_str()
+ );
+ return retval;
+ }
+
+
+ // Next delete VM
+ //
+ command = "unregistervm \"" + vm_name + "\" ";
+ command += "--delete ";
+
+ retval = vbm_popen(command, output);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s Error removing virtual machine from virtual box! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ retval,
+ command.c_str(),
+ output.c_str()
+ );
+ return retval;
+ }
+
+
+ // Lastly delete medium from Virtual Box Media Registry
+ //
+ command = "closemedium disk \"" + virtual_machine_root_dir + "/" + image_filename + "\" ";
+
+ retval = vbm_popen(command, output);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s Error removing virtual hdd from virtual box! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ retval,
+ command.c_str(),
+ output.c_str()
+ );
+ return retval;
+ }
+
+ return 0;
+}
+
+
+int VBOX_VM::deregister_stale_vm() {
+ string command;
+ string output;
+ string virtual_machine_root_dir;
+ size_t uuid_location;
+ size_t uuid_length;
+ char buf[256];
+ int retval;
+
+ generate_vm_root_dir(virtual_machine_root_dir);
+
+ // We need to determine what the name or uuid is of the previous VM which owns
+ // this virtual disk
+ //
+ command = "showhdinfo \"" + virtual_machine_root_dir + "/" + image_filename + "\" ";
+
+ retval = vbm_popen(command, output);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s Error retrieving virtual hard disk information from virtual box! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ retval,
+ command.c_str(),
+ output.c_str()
+ );
+ return retval;
+ }
+
+
+ // Output should look a little like this:
+ // UUID: c119acaf-636c-41f6-86c9-38e639a31339
+ // Accessible: yes
+ // Logical size: 10240 MBytes
+ // Current size on disk: 0 MBytes
+ // Type: normal (base)
+ // Storage format: VDI
+ // Format variant: dynamic default
+ // In use by VMs: test2 (UUID: 000ab2be-1254-4c6a-9fdc-1536a478f601)
+ // Location: C:\Users\romw\VirtualBox VMs\test2\test2.vdi
+ //
+ uuid_location = output.find("(UUID: ");
+ if (uuid_location != string::npos) {
+ // We can parse the virtual machine ID from the output
+ uuid_location += 7;
+ uuid_length = output.find(")", uuid_location);
+ vm_name = output.substr(uuid_location, uuid_length);
+
+ // Deregister stale VM by UUID
+ return deregister_vm();
+ } else {
+ // Did the user delete the VM in VirtualBox and not the medium? If so,
+ // just remove the medium.
+ command = "closemedium \"" + virtual_machine_root_dir + "/" + image_filename + "\" ";
+
+ retval = vbm_popen(command, output);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s Error removing virtual hdd from virtual box! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ retval,
+ command.c_str(),
+ output.c_str()
+ );
+ return retval;
+ }
+ }
+
+ return 0;
+}
+
+
+int VBOX_VM::startvm() {
+ string command;
+ string output;
+ char buf[256];
+ int retval;
+
+ command = "startvm \"" + vm_name + "\" --type headless";
+ retval = vbm_popen(command, output);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s Error starting virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ retval,
+ command.c_str(),
+ output.c_str()
+ );
+ return retval;
+ }
+ return 0;
+}
+
+
+int VBOX_VM::stop() {
+ string command;
+ string output;
+ char buf[256];
+ int retval;
+
+ command = "controlvm \"" + vm_name + "\" savestate";
+ retval = vbm_popen(command, output);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s Error stopping virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ retval,
+ command.c_str(),
+ output.c_str()
+ );
+ return retval;
+ }
+ return 0;
+}
+
+
+int VBOX_VM::pause() {
+ string command;
+ string output;
+ char buf[256];
+ int retval;
+
+ command = "controlvm \"" + vm_name + "\" pause";
+ retval = vbm_popen(command, output);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s Error pausing virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ retval,
+ command.c_str(),
+ output.c_str()
+ );
+ return retval;
+ }
+ suspended = true;
+ return 0;
+}
+
+
+int VBOX_VM::resume() {
+ string command;
+ string output;
+ char buf[256];
+ int retval;
+
+ command = "controlvm \"" + vm_name + "\" resume";
+ retval = vbm_popen(command, output);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s Error resuming virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ retval,
+ command.c_str(),
+ output.c_str()
+ );
+ return retval;
+ }
+ suspended = false;
+ 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 itself is NOT signed. Doing so opens up
+// the network behind the firewall to attack.
+//
+int VBOX_VM::set_network_access(bool enabled) {
+ string command;
+ string output;
+ char buf[256];
+ int retval;
+
+ network_suspended = !enabled;
+
+ if (enabled) {
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--cableconnected1 on ";
+
+ retval = vbm_popen(command, output);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s Error enabling network access for virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ retval,
+ command.c_str(),
+ output.c_str()
+ );
+ return retval;
+ }
+ } else {
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--cableconnected1 off ";
+
+ retval = vbm_popen(command, output);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s Error disabling network access for virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ retval,
+ command.c_str(),
+ output.c_str()
+ );
+ return retval;
+ }
+ }
+ return 0;
+}
+
+
+int VBOX_VM::set_cpu_usage_fraction(double x) {
+ string command;
+ string output;
+ char buf[256];
+ int retval;
+
+ // the arg to modifyvm is percentage
+ //
+ sprintf(buf, "%d", (int)(x*100.));
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--cpuexecutioncap ";
+ command += buf;
+ command += " ";
+
+ retval = vbm_popen(command, output);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s Error setting cpu throttle for virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ retval,
+ command.c_str(),
+ output.c_str()
+ );
+ return retval;
+ }
+ return 0;
+}
+
+
+int VBOX_VM::set_network_max_bytes_sec(double x) {
+ string command;
+ string output;
+ char buf[256];
+ int retval;
+
+
+ // the argument to modifyvm is in Kbps
+ //
+ sprintf(buf, "%d", (int)(x*8./1000.));
+ command = "modifyvm \"" + vm_name + "\" ";
+ command += "--nicspeed1 ";
+ command += buf;
+ command += " ";
+
+ retval = vbm_popen(command, output);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s Error setting network throttle for virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ retval,
+ command.c_str(),
+ output.c_str()
+ );
+ return retval;
+ }
+ return 0;
+}
+
+
+int VBOX_VM::get_vm_process_id(long& process_id) {
+ string command;
+ string output;
+ string pid;
+ size_t pid_location;
+ size_t pid_length;
+ char buf[256];
+ int retval;
+
+ command = "showvminfo \"" + vm_name + "\" ";
+ command += "--log 0 ";
+
+ retval = vbm_popen(command, output);
+ if (retval) {
+ fprintf(
+ stderr,
+ "%s Error getting process id from log file for virtual machine! rc = 0x%x\nCommand:\n%s\nOutput:\n%s\n",
+ boinc_msg_prefix(buf, sizeof(buf)),
+ retval,
+ command.c_str(),
+ output.c_str()
+ );
+ return retval;
+ }
+
+ // Output should look a little 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_location = output.find("Process ID: ");
+ if (pid_location != string::npos) {
+ // We can parse the Process ID from the output
+ pid_location += 12;
+ pid_length = output.find("\n", pid_location);
+ pid = output.substr(pid_location, pid_length);
+
+ if (pid.size() > 0) {
+ process_id = atol(pid.c_str());
+ retval = 0;
+ } else {
+ retval = 1;
+ }
+ }
+
+ return retval;
+}
+
diff --git a/samples/vboxwrapper/vbox.h b/samples/vboxwrapper/vbox.h
index a6aa8da5d7..45660e7fd6 100644
--- a/samples/vboxwrapper/vbox.h
+++ b/samples/vboxwrapper/vbox.h
@@ -59,9 +59,10 @@ struct VBOX_VM {
int set_network_access(bool enabled);
int set_cpu_usage_fraction(double);
int set_network_max_bytes_sec(double);
+ int get_vm_process_id(long& process_id);
static int initialize();
- static int generate_vm_root_dir( std::string& dir );
+ static int generate_vm_root_dir(std::string& dir);
static int vbm_popen(std::string&, std::string&);
static int get_install_directory(std::string&);
};