/** * NAME: os_SubstSuffix * PURPOSE: replace the suffix of the path with the given new suffix * ARGS: path - path of the file * suffix - new suffix * RETURN: new path allocated in the heap */ char * os_SubstSuffix(char const *path, char const *suffix) { char *dp; char *sp; char *r; if (path == NULL) return NULL; sp = strrchr(path, '/'); dp = strrchr(path, '.'); if (dp == NULL || (sp != NULL && sp > dp)) { return xstrmerge2(path, suffix); } r = xstrdup(path); r[dp - path] = 0; return xstrmerge1(r, suffix); }
int super_html_read_serve( FILE *flog, const unsigned char *path, const struct ejudge_cfg *config, const struct contest_desc *cnts, struct sid_state *sstate) { struct stat sb; int cond_count = 0, total, i, cur_id, j, arch, k; struct generic_section_config *pg; struct section_global_data *global; struct section_problem_data *prob, *aprob; struct section_tester_data *tst, *atst; struct section_language_data *lang; size_t vm_size, st_size; size_t *mem_lims, *st_lims; int *mem_cnt, *st_cnt; //int mem_u, st_u, max_i; path_t check_cmd = { 0 }; FILE *fuh; char *fuh_text = 0; size_t fuh_size = 0; path_t cs_spool_dir; path_t conf_dir; path_t tmppath; unsigned char *prob_no_any = 0; size_t cs_spool_dir_len = 0; unsigned char cs_conf_file[PATH_MAX]; if (!cnts) { fprintf(flog, "No contest XML description\n"); return -1; } if (!cnts->conf_dir || !*cnts->conf_dir) { snprintf(conf_dir, sizeof(conf_dir), "%s/%s", cnts->root_dir, "conf"); } else if (!os_IsAbsolutePath(cnts->conf_dir)) { snprintf(conf_dir, sizeof(conf_dir), "%s/%s", cnts->root_dir, cnts->conf_dir); } else { snprintf(conf_dir, sizeof(conf_dir), "%s", cnts->conf_dir); } if (stat(path, &sb) < 0) { // file do not exist return 0; } if (!S_ISREG(sb.st_mode)) { fprintf(flog, "File `%s' not a regular file\n", path); return -1; } if (access(path, R_OK) < 0) { fprintf(flog, "File `%s' is not readable\n", path); return -1; } // FIXME: redirect output? if (!(sstate->cfg = prepare_parse_config_file(path, &cond_count))) { fprintf(flog, "Parsing of `%s' failed\n", path); return -1; } if (cond_count > 0) { fprintf(flog, "The configuration file uses conditional compilation directives\n"); return -1; } // find global section for (pg = sstate->cfg; pg; pg = pg->next) if (!pg->name[0] || !strcmp(pg->name, "global")) break; if (!pg) { fprintf(flog, "The global section is not defined\n"); return -1; } global = sstate->global = (struct section_global_data *) pg; // set up the default value of the root_dir if (!global->root_dir[0]) { snprintf(global->root_dir, sizeof(global->root_dir), "%06d", global->contest_id); } if (!os_IsAbsolutePath(global->root_dir) && config && config->contests_home_dir && os_IsAbsolutePath(config->contests_home_dir)) { snprintf(tmppath, sizeof(tmppath), "%s/%s", config->contests_home_dir, global->root_dir); snprintf(global->root_dir, sizeof(global->root_dir), "%s", tmppath); } #if defined EJUDGE_CONTESTS_HOME_DIR if (!os_IsAbsolutePath(global->root_dir)) { snprintf(tmppath, sizeof(tmppath), "%s/%s", EJUDGE_CONTESTS_HOME_DIR, global->root_dir); snprintf(global->root_dir, sizeof(global->root_dir), "%s", tmppath); } #endif if (!os_IsAbsolutePath(global->root_dir)) { err("global.root_dir must be absolute directory!"); return -1; } // check variables that we don't want to be ever set if (prepare_check_forbidden_global(flog, global) < 0) return -1; // contest_id, conf_dir, root_dir must match if (global->contest_id != cnts->id) { fprintf(flog, "contest_id does not match\n"); return -1; } if (strcmp(global->root_dir, cnts->root_dir)) { fprintf(flog, "root_dir does not match\n"); return -1; } /* if ((!cnts->conf_dir && global->conf_dir) || (cnts->conf_dir && strcmp(cnts->conf_dir, global->conf_dir))) { fprintf(flog, "conf_dir does not match\n"); return -1; } */ // compile server must be used if (!global->compile_dir[0]) { fprintf(flog, "compilation server is not used\n"); return -1; } if (!os_IsAbsolutePath(global->compile_dir)) { snprintf(tmppath, sizeof(tmppath), "%s/var/%s", global->root_dir, global->compile_dir); path_normalize(tmppath, sizeof(tmppath)); snprintf(global->compile_dir, sizeof(global->compile_dir), "%s", tmppath); } if (!config->compile_home_dir) { fprintf(flog, "compile server home dir is not set\n"); return -1; } // cut off "/var/compile" suffix from the compile dir snprintf(cs_spool_dir, sizeof(cs_spool_dir), "%s", global->compile_dir); cs_spool_dir_len = strlen(cs_spool_dir); if (cs_spool_dir_len < sizeof(compile_dir_suffix) || strcmp(cs_spool_dir+cs_spool_dir_len-sizeof(compile_dir_suffix)+1, compile_dir_suffix) != 0) { fprintf(flog, "invalid `compile_dir' %s\n", cs_spool_dir); return -1; } cs_spool_dir[cs_spool_dir_len-sizeof(compile_dir_suffix)+1] = 0; sstate->compile_home_dir = xstrdup(cs_spool_dir); //fprintf(stderr, "compile_home_dir>>%s<<\n", sstate->compile_home_dir); /* snprintf(cs_spool_dir, sizeof(cs_spool_dir), "%s/var/compile", config->compile_home_dir); if (strcmp(cs_spool_dir, global->compile_dir)) { fprintf(flog, "non-default compilation server is used\n"); return -1; } */ prepare_set_global_defaults(global); if (global->stand2_file_name[0]) sstate->enable_stand2 = 1; if (global->plog_file_name[0]) sstate->enable_plog = 1; if (global->stand_extra_format[0]) sstate->enable_extra_col = 1; fuh = open_memstream(&fuh_text, &fuh_size); prepare_unparse_unhandled_global(fuh, global); close_memstream(fuh); fuh = 0; if (fuh_text && *fuh_text) { global->unhandled_vars = fuh_text; } else { xfree(fuh_text); } fuh_text = 0; fuh_size = 0; // collect languages total = 0; cur_id = 0; for (pg = sstate->cfg; pg; pg = pg->next) { if (strcmp(pg->name, "language") != 0) continue; lang = (struct section_language_data*) pg; if (!lang->id) lang->id = cur_id + 1; cur_id = lang->id; if (lang->id <= 0 || lang->id > EJ_MAX_LANG_ID) { fprintf(flog, "Invalid language ID\n"); return -1; } if (lang->id >= total) total = lang->id + 1; } sstate->lang_a = 0; sstate->langs = 0; sstate->loc_cs_map = 0; sstate->lang_opts = 0; sstate->lang_flags = 0; if (total > 0) { sstate->lang_a = 4; while (total > sstate->lang_a) sstate->lang_a *= 2; XCALLOC(sstate->langs, sstate->lang_a); XCALLOC(sstate->loc_cs_map, sstate->lang_a); XCALLOC(sstate->lang_opts, sstate->lang_a); XCALLOC(sstate->lang_flags, sstate->lang_a); for (pg = sstate->cfg; pg; pg = pg->next) { if (strcmp(pg->name, "language") != 0) continue; lang = (struct section_language_data*) pg; if (sstate->langs[lang->id]) { fprintf(flog, "Duplicated language ID %d\n", lang->id); return -1; } sstate->langs[lang->id] = lang; } } // load the compilation server state and establish correspondence if (super_load_cs_languages(config, sstate, global->extra_compile_dirs, 0, cs_conf_file, sizeof(cs_conf_file)) < 0) { fprintf(flog, "Failed to load compilation server configuration\n"); return -1; } for (i = 1; i < sstate->lang_a; i++) { if (!(lang = sstate->langs[i])) continue; if (!lang->compile_id) lang->compile_id = lang->id; if (prepare_check_forbidden_lang(flog, lang) < 0) return -1; /* if (lang->compile_id <= 0 || lang->compile_id >= sstate->cs_lang_total || !sstate->cs_langs[lang->compile_id]) { } */ // improve error messaging if (lang->compile_id > 0 && lang->compile_id < sstate->cs_lang_total && sstate->cs_langs[lang->compile_id] && strcmp(lang->short_name, sstate->cs_langs[lang->compile_id]->short_name) != 0) { fprintf(flog, "contest configuration file '%s' specifies language short name '%s' for language %d\n" "and it is different from language short name '%s' in compilation configuration file '%s'\n", path, lang->short_name, lang->compile_id, sstate->cs_langs[lang->compile_id]->short_name, cs_conf_file); return -1; } if (lang->compile_id <= 0 || lang->compile_id >= sstate->cs_lang_total || !sstate->cs_langs[lang->compile_id] || strcmp(lang->short_name, sstate->cs_langs[lang->compile_id]->short_name) != 0) { lang->compile_id = 0; } for (int j = 1; j < sstate->cs_lang_total; ++j) { if (sstate->cs_langs[j] && !strcmp(lang->short_name, sstate->cs_langs[j]->short_name)) { lang->compile_id = j; break; } } if (lang->compile_id <= 0) { fprintf(flog, "Invalid compile_id\n"); return -1; } sstate->loc_cs_map[lang->id] = lang->compile_id; sstate->cs_loc_map[lang->compile_id] = lang->id; fuh = open_memstream(&fuh_text, &fuh_size); prepare_unparse_unhandled_lang(fuh, lang); close_memstream(fuh); fuh = 0; if (fuh_text && *fuh_text) { lang->unhandled_vars = fuh_text; } else { xfree(fuh_text); } fuh_text = 0; fuh_size = 0; if (lang->compiler_env) { for (j = 0; lang->compiler_env[j]; j++) { if (!strncmp(lang->compiler_env[j], "EJUDGE_FLAGS=", 13)) { sstate->lang_opts[lang->id] = xstrmerge1(sstate->lang_opts[lang->id], lang->compiler_env[j] + 13); } } for (--j; j >= 0; --j) { if (!strncmp(lang->compiler_env[j], "EJUDGE_FLAGS=", 13)) { xfree(lang->compiler_env[j]); lang->compiler_env[j] = 0; for (k = j + 1; lang->compiler_env[k]; k++) { lang->compiler_env[k - 1] = lang->compiler_env[k]; } lang->compiler_env[k - 1] = lang->compiler_env[k]; } } } } // collect abstract problems for (pg = sstate->cfg, total = 0; pg; pg = pg->next) { if (!strcmp(pg->name, "problem") && (prob = (struct section_problem_data*) pg)->abstract) total++; } sstate->aprob_a = 0; sstate->aprob_u = 0; sstate->aprobs = 0; sstate->aprob_flags = 0; if (total) { sstate->aprob_a = 4; while (total > sstate->aprob_a) sstate->aprob_a *= 2; XCALLOC(sstate->aprobs, sstate->aprob_a); XCALLOC(sstate->aprob_flags, sstate->aprob_a); for (pg = sstate->cfg, i = 0; pg; pg = pg->next) { if (strcmp(pg->name, "problem") != 0) continue; prob = (struct section_problem_data*) pg; if (!prob->abstract) continue; sstate->aprobs[i++] = prob; if (!prob->short_name[0]) { fprintf(flog, "Abstract problem must have `short_name' field set\n"); return -1; } if (prob->super[0]) { fprintf(flog, "Abstract problem must not have a superproblem\n"); return -1; } if (prepare_check_forbidden_prob(flog, prob) < 0) return -1; prepare_set_abstr_problem_defaults(prob, global); fuh = open_memstream(&fuh_text, &fuh_size); prepare_unparse_unhandled_prob(fuh, prob, global); close_memstream(fuh); fuh = 0; if (fuh_text && *fuh_text) { prob->unhandled_vars = fuh_text; } else { xfree(fuh_text); } fuh_text = 0; fuh_size = 0; } ASSERT(i == total); sstate->aprob_u = total; } // collect concrete problems total = 0; cur_id = 0; for (pg = sstate->cfg; pg; pg = pg->next) { if (strcmp(pg->name, "problem") != 0) continue; prob = (struct section_problem_data*) pg; if (prob->abstract) continue; if (!prob->id) prob->id = cur_id + 1; cur_id = prob->id; if (prob->id <= 0 || prob->id > EJ_MAX_PROB_ID) { fprintf(flog, "Invalid problem ID\n"); return -1; } if (prob->id >= total) total = prob->id + 1; } sstate->probs = 0; sstate->prob_a = 0; sstate->prob_flags = 0; if (total > 0) { sstate->prob_a = 4; while (total > sstate->prob_a) sstate->prob_a *= 2; XCALLOC(sstate->probs, sstate->prob_a); XCALLOC(sstate->prob_flags, sstate->prob_a); XALLOCAZ(prob_no_any, sstate->prob_a); for (pg = sstate->cfg; pg; pg = pg->next) { if (strcmp(pg->name, "problem") != 0) continue; prob = (struct section_problem_data*) pg; if (prob->abstract) continue; if (sstate->probs[prob->id]) { fprintf(flog, "Duplicated problem id %d\n", prob->id); return -1; } sstate->probs[prob->id] = prob; if (prob->super[0]) { for (i = 0; i < sstate->aprob_u; i++) if (!strcmp(prob->super, sstate->aprobs[i]->short_name)) break; if (i == sstate->aprob_u) { fprintf(flog, "Abstract problem `%s' not found\n", prob->super); return -1; } } if (prepare_check_forbidden_prob(flog, prob) < 0) return -1; prepare_set_concr_problem_defaults(prob, global); fuh = open_memstream(&fuh_text, &fuh_size); prepare_unparse_unhandled_prob(fuh, prob, global); close_memstream(fuh); fuh = 0; if (fuh_text && *fuh_text) { prob->unhandled_vars = fuh_text; } else { xfree(fuh_text); } fuh_text = 0; fuh_size = 0; } } // collect abstract testers total = 0; for (pg = sstate->cfg; pg; pg = pg->next) { if (strcmp(pg->name, "tester")) continue; tst = (struct section_tester_data*) pg; if (!tst->abstract) continue; // check, that we know such abstract tester if ((arch = prepare_unparse_is_supported_tester(tst->name)) < 0) { fprintf(flog, "Unsupported abstract tester `%s'\n", tst->name); return -1; } if ((i = prepare_unparse_is_supported_arch(tst->arch)) < 0) { fprintf(flog, "Unsupported tester architecture `%s'\n", tst->arch); return -1; } if (i != arch) { fprintf(flog, "Abstract tester name does not match tester arch\n"); return -1; } if (tst->id) { fprintf(flog, "Abstract tester must not define tester ID\n"); return -1; } if (tst->problem_name[0]) { fprintf(flog, "Abstract tester must not define problem name\n"); return -1; } if (tst->problem) { fprintf(flog, "Abstract tester must not define problem ID\n"); return -1; } total++; } /* Relax, try to work without testers... */ /* if (!total) { fprintf(flog, "No abstract testers defined\n"); return -1; } */ sstate->atester_total = total; if (total > 0) { XCALLOC(sstate->atesters, sstate->atester_total); for (pg = sstate->cfg, i = 0; pg; pg = pg->next) { if (strcmp(pg->name, "tester")) continue; tst = (struct section_tester_data*) pg; if (!tst->abstract) continue; sstate->atesters[i++] = tst; // FIXME: check for unhandled fields } } // collect concrete testers, attempting to recover vm limit, stack limit // and checker name total = 0; cur_id = 0; for (pg = sstate->cfg; pg; pg = pg->next) { if (strcmp(pg->name, "tester") != 0) continue; tst = (struct section_tester_data*) pg; if (tst->abstract) continue; if (!tst->id) tst->id = cur_id + 1; cur_id = tst->id; if (tst->id <= 0 || tst->id > EJ_MAX_TESTER) { fprintf(flog, "Invalid tester ID\n"); return -1; } if (tst->id >= total) total = tst->id + 1; } sstate->tester_total = total; if (total > 0) { XCALLOC(sstate->testers, sstate->tester_total); } for (pg = sstate->cfg; pg; pg = pg->next) { if (strcmp(pg->name, "tester") != 0) continue; tst = (struct section_tester_data*) pg; if (tst->abstract) continue; if (sstate->testers[tst->id]) { fprintf(flog, "Duplicated tester ID %d\n", tst->id); return -1; } sstate->testers[tst->id] = tst; if (tst->super && tst->super[0] && tst->super[1]) { fprintf(flog, "Tester %d has several supertesters\n", tst->id); return -1; } atst = 0; if (tst->super) { for (i = 0; i < sstate->atester_total; i++) if (!strcmp(sstate->atesters[i]->name, tst->super[0])) break; if (i == sstate->atester_total) { fprintf(flog, "Abstract tester `%s' not found\n", tst->super[0]); return -1; } atst = sstate->atesters[i]; } if (tst->any) { continue; } prob = 0; if (tst->problem && tst->problem_name[0]) { fprintf(flog, "Both problem and problem_name fields cannot be set\n"); return -1; } else if (tst->problem) { if (tst->problem <= 0 || tst->problem >= sstate->prob_a || !sstate->probs[tst->problem]) { fprintf(flog, "problem %d is invalid\n", tst->problem); return -1; } prob = sstate->probs[tst->problem]; } else if (tst->problem_name[0]) { for (i = 1; i < sstate->prob_a; i++) if (sstate->probs[i] && !strcmp(sstate->probs[i]->short_name, tst->problem_name)) break; if (i == sstate->prob_a) { fprintf(flog, "Problem `%s' does not exist\n", tst->problem_name); return -1; } prob = sstate->probs[i]; } else { fprintf(flog, "Neither problem not problem_name are set\n"); return -1; } prob_no_any[prob->id] = 1; vm_size = tst->max_vm_size; if (vm_size == -1L && atst) vm_size = atst->max_vm_size; st_size = tst->max_stack_size; if (st_size == -1L && atst) st_size = atst->max_stack_size; if (vm_size != -1L) { if (prob->max_vm_size == -1L) prob->max_vm_size = vm_size; if (prob->max_vm_size != vm_size) { fprintf(flog, "Conflicting max_vm_size specifications for problem `%s'\n", prob->short_name); return -1; } } if (st_size != -1L) { if (prob->max_stack_size == -1L) prob->max_stack_size = st_size; if (prob->max_stack_size != st_size) { fprintf(flog, "Conflicting max_stack_size specifications for problem `%s'\n", prob->short_name); return -1; } } } for (i = 0; i < sstate->tester_total; i++) { if (!(tst = sstate->testers[i])) continue; atst = 0; if (tst->super) { for (j = 0; j < sstate->atester_total; j++) if (!strcmp(sstate->atesters[j]->name, tst->super[0])) break; if (j == sstate->atester_total) { fprintf(flog, "Abstract tester `%s' not found\n", tst->super[0]); return -1; } atst = sstate->atesters[j]; } if (!tst->any) continue; for (j = 0; j < sstate->prob_a; j++) { if (!(prob = sstate->probs[j])) continue; if (prob_no_any[j]) continue; vm_size = tst->max_vm_size; if (vm_size == -1L && atst) vm_size = atst->max_vm_size; st_size = tst->max_stack_size; if (st_size == -1L && atst) st_size = atst->max_stack_size; if (vm_size != -1L) { if (prob->max_vm_size == -1L) prob->max_vm_size = vm_size; if (prob->max_vm_size != vm_size) { fprintf(flog, "Conflicting max_vm_size specifications for problem `%s'\n", prob->short_name); return -1; } } if (st_size != -1L) { if (prob->max_stack_size == -1L) prob->max_stack_size = st_size; if (prob->max_stack_size != st_size) { fprintf(flog, "Conflicting max_stack_size specifications for problem `%s'\n", prob->short_name); return -1; } } } } XALLOCA(mem_lims, sstate->prob_a); XALLOCA(st_lims, sstate->prob_a); XALLOCA(mem_cnt, sstate->prob_a); XALLOCA(st_cnt, sstate->prob_a); // propagate most used memory limit to superproblem /* for (i = 0; i < sstate->aprob_u; i++) { aprob = sstate->aprobs[i]; mem_u = 0; st_u = 0; XMEMZERO(mem_cnt, sstate->prob_a); XMEMZERO(st_cnt, sstate->prob_a); for (j = 1; j < sstate->prob_a; j++) { if (!(prob = sstate->probs[j]) || !prob->super[0] || strcmp(prob->super, aprob->short_name)) continue; if (prob->max_vm_size != -1L) { for (k = 0; k < mem_u; k++) if (mem_lims[k] == prob->max_vm_size) break; if (k == mem_u) mem_u++; mem_lims[k] = prob->max_vm_size; mem_cnt[k]++; } if (prob->max_stack_size != -1L) { for (k = 0; k < st_u; k++) if (st_lims[k] == prob->max_stack_size) break; if (k == st_u) st_u++; st_lims[k] = prob->max_stack_size; st_cnt[k]++; } } if (mem_u > 0) { max_i = 0; for (i = 1; i < mem_u; i++) if (mem_cnt[i] > mem_cnt[max_i]) max_i = i; aprob->max_vm_size = mem_lims[max_i]; for (j = 1; j < sstate->prob_a; j++) { if (!(prob = sstate->probs[j]) || !prob->super[0] || strcmp(prob->super, aprob->short_name)) continue; if (prob->max_vm_size == -1L) { prob->max_vm_size = 0; } else if (prob->max_vm_size == aprob->max_vm_size) { prob->max_vm_size = -1L; } } } if (st_u > 0) { max_i = 0; for (i = 1; i < st_u; i++) if (st_cnt[i] > st_cnt[max_i]) max_i = i; aprob->max_stack_size = st_lims[max_i]; for (j = 1; j < sstate->prob_a; j++) { if (!(prob = sstate->probs[j]) || !prob->super[0] || strcmp(prob->super, aprob->short_name)) continue; if (prob->max_stack_size == -1L) { prob->max_stack_size = 0; } else if (prob->max_stack_size == aprob->max_stack_size) { prob->max_stack_size = -1L; } } } } */ // assign this check_cmd to all abstract problems without check_cmd for (i = 0; i < sstate->aprob_u; i++) if (!(aprob = sstate->aprobs[i])->check_cmd[0]) snprintf(aprob->check_cmd, sizeof(aprob->check_cmd), "%s", check_cmd); sstate->contest_start_cmd_text = do_load_file(conf_dir, global->contest_start_cmd); sstate->contest_stop_cmd_text = do_load_file(conf_dir, global->contest_stop_cmd); sstate->stand_header_text = do_load_file(conf_dir, global->stand_header_file); sstate->stand_footer_text = do_load_file(conf_dir, global->stand_footer_file); sstate->stand2_header_text = do_load_file(conf_dir, global->stand2_header_file); sstate->stand2_footer_text = do_load_file(conf_dir, global->stand2_footer_file); sstate->plog_header_text = do_load_file(conf_dir, global->plog_header_file); sstate->plog_footer_text = do_load_file(conf_dir, global->plog_footer_file); return 0; }