Ejemplo n.º 1
0
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
}
Ejemplo n.º 2
0
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
}