boinc/samples/vboxwrapper/cernvm/cernvmwrapper.cpp

1198 lines
35 KiB
C++
Raw Normal View History

2012-12-12 23:12:40 +00:00
// 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