// This file is part of BOINC. // http://boinc.berkeley.edu // Copyright (C) 2023 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 . // Parse a project configuration file (config.xml) #include "boinc_stdio.h" #include #include #include #include "error_numbers.h" #include "filesys.h" #include "parse.h" #include "str_replace.h" #include "str_util.h" #include "sched_msgs.h" #include "sched_util.h" #include "sched_config.h" const char* CONFIG_FILE = "config.xml"; const char* CONFIG_FILE_AUX = "config_aux.xml"; SCHED_CONFIG config; const int MAX_NCPUS = 64; // max multiplier for daily_result_quota. // need to change as multicore processors expand int SCHED_CONFIG::parse_aux(FILE* f) { MIOFILE mf; XML_PARSER xp(&mf); mf.init_file(f); if (!xp.parse_start("config")) return ERR_XML_PARSE; while (!xp.get_tag()) { if (!xp.is_tag) { boinc::fprintf(stderr, "SCHED_CONFIG::parse(): unexpected text %s\n", xp.parsed_tag ); continue; } if (xp.match_tag("/config")) { return 0; } if (xp.match_tag("max_jobs_in_progress")) { max_jobs_in_progress.parse(xp, "/max_jobs_in_progress"); } } return ERR_XML_PARSE; } int SCHED_CONFIG::parse(FILE* f) { char buf[256]; MIOFILE mf; XML_PARSER xp(&mf); int retval, itemp; regex_t re; double x; mf.init_file(f); memset(this, 0, sizeof(*this)); ban_os = new vector; ban_cpu = new vector; locality_scheduling_workunit_file = new vector; locality_scheduling_sticky_file = new vector; max_wus_to_send = 10; default_disk_min_free_gb = 1; sched_debug_level = MSG_NORMAL; fuh_debug_level = MSG_NORMAL; fuh_set_initial_permission = -1; fuh_set_completed_permission = -1; strcpy(httpd_user, "apache"); max_ncpus = MAX_NCPUS; scheduler_log_buffer = 32768; version_select_random_factor = 1.; maintenance_delay = 3600; user_url = true; user_country = true; if (!xp.parse_start("boinc")) return ERR_XML_PARSE; if (!xp.parse_start("config")) return ERR_XML_PARSE; while (!xp.get_tag()) { if (!xp.is_tag) { boinc::fprintf(stderr, "SCHED_CONFIG::parse(): unexpected text %s\n", xp.parsed_tag ); continue; } if (xp.match_tag("/config")) { char hostname[256]; gethostname(hostname, 256); if (!strcmp(hostname, db_host)) strcpy(db_host, "localhost"); if (!strlen(replica_db_host)) { safe_strcpy(replica_db_host, db_host); } if (!strlen(replica_db_name)) { safe_strcpy(replica_db_name, db_name); } if (!strlen(replica_db_user)) { safe_strcpy(replica_db_user, db_user); } if (!strlen(replica_db_passwd)) { safe_strcpy(replica_db_passwd, db_passwd); } return 0; } if (xp.parse_str("master_url", master_url, sizeof(master_url))) continue; if (xp.parse_str("long_name", long_name, sizeof(long_name))) continue; if (xp.parse_str("db_name", db_name, sizeof(db_name))) continue; if (xp.parse_str("db_user", db_user, sizeof(db_user))) continue; if (xp.parse_str("db_passwd", db_passwd, sizeof(db_passwd))) continue; if (xp.parse_str("db_host", db_host, sizeof(db_host))) continue; if (xp.parse_str("replica_db_name", replica_db_name, sizeof(replica_db_name))) continue; if (xp.parse_str("replica_db_user", replica_db_user, sizeof(replica_db_user))) continue; if (xp.parse_str("replica_db_passwd", replica_db_passwd, sizeof(replica_db_passwd))) continue; if (xp.parse_str("replica_db_host", replica_db_host, sizeof(replica_db_host))) continue; if (xp.parse_str("project_dir", project_dir, sizeof(project_dir))) continue; if (xp.parse_int("shmem_key", shmem_key)) continue; if (xp.parse_str("key_dir", key_dir, sizeof(key_dir))) continue; if (xp.parse_str("download_url", download_url, sizeof(download_url))) continue; if (xp.parse_str("download_dir", download_dir, sizeof(download_dir))) continue; if (xp.parse_str("upload_url", upload_url, sizeof(upload_url))) continue; if (xp.parse_str("upload_dir", upload_dir, sizeof(upload_dir))) 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_int("homogeneous_redundancy", homogeneous_redundancy)) continue; if (xp.parse_bool("hr_class_static", hr_class_static)) continue; if (xp.parse_bool("hr_allocate_slots", hr_allocate_slots)) continue; if (xp.parse_bool("msg_to_host", msg_to_host)) continue; if (xp.parse_bool("ignore_upload_certificates", ignore_upload_certificates)) continue; if (xp.parse_bool("dont_generate_upload_certificates", dont_generate_upload_certificates)) continue; if (xp.parse_int("uldl_dir_fanout", uldl_dir_fanout)) continue; if (xp.parse_bool("cache_md5_info", cache_md5_info)) continue; if (xp.parse_int("fuh_debug_level", fuh_debug_level)) continue; if (xp.parse_str("fuh_set_completed_permission", buf, sizeof(buf))) { long int l = strtol(buf, NULL, 8); if (l > 0 && l < LONG_MAX) { fuh_set_completed_permission = (int)l; } else { log_messages.printf(MSG_CRITICAL, "wrong fuh_set_completed_permission: %s\n", buf); } continue; } if (xp.parse_str("fuh_set_initial_permission", buf, sizeof(buf))) { long int l = strtol(buf, NULL, 8); if (l > 0 && l < LONG_MAX) { fuh_set_initial_permission = (int)l; } else { log_messages.printf(MSG_CRITICAL, "wrong fuh_set_initial_permission: %s\n", buf); } continue; } if (xp.parse_int("reliable_priority_on_over", reliable_priority_on_over)) continue; if (xp.parse_int("reliable_priority_on_over_except_error", reliable_priority_on_over_except_error)) continue; if (xp.parse_int("reliable_on_priority", reliable_on_priority)) continue; if (xp.parse_double("grace_period_hours", x)) { report_grace_period = (int)(x*3600); continue; } if (xp.parse_int("report_grace_period", report_grace_period)) continue; if (xp.parse_double("delete_delay_hours", x)) { delete_delay = x*3600; continue; } if (xp.parse_bool("distinct_beta_apps", distinct_beta_apps)) continue; if (xp.parse_bool("ended", ended)) continue; if (xp.parse_int("shmem_work_items", shmem_work_items)) continue; if (xp.parse_int("feeder_query_size", feeder_query_size)) continue; if (xp.parse_str("httpd_user", httpd_user, sizeof(httpd_user))) continue; if (xp.parse_bool("enable_vda", enable_vda)) continue; if (xp.parse_double("vda_host_timeout", vda_host_timeout)) continue; if (xp.parse_bool("enable_assignment", enable_assignment)) continue; if (xp.parse_bool("enable_assignment_multi", enable_assignment_multi)) continue; if (xp.parse_bool("job_size_matching", job_size_matching)) continue; if (xp.parse_bool("dont_send_jobs", dont_send_jobs)) continue; if (xp.parse_bool("estimate_flops_from_hav_pfc", estimate_flops_from_hav_pfc)) continue; if (xp.parse_bool("user_url", user_url)) continue; if (xp.parse_bool("user_country", user_country)) continue; //////////// STUFF RELEVANT ONLY TO SCHEDULER STARTS HERE /////// if (xp.parse_str("ban_cpu", buf, sizeof(buf))) { retval = regcomp(&re, buf, REG_EXTENDED|REG_NOSUB); if (retval) { log_messages.printf(MSG_CRITICAL, "BAD REGEXP: %s\n", buf); } else { ban_cpu->push_back(re); } continue; } if (xp.parse_str("ban_os", buf, sizeof(buf))) { retval = regcomp(&re, buf, REG_EXTENDED|REG_NOSUB); if (retval) { log_messages.printf(MSG_CRITICAL, "BAD REGEXP: %s\n", buf); } else { ban_os->push_back(re); } continue; } if (xp.parse_int("dont_search_host_for_user", retval)) { dont_search_host_for_userid.push_back(retval); continue; } if (xp.parse_int("daily_result_quota", daily_result_quota)) continue; if (xp.parse_double("default_disk_min_free_gb", default_disk_min_free_gb)) continue; if (xp.parse_bool("dont_store_success_stderr", dont_store_success_stderr)) continue; if (xp.parse_int("file_deletion_strategy", file_deletion_strategy)) continue; if (xp.parse_int("gpu_multiplier", gpu_multiplier)) continue; if (xp.parse_bool("ignore_delay_bound", ignore_delay_bound)) continue; if (xp.parse_bool("locality_scheduling", locality_scheduling)) continue; if (xp.parse_double("locality_scheduler_fraction", locality_scheduler_fraction)) continue; if (xp.parse_bool("locality_scheduling_sorted_order", locality_scheduling_sorted_order)) continue; if (xp.parse_int("locality_scheduling_wait_period", locality_scheduling_wait_period)) continue; if (xp.parse_int("locality_scheduling_send_timeout", locality_scheduling_send_timeout)) continue; if (xp.parse_str("locality_scheduling_workunit_file", buf, sizeof(buf))) { retval = regcomp(&re, buf, REG_EXTENDED|REG_NOSUB); if (retval) { log_messages.printf(MSG_CRITICAL, "BAD REGEXP: %s\n", buf); } else { locality_scheduling_workunit_file->push_back(re); } continue; } if (xp.parse_str("locality_scheduling_sticky_file", buf, sizeof(buf))) { retval = regcomp(&re, buf, REG_EXTENDED|REG_NOSUB); if (retval) { log_messages.printf(MSG_CRITICAL, "BAD REGEXP: %s\n", buf); } else { locality_scheduling_sticky_file->push_back(re); } continue; } if (xp.parse_bool("sched_old", sched_old)) continue; if (xp.parse_int("max_ncpus", max_ncpus)) continue; if (xp.parse_int("max_wus_in_progress", itemp)) { max_jobs_in_progress.project_limits.proc_type_limits[PROC_TYPE_CPU].base_limit = itemp; max_jobs_in_progress.project_limits.proc_type_limits[PROC_TYPE_CPU].per_proc = true; continue; } if (xp.parse_int("max_wus_in_progress_gpu", itemp)) { for (int i=1; ipush_back(re); } continue; } if (xp.parse_bool("no_vista_sandbox", no_vista_sandbox)) continue; if (xp.parse_bool("nowork_skip", nowork_skip)) continue; if (xp.parse_bool("one_result_per_host_per_wu", one_result_per_host_per_wu)) continue; if (xp.parse_bool("one_result_per_user_per_wu", one_result_per_user_per_wu)) continue; if (xp.parse_int("reliable_max_avg_turnaround", reliable_max_avg_turnaround)) continue; if (xp.parse_double("reliable_max_error_rate", reliable_max_error_rate)) continue; if (xp.parse_double("reliable_reduced_delay_bound", reliable_reduced_delay_bound)) continue; if (xp.parse_str("replace_download_url_by_timezone", replace_download_url_by_timezone, sizeof(replace_download_url_by_timezone))) continue; if (xp.parse_int("max_download_urls_per_file", max_download_urls_per_file)) continue; if (xp.parse_int("report_max", report_max)) continue; if (xp.parse_bool("request_time_stats_log", request_time_stats_log)) continue; if (xp.parse_bool("resend_lost_results", resend_lost_results)) continue; if (xp.parse_int("sched_debug_level", sched_debug_level)) continue; if (xp.parse_int("scheduler_log_buffer", scheduler_log_buffer)) continue; if (xp.parse_str("sched_lockfile_dir", sched_lockfile_dir, sizeof(sched_lockfile_dir))) continue; if (xp.parse_bool("send_result_abort", send_result_abort)) continue; if (xp.parse_str("symstore", symstore, sizeof(symstore))) continue; if (xp.parse_bool("user_filter", user_filter)) continue; if (xp.parse_bool("workload_sim", workload_sim)) continue; if (xp.parse_bool("prefer_primary_platform", prefer_primary_platform)) continue; if (xp.parse_double("version_select_random_factor", version_select_random_factor)) continue; if (xp.parse_double("maintenance_delay", maintenance_delay)) continue; if (xp.parse_bool("credit_by_app", credit_by_app)) continue; if (xp.parse_bool("keyword_sched", keyword_sched)) continue; if (xp.parse_bool("rte_no_stats", rte_no_stats)) continue; //////////// SCHEDULER LOG FLAGS ///////// if (xp.parse_bool("debug_assignment", debug_assignment)) continue; if (xp.parse_bool("debug_client_files", debug_client_files)) continue; if (xp.parse_bool("debug_credit", debug_credit)) continue; if (xp.parse_bool("debug_edf_sim_detail", debug_edf_sim_detail)) continue; if (xp.parse_bool("debug_edf_sim_workload", debug_edf_sim_workload)) continue; if (xp.parse_bool("debug_fcgi", debug_fcgi)) continue; if (xp.parse_bool("debug_handle_results", debug_handle_results)) continue; if (xp.parse_bool("debug_keyword", debug_keyword)) continue; if (xp.parse_bool("debug_locality", debug_locality)) continue; if (xp.parse_bool("debug_locality_lite", debug_locality_lite)) continue; if (xp.parse_bool("debug_prefs", debug_prefs)) continue; if (xp.parse_bool("debug_quota", debug_quota)) continue; if (xp.parse_bool("debug_request_details", debug_request_details)) continue; if (xp.parse_bool("debug_request_headers", debug_request_headers)) continue; if (xp.parse_bool("debug_resend", debug_resend)) continue; if (xp.parse_bool("debug_send", debug_send)) continue; if (xp.parse_bool("debug_send_job", debug_send_job)) continue; if (xp.parse_bool("debug_send_scan", debug_send_scan)) continue; if (xp.parse_bool("debug_user_messages", debug_user_messages)) continue; if (xp.parse_bool("debug_vda", debug_vda)) continue; if (xp.parse_bool("debug_version_select", debug_version_select)) continue; if (xp.parse_str("debug_req_reply_dir", debug_req_reply_dir, sizeof(debug_req_reply_dir))) continue; // don't complain about unparsed XML; // there are lots of tags the scheduler doesn't know about xp.skip_unexpected(false, "SCHED_CONFIG::parse"); } return ERR_XML_PARSE; } int SCHED_CONFIG::parse_file(const char* dir) { char path[MAXPATHLEN], path_aux[MAXPATHLEN]; int retval; if (dir && strlen(dir)) { snprintf(path, sizeof(path), "%s/%s", dir, CONFIG_FILE); snprintf(path_aux, sizeof(path_aux), "%s/%s", dir, CONFIG_FILE_AUX); } else { safe_strcpy(path, project_path(CONFIG_FILE)); safe_strcpy(path_aux, project_path(CONFIG_FILE_AUX)); } FILE* f = boinc::fopen(path, "r"); if (!f) return ERR_FOPEN; retval = parse(f); boinc::fclose(f); if (retval) return retval; FILE* f_aux = boinc::fopen(path_aux, "r"); if (!f_aux) return 0; retval = parse_aux(f_aux); boinc::fclose(f_aux); return retval; } int SCHED_CONFIG::upload_path(const char* filename, char* path) { return dir_hier_path(filename, upload_dir, uldl_dir_fanout, path, true); } int SCHED_CONFIG::download_path(const char* filename, char* path) { return dir_hier_path(filename, download_dir, uldl_dir_fanout, path, true); } static bool is_project_dir(const char* dir) { char buf[1024]; snprintf(buf, sizeof(buf), "%s/%s", dir, CONFIG_FILE); if (!is_file_follow_symlinks(buf)) return false; snprintf(buf, sizeof(buf), "%s/cgi-bin", dir); if (!is_dir_follow_symlinks(buf)) return false; return true; } // Does 2 things: // - locate project directory. This is either // a) env var BOINC_PROJECT_DIR, if defined // b) current dir, if config.xml exists there // c) parent dir, if config.xml exists there // - returns a path relative to the project dir, // specified by a format string + args // const char *SCHED_CONFIG::project_path(const char *fmt, ...) { static char path[MAXPATHLEN]; va_list ap; if (!strlen(project_dir)) { char *p = getenv("BOINC_PROJECT_DIR"); if (p) { if (!is_project_dir(p)) { boinc::fprintf(stderr, "BOINC_PROJECT_DIR env var exists but is not a project dir\n"); exit(1); } strlcpy(project_dir, p, sizeof(project_dir)); } else if (is_project_dir(".")) { strcpy(project_dir, "."); } else if (is_project_dir("..")) { strcpy(project_dir, ".."); } else { boinc::fprintf(stderr, "Not in a project directory or subdirectory\n"); exit(1); } } va_start(ap, fmt); snprintf(path, sizeof(path), "%s/", project_dir); vsnprintf(path + strlen(path), sizeof(path) - strlen(path), fmt, ap); va_end(ap); return (const char *)path; }