diff --git a/samples/vboxwrapper/vbox_common.cpp b/samples/vboxwrapper/vbox_common.cpp index f8da6411ea..d847ea0cfb 100644 --- a/samples/vboxwrapper/vbox_common.cpp +++ b/samples/vboxwrapper/vbox_common.cpp @@ -1186,13 +1186,25 @@ int VBOX_BASE::vbm_popen(string& command, string& output, const char* item, bool // 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; + if ((output.find("Cannot attach medium") != string::npos) && + (output.find("the media type") != string::npos) && + (output.find("MultiAttach") != string::npos) && + (output.find("can only be attached to machines that were created with VirtualBox 4.0 or later") != string::npos)) { + // VirtualBox occasionally writes the 'MultiAttach' attribute to + // the disk entry in VirtualBox.xml although this is not allowed there. + // As a result all VMs trying to connect that disk fail. + // Report the error back immediately without a retry. + // + break; + } else { + 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; + } } } @@ -1482,4 +1494,3 @@ VBOX_VM::VBOX_VM() { VBOX_VM::~VBOX_VM() { } - diff --git a/samples/vboxwrapper/vbox_vboxmanage.cpp b/samples/vboxwrapper/vbox_vboxmanage.cpp index 1d2f04e1c4..6aef5daae3 100644 --- a/samples/vboxwrapper/vbox_vboxmanage.cpp +++ b/samples/vboxwrapper/vbox_vboxmanage.cpp @@ -16,10 +16,10 @@ // along with BOINC. If not, see . #ifdef _WIN32 -#include #include "boinc_win.h" #include "win_util.h" #else +#include #include #include #include @@ -543,53 +543,147 @@ namespace vboxmanage { // See: https://www.virtualbox.org/manual/ch05.html#hdimagewrites // https://www.virtualbox.org/manual/ch05.html#diffimages // the vdi file downloaded to the projects dir becomes the parent (read only) - // "--setuid" must not be used // each task gets it's own differencing image (writable) // differencing images are written to the VM's snapshot folder // string medium_file = aid.project_dir; medium_file += "/" + multiattach_vdi_file; -#ifdef _WIN32 - replace(medium_file.begin(), medium_file.end(), '\\', '/'); -#endif - vboxlog_msg("Adding virtual disk drive to VM. (%s)", multiattach_vdi_file.c_str()); - command = "list hdds"; - retval = vbm_popen(command, output, "check if parent hdd is registered", false, false); - if (retval) return retval; + int retry_count = 0; + bool log_error = false; + bool vbox_bug_mitigation = false; -#ifdef _WIN32 - replace(output.begin(), output.end(), '\\', '/'); -#endif + do { + string set_new_uuid = ""; + string type_line = ""; + size_t type_start; + size_t type_end; - if (output.find(medium_file) == string::npos) { - // parent hdd is not registered - // vdi files can't be registered and set to multiattach mode within 1 step. - // They must first be attached to a VM in normal mode, then detached from the VM + command = "showhdinfo \"" + medium_file + "\" "; + + retval = vbm_popen(command, output, "check if parent hdd is registered", false); + if (retval) { + // showhdinfo implicitly registers unregistered hdds. + // Hence, this has to be handled first. + // + if ((output.rfind("VBoxManage: error:", 0) != string::npos) && + (output.find("Cannot register the hard disk") != string::npos) && + (output.find("because a hard disk") != string::npos) && + (output.find("with UUID") != string::npos) && + (output.find("already exists") != string::npos)) { + // May happen if the project admin didn't set a new UUID. + set_new_uuid = "--setuuid \"\" "; + + vboxlog_msg("Disk UUID conflicts with an already existing disk.\nWill set a new UUID for '%s'.\nThe project admin should be informed to do this server side running:\nvboxmanage clonemedium \n", + multiattach_vdi_file.c_str() + ); + } else { + // other errors + vboxlog_msg("Error in check if parent hdd is registered.\nCommand:\n%s\nOutput:\n%s", + command.c_str(), + output.c_str() + ); + return retval; + } + } + + // Output from showhdinfo should look a little like this: + // UUID: c119bcaf-636c-41f6-86c9-384739a31339 + // Parent UUID: base + // State: created + // Type: multiattach + // Location: C:\Users\romw\VirtualBox VMs\test2\test2.vdi + // Storage format: VDI + // Format variant: dynamic default + // Capacity: 2048 MBytes + // Size on disk: 2 MBytes + // Encryption: disabled + // Property: AllocationBlockSize=1048576 + // Child UUIDs: dcb0daa5-3bf9-47cb-bfff-c65e74484615 // - command = command_fix_part; - command += "--medium \"" + medium_file + "\" "; - retval = vbm_popen(command, output, "register parent hdd", false, false); - if (retval) return retval; + type_line = output; + transform(type_line.cbegin(), type_line.cend(), + type_line.begin(), [](unsigned char c) { return tolower(c); }); + type_start = type_line.find("\ntype: ") + 1; + type_end = type_line.find("\n", type_start) - type_start; + type_line = type_line.substr(type_start, type_end); - command = command_fix_part; - command += "--medium none "; + if (type_line.find("multiattach") == string::npos) { + // Parent hdd is not (yet) of type multiattach. + // Vdi files can't be registered and set to multiattach mode within 1 step. + // They must first be attached to a VM in normal mode, then detached from the VM - retval = vbm_popen(command, output, "detach parent vdi", false, false); - if (retval) return retval; - // the vdi file is now registered and ready to be attached in multiattach mode - // + command = command_fix_part; + command += set_new_uuid + "--medium \"" + medium_file + "\" "; + + retval = vbm_popen(command, output, "register parent vdi"); + if (retval) return retval; + + command = command_fix_part; + command += "--medium none "; + + retval = vbm_popen(command, output, "detach parent vdi"); + if (retval) return retval; + // the vdi file is now registered and ready to be attached in multiattach mode + // + } + + do { + command = command_fix_part; + command += "--mtype multiattach "; + command += "--medium \"" + medium_file + "\" "; + + retval = vbm_popen(command, output, "storage attach (fixed disk - multiattach mode)", log_error); + if (retval) { + // VirtualBox occasionally writes the 'MultiAttach' attribute to + // the disk entry in VirtualBox.xml although this is not allowed there. + // As a result all VMs trying to connect that disk fail. + // The error needs to be cleaned here to allow vboxwrapper to + // succeed even with uncorrected VirtualBox versions. + // + // After cleanup attaching the disk should be tried again. + + if ((retry_count < 1) && + (output.find("Cannot attach medium") != string::npos) && + (output.find("the media type") != string::npos) && + (output.find("MultiAttach") != string::npos) && + (output.find("can only be attached to machines that were created with VirtualBox 4.0 or later") != string::npos)) { + // try to deregister the medium from the global media store + command = "closemedium \"" + medium_file + "\" "; + + retval = vbm_popen(command, output, "deregister parent vdi"); + if (retval) return retval; + + retry_count++; + log_error = true; + boinc_sleep(1.0); + break; + } + + if (retry_count >= 1) { + // in case of other errors or if retry also failed + vboxlog_msg("Error in storage attach (fixed disk - multiattach mode).\nCommand:\n%s\nOutput:\n%s", + command.c_str(), + output.c_str() + ); + return retval; + } + + retry_count++; + log_error = true; + boinc_sleep(1.0); + + } else { + vbox_bug_mitigation = true; + break; + } + } + while (true); } - - command = command_fix_part; - command += "--mtype multiattach "; - command += "--medium \"" + medium_file + "\" "; - - retval = vbm_popen(command, output, "storage attach (fixed disk - multiattach mode)"); - if (retval) return retval; + while (!vbox_bug_mitigation); }