// 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 . #ifdef _WIN32 #include "boinc_win.h" #include "win_util.h" #if defined(_MSC_VER) #define getcwd _getcwd #define stricmp _stricmp #endif #else #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #endif using std::string; #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_common.h" VBOX_BASE::VBOX_BASE() { virtualbox_home_directory.clear(); virtualbox_install_directory.clear(); virtualbox_guest_additions.clear(); virtualbox_version.clear(); pFloppy = NULL; vm_log.clear(); vm_log_timestamp.hours = 0; vm_log_timestamp.minutes = 0; vm_log_timestamp.seconds = 0; vm_log_timestamp.milliseconds = 0; vm_master_name.clear(); vm_master_description.clear(); vm_name.clear(); vm_cpu_count.clear(); vm_disk_controller_type.clear(); vm_disk_controller_model.clear(); os_name.clear(); memory_size_mb = 0.0; image_filename.clear(); iso_image_filename.clear(); cache_disk_filename.clear(); floppy_image_filename.clear(); job_duration = 0.0; current_cpu_time = 0.0; minimum_checkpoint_interval = 600.0; fraction_done_filename.clear(); suspended = false; network_suspended = false; online = false; saving = false; restoring = false; crashed = false; enable_cern_dataformat = false; enable_shared_directory = false; enable_floppyio = false; enable_cache_disk = false; enable_isocontextualization = false; enable_remotedesktop = false; enable_gbac = false; register_only = false; enable_network = false; network_bridged_mode = false; pf_guest_port = 0; pf_host_port = 0; headless = true; vm_pid = 0; vboxsvc_pid = 0; #ifdef _WIN32 vm_pid_handle = 0; vboxsvc_pid_handle = 0; #endif // Initialize default values vm_disk_controller_type = "ide"; vm_disk_controller_model = "PIIX4"; } VBOX_BASE::~VBOX_BASE() { if (pFloppy) { delete pFloppy; pFloppy = NULL; } #ifdef _WIN32 if (vm_pid_handle) { CloseHandle(vm_pid_handle); vm_pid_handle = NULL; } if (vboxsvc_pid_handle) { CloseHandle(vboxsvc_pid_handle); vboxsvc_pid_handle = NULL; } #endif } int VBOX_BASE::initialize() { return ERR_EXEC; } int VBOX_BASE::create_vm() { return ERR_EXEC; } int VBOX_BASE::register_vm() { return ERR_EXEC; } int VBOX_BASE::deregister_vm(bool delete_media) { return ERR_EXEC; } int VBOX_BASE::deregister_stale_vm() { return ERR_EXEC; } void VBOX_BASE::poll(bool /*log_state*/) { } int VBOX_BASE::start() { return ERR_EXEC; } int VBOX_BASE::stop() { return ERR_EXEC; } int VBOX_BASE::poweroff() { return ERR_EXEC; } int VBOX_BASE::pause() { return ERR_EXEC; } int VBOX_BASE::resume() { return ERR_EXEC; } int VBOX_BASE::create_snapshot(double elapsed_time) { return ERR_EXEC; } int VBOX_BASE::cleanup_snapshots(bool delete_active) { return ERR_EXEC; } int VBOX_BASE::restore_snapshot() { return ERR_EXEC; } int VBOX_BASE::run(bool do_restore_snapshot) { int retval; retval = is_registered(); if (ERR_TIMEOUT == retval) { return VBOXWRAPPER_ERR_RECOVERABLE; } else if (ERR_NOT_FOUND == retval) { if (is_vm_machine_configuration_available()) { retval = register_vm(); if (retval) return retval; } else { if (is_disk_image_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 = create_vm(); if (retval) return retval; } } // The user has requested that we exit after registering the VM, so return an // error to stop further processing. if (register_only) return ERR_FOPEN; // If we are restarting an already registered VM, then the vm_name variable // will be empty right now, so populate it with the master name so all of the // various other functions will work. vm_name = vm_master_name; // Check to see if the VM is already in a running state, if so, poweroff. poll(false); if (online) { retval = poweroff(); if (retval) return ERR_NOT_EXITED; } // If our last checkpoint time is greater than 0, restore from the previously // saved snapshot if (do_restore_snapshot) { retval = restore_snapshot(); if (retval) return retval; } // Has BOINC signaled that we should quit? // Try to prevent starting the VM in an environment where we might be terminated any // second. This can happen if BOINC has been told to shutdown or the volunteer has // told BOINC to switch to a different project. // if (boinc_status.no_heartbeat || boinc_status.quit_request) { return VBOXWRAPPER_ERR_RECOVERABLE; } // Start the VM retval = start(); if (retval) return retval; return 0; } void VBOX_BASE::cleanup() { poweroff(); deregister_vm(true); // Give time enough for external processes to finish the cleanup process boinc_sleep(5.0); } void VBOX_BASE::dump_hypervisor_logs(bool include_error_logs) { string local_system_log; string local_vm_log; string local_trace_log; unsigned long vm_exit_code = 0; get_system_log(local_system_log); get_vm_log(local_vm_log); get_trace_log(local_trace_log); get_vm_exit_code(vm_exit_code); if (include_error_logs) { fprintf( stderr, "\n" " Hypervisor System Log:\n\n" "%s\n" " VM Execution Log:\n\n" "%s\n" " VM Trace Log:\n\n" "%s", local_system_log.c_str(), local_vm_log.c_str(), local_trace_log.c_str() ); } if (vm_exit_code) { fprintf( stderr, "\n" " VM Exit Code: %d (0x%x)\n\n", (unsigned int)vm_exit_code, (unsigned int)vm_exit_code ); } } void VBOX_BASE::dump_hypervisor_status_reports() { } // t1 > t2 static bool is_timestamp_newer(VBOX_TIMESTAMP& t1, VBOX_TIMESTAMP& t2) { if (t1.hours > t2.hours) return true; if (t1.hours < t2.hours) return false; if (t1.minutes > t2.minutes) return true; if (t1.minutes < t2.minutes) return false; if (t1.seconds > t2.seconds) return true; if (t1.seconds < t2.seconds) return false; if (t1.milliseconds > t2.milliseconds) return true; return false; } // Dump any new guest log messages which are generated by applications running within // the guest VM. void VBOX_BASE::dump_vmguestlog_entries() { string local_vm_log; string line; size_t eol_pos; size_t eol_prev_pos; size_t line_pos; VBOX_TIMESTAMP current_timestamp; string msg; char buf[256]; get_vm_log(local_vm_log, true, 16*1024); eol_prev_pos = 0; eol_pos = local_vm_log.find("\n"); while (eol_pos != string::npos) { line = local_vm_log.substr(eol_prev_pos, eol_pos - eol_prev_pos); line_pos = line.find("Guest Log:"); if (line_pos != string::npos) { sscanf( line.c_str(), "%d:%d:%d.%d", ¤t_timestamp.hours, ¤t_timestamp.minutes, ¤t_timestamp.seconds, ¤t_timestamp.milliseconds ); if (is_timestamp_newer(current_timestamp, vm_log_timestamp)) { vm_log_timestamp = current_timestamp; msg = line.substr(line_pos, line.size() - line_pos); fprintf( stderr, "%s %s\n", vboxwrapper_msg_prefix(buf, sizeof(buf)), msg.c_str() ); } } eol_prev_pos = eol_pos + 1; eol_pos = local_vm_log.find("\n", eol_prev_pos); } } int VBOX_BASE::is_registered() { return ERR_NOT_FOUND; } bool VBOX_BASE::is_system_ready(std::string& message) { return false; } bool VBOX_BASE::is_vm_machine_configuration_available() { string virtual_machine_slot_directory; string vm_machine_configuration_file; APP_INIT_DATA aid; boinc_get_init_data_p(&aid); get_slot_directory(virtual_machine_slot_directory); vm_machine_configuration_file = virtual_machine_slot_directory + "/" + vm_master_name + "/" + vm_master_name + ".vbox"; if (boinc_file_exists(vm_machine_configuration_file.c_str())) { return true; } return false; } bool VBOX_BASE::is_disk_image_registered() { return false; } bool VBOX_BASE::is_extpack_installed() { return false; } bool VBOX_BASE::is_logged_failure_vm_extensions_disabled() { if (vm_log.find("VERR_VMX_MSR_LOCKED_OR_DISABLED") != string::npos) return true; if (vm_log.find("VERR_SVM_DISABLED") != string::npos) return true; // VirtualBox 4.3.x or better if (vm_log.find("VERR_VMX_MSR_VMXON_DISABLED") != string::npos) return true; if (vm_log.find("VERR_VMX_MSR_SMX_VMXON_DISABLED") != string::npos) return true; return false; } bool VBOX_BASE::is_logged_failure_vm_extensions_in_use() { if (vm_log.find("VERR_VMX_IN_VMX_ROOT_MODE") != string::npos) return true; if (vm_log.find("VERR_SVM_IN_USE") != string::npos) return true; return false; } bool VBOX_BASE::is_logged_failure_vm_extensions_not_supported() { if (vm_log.find("VERR_VMX_NO_VMX") != string::npos) return true; if (vm_log.find("VERR_SVM_NO_SVM") != string::npos) return true; return false; } bool VBOX_BASE::is_logged_failure_vm_powerup() { if (vm_log.find("Power up failed (vrc=VINF_SUCCESS, rc=E_FAIL (0X80004005))") != string::npos) return true; return false; } bool VBOX_BASE::is_logged_failure_host_out_of_memory() { if (vm_log.find("VERR_EM_NO_MEMORY") != string::npos) return true; if (vm_log.find("VERR_NO_MEMORY") != string::npos) return true; return false; } bool VBOX_BASE::is_logged_failure_guest_job_out_of_memory() { if (vm_log.find("EXIT_OUT_OF_MEMORY") != string::npos) return true; return false; } bool VBOX_BASE::is_virtualbox_version_newer(int maj, int min, int rel) { int vbox_major = 0, vbox_minor = 0, vbox_release = 0; if (3 == sscanf(virtualbox_version.c_str(), "%d.%d.%d", &vbox_major, &vbox_minor, &vbox_release)) { if (maj < vbox_major) return true; if (maj > vbox_major) return false; if (min < vbox_minor) return true; if (min > vbox_minor) return false; if (rel < vbox_release) return true; } return false; } int VBOX_BASE::get_install_directory(std::string& dir) { return ERR_EXEC; } int VBOX_BASE::get_version_information(std::string& version) { return ERR_EXEC; } int VBOX_BASE::get_guest_additions(std::string& dir) { return ERR_EXEC; } // Returns the current directory in which the executable resides. // int VBOX_BASE::get_slot_directory(string& dir) { char slot_dir[256]; getcwd(slot_dir, sizeof(slot_dir)); dir = slot_dir; if (!dir.empty()) { return 1; } return 0; } int VBOX_BASE::get_default_network_interface(std::string& iface) { return ERR_EXEC; } int VBOX_BASE::get_vm_network_bytes_sent(double& sent) { return ERR_EXEC; } int VBOX_BASE::get_vm_network_bytes_received(double& received) { return ERR_EXEC; } int VBOX_BASE::get_vm_process_id() { return 0; } int VBOX_BASE::get_vm_exit_code(unsigned long& exit_code) { return ERR_EXEC; } double VBOX_BASE::get_vm_cpu_time() { return 0.0; } int VBOX_BASE::get_system_log(string& log, bool tail_only, unsigned int buffer_size) { string slot_directory; string virtualbox_system_log_src; string virtualbox_system_log_dst; string::iterator iter; int retval = BOINC_SUCCESS; // Where should we copy temp files to? get_slot_directory(slot_directory); // Locate and read log file virtualbox_system_log_src = virtualbox_home_directory + "/VBoxSVC.log"; virtualbox_system_log_dst = slot_directory + "/VBoxSVC.log"; if (boinc_file_exists(virtualbox_system_log_src.c_str())) { // Skip having to deal with various forms of file locks by just making a temp // copy of the log file. boinc_copy(virtualbox_system_log_src.c_str(), virtualbox_system_log_dst.c_str()); } if (boinc_file_exists(virtualbox_system_log_dst.c_str())) { if (tail_only) { // Keep only the last 8k if it is larger than that. read_file_string(virtualbox_system_log_dst.c_str(), log, buffer_size, true); } else { read_file_string(virtualbox_system_log_dst.c_str(), log); } sanitize_output(log); if (tail_only) { if (log.size() >= (buffer_size - 115)) { // Look for the next whole line of text. iter = log.begin(); while (iter != log.end()) { if (*iter == '\n') { log.erase(iter); break; } iter = log.erase(iter); } } } } return retval; } int VBOX_BASE::get_vm_log(string& log, bool tail_only, unsigned int buffer_size) { string slot_directory; string virtualbox_vm_log_src; string virtualbox_vm_log_dst; string::iterator iter; int retval = BOINC_SUCCESS; // Where should we copy temp files to? get_slot_directory(slot_directory); // Locate and read log file virtualbox_vm_log_src = vm_master_name + "/Logs/VBox.log"; virtualbox_vm_log_dst = slot_directory + "/VBox.log"; if (boinc_file_exists(virtualbox_vm_log_src.c_str())) { // Skip having to deal with various forms of file locks by just making a temp // copy of the log file. boinc_copy(virtualbox_vm_log_src.c_str(), virtualbox_vm_log_dst.c_str()); } if (boinc_file_exists(virtualbox_vm_log_dst.c_str())) { if (tail_only) { // Keep only the last 8k if it is larger than that. read_file_string(virtualbox_vm_log_dst.c_str(), log, buffer_size, true); } else { read_file_string(virtualbox_vm_log_dst.c_str(), log); } sanitize_output(log); if (tail_only) { if (log.size() >= (buffer_size - 115)) { // Look for the next whole line of text. iter = log.begin(); while (iter != log.end()) { if (*iter == '\n') { log.erase(iter); break; } iter = log.erase(iter); } } } } return retval; } int VBOX_BASE::get_trace_log(string& log, bool tail_only, unsigned int buffer_size) { string slot_directory; string vm_trace_log; string::iterator iter; int retval = BOINC_SUCCESS; // Where should we copy temp files to? get_slot_directory(slot_directory); // Locate and read log file vm_trace_log = slot_directory + "/" + TRACELOG_FILENAME; if (boinc_file_exists(vm_trace_log.c_str())) { if (tail_only) { // Keep only the last 8k if it is larger than that. read_file_string(vm_trace_log.c_str(), log, buffer_size, true); } else { read_file_string(vm_trace_log.c_str(), log); } sanitize_output(log); if (tail_only) { if (log.size() >= (buffer_size - 115)) { // Look for the next whole line of text. iter = log.begin(); while (iter != log.end()) { if (*iter == '\n') { log.erase(iter); break; } iter = log.erase(iter); } } } } else { retval = ERR_NOT_FOUND; } return retval; } int VBOX_BASE::set_network_access(bool enabled) { return ERR_EXEC; } int VBOX_BASE::set_cpu_usage(int percentage) { return ERR_EXEC; } int VBOX_BASE::set_network_usage(int kilobytes) { return ERR_EXEC; } int VBOX_BASE::read_floppy(std::string& data) { if (enable_floppyio && pFloppy) { data = pFloppy->receive(); return 0; } return 1; } int VBOX_BASE::write_floppy(std::string& data) { if (enable_floppyio && pFloppy) { pFloppy->send(data); return 0; } return 1; } void VBOX_BASE::lower_vm_process_priority() { } void VBOX_BASE::reset_vm_process_priority() { } void VBOX_BASE::sanitize_output(std::string& output) { #ifdef _WIN32 // Remove \r from the log spew string::iterator iter = output.begin(); while (iter != output.end()) { if (*iter == '\r') { iter = output.erase(iter); } else { ++iter; } } #endif } // 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_BASE::launch_vboxsvc() { APP_INIT_DATA aid; PROC_MAP pm; PROCINFO p; string command; int retval = ERR_EXEC; #ifdef _WIN32 char buf[256]; char error_msg[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, (LPTSTR)virtualbox_home_directory.c_str(), &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 { vboxwrapper_msg_prefix(buf, sizeof(buf)); fprintf( stderr, "%s Status Report: Launching vboxsvc.exe failed!.\n" "%s Error: %s\n", buf, buf, windows_format_error_string(GetLastError(), error_msg, sizeof(error_msg)) ); #ifdef _DEBUG fprintf( stderr, "%s Vbox Version: '%s'\n", buf, virtualbox_version.c_str() ); fprintf( stderr, "%s Vbox Install Directory: '%s'\n", buf, virtualbox_install_directory.c_str() ); fprintf( stderr, "%s Vbox Home Directory: '%s'\n", buf, virtualbox_home_directory.c_str() ); #endif } vbm_trace(command, std::string(""), retval); } } } #endif return retval; } // Launch the VM. int VBOX_BASE::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= 1000)) break; Sleep(250); ulExitTimeout += 250; } if (ulExitCode != STILL_ACTIVE) { sanitize_output(output); 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); #else int pid = fork(); if (-1 == pid) { output = strerror(errno); fprintf( stderr, "%s Status Report: Launching virtualbox.exe/vboxheadless.exe failed!.\n" " Error: %s", vboxwrapper_msg_prefix(buf, sizeof(buf)), output.c_str() ); retval = ERR_FORK; } else if (0 == pid) { if (-1 == execv(argv[0], argv)) { _exit(errno); } } else { vm_pid = pid; retval = BOINC_SUCCESS; } #endif string cmd_line = cmdline; vbm_trace(cmd_line, output, retval); return retval; } // If there are errors we can recover from, process them here. // int VBOX_BASE::vbm_popen(string& command, string& output, const char* item, bool log_error, bool retry_failures, unsigned int timeout, bool log_trace) { int retval = 0; int retry_count = 0; double sleep_interval = 1.0; char buf[256]; string retry_notes; // Initialize command line command = "VBoxManage -q " + command; do { retval = vbm_popen_raw(command, output, timeout); sanitize_output(command); sanitize_output(output); if (log_trace) { vbm_trace(command, output, retval); } if (retval) { // VirtualBox designed the concept of sessions to prevent multiple applications using // the VirtualBox COM API (virtualbox.exe, vboxmanage.exe) from modifying the same VM // at the same time. // // The problem here is that vboxwrapper uses vboxmanage.exe to modify and control the // VM. Vboxmanage.exe can only maintain the session lock for as long as it takes it // to run. So that means 99% of the time that a VM is running under BOINC technology // it is running without a session lock. // // If a volunteer opens another VirtualBox management application and goes poking around // that application can aquire the session lock and not give it up for some time. // // If we detect that condition retry the desired command. // // Experiments performed by jujube suggest changing the sleep interval to an exponential // style backoff would increase our chances of success in situations where the previous // lock is held by a previous instance of vboxmanage whos instance data hasn't been // cleaned up within vboxsvc yet. // // Error Code: VBOX_E_INVALID_OBJECT_STATE (0x80bb0007) // if (VBOX_E_INVALID_OBJECT_STATE == (unsigned int)retval) { if (retry_notes.find("Another VirtualBox management") == string::npos) { retry_notes += "Another VirtualBox management application has locked the session for\n"; retry_notes += "this VM. BOINC cannot properly monitor this VM\n"; retry_notes += "and so this job will be aborted.\n\n"; } if (retry_count) { sleep_interval *= 2; } } // VboxManage has to be able to communicate with vboxsvc in order to actually issue a // command. In cases where we detect CO_E_SERVER_EXEC_FAILURE, we should just automatically // try the command again. Vboxmanage wasn't even able to issue the desired command // anyway. // // Experiments performed by jujube suggest changing the sleep interval to an exponential // style backoff would increase our chances of success. // // Error Code: CO_E_SERVER_EXEC_FAILURE (0x80080005) // if (CO_E_SERVER_EXEC_FAILURE == (unsigned int)retval) { if (retry_notes.find("Unable to communicate with VirtualBox") == string::npos) { retry_notes += "Unable to communicate with VirtualBox. VirtualBox may need to\n"; retry_notes += "be reinstalled.\n\n"; } if (retry_count) { sleep_interval *= 2; } } // Retry? if (!retry_failures && (VBOX_E_INVALID_OBJECT_STATE != (unsigned int)retval) && (CO_E_SERVER_EXEC_FAILURE != (unsigned int)retval) ) { break; } // Timeout? if (retry_count >= 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_BASE::vbm_popen_raw(string& command, string& output, unsigned int timeout) { char buf[256]; size_t errcode_start; size_t errcode_end; string errcode; int retval = BOINC_SUCCESS; // Reset output buffer output.clear(); #ifdef _WIN32 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)); InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); SetSecurityDescriptorDacl(&sd, true, NULL, false); sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = &sd; if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, NULL)) { fprintf( stderr, "%s CreatePipe failed! (%d).\n", vboxwrapper_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", vboxwrapper_msg_prefix(buf, sizeof(buf)), GetLastError() ); goto CLEANUP; } // Wait until process has completed while(1) { if (timeout == 0) { WaitForSingleObject(pi.hProcess, INFINITE); } GetExitCodeProcess(pi.hProcess, &ulExitCode); // Copy stdout/stderr to output buffer, handle in the loop so that we can // copy the pipe as it is populated and prevent the child process from blocking // in case the output is bigger than pipe buffer. PeekNamedPipe(hReadPipe, NULL, NULL, NULL, &dwCount, NULL); if (dwCount) { pBuf = malloc(dwCount+1); memset(pBuf, 0, dwCount+1); if (ReadFile(hReadPipe, pBuf, dwCount, &dwCount, NULL)) { output += (char*)pBuf; } free(pBuf); } if (ulExitCode != STILL_ACTIVE) break; // Timeout? if (ulExitTimeout >= (timeout * 1000)) { if (!TerminateProcess(pi.hProcess, EXIT_FAILURE)) { fprintf( stderr, "%s TerminateProcess failed! (%d).\n", vboxwrapper_msg_prefix(buf, sizeof(buf)), GetLastError() ); } ulExitCode = 0; retval = ERR_TIMEOUT; Sleep(1000); } Sleep(250); ulExitTimeout += 250; } 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)) { // 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); } // If something couldn't be found, just return ERR_FOPEN if (!retval) retval = ERR_FOPEN; } #else FILE* fp; // 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); } } #endif // 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_BASE::vbm_replay(std::string& command) { FILE* f = fopen(REPLAYLOG_FILENAME, "a"); if (f) { fprintf(f, "%s\n", command.c_str()); fclose(f); } } void VBOX_BASE::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); } } VBOX_VM::VBOX_VM() { VBOX_BASE::VBOX_BASE(); } VBOX_VM::~VBOX_VM() { VBOX_BASE::~VBOX_BASE(); }