// 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 "cpp.h" #ifdef _WIN32 #include "boinc_win.h" #include "zlib.h" #else #include "config.h" // Somehow having config.h define _FILE_OFFSET_BITS or _LARGE_FILES is // causing open to be redefined to open64 which somehow, in some versions // of zlib.h causes gzopen to be redefined as gzopen64 which subsequently gets // reported as a linker error. So for this file, we compile in small files // mode, regardless of these settings #undef _FILE_OFFSET_BITS #undef _LARGE_FILES #undef _LARGEFILE_SOURCE #undef _LARGEFILE64_SOURCE #include #include #include #include #endif #include "error_numbers.h" #include "file_names.h" #include "filesys.h" #include "client_msgs.h" #include "log_flags.h" #include "parse.h" #include "util.h" #include "str_util.h" #include "str_replace.h" #include "client_state.h" #include "pers_file_xfer.h" #include "sandbox.h" #include "client_types.h" using std::string; using std::vector; PROJECT::PROJECT() { init(); } void PROJECT::init() { strcpy(master_url, ""); strcpy(authenticator, ""); project_specific_prefs = ""; gui_urls = ""; resource_share = 100; for (int i=0; ino_rsc_ams[i] = true; } static void handle_no_rsc_pref(PROJECT* p, const char* name) { int i = rsc_index(name); if (i<0) return; p->no_rsc_pref[i] = true; } static void handle_no_rsc_apps(PROJECT* p, const char* name) { int i = rsc_index(name); if (i < 0) return; p->no_rsc_apps[i] = true; } static bool parse_rsc_param(XML_PARSER& xp, const char* end_tag, int& rsc_type, double& value) { char name[256]; bool val_found = false; rsc_type = -1; while (!xp.get_tag()) { if (xp.match_tag(end_tag)) { return (rsc_type > 0 && val_found); } if (xp.parse_str("name", name, sizeof(name))) { rsc_type = rsc_index(name); continue; } if (xp.parse_double("rsc_type", value)) { val_found = true; } } return false; } // parse project fields from client_state.xml // int PROJECT::parse_state(XML_PARSER& xp) { char buf[256]; std::string sched_url, stemp; string str1, str2; int retval, rt; double x; bool btemp; init(); while (!xp.get_tag()) { if (xp.match_tag("/project")) { if (cpid_time == 0) { cpid_time = user_create_time; } return 0; } if (xp.parse_string("scheduler_url", sched_url)) { scheduler_urls.push_back(sched_url); continue; } if (xp.parse_str("master_url", master_url, sizeof(master_url))) continue; if (xp.parse_str("project_name", project_name, sizeof(project_name))) continue; if (xp.parse_str("symstore", symstore, sizeof(symstore))) continue; if (xp.parse_str("user_name", user_name, sizeof(user_name))) continue; if (xp.parse_str("team_name", team_name, sizeof(team_name))) continue; if (xp.parse_str("host_venue", host_venue, sizeof(host_venue))) continue; if (xp.parse_str("email_hash", email_hash, sizeof(email_hash))) continue; if (xp.parse_str("cross_project_id", cross_project_id, sizeof(cross_project_id))) continue; if (xp.parse_double("cpid_time", cpid_time)) continue; if (xp.parse_double("user_total_credit", user_total_credit)) continue; if (xp.parse_double("user_expavg_credit", user_expavg_credit)) continue; if (xp.parse_double("user_create_time", user_create_time)) continue; if (xp.parse_int("rpc_seqno", rpc_seqno)) continue; if (xp.parse_int("userid", userid)) continue; if (xp.parse_int("teamid", teamid)) continue; if (xp.parse_int("hostid", hostid)) continue; if (xp.parse_double("host_total_credit", host_total_credit)) continue; if (xp.parse_double("host_expavg_credit", host_expavg_credit)) continue; if (xp.parse_double("host_create_time", host_create_time)) continue; if (xp.match_tag("code_sign_key")) { retval = copy_element_contents( xp.f->f, "", code_sign_key, sizeof(code_sign_key) ); if (retval) return retval; strip_whitespace(code_sign_key); continue; } if (xp.parse_int("nrpc_failures", nrpc_failures)) continue; if (xp.parse_int("master_fetch_failures", master_fetch_failures)) continue; if (xp.parse_double("min_rpc_time", min_rpc_time)) continue; if (xp.parse_bool("master_url_fetch_pending", master_url_fetch_pending)) continue; if (xp.parse_int("sched_rpc_pending", sched_rpc_pending)) continue; if (xp.parse_double("next_rpc_time", next_rpc_time)) continue; if (xp.parse_bool("trickle_up_pending", trickle_up_pending)) continue; if (xp.parse_int("send_time_stats_log", send_time_stats_log)) continue; if (xp.parse_int("send_job_log", send_job_log)) continue; if (xp.parse_bool("send_full_workload", send_full_workload)) continue; if (xp.parse_bool("non_cpu_intensive", non_cpu_intensive)) continue; if (xp.parse_bool("verify_files_on_app_start", verify_files_on_app_start)) continue; if (xp.parse_bool("suspended_via_gui", suspended_via_gui)) continue; if (xp.parse_bool("dont_request_more_work", dont_request_more_work)) continue; if (xp.parse_bool("detach_when_done", detach_when_done)) continue; if (xp.parse_bool("ended", ended)) continue; if (xp.parse_double("rec", pwf.rec)) continue; if (xp.parse_double("rec_time", pwf.rec_time)) continue; if (xp.parse_double("cpu_backoff_interval", rsc_pwf[0].backoff_interval)) continue; if (xp.parse_double("cpu_backoff_time", rsc_pwf[0].backoff_time)) { if (rsc_pwf[0].backoff_time > gstate.now + 28*SECONDS_PER_DAY) { rsc_pwf[0].backoff_time = gstate.now + 28*SECONDS_PER_DAY; } continue; } if (xp.match_tag("rsc_backoff_interval")) { if (parse_rsc_param(xp, "/rsc_backoff_interval", rt, x)) { rsc_pwf[rt].backoff_interval = x; } continue; } if (xp.match_tag("rsc_backoff_time")) { if (parse_rsc_param(xp, "/rsc_backoff_time", rt, x)) { rsc_pwf[rt].backoff_time = x; } continue; } if (xp.parse_double("resource_share", resource_share)) continue; // not authoritative if (xp.parse_double("duration_correction_factor", duration_correction_factor)) continue; if (xp.parse_bool("attached_via_acct_mgr", attached_via_acct_mgr)) continue; if (xp.parse_bool("no_cpu_apps", btemp)) { if (btemp) handle_no_rsc_apps(this, "CPU"); continue; } if (xp.parse_bool("no_cuda_apps", btemp)) { if (btemp) handle_no_rsc_apps(this, GPU_TYPE_NVIDIA); continue; } if (xp.parse_bool("no_ati_apps", btemp)) { if (btemp) handle_no_rsc_apps(this, GPU_TYPE_ATI); continue; } if (xp.parse_str("no_rsc_apps", buf, sizeof(buf))) { handle_no_rsc_apps(this, buf); continue; } if (xp.parse_bool("no_cpu_ams", btemp)) { if (btemp) handle_no_rsc_ams(this, "CPU"); continue; } if (xp.parse_bool("no_cuda_ams", btemp)) { if (btemp) handle_no_rsc_ams(this, GPU_TYPE_NVIDIA); continue; } if (xp.parse_bool("no_ati_ams", btemp)) { if (btemp) handle_no_rsc_ams(this, GPU_TYPE_ATI); continue; } if (xp.parse_str("no_rsc_ams", buf, sizeof(buf))) { handle_no_rsc_ams(this, buf); continue; } if (xp.parse_str("no_rsc_pref", buf, sizeof(buf))) { handle_no_rsc_pref(this, buf); continue; } // backwards compat - old state files had ams_resource_share = 0 if (xp.parse_double("ams_resource_share_new", ams_resource_share)) continue; if (xp.parse_double("ams_resource_share", x)) { if (x > 0) ams_resource_share = x; continue; } if (xp.parse_bool("scheduler_rpc_in_progress", btemp)) continue; if (xp.parse_bool("use_symlinks", use_symlinks)) continue; if (xp.parse_bool("anonymous_platform", btemp)) continue; if (xp.parse_string("trickle_up_url", stemp)) { trickle_up_ops.push_back(new TRICKLE_UP_OP(stemp)); continue; } if (log_flags.unparsed_xml) { msg_printf(0, MSG_INFO, "[unparsed_xml] PROJECT::parse_state(): unrecognized: %s", xp.parsed_tag ); } xp.skip_unexpected(); } return ERR_XML_PARSE; } // Write project information to client state file or GUI RPC reply // int PROJECT::write_state(MIOFILE& out, bool gui_rpc) { unsigned int i; char un[2048], tn[2048]; out.printf( "\n" ); xml_escape(user_name, un, sizeof(un)); xml_escape(team_name, tn, sizeof(tn)); out.printf( " %s\n" " %s\n" " %s\n" " %s\n" " %s\n" " %s\n" " %s\n" " %s\n" " %f\n" " %f\n" " %f\n" " %f\n" " %d\n" " %d\n" " %d\n" " %d\n" " %f\n" " %f\n" " %f\n" " %d\n" " %d\n" " %f\n" " %f\n" " %f\n" " %f\n" " %f\n" " %f\n" " %d\n" " %d\n" " %d\n" "%s%s%s%s%s%s%s%s%s%s%s%s%s", master_url, project_name, symstore, un, tn, host_venue, email_hash, cross_project_id, cpid_time, user_total_credit, user_expavg_credit, user_create_time, rpc_seqno, userid, teamid, hostid, host_total_credit, host_expavg_credit, host_create_time, nrpc_failures, master_fetch_failures, min_rpc_time, next_rpc_time, pwf.rec, pwf.rec_time, resource_share, duration_correction_factor, sched_rpc_pending, send_time_stats_log, send_job_log, anonymous_platform?" \n":"", master_url_fetch_pending?" \n":"", trickle_up_pending?" \n":"", send_full_workload?" \n":"", non_cpu_intensive?" \n":"", verify_files_on_app_start?" \n":"", suspended_via_gui?" \n":"", dont_request_more_work?" \n":"", detach_when_done?" \n":"", ended?" \n":"", attached_via_acct_mgr?" \n":"", (this == gstate.scheduler_op->cur_proj)?" \n":"", use_symlinks?" \n":"" ); for (int j=0; j\n" " %s\n" " %f\n" " \n" " \n" " %s\n" " %f\n" " \n", rsc_name(j), rsc_pwf[j].backoff_interval, rsc_name(j), rsc_pwf[j].backoff_time ); if (no_rsc_ams[j]) { out.printf(" %s\n", rsc_name(j)); } if (no_rsc_apps[j]) { out.printf(" %s\n", rsc_name(j)); } if (no_rsc_pref[j]) { out.printf(" %s\n", rsc_name(j)); } } if (ams_resource_share >= 0) { out.printf(" %f\n", ams_resource_share ); } if (gui_rpc) { out.printf( "%s" " %f\n" " %f\n" " %f\n", gui_urls.c_str(), sched_priority, last_rpc_time, project_files_downloaded_time ); if (download_backoff.next_xfer_time > gstate.now) { out.printf( " %f\n", download_backoff.next_xfer_time - gstate.now ); } if (upload_backoff.next_xfer_time > gstate.now) { out.printf( " %f\n", upload_backoff.next_xfer_time - gstate.now ); } if (strlen(host_venue)) { out.printf(" %s\n", host_venue); } } else { for (i=0; i%s\n", scheduler_urls[i].c_str() ); } if (strlen(code_sign_key)) { out.printf( " \n%s\n\n", code_sign_key ); } for (i=0; i%s\n", t->url.c_str() ); } } out.printf( "\n" ); return 0; } // Some project data is stored in account file, other in client_state.xml // Copy fields that are stored in client_state.xml from "p" into "this" // 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); safe_strcpy(host_venue, p.host_venue); safe_strcpy(email_hash, p.email_hash); safe_strcpy(cross_project_id, p.cross_project_id); user_total_credit = p.user_total_credit; user_expavg_credit = p.user_expavg_credit; user_create_time = p.user_create_time; cpid_time = p.cpid_time; rpc_seqno = p.rpc_seqno; userid = p.userid; teamid = p.teamid; hostid = p.hostid; host_total_credit = p.host_total_credit; host_expavg_credit = p.host_expavg_credit; host_create_time = p.host_create_time; nrpc_failures = p.nrpc_failures; master_fetch_failures = p.master_fetch_failures; min_rpc_time = p.min_rpc_time; next_rpc_time = p.next_rpc_time; master_url_fetch_pending = p.master_url_fetch_pending; sched_rpc_pending = p.sched_rpc_pending; trickle_up_pending = p.trickle_up_pending; safe_strcpy(code_sign_key, p.code_sign_key); for (int i=0; i= 0) { resource_share = ams_resource_share; } use_symlinks = p.use_symlinks; } // Write project statistic to project statistics file // int PROJECT::write_statistics(MIOFILE& out, bool /*gui_rpc*/) { out.printf( "\n" " %s\n", master_url ); for (std::vector::iterator i=statistics.begin(); i!=statistics.end(); ++i ) { out.printf( " \n" " %f\n" " %f\n" " %f\n" " %f\n" " %f\n" " \n", i->day, i->user_total_credit, i->user_expavg_credit, i->host_total_credit, i->host_expavg_credit ); } out.printf( "\n" ); return 0; } void PROJECT::suspend() { suspended_via_gui = true; gstate.request_schedule_cpus("project suspended"); gstate.request_work_fetch("project suspended"); } void PROJECT::resume() { suspended_via_gui = false; gstate.request_schedule_cpus("project resumed"); gstate.request_work_fetch("project resumed"); } void PROJECT::abort_not_started() { for (unsigned int i=0; iproject != this) continue; if (rp->is_not_started()) { rp->abort_inactive(ERR_ABORTED_VIA_GUI); } } } void PROJECT::get_task_durs(double& not_started_dur, double& in_progress_dur) { not_started_dur = 0; in_progress_dur = 0; for (unsigned int i=0; iproject != this) continue; double d = rp->estimated_time_remaining(); if (rp->is_not_started()) { not_started_dur += d; } else { in_progress_dur += d; } } } const char* PROJECT::get_scheduler_url(int index, double r) { int n = (int) scheduler_urls.size(); int ir = (int)(r*n); int i = (index + ir)%n; return scheduler_urls[i].c_str(); } bool FILE_XFER_BACKOFF::ok_to_transfer() { double dt = next_xfer_time - gstate.now; if (dt > gstate.pers_retry_delay_max) { // must have changed the system clock // dt = 0; } return (dt <= 0); } void FILE_XFER_BACKOFF::file_xfer_failed(PROJECT* p) { file_xfer_failures++; if (file_xfer_failures < FILE_XFER_FAILURE_LIMIT) { next_xfer_time = 0; } else { double backoff = calculate_exponential_backoff( file_xfer_failures, gstate.pers_retry_delay_min, gstate.pers_retry_delay_max ); if (log_flags.file_xfer_debug) { msg_printf(p, MSG_INFO, "[file_xfer] project-wide xfer delay for %f sec", backoff ); } next_xfer_time = gstate.now + backoff; } } void FILE_XFER_BACKOFF::file_xfer_succeeded() { file_xfer_failures = 0; next_xfer_time = 0; } int PROJECT::parse_project_files(XML_PARSER& xp, bool delete_existing_symlinks) { unsigned int i; char project_dir[256], path[256]; int retval; if (delete_existing_symlinks) { // delete current sym links. // This is done when parsing scheduler reply, // to ensure that we get rid of sym links for // project files no longer in use // get_project_dir(this, project_dir, sizeof(project_dir)); for (i=0; i::iterator fref_iter; fref_iter = project_files.begin(); while (fref_iter != project_files.end()) { FILE_REF& fref = *fref_iter; fip = gstate.lookup_file_info(this, fref.file_name); if (!fip) { msg_printf(this, MSG_INTERNAL_ERROR, "project file refers to non-existent %s", fref.file_name ); fref_iter = project_files.erase(fref_iter); continue; } fref.file_info = fip; fip->is_project_file = true; fref_iter++; } if (recreate_symlink_files) { for (unsigned i=0; iproject == this && fip->is_project_file && fip->status == FILE_PRESENT) { write_symlink_for_project_file(fip); } } } } void PROJECT::write_project_files(MIOFILE& f) { unsigned int i; if (!project_files.size()) return; f.printf("\n"); for (i=0; i\n"); } // write symlinks for project files. // Note: it's conceivable that one physical file // has several logical names, so try them all // int PROJECT::write_symlink_for_project_file(FILE_INFO* fip) { char project_dir[256], link_path[256], file_path[256]; unsigned int i; get_project_dir(this, project_dir, sizeof(project_dir)); for (i=0; iname); make_soft_link(this, link_path, file_path); } return 0; } // a project file download just finished. // If it's the last one, update project_files_downloaded_time // void PROJECT::update_project_files_downloaded_time() { unsigned int i; for (i=0; istatus != FILE_PRESENT) continue; } project_files_downloaded_time = gstate.now; } int APP::parse(XML_PARSER& xp) { strcpy(name, ""); strcpy(user_friendly_name, ""); project = NULL; non_cpu_intensive = false; while (!xp.get_tag()) { if (xp.match_tag("/app")) { if (!strlen(user_friendly_name)) { strcpy(user_friendly_name, name); } return 0; } if (xp.parse_str("name", name, sizeof(name))) continue; if (xp.parse_str("user_friendly_name", user_friendly_name, sizeof(user_friendly_name))) continue; #ifdef SIM if (xp.parse_double("latency_bound", latency_bound)) continue; if (xp.parse_double("fpops_est", fpops_est)) continue; if (xp.parse_double("weight", weight)) continue; if (xp.parse_double("working_set", working_set)) continue; if (xp.match_tag("fpops")) { fpops.parse(xp, "/fpops"); continue; } if (xp.match_tag("checkpoint_period")) { checkpoint_period.parse(xp, "/checkpoint_period"); continue; } if (xp.parse_bool("non_cpu_intensive", non_cpu_intensive)) continue; #endif if (log_flags.unparsed_xml) { msg_printf(0, MSG_INFO, "[unparsed_xml] APP::parse(): unrecognized: %s\n", xp.parsed_tag ); } xp.skip_unexpected(); } return ERR_XML_PARSE; } int APP::write(MIOFILE& out) { out.printf( "\n" " %s\n" " %s\n" " %d\n" "\n", name, user_friendly_name, non_cpu_intensive?1:0 ); return 0; } FILE_INFO::FILE_INFO() { strcpy(name, ""); strcpy(md5_cksum, ""); max_nbytes = 0; nbytes = 0; upload_offset = -1; status = FILE_NOT_PRESENT; executable = false; uploaded = false; sticky = false; gzip_when_done = false; signature_required = false; is_user_file = false; is_project_file = false; is_auto_update_file = false; anonymous_platform_file = false; pers_file_xfer = NULL; result = NULL; project = NULL; download_urls.clear(); upload_urls.clear(); strcpy(xml_signature, ""); strcpy(file_signature, ""); cert_sigs = 0; } void FILE_INFO::reset() { status = FILE_NOT_PRESENT; delete_file(); error_msg = ""; } // 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, sizeof(pathname)); if (g_use_sandbox) { // give exec permissions for user, group and others but give // read permissions only for user and group to protect account keys retval = set_to_project_group(pathname); if (retval) return retval; if (executable) { retval = chmod(pathname, S_IRUSR|S_IWUSR|S_IXUSR |S_IRGRP|S_IWGRP|S_IXGRP |S_IXOTH ); } else { retval = chmod(pathname, S_IRUSR|S_IWUSR |S_IRGRP|S_IWGRP ); } } else { // give read/exec permissions for user, group and others // in case someone runs BOINC from different user if (executable) { retval = chmod(pathname, S_IRUSR|S_IWUSR|S_IXUSR |S_IRGRP|S_IXGRP |S_IROTH|S_IXOTH ); } else { retval = chmod(pathname, S_IRUSR|S_IWUSR |S_IRGRP |S_IROTH ); } } return retval; #endif } int FILE_INFO::parse(XML_PARSER& xp) { char buf2[1024]; std::string url; PERS_FILE_XFER *pfxp; int retval; bool btemp; while (!xp.get_tag()) { if (xp.match_tag("/file_info") || xp.match_tag("/file")) { if (!strlen(name)) return ERR_BAD_FILENAME; if (strstr(name, "..")) return ERR_BAD_FILENAME; if (strstr(name, "%")) return ERR_BAD_FILENAME; return 0; } if (xp.match_tag("xml_signature")) { retval = copy_element_contents( xp.f->f, "", xml_signature, sizeof(xml_signature) ); if (retval) return retval; strip_whitespace(xml_signature); continue; } if (xp.match_tag("file_signature")) { retval = copy_element_contents( xp.f->f, "", file_signature, sizeof(file_signature) ); if (retval) return retval; strip_whitespace(file_signature); continue; } if (xp.match_tag("signatures")) { if (!cert_sigs->parse(xp)) { msg_printf(0, MSG_INTERNAL_ERROR, "FILE_INFO::parse(): cannot parse \n" ); return ERR_XML_PARSE; } continue; } if (xp.parse_str("name", name, sizeof(name))) continue; if (xp.parse_string("url", url)) { if (strstr(url.c_str(), "file_upload_handler")) { upload_urls.urls.push_back(url); } else { download_urls.urls.push_back(url); } continue; } if (xp.parse_string("download_url", url)) { download_urls.urls.push_back(url); continue; } if (xp.parse_string("upload_url", url)) { upload_urls.urls.push_back(url); continue; } if (xp.parse_str("md5_cksum", md5_cksum, sizeof(md5_cksum))) continue; if (xp.parse_double("nbytes", nbytes)) continue; if (xp.parse_double("max_nbytes", max_nbytes)) continue; if (xp.parse_int("status", status)) continue; if (xp.parse_bool("executable", executable)) continue; if (xp.parse_bool("uploaded", uploaded)) continue; if (xp.parse_bool("sticky", sticky)) continue; if (xp.parse_bool("gzip_when_done", gzip_when_done)) continue; if (xp.parse_bool("signature_required", signature_required)) continue; if (xp.parse_bool("is_project_file", is_project_file)) continue; if (xp.parse_bool("no_delete", btemp)) continue; if (xp.match_tag("persistent_file_xfer")) { pfxp = new PERS_FILE_XFER; retval = pfxp->parse(xp); #ifdef SIM delete pfxp; continue; #endif if (!retval) { pers_file_xfer = pfxp; } else { delete pfxp; } continue; } if (xp.match_tag("file_xfer")) { while (!xp.get_tag()) { if (xp.match_tag("/file_xfer")) break; } continue; } if (xp.match_tag("error_msg")) { retval = copy_element_contents( xp.f->f, "", buf2, sizeof(buf2) ); if (retval) return retval; error_msg = buf2; continue; } // deprecated tags if (xp.parse_bool("generated_locally", btemp)) continue; if (xp.parse_bool("upload_when_present", btemp)) continue; if (log_flags.unparsed_xml) { msg_printf(0, MSG_INFO, "[unparsed_xml] FILE_INFO::parse(): unrecognized: %s\n", xp.parsed_tag ); } xp.skip_unexpected(); } return ERR_XML_PARSE; } int FILE_INFO::write(MIOFILE& out, bool to_server) { unsigned int i; int retval; char buf[1024]; if (to_server) { out.printf("\n"); } else { out.printf("\n"); } out.printf( " %s\n" " %f\n" " %f\n", name, nbytes, max_nbytes ); if (strlen(md5_cksum)) { out.printf( " %s\n", md5_cksum ); } if (!to_server) { out.printf(" %d\n", status); if (executable) out.printf(" \n"); if (uploaded) out.printf(" \n"); if (sticky) out.printf(" \n"); if (gzip_when_done) out.printf(" \n"); if (signature_required) out.printf(" \n"); if (is_user_file) out.printf(" \n"); if (strlen(file_signature)) out.printf(" \n%s\n\n", file_signature); } for (i=0; i%s\n", buf); } for (i=0; i%s\n", buf); } if (!to_server && pers_file_xfer) { retval = pers_file_xfer->write(out); if (retval) return retval; } if (!to_server) { if (strlen(xml_signature)) { out.printf( " \n%s \n", xml_signature ); } } if (!error_msg.empty()) { strip_whitespace(error_msg); out.printf(" \n%s\n\n", error_msg.c_str()); } if (to_server) { out.printf("\n"); } else { out.printf("\n"); } return 0; } // called only for files with a PERS_FILE_XFER // int FILE_INFO::write_gui(MIOFILE& out) { out.printf( "\n" " %s\n" " %s\n" " %s\n" " %f\n" " %f\n" " %d\n", project->master_url, project->project_name, name, nbytes, max_nbytes, status ); pers_file_xfer->write(out); FILE_XFER_BACKOFF& fxb = project->file_xfer_backoff(pers_file_xfer->is_upload); if (fxb.next_xfer_time > gstate.now) { out.printf(" %f\n", fxb.next_xfer_time - gstate.now ); } out.printf("\n"); return 0; } // delete physical underlying file associated with FILE_INFO // int FILE_INFO::delete_file() { char path[256]; get_pathname(this, path, sizeof(path)); int retval = delete_project_owned_file(path, true); if (retval && status != FILE_NOT_PRESENT) { msg_printf(project, MSG_INTERNAL_ERROR, "Couldn't delete file %s", path); } status = FILE_NOT_PRESENT; return retval; } const char* URL_LIST::get_init_url() { if (!urls.size()) { return NULL; } // if a project supplies multiple URLs, try them in order // (e.g. in Einstein@home they're ordered by proximity to client). // current_index = 0; start_index = current_index; return urls[current_index].c_str(); } // Call this to get the next URL. // NULL return means you've tried them all. // const char* URL_LIST::get_next_url() { if (!urls.size()) return NULL; while(1) { current_index = (current_index + 1)%((int)urls.size()); if (current_index == start_index) { return NULL; } return urls[current_index].c_str(); } } const char* URL_LIST::get_current_url(FILE_INFO& fi) { if (current_index < 0) { return get_init_url(); } if (current_index >= (int)urls.size()) { msg_printf(fi.project, MSG_INTERNAL_ERROR, "File %s has no URL", fi.name ); return NULL; } return urls[current_index].c_str(); } // merges information from a new FILE_INFO that has the same name as one // that is already present in the client state file. // int FILE_INFO::merge_info(FILE_INFO& new_info) { char buf[256]; if (max_nbytes <= 0 && new_info.max_nbytes) { max_nbytes = new_info.max_nbytes; sprintf(buf, " %.0f\n", new_info.max_nbytes); } // replace existing URLs with new ones // download_urls.replace(new_info.download_urls); upload_urls.replace(new_info.upload_urls); // replace signatures // if (strlen(new_info.file_signature)) { strcpy(file_signature, new_info.file_signature); } if (strlen(new_info.xml_signature)) { strcpy(xml_signature, new_info.xml_signature); } return 0; } // 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; } void FILE_INFO::failure_message(string& s) { char buf[1024]; sprintf(buf, "\n" " %s\n" " %d\n", name, status ); s = buf; if (error_msg.size()) { sprintf(buf, " %s\n", error_msg.c_str() ); s = s + buf; } s = s + "\n"; } #define BUFSIZE 16384 int FILE_INFO::gzip() { char buf[BUFSIZE]; char inpath[256], outpath[256]; get_pathname(this, inpath, sizeof(inpath)); strcpy(outpath, inpath); strcat(outpath, ".gz"); FILE* in = boinc_fopen(inpath, "rb"); if (!in) return ERR_FOPEN; gzFile out = gzopen(outpath, "wb"); while (1) { int n = (int)fread(buf, 1, BUFSIZE, in); if (n <= 0) break; int m = gzwrite(out, buf, n); if (m != n) { fclose(in); gzclose(out); return ERR_WRITE; } } fclose(in); gzclose(out); delete_project_owned_file(inpath, true); boinc_rename(outpath, inpath); return 0; } int APP_VERSION::parse(XML_PARSER& xp) { FILE_REF file_ref; double dtemp; strcpy(app_name, ""); strcpy(api_version, ""); version_num = 0; strcpy(platform, ""); strcpy(plan_class, ""); strcpy(cmdline, ""); strcpy(file_prefix, ""); avg_ncpus = 1; max_ncpus = 1; gpu_usage.rsc_type = 0; gpu_usage.usage = 0; gpu_ram = 0; app = NULL; project = NULL; flops = gstate.host_info.p_fpops; missing_coproc = false; strcpy(missing_coproc_name, ""); dont_throttle = false; needs_network = false; while (!xp.get_tag()) { if (xp.match_tag("/app_version")) return 0; if (xp.parse_str("app_name", app_name, sizeof(app_name))) continue; if (xp.match_tag("file_ref")) { file_ref.parse(xp); app_files.push_back(file_ref); continue; } if (xp.parse_int("version_num", version_num)) continue; if (xp.parse_str("api_version", api_version, sizeof(api_version))) continue; if (xp.parse_str("platform", platform, sizeof(platform))) continue; if (xp.parse_str("plan_class", plan_class, sizeof(plan_class))) continue; if (xp.parse_double("avg_ncpus", avg_ncpus)) continue; if (xp.parse_double("max_ncpus", max_ncpus)) continue; if (xp.parse_double("flops", dtemp)) { if (dtemp <= 0) { msg_printf(0, MSG_INTERNAL_ERROR, "non-positive FLOPS in app version" ); } else { flops = dtemp; } continue; } if (xp.parse_str("cmdline", cmdline, sizeof(cmdline))) continue; if (xp.parse_str("file_prefix", file_prefix, sizeof(file_prefix))) continue; if (xp.parse_double("gpu_ram", gpu_ram)) continue; if (xp.match_tag("coproc")) { COPROC_REQ cp; int retval = cp.parse(xp); if (!retval) { int rt = rsc_index(cp.type); if (rt > 0) { gpu_usage.rsc_type = rt; gpu_usage.usage = cp.count; } else { missing_coproc = true; missing_coproc_usage = cp.count; strcpy(missing_coproc_name, cp.type); } } else { msg_printf(0, MSG_INTERNAL_ERROR, "Error parsing "); } continue; } if (xp.parse_bool("dont_throttle", dont_throttle)) continue; if (xp.parse_bool("needs_network", needs_network)) continue; if (log_flags.unparsed_xml) { msg_printf(0, MSG_INFO, "[unparsed_xml] APP_VERSION::parse(): unrecognized: %s\n", xp.parsed_tag ); } xp.skip_unexpected(); } return ERR_XML_PARSE; } int APP_VERSION::write(MIOFILE& out, bool write_file_info) { unsigned int i; int retval; out.printf( "\n" " %s\n" " %d\n" " %s\n" " %f\n" " %f\n" " %f\n", app_name, version_num, platform, avg_ncpus, max_ncpus, flops ); if (strlen(plan_class)) { out.printf(" %s\n", plan_class); } if (strlen(api_version)) { out.printf(" %s\n", api_version); } if (strlen(cmdline)) { out.printf(" %s\n", cmdline); } if (strlen(file_prefix)) { out.printf(" %s\n", file_prefix); } if (write_file_info) { for (i=0; i\n" " %s\n" " %f\n" " \n", p, gpu_usage.usage ); } if (missing_coproc && strlen(missing_coproc_name)) { out.printf( " \n" " %s\n" " %f\n" " \n", missing_coproc_name, missing_coproc_usage ); } if (gpu_ram) { out.printf( " %f\n", gpu_ram ); } if (dont_throttle) { out.printf( " \n" ); } if (needs_network) { out.printf( " \n" ); } out.printf( "\n" ); return 0; } bool APP_VERSION::had_download_failure(int& failnum) { unsigned int i; for (i=0; ihad_failure(failnum)) { return true; } } return false; } void APP_VERSION::get_file_errors(string& str) { int errnum; unsigned int i; FILE_INFO* fip; string msg; str = "couldn't get input files:\n"; for (i=0; ihad_failure(errnum)) { fip->failure_message(msg); str = str + msg; } } } void APP_VERSION::clear_errors() { int x; unsigned int i; for (i=0; ihad_failure(x)) { fip->reset(); } } } int APP_VERSION::api_major_version() { int v, n; n = sscanf(api_version, "%d", &v); if (n != 1) return 0; return v; } int FILE_REF::parse(XML_PARSER& xp) { bool temp; strcpy(file_name, ""); strcpy(open_name, ""); main_program = false; copy_file = false; optional = false; while (!xp.get_tag()) { if (xp.match_tag("/file_ref")) return 0; if (xp.parse_str("file_name", file_name, sizeof(file_name))) continue; if (xp.parse_str("open_name", open_name, sizeof(open_name))) continue; if (xp.parse_bool("main_program", main_program)) continue; if (xp.parse_bool("copy_file", copy_file)) continue; if (xp.parse_bool("optional", optional)) continue; if (xp.parse_bool("no_validate", temp)) continue; if (log_flags.unparsed_xml) { msg_printf(0, MSG_INFO, "[unparsed_xml] FILE_REF::parse(): unrecognized: '%s'\n", xp.parsed_tag ); } xp.skip_unexpected(); } return ERR_XML_PARSE; } int FILE_REF::write(MIOFILE& out) { out.printf( " \n" " %s\n", file_name ); if (strlen(open_name)) { out.printf(" %s\n", open_name); } if (main_program) { out.printf(" \n"); } if (copy_file) { out.printf(" \n"); } if (optional) { out.printf(" \n"); } out.printf(" \n"); return 0; } int WORKUNIT::parse(XML_PARSER& xp) { FILE_REF file_ref; double dtemp; strcpy(name, ""); strcpy(app_name, ""); version_num = 0; 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 (!xp.get_tag()) { if (xp.match_tag("/workunit")) return 0; if (xp.parse_str("name", name, sizeof(name))) continue; if (xp.parse_str("app_name", app_name, sizeof(app_name))) continue; if (xp.parse_int("version_num", version_num)) continue; if (xp.parse_string("command_line", command_line)) { strip_whitespace(command_line); continue; } //if (xp.parse_str("env_vars", env_vars, sizeof(env_vars))) continue; if (xp.parse_double("rsc_fpops_est", rsc_fpops_est)) continue; if (xp.parse_double("rsc_fpops_bound", rsc_fpops_bound)) continue; if (xp.parse_double("rsc_memory_bound", rsc_memory_bound)) continue; if (xp.parse_double("rsc_disk_bound", rsc_disk_bound)) continue; if (xp.match_tag("file_ref")) { file_ref.parse(xp); #ifndef SIM input_files.push_back(file_ref); #endif continue; } // unused stuff if (xp.parse_double("credit", dtemp)) continue; if (log_flags.unparsed_xml) { msg_printf(0, MSG_INFO, "[unparsed_xml] WORKUNIT::parse(): unrecognized: %s\n", xp.parsed_tag ); } xp.skip_unexpected(); } return ERR_XML_PARSE; } int WORKUNIT::write(MIOFILE& out) { unsigned int i; out.printf( "\n" " %s\n" " %s\n" " %d\n" //" %s\n" " %f\n" " %f\n" " %f\n" " %f\n", name, app_name, version_num, //env_vars, rsc_fpops_est, rsc_fpops_bound, rsc_memory_bound, rsc_disk_bound ); if (command_line.size()) { out.printf( " \n" "%s\n" " \n", command_line.c_str() ); } for (i=0; i\n"); return 0; } bool WORKUNIT::had_download_failure(int& failnum) { unsigned int i; for (i=0;ihad_failure(failnum)) { return true; } } return false; } void WORKUNIT::get_file_errors(string& str) { int x; unsigned int i; FILE_INFO* fip; string msg; str = "couldn't get input files:\n"; for (i=0;ihad_failure(x)) { fip->failure_message(msg); str = str + msg; } } } // if any input files had download error from previous WU, // reset them to try download again // void WORKUNIT::clear_errors() { int x; unsigned int i; for (i=0; ihad_failure(x)) { fip->reset(); } } } int RESULT::parse_name(XML_PARSER& xp, const char* end_tag) { strcpy(name, ""); while (!xp.get_tag()) { if (xp.match_tag(end_tag)) return 0; if (xp.parse_str("name", name, sizeof(name))) continue; if (log_flags.unparsed_xml) { msg_printf(0, MSG_INFO, "[unparsed_xml] RESULT::parse_name(): unrecognized: %s\n", xp.parsed_tag ); } xp.skip_unexpected(); } return ERR_XML_PARSE; } void RESULT::clear() { strcpy(name, ""); strcpy(wu_name, ""); report_deadline = 0; received_time = 0; output_files.clear(); _state = RESULT_NEW; ready_to_report = false; completed_time = 0; got_server_ack = false; final_cpu_time = 0; final_elapsed_time = 0; #ifdef SIM peak_flop_count = 0; #endif exit_status = 0; stderr_out = ""; suspended_via_gui = false; rr_sim_misses_deadline = false; fpops_per_cpu_sec = 0; fpops_cumulative = 0; intops_per_cpu_sec = 0; intops_cumulative = 0; app = NULL; wup = NULL; project = NULL; version_num = 0; strcpy(platform, ""); strcpy(plan_class, ""); strcpy(resources, ""); coproc_missing = false; report_immediately = false; schedule_backoff = 0; } // parse a element from scheduling server. // int RESULT::parse_server(XML_PARSER& xp) { FILE_REF file_ref; clear(); while (!xp.get_tag()) { if (xp.match_tag("/result")) return 0; if (xp.parse_str("name", name, sizeof(name))) continue; if (xp.parse_str("wu_name", wu_name, sizeof(wu_name))) continue; if (xp.parse_double("report_deadline", report_deadline)) continue; if (xp.parse_str("platform", platform, sizeof(platform))) continue; if (xp.parse_str("plan_class", plan_class, sizeof(plan_class))) continue; if (xp.parse_int("version_num", version_num)) continue; if (xp.match_tag("file_ref")) { file_ref.parse(xp); output_files.push_back(file_ref); continue; } if (xp.parse_bool("report_immediately", report_immediately)) continue; if (log_flags.unparsed_xml) { msg_printf(0, MSG_INFO, "[unparsed_xml] RESULT::parse(): unrecognized: %s\n", xp.parsed_tag ); } xp.skip_unexpected(); } return ERR_XML_PARSE; } // parse a element from state file // int RESULT::parse_state(XML_PARSER& xp) { FILE_REF file_ref; clear(); while (!xp.get_tag()) { if (xp.match_tag("/result")) { // set state to something reasonable in case of bad state file // if (got_server_ack || ready_to_report) { switch (state()) { case RESULT_NEW: case RESULT_FILES_DOWNLOADING: case RESULT_FILES_DOWNLOADED: case RESULT_FILES_UPLOADING: set_state(RESULT_FILES_UPLOADED, "RESULT::parse_state"); break; } } return 0; } if (xp.parse_str("name", name, sizeof(name))) continue; if (xp.parse_str("wu_name", wu_name, sizeof(wu_name))) continue; if (xp.parse_double("received_time", received_time)) continue; if (xp.parse_double("report_deadline", report_deadline)) { continue; } if (xp.match_tag("file_ref")) { file_ref.parse(xp); #ifndef SIM output_files.push_back(file_ref); #endif continue; } if (xp.parse_double("final_cpu_time", final_cpu_time)) continue; if (xp.parse_double("final_elapsed_time", final_elapsed_time)) continue; if (xp.parse_int("exit_status", exit_status)) continue; if (xp.parse_bool("got_server_ack", got_server_ack)) continue; if (xp.parse_bool("ready_to_report", ready_to_report)) continue; if (xp.parse_double("completed_time", completed_time)) continue; if (xp.parse_bool("suspended_via_gui", suspended_via_gui)) continue; if (xp.parse_bool("report_immediately", report_immediately)) continue; if (xp.parse_int("state", _state)) continue; if (xp.parse_string("stderr_out", stderr_out)) continue; if (xp.parse_double("fpops_per_cpu_sec", fpops_per_cpu_sec)) continue; if (xp.parse_double("fpops_cumulative", fpops_cumulative)) continue; if (xp.parse_double("intops_per_cpu_sec", intops_per_cpu_sec)) continue; if (xp.parse_double("intops_cumulative", intops_cumulative)) continue; if (xp.parse_str("platform", platform, sizeof(platform))) continue; if (xp.parse_str("plan_class", plan_class, sizeof(plan_class))) continue; if (xp.parse_int("version_num", version_num)) continue; if (log_flags.unparsed_xml) { msg_printf(0, MSG_INFO, "[unparsed_xml] RESULT::parse(): unrecognized: %s\n", xp.parsed_tag ); } xp.skip_unexpected(); } return ERR_XML_PARSE; } int RESULT::write(MIOFILE& out, bool to_server) { unsigned int i; FILE_INFO* fip; int n, retval; out.printf( "\n" " %s\n" " %f\n" " %f\n" " %d\n" " %d\n" " %s\n" " %d\n", name, final_cpu_time, final_elapsed_time, exit_status, state(), platform, version_num ); if (strlen(plan_class)) { out.printf(" %s\n", plan_class); } if (fpops_per_cpu_sec) { out.printf(" %f\n", fpops_per_cpu_sec); } if (fpops_cumulative) { out.printf(" %f\n", fpops_cumulative); } if (intops_per_cpu_sec) { out.printf(" %f\n", intops_per_cpu_sec); } if (intops_cumulative) { out.printf(" %f\n", intops_cumulative); } if (to_server) { out.printf( " %d\n", wup->version_num ); } n = (int)stderr_out.length(); if (n || to_server) { out.printf("\n"); // the following is here so that it gets recorded on server // (there's no core_client_version field of result table) // if (to_server) { out.printf( "%d.%d.%d\n", gstate.core_client_version.major, gstate.core_client_version.minor, gstate.core_client_version.release ); } if (n) { out.printf("\n"); } out.printf("\n"); } if (to_server) { for (i=0; iuploaded) { retval = fip->write(out, true); if (retval) return retval; } } } else { if (got_server_ack) out.printf(" \n"); if (ready_to_report) out.printf(" \n"); if (completed_time) out.printf(" %f\n", completed_time); if (suspended_via_gui) out.printf(" \n"); if (report_immediately) out.printf(" \n"); out.printf( " %s\n" " %f\n" " %f\n", wu_name, report_deadline, received_time ); for (i=0; i\n"); return 0; } #ifndef SIM int RESULT::write_gui(MIOFILE& out) { out.printf( "\n" " %s\n" " %s\n" " %d\n" " %s\n" " %s\n" " %f\n" " %f\n" " %d\n" " %d\n" " %f\n" " %f\n" " %f\n", name, wu_name, version_num, plan_class, project->master_url, final_cpu_time, final_elapsed_time, exit_status, state(), report_deadline, received_time, estimated_time_remaining() ); if (got_server_ack) out.printf(" \n"); if (ready_to_report) out.printf(" \n"); if (completed_time) out.printf(" %f\n", completed_time); if (suspended_via_gui) out.printf(" \n"); if (project->suspended_via_gui) out.printf(" \n"); if (report_immediately) out.printf(" \n"); if (edf_scheduled) out.printf(" \n"); if (coproc_missing) out.printf(" \n"); if (schedule_backoff > gstate.now) out.printf(" \n"); if (avp->needs_network && gstate.network_suspended) out.printf(" \n"); ACTIVE_TASK* atp = gstate.active_tasks.lookup_result(this); if (atp) { atp->write_gui(out); } if (!strlen(resources)) { // only need to compute this string once // if (avp->gpu_usage.rsc_type) { sprintf(resources, "%.2f CPUs + %.2f %s GPUs", avp->avg_ncpus, avp->gpu_usage.usage, rsc_name(avp->gpu_usage.rsc_type) ); } else if (avp->missing_coproc) { sprintf(resources, "%.2f CPUs + %s GPU (missing)", avp->avg_ncpus, avp->missing_coproc_name ); } else if (!project->non_cpu_intensive && (avp->avg_ncpus != 1)) { sprintf(resources, "%.2f CPUs", avp->avg_ncpus); } else { strcpy(resources, " "); } } if (strlen(resources)>1) { char buf[256]; strcpy(buf, ""); if (atp && atp->task_state() == PROCESS_EXECUTING) { if (avp->gpu_usage.rsc_type) { COPROC& cp = coprocs.coprocs[avp->gpu_usage.rsc_type]; if (cp.count > 1) { sprintf(buf, " (device %d)", cp.device_nums[coproc_indices[0]] ); } } } out.printf( " %s%s\n", resources, buf ); } out.printf("\n"); return 0; } #endif // 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; iuploadable()) { if (fip->had_failure(retval)) continue; if (!fip->uploaded) { return false; } } } return true; } // resets all FILE_INFO's in result to uploaded = false // void RESULT::clear_uploaded_flags() { unsigned int i; FILE_INFO* fip; for (i=0; iuploaded = false; } } bool RESULT::is_not_started() { if (computing_done()) return false; if (gstate.active_tasks.lookup_result(this)) return false; return true; } bool PROJECT::some_download_stalled() { #ifndef SIM unsigned int i; if (!download_backoff.ok_to_transfer()) return true; for (i=0; ipers_file_xfers.size(); i++) { PERS_FILE_XFER* pfx = gstate.pers_file_xfers->pers_file_xfers[i]; if (pfx->fip->project != this) continue; if (pfx->is_upload) continue; if (pfx->next_request_time > gstate.now) return true; } #endif return false; } // return true if some file needed by this result (input or application) // is downloading and backed off // bool RESULT::some_download_stalled() { #ifndef SIM unsigned int i; FILE_INFO* fip; PERS_FILE_XFER* pfx; bool some_file_missing = false; for (i=0; iinput_files.size(); i++) { fip = wup->input_files[i].file_info; if (fip->status != FILE_PRESENT) some_file_missing = true; pfx = fip->pers_file_xfer; if (pfx && pfx->next_request_time > gstate.now) { return true; } } for (i=0; iapp_files.size(); i++) { fip = avp->app_files[i].file_info; if (fip->status != FILE_PRESENT) some_file_missing = true; pfx = fip->pers_file_xfer; if (pfx && pfx->next_request_time > gstate.now) { return true; } } if (some_file_missing && !project->download_backoff.ok_to_transfer()) { return true; } #endif return false; } FILE_REF* RESULT::lookup_file(FILE_INFO* fip) { for (unsigned int i=0; irsc_fpops_est, name, final_elapsed_time ); fclose(f); } // abort a result that's not currently running // void RESULT::abort_inactive(int status) { if (state() >= RESULT_COMPUTE_ERROR) return; set_state(RESULT_ABORTED, "RESULT::abort_inactive"); exit_status = status; } RUN_MODE::RUN_MODE() { perm_mode = 0; temp_mode = 0; temp_timeout = 0; } void RUN_MODE::set(int mode, double duration) { if (mode == RUN_MODE_RESTORE) { temp_timeout = 0; temp_mode = perm_mode; return; } if (duration) { temp_mode = mode; temp_timeout = gstate.now + duration; } else { temp_timeout = 0; temp_mode = mode; perm_mode = mode; gstate.set_client_state_dirty("Set mode"); } } int RUN_MODE::get_perm() { return perm_mode; } int RUN_MODE::get_current() { if (temp_timeout > gstate.now) { return temp_mode; } else { return perm_mode; } } double RUN_MODE::delay() { if (temp_timeout > gstate.now) { return temp_timeout - gstate.now; } else { return 0; } }