mirror of https://github.com/BOINC/boinc.git
1198 lines
35 KiB
C++
1198 lines
35 KiB
C++
// 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
|