// 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 . #include "config.h" #include #include #include #include using namespace std; #include "parse.h" #include "error_numbers.h" #include "str_util.h" #include "util.h" #include "main.h" #include "sched_util.h" #include "sched_msgs.h" #include "time_stats_log.h" #include "server_types.h" #ifdef _USING_FCGI_ #include "fcgi_stdio.h" #endif // remove (by truncating) any quotes from the given string. // This is for things (e.g. authenticator) that will be used in // a SQL query, to prevent SQL injection attacks // void remove_quotes(char* p) { int i, n=strlen(p); for (i=0; i")) return 0; if (parse_str(buf, "", app_name, 256)) continue; if (parse_int(buf, "", version_num)) continue; } return ERR_XML_PARSE; } int FILE_INFO::parse(FILE* f) { char buf[256]; memset(this, 0, sizeof(FILE_INFO)); while (fgets(buf, sizeof(buf), f)) { if (match_tag(buf, "")) { if (!strlen(name)) return ERR_XML_PARSE; return 0; } if (parse_str(buf, "", name, 256)) continue; } return ERR_XML_PARSE; } int OTHER_RESULT::parse(FILE* f) { char buf[256]; name = ""; while (fgets(buf, sizeof(buf), f)) { if (match_tag(buf, "")) { if (name=="") return ERR_XML_PARSE; return 0; } if (parse_str(buf, "", name)) continue; } return ERR_XML_PARSE; } int IP_RESULT::parse(FILE* f) { char buf[256]; report_deadline = 0; cpu_time_remaining = 0; strcpy(name, ""); while (fgets(buf, sizeof(buf), f)) { if (match_tag(buf, "")) return 0; if (parse_str(buf, "", name, sizeof(name))) continue; if (parse_double(buf, "", report_deadline)) continue; if (parse_double(buf, "", cpu_time_remaining)) continue; } return ERR_XML_PARSE; } int CLIENT_PLATFORM::parse(FILE* fin) { char buf[256]; strcpy(name, ""); while (fgets(buf, sizeof(buf), fin)) { if (match_tag(buf, "")) return 0; if (parse_str(buf, "", name, sizeof(name))) continue; } return ERR_XML_PARSE; } void WORK_REQ::insert_no_work_message(USER_MESSAGE& um) { bool found = false; for (unsigned int i=0; i")) return "no start tag"; while (fgets(buf, sizeof(buf), fin)) { // If a line is too long, ignore it. // This can happen e.g. if the client has bad global_prefs.xml // This won't be necessary if we rewrite this using XML_PARSER // if (!strchr(buf, '\n')) { while (fgets(buf, sizeof(buf), fin)) { if (strchr(buf, '\n')) break; } continue; } if (match_tag(buf, "")) return NULL; if (parse_str(buf, "", authenticator, sizeof(authenticator))) { remove_quotes(authenticator); continue; } if (parse_str(buf, "", cross_project_id, sizeof(cross_project_id))) continue; if (parse_int(buf, "", hostid)) continue; if (parse_int(buf, "", rpc_seqno)) continue; if (parse_str(buf, "", platform.name, sizeof(platform.name))) continue; if (match_tag(buf, "")) { CLIENT_PLATFORM cp; retval = cp.parse(fin); if (!retval) { alt_platforms.push_back(cp); } continue; } if (match_tag(buf, "")) { while (fgets(buf, sizeof(buf), fin)) { if (match_tag(buf, "")) break; if (match_tag(buf, "")) { CLIENT_APP_VERSION cav; cav.parse(fin); client_app_versions.push_back(cav); } } continue; } if (parse_int(buf, "", core_client_major_version)) continue; if (parse_int(buf, "", core_client_minor_version)) continue; if (parse_int(buf, "", core_client_release)) continue; if (parse_double(buf, "", work_req_seconds)) continue; if (parse_double(buf, "", resource_share_fraction)) continue; if (parse_double(buf, "", rrs_fraction)) continue; if (parse_double(buf, "", prrs_fraction)) continue; if (parse_double(buf, "", estimated_delay)) continue; if (parse_double(buf, "", host.duration_correction_factor)) continue; if (match_tag(buf, "")) { strcpy(global_prefs_xml, "\n"); while (fgets(buf, sizeof(buf), fin)) { if (strstr(buf, "")) break; safe_strcat(global_prefs_xml, buf); } safe_strcat(global_prefs_xml, "\n"); continue; } if (match_tag(buf, "")) { while (fgets(buf, sizeof(buf), fin)) { if (strstr(buf, "")) break; safe_strcat(working_global_prefs_xml, buf); } continue; } if (parse_str(buf, "", global_prefs_source_email_hash, sizeof(global_prefs_source_email_hash))) continue; if (match_tag(buf, "")) { host.parse(fin); continue; } if (match_tag(buf, "")) { host.parse_time_stats(fin); continue; } if (match_tag(buf, "")) { handle_time_stats_log(fin); have_time_stats_log = true; continue; } if (match_tag(buf, "")) { host.parse_net_stats(fin); continue; } if (match_tag(buf, "")) { host.parse_disk_usage(fin); continue; } if (match_tag(buf, "")) { result.parse_from_client(fin); results.push_back(result); continue; } if (match_tag(buf, "")) { copy_element_contents(fin, "", code_sign_key, sizeof(code_sign_key)); continue; } if (match_tag(buf, "")) { MSG_FROM_HOST_DESC md; retval = md.parse(fin); if (!retval) { msgs_from_host.push_back(md); } continue; } if (match_tag(buf, "")) { FILE_INFO fi; retval = fi.parse(fin); if (!retval) { file_infos.push_back(fi); } continue; } if (match_tag(buf, "")) { continue; } if (match_tag(buf, "")) { have_other_results_list = true; while (fgets(buf, sizeof(buf), fin)) { if (match_tag(buf, "")) break; if (match_tag(buf, "")) { OTHER_RESULT o_r; retval = o_r.parse(fin); if (!retval) { other_results.push_back(o_r); } } } continue; } if (match_tag(buf, "")) { have_ip_results_list = true; int i = 0; double now = time(0); while (fgets(buf, sizeof(buf), fin)) { if (match_tag(buf, "")) break; if (match_tag(buf, "")) { IP_RESULT ir; retval = ir.parse(fin); if (!retval) { if (!strlen(ir.name)) { sprintf(ir.name, "ip%d", i++); } ir.report_deadline -= now; ip_results.push_back(ir); } } } continue; } if (match_tag(buf, "coprocs")) { coprocs.parse(fin); continue; } if (parse_bool(buf, "client_cap_plan_class", client_cap_plan_class)) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "\n" " %s\n" " %s\n" " %s\n" " %d\n" " %d\n" " %d\n" " %d\n" " %d\n" " %.15f\n" " %.15f\n" " %.15f\n" " %.15f\n" " %.15f\n" " %s\n" " %s\n", authenticator, platform.name, cross_project_id, hostid, core_client_major_version, core_client_minor_version, core_client_release, rpc_seqno, work_req_seconds, resource_share_fraction, rrs_fraction, prrs_fraction, estimated_delay, code_sign_key, anonymous_platform?"true":"false" ); for (i=0; i\n" " %s\n" " %d\n" " \n", client_app_versions[i].app_name, client_app_versions[i].version_num ); } fprintf(fout, " \n" " %s" " \n", global_prefs_xml ); fprintf(fout, " %s\n", global_prefs_source_email_hash ); fprintf(fout, " \n" " %d\n" " %d\n" " %d\n" " %.15f\n" " %.15f\n" " %.15f\n" " %.15f\n" " %.15f\n", host.id, host.rpc_time, host.timezone, host.d_total, host.d_free, host.d_boinc_used_total, host.d_boinc_used_project, host.d_boinc_max ); for (i=0; i\n" " %s\n" " %d\n" " %.15f\n" " %d\n" " %d\n" " \n", results[i].name, results[i].client_state, results[i].cpu_time, results[i].exit_status, results[i].app_version_num ); } for (i=0; i\n" " %s\n" " %s\n" " \n", msgs_from_host[i].variety, msgs_from_host[i].msg_text.c_str() ); } for (i=0; i\n" " %s\n" " \n", file_infos[i].name ); fprintf(fout, "\n"); } return 0; } int MSG_FROM_HOST_DESC::parse(FILE* fin) { char buf[256]; msg_text = ""; while (fgets(buf, sizeof(buf), fin)) { if (match_tag(buf, "")) return 0; if (parse_str(buf, "", variety, sizeof(variety))) continue; msg_text += buf; } return ERR_XML_PARSE; } SCHEDULER_REPLY::SCHEDULER_REPLY() { memset(&wreq, 0, sizeof(wreq)); memset(&disk_limits, 0, sizeof(disk_limits)); request_delay = 0; hostid = 0; send_global_prefs = false; strcpy(code_sign_key, ""); strcpy(code_sign_key_signature, ""); memset(&user, 0, sizeof(user)); memset(&host, 0, sizeof(host)); memset(&team, 0, sizeof(team)); nucleus_only = false; project_is_down = false; send_msg_ack = false; strcpy(email_hash, ""); } SCHEDULER_REPLY::~SCHEDULER_REPLY() { } int SCHEDULER_REPLY::write(FILE* fout) { unsigned int i; char buf[BLOB_SIZE]; // Note: at one point we had // "\n" // after the Content-type (to make it legit XML), // but this broke 4.19 clients // fprintf(fout, "Content-type: text/xml\n\n" "\n" "%d\n", BOINC_MAJOR_VERSION*100+BOINC_MINOR_VERSION ); if (strlen(config.master_url)) { fprintf(fout, "%s\n", config.master_url ); } if (config.ended) { fprintf(fout, " 1\n"); } // if the scheduler has requested a delay OR the sysadmin has configured // the scheduler with a minimum time between RPCs, send a delay request. // Make it 1% larger than the min required to take care of time skew. // If this is less than one second bigger, bump up by one sec. // if (request_delay || config.min_sendwork_interval) { double min_delay_needed = 1.01*config.min_sendwork_interval; if (min_delay_needed < config.min_sendwork_interval+1) { min_delay_needed = config.min_sendwork_interval+1; } if (request_delay%f\n", request_delay); } log_messages.printf(MSG_NORMAL, "Sending reply to [HOST#%d]: %d results, delay req %f [scheduler ran %f seconds]\n", host.id, wreq.nresults, request_delay, elapsed_wallclock_time() ); if (wreq.core_client_version <= 419) { std::string msg; std::string pri = "low"; for (i=0; i0) { // any newlines will break message printing under 4.19 and under! // replace them with spaces. // while (1) { string::size_type pos = msg.find("\n", 0); if (pos == string::npos) break; msg.replace(pos, 1, " "); } fprintf(fout, "%s\n", pri.c_str(), msg.c_str() ); } } else { for (i=0; i%s\n", um.priority.c_str(), um.message.c_str() ); } } fprintf(fout, "%s\n", config.long_name ); if (config.request_time_stats_log) { if (!have_time_stats_log(*this)) { fprintf(fout, "1\n"); } } if (project_is_down) { fprintf(fout,"\n"); goto end; } if (nucleus_only) goto end; if (strlen(config.symstore)) { fprintf(fout, "%s\n", config.symstore); } if (config.next_rpc_delay) { fprintf(fout, "%f\n", config.next_rpc_delay); } if (user.id) { xml_escape(user.name, buf, sizeof(buf)); fprintf(fout, "%s\n" "%f\n" "%f\n" "%d\n", buf, user.total_credit, user.expavg_credit, user.create_time ); // be paranoid about the following to avoid sending null // if (strlen(email_hash)) { fprintf(fout, "%s\n", email_hash ); } if (strlen(user.cross_project_id)) { fprintf(fout, "%s\n", user.cross_project_id ); } if (send_global_prefs) { fputs(user.global_prefs, fout); fputs("\n", fout); } // always send project prefs // fputs(user.project_prefs, fout); fputs("\n", fout); } if (hostid) { fprintf(fout, "%d\n", hostid ); } fprintf(fout, "%f\n" "%f\n" "%s\n" "%d\n", host.total_credit, host.expavg_credit, host.venue, host.create_time ); // might want to send team credit too. // if (team.id) { xml_escape(team.name, buf, sizeof(buf)); fprintf(fout, "%s\n", buf ); } else { fprintf(fout, "\n" ); } // acknowledge results // for (i=0; i\n" " %s\n" "\n", result_acks[i].c_str() ); } // abort results // for (i=0; i\n" " %s\n" "\n", result_aborts[i].c_str() ); } // abort results not started // for (i=0; i\n" " %s\n" "\n", result_abort_if_not_starteds[i].c_str() ); } for (i=0; i\n", fout); fputs(code_sign_key, fout); fputs("\n", fout); } if (strlen(code_sign_key_signature)) { fputs("\n", fout); fputs(code_sign_key_signature, fout); fputs("\n", fout); } if (send_msg_ack) { fputs("\n", fout); } for (i=0; i\n"); } if (config.verify_files_on_app_start) { fprintf(fout, "\n"); } for (i=0; i%s\n", file_deletes[i].name ); } gui_urls.get_gui_urls(user, host, team, buf); fputs(buf, fout); if (project_files.text) { fputs(project_files.text, fout); } end: fprintf(fout, "\n" ); return 0; } // set delay to the MAX of the existing value or the requested value // never send a delay request longer than two days. // void SCHEDULER_REPLY::set_delay(double delay) { if (request_delay < delay) { request_delay = delay; } if (request_delay > DELAY_MAX) { request_delay = DELAY_MAX; } } void SCHEDULER_REPLY::insert_app_unique(APP& app) { unsigned int i; for (i=0; i\n" " %s\n" " %s\n" "\n", name, user_friendly_name ); return 0; } int APP_VERSION::write(FILE* fout) { char buf[APP_VERSION_XML_BLOB_SIZE]; unsigned int i; strcpy(buf, xml_doc); char* p = strstr(buf, ""); if (!p) { fprintf(stderr, "ERROR: app version %d XML has no end tag!\n", id); return -1; } *p = 0; fputs(buf, fout); PLATFORM* pp = ssp->lookup_platform_id(platformid); fprintf(fout, " %s\n", pp->name); if (strlen(plan_class)) { fprintf(fout, " %s\n", plan_class); } fprintf(fout, " %f\n" " %f\n" " %f\n", bavp->host_usage.avg_ncpus, bavp->host_usage.max_ncpus, bavp->host_usage.flops ); if (strlen(bavp->host_usage.cmdline)) { fprintf(fout, " %s\n", bavp->host_usage.cmdline ); } for (i=0; ihost_usage.coprocs.coprocs.size(); i++) { COPROC* cp = bavp->host_usage.coprocs.coprocs[i]; fprintf(fout, " \n" " %s\n" " %d\n" " \n", cp->type, cp->count ); } fputs("\n", fout); return 0; } int RESULT::write_to_client(FILE* fout) { char buf[BLOB_SIZE]; strcpy(buf, xml_doc_in); char* p = strstr(buf, ""); if (!p) { fprintf(stderr, "ERROR: result %d XML has no end tag!\n", id); return -1; } *p = 0; fputs(buf, fout); APP_VERSION* avp = bavp->avp; if (avp == (APP_VERSION*)1) avp = NULL; if (avp) { PLATFORM* pp = ssp->lookup_platform_id(avp->platformid); fprintf(fout, " %s\n" " %d\n" " %s\n", pp->name, avp->version_num, avp->plan_class ); } fputs("\n", fout); return 0; } int RESULT::parse_from_client(FILE* fin) { char buf[256]; // should be non-zero if exit_status is not found exit_status = ERR_NO_EXIT_STATUS; memset(this, 0, sizeof(RESULT)); while (fgets(buf, sizeof(buf), fin)) { if (match_tag(buf, "")) return 0; if (parse_str(buf, "", name, sizeof(name))) continue; if (parse_int(buf, "", client_state)) continue; if (parse_double(buf, "", cpu_time)) continue; if (parse_int(buf, "", exit_status)) continue; if (parse_int(buf, "", app_version_num)) continue; if (parse_double(buf, "", fpops_per_cpu_sec)) continue; if (parse_double(buf, "", fpops_cumulative)) continue; if (parse_double(buf, "", intops_per_cpu_sec)) continue; if (parse_double(buf, "", intops_cumulative)) continue; if (match_tag(buf, "")) { safe_strcat(xml_doc_out, buf); while (fgets(buf, sizeof(buf), fin)) { safe_strcat(xml_doc_out, buf); if (match_tag(buf, "")) break; } continue; } if (match_tag(buf, "" )) { while (fgets(buf, sizeof(buf), fin)) { if (match_tag(buf, "")) break; safe_strcat(stderr_out, buf); } continue; } if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; log_messages.printf(MSG_NORMAL, "RESULT::parse_from_client(): unrecognized: %s\n", buf ); } return ERR_XML_PARSE; } int HOST::parse(FILE* fin) { char buf[1024]; p_ncpus = 1; while (fgets(buf, sizeof(buf), fin)) { if (match_tag(buf, "")) return 0; if (parse_int(buf, "", timezone)) continue; if (parse_str(buf, "", domain_name, sizeof(domain_name))) continue; if (parse_str(buf, "", serialnum, sizeof(serialnum))) continue; if (parse_str(buf, "", last_ip_addr, sizeof(last_ip_addr))) continue; if (parse_str(buf, "", host_cpid, sizeof(host_cpid))) continue; if (parse_int(buf, "", p_ncpus)) continue; if (parse_str(buf, "", p_vendor, sizeof(p_vendor))) continue; if (parse_str(buf, "", p_model, sizeof(p_model))) continue; if (parse_double(buf, "", p_fpops)) continue; if (parse_double(buf, "", p_iops)) continue; if (parse_double(buf, "", p_membw)) continue; if (parse_str(buf, "", os_name, sizeof(os_name))) continue; if (parse_str(buf, "", os_version, sizeof(os_version))) continue; if (parse_double(buf, "", m_nbytes)) continue; if (parse_double(buf, "", m_cache)) continue; if (parse_double(buf, "", m_swap)) continue; if (parse_double(buf, "", d_total)) continue; if (parse_double(buf, "", d_free)) continue; if (parse_double(buf, "", n_bwup)) continue; if (parse_double(buf, "", n_bwdown)) continue; // parse deprecated fields to avoid error messages // if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; // fields reported by 5.5+ clients, not currently used // if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; #if 1 // not sure where thees fields belong in the above // categories // if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; #endif log_messages.printf(MSG_NORMAL, "HOST::parse(): unrecognized: %s\n", buf ); } return ERR_XML_PARSE; } int HOST::parse_time_stats(FILE* fin) { char buf[256]; while (fgets(buf, sizeof(buf), fin)) { if (match_tag(buf, "")) return 0; if (parse_double(buf, "", on_frac)) continue; if (parse_double(buf, "", connected_frac)) continue; if (parse_double(buf, "", active_frac)) continue; if (parse_double(buf, "", cpu_efficiency)) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; log_messages.printf(MSG_NORMAL, "HOST::parse_time_stats(): unrecognized: %s\n", buf ); } return ERR_XML_PARSE; } int HOST::parse_net_stats(FILE* fin) { char buf[256]; while (fgets(buf, sizeof(buf), fin)) { if (match_tag(buf, "")) return 0; if (parse_double(buf, "", n_bwup)) continue; if (parse_double(buf, "", n_bwdown)) continue; // items reported by 5.10+ clients, not currently used // if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; if (match_tag(buf, "")) continue; log_messages.printf(MSG_NORMAL, "HOST::parse_net_stats(): unrecognized: %s\n", buf ); } return ERR_XML_PARSE; } int HOST::parse_disk_usage(FILE* fin) { char buf[256]; while (fgets(buf, sizeof(buf), fin)) { if (match_tag(buf, "")) return 0; if (parse_double(buf, "", d_boinc_used_total)) continue; if (parse_double(buf, "", d_boinc_used_project)) continue; log_messages.printf(MSG_NORMAL, "HOST::parse_disk_usage(): unrecognized: %s\n", buf ); } return ERR_XML_PARSE; } void GLOBAL_PREFS::parse(const char* buf, const char* venue) { char buf2[BLOB_SIZE]; double dtemp; defaults(); if (parse_double(buf, "", mod_time)) { // mod_time is outside of venue if (mod_time > dtime()) mod_time = dtime(); } extract_venue(buf, venue, buf2); parse_double(buf2, "", disk_max_used_gb); parse_double(buf2, "", disk_max_used_pct); parse_double(buf2, "", disk_min_free_gb); parse_double(buf2, "", work_buf_min_days); if (parse_double(buf2, "", dtemp)) { ram_max_used_busy_frac = dtemp/100.; } if (parse_double(buf2, "", dtemp)) { ram_max_used_idle_frac = dtemp/100.; } parse_double(buf2, "", max_ncpus_pct); } void GLOBAL_PREFS::defaults() { memset(this, 0, sizeof(GLOBAL_PREFS)); } void GUI_URLS::init() { text = 0; read_file_malloc("../gui_urls.xml", text); } void GUI_URLS::get_gui_urls(USER& user, HOST& host, TEAM& team, char* buf) { bool found; char userid[256], teamid[256], hostid[256]; strcpy(buf, ""); if (!text) return; strcpy(buf, text); sprintf(userid, "%d", user.id); sprintf(hostid, "%d", host.id); if (user.teamid) { sprintf(teamid, "%d", team.id); } else { strcpy(teamid, "0"); while (remove_element(buf, "", "")) { continue; } } while (1) { found = false; found |= str_replace(buf, "", userid); found |= str_replace(buf, "", user.name); found |= str_replace(buf, "", hostid); found |= str_replace(buf, "", teamid); found |= str_replace(buf, "", team.name); found |= str_replace(buf, "", user.authenticator); if (!found) break; } } void PROJECT_FILES::init() { text = 0; read_file_malloc("../project_files.xml", text); } const char *BOINC_RCSID_ea659117b3 = "$Id$";