boinc/samples/vboxwrapper/cernvm/cernvmwrapper.cpp

1198 lines
37 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// This file is part of BOINC.
// http://boinc.berkeley.edu
// Copyright (C) 2008 University of California
//
// BOINC is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License
// as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// BOINC is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
// wrapper.C
// wrapper program for CernVM - lets you use CernVM on BOINC
//
// Handles:
// - suspend/resume/quit/abort virtual machine
//
// Contributor: Jie Wu <jiewu AT cern DOT ch>
//
// Contributor: Daniel Lombraña González <teleyinex AT gmail DOT com>
#include <stdio.h>
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <time.h>
#include <stdlib.h>
#include "zlib.h"
#ifdef _WIN32
#include <windows.h>
#include <tchar.h>
#include <shlwapi.h>
#pragma comment(lib, "shlwapi.lib")
#include <stdio.h>
#include <conio.h>
#include <string.h>
#pragma hdrstop
#include "boinc_win.h"
#include "win_util.h"
#else
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "procinfo.h"
#endif
#include "boinc_api.h"
#include "diagnostics.h"
#include "filesys.h"
#include "parse.h"
#include "str_util.h"
#include "str_replace.h"
#include "util.h"
#include "error_numbers.h"
#define VM_NAME "VMName"
#define CPU_TIME "CpuTime"
#define PROGRESS_FN "ProgressFile"
#define TRICK_PERIOD 45.0*60
#define CHECK_PERIOD 2.0*60
#define POLL_PERIOD 1.0
#define MESSAGE "CPUTIME"
#define YEAR_SECS 365*24*60*60
#define BUFSIZE 4096
using std::string;
struct VM {
string virtual_machine_name;
string disk_name;
string disk_path;
string name_path;
double current_period;
time_t last_poll_point;
bool suspended;
VM();
void create();
void throttle();
void start(bool vrde, bool headless);
void kill();
void pause();
void savestate();
void resume();
void Check();
void remove();
void release(); //release the virtual disk
int send_cputime_message();
void poll();
};
void write_cputime(double);
APP_INIT_DATA aid;
int unzip (const char *infilename, const char *outfilename)
{
gzFile infile = gzopen(infilename, "rb");
FILE *outfile = fopen(outfilename, "wb");
if (!infile || !outfile) return -1;
char buffer[128];
int num_read = 0;
while ((num_read = gzread(infile, buffer, sizeof(buffer))) > 0)
{
fwrite(buffer, 1, num_read, outfile);
}
gzclose(infile);
fclose(outfile);
}
#ifdef _WIN32
bool IsWinNT() //check if we're running NT
{
OSVERSIONINFO osv;
osv.dwOSVersionInfoSize = sizeof(osv);
GetVersionEx(&osv);
return (osv.dwPlatformId == VER_PLATFORM_WIN32_NT);
}
#endif
bool vbm_popen(string arg_list,
char * buffer=NULL,
int nSize=1024,
string command="VBoxManage -q "){
//when buffer is NULL, this function will not return the input of new process.
//Otherwise, it will not redirect the input of new process to buffer
#ifdef _WIN32
STARTUPINFO si;
SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR sd; //security information for pipes
PROCESS_INFORMATION pi;
HANDLE newstdout,read_stdout; //pipe handles
unsigned long exit=0;
if(buffer!=0){
if (IsWinNT()) //initialize security descriptor (Windows NT)
{
InitializeSecurityDescriptor(&sd,SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, true, NULL, false);
sa.lpSecurityDescriptor = &sd;
}
else sa.lpSecurityDescriptor = NULL;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = true; //allow inheritable handles
if (!CreatePipe(&read_stdout,&newstdout,&sa,0)) //create stdout pipe
{
fprintf(stderr,"CreatePipe Failed\n");
CloseHandle(newstdout);
CloseHandle(read_stdout);
return false;
}
}
GetStartupInfo(&si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
if(buffer!=NULL){
si.dwFlags = STARTF_USESTDHANDLES|si.dwFlags;
si.hStdOutput = newstdout;
si.hStdError = newstdout; //set the new handles for the child process
si.hStdInput = NULL;
}
command+=arg_list;
if (!CreateProcess( NULL,
(LPTSTR)command.c_str(),
NULL,
NULL,
TRUE,
CREATE_NO_WINDOW,
NULL,
NULL,
&si,
&pi))
{
fprintf(stderr,"CreateProcess Failed!");
if(buffer!=NULL){
CloseHandle(newstdout);
CloseHandle(read_stdout);
}
return false;
}
//while(1)
//{
// GetExitCodeProcess(pi.hProcess,&exit); //while the process is running
// if (exit != STILL_ACTIVE)
// break;
//}
// Wait until process exists.
WaitForSingleObject( pi.hProcess, INFINITE );
// Close process and thread handles.
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
if(buffer!=NULL){
memset(buffer,0,nSize);
DWORD bread;
BOOL bSuccess = false;
for (;;)
{
ReadFile(read_stdout,buffer,nSize-1,&bread,NULL);
if ( ! bSuccess || bread == 0 ) break;
}
// buffer[bread]=0;
CloseHandle(newstdout);
CloseHandle(read_stdout);
}
if(exit==0)
return true;
else
return false;
#else //linux
FILE* fp;
char temp[256];
string strTemp="";
command+=arg_list;
if(buffer==NULL){
if(!system(command.c_str()))
return true;
else return false;
}
fp = popen(command.c_str(),"r");
if (fp == NULL){
fprintf(stderr,"ERROR: vbm_popen popen failed!\n");
return false;
}
memset(buffer,0,nSize);
while (fgets(temp,256,fp) != NULL){
strTemp+=temp;
}
pclose(fp);
strncpy(buffer,strTemp.c_str(),nSize-1);
return true;
#endif
}
VM::VM(){
char buffer[256];
virtual_machine_name="";
current_period=0;
suspended=false;
last_poll_point=0;
//boinc_resolve_filename_s("cernvm.vmdk.gz",disk_path);
// fprintf(stderr,"%s\n",disk_path.c_str());
// relative_to_absolute(buffer,(char*)disk_path.c_str());
boinc_getcwd(buffer);
disk_name = "cernvm.vmdk";
disk_path = "cernvm.vmdk";
disk_path="/"+disk_path;
disk_path=buffer+disk_path;
disk_path="\""+disk_path+"\"";
// disk_path=buffer;
// fprintf(stderr, "%s\n",disk_path.c_str());
name_path="";
//boinc_get_init_data(aid);
//name_path += aid.project_dir;
//name_path += "/";
name_path += VM_NAME;
}
void VM::create() {
time_t rawtime;
string arg_list;
char buffer[256];
FILE* fp;
//rawtime=time(NULL);
//strftime ( buffer, 256, "%Y%m%d%H%M%S", localtime (&rawtime) );
//virtual_machine_name="";
//virtual_machine_name += "BOINC_VM_";
//virtual_machine_name += buffer;
//
//First release old virtual disks
//release();
//createvm
arg_list="";
arg_list="createvm --name "+virtual_machine_name+ \
" --ostype Linux26 --register";
if(!vbm_popen(arg_list)){
fprintf(stderr,"ERROR: Create VM method: createvm failed!\n");
fprintf(stderr,"ERROR: %s\n",arg_list.c_str());
fprintf(stderr,"INFO: Cleaning registered VM from a failure...\n");
remove();
fprintf(stderr,"INFO: createvm() Aborting\n");
boinc_finish(1);
}
//modifyvm
arg_list="";
arg_list="modifyvm "+virtual_machine_name+ \
" --memory 256 --acpi on --ioapic on \
--boot1 disk --boot2 none --boot3 none --boot4 none \
--nic1 nat \
--natdnsproxy1 on";
//CernVM BOINC version doesn't need hostonly network interface
/*
#ifdef _WIN32
arg_list+="--nic2 hostonly --hostonlyadapter2 \"VirtualBox Host-Only Ethernet Adapter\"";
#else
arg_list+="--nic2 hostonly --hostonlyadapter2 \"vboxnet0\"";
#endif
*/
vbm_popen(arg_list);
//storagectl
arg_list="";
arg_list="storagectl "+virtual_machine_name+ \
" --name \"IDE Controller\" --add ide --controller PIIX4";
vbm_popen(arg_list);
//openmedium
// arg_list="";
// arg_list="openmedium disk "+disk_path;
// vbm_popen(arg_list);
//storageattach
arg_list="";
arg_list="storageattach "+virtual_machine_name+ \
" --storagectl \"IDE Controller\" \
--port 0 --device 0 --type hdd --medium " \
+disk_path;
if(!vbm_popen(arg_list)){
fprintf(stderr,"ERROR: Create storageattach failed!\n");
fprintf(stderr,"ERROR: %s\n",arg_list.c_str());
fprintf(stderr,"INFO: storageattach() Aborting\n");
//DEBUG for knowing which filename is being used
//fprintf(stderr,disk_path.c_str());
//fprintf(stderr,"\n");
remove();
boinc_finish(1);
exit(0);
}
// Write down the name of the virtual machine in a file called VM_NAME
if((fp=fopen(name_path.c_str(),"w"))==NULL){
fprintf(stderr,"ERROR: Saving VM name failed. Details: fopen failed!\n");
fprintf(stderr,"INFO: VM_NAME Aborting\n");
boinc_finish(1);
}
fputs(virtual_machine_name.c_str(),fp);
fclose(fp);
}
void VM::throttle()
{
// Check the BOINC CPU preferences for running the VM accordingly
string arg_list = "";
boinc_get_init_data(aid);
double max_vm_cpu_pct = 100.0;
if (aid.project_preferences)
{
if (!aid.project_preferences) return;
if (parse_double(aid.project_preferences, "<max_vm_cpu_pct>", max_vm_cpu_pct))
{
fprintf(stderr,"INFO: Maximum usage of CPU: %f\n", max_vm_cpu_pct);
fprintf(stderr,"INFO: Setting how much CPU time the virtual CPU can use: %i\n", int(max_vm_cpu_pct));
std::stringstream out;
out << int(max_vm_cpu_pct);
arg_list = " controlvm " + virtual_machine_name + " cpuexecutioncap " + out.str();
if (!vbm_popen(arg_list))
{
fprintf(stderr,"ERROR: Impossible to set up CPU percentage usage limit\n");
}
else
{
fprintf(stderr,"INFO: Success!\n");
}
}
}
}
void VM::start(bool vrde=false, bool headless=false) {
// Start the VM in headless mode
boinc_begin_critical_section();
string arg_list="";
if (headless) arg_list=" startvm "+ virtual_machine_name + " --type headless";
else arg_list = " startvm "+ virtual_machine_name;
if (!vbm_popen(arg_list))
{
fprintf(stderr,"ERROR: Impossible to start the VM\n");
fprintf(stderr,"ERROR: %s\n",arg_list.c_str());
fprintf(stderr,"INFO: Removing VM...\n");
remove();
boinc_end_critical_section();
boinc_finish(1);
}
// Enable or disable VRDP for the VM: (by default is disabled)
if (vrde)
{
arg_list = "";
arg_list = " controlvm " + virtual_machine_name + " vrde on";
}
else
{
arg_list = "";
arg_list = " controlvm " + virtual_machine_name + " vrde off";
}
vbm_popen(arg_list);
// If not running in Headless mode, don't allow the user to save, shutdown, power off or restore the VM
if (!headless)
{
arg_list = "";
// Don't allow the user to save, shutdown, power off or restore the VM
arg_list = " setextradata " + virtual_machine_name + " GUI/RestrictedCloseActions SaveState,Shutdown,PowerOff,Restore";
vbm_popen(arg_list);
}
throttle();
boinc_end_critical_section();
}
void VM::kill() {
boinc_begin_critical_section();
string arg_list="";
arg_list="controlvm "+virtual_machine_name+" poweroff";
vbm_popen(arg_list);
boinc_end_critical_section();
}
void VM::pause() {
boinc_begin_critical_section();
time_t current_time;
string arg_list="";
arg_list="controlvm "+virtual_machine_name+" pause";
if(vbm_popen(arg_list)) {
suspended = true;
current_time=time(NULL);
current_period += difftime (current_time,last_poll_point);
}
boinc_end_critical_section();
}
void VM::resume() {
boinc_begin_critical_section();
string arg_list="";
arg_list="controlvm "+virtual_machine_name+" resume";
if(vbm_popen(arg_list)) {
suspended = false;
last_poll_point=time(NULL);
}
boinc_end_critical_section();
}
void VM::Check(){
boinc_begin_critical_section();
string arg_list="";
if(suspended){
arg_list="controlvm "+virtual_machine_name+" resume";
vbm_popen(arg_list);
}
arg_list="controlvm "+virtual_machine_name+" savestate";
vbm_popen(arg_list);
boinc_end_critical_section();
}
void VM::savestate()
{
boinc_begin_critical_section();
string arg_list = "";
arg_list = "controlvm " + virtual_machine_name + " savestate";
if (!vbm_popen(arg_list))
{
fprintf(stderr,"ERROR: The VM could not be saved.\n");
}
boinc_end_critical_section();
}
void VM::remove(){
boinc_begin_critical_section();
string arg_list="",vminfo, vboxfolder, vboxXML, vboxXMLNew, vmfolder, vmdisk, line;
char * env;
char buffer[4096];
size_t found_init, found_end;
FILE * fp;
bool vmRegistered = false;
arg_list = "";
arg_list = " discardstate " + virtual_machine_name;
if (vbm_popen(arg_list)) fprintf(stderr,"INFO: VM state discarded!\n");
else fprintf(stderr,"WARNING: it was not possible to discard the state of the VM.\n");
// Unregistervm command with --delete option. VBox 4.1 should work well
arg_list = "";
arg_list = " unregistervm " + virtual_machine_name + " --delete";
if (vbm_popen(arg_list)) fprintf(stderr, "INFO: VM unregistered and deleted via VBoxManage.\n");
else fprintf(stderr, "WARNING: The VM could not be removed via VBoxManage.\n");
// We test if we can remove the hard disk controller. If the command works, the cernvm.vmdk virtual disk will be also
// removed automatically
arg_list = "";
arg_list = " storagectl " + virtual_machine_name + " --name \"IDE Controller\" --remove";
if (vbm_popen(arg_list)) fprintf(stderr, "INFO: Hard disk removed!\n");
else fprintf(stderr,"WARNING: it was not possible to remove the IDE controller.\n");
#ifdef _WIN32
env = getenv("HOMEDRIVE");
fprintf(stderr,"INFO: I´m running in a Windows system...\n");
vboxXML = string(env);
env = getenv("HOMEPATH");
vboxXML = vboxXML + string(env);
vboxfolder = vboxXML + "\\VirtualBox VMs\\";
vboxXML = vboxXML + "\\.VirtualBox\\VirtualBox.xml";
//fprintf(stderr,"INFO: VirtualBox XML file: %s\n",vboxXML.c_str());
#else
// New idea
env = getenv("HOME");
vboxXML = string(env);
if (vboxXML.find("Users") == string::npos)
{
// GNU/Linux
vboxXML = vboxXML + "/.VirtualBox/VirtualBox.xml";
vboxfolder = string(env) + "/.VirtualBox/";
fprintf(stderr,"INFO: I'm running in a GNU/Linux system...\n");
}
else
{
// Mac OS X
vboxXML = vboxXML + "/Library/VirtualBox/VirtualBox.xml";
vboxfolder = string(env) + "/Library/VirtualBox/";
fprintf(stderr,"INFO: I'm running in a Mac OS X system...\n");
}
#endif
std::ifstream in(vboxXML.c_str());
if (in.is_open())
{
vboxXMLNew = vboxfolder + "VirtualBox.xmlNew";
std::ofstream out(vboxXMLNew.c_str());
int line_n = 0;
while (std::getline(in,line))
{
found_init = line.find("BOINC_VM");
if (found_init == string::npos)
out << line + "\n";
else
{
vmRegistered = true;
fprintf(stderr,"INFO: Obtaining the VM folder...\n");
found_init = line.find("src=");
found_end = line.find(virtual_machine_name + ".vbox");
if (found_end != string::npos)
fprintf(stderr,"INFO: .vbox found at line %i in VirtualBox.xml file\n", line_n);
vmfolder = line.substr(found_init+5,found_end-(found_init+5));
// For debugging, uncomment following line:
//fprintf(stderr,"INFO: %s VM folder: %s\n", virtual_machine_name.c_str(),vmfolder.c_str());
fprintf(stderr,"INFO: Done!\n");
}
line_n +=1;
}
in.close();
out.close();
}
// When the project is reset, we have to first unregister the VM, else we will have an error.
arg_list="unregistervm "+virtual_machine_name;
if(!vbm_popen(arg_list))
{
fprintf(stderr,"INFO: CernVM does not exist, so it is not necessary to unregister.\n");
}
else
{
fprintf(stderr,"INFO: Successfully unregistered the CernVM\n");
}
// Delete old VirtualBox.xml and replace with new one
std::remove(vboxXML.c_str());
std::rename(vboxXMLNew.c_str(),vboxXML.c_str());
// Remove remaining BOINC_VM folder
#ifdef _WIN32
if (vmRegistered)
{
vmfolder = "RMDIR \"" + vmfolder + "\" /s /q";
if (system(vmfolder.c_str()) == 0)
fprintf(stderr,"INFO: VM folder deleted!\n");
else
fprintf(stderr,"INFO: System was clean, nothing to delete.\n");
}
else
{
fprintf(stderr,"INFO: VM was not registered, deleting old VM folders...\n");
vmfolder = "RMDIR \"" + vboxfolder + virtual_machine_name + "\" /s /q";
if ( system(vmfolder.c_str()) == 0 )
fprintf(stderr,"INFO: VM folder deleted!\n");
else
fprintf(stderr,"INFO: System was clean, nothing to delete.\n");
}
#else // GNU/Linux and Mac OS X
// First delete the VM folder obtained in VirtualBox.xml
if (vmRegistered)
{
vmfolder = "rm -rf \"" + vmfolder + "\"";
if ( system(vmfolder.c_str()) == 0 )
fprintf(stderr,"INFO: VM folder deleted!\n");
else
{
fprintf(stderr,"INFO: System was clean, nothing to delete.\n");
}
}
else
{
fprintf(stderr,"INFO: VM was not registered, deleting old VM folders...\n");
vmfolder = "rm -rf \"" + string(env) + "/VirtualBox VMs/" + virtual_machine_name + "\" ";
if ( system(vmfolder.c_str()) == 0 )
fprintf(stderr,"INFO: VM folder deleted!\n");
else
fprintf(stderr,"INFO: System was clean, nothing to delete.\n");
}
#endif
boinc_end_critical_section();
}
void VM::release(){
boinc_begin_critical_section();
string arg_list="";
arg_list="closemedium disk "+disk_path;
if(!vbm_popen(arg_list))
{
fprintf(stderr,"ERROR: It was impossible to release the virtual hard disk\n");
}
else
fprintf(stderr,"INFO: Virtual Hard disk unregistered\n");
boinc_end_critical_section();
}
int VM::send_cputime_message() {
char text[256];
int reval;
string variety=MESSAGE;
sprintf(text,"<run_time>%lf</run_time>",current_period);
reval=boinc_send_trickle_up((char *)variety.c_str(), text);
current_period=0;
write_cputime(0);
return reval;
}
void VM::poll() {
boinc_begin_critical_section();
FILE* fp;
string arg_list, status;
char buffer[1024];
time_t current_time;
arg_list="";
arg_list="showvminfo "+virtual_machine_name+" --machinereadable" ;
if (!vbm_popen(arg_list,buffer,sizeof(buffer))){
fprintf(stderr,"ERROR: Get status from VM failed!\n");
fprintf(stderr,"INFO: poll() Aborting\n");
boinc_end_critical_section();
boinc_finish(1);
}
status=buffer;
if(status.find("VMState=\"running\"") !=string::npos){
if(suspended){
suspended=false;
last_poll_point=time(NULL);
}
else{
current_time=time(NULL);
current_period += difftime (current_time,last_poll_point);
last_poll_point=current_time;
fprintf(stderr,"INFO: VM poll is running\n");
}
boinc_end_critical_section();
return;
fprintf(stderr,"INFO: VM is running!\n"); //testing
}
if(status.find("VMState=\"paused\"") != string::npos){
time_t current_time;
if(!suspended){
suspended=true;
current_time=time(NULL);
current_period += difftime (current_time,last_poll_point);
}
fprintf(stderr,"INFO: VM is paused!\n"); //testing
boinc_end_critical_section();
return;
}
//if(status.find("VMState=\"poweroff\"") != string::npos
// ||status.find("VMState=\"saved\"") != string::npos){
// fprintf(stderr,"INFO: VM is powered off or saved!\n"); //testing
// exit(0);
//}
if (status.find("VMState=\"poweroff\"") != string::npos)
{
fprintf(stderr, "INFO: VM is powered off and it shouldn't\n");
fprintf(stderr, "INFO: Cancelling WU...\n");
boinc_end_critical_section();
boinc_finish(1);
exit(1);
}
fprintf(stderr,"ERROR: Get cernvm status error!\n");
fprintf(stderr,"INFO: cernvm status error Aborting\n");
remove();
boinc_end_critical_section();
boinc_finish(1);
}
void poll_boinc_messages(VM& vm, BOINC_STATUS &status) {
if (status.reread_init_data_file)
{
fprintf(stderr,"INFO: Project preferences changed\n");
vm.throttle();
}
if (status.no_heartbeat) {
fprintf(stderr,"INFO: BOINC no_heartbeat\n");
//vm.Check();
vm.savestate();
exit(0);
}
if (status.quit_request) {
fprintf(stderr,"INFO: BOINC status quit_request = True\n");
//vm.Check();
vm.savestate();
exit(0);
}
if (status.abort_request) {
fprintf(stderr,"INFO: BOINC status abort_request = True\n");
fprintf(stderr,"INFO: saving state of the vm and removing it...\n");
vm.savestate();
//vm.send_cputime_message();
vm.remove();
fprintf(stderr,"INFO: VM removed and task aborted\n");
boinc_finish(0);
}
if (status.suspended) {
fprintf(stderr,"INFO: BOINC status suspend = True. Stopping VM\n");
if (!vm.suspended) {
vm.pause();
}
} else {
fprintf(stderr,"INFO: BOINC status suspend = False. Resuming VM\n");
if (vm.suspended) {
vm.resume();
}
}
}
void write_cputime(double cpu) {
// TODO: modify this method to use real CPU usage from VirtualBox API.
FILE* f = fopen(CPU_TIME, "w");
if (!f) return;
fprintf(f, "%lf\n", cpu);
fclose(f);
}
void read_cputime(double& cpu) {
long int c;
cpu = 0;
FILE* f = fopen(CPU_TIME, "r");
if (!f) return;
int n = fscanf(f, "%ld",&c);
fclose(f);
if (n != 1) return;
cpu = c;
}
void write_progress(time_t secs)
{
FILE* f = fopen(PROGRESS_FN, "w");
fprintf(f,"%ld\n", secs);
//Flushing progress file after 5 minutes for not losing work
if ((int)boinc_elapsed_time() % (5*60) == 0)
{
#ifdef _WIN32
fprintf(stderr,"INFO: Flushing buffers after 5 minutes!\n");
fflush(f);
_commit(_fileno(f));
#else
fprintf(stderr,"INFO: Flushing buffers after 5 minutes!\n");
fsync(fileno(f));
#endif
}
fclose(f);
}
time_t read_progress() {
time_t stored_secs;
FILE* f = fopen(PROGRESS_FN, "r");
if (!f) return(0);
int n = fscanf(f, "%ld",&stored_secs);
fclose(f);
if (n != 1) return(0);
else return(stored_secs);
}
time_t update_progress(time_t secs) {
time_t old_secs;
old_secs = read_progress();
write_progress(old_secs + secs);
return(old_secs + secs);
}
int main(int argc, char** argv) {
BOINC_OPTIONS options;
BOINC_STATUS status;
double cpu_time=0, cpu_chkpt_time=0;
FILE*fp;
char buffer[2048]; // Enough size for the VBoxManage list vms output
unsigned int i;
bool graphics = false;
bool headless = false;
bool vrde = false;
bool vm_name = false;
bool retval = false;
// Name for the VM vmdk filename
string cernvm = "cernvm.vmdk";
string resolved_name;
unsigned int output;
// Get BOINC APP INIT DATA
//
boinc_get_init_data(aid);
// The VM
VM vm;
// Registering time for progress accounting
time_t init_secs = time (NULL);
//fprintf(stderr,"INFO: %ld seconds since January 1, 1970\n", init_secs);
// Checking command line options
for (i=1; i<(unsigned int)argc; i++)
{
if (!strcmp(argv[i], "--graphics")) graphics = true;
if (!strcmp(argv[i], "--headless")) headless = true;
if (!strcmp(argv[i], "--vmname"))
{
vm.virtual_machine_name = argv[i+1];
fprintf(stderr,"INFO: The name of the VM is: %s\n",vm.virtual_machine_name.c_str());
}
}
// If the wrapper has not be called with the command line argument --vmname NAME, give a default name to the VM
if (vm.virtual_machine_name.empty())
{
vm.virtual_machine_name = "BOINC_VM";
}
memset(&options, 0, sizeof(options));
options.main_program = true;
options.check_heartbeat = true;
options.handle_process_control = true;
options.send_status_msgs = true;
if (graphics) {
options.backwards_compatible_graphics = true;
}
boinc_init_options(&options);
// Setting up the PATH for Windows machines:
#ifdef _WIN32
// DEBUG information:
fprintf(stderr,"\nSetting VirtualBox PATH in Windows...\n");
// First get the HKEY_LOCAL_MACHINE\SOFTWARE\Oracle\VirtualBox
fprintf(stderr,"Trying to grab installation path of VirtualBox from Windows Registry...\n");
TCHAR szPath[4096];
DWORD dwType;
DWORD cbSize = sizeof(szPath) - sizeof(TCHAR); // Leave room for nul terminator
if (SHGetValue(HKEY_LOCAL_MACHINE,TEXT("SOFTWARE\\Oracle\\VirtualBox"),TEXT("InstallDir"),&dwType,szPath,&cbSize) == ERROR_SUCCESS)
{
//szPath[cbSize / sizeof(TCHAR)] = TEXT(´\0´);
fprintf(stderr,"Success!!! Installation PATH of VirtualBox is: %s.\n",szPath);
}
else
{
fprintf(stderr,"ERROR: Retrieving the HKEY_LOCAL_MACHINE\\SOFTWARE\\Oracle\\VirtualBox\\InstallDir value was impossible\n\n");
fprintf(stderr,"Trying with VBOX_INSTALL_PATH environment variable...\n");
LPTSTR VBoxInsPath;
DWORD dwRet, dwErr;
BOOL fExist, fSuccess;
// Retrieve old PATH variable
VBoxInsPath = (LPTSTR) malloc(4096*sizeof(TCHAR));
if(NULL == VBoxInsPath)
{
fprintf(stderr,"ERROR: malloc for VBoxInsPAth variable. Reason: Out of memory\n");
return FALSE;
}
dwRet = GetEnvironmentVariable("VBOX_INSTALL_PATH", VBoxInsPath, 4096);
if(0 == dwRet)
{
dwErr = GetLastError();
if( ERROR_ENVVAR_NOT_FOUND == dwErr )
{
fprintf(stderr,"ERROR: VBOX_INSTALL_PATH environment variable does not exist.\n");
fprintf(stderr,"ERROR: Impossible to set up the VirtualBox PATH. Aborting execution.\n\n");
fExist=FALSE;
boinc_finish(1);
}
else
{
fprintf(stderr,"ERROR: GetLastError ouput for VBOX_INSTALL_PATH environment variable: %u\n", dwErr);
fprintf(stderr,"INFO: GetLastError Aborting\n");
fExist=FALSE;
boinc_finish(1);
}
}
free(VBoxInsPath);
}
// New variables for setting the environment variable PATH
LPTSTR pszOldVal;
LPTSTR newVirtualBoxPath;
LPTSTR virtualbox;
DWORD dwRet, dwErr;
BOOL fExist, fSuccess;
// Create the new PATH variable
newVirtualBoxPath = (LPTSTR) malloc(4096*sizeof(TCHAR));
if(NULL == newVirtualBoxPath)
{
fprintf(stderr, "ERROR: malloc for newVirtualBoxPath variable. Reason: Out of memory\n");
return FALSE;
}
virtualbox = szPath;
// Retrieve old PATH variable
pszOldVal = (LPTSTR) malloc(4096*sizeof(TCHAR));
if(NULL == pszOldVal)
{
fprintf(stderr,"ERROR: malloc of pszOldVal variable. Reason: Out of memory\n");
return FALSE;
}
dwRet = GetEnvironmentVariable("PATH", pszOldVal, 4096);
if(0 == dwRet)
{
dwErr = GetLastError();
if( ERROR_ENVVAR_NOT_FOUND == dwErr )
{
fprintf(stderr,"ERROR: PATH environment variable does not exist.\n");
fExist=FALSE;
exit(1);
}
}
else
{
// DEBUG: print old PATH enviroment variable
fprintf(stderr,"Old PATH environment variable:\n");
fprintf(stderr,pszOldVal);
fprintf(stderr,"\n");
// Set new PATH environment variable
lstrcat(pszOldVal,";"); // Concat ; to old PATH
// Add VirtualBox path
SetEnvironmentVariable("PATH",lstrcat(pszOldVal,virtualbox));
dwRet = GetEnvironmentVariable("PATH", pszOldVal, 4096);
fprintf(stderr,"\nAdding VirtualBox to PATH:\n");
fprintf(stderr,pszOldVal);
fprintf(stderr,"\n");
}
// Free memory
free(pszOldVal);
free(newVirtualBoxPath);
#endif
// We check if the VM has already been created and launched
if (fp=fopen("VMName","r"))
{
fclose(fp);
vm_name = true;
}
else
{
// First remove old versions
fprintf(stderr,"INFO: Cleaning old VMs of the project...\n");
vm.remove();
fprintf(stderr,"INFO: Cleaning completed\n");
// Then, Decompress the new VM.gz file
fprintf(stderr,"\nInitializing VM...\n");
fprintf(stderr,"Decompressing the VM\n");
retval = boinc_resolve_filename_s("cernvm.vmdk.gz",resolved_name);
if (retval) fprintf(stderr,"can't resolve cernvm.vmdk.gz filename");
unzip(resolved_name.c_str(),cernvm.c_str());
fprintf(stderr,"Uncompressed finished\n");
vm_name= false;
}
if (vm_name)
{
fprintf(stderr,"VMName exists\n");
bool VMexist=false;
string arg_list;
//if((fp=fopen(vm.name_path.c_str(),"r"))==NULL){
// fprintf(stderr,"Main fopen failed\n");
// boinc_finish(1);
//}
//if(fgets(buffer,256,fp)) vm.virtual_machine_name=buffer;
//fclose(fp);
fprintf(stderr,"INFO: Virtual machine name %s\n",vm.virtual_machine_name.c_str());
// DEBUG for the name of the VM
// fprintf(stderr,"Name of the VM:\n");
// fprintf(stderr,vm.virtual_machine_name.c_str());
arg_list="";
arg_list=" list vms";
if (!vbm_popen(arg_list,buffer,sizeof(buffer))){
fprintf(stderr, "CernVMManager list failed!\n");
boinc_finish(1);
}
string VMlist=buffer;
// DEBUG for the list of running VMs
// fprintf(stderr,"List of running VMs:\n");
// fprintf(stderr,VMlist.c_str());
// fprintf(stderr,"\n");
if(VMlist.find(vm.virtual_machine_name.c_str()) != string::npos){
VMexist=true;
}
//Maybe voluteers delete CernVM using VB GUI
if(!VMexist){
fprintf(stderr,"INFO: VM does not exists.\n");
fprintf(stderr,"INFO: Cleaning old instances...\n");
vm.remove();
fprintf(stderr,"INFO: Done!\n");
fprintf(stderr,"INFO: Unzipping image...\n");
retval = boinc_resolve_filename_s("cernvm.vmdk.gz",resolved_name);
if (retval) fprintf(stderr,"can't resolve cernvm.vmdk.gz filename");
unzip(resolved_name.c_str(),cernvm.c_str());
fprintf(stderr,"INFO: Uncompressed finished\n");
fprintf(stderr,"Registering a new VM from an unzipped image...\n");
vm.create();
fprintf(stderr,"Done!\n");
}
}
else{
fprintf(stderr,"INFO: Cleaning old instances...\n");
vm.remove();
fprintf(stderr,"Registering a new VM from unzipped image...\n");
vm.create();
fprintf(stderr,"Done!\n");
}
time_t elapsed_secs = 0, dif_secs = 0;
long int t = 0;
double frac_done = 0;
read_cputime(cpu_time);
cpu_chkpt_time = cpu_time;
vm.current_period=cpu_time;
vm.start(vrde,headless);
vm.last_poll_point = time(NULL);
while (1) {
boinc_get_status(&status);
poll_boinc_messages(vm, status);
// Report progress to BOINC client
if (!status.suspended)
{
vm.poll();
if (vm.suspended)
{
fprintf(stderr,"WARNING: VM should be running as the WU is not suspended.\n");
vm.resume();
}
//if(vm.current_period >= CHECK_PERIOD)
// write_cputime(vm.current_period);
//if(vm.current_period >= TRICK_PERIOD)
//vm.send_cputime_message();
elapsed_secs = time(NULL);
dif_secs = update_progress(elapsed_secs - init_secs);
// Convert it for Windows machines:
t = static_cast<int>(dif_secs);
fprintf(stderr,"INFO: Running seconds %ld\n",dif_secs);
// For 24 hours:
frac_done = floor((t/86400.0)*100.0)/100.0;
fprintf(stderr,"INFO: Fraction done %f\n",frac_done);
// Checkpoint for reporting correctly the time
boinc_time_to_checkpoint();
boinc_checkpoint_completed();
boinc_fraction_done(frac_done);
if (frac_done >= 1.0)
{
fprintf(stderr,"INFO: Stopping the VM...\n");
vm.savestate();
fprintf(stderr,"INFO: VM stopped!\n");
vm.remove();
// Update the ProgressFile for starting from zero next WU
write_progress(0);
fprintf(stderr,"INFO: Done!! Cleanly exiting.\n");
fprintf(stderr,"INFO: Work Unit completed.\n");
// Output file:
fprintf(stderr,"INFO: Creating output file...\n");
FILE* output = fopen("output", "w");
fprintf(output, "Work Unit completed!\n");
fclose(output);
fprintf(stderr,"INFO: Done!\n");
boinc_finish(0);
}
else
{
init_secs = elapsed_secs;
boinc_sleep(POLL_PERIOD);
}
}
else
{
init_secs = time(NULL);
boinc_sleep(POLL_PERIOD);
}
}
}
#ifdef _WIN32
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR Args, int WinMode) {
LPSTR command_line;
char* argv[100];
int argc;
command_line = GetCommandLine();
argc = parse_command_line(command_line, argv);
return main(argc, argv);
}
#endif