Exemple #1
0
static void
setup_log_file(void)
{
  path_t buf;
  const unsigned char *s1, *s2;

  if (ejudge_config->new_server_log
      && os_IsAbsolutePath(ejudge_config->new_server_log))
    return;
  if (ejudge_config->var_dir && os_IsAbsolutePath(ejudge_config->var_dir)) {
    if (!(s1 = ejudge_config->new_server_log)) s1 = "ej-contests.log";
    snprintf(buf, sizeof(buf), "%s/%s", ejudge_config->var_dir, s1);
    xfree(ejudge_config->new_server_log);
    ejudge_config->new_server_log = xstrdup(buf);
    return;
  }
  if (ejudge_config->contests_home_dir
      && os_IsAbsolutePath(ejudge_config->contests_home_dir)){
    if (!(s1 = ejudge_config->new_server_log)) s1 = "ej-contests.log";
    if (!(s2 = ejudge_config->var_dir)) s2 = "var";
    snprintf(buf, sizeof(buf), "%s/%s/%s", ejudge_config->contests_home_dir,
             s2, s1);
    xfree(ejudge_config->new_server_log);
    ejudge_config->new_server_log = xstrdup(buf);
    return;
  }
  ejudge_config->new_server_log = xstrdup("/tmp/ej-contests.log");
}
Exemple #2
0
int
pathmake3(char *dst, ...)
{
  va_list  args;
  char    *p;
  path_t   temp;

  temp[0] = 0;
  va_start(args, dst);
  while ((p = va_arg(args, char*))) {
    if (p[0] == '/' && p[1] == 0) {
      strmcat(temp, PATH_SEP, PATH_MAX);
    } else {
      strmcat(temp, p, PATH_MAX);
    }
  }
  va_end(args);
  
  dst[0] = 0;
  if (!os_IsAbsolutePath(temp)) {
    os_rGetWorkingDir(dst, PATH_MAX, 1);
    pathcat(dst, PATH_SEP);
  }
  pathcat(dst, temp);
  return strlen(dst);
}
Exemple #3
0
void
path_make_relative(
        unsigned char *out,
        size_t size,
        const unsigned char *path,
        const unsigned char *relto,
        const unsigned char *prefix)
{
  path_t path1, relto1, prefix1;
  unsigned char **s_path = 0, **s_relto = 0, **s_prefix = 0;

  ASSERT(path);
  snprintf(path1, sizeof(path1), "%s", path);

  if (!relto || !prefix) goto do_nothing;
  snprintf(relto1, sizeof(relto1), "%s", relto);
  snprintf(prefix1, sizeof(prefix1), "%s", prefix);

  if (!os_IsAbsolutePath(path1) || !os_IsAbsolutePath(relto1)
      || !os_IsAbsolutePath(prefix1))
    goto do_nothing;
  path_split(path1, &s_path);
  path_normalize_split(s_path);
  path_split(relto1, &s_relto);
  path_normalize_split(s_relto);
  path_split(prefix1, &s_prefix);
  path_normalize_split(s_prefix);
  if (!path_is_prefix(s_path, s_prefix) || !path_is_prefix(s_relto, s_prefix))
    goto do_nothing;
  do_relative(out, size, s_path, s_relto);
  goto cleanup;

 do_nothing:
  snprintf(out, size, "%s", path1);

 cleanup:
  path_split_free(s_path);
  path_split_free(s_relto);
  path_split_free(s_prefix);
}
Exemple #4
0
void
path_normalize(unsigned char *path, size_t size)
{
  unsigned char **split = 0;
  int i, j, len;
  unsigned char *t;

  if (!os_IsAbsolutePath(path)) return;
  path_split(path, &split);
  i = j = 1;
  while (split[i]) {
    if (!strcmp(split[i], "..")) {
      if (j > 1) j--;
      i++;
    } else if (!strcmp(split[i], ".")) {
      i++;
    } else {
      if (i == j) {
        i++; j++;
      } else {
        t = split[j]; split[j] = split[i]; split[i] = t;
        i++; j++;
      }
    }
  }
  if (i != j) {
    t = split[j]; split[j] = split[i]; split[i] = t;
  }
  for (; i > j; i--) {
    xfree(split[i]); split[i] = 0;
  }
  len = 0;
  if (split[0][0] != '/') len += strlen(split[0]);
  for (len = 0, i = 1; split[i]; i++)
    len += strlen(split[i]) + 1;
  if (len >= size) goto cleanup;
  t = path;
  if (split[0][0] != '/') t += sprintf(t, "%s", split[0]);
  for (i = 1; split[i]; i++)
    t += sprintf(t, "/%s", split[i]);

 cleanup:
  for (i = 0; split[i]; i++)
    xfree(split[i]);
  xfree(split);
}
Exemple #5
0
static void
get_program_dir(const unsigned char *program_path)
{
  unsigned char *workdir = 0;
  unsigned char fullpath[EJ_PATH_MAX];

  if (os_IsAbsolutePath(program_path)) {
    program_dir = os_DirName(program_path);
    os_normalize_path(program_dir);
    return;
  }

  workdir = os_GetWorkingDir();
  snprintf(fullpath, sizeof(fullpath), "%s/%s", workdir, program_path);
  xfree(workdir); workdir = 0;
  os_normalize_path(fullpath);
  program_dir = os_DirName(fullpath);
}
Exemple #6
0
static unsigned char *
do_load_file(const unsigned char *conf_path, const unsigned char *file)
{
  unsigned char full_path[PATH_MAX];
  char *buf = 0;
  size_t buf_size = 0;

  if (!file || !*file) return 0;

  if (!os_IsAbsolutePath(file)) {
    snprintf(full_path, sizeof(full_path), "%s/%s", conf_path, file);
  } else {
    snprintf(full_path, sizeof(full_path), "%s", file);
  }

  if (generic_read_file(&buf, 0, &buf_size, 0, 0, full_path, 0) < 0) return 0;
  return buf;
}
Exemple #7
0
int
pathmake2(char *dst, ...)
{
  va_list  args;
  char    *p;
  path_t   temp;

  if (os_IsAbsolutePath(dst)) return strlen(dst);
  
  temp[0] = 0;
  va_start(args, dst);
  while ((p = va_arg(args, char*))) {
    if (p[0] == '/' && p[1] == 0) {
      strmcat(temp, PATH_SEP, PATH_MAX);
    } else {
      strmcat(temp, p, PATH_MAX);
    }
  }
  va_end(args);
  pathcpy(dst, temp);
  return strlen(dst);
}
Exemple #8
0
static int
process_contest(int contest_id)
{
  const struct contest_desc *cnts = 0;
  unsigned char config_path[PATH_MAX];
  unsigned char out_config_path[PATH_MAX];
  unsigned char old_config_path[PATH_MAX];
  const unsigned char *conf_dir = 0;
  struct stat stbuf;
  serve_state_t state = 0;
  struct section_global_data *global = 0;
  int lang_id;
  struct section_language_data *lang, *cs_lang_by_short, *cs_lang_by_id, *cs_lang;
  int compile_id;
  int i;
  int has_to_convert = 0, has_errors = 0;
  int *lang_map = 0;
  unsigned char **lang_shorts = 0;
  unsigned char short_name[1024];
  struct textfile config_text;
  FILE *config_file = NULL;
  FILE *out_config_file = NULL;
  unsigned char cmd_buf[PATH_MAX];

  memset(&config_text, 0, sizeof(config_text));

  fprintf(stderr, "Processing contest %d\n", contest_id);

  if (contests_get(contest_id, &cnts) < 0 || !cnts) {
    error("cannot read contest XML for contest %d", contest_id);
    goto failure;
  }

  if (cnts->conf_dir && os_IsAbsolutePath(cnts->conf_dir)) {
    snprintf(config_path, sizeof(config_path), "%s/serve.cfg", cnts->conf_dir);
  } else {
    if (!cnts->root_dir) {
      error("contest %d root_dir is not set", contest_id);
      goto failure;
    } else if (!os_IsAbsolutePath(cnts->root_dir)) {
      error("contest %d root_dir %s is not absolute", contest_id, cnts->root_dir);
      goto failure;
    }
    if (!(conf_dir = cnts->conf_dir)) conf_dir = "conf";
    snprintf(config_path, sizeof(config_path),
             "%s/%s/serve.cfg", cnts->root_dir, conf_dir);
  }

  if (stat(config_path, &stbuf) < 0) {
    error("contest %d config file %s does not exist", contest_id, config_path);
    goto failure;
  }
  if (!S_ISREG(stbuf.st_mode)) {
    error("contest %d config file %s is not a regular file",
          contest_id, config_path);
    goto failure;
  }
  if (access(config_path, R_OK) < 0) {
    error("contest %d config file %s is not readable",
          contest_id, config_path);
    goto failure;
  }

  state = serve_state_init(contest_id);
  state->config_path = xstrdup(config_path);
  state->current_time = time(0);
  state->load_time = state->current_time;
  if (prepare(NULL, state, state->config_path, 0, PREPARE_SERVE, "", 1, 0, 0) < 0)
    goto failure;
  global = state->global;
  if (!global) {
    error("contest %d has no global section", contest_id);
    goto failure;
  }
  if (strcmp(global->rundb_plugin, "mysql") != 0) {
    fprintf(stderr, "contest %d does not use mysql\n", contest_id);
    goto failure;
  }

  if (state->max_lang >= 0) {
    XCALLOC(lang_map, state->max_lang + 1);
    XCALLOC(lang_shorts, state->max_lang + 1);
  }

  for (lang_id = 1; lang_id <= state->max_lang; ++lang_id) {
    if (!(lang = state->langs[lang_id])) continue;
    compile_id = lang->compile_id;
    if (compile_id <= 0) compile_id = lang->id;

    if (lang->id > 1000) {
      fprintf(stderr, "  language %s id > 1000 (%d)\n",
              lang->short_name, lang->id);
      has_errors = 1;
      continue;
    }

    snprintf(short_name, sizeof(short_name), "%s", lang->short_name);
    map_lang_aliases(short_name, sizeof(short_name));

    /* search the language in the compilation server by short_name and by id */
    cs_lang_by_short = 0;
    cs_lang_by_id = 0;
    for (i = 1; i < cs_lang_total; ++i) {
      if ((cs_lang = cs_langs[i]) && cs_lang->id == compile_id) {
        cs_lang_by_id = cs_lang;
        break;
      }
    }
    for (i = 1; i < cs_lang_total; ++i) {
      if ((cs_lang = cs_langs[i]) && !strcmp(cs_lang->short_name, short_name)) {
        cs_lang_by_short = cs_lang;
        break;
      }
    }

    /*
      condition to convert:
        1) contest language id does not match to compilation server language id;
        2) contest language short name, compilation server language short name match.
     */
    if (lang->id != compile_id && cs_lang_by_short != NULL
        && cs_lang_by_short == cs_lang_by_id) {
      has_to_convert = 1;
      fprintf(stderr, "  language %s id %d to be changed to %d\n",
              lang->short_name, lang->id, compile_id);
      lang_map[lang_id] = compile_id;
      lang_shorts[lang_id] = xstrdup(lang->short_name);
    } else if (lang->id == compile_id && cs_lang_by_short != NULL
               && cs_lang_by_short == cs_lang_by_id) {
      /*
        condition to do nothing:
          1) contest language id match compilation server language id;
          2) contest language short name, compilation server language short name match.
      */
    } else {
      has_errors = 1;
      fprintf(stderr, "  unexpected language %s, id %d, compile id %d\n",
              lang->short_name, lang->id, lang->compile_id);
      if (cs_lang_by_id) {
        fprintf(stderr, "    CS lang by id: id %d, short %s\n",
                cs_lang_by_id->id, cs_lang_by_id->short_name);
      } else {
        fprintf(stderr, "    CS lang by id: NULL\n");
      }
      if (cs_lang_by_short) {
        fprintf(stderr, "    CS lang by short name: id %d, short %s\n",
                cs_lang_by_short->id, cs_lang_by_short->short_name);
      } else {
        fprintf(stderr, "    CS lang by short name: NULL\n");
      }
    }
  }

  if (has_errors) {
    fprintf(stderr, "contest %d cannot be converted\n", contest_id);
    return 0;
  }
  if (!has_to_convert) {
    fprintf(stderr, "contest %d is ok\n", contest_id);
    return 0;
  }

  config_file = fopen(config_path, "r");
  if (!config_file) {
    fprintf(stderr, "cannot open %s\n", config_path);
    return 0;
  }
  if (gettextfile(config_file, &config_text) <= 0) {
    fprintf(stderr, "configuration file %s is empty\n", config_path);
    return 0;
  }
  fclose(config_file); config_file = NULL;

  normalize_text(&config_text);

  process_text(&config_text, state->max_lang + 1,
               lang_map, lang_shorts);

  snprintf(out_config_path, sizeof(out_config_path),
           "%s.out", config_path);
  out_config_file = fopen(out_config_path, "w");
  if (!out_config_file) {
    fprintf(stderr, "cannot open %s\n", out_config_path);
    return 0;
  }
  puttext(out_config_file, &config_text);
  fclose(out_config_file); out_config_file = NULL;

  snprintf(cmd_buf, sizeof(cmd_buf), "diff -u %s %s",
           config_path, out_config_path);
  //fprintf(stderr, ">>%s\n", cmd_buf);
  system(cmd_buf);

  process_db(contest_id, state->max_lang + 1, lang_map);

  snprintf(old_config_path, sizeof(old_config_path),
           "%s.old", config_path);
  fprintf(stderr, "Rename: %s->%s, %s->%s\n", config_path, old_config_path,
          out_config_path, config_path);
  if (rename(config_path, old_config_path) < 0) {
    fprintf(stderr, "Rename: %s->%s failed\n", config_path, old_config_path);
  }
  if (rename(out_config_path, config_path) < 0) {
    fprintf(stderr, "Rename: %s->%s failed\n", out_config_path, config_path);
  }

  return 0;

 failure:
  return 1;
}
Exemple #9
0
int
super_load_cs_languages(
        const struct ejudge_cfg *config,
        struct sid_state *sstate,
        char **extra_compile_dirs,
        int check_version_flag,
        unsigned char *cs_conf_file_buf,
        int cs_conf_file_len)
{
  path_t extra_cs_conf_path;
  struct generic_section_config *cfg = 0, *p;
  struct section_language_data *lp;
  int max_lang = -1;
  int cur_lang = 1;
  path_t cmdpath;
  path_t script_dir;

  sstate->cs_langs_loaded = 1;

  if (!sstate->compile_home_dir) {
    sstate->compile_home_dir = xstrdup(config->compile_home_dir);
  }
  snprintf(cs_conf_file_buf, cs_conf_file_len, "%s/conf/compile.cfg",
           sstate->compile_home_dir);
  if (!(cfg = prepare_parse_config_file(cs_conf_file_buf, 0))) return -1;
  sstate->cs_cfg = cfg;

  if (extra_compile_dirs) {
    int extra_cs_total = sarray_len(extra_compile_dirs);
    if (extra_cs_total > 0) {
      sstate->extra_cs_cfgs_total = extra_cs_total;
      XCALLOC(sstate->extra_cs_cfgs, sstate->extra_cs_cfgs_total + 1);
    }
  }

  if (sstate->extra_cs_cfgs_total > 0) {
    for (int i = 0; i < sstate->extra_cs_cfgs_total; ++i) {
      // check for win32_compile
      if (!strcmp(extra_compile_dirs[i], "win32_compile")) {
        sstate->enable_win32_languages = 1;
      }
      extra_cs_conf_path[0] = 0;
      if (os_IsAbsolutePath(extra_compile_dirs[i])) {
        snprintf(extra_cs_conf_path, sizeof(extra_cs_conf_path),
                 "%s/conf/compile.cfg", extra_compile_dirs[i]);
      } else if (config && config->contests_home_dir) {
        snprintf(extra_cs_conf_path, sizeof(extra_cs_conf_path),
                 "%s/%s/conf/compile.cfg", config->contests_home_dir,
                 extra_compile_dirs[i]);
      } else {
#if defined EJUDGE_CONTESTS_HOME_DIR
        snprintf(extra_cs_conf_path, sizeof(extra_cs_conf_path),
                 "%s/%s/conf/compile.cfg", EJUDGE_CONTESTS_HOME_DIR,
                 extra_compile_dirs[i]);
#endif
      }
      if (extra_cs_conf_path[0]) {
        sstate->extra_cs_cfgs[i] = prepare_parse_config_file(extra_cs_conf_path, 0);
      }
    }
  }

  cfg = sstate->cs_cfg;
  for (p = cfg; p; p = p->next) {
    if (strcmp(p->name, "language") != 0) continue;
    lp = (typeof(lp)) p;
    if (lp->id < 0) {
      fprintf(stderr, "%s: language identifier is negative\n", cs_conf_file_buf);
      goto failed;
    }
    if (!lp->id) lp->id = cur_lang++;
    if (lp->id > max_lang) max_lang = lp->id;
  }

  if (max_lang <= 0) {
    fprintf(stderr, "%s: no languages defined\n", cs_conf_file_buf);
    goto failed;
  }

  if (sstate->extra_cs_cfgs_total > 0) {
    for (int i = 0; i < sstate->extra_cs_cfgs_total; ++i) {
      cfg = sstate->extra_cs_cfgs[i];
      for (p = cfg; p; p = p->next) {
        if (strcmp(p->name, "language") != 0) continue;
        lp = (typeof(lp)) p;
        if (lp->id > 0 && lp->id > max_lang) max_lang = lp->id;
      }
    }
  }

  sstate->cs_lang_total = max_lang + 1;
  XCALLOC(sstate->cs_langs, sstate->cs_lang_total);
  XCALLOC(sstate->cs_loc_map, sstate->cs_lang_total);
  XCALLOC(sstate->cs_lang_names, sstate->cs_lang_total);

  cfg = sstate->cs_cfg;
  for (p = cfg; p; p = p->next) {
    if (strcmp(p->name, "language") != 0) continue;
    lp = (typeof(lp)) p;
    if (sstate->cs_langs[lp->id]) {
      fprintf(stderr, "%s: duplicated language id %d\n", cs_conf_file_buf, lp->id);
      goto failed;
    }
    sstate->cs_langs[lp->id] = lp;
  }

  if (sstate->extra_cs_cfgs_total > 0) {
    for (int i = 0; i < sstate->extra_cs_cfgs_total; ++i) {
      cfg = sstate->extra_cs_cfgs[i];
      for (p = cfg; p; p = p->next) {
        if (strcmp(p->name, "language") != 0) continue;
        lp = (typeof(lp)) p;
        if (lp->id > 0 && !sstate->cs_langs[lp->id]) {
          sstate->cs_langs[lp->id] = lp;
          lp->compile_dir_index = i + 1;
        }
      }
    }
  }

  /*
  script_dir[0] = 0;
  if (config->script_dir) {
    snprintf(script_dir, sizeof(script_dir), "%s", config->script_dir);
  }
#if defined EJUDGE_SCRIPT_DIR
  if (!*script_dir) {
    snprintf(script_dir, sizeof(script_dir), "%s", EJUDGE_SCRIPT_DIR);
  }
#endif
  */
  script_dir[0] = 0;
  if (config->compile_home_dir) {
    snprintf(script_dir, sizeof(script_dir), "%s/scripts",
             config->compile_home_dir);
  }
  if (!script_dir[0] && config->contests_home_dir) {
    snprintf(script_dir, sizeof(script_dir), "%s/compile/scripts",
             config->contests_home_dir);
  }
#if defined EJUDGE_CONTESTS_HOME_DIR
  if (!script_dir[0]) {
    snprintf(script_dir, sizeof(script_dir), "%s/compile/scripts",
             EJUDGE_CONTESTS_HOME_DIR);
  }
#endif

  if (*script_dir) {
    // detect actual language versions
    for (cur_lang = 1; cur_lang < sstate->cs_lang_total; cur_lang++) {
      if (!(lp = sstate->cs_langs[cur_lang])) continue;
      if (lp->compile_dir_index > 0) {
        sstate->cs_lang_names[cur_lang] = xstrdup(lp->long_name);
        continue;
      }
      snprintf(cmdpath, sizeof(cmdpath), "%s/%s-version", script_dir, lp->cmd);

      if (access(cmdpath, X_OK) >= 0) {
        char *args[4];
        args[0] = cmdpath;
        args[1] = "-f";
        args[2] = NULL;
        unsigned char *stdout_text = NULL;
        unsigned char *stderr_text = NULL;
        int r = ejudge_invoke_process(args, NULL, NULL, "/dev/null", NULL, 0, &stdout_text, &stderr_text);
        if (!r) {
          if (!stdout_text) stdout_text = xstrdup("");
          sstate->cs_lang_names[cur_lang] = chop2(stdout_text);
          stdout_text = NULL;
        } else {
          sstate->cs_lang_names[cur_lang] = xstrdup("");
          if (!stderr_text) stderr_text = xstrdup("");
          for (unsigned char *s = stderr_text; *s; ++s) {
            if (*s < ' ') *s = ' ';
          }
          fprintf(stderr, "%s: %s\n", sstate->cs_langs[cur_lang]->short_name, stderr_text);
        }
        xfree(stdout_text); stdout_text = NULL;
        xfree(stderr_text); stderr_text = NULL;
      }
    }
  }

  return 0;

 failed:
  return -1;
}
Exemple #10
0
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;
}
Exemple #11
0
static int
run_all_tests(int argc, char *argv[])
{
  unsigned char tmp_work_dir[PATH_MAX];
  unsigned char abs_prog_name[PATH_MAX];
  unsigned char abs_test_dir[PATH_MAX];
  unsigned char abs_work_dir[PATH_MAX];
  unsigned char test_base[PATH_MAX];
  unsigned char test_path[PATH_MAX];
  unsigned char corr_base[PATH_MAX];
  unsigned char corr_path[PATH_MAX];
  unsigned char info_base[PATH_MAX];
  unsigned char info_path[PATH_MAX];
  unsigned char tgzdir_base[PATH_MAX];
  unsigned char tgzdir_path[PATH_MAX];
  const unsigned char *s;
  int pid = getpid(), serial = 0;
  int retval = 0, status;
  long cpu_time, real_time;

  tmp_work_dir[0] = 0;

  if (!working_dir || !*working_dir) {
    s = getenv("TMPDIR");
    if (!s) s = getenv("TEMPDIR");
#if defined P_tmpdir
    if (!s) s = P_tmpdir;
#endif
    if (!s) s = "/tmp";
    while (1) {
      snprintf(tmp_work_dir, sizeof(tmp_work_dir), "%s/%d.%d", s, pid, ++serial);
      if (mkdir(tmp_work_dir, 0700) >= 0) break;
      if (errno != EEXIST) {
        fatal("cannot create directory %s: %s", tmp_work_dir, os_ErrorMsg());
      }
    }
    working_dir = xstrdup(tmp_work_dir);
  }
  if (!os_IsAbsolutePath(working_dir)) {
    snprintf(abs_work_dir, sizeof(abs_work_dir), "%s/%s", current_dir, working_dir);
    working_dir = xstrdup(abs_work_dir);
  }

  if (!os_IsAbsolutePath(argv[0])) {
    snprintf(abs_prog_name, sizeof(abs_prog_name), "%s/%s", current_dir, argv[0]);
    argv[0] = xstrdup(abs_prog_name);
  }
  if (!os_IsAbsolutePath(test_dir)) {
    snprintf(abs_test_dir, sizeof(abs_test_dir), "%s/%s", current_dir, test_dir);
    test_dir = xstrdup(abs_test_dir);
  }

  serial = 0;
  while (1) {
    snprintf(test_base, sizeof(test_base), test_pattern, ++serial);
    snprintf(test_path, sizeof(test_path), "%s/%s", test_dir, test_base);
    test_file = xstrdup(test_path);
    if (os_CheckAccess(test_path, REUSE_F_OK) < 0) break;
    corr_path[0] = 0;
    info_path[0] = 0;
    corr_file = NULL;
    info_file = NULL;
    if (corr_pattern && corr_pattern[0]) {
      snprintf(corr_base, sizeof(corr_base), corr_pattern, serial);
      snprintf(corr_path, sizeof(corr_path), "%s/%s", test_dir, corr_base);
      corr_file = xstrdup(corr_path);
    }
    if (info_pattern && info_pattern[0]) {
      snprintf(info_base, sizeof(info_base), info_pattern, serial);
      snprintf(info_path, sizeof(info_path), "%s/%s", test_dir, info_base);
      info_file = xstrdup(info_path);
    }
    if (tgzdir_pattern && tgzdir_pattern[0]) {
      snprintf(tgzdir_base, sizeof(tgzdir_base), tgzdir_pattern, serial);
      snprintf(tgzdir_path, sizeof(tgzdir_path), "%s/%s", test_dir, tgzdir_path);
      tgzdir_file = xstrdup(tgzdir_path);
    }
    cpu_time = 0;
    real_time = 0;
    status = run_program(argc, argv, &cpu_time, &real_time);
    if (status == RUN_CHECK_FAILED) retval = RUN_CHECK_FAILED;
    if (status != RUN_OK && retval == RUN_OK) retval = RUN_PARTIAL;
    if (quiet_flag <= 0) {
      printf("%-8d%-8.8s%-8ld%-8ld\n", serial, get_run_status_str(status),
             cpu_time, real_time);
    }
  }

  if (tmp_work_dir[0]) {
    remove_directory_recursively(tmp_work_dir, 0);
  }
  return retval;
}