mirror of https://github.com/BOINC/boinc.git
907 lines
28 KiB
C
907 lines
28 KiB
C
// The contents of this file are subject to the BOINC Public License
|
|
// Version 1.0 (the "License"); you may not use this file except in
|
|
// compliance with the License. You may obtain a copy of the License at
|
|
// http://boinc.berkeley.edu/license_1.0.txt
|
|
//
|
|
// Software distributed under the License is distributed on an "AS IS"
|
|
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
|
// License for the specific language governing rights and limitations
|
|
// under the License.
|
|
//
|
|
// The Original Code is the Berkeley Open Infrastructure for Network Computing.
|
|
//
|
|
// The Initial Developer of the Original Code is the SETI@home project.
|
|
// Portions created by the SETI@home project are Copyright (C) 2002
|
|
// University of California at Berkeley. All Rights Reserved.
|
|
//
|
|
// Contributor(s):
|
|
//
|
|
|
|
#include "cpp.h"
|
|
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "error_numbers.h"
|
|
#include "file_names.h"
|
|
#include "filesys.h"
|
|
#include "message.h"
|
|
#include "parse.h"
|
|
#include "util.h"
|
|
#include "pers_file_xfer.h"
|
|
|
|
#include "client_types.h"
|
|
|
|
PROJECT::PROJECT() {
|
|
init();
|
|
}
|
|
|
|
void PROJECT::init() {
|
|
strcpy(master_url, "");
|
|
strcpy(authenticator, "");
|
|
strcpy(project_specific_prefs, "");
|
|
resource_share = 100;
|
|
strcpy(project_name, "");
|
|
strcpy(user_name, "");
|
|
strcpy(team_name, "");
|
|
user_total_credit = 0;
|
|
user_expavg_credit = 0;
|
|
user_create_time = 0;
|
|
rpc_seqno = 0;
|
|
hostid = 0;
|
|
host_total_credit = 0;
|
|
host_expavg_credit = 0;
|
|
host_create_time = 0;
|
|
exp_avg_cpu = 0;
|
|
exp_avg_mod_time = 0;
|
|
strcpy(code_sign_key, "");
|
|
nrpc_failures = 0;
|
|
min_rpc_time = 0;
|
|
min_report_min_rpc_time = 0;
|
|
master_fetch_failures = 0;
|
|
resource_debt = 0;
|
|
debt_order = 0;
|
|
master_url_fetch_pending = false;
|
|
sched_rpc_pending = false;
|
|
tentative = false;
|
|
}
|
|
|
|
PROJECT::~PROJECT() {
|
|
}
|
|
|
|
// write account_*.xml file
|
|
//
|
|
int PROJECT::write_account_file() {
|
|
char path[256];
|
|
FILE* f;
|
|
int retval;
|
|
|
|
get_account_filename(master_url, path);
|
|
f = fopen(TEMP_FILE_NAME, "w");
|
|
if (!f) return ERR_FOPEN;
|
|
|
|
fprintf(f,
|
|
"<account>\n"
|
|
" <master_url>%s</master_url>\n"
|
|
" <authenticator>%s</authenticator>\n",
|
|
master_url,
|
|
authenticator
|
|
);
|
|
if (strlen(project_specific_prefs)) {
|
|
fprintf(f, "%s", project_specific_prefs);
|
|
}
|
|
if (tentative) {
|
|
fprintf(f, " <tentative/>\n");
|
|
}
|
|
fprintf(f, "</account>\n");
|
|
fclose(f);
|
|
retval = boinc_rename(TEMP_FILE_NAME, path);
|
|
if (retval) return ERR_RENAME;
|
|
return 0;
|
|
}
|
|
|
|
// parse project fields from account_*.xml
|
|
//
|
|
int PROJECT::parse_account(FILE* in) {
|
|
char buf[256];//, venue[256];
|
|
int retval;
|
|
// bool in_venue = false, in_correct_venue;
|
|
|
|
// Assume master_url_fetch_pending, sched_rpc_pending are
|
|
// true until we read client_state.xml
|
|
//
|
|
master_url_fetch_pending = true;
|
|
sched_rpc_pending = true;
|
|
strcpy(master_url, "");
|
|
strcpy(authenticator, "");
|
|
while (fgets(buf, 256, in)) {
|
|
if (match_tag(buf, "<account>")) continue;
|
|
|
|
/*
|
|
if (in_venue) {
|
|
if (match_tag(buf, "</venue>")) {
|
|
if (in_correct_venue)
|
|
return 0;
|
|
else {
|
|
in_venue = false;
|
|
continue;
|
|
}
|
|
} else if (!in_correct_venue)
|
|
continue;
|
|
} else if (match_tag(buf, "<venue")) {
|
|
in_venue = true;
|
|
parse_attr(buf, "name", venue, sizeof(venue));
|
|
if (!strcmp(venue, gstate.host_venue)) {
|
|
init();
|
|
in_correct_venue = true;
|
|
} else
|
|
in_correct_venue = false;
|
|
continue;
|
|
}
|
|
*/
|
|
|
|
if (match_tag(buf, "</account>")) return 0;
|
|
else if (parse_str(buf, "<master_url>", master_url, sizeof(master_url))) {
|
|
canonicalize_master_url(master_url);
|
|
continue;
|
|
}
|
|
else if (parse_str(buf, "<authenticator>", authenticator, sizeof(authenticator))) continue;
|
|
else if (parse_double(buf, "<resource_share>", resource_share)) continue;
|
|
else if (match_tag(buf, "<send_email/>")) continue;
|
|
else if (match_tag(buf, "<show_email/>")) continue;
|
|
else if (match_tag(buf, "<tentative/>")) {
|
|
tentative = true;
|
|
continue;
|
|
}
|
|
else if (match_tag(buf, "<project_specific>")) {
|
|
retval = copy_element_contents(
|
|
in,
|
|
"</project_specific>",
|
|
project_specific_prefs,
|
|
sizeof(project_specific_prefs)
|
|
);
|
|
if (retval) return ERR_XML_PARSE;
|
|
continue;
|
|
}
|
|
else msg_printf(NULL, MSG_ERROR, "PROJECT::parse_account(): unrecognized: %s\n", buf);
|
|
}
|
|
return ERR_XML_PARSE;
|
|
}
|
|
|
|
// parse project fields from client_state.xml
|
|
//
|
|
int PROJECT::parse_state(FILE* in) {
|
|
char buf[256];
|
|
STRING256 string;
|
|
|
|
strcpy(project_name, "");
|
|
strcpy(user_name, "");
|
|
strcpy(team_name, "");
|
|
resource_share = 100;
|
|
exp_avg_cpu = 0;
|
|
exp_avg_mod_time = 0;
|
|
min_rpc_time = 0;
|
|
min_report_min_rpc_time = 0;
|
|
nrpc_failures = 0;
|
|
master_url_fetch_pending = false;
|
|
sched_rpc_pending = false;
|
|
scheduler_urls.clear();
|
|
while (fgets(buf, 256, in)) {
|
|
if (match_tag(buf, "</project>")) return 0;
|
|
else if (parse_str(buf, "<scheduler_url>", string.text, sizeof(string.text))) {
|
|
scheduler_urls.push_back(string);
|
|
continue;
|
|
}
|
|
else if (parse_str(buf, "<master_url>", master_url, sizeof(master_url))) continue;
|
|
else if (parse_str(buf, "<project_name>", project_name, sizeof(project_name))) continue;
|
|
else if (parse_str(buf, "<user_name>", user_name, sizeof(user_name))) continue;
|
|
else if (parse_str(buf, "<team_name>", team_name, sizeof(team_name))) continue;
|
|
else if (parse_double(buf, "<user_total_credit>", user_total_credit)) continue;
|
|
else if (parse_double(buf, "<user_expavg_credit>", user_expavg_credit)) continue;
|
|
else if (parse_int(buf, "<user_create_time>", (int &)user_create_time)) continue;
|
|
else if (parse_int(buf, "<rpc_seqno>", rpc_seqno)) continue;
|
|
else if (parse_int(buf, "<hostid>", hostid)) continue;
|
|
else if (parse_double(buf, "<host_total_credit>", host_total_credit)) continue;
|
|
else if (parse_double(buf, "<host_expavg_credit>", host_expavg_credit)) continue;
|
|
else if (parse_int(buf, "<host_create_time>", (int &)host_create_time)) continue;
|
|
else if (parse_double(buf, "<exp_avg_cpu>", exp_avg_cpu)) continue;
|
|
else if (parse_int(buf, "<exp_avg_mod_time>", exp_avg_mod_time)) continue;
|
|
else if (match_tag(buf, "<code_sign_key>")) {
|
|
copy_element_contents(
|
|
in,
|
|
"</code_sign_key>",
|
|
code_sign_key,
|
|
sizeof(code_sign_key)
|
|
);
|
|
}
|
|
else if (parse_int(buf, "<nrpc_failures>", nrpc_failures)) continue;
|
|
else if (parse_int(buf, "<master_fetch_failures>", master_fetch_failures)) continue;
|
|
else if (parse_int(buf, "<min_rpc_time>", (int&)min_rpc_time)) continue;
|
|
else if (match_tag(buf, "<master_url_fetch_pending/>")) master_url_fetch_pending = true;
|
|
else if (match_tag(buf, "<sched_rpc_pending/>")) sched_rpc_pending = true;
|
|
else msg_printf(NULL, MSG_ERROR, "PROJECT::parse_state(): unrecognized: %s\n", buf);
|
|
}
|
|
return ERR_XML_PARSE;
|
|
}
|
|
|
|
// Write the project information to client state file
|
|
//
|
|
int PROJECT::write_state(FILE* out) {
|
|
unsigned int i;
|
|
|
|
fprintf(out,
|
|
"<project>\n"
|
|
);
|
|
for (i=0; i<scheduler_urls.size(); i++) {
|
|
fprintf(out,
|
|
" <scheduler_url>%s</scheduler_url>\n",
|
|
scheduler_urls[i].text
|
|
);
|
|
}
|
|
fprintf(out,
|
|
" <master_url>%s</master_url>\n"
|
|
" <project_name>%s</project_name>\n"
|
|
" <user_name>%s</user_name>\n"
|
|
" <team_name>%s</team_name>\n"
|
|
" <user_total_credit>%f</user_total_credit>\n"
|
|
" <user_expavg_credit>%f</user_expavg_credit>\n"
|
|
" <user_create_time>%d</user_create_time>\n"
|
|
" <rpc_seqno>%d</rpc_seqno>\n"
|
|
" <hostid>%d</hostid>\n"
|
|
" <host_total_credit>%f</host_total_credit>\n"
|
|
" <host_expavg_credit>%f</host_expavg_credit>\n"
|
|
" <host_create_time>%d</host_create_time>\n"
|
|
" <exp_avg_cpu>%f</exp_avg_cpu>\n"
|
|
" <exp_avg_mod_time>%d</exp_avg_mod_time>\n"
|
|
" <nrpc_failures>%d</nrpc_failures>\n"
|
|
" <master_fetch_failures>%d</master_fetch_failures>\n"
|
|
" <min_rpc_time>%d</min_rpc_time>\n"
|
|
"%s%s",
|
|
master_url,
|
|
project_name,
|
|
user_name,
|
|
team_name,
|
|
user_total_credit,
|
|
user_expavg_credit,
|
|
user_create_time,
|
|
rpc_seqno,
|
|
hostid,
|
|
host_total_credit,
|
|
host_expavg_credit,
|
|
host_create_time,
|
|
exp_avg_cpu,
|
|
exp_avg_mod_time,
|
|
nrpc_failures,
|
|
master_fetch_failures,
|
|
(int)min_rpc_time,
|
|
master_url_fetch_pending?" <master_url_fetch_pending/>\n":"",
|
|
sched_rpc_pending?" <sched_rpc_pending/>\n":""
|
|
);
|
|
if (strlen(code_sign_key)) {
|
|
fprintf(out,
|
|
" <code_sign_key>\n%s</code_sign_key>\n", code_sign_key
|
|
);
|
|
}
|
|
fprintf(out,
|
|
"</project>\n"
|
|
);
|
|
return 0;
|
|
}
|
|
|
|
// copy fields from "p" into "this" that are stored in client_state.xml
|
|
//
|
|
void PROJECT::copy_state_fields(PROJECT& p) {
|
|
scheduler_urls = p.scheduler_urls;
|
|
safe_strcpy(project_name, p.project_name);
|
|
safe_strcpy(user_name, p.user_name);
|
|
safe_strcpy(team_name, p.team_name);
|
|
user_total_credit = p.user_total_credit;
|
|
user_expavg_credit = p.user_expavg_credit;
|
|
user_create_time = p.user_create_time;
|
|
rpc_seqno = p.rpc_seqno;
|
|
hostid = p.hostid;
|
|
host_total_credit = p.host_total_credit;
|
|
host_expavg_credit = p.host_expavg_credit;
|
|
host_create_time = p.host_create_time;
|
|
exp_avg_cpu = p.exp_avg_cpu;
|
|
exp_avg_mod_time = p.exp_avg_mod_time;
|
|
nrpc_failures = p.nrpc_failures;
|
|
master_fetch_failures = p.master_fetch_failures;
|
|
min_rpc_time = p.min_rpc_time;
|
|
master_url_fetch_pending = p.master_url_fetch_pending;
|
|
sched_rpc_pending = p.sched_rpc_pending;
|
|
safe_strcpy(code_sign_key, p.code_sign_key);
|
|
}
|
|
|
|
char* PROJECT::get_project_name() {
|
|
if (strlen(project_name)) {
|
|
return project_name;
|
|
} else {
|
|
return master_url;
|
|
}
|
|
}
|
|
|
|
int APP::parse(FILE* in) {
|
|
char buf[256];
|
|
|
|
strcpy(name, "");
|
|
project = NULL;
|
|
while (fgets(buf, 256, in)) {
|
|
if (match_tag(buf, "</app>")) return 0;
|
|
else if (parse_str(buf, "<name>", name, sizeof(name))) continue;
|
|
else msg_printf(NULL, MSG_ERROR, "APP::parse(): unrecognized: %s\n", buf);
|
|
}
|
|
return ERR_XML_PARSE;
|
|
}
|
|
|
|
int APP::write(FILE* out) {
|
|
fprintf(out,
|
|
"<app>\n"
|
|
" <name>%s</name>\n"
|
|
"</app>\n",
|
|
name
|
|
);
|
|
return 0;
|
|
}
|
|
|
|
FILE_INFO::FILE_INFO() {
|
|
}
|
|
|
|
FILE_INFO::~FILE_INFO() {
|
|
if (pers_file_xfer) {
|
|
msg_printf(NULL, MSG_ERROR, "FILE_INFO::~FILE_INFO(): removing FILE_INFO when a pers_file_xfer still points to me\n");
|
|
pers_file_xfer->fip = NULL;
|
|
}
|
|
}
|
|
|
|
// Set the appropriate permissions depending on whether
|
|
// it's an executable file
|
|
// This doesn't seem to exist in Windows
|
|
//
|
|
int FILE_INFO::set_permissions() {
|
|
#ifdef _WIN32
|
|
return 0;
|
|
#else
|
|
int retval;
|
|
char pathname[256];
|
|
get_pathname(this, pathname);
|
|
if (executable) {
|
|
retval = chmod(pathname, S_IEXEC|S_IREAD|S_IWRITE);
|
|
} else {
|
|
retval = chmod(pathname, S_IREAD|S_IWRITE);
|
|
}
|
|
return retval;
|
|
#endif
|
|
}
|
|
|
|
// If from server, make an exact copy of everything
|
|
// except the start/end tags and the <xml_signature> element.
|
|
//
|
|
int FILE_INFO::parse(FILE* in, bool from_server) {
|
|
char buf[256], buf2[1024];
|
|
STRING256 url;
|
|
PERS_FILE_XFER *pfxp;
|
|
int retval;
|
|
|
|
strcpy(name, "");
|
|
strcpy(md5_cksum, "");
|
|
max_nbytes = 0;
|
|
nbytes = 0;
|
|
upload_offset = -1;
|
|
generated_locally = false;
|
|
status = FILE_NOT_PRESENT;
|
|
executable = false;
|
|
approval_pending = false;
|
|
uploaded = false;
|
|
upload_when_present = false;
|
|
sticky = false;
|
|
signature_required = false;
|
|
pers_file_xfer = NULL;
|
|
result = NULL;
|
|
project = NULL;
|
|
urls.clear();
|
|
start_url = -1;
|
|
current_url = -1;
|
|
strcpy(signed_xml, "");
|
|
strcpy(xml_signature, "");
|
|
strcpy(file_signature, "");
|
|
while (fgets(buf, 256, in)) {
|
|
if (match_tag(buf, "</file_info>")) return 0;
|
|
else if (match_tag(buf, "<xml_signature>")) {
|
|
copy_element_contents(
|
|
in,
|
|
"</xml_signature>",
|
|
xml_signature,
|
|
sizeof(xml_signature)
|
|
);
|
|
continue;
|
|
}
|
|
if (from_server) {
|
|
strcat(signed_xml, buf);
|
|
}
|
|
if (parse_str(buf, "<name>", name, sizeof(name))) continue;
|
|
else if (parse_str(buf, "<url>", url.text, sizeof(url.text))) {
|
|
urls.push_back(url);
|
|
continue;
|
|
}
|
|
else if (match_tag(buf, "<file_signature>")) {
|
|
copy_element_contents(
|
|
in,
|
|
"</file_signature>",
|
|
file_signature,
|
|
sizeof(file_signature)
|
|
);
|
|
continue;
|
|
}
|
|
else if (parse_str(buf, "<md5_cksum>", md5_cksum, sizeof(md5_cksum))) continue;
|
|
else if (parse_double(buf, "<nbytes>", nbytes)) continue;
|
|
else if (parse_double(buf, "<max_nbytes>", max_nbytes)) continue;
|
|
else if (match_tag(buf, "<generated_locally/>")) generated_locally = true;
|
|
else if (parse_int(buf, "<status>", status)) continue;
|
|
else if (match_tag(buf, "<executable/>")) executable = true;
|
|
else if (match_tag(buf, "<approval_pending/>")) approval_pending = true;
|
|
else if (match_tag(buf, "<uploaded/>")) uploaded = true;
|
|
else if (match_tag(buf, "<upload_when_present/>")) upload_when_present = true;
|
|
else if (match_tag(buf, "<sticky/>")) sticky = true;
|
|
else if (match_tag(buf, "<signature_required/>")) signature_required = true;
|
|
else if (match_tag(buf, "<persistent_file_xfer>")) {
|
|
pfxp = new PERS_FILE_XFER;
|
|
retval = pfxp->parse(in);
|
|
if (!retval) {
|
|
pers_file_xfer = pfxp;
|
|
} else {
|
|
delete pfxp;
|
|
}
|
|
} else if (!from_server && match_tag(buf, "<signed_xml>")) {
|
|
copy_element_contents(
|
|
in,
|
|
"</signed_xml>",
|
|
signed_xml,
|
|
sizeof(signed_xml)
|
|
);
|
|
continue;
|
|
} else if (match_tag(buf, "<error_msg>")) {
|
|
copy_element_contents(in, "</error_msg>", buf2, sizeof(buf2));
|
|
error_msg = buf2;
|
|
} else {
|
|
msg_printf(NULL, MSG_ERROR, "FILE_INFO::parse(): unrecognized: %s\n", buf);
|
|
}
|
|
}
|
|
return ERR_XML_PARSE;
|
|
}
|
|
|
|
// Write out XML based file info
|
|
//
|
|
int FILE_INFO::write(FILE* out, bool to_server) {
|
|
unsigned int i;
|
|
int retval;
|
|
|
|
fprintf(out,
|
|
"<file_info>\n"
|
|
" <name>%s</name>\n"
|
|
" <nbytes>%f</nbytes>\n"
|
|
" <max_nbytes>%f</max_nbytes>\n",
|
|
name, nbytes, max_nbytes
|
|
);
|
|
if (strlen(md5_cksum)) {
|
|
fprintf(out,
|
|
" <md5_cksum>%s</md5_cksum>\n",
|
|
md5_cksum
|
|
);
|
|
}
|
|
if (!to_server) {
|
|
if (generated_locally) fprintf(out, " <generated_locally/>\n");
|
|
fprintf(out, " <status>%d</status>\n", status);
|
|
if (executable) fprintf(out, " <executable/>\n");
|
|
if (approval_pending) fprintf(out, " <approval_pending/>\n");
|
|
if (uploaded) fprintf(out, " <uploaded/>\n");
|
|
if (upload_when_present) fprintf(out, " <upload_when_present/>\n");
|
|
if (sticky) fprintf(out, " <sticky/>\n");
|
|
if (signature_required) fprintf(out, " <signature_required/>\n");
|
|
if (file_signature) fprintf(out," <file_signature>\n%s</file_signature>\n", file_signature);
|
|
}
|
|
for (i=0; i<urls.size(); i++) {
|
|
fprintf(out, " <url>%s</url>\n", urls[i].text);
|
|
}
|
|
if (!to_server && pers_file_xfer) {
|
|
retval = pers_file_xfer->write(out);
|
|
if (retval) return retval;
|
|
}
|
|
if (!to_server) {
|
|
if (strlen(signed_xml) && strlen(xml_signature)) {
|
|
fprintf(out, " <signed_xml>\n%s </signed_xml>\n", signed_xml);
|
|
}
|
|
if (strlen(xml_signature)) {
|
|
fprintf(out, " <xml_signature>\n%s </xml_signature>\n", xml_signature);
|
|
}
|
|
}
|
|
if (!error_msg.empty()) {
|
|
fprintf(out, " <error_msg>\n%s</error_msg>\n", error_msg.c_str());
|
|
}
|
|
fprintf(out, "</file_info>\n");
|
|
return 0;
|
|
}
|
|
|
|
// delete physical underlying file associated with FILE_INFO
|
|
//
|
|
int FILE_INFO::delete_file() {
|
|
char path[256];
|
|
|
|
get_pathname(this, path);
|
|
int retval = file_delete(path);
|
|
if (retval && status != FILE_NOT_PRESENT) {
|
|
msg_printf(project, MSG_ERROR, "Couldn't delete file %s\n", path);
|
|
}
|
|
status = FILE_NOT_PRESENT;
|
|
return retval;
|
|
}
|
|
|
|
// get the currently selected url to download/upload file, or
|
|
// select one if none is chosen yet
|
|
//
|
|
char* FILE_INFO::get_url() {
|
|
double temp;
|
|
if (current_url < 0) {
|
|
temp = rand();
|
|
temp *= urls.size();
|
|
temp /= RAND_MAX;
|
|
current_url = (int)temp;
|
|
start_url = current_url;
|
|
}
|
|
|
|
return urls[current_url].text;
|
|
}
|
|
|
|
// Returns true if the file had an unrecoverable error
|
|
// (couldn't download, RSA/MD5 check failed, etc)
|
|
//
|
|
bool FILE_INFO::had_failure(int& failnum) {
|
|
if (status != FILE_NOT_PRESENT && status != FILE_PRESENT) {
|
|
failnum = status;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Parse XML based app_version information, usually from client_state.xml
|
|
//
|
|
int APP_VERSION::parse(FILE* in) {
|
|
char buf[256];
|
|
FILE_REF file_ref;
|
|
|
|
strcpy(app_name, "");
|
|
version_num = 0;
|
|
app = NULL;
|
|
project = NULL;
|
|
while (fgets(buf, 256, in)) {
|
|
if (match_tag(buf, "</app_version>")) return 0;
|
|
else if (parse_str(buf, "<app_name>", app_name, sizeof(app_name))) continue;
|
|
else if (match_tag(buf, "<file_ref>")) {
|
|
file_ref.parse(in);
|
|
app_files.push_back(file_ref);
|
|
continue;
|
|
}
|
|
else if (parse_int(buf, "<version_num>", version_num)) continue;
|
|
else msg_printf(NULL, MSG_ERROR, "APP_VERSION::parse(): unrecognized: %s\n", buf);
|
|
}
|
|
return ERR_XML_PARSE;
|
|
}
|
|
|
|
int APP_VERSION::write(FILE* out) {
|
|
unsigned int i;
|
|
int retval;
|
|
|
|
fprintf(out,
|
|
"<app_version>\n"
|
|
" <app_name>%s</app_name>\n"
|
|
" <version_num>%d</version_num>\n",
|
|
app_name,
|
|
version_num
|
|
);
|
|
for (i=0; i<app_files.size(); i++) {
|
|
retval = app_files[i].write(out);
|
|
if (retval) return retval;
|
|
}
|
|
fprintf(out,
|
|
"</app_version>\n"
|
|
);
|
|
return 0;
|
|
}
|
|
|
|
int FILE_REF::parse(FILE* in) {
|
|
char buf[256], buf2[256];
|
|
|
|
strcpy(file_name, "");
|
|
strcpy(open_name, "");
|
|
fd = -1;
|
|
main_program = false;
|
|
copy_file = false;
|
|
optional = false;
|
|
while (fgets(buf, 256, in)) {
|
|
if (match_tag(buf, "</file_ref>")) return 0;
|
|
else if (parse_str(buf, "<file_name>", file_name, sizeof(file_name))) continue;
|
|
else if (parse_str(buf, "<open_name>", open_name, sizeof(open_name))) continue;
|
|
else if (parse_int(buf, "<fd>", fd)) continue;
|
|
else if (match_tag(buf, "<main_program/>")) main_program = true;
|
|
else if (match_tag(buf, "<optional")) {
|
|
optional = true;
|
|
parse_attr(buf, "deadline", buf2, sizeof(buf2));
|
|
optional_deadline = time(0) + atoi(buf2);
|
|
}
|
|
else if (match_tag(buf, "<copy_file/>")) copy_file = true;
|
|
else msg_printf(NULL, MSG_ERROR, "FILE_REF::parse(): unrecognized: %s\n", buf);
|
|
}
|
|
return ERR_XML_PARSE;
|
|
}
|
|
|
|
int FILE_REF::write(FILE* out) {
|
|
|
|
fprintf(out,
|
|
" <file_ref>\n"
|
|
" <file_name>%s</file_name>\n",
|
|
file_name
|
|
);
|
|
if (strlen(open_name)) {
|
|
fprintf(out, " <open_name>%s</open_name>\n", open_name);
|
|
}
|
|
if (fd >= 0) {
|
|
fprintf(out, " <fd>%d</fd>\n", fd);
|
|
}
|
|
if (main_program) {
|
|
fprintf(out, " <main_program/>\n");
|
|
}
|
|
if (copy_file) {
|
|
fprintf(out, " <copy_file/>\n");
|
|
}
|
|
fprintf(out, " </file_ref>\n");
|
|
return 0;
|
|
}
|
|
|
|
int WORKUNIT::parse(FILE* in) {
|
|
char buf[256];
|
|
FILE_REF file_ref;
|
|
|
|
strcpy(name, "");
|
|
strcpy(app_name, "");
|
|
version_num = 0;
|
|
strcpy(command_line, "");
|
|
strcpy(env_vars, "");
|
|
app = NULL;
|
|
project = NULL;
|
|
// Default these to very large values (1 week on a 1 cobblestone machine)
|
|
// so we don't keep asking the server for more work
|
|
rsc_fpops_est = 1e9*SECONDS_PER_DAY*7;
|
|
rsc_fpops_bound = 4e9*SECONDS_PER_DAY*7;
|
|
rsc_memory_bound = 1e8;
|
|
rsc_disk_bound = 1e9;
|
|
while (fgets(buf, 256, in)) {
|
|
if (match_tag(buf, "</workunit>")) return 0;
|
|
else if (parse_str(buf, "<name>", name, sizeof(name))) continue;
|
|
else if (parse_str(buf, "<app_name>", app_name, sizeof(app_name))) continue;
|
|
else if (parse_int(buf, "<version_num>", version_num)) continue;
|
|
else if (parse_str(buf, "<command_line>", command_line, sizeof(command_line))) continue;
|
|
else if (parse_str(buf, "<env_vars>", env_vars, sizeof(env_vars))) continue;
|
|
else if (parse_double(buf, "<rsc_fpops_est>", rsc_fpops_est)) continue;
|
|
else if (parse_double(buf, "<rsc_fpops_bound>", rsc_fpops_bound)) continue;
|
|
else if (parse_double(buf, "<rsc_memory_bound>", rsc_memory_bound)) continue;
|
|
else if (parse_double(buf, "<rsc_disk_bound>", rsc_disk_bound)) continue;
|
|
else if (match_tag(buf, "<file_ref>")) {
|
|
file_ref.parse(in);
|
|
input_files.push_back(file_ref);
|
|
continue;
|
|
}
|
|
else msg_printf(NULL, MSG_ERROR, "WORKUNIT::parse(): unrecognized: %s\n", buf);
|
|
}
|
|
return ERR_XML_PARSE;
|
|
}
|
|
|
|
int WORKUNIT::write(FILE* out) {
|
|
unsigned int i;
|
|
|
|
fprintf(out,
|
|
"<workunit>\n"
|
|
" <name>%s</name>\n"
|
|
" <app_name>%s</app_name>\n"
|
|
" <version_num>%d</version_num>\n"
|
|
" <command_line>%s</command_line>\n"
|
|
" <env_vars>%s</env_vars>\n"
|
|
" <rsc_fpops_est>%f</rsc_fpops_est>\n"
|
|
" <rsc_fpops_bound>%f</rsc_fpops_bound>\n"
|
|
" <rsc_memory_bound>%f</rsc_memory_bound>\n"
|
|
" <rsc_disk_bound>%f</rsc_disk_bound>\n",
|
|
name,
|
|
app_name,
|
|
version_num,
|
|
command_line,
|
|
env_vars,
|
|
rsc_fpops_est,
|
|
rsc_fpops_bound,
|
|
rsc_memory_bound,
|
|
rsc_disk_bound
|
|
);
|
|
for (i=0; i<input_files.size(); i++) {
|
|
input_files[i].write(out);
|
|
}
|
|
fprintf(out, "</workunit>\n");
|
|
return 0;
|
|
}
|
|
|
|
bool WORKUNIT::had_failure(int& failnum) {
|
|
unsigned int i;
|
|
|
|
for (i=0;i<input_files.size();i++) {
|
|
if (input_files[i].file_info->had_failure(failnum)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void WORKUNIT::get_file_errors(string& str) {
|
|
int x;
|
|
unsigned int i;
|
|
FILE_INFO* fip;
|
|
str = "couldn't get input files:\n";
|
|
for (i=0;i<input_files.size();i++) {
|
|
fip = input_files[i].file_info;
|
|
if (fip->had_failure(x)) {
|
|
str = str + fip->name + ": " + fip->error_msg + "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
int RESULT::parse_ack(FILE* in) {
|
|
char buf[256];
|
|
|
|
strcpy(name, "");
|
|
while (fgets(buf, 256, in)) {
|
|
if (match_tag(buf, "</result_ack>")) return 0;
|
|
else if (parse_str(buf, "<name>", name, sizeof(name))) continue;
|
|
else msg_printf(NULL, MSG_ERROR, "RESULT::parse(): unrecognized: %s\n", buf);
|
|
}
|
|
return ERR_XML_PARSE;
|
|
}
|
|
|
|
void RESULT::clear() {
|
|
strcpy(name, "");
|
|
strcpy(wu_name, "");
|
|
report_deadline = 0;
|
|
output_files.clear();
|
|
is_active = false;
|
|
state = RESULT_NEW;
|
|
ready_to_report = false;
|
|
got_server_ack = false;
|
|
final_cpu_time = 0;
|
|
exit_status = 0;
|
|
active_task_state = 0;
|
|
signal = 0;
|
|
stderr_out = "";
|
|
app = NULL;
|
|
wup = NULL;
|
|
project = NULL;
|
|
}
|
|
|
|
// parse a <result> element from scheduling server.
|
|
//
|
|
int RESULT::parse_server(FILE* in) {
|
|
char buf[256];
|
|
FILE_REF file_ref;
|
|
|
|
clear();
|
|
while (fgets(buf, 256, in)) {
|
|
if (match_tag(buf, "</result>")) return 0;
|
|
if (parse_str(buf, "<name>", name, sizeof(name))) continue;
|
|
if (parse_str(buf, "<wu_name>", wu_name, sizeof(wu_name))) continue;
|
|
if (parse_int(buf, "<report_deadline>", report_deadline)) continue;
|
|
if (match_tag(buf, "<file_ref>")) {
|
|
file_ref.parse(in);
|
|
output_files.push_back(file_ref);
|
|
continue;
|
|
}
|
|
else msg_printf(NULL, MSG_ERROR, "RESULT::parse(): unrecognized: %s\n", buf);
|
|
}
|
|
return ERR_XML_PARSE;
|
|
}
|
|
|
|
// parse a <result> element from state file
|
|
//
|
|
int RESULT::parse_state(FILE* in) {
|
|
char buf[256];
|
|
FILE_REF file_ref;
|
|
|
|
clear();
|
|
while (fgets(buf, 256, in)) {
|
|
if (match_tag(buf, "</result>")) return 0;
|
|
if (parse_str(buf, "<name>", name, sizeof(name))) continue;
|
|
if (parse_str(buf, "<wu_name>", wu_name, sizeof(wu_name))) continue;
|
|
if (parse_int(buf, "<report_deadline>", report_deadline)) continue;
|
|
if (match_tag(buf, "<file_ref>")) {
|
|
file_ref.parse(in);
|
|
output_files.push_back(file_ref);
|
|
continue;
|
|
}
|
|
else if (parse_double(buf, "<final_cpu_time>", final_cpu_time)) continue;
|
|
else if (parse_int(buf, "<exit_status>", exit_status)) continue;
|
|
else if (match_tag(buf, "<got_server_ack/>")) got_server_ack = true;
|
|
else if (match_tag(buf, "<ready_to_report/>")) ready_to_report = true;
|
|
else if (parse_int(buf, "<state>", state)) continue;
|
|
else if (match_tag(buf, "<stderr_out>")) {
|
|
while (fgets(buf, 256, in)) {
|
|
if (match_tag(buf, "</stderr_out>")) break;
|
|
stderr_out.append(buf);
|
|
}
|
|
continue;
|
|
}
|
|
else msg_printf(NULL, MSG_ERROR, "RESULT::parse(): unrecognized: %s\n", buf);
|
|
}
|
|
return ERR_XML_PARSE;
|
|
}
|
|
|
|
int RESULT::write(FILE* out, bool to_server) {
|
|
unsigned int i;
|
|
FILE_INFO* fip;
|
|
int n, retval;
|
|
|
|
fprintf(out,
|
|
"<result>\n"
|
|
" <name>%s</name>\n"
|
|
" <final_cpu_time>%f</final_cpu_time>\n"
|
|
" <state>%d</state>\n",
|
|
name,
|
|
final_cpu_time,
|
|
state
|
|
);
|
|
n = stderr_out.length();
|
|
if (n) {
|
|
fprintf(out, "<stderr_out>\n");
|
|
fprintf(out, stderr_out.c_str());
|
|
if (stderr_out[n-1] != '\n') fprintf(out, "\n");
|
|
fprintf(out, "</stderr_out>\n");
|
|
}
|
|
if (!to_server) {
|
|
if (got_server_ack) fprintf(out, " <got_server_ack/>\n");
|
|
if (ready_to_report) fprintf(out, " <ready_to_report/>\n");
|
|
fprintf(out,
|
|
" <wu_name>%s</wu_name>\n"
|
|
" <report_deadline>%d</report_deadline>\n",
|
|
wu_name,
|
|
report_deadline
|
|
);
|
|
for (i=0; i<output_files.size(); i++) {
|
|
retval = output_files[i].write(out);
|
|
if (retval) return retval;
|
|
}
|
|
} else {
|
|
for (i=0; i<output_files.size(); i++) {
|
|
fip = output_files[i].file_info;
|
|
if (fip->uploaded) {
|
|
retval = fip->write(out, to_server);
|
|
if (retval) return retval;
|
|
}
|
|
}
|
|
}
|
|
fprintf(out, "</result>\n");
|
|
return 0;
|
|
}
|
|
|
|
// this is called after the result state is RESULT_COMPUTE_DONE.
|
|
// Returns true if the result's output files are all either
|
|
// successfully uploaded or have unrecoverable errors
|
|
//
|
|
bool RESULT::is_upload_done() {
|
|
unsigned int i;
|
|
FILE_INFO* fip;
|
|
int retval;
|
|
|
|
for (i=0; i<output_files.size(); i++) {
|
|
fip = output_files[i].file_info;
|
|
if (fip->upload_when_present) {
|
|
if (fip->had_failure(retval)) continue;
|
|
if (!fip->uploaded) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|