diff --git a/lib/win_util.cpp b/lib/win_util.cpp index fe4b15ff831c643c416ae78c8907b4e1ca90ab53..a14b3b6aab942732295701349fed93608da74715 100644 --- a/lib/win_util.cpp +++ b/lib/win_util.cpp @@ -161,7 +161,7 @@ char* windows_format_error_string( // include the hex error code as well snprintf(pszBuf, iSize, "%S (0x%x)", lpszTemp, dwError); if (lpszTemp) { - LocalFree((HLOCAL) lpszTemp); + LocalFree((HLOCAL)lpszTemp); } } else { strcpy(pszBuf, "(unknown error)"); diff --git a/samples/vboxwrapper/vbox_common.cpp b/samples/vboxwrapper/vbox_common.cpp index d7950c125c999da8e4af24a1f5420902abbbe4e7..e1f20ba47a59b221f441e795791373913cfa27c8 100644 --- a/samples/vboxwrapper/vbox_common.cpp +++ b/samples/vboxwrapper/vbox_common.cpp @@ -21,6 +21,7 @@ #if defined(_MSC_VER) #define getcwd _getcwd +#define stricmp _stricmp #endif #else @@ -393,10 +394,6 @@ bool VBOX_BASE::is_extpack_installed() { return false; } -bool VBOX_BASE::is_virtualbox_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; @@ -686,6 +683,650 @@ void VBOX_BASE::sanitize_output(std::string& output) { #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<char*>("VboxHeadless.exe"); + } else { + argv[0] = const_cast<char*>("VirtualBox.exe"); + } + argv[1] = const_cast<char*>("--startvm"); + argv[2] = const_cast<char*>(vm_name.c_str()); + if (headless) { + argv[3] = const_cast<char*>("--vrde config"); + } else { + argv[3] = const_cast<char*>("--no-startvm-errormsgbox"); + } + argv[4] = NULL; + argc = 4; + + strcpy(cmdline, ""); + for (int i=0; i<argc; i++) { + strcat(cmdline, argv[i]); + if (i<argc-1) { + strcat(cmdline, " "); + } + } + +#ifdef _WIN32 + 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)); + + 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, + cmdline, + NULL, + NULL, + TRUE, + CREATE_NO_WINDOW, + NULL, + NULL, + &si, + &pi + )) { + vboxwrapper_msg_prefix(buf, sizeof(buf)); + fprintf( + stderr, + "%s Status Report: Launching virtualbox.exe/vboxheadless.exe failed!.\n" + "%s Error: %s (%d)\n", + buf, + buf, + windows_format_error_string(GetLastError(), error_msg, sizeof(error_msg)), + GetLastError() + ); + + goto CLEANUP; + } + + while(1) { + GetExitCodeProcess(pi.hProcess, &ulExitCode); + + // Copy stdout/stderr to output buffer, handle in the loop so that we can + // copy the pipe as it is populated and prevent the child process from blocking + // in case the output is bigger than pipe buffer. + PeekNamedPipe(hReadPipe, NULL, NULL, NULL, &dwCount, NULL); + if (dwCount) { + pBuf = malloc(dwCount+1); + memset(pBuf, 0, dwCount+1); + + if (ReadFile(hReadPipe, pBuf, dwCount, &dwCount, NULL)) { + output += (char*)pBuf; + } + + free(pBuf); + } + + if ((ulExitCode != STILL_ACTIVE) || (ulExitTimeout >= 1000)) break; + + Sleep(250); + ulExitTimeout += 250; + } + + if (ulExitCode != STILL_ACTIVE) { + 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(); } diff --git a/samples/vboxwrapper/vbox_common.h b/samples/vboxwrapper/vbox_common.h index 2467e3f1a0ada19429b81d06269566017fb3d5ba..44d90b5c8691db007fca44b99b8aca3aab8af2f0 100644 --- a/samples/vboxwrapper/vbox_common.h +++ b/samples/vboxwrapper/vbox_common.h @@ -274,7 +274,6 @@ public: virtual bool is_vm_machine_configuration_available(); virtual bool is_disk_image_registered(); virtual bool is_extpack_installed(); - static bool is_virtualbox_installed(); virtual bool is_logged_failure_vm_extensions_disabled(); virtual bool is_logged_failure_vm_extensions_in_use(); virtual bool is_logged_failure_vm_extensions_not_supported(); @@ -283,8 +282,8 @@ public: virtual bool is_logged_failure_guest_job_out_of_memory(); virtual bool is_virtualbox_version_newer(int maj, int min, int rel); - virtual int get_install_directory(std::string& dir); - virtual int get_version_information(std::string& version); + static int get_install_directory(std::string& dir); + static int get_version_information(std::string& version); virtual int get_guest_additions(std::string& dir); virtual int get_slot_directory(std::string& dir); virtual int get_default_network_interface(std::string& iface); @@ -308,7 +307,19 @@ public: virtual void lower_vm_process_priority(); virtual void reset_vm_process_priority(); - virtual void sanitize_output(std::string& output); + static void sanitize_output(std::string& output); + + virtual int launch_vboxsvc(); + virtual int launch_vboxvm(); + + 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 + ); + int vbm_popen_raw( + std::string& command, std::string& output, unsigned int timeout + ); + static void vbm_replay(std::string& command); + static void vbm_trace(std::string& command, std::string& ouput, int retval); }; class VBOX_VM : public VBOX_BASE { diff --git a/samples/vboxwrapper/vbox_mscom_impl.cpp b/samples/vboxwrapper/vbox_mscom_impl.cpp index d31b63501c420244829514aca095547e13123c07..d977263161293495bbc5d38c904ea226e6e38eea 100644 --- a/samples/vboxwrapper/vbox_mscom_impl.cpp +++ b/samples/vboxwrapper/vbox_mscom_impl.cpp @@ -199,8 +199,7 @@ int VBOX_VM::initialize() { int rc = BOINC_SUCCESS; string old_path; string new_path; - string command; - string output; + string version; APP_INIT_DATA aid; bool force_sandbox = false; char buf[256]; @@ -256,6 +255,12 @@ int VBOX_VM::initialize() { } } + // Launch vboxsvc manually so that the DCOM subsystem won't be able too. Our version + // will have permission and direction to write its state information to the BOINC + // data directory. + // + launch_vboxsvc(); + // Instantiate the VirtualBox root object. rc = m_pPrivate->m_pVirtualBox.CreateInstance(CLSID_VirtualBox); if (FAILED(rc)) { @@ -280,9 +285,13 @@ int VBOX_VM::initialize() { return rc; } - rc = get_version_information(virtualbox_version); + rc = get_version_information(version); if (rc) return rc; + // Fix-up version string + virtualbox_version = "VirtualBox COM Interface (Version: " + version + ")"; + + // Get the guest addition information get_guest_additions(virtualbox_guest_additions); return rc; @@ -2133,16 +2142,6 @@ bool VBOX_VM::is_extpack_installed() { return false; } -bool VBOX_VM::is_virtualbox_installed() { - HRESULT rc; - CComPtr<IVirtualBox> pVirtualBox; - rc = pVirtualBox.CoCreateInstance(CLSID_VirtualBox); - if (SUCCEEDED(rc)) { - return true; - } - return false; -} - int VBOX_VM::get_install_directory(string& install_directory ) { LONG lReturnValue; HKEY hkSetupHive; @@ -2195,17 +2194,54 @@ int VBOX_VM::get_install_directory(string& install_directory ) { } int VBOX_VM::get_version_information(string& version) { - int retval = ERR_EXEC; - HRESULT rc; - CComBSTR tmp; + LONG lReturnValue; + HKEY hkSetupHive; + LPTSTR lpszRegistryValue = NULL; + DWORD dwSize = 0; - rc = m_pPrivate->m_pVirtualBox->get_VersionNormalized(&tmp); - if (SUCCEEDED(rc)) { - version = string("VirtualBox COM Interface (Version: ") + string(CW2A(tmp)) + string(")"); - retval = BOINC_SUCCESS; + // 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("VersionExt"), + NULL, + NULL, + NULL, + &dwSize + ); + if (lReturnValue != ERROR_FILE_NOT_FOUND) { + // Allocate the buffer space. + lpszRegistryValue = (LPTSTR) malloc(dwSize); + (*lpszRegistryValue) = NULL; + + // Now get the data + lReturnValue = RegQueryValueEx( + hkSetupHive, + _T("VersionExt"), + NULL, + NULL, + (LPBYTE)lpszRegistryValue, + &dwSize + ); + + version = lpszRegistryValue; + } } - return retval; + if (hkSetupHive) RegCloseKey(hkSetupHive); + if (lpszRegistryValue) free(lpszRegistryValue); + if (version.empty()) { + return ERR_FREAD; + } + return BOINC_SUCCESS; } int VBOX_VM::get_guest_additions(string& guest_additions) { diff --git a/samples/vboxwrapper/vbox_mscom_impl.h b/samples/vboxwrapper/vbox_mscom_impl.h index fff84a80aa498f71bcf07736fb69f9f7a7a33918..c0e7a2318989823a72af04654daaed093051544a 100644 --- a/samples/vboxwrapper/vbox_mscom_impl.h +++ b/samples/vboxwrapper/vbox_mscom_impl.h @@ -43,10 +43,9 @@ public: bool is_system_ready(std::string& message); bool is_disk_image_registered(); bool is_extpack_installed(); - static bool is_virtualbox_installed(); - int get_install_directory(std::string& dir); - int get_version_information(std::string& version); + static int get_install_directory(std::string& dir); + static int get_version_information(std::string& version); int get_guest_additions(std::string& dir); int get_default_network_interface(std::string& iface); int get_vm_network_bytes_sent(double& sent); diff --git a/samples/vboxwrapper/vbox_vboxmanage.cpp b/samples/vboxwrapper/vbox_vboxmanage.cpp index 35ab24a9dd804065fe6b8f7b0a2fb3921c0b9d36..dad739fce5647a945d562f97bc967bd7bac81aca 100644 --- a/samples/vboxwrapper/vbox_vboxmanage.cpp +++ b/samples/vboxwrapper/vbox_vboxmanage.cpp @@ -159,6 +159,14 @@ int VBOX_VM::initialize() { #endif } +#ifdef _WIN32 + // Launch vboxsvc manually so that the DCOM subsystem won't be able too. Our version + // will have permission and direction to write its state information to the BOINC + // data directory. + // + launch_vboxsvc(); +#endif + rc = get_version_information(virtualbox_version); if (rc) return rc; @@ -421,7 +429,7 @@ int VBOX_VM::create_vm() { vboxwrapper_msg_prefix(buf, sizeof(buf)), memory_size_mb ); - sprintf(buf, "%f", memory_size_mb); + sprintf(buf, "%d", (int)memory_size_mb); command = "modifyvm \"" + vm_name + "\" "; command += "--memory " + string(buf) + " "; @@ -1698,9 +1706,60 @@ bool VBOX_VM::is_extpack_installed() { return false; } -int VBOX_VM::get_install_directory(string& install_directory ) { +int VBOX_VM::get_install_directory(string& 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 + ); + + install_directory = lpszRegistryValue; + } + } + + if (hkSetupHive) RegCloseKey(hkSetupHive); + if (lpszRegistryValue) free(lpszRegistryValue); + if (install_directory.empty()) { + return ERR_FREAD; + } + return BOINC_SUCCESS; +#else install_directory = ""; return 0; +#endif } int VBOX_VM::get_version_information(string& version) { @@ -2090,617 +2149,4 @@ void VBOX_VM::reset_vm_process_priority() { #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_VM::launch_vboxsvc() { - APP_INIT_DATA aid; - PROC_MAP pm; - PROCINFO p; - string command; - int retval = ERR_EXEC; - -#ifdef _WIN32 - 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)) - ); - } - - vbm_trace(command, std::string(""), retval); - } - } - } -#endif - - 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; - - // Construct the command line parameters - // - if (headless) { - argv[0] = const_cast<char*>("VboxHeadless.exe"); - } else { - argv[0] = const_cast<char*>("VirtualBox.exe"); - } - argv[1] = const_cast<char*>("--startvm"); - argv[2] = const_cast<char*>(vm_name.c_str()); - if (headless) { - argv[3] = const_cast<char*>("--vrde config"); - } else { - argv[3] = const_cast<char*>("--no-startvm-errormsgbox"); - } - argv[4] = NULL; - argc = 4; - - strcpy(cmdline, ""); - for (int i=0; i<argc; i++) { - strcat(cmdline, argv[i]); - if (i<argc-1) { - strcat(cmdline, " "); - } - } - -#ifdef _WIN32 - 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)); - - 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, - cmdline, - NULL, - NULL, - TRUE, - CREATE_NO_WINDOW, - NULL, - NULL, - &si, - &pi - )) { - vboxwrapper_msg_prefix(buf, sizeof(buf)); - fprintf( - stderr, - "%s Status Report: Launching virtualbox.exe/vboxheadless.exe failed!.\n" - "%s Error: %s (%d)\n", - buf, - buf, - windows_format_error_string(GetLastError(), error_msg, sizeof(error_msg)), - GetLastError() - ); - - goto CLEANUP; - } - - while(1) { - GetExitCodeProcess(pi.hProcess, &ulExitCode); - - // Copy stdout/stderr to output buffer, handle in the loop so that we can - // copy the pipe as it is populated and prevent the child process from blocking - // in case the output is bigger than pipe buffer. - PeekNamedPipe(hReadPipe, NULL, NULL, NULL, &dwCount, NULL); - if (dwCount) { - pBuf = malloc(dwCount+1); - memset(pBuf, 0, dwCount+1); - - if (ReadFile(hReadPipe, pBuf, dwCount, &dwCount, NULL)) { - output += (char*)pBuf; - } - - free(pBuf); - } - - if ((ulExitCode != STILL_ACTIVE) || (ulExitTimeout >= 1000)) break; - - Sleep(250); - ulExitTimeout += 250; - } - - if (ulExitCode != STILL_ACTIVE) { - 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_VM::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_VM::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; - - // Launch vboxsvc in case it was shutdown for being idle - launch_vboxsvc(); - - // 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_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_vboxmanage.h b/samples/vboxwrapper/vbox_vboxmanage.h index 0810b5dc32661d0db5fe64924ec0ceb56012dd47..330feb5bda792de08912cff0bb9865ad67a9476e 100644 --- a/samples/vboxwrapper/vbox_vboxmanage.h +++ b/samples/vboxwrapper/vbox_vboxmanage.h @@ -49,7 +49,7 @@ namespace vboxmanage { bool is_system_ready(std::string& message); bool is_disk_image_registered(); bool is_extpack_installed(); - static bool is_virtualbox_installed(); + bool is_virtualbox_installed(); int get_install_directory(std::string& dir); int get_version_information(std::string& version); @@ -67,23 +67,6 @@ namespace vboxmanage { void lower_vm_process_priority(); void reset_vm_process_priority(); - - int launch_vboxsvc(); - int launch_vboxvm(); - - 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 - ); - int vbm_popen_raw( - std::string& command, std::string& output, unsigned int timeout - ); - void vbm_replay( - std::string& command - ); - void vbm_trace( - std::string& command, std::string& ouput, int retval - ); - }; }; diff --git a/samples/vboxwrapper/vboxwrapper.cpp b/samples/vboxwrapper/vboxwrapper.cpp index 14712b164a8b1d7f4700152aaf8906efd555cef0..0e05cf6faeb56e3c3b14be26cc360eefdbd18bf6 100644 --- a/samples/vboxwrapper/vboxwrapper.cpp +++ b/samples/vboxwrapper/vboxwrapper.cpp @@ -39,11 +39,11 @@ // - a bunch of other stuff; see the wiki page // // Contributors: +// Rom Walton +// David Anderson // Andrew J. Younge (ajy4490 AT umiacs DOT umd DOT edu) // Jie Wu <jiewu AT cern DOT ch> // Daniel Lombraña González <teleyinex AT gmail DOT com> -// Rom Walton -// David Anderson #ifdef _WIN32 #include "boinc_win.h" @@ -616,11 +616,11 @@ void check_trickle_period() { } int main(int argc, char** argv) { - int retval; + int retval = 0; int loop_iteration = 0; BOINC_OPTIONS boinc_options; APP_INIT_DATA aid; - VBOX_VM* pVM; + VBOX_VM* pVM = NULL; double random_checkpoint_factor = 0; double fraction_done = 0; double current_cpu_time = 0; @@ -653,6 +653,7 @@ int main(int argc, char** argv) { boinc_options.handle_process_control = true; boinc_init_options(&boinc_options); + // Prepare environment for detecting system conditions // boinc_get_init_data_p(&aid); @@ -691,11 +692,30 @@ int main(int argc, char** argv) { #ifdef _WIN32 - if (vbox42::VBOX_VM::is_virtualbox_installed()) { - pVM = (VBOX_VM*) new vbox42::VBOX_VM(); - } else if (vbox43::VBOX_VM::is_virtualbox_installed()) { - pVM = (VBOX_VM*) new vbox43::VBOX_VM(); - } else { + // Determine what version of VirtualBox we are using via the registry. Use a + // namespace specific version of the function because VirtualBox has been known + // to change the registry location from time to time. + // + // NOTE: We cannot use COM to automatically detect which interfaces are installed + // on the machine because it will attempt to launch the 'vboxsvc' process + // without out environment variable changes and muck everything up. + // + string vbox_version; + int vbox_major = 0, vbox_minor = 0; + + if (BOINC_SUCCESS != vbox42::VBOX_VM::get_version_information(vbox_version)) { + vbox43::VBOX_VM::get_version_information(vbox_version); + } + if (!vbox_version.empty()) { + sscanf(vbox_version.c_str(), "%d.%d", &vbox_major, &vbox_minor); + if ((4 == vbox_major) && (2 == vbox_minor)) { + pVM = (VBOX_VM*) new vbox42::VBOX_VM(); + } + if ((4 == vbox_major) && (3 == vbox_minor)) { + pVM = (VBOX_VM*) new vbox43::VBOX_VM(); + } + } + if (!pVM) { pVM = (VBOX_VM*) new vboxmanage::VBOX_VM(); } #else