int main(int argc, char** argv) { int retval; BOINC_OPTIONS boinc_options; VBOX_VM vm; APP_INIT_DATA aid; double elapsed_time = 0; double trickle_period = 0; double fraction_done = 0; double checkpoint_cpu_time = 0; double last_status_report_time = 0; double last_trickle_report_time = 0; double stopwatch_time = 0; double stopwatch_endtime = 0; double sleep_time = 0; double bytes_sent = 0; double bytes_received = 0; double ncpus = 0; bool report_vm_pid = false; bool report_net_usage = false; int vm_pid = 0; int vm_image = 0; unsigned long vm_exit_code = 0; string vm_log; string system_log; string message; vector<string> copy_to_shared; char buf[256]; for (int i=1; i<argc; i++) { if (!strcmp(argv[i], "--trickle")) { trickle_period = atof(argv[++i]); } if (!strcmp(argv[i], "--nthreads")) { ncpus = atof(argv[++i]); } if (!strcmp(argv[i], "--vmimage")) { vm_image = atoi(argv[++i]); } if (!strcmp(argv[i], "--register_only")) { vm.register_only = true; } } memset(&boinc_options, 0, sizeof(boinc_options)); boinc_options.main_program = true; boinc_options.check_heartbeat = true; boinc_options.handle_process_control = true; if (trickle_period > 0.0) { boinc_options.handle_trickle_ups = true; } boinc_init_options(&boinc_options); // Prepare environment for detecting system conditions // boinc_get_init_data_p(&aid); // Log banner // fprintf( stderr, "%s vboxwrapper: starting\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); // Log important information // #if defined(_WIN32) && defined(USE_WINSOCK) WSADATA wsdata; retval = WSAStartup( MAKEWORD( 1, 1 ), &wsdata); if (retval) { fprintf( stderr, "%s can't initialize winsock: %d\n", vboxwrapper_msg_prefix(buf, sizeof(buf)), retval ); boinc_finish(retval); } #endif if (trickle_period > 0.0) { fprintf( stderr, "%s Feature: Enabling trickle-ups (Interval: %f)\n", vboxwrapper_msg_prefix(buf, sizeof(buf)), trickle_period ); } // Check for architecture incompatibilities // #if defined(_WIN32) && defined(_M_IX86) if (strstr(aid.host_info.os_version, "x64")) { fprintf( stderr, "%s 64-bit version of BOINC is required, please upgrade, telling BOINC to reschedule execution for a later date.\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); boinc_temporary_exit(86400, "Architecture incompatibility detected."); } #endif // Initialize VM Hypervisor // retval = vm.initialize(); if (retval) { fprintf( stderr, "%s couldn't detect VM Hypervisor, telling BOINC to reschedule execution for a later date.\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); boinc_temporary_exit(86400, "Detection of VM Hypervisor failed."); } // Record what version of VirtualBox was used. // if (!vm.virtualbox_version.empty()) { fprintf( stderr, "%s Detected: %s\n", vboxwrapper_msg_prefix(buf, sizeof(buf)), vm.virtualbox_version.c_str() ); } // Record if anonymous platform was used. // if (boinc_file_exists((std::string(aid.project_dir) + std::string("/app_info.xml")).c_str())) { fprintf( stderr, "%s Detected: Anonymous Platform Enabled\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); } // Record if the sandboxed configuration is going to be used. // if (aid.using_sandbox) { fprintf( stderr, "%s Detected: Sandbox Configuration Enabled\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); } // Check against known incompatible versions of VirtualBox. // NOTE: Incompatible in this case means that VirtualBox 4.2.6 crashes during snapshot operations // and 4.2.18 fails to restore from snapshots properly. // if ((vm.virtualbox_version.find("4.2.6") != std::string::npos) || (vm.virtualbox_version.find("4.2.18") != std::string::npos) || (vm.virtualbox_version.find("4.3.0") != std::string::npos) ) { fprintf( stderr, "%s Incompatible version of VirtualBox detected. Please upgrade to a later version.\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); boinc_temporary_exit(86400, "Incompatible version of VirtualBox detected."); } // Check to see if the system is in a state in which we expect to be able to run // VirtualBox successfully. Sometimes the system is in a wierd state after a // reboot and the system needs a little bit of time. // if (!vm.is_system_ready(message)) { fprintf( stderr, "%s couldn't communicate with VM Hypervisor, telling BOINC to reschedule execution for a later date.\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); boinc_temporary_exit(300, message.c_str()); } // Parse Job File // retval = parse_job_file(vm, copy_to_shared); if (retval) { fprintf( stderr, "%s can't parse job file: %d\n", vboxwrapper_msg_prefix(buf, sizeof(buf)), retval ); boinc_finish(retval); } // Validate whatever configuration options we can // if (vm.enable_shared_directory) { if (boinc_file_exists("shared")) { if (!is_dir("shared")) { fprintf( stderr, "%s 'shared' exists but is not a directory.\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); } } else { retval = boinc_mkdir("shared"); if (retval) { fprintf(stderr, "%s couldn't created shared directory: %s.\n", vboxwrapper_msg_prefix(buf, sizeof(buf)), boincerror(retval) ); } } } // Copy files to the shared directory // if (vm.enable_shared_directory && copy_to_shared.size()) { for (vector<string>::iterator iter = copy_to_shared.begin(); iter != copy_to_shared.end(); iter++) { string source = *iter; string destination = string("shared/") + *iter; if (!boinc_file_exists(destination.c_str())) { if (!boinc_copy(source.c_str(), destination.c_str())) { fprintf(stderr, "%s successfully copied '%s' to the shared directory.\n", vboxwrapper_msg_prefix(buf, sizeof(buf)), source.c_str() ); } else { fprintf(stderr, "%s failed to copy '%s' to the shared directory.\n", vboxwrapper_msg_prefix(buf, sizeof(buf)), source.c_str() ); } } } } // Configure Instance specific VM Parameters // vm.vm_master_name = "boinc_"; vm.image_filename = IMAGE_FILENAME_COMPLETE; if (boinc_is_standalone()) { vm.vm_master_name += "standalone"; vm.vm_master_description = "standalone"; if (vm.enable_floppyio) { sprintf(buf, "%s.%s", FLOPPY_IMAGE_FILENAME, FLOPPY_IMAGE_FILENAME_EXTENSION); vm.floppy_image_filename = buf; } } else { vm.vm_master_name += md5_string(std::string(aid.result_name)).substr(0, 16); vm.vm_master_description = aid.result_name; if (vm_image) { sprintf(buf, "%s_%d.%s", IMAGE_FILENAME, vm_image, IMAGE_FILENAME_EXTENSION); vm.image_filename = buf; } if (vm.enable_floppyio) { sprintf(buf, "%s_%d.%s", FLOPPY_IMAGE_FILENAME, aid.slot, FLOPPY_IMAGE_FILENAME_EXTENSION); vm.floppy_image_filename = buf; } } if (aid.ncpus > 1.0 || ncpus > 1.0) { if (ncpus) { sprintf(buf, "%d", (int)ceil(ncpus)); } else { sprintf(buf, "%d", (int)ceil(aid.ncpus)); } vm.vm_cpu_count = buf; } else { vm.vm_cpu_count = "1"; } if (aid.vbox_window && !aid.using_sandbox) { vm.headless = false; } // Restore from checkpoint // read_checkpoint(checkpoint_cpu_time, vm); elapsed_time = checkpoint_cpu_time; // Should we even try to start things up? // if (vm.job_duration && (elapsed_time > vm.job_duration)) { return EXIT_TIME_LIMIT_EXCEEDED; } retval = vm.run(elapsed_time); if (retval) { // All failure to start error are unrecoverable by default bool unrecoverable_error = true; char* temp_reason = (char*)""; int temp_delay = 300; // Get logs before cleanup vm.get_system_log(system_log); vm.get_vm_log(vm_log); // Attempt to cleanup the VM vm.cleanup(); write_checkpoint(elapsed_time, vm); fprintf( stderr, "%s VM failed to start.\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); if ((vm_log.find("VERR_VMX_MSR_LOCKED_OR_DISABLED") != string::npos) || (vm_log.find("VERR_SVM_DISABLED") != string::npos)) { fprintf( stderr, "%s NOTE: BOINC has detected that your computer's processor supports hardware acceleration for\n" " virtual machines but the hypervisor failed to successfully launch with this feature enabled.\n" " This means that the hardware acceleration feature has been disabled in the computer's BIOS.\n" " Please enable this feature in your computer's BIOS.\n" " Intel calls it 'VT-x'\n" " AMD calls it 'AMD-V'\n" " More information can be found here: http://en.wikipedia.org/wiki/X86_virtualization\n" " Error Code: ERR_CPU_VM_EXTENSIONS_DISABLED\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); } else if ((vm_log.find("VERR_VMX_IN_VMX_ROOT_MODE") != string::npos) || (vm_log.find("VERR_SVM_IN_USE") != string::npos)) { fprintf( stderr, "%s NOTE: VirtualBox hypervisor reports that another hypervisor has locked the hardware acceleration\n" " for virtual machines feature in exclusive mode. You'll either need to reconfigure the other hypervisor\n" " to not use the feature exclusively or just let BOINC run this project in software emulation mode.\n" " Error Code: ERR_CPU_VM_EXTENSIONS_DISABLED\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); } else if ((vm_log.find("VERR_VMX_NO_VMX") != string::npos) || (vm_log.find("VERR_SVM_NO_SVM") != string::npos)) { fprintf( stderr, "%s NOTE: VirtualBox has reported an improperly configured virtual machine. It was configured to require\n" " hardware acceleration for virtual machines, but your processor does not support the required feature.\n" " Please report this issue to the project so that it can be addresssed.\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); } else if ((vm_log.find("VERR_EM_NO_MEMORY") != string::npos) || (vm_log.find("VERR_NO_MEMORY") != string::npos)) { fprintf( stderr, "%s NOTE: VirtualBox has failed to allocate enough memory to start the configured virtual machine.\n" " This might be a temporary problem and so this job will be rescheduled for another time.\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); unrecoverable_error = false; temp_reason = (char*)"VM Hypervisor was unable to allocate enough memory to start VM."; } else { fprintf( stderr, "%s Hypervisor System Log:\n\n" "%s\n" "%s VM Execution Log:\n\n" "%s\n", vboxwrapper_msg_prefix(buf, sizeof(buf)), system_log.c_str(), vboxwrapper_msg_prefix(buf, sizeof(buf)), vm_log.c_str() ); } if (unrecoverable_error) { boinc_finish(retval); } else { boinc_temporary_exit(temp_delay, temp_reason); } } set_floppy_image(aid, vm); set_port_forwarding_info(aid, vm); set_remote_desktop_info(aid, vm); set_throttles(aid, vm); write_checkpoint(elapsed_time, vm); while (1) { // Begin stopwatch timer stopwatch_time = dtime(); // Discover the VM's current state vm.poll(); vm.get_vm_log(vm_log); if (boinc_status.no_heartbeat || boinc_status.quit_request) { vm.reset_vm_process_priority(); vm.poweroff(); boinc_temporary_exit(300); } if (boinc_status.abort_request) { vm.reset_vm_process_priority(); vm.cleanup(); boinc_finish(EXIT_ABORTED_BY_CLIENT); } if (!vm.online) { if (vm.crashed || (elapsed_time < vm.job_duration)) { vm.get_system_log(system_log); vm.get_vm_exit_code(vm_exit_code); } // Is this a type of event we can recover from? if ((vm_log.find("VERR_EM_NO_MEMORY") != std::string::npos) || (vm_log.find("VERR_NO_MEMORY") != std::string::npos)) { fprintf( stderr, "%s NOTE: VirtualBox has failed to allocate enough memory to continue.\n" " This might be a temporary problem and so this job will be rescheduled for another time.\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); vm.reset_vm_process_priority(); vm.poweroff(); boinc_temporary_exit(300, "VM Hypervisor was unable to allocate enough memory."); } else { vm.cleanup(); if (vm.crashed || (elapsed_time < vm.job_duration)) { fprintf( stderr, "%s VM Premature Shutdown Detected.\n" " Hypervisor System Log:\n\n" "%s\n" " VM Execution Log:\n\n" "%s\n" " VM Exit Code: %d (0x%x)\n\n", vboxwrapper_msg_prefix(buf, sizeof(buf)), system_log.c_str(), vm_log.c_str(), (unsigned int)vm_exit_code, (unsigned int)vm_exit_code ); if (vm_exit_code) { boinc_finish(vm_exit_code); } else { boinc_finish(EXIT_ABORTED_BY_CLIENT); } } else { fprintf( stderr, "%s Virtual machine exited.\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); boinc_finish(0); } } } else { // Check to see if the guest VM has any log messages that indicate that we need need // to take action. if (vm_log.find("EXIT_OUT_OF_MEMORY") != std::string::npos) { fprintf( stderr, "%s ERROR: VM reports there is not enough memory to finish the task.\n\n", " VM Execution Log:\n\n" "%s\n", vboxwrapper_msg_prefix(buf, sizeof(buf)), vm_log.c_str() ); vm.reset_vm_process_priority(); vm.poweroff(); boinc_finish(EXIT_OUT_OF_MEMORY); } } if (boinc_status.suspended) { if (!vm.suspended) { vm.pause(); } } else { if (vm.suspended) { vm.resume(); } if (!vm_pid) { vm.get_vm_process_id(vm_pid); if (vm_pid) { vm.lower_vm_process_priority(); report_vm_pid = true; } } if (boinc_time_to_checkpoint()) { // Only peform a VM checkpoint every ten minutes or so. // if (elapsed_time >= checkpoint_cpu_time + 600.0) { // Basic bookkeeping if (vm.job_duration) { fraction_done = elapsed_time / vm.job_duration; } else if (vm.fraction_done_filename.size() > 0) { read_fraction_done(fraction_done, vm); } if (fraction_done > 1.0) { fraction_done = 1.0; } if ((elapsed_time - last_status_report_time) >= 6000.0) { last_status_report_time = elapsed_time; if (aid.global_prefs.daily_xfer_limit_mb) { fprintf( stderr, "%s Status Report: Job Duration: '%f', Elapsed Time: '%f', Network Bytes Sent (Total): '%f', Network Bytes Received (Total): '%f'\n", vboxwrapper_msg_prefix(buf, sizeof(buf)), vm.job_duration, elapsed_time, bytes_sent, bytes_received ); } else { fprintf( stderr, "%s Status Report: Job Duration: '%f', Elapsed Time: '%f'\n", vboxwrapper_msg_prefix(buf, sizeof(buf)), vm.job_duration, elapsed_time ); } } // Checkpoint if (!vm.createsnapshot(elapsed_time)) { checkpoint_cpu_time = elapsed_time; write_checkpoint(checkpoint_cpu_time, vm); boinc_report_app_status( elapsed_time, checkpoint_cpu_time, fraction_done ); boinc_checkpoint_completed(); } } } if (report_vm_pid || report_net_usage) { retval = boinc_report_app_status_aux( elapsed_time, checkpoint_cpu_time, fraction_done, vm_pid, bytes_sent, bytes_received ); if (!retval) { report_vm_pid = false; report_net_usage = false; } } if (trickle_period) { if ((elapsed_time - last_trickle_report_time) >= trickle_period) { fprintf( stderr, "%s Status Report: Trickle-Up Event.\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); last_trickle_report_time = elapsed_time; sprintf(buf, "<cpu_time>%f</cpu_time>", last_trickle_report_time); retval = boinc_send_trickle_up(const_cast<char*>("cpu_time"), buf); if (retval) { fprintf( stderr, "%s Sending Trickle-Up Event failed (%d).\n", vboxwrapper_msg_prefix(buf, sizeof(buf)), retval ); } } } if (boinc_status.reread_init_data_file) { boinc_status.reread_init_data_file = false; fprintf( stderr, "%s Preference change detected\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); boinc_parse_init_data_file(); boinc_get_init_data_p(&aid); set_throttles(aid, vm); } // if the VM has a maximum amount of time it is allowed to run, // shut it down gacefully and exit. // if (vm.job_duration && (elapsed_time > vm.job_duration)) { vm.cleanup(); if (vm.enable_cern_dataformat) { FILE* output = fopen("output", "w"); if (output) { fprintf( output, "Work Unit completed!\n" ); fclose(output); } } boinc_finish(0); } } if (vm.enable_network) { if (boinc_status.network_suspended) { if (!vm.network_suspended) { vm.set_network_access(false); } } else { if (vm.network_suspended) { vm.set_network_access(true); } } } // report network usage every 10 min so the client can enforce quota // static double net_usage_timer=600; if (aid.global_prefs.daily_xfer_limit_mb && vm.enable_network && !vm.suspended ) { net_usage_timer -= POLL_PERIOD; if (net_usage_timer <= 0) { net_usage_timer = 600; double sent, received; retval = vm.get_network_bytes_sent(sent); if (!retval && (sent != bytes_sent)) { bytes_sent = sent; report_net_usage = true; } retval = vm.get_network_bytes_received(received); if (!retval && (received != bytes_received)) { bytes_received = received; report_net_usage = true; } } } stopwatch_endtime = dtime(); // Sleep for the remainder of the polling period sleep_time = POLL_PERIOD - (stopwatch_endtime - stopwatch_time); if (sleep_time > 0) { boinc_sleep(sleep_time); } // Calculate the elapsed time after all potiential commands have been executed // and base it off of wall clock time instead of a fixed interval. if (!boinc_status.suspended) { if (sleep_time > 0) { elapsed_time += POLL_PERIOD; } else { elapsed_time += stopwatch_endtime - stopwatch_time; } } } #if defined(_WIN32) && defined(USE_WINSOCK) WSACleanup(); #endif }
int main(int argc, char** argv) { int retval; BOINC_OPTIONS boinc_options; VBOX_VM vm; APP_INIT_DATA aid; double elapsed_time = 0; double trickle_period = 0; double trickle_cpu_time = 0; double fraction_done = 0; double checkpoint_cpu_time = 0; double last_status_report_time = 0; double stopwatch_time = 0; double sleep_time = 0; double bytes_sent = 0; double bytes_received = 0; double ncpus = 0; bool report_vm_pid = false; bool report_net_usage = false; int vm_pid = 0; unsigned long vm_exit_code = 0; std::string vm_log; std::string system_log; char buf[256]; memset(&boinc_options, 0, sizeof(boinc_options)); boinc_options.main_program = true; boinc_options.check_heartbeat = true; boinc_options.handle_process_control = true; boinc_init_options(&boinc_options); for (int i=1; i<argc; i++) { if (!strcmp(argv[i], "--trickle")) { trickle_period = atof(argv[++i]); } if (!strcmp(argv[i], "--nthreads")) { ncpus = atof(argv[++i]); } if (!strcmp(argv[i], "--register_only")) { vm.register_only = true; } } fprintf( stderr, "%s vboxwrapper: starting\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); #if defined(_WIN32) && defined(USE_WINSOCK) WSADATA wsdata; retval = WSAStartup( MAKEWORD( 1, 1 ), &wsdata); if (retval) { fprintf( stderr, "%s can't initialize winsock: %d\n", vboxwrapper_msg_prefix(buf, sizeof(buf)), retval ); boinc_finish(retval); } #endif retval = parse_job_file(vm); if (retval) { fprintf( stderr, "%s can't parse job file: %d\n", vboxwrapper_msg_prefix(buf, sizeof(buf)), retval ); boinc_finish(retval); } // Validate whatever configuration options we can // if (vm.enable_shared_directory) { if (boinc_file_exists("shared")) { if (!is_dir("shared")) { fprintf( stderr, "%s 'shared' exists but is not a directory.\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); } } else { retval = boinc_mkdir("shared"); if (retval) { fprintf(stderr, "%s couldn't created shared directory: %s.\n", vboxwrapper_msg_prefix(buf, sizeof(buf)), boincerror(retval) ); } } } boinc_get_init_data_p(&aid); vm.vm_master_name = "boinc_"; vm.image_filename = IMAGE_FILENAME_COMPLETE; if (boinc_is_standalone()) { vm.vm_master_name += "standalone"; if (vm.enable_floppyio) { sprintf(buf, "%s.%s", FLOPPY_IMAGE_FILENAME, FLOPPY_IMAGE_FILENAME_EXTENSION); vm.floppy_image_filename = buf; } } else { vm.vm_master_name += aid.result_name; if (vm.enable_floppyio) { sprintf(buf, "%s_%d.%s", FLOPPY_IMAGE_FILENAME, aid.slot, FLOPPY_IMAGE_FILENAME_EXTENSION); vm.floppy_image_filename = buf; } } if (aid.ncpus > 1.0 || ncpus > 1.0) { if (ncpus) { sprintf(buf, "%d", (int)ceil(ncpus)); } else { sprintf(buf, "%d", (int)ceil(aid.ncpus)); } vm.vm_cpu_count = buf; } else { vm.vm_cpu_count = "1"; } if (aid.vbox_window && !aid.using_sandbox) { vm.headless = false; } // Restore from checkpoint read_checkpoint(checkpoint_cpu_time, vm); elapsed_time = checkpoint_cpu_time; // Should we even try to start things up? if (vm.job_duration && (elapsed_time > vm.job_duration)) { return EXIT_TIME_LIMIT_EXCEEDED; } retval = vm.run(elapsed_time); if (retval) { // All failure to start error are unrecoverable by default bool unrecoverable_error = true; char* temp_reason = (char*)""; int temp_delay = 300; // Get logs before cleanup vm.get_system_log(system_log); vm.get_vm_log(vm_log); // Attempt to cleanup the VM vm.cleanup(); write_checkpoint(elapsed_time, vm); fprintf( stderr, "%s VM failed to start.\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); if ((vm_log.find("VERR_VMX_MSR_LOCKED_OR_DISABLED") != std::string::npos) || (vm_log.find("VERR_SVM_DISABLED") != std::string::npos)) { fprintf( stderr, "%s NOTE: BOINC has detected that your processor supports hardware acceleration for virtual machines\n" " but the hypervisor failed to successfully launch with this feature enabled. This means that the\n" " hardware acceleration feature has been disabled in the computers BIOS. Please enable this\n" " feature in your BIOS.\n" " Intel Processors call it 'VT-x'\n" " AMD Processors call it 'AMD-V'\n" " More information can be found here: http://en.wikipedia.org/wiki/X86_virtualization\n" " Error Code: ERR_CPU_VM_EXTENSIONS_DISABLED\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); } else if ((vm_log.find("VERR_VMX_IN_VMX_ROOT_MODE") != std::string::npos) || (vm_log.find("VERR_SVM_IN_USE") != std::string::npos)) { fprintf( stderr, "%s NOTE: VirtualBox hypervisor reports that another hypervisor has locked the hardware acceleration\n" " for virtual machines feature in exclusive mode. You'll either need to reconfigure the other hypervisor\n" " to not use the feature exclusively or just let BOINC run this project in software emulation mode.\n" " Error Code: ERR_CPU_VM_EXTENSIONS_DISABLED\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); } else if ((vm_log.find("VERR_VMX_NO_VMX") != std::string::npos) || (vm_log.find("VERR_SVM_NO_SVM") != std::string::npos)) { fprintf( stderr, "%s NOTE: VirtualBox has reported an improperly configured virtual machine. It was configured to require\n" " hardware acceleration for virtual machines, but your processor does not support the required feature.\n" " Please report this issue to the project so that it can be addresssed.\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); } else if ((vm_log.find("VERR_EM_NO_MEMORY") != std::string::npos) || (vm_log.find("VERR_NO_MEMORY") != std::string::npos)) { fprintf( stderr, "%s NOTE: VirtualBox has failed to allocate enough memory to start the configured virtual machine.\n" " This might be a temporary problem and so this job will be rescheduled for another time.\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); unrecoverable_error = false; temp_reason = (char*)"VM Hypervisor was unable to allocate enough memory to start VM."; } else { fprintf( stderr, "%s Hypervisor System Log:\n\n" "%s\n" "%s VM Execution Log:\n\n" "%s\n", vboxwrapper_msg_prefix(buf, sizeof(buf)), system_log.c_str(), vboxwrapper_msg_prefix(buf, sizeof(buf)), vm_log.c_str() ); } if (unrecoverable_error) { boinc_finish(retval); } else { boinc_temporary_exit(temp_delay, temp_reason); } } set_floppy_image(aid, vm); set_port_forwarding_info(aid, vm); set_remote_desktop_info(aid, vm); set_throttles(aid, vm); write_checkpoint(elapsed_time, vm); while (1) { // Begin stopwatch timer stopwatch_time = dtime(); // Discover the VM's current state vm.poll(); if (boinc_status.no_heartbeat || boinc_status.quit_request) { vm.reset_vm_process_priority(); vm.poweroff(); boinc_temporary_exit(300); } if (boinc_status.abort_request) { vm.reset_vm_process_priority(); vm.cleanup(); boinc_finish(EXIT_ABORTED_BY_CLIENT); } if (!vm.online) { if (vm.crashed || (elapsed_time < vm.job_duration)) { vm.get_system_log(system_log); vm.get_vm_log(vm_log); vm.get_vm_exit_code(vm_exit_code); } // Is this a type of event we can recover from? if ((vm_log.find("VERR_EM_NO_MEMORY") != std::string::npos) || (vm_log.find("VERR_NO_MEMORY") != std::string::npos)) { fprintf( stderr, "%s NOTE: VirtualBox has failed to allocate enough memory to continue.\n" " This might be a temporary problem and so this job will be rescheduled for another time.\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); vm.reset_vm_process_priority(); vm.poweroff(); boinc_temporary_exit(300, "VM Hypervisor was unable to allocate enough memory."); } else { vm.cleanup(); if (vm.crashed || (elapsed_time < vm.job_duration)) { fprintf( stderr, "%s VM Premature Shutdown Detected.\n" " Hypervisor System Log:\n\n" "%s\n" " VM Execution Log:\n\n" "%s\n" " VM Exit Code: %d (0x%x)\n\n", vboxwrapper_msg_prefix(buf, sizeof(buf)), system_log.c_str(), vm_log.c_str(), (unsigned int)vm_exit_code, (unsigned int)vm_exit_code ); if (vm_exit_code) { boinc_finish(vm_exit_code); } else { boinc_finish(EXIT_ABORTED_BY_CLIENT); } } else { fprintf( stderr, "%s Virtual machine exited.\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); boinc_finish(0); } } } if (boinc_status.suspended) { if (!vm.suspended) { vm.pause(); } } else { if (vm.suspended) { vm.resume(); } elapsed_time += POLL_PERIOD; if (!vm_pid) { vm.get_vm_process_id(vm_pid); if (vm_pid) { vm.lower_vm_process_priority(); report_vm_pid = true; } } if (boinc_time_to_checkpoint()) { // Only peform a VM checkpoint every ten minutes or so. // if (elapsed_time >= checkpoint_cpu_time + 600.0) { // Basic bookkeeping if (vm.job_duration) { fraction_done = elapsed_time / vm.job_duration; if (fraction_done > 1.0) { fraction_done = 1.0; } } if ((elapsed_time - last_status_report_time) >= 6000.0) { last_status_report_time = elapsed_time; if (aid.global_prefs.daily_xfer_limit_mb) { fprintf( stderr, "%s Status Report: Job Duration: '%f', Elapsed Time: '%f', Network Bytes Sent (Total): '%f', Network Bytes Received (Total): '%f'\n", vboxwrapper_msg_prefix(buf, sizeof(buf)), vm.job_duration, elapsed_time, bytes_sent, bytes_received ); } else { fprintf( stderr, "%s Status Report: Job Duration: '%f', Elapsed Time: '%f'\n", vboxwrapper_msg_prefix(buf, sizeof(buf)), vm.job_duration, elapsed_time ); } } // Checkpoint if (!vm.createsnapshot(elapsed_time, checkpoint_cpu_time)) { checkpoint_cpu_time = elapsed_time; write_checkpoint(checkpoint_cpu_time, vm); boinc_report_app_status( elapsed_time, checkpoint_cpu_time, fraction_done ); boinc_checkpoint_completed(); } } } if (report_vm_pid || report_net_usage) { retval = boinc_report_app_status_aux( elapsed_time, checkpoint_cpu_time, fraction_done, vm_pid, bytes_sent, bytes_received ); if (!retval) { report_vm_pid = false; report_net_usage = false; } } if (trickle_period) { trickle_cpu_time += POLL_PERIOD; if (trickle_cpu_time >= trickle_period) { sprintf(buf, "<cpu_time>%f</cpu_time>", trickle_cpu_time); boinc_send_trickle_up(const_cast<char*>("cpu_time"), buf); trickle_cpu_time = 0; } } if (boinc_status.reread_init_data_file) { boinc_status.reread_init_data_file = false; fprintf( stderr, "%s Preference change detected\n", vboxwrapper_msg_prefix(buf, sizeof(buf)) ); boinc_parse_init_data_file(); boinc_get_init_data_p(&aid); set_throttles(aid, vm); } // if the VM has a maximum amount of time it is allowed to run, // shut it down gacefully and exit. // if (vm.job_duration && (elapsed_time > vm.job_duration)) { vm.cleanup(); if (vm.enable_cern_dataformat) { FILE* output = fopen("output", "w"); if (output) { fprintf( output, "Work Unit completed!\n" ); fclose(output); } } boinc_finish(0); } } if (vm.enable_network) { if (boinc_status.network_suspended) { if (!vm.network_suspended) { vm.set_network_access(false); } } else { if (vm.network_suspended) { vm.set_network_access(true); } } } // report network usage every 10 min so the client can enforce quota // static double net_usage_timer=600; if (aid.global_prefs.daily_xfer_limit_mb && vm.enable_network && !vm.suspended ) { net_usage_timer -= POLL_PERIOD; if (net_usage_timer <= 0) { net_usage_timer = 600; double sent, received; retval = vm.get_network_bytes_sent(sent); if (!retval && (sent != bytes_sent)) { bytes_sent = sent; report_net_usage = true; } retval = vm.get_network_bytes_received(received); if (!retval && (received != bytes_received)) { bytes_received = received; report_net_usage = true; } } } // Sleep for the remainder of the polling period sleep_time = POLL_PERIOD - (dtime() - stopwatch_time); if (sleep_time > 0) { boinc_sleep(sleep_time); } } #if defined(_WIN32) && defined(USE_WINSOCK) WSACleanup(); #endif }