Exemple #1
0
static int
check_style_only(
        const struct section_global_data *global,
        struct compile_request_packet *req,
        struct compile_reply_packet *rpl,
        const unsigned char *pkt_name,
        const unsigned char *run_name,
        const unsigned char *work_run_name,
        const unsigned char *report_dir,
        const unsigned char *status_dir)
{
  void *reply_bin = 0;
  size_t reply_bin_size = 0;
  unsigned char msgbuf[1024] = { 0 };
  path_t log_path;
  path_t txt_path;
  path_t work_src_path;
  path_t work_log_path;
  int r, i;
  const unsigned char *src_sfx = "";
  tpTask tsk = 0;

  // input file: ${global->compile_src_dir}/${pkt_name}${req->src_sfx}
  // output log file: ${report_dir}/${run_name}
  // file listing: ${report_dir}/${run_name} (if OK status)
  // working directory: ${global->compile_work_dir}

  snprintf(log_path, sizeof(log_path), "%s/%s", report_dir, run_name);
  snprintf(txt_path, sizeof(txt_path), "%s/%s.txt", report_dir, run_name);
  if (req->src_sfx) src_sfx = req->src_sfx;
  snprintf(work_src_path, sizeof(work_src_path), "%s/%s%s",
           global->compile_work_dir, work_run_name, src_sfx);
  snprintf(work_log_path, sizeof(work_log_path), "%s/%s.log",
           global->compile_work_dir, work_run_name);

  r = generic_copy_file(REMOVE, global->compile_src_dir, pkt_name, src_sfx,
                        0, global->compile_work_dir, work_run_name, src_sfx);
  if (!r) {
    snprintf(msgbuf, sizeof(msgbuf), "The source file %s/%s%s is missing.\n",
             global->compile_src_dir, pkt_name, src_sfx);
    goto internal_error;
  }
  if (r < 0) {
    snprintf(msgbuf, sizeof(msgbuf),
             "Read error on the source file %s/%s%s is missing.\n",
             global->compile_src_dir, pkt_name, src_sfx);
    goto internal_error;
  }

  //info("Starting: %s %s", req->style_checker, work_src_path);
  tsk = task_New();
  task_AddArg(tsk, req->style_checker);
  task_AddArg(tsk, work_src_path);
  task_SetPathAsArg0(tsk);
  task_SetWorkingDir(tsk, global->compile_work_dir);
  task_EnableProcessGroup(tsk);
  task_SetRedir(tsk, 0, TSR_FILE, "/dev/null", TSK_READ);
  task_SetRedir(tsk, 1, TSR_FILE, work_log_path, TSK_REWRITE, 0777);
  task_SetRedir(tsk, 2, TSR_DUP, 1);
  if (req->sc_env_num > 0) {
    for (i = 0; i < req->sc_env_num; i++)
      task_PutEnv(tsk, req->sc_env_vars[i]);
  }
  task_EnableAllSignals(tsk);

  task_PrintArgs(tsk);

  if (task_Start(tsk) < 0) {
    err("Failed to start style checker process");
    snprintf(msgbuf, sizeof(msgbuf), "Failed to start style checker %s\n",
             req->style_checker);
    goto internal_error;
  }

  task_Wait(tsk);
  if (task_IsTimeout(tsk)) {
    err("Style checker process is timed out");
    snprintf(msgbuf, sizeof(msgbuf), "Style checker %s process timeout\n",
             req->style_checker);
    goto internal_error;
  }
  r = task_Status(tsk);
  if (r != TSK_EXITED && r != TSK_SIGNALED) {
    err("Style checker invalid task status");
    snprintf(msgbuf, sizeof(msgbuf),
             "Style checker %s invalid task status %d\n",
             req->style_checker, r);
    goto internal_error;
  }
  if (r == TSK_SIGNALED) {
    err("Style checker terminated by signal");
    snprintf(msgbuf, sizeof(msgbuf),
             "Style checker %s terminated by signal %d\n",
             req->style_checker, task_TermSignal(tsk));
    goto internal_error;
  }
  r = task_ExitCode(tsk);
  if (r != 0 && r != RUN_COMPILE_ERR && r != RUN_PRESENTATION_ERR
      && r != RUN_WRONG_ANSWER_ERR && r != RUN_STYLE_ERR) {
    err("Invalid style checker exit code");
    snprintf(msgbuf, sizeof(msgbuf),
             "Style checker %s exit code %d\n",
             req->style_checker, r);
    goto internal_error;
  }
  if (r) {
    // style checker error
    rpl->status = RUN_STYLE_ERR;
    get_current_time(&rpl->ts3, &rpl->ts3_us);
    generic_copy_file(0, 0, work_log_path, "", 0, 0, log_path, "");
    generic_copy_file(0, 0, work_log_path, "", 0, 0, txt_path, "");
  } else {
    // success
    rpl->status = RUN_OK;
    get_current_time(&rpl->ts3, &rpl->ts3_us);
    generic_copy_file(0, 0, work_log_path, "", 0, 0, txt_path, "");
  }

  if (compile_reply_packet_write(rpl, &reply_bin_size, &reply_bin) < 0)
    goto cleanup;
  // ignore error: we cannot do anything anyway
  generic_write_file(reply_bin, reply_bin_size, SAFE, status_dir, run_name, 0);

cleanup:
  task_Delete(tsk); tsk = 0;
  xfree(reply_bin); reply_bin = 0;
  req = compile_request_packet_free(req);
  clear_directory(global->compile_work_dir);
  return 0;

internal_error:
  rpl->status = RUN_CHECK_FAILED;
  get_current_time(&rpl->ts3, &rpl->ts3_us);
  if (compile_reply_packet_write(rpl, &reply_bin_size, &reply_bin) < 0)
    goto cleanup;
  if (generic_write_file(msgbuf, strlen(msgbuf), 0, 0, log_path, 0) < 0)
    goto cleanup;
  if (generic_write_file(reply_bin, reply_bin_size, SAFE, status_dir,
                         run_name, 0) < 0) {
    unlink(log_path);
  }
  goto cleanup;
}
Exemple #2
0
static int
run_program(
        const struct nwrun_in_packet *packet,
        const unsigned char *program_path,
        const unsigned char *input_path,
        const unsigned char *output_path,
        const unsigned char *error_path,
        struct nwrun_out_packet *result)
{
  tpTask tsk = 0;

  tsk = task_New();
  if (!tsk) {
    snprintf(result->comment, sizeof(result->comment),
             "cannot create a new task");
    return RUN_CHECK_FAILED;
  }

  task_AddArg(tsk, program_path);
  task_SetPathAsArg0(tsk);
  task_SetWorkingDir(tsk, global->work_dir);
  if (packet->disable_stdin > 0) {
    task_SetRedir(tsk, 0, TSR_FILE, "/dev/null", TSK_READ);
  } else if (packet->redirect_stdin > 0 || packet->combined_stdin > 0) {
    task_SetRedir(tsk, 0, TSR_FILE, input_path, TSK_READ);
  }
  if (packet->ignore_stdout > 0) {
    task_SetRedir(tsk, 1, TSR_FILE, "/dev/null", TSK_WRITE, TSK_FULL_RW);
  } else if (packet->redirect_stdout > 0 || packet->combined_stdout > 0) {
    task_SetRedir(tsk, 1, TSR_FILE, output_path, TSK_REWRITE, TSK_FULL_RW);
  }
  if (packet->ignore_stderr > 0) {
    task_SetRedir(tsk, 2, TSR_FILE, "/dev/null", TSK_WRITE, TSK_FULL_RW);
  } else if (packet->redirect_stderr > 0) {
    task_SetRedir(tsk, 2, TSR_FILE, error_path, TSK_REWRITE, TSK_FULL_RW);
  }

  if (packet->time_limit_millis > 0) {
    task_SetMaxTimeMillis(tsk, packet->time_limit_millis);
  }
  if (packet->real_time_limit_millis > 0) {
    task_SetMaxRealTime(tsk, (packet->real_time_limit_millis + 999) / 1000);
  }
  if (packet->max_stack_size > 0) {
    task_SetStackSize(tsk, packet->max_stack_size);
  }
  if (packet->max_data_size > 0) {
    task_SetDataSize(tsk, packet->max_data_size);
  }
  if (packet->max_vm_size > 0) {
    task_SetVMSize(tsk, packet->max_vm_size);
  }
  task_SetMaxProcessCount(tsk, 1);
  if (packet->enable_secure_run > 0) {
    task_EnableSecureExec(tsk);
  }
  if (packet->enable_secure_run > 0 && packet->enable_memory_limit_error > 0) {
    task_EnableMemoryLimitError(tsk);
  }
  if (packet->enable_secure_run > 0 && packet->enable_security_violation_error > 0) {
    task_EnableSecurityViolationError(tsk);
  }

  task_EnableAllSignals(tsk);

  if (task_Start(tsk) < 0) {
    snprintf(result->comment, sizeof(result->comment),
             "task start is failed");
    task_Delete(tsk);
    return RUN_CHECK_FAILED;
  }

  task_Wait(tsk);

  result->cpu_time_millis = task_GetRunningTime(tsk);
  result->real_time_available = 1;
  result->real_time_millis = task_GetRealTime(tsk);
  result->max_memory_used = task_GetMemoryUsed(tsk);

  result->comment[0] = 0;

  if (packet->enable_secure_run > 0 
      && packet->enable_memory_limit_error > 0 && task_IsMemoryLimit(tsk)) {
    task_Delete(tsk);
    return RUN_MEM_LIMIT_ERR;
  }
  if (packet->enable_secure_run > 0
      && packet->enable_security_violation_error && task_IsSecurityViolation(tsk)) {
    task_Delete(tsk);
    return RUN_SECURITY_ERR;
  }
  if (task_IsTimeout(tsk)) {
    task_Delete(tsk);
    return RUN_TIME_LIMIT_ERR;
  }
  if (task_Status(tsk) == TSK_SIGNALED) {
    result->is_signaled = 1;
    result->signal_num = task_TermSignal(tsk);
    snprintf(result->exit_comment, sizeof(result->exit_comment),
             "%s", os_GetSignalString(result->signal_num));
    task_Delete(tsk);
    return RUN_RUN_TIME_ERR;
  }
  result->exit_code = task_ExitCode(tsk);
  task_Delete(tsk);
  return result->exit_code == 0 ? RUN_OK : RUN_RUN_TIME_ERR;
}
Exemple #3
0
static int
do_loop(void)
{
  path_t src_name;
  path_t exe_name;

  path_t src_path;
  path_t exe_path;
  path_t log_path;

  path_t exe_out;
  path_t log_out;
  path_t txt_out;
  path_t report_dir, status_dir;

  path_t  pkt_name, run_name, work_run_name;
  char   *pkt_ptr;
  size_t  pkt_len;
  int    r, i;
  tpTask tsk = 0;
  unsigned char msgbuf[512];
  int ce_flag;
  struct compile_request_packet *req = 0;
  struct compile_reply_packet rpl;
  void *rpl_pkt = 0;
  size_t rpl_size = 0;
  const unsigned char *tail_message = 0;
#if HAVE_TRUNCATE - 0
  struct stat stb;
#endif /* HAVE_TRUNCATE */
  FILE *log_f = 0;
  struct section_language_data *lang = 0;
  const struct section_global_data *global = serve_state.global;

  // if (cr_serialize_init(&serve_state) < 0) return -1;
  interrupt_init();
  interrupt_disable();

  while (1) {
    // terminate if signaled
    if (interrupt_get_status() || interrupt_restart_requested()) break;

    r = scan_dir(global->compile_queue_dir, pkt_name, sizeof(pkt_name));

    if (r < 0) {
      switch (-r) {
      case ENOMEM:
      case ENOENT:
      case ENFILE:
        err("trying to recover, sleep for 5 seconds");
        interrupt_enable();
        os_Sleep(5000);
        interrupt_disable();
        continue;
      default:
        err("unrecoverable error, exiting");
        return -1;
      }
    }

    if (!r) {
      interrupt_enable();
      os_Sleep(global->sleep_time);
      interrupt_disable();
      continue;
    }

    pkt_ptr = 0;
    pkt_len = 0;
    r = generic_read_file(&pkt_ptr, 0, &pkt_len, SAFE | REMOVE,
                          global->compile_queue_dir, pkt_name, "");
    if (r == 0) continue;
    if (r < 0 || !pkt_ptr) {
      // it looks like there's no reasonable recovery strategy
      // so, just ignore the error
      continue;
    }

    r = compile_request_packet_read(pkt_len, pkt_ptr, &req);
    xfree(pkt_ptr); pkt_ptr = 0;
    if (r < 0) {
      /*
       * the incoming packet is completely broken, so just drop it
       */
      goto cleanup_and_continue;
    }

    if (!req->contest_id) {
      // special packets
      r = req->lang_id;
      req = compile_request_packet_free(req);
      switch (r) {
      case 1:
        interrupt_flag_interrupt();
        break;
      case 2:
        interrupt_flag_sighup();
        break;
      }
      continue;
    }

    memset(&rpl, 0, sizeof(rpl));
    rpl.judge_id = req->judge_id;
    rpl.contest_id = req->contest_id;
    rpl.run_id = req->run_id;
    rpl.ts1 = req->ts1;
    rpl.ts1_us = req->ts1_us;
    rpl.use_uuid = req->use_uuid;
    rpl.uuid[0] = req->uuid[0];
    rpl.uuid[1] = req->uuid[1];
    rpl.uuid[2] = req->uuid[2];
    rpl.uuid[3] = req->uuid[3];
    get_current_time(&rpl.ts2, &rpl.ts2_us);
    rpl.run_block_len = req->run_block_len;
    rpl.run_block = req->run_block; /* !!! shares memory with req */
    msgbuf[0] = 0;

    /* prepare paths useful to report messages to the serve */
    snprintf(report_dir, sizeof(report_dir),
             "%s/%06d/report", global->compile_dir, rpl.contest_id);
    snprintf(status_dir, sizeof(status_dir),
             "%s/%06d/status", global->compile_dir, rpl.contest_id);
    if (req->use_uuid > 0) {
      snprintf(run_name, sizeof(run_name), "%s", ej_uuid_unparse(req->uuid, NULL));
    } else {
      snprintf(run_name, sizeof(run_name), "%06d", rpl.run_id);
    }
    snprintf(work_run_name, sizeof(work_run_name), "%06d", rpl.run_id);
    pathmake(log_out, report_dir, "/", run_name, NULL);
    snprintf(txt_out, sizeof(txt_out), "%s/%s.txt", report_dir, run_name);

    make_all_dir(status_dir, 0777);
    make_dir(report_dir, 0777);

    if (!r) {
      /*
       * there is something wrong, but we have contest_id, judge_id
       * and run_id in place, so we can report an error back
       * to serve
       */
      snprintf(msgbuf, sizeof(msgbuf), "invalid compile packet\n");
      goto report_internal_error;
    }

    if (req->style_check_only && req->style_checker && req->style_checker[0]) {
      check_style_only(global, req, &rpl, pkt_name, run_name, work_run_name,
                       report_dir, status_dir);
      req = 0;
      continue;
    }

    if (req->lang_id <= 0 || req->lang_id > serve_state.max_lang
        || !(lang = serve_state.langs[req->lang_id])) {
      snprintf(msgbuf, sizeof(msgbuf), "invalid lang_id %d\n", req->lang_id);
      goto report_internal_error;
    }
    pathmake(src_name, work_run_name, lang->src_sfx, NULL);
    pathmake(exe_name, work_run_name, lang->exe_sfx, NULL);

    pathmake(src_path, global->compile_work_dir, "/", src_name, NULL);
    pathmake(exe_path, global->compile_work_dir, "/", exe_name, NULL);
    pathmake(log_path, global->compile_work_dir, "/", "log", NULL);
    /* the resulting executable file */
    snprintf(exe_out, sizeof(exe_out), "%s/%s%s", report_dir, run_name, lang->exe_sfx);

    /* move the source file into the working dir */
    r = generic_copy_file(REMOVE, global->compile_src_dir, pkt_name,
                          lang->src_sfx,
                          0, global->compile_work_dir, src_name, "");
    if (!r) {
      snprintf(msgbuf, sizeof(msgbuf), "the source file is missing\n");
      err("the source file is missing");
      goto report_internal_error;
    }
    if (r < 0) {
      snprintf(msgbuf, sizeof(msgbuf), "error reading the source file\n");
      err("cannot read the source file");
      goto report_internal_error;
    }

    tail_message = 0;
    ce_flag = 0;

    if (req->output_only) {
      // copy src_path -> exe_path
      generic_copy_file(0, NULL, src_path, NULL, 0, NULL, exe_path, NULL);
      ce_flag = 0;
      rpl.status = RUN_OK;
    } else {
      if (req->style_checker) {
        /* run style checker */
        //info("Starting: %s %s", req->style_checker, src_path);
        tsk = task_New();
        task_AddArg(tsk, req->style_checker);
        task_AddArg(tsk, src_path);
        task_SetPathAsArg0(tsk);
        task_SetWorkingDir(tsk, global->compile_work_dir);
        task_EnableProcessGroup(tsk);
        task_SetRedir(tsk, 0, TSR_FILE, "/dev/null", TSK_READ);
        task_SetRedir(tsk, 1, TSR_FILE, log_path, TSK_REWRITE, 0777);
        task_SetRedir(tsk, 2, TSR_DUP, 1);
        if (req->sc_env_num > 0) {
          for (i = 0; i < req->sc_env_num; i++)
            task_PutEnv(tsk, req->sc_env_vars[i]);
        }
        if (lang->compile_real_time_limit > 0) {
          task_SetMaxRealTime(tsk, lang->compile_real_time_limit);
        }
        task_EnableAllSignals(tsk);

        task_PrintArgs(tsk);

        if (task_Start(tsk) < 0) {
          err("Failed to start style checker process");
          tail_message = "\n\nFailed to start style checker";
          ce_flag = 1;
          rpl.status = RUN_STYLE_ERR;
        } else {
          task_Wait(tsk);
          if (task_IsTimeout(tsk)) {
            err("Style checker process timed out");
            tail_message = "\n\nStyle checker process timed out";
            ce_flag = 1;
            rpl.status = RUN_STYLE_ERR;
          } else if (task_IsAbnormal(tsk)) {
            info("Style checker failed");
            ce_flag = 1;
            rpl.status = RUN_STYLE_ERR;
          } else {
            info("Style checker sucessful");
            ce_flag = 0;
            rpl.status = RUN_OK;
          }
        }
        task_Delete(tsk); tsk = 0;
      }

      if (!ce_flag) {
        //info("Starting: %s %s %s", lang->cmd, src_name, exe_name);
        tsk = task_New();
        task_AddArg(tsk, lang->cmd);
        task_AddArg(tsk, src_name);
        task_AddArg(tsk, exe_name);
        task_SetPathAsArg0(tsk);
        task_EnableProcessGroup(tsk);
        if (((ssize_t) req->max_vm_size) > 0) {
          task_SetVMSize(tsk, req->max_vm_size);
        } else if (((ssize_t) lang->max_vm_size) > 0) {
          task_SetVMSize(tsk, lang->max_vm_size);
        } else if (((ssize_t) global->compile_max_vm_size) > 0) {
          task_SetVMSize(tsk, global->compile_max_vm_size);
        }
        if (((ssize_t) req->max_stack_size) > 0) {
          task_SetStackSize(tsk, req->max_stack_size);
        } else if (((ssize_t) lang->max_stack_size) > 0) {
          task_SetStackSize(tsk, lang->max_stack_size);
        } else if (((ssize_t) global->compile_max_stack_size) > 0) {
          task_SetStackSize(tsk, global->compile_max_stack_size);
        }
        if (((ssize_t) req->max_file_size) > 0) {
          task_SetMaxFileSize(tsk, req->max_file_size);
        } else if (((ssize_t) lang->max_file_size) > 0) {
          task_SetMaxFileSize(tsk, lang->max_file_size);
        } else if (((ssize_t) global->compile_max_file_size) > 0) {
          task_SetMaxFileSize(tsk, global->compile_max_file_size);
        }

        if (req->env_num > 0) {
          for (i = 0; i < req->env_num; i++)
            task_PutEnv(tsk, req->env_vars[i]);
        }
        task_SetWorkingDir(tsk, global->compile_work_dir);
        task_SetRedir(tsk, 0, TSR_FILE, "/dev/null", TSK_READ);
        task_SetRedir(tsk, 1, TSR_FILE, log_path, TSK_APPEND, 0777);
        task_SetRedir(tsk, 2, TSR_DUP, 1);
        if (lang->compile_real_time_limit > 0) {
          task_SetMaxRealTime(tsk, lang->compile_real_time_limit);
        }
        task_EnableAllSignals(tsk);
        
        /*
        if (cr_serialize_lock(&serve_state) < 0) {
          // FIXME: propose reasonable recovery?
          return -1;
        }
        */

        task_PrintArgs(tsk);
        task_Start(tsk);
        task_Wait(tsk);

        /*
        if (cr_serialize_unlock(&serve_state) < 0) {
          // FIXME: propose reasonable recovery?
          return -1;
        }
        */

        if (task_IsTimeout(tsk)) {
          err("Compilation process timed out");
          tail_message = "\n\nCompilation process timed out";
          ce_flag = 1;
          rpl.status = RUN_COMPILE_ERR;
        } else if (task_IsAbnormal(tsk)) {
          info("Compilation failed");
          ce_flag = 1;
          rpl.status = RUN_COMPILE_ERR;
        } else {
          info("Compilation sucessful");
          ce_flag = 0;
          rpl.status = RUN_OK;
        }
      }
    }

    get_current_time(&rpl.ts3, &rpl.ts3_us);
    if (compile_reply_packet_write(&rpl, &rpl_size, &rpl_pkt) < 0)
      goto cleanup_and_continue;

    while (1) {
      if (ce_flag) {
#if HAVE_TRUNCATE - 0
        // truncate log file at size 1MB
        if (stat(log_path, &stb) >= 0 && stb.st_size > MAX_LOG_SIZE) {
          truncate(log_path, MAX_LOG_SIZE);
          if ((log_f = fopen(log_path, "a"))) {
            fprintf(log_f, "\n\nCompilation log is truncated by ejudge!\n");
            fclose(log_f); log_f = 0;
          }
        }
#endif
        // append tail_message
        if (tail_message && (log_f = fopen(log_path, "a"))) {
          fprintf(log_f, "%s\n", tail_message);
          fclose(log_f); log_f = 0;
        }
        r = generic_copy_file(0, 0, log_path, "", 0, 0, log_out, "");
      } else {
        r = generic_copy_file(0, 0, exe_path, "", 0, 0, exe_out, "");
        generic_copy_file(0, 0, log_path, "", 0, 0, txt_out, "");
      }
      if (r >= 0 && generic_write_file(rpl_pkt, rpl_size, SAFE,
                                       status_dir, run_name, "") >= 0)
        break;

      info("waiting 5 seconds hoping for things to change");
      interrupt_enable();
      os_Sleep(5000);
      interrupt_disable();
    }
    goto cleanup_and_continue;

  report_internal_error:;
    rpl.status = RUN_CHECK_FAILED;
    get_current_time(&rpl.ts3, &rpl.ts3_us);
    if (compile_reply_packet_write(&rpl, &rpl_size, &rpl_pkt) < 0)
      goto cleanup_and_continue;
    if (generic_write_file(msgbuf, strlen(msgbuf), 0, 0, log_out, 0) < 0)
      goto cleanup_and_continue;
    if (generic_write_file(rpl_pkt, rpl_size, SAFE, status_dir, run_name, 0) < 0)
      unlink(log_out);
    goto cleanup_and_continue;

  cleanup_and_continue:;
    task_Delete(tsk); tsk = 0;
    clear_directory(global->compile_work_dir);
    xfree(rpl_pkt); rpl_pkt = 0;
    req = compile_request_packet_free(req);
  } /* while (1) */

  return 0;
}
Exemple #4
0
static int
run_program(int argc, char *argv[], long *p_cpu_time, long *p_real_time)
{
  tTask *tsk = 0;
  int i;
  int retcode = RUN_CHECK_FAILED;
  struct testinfo_struct tinfo;
  unsigned char input_path[PATH_MAX];
  unsigned char output_path[PATH_MAX];
  unsigned char error_path[PATH_MAX];
  unsigned char buf[1024];
  long used_vm_size;

  memset(&tinfo, 0, sizeof(tinfo));
  input_path[0] = 0;
  output_path[0] = 0;
  error_path[0] = 0;

  if (!all_tests) {
    if (test_file && test_file[0] && test_pattern && test_pattern[0]) {
      // guess test_num from test_file and test_pat
      // FIXME: dumb!
      i = 0;
      do {
        ++i;
        snprintf(buf, sizeof(buf), test_pattern, i);
      } while (i < 1000 && strcmp(buf, test_file) != 0);
      if (i >= 1000) {
        fatal("failed to guess test_num from test_file and test_pattern");
      }
      test_num = i;
      test_file = NULL;
    }

    if (test_num > 0) {
      if (test_pattern && test_pattern[0]) {
        snprintf(buf, sizeof(buf), test_pattern, test_num);
        test_file = strdup(buf);
      }
      if (corr_pattern && corr_pattern[0]) {
        snprintf(buf, sizeof(buf), corr_pattern, test_num);
        corr_file = strdup(buf);
      }
      if (info_pattern && info_pattern[0]) {
        snprintf(buf, sizeof(buf), info_pattern, test_num);
        info_file = strdup(buf);
      }
      if (tgzdir_pattern && tgzdir_pattern[0]) {
        snprintf(buf, sizeof(buf), tgzdir_pattern, test_num);
        tgzdir_file = strdup(buf);
      }
    }
  }

  if (info_file && (i = testinfo_parse(info_file, &tinfo, NULL)) < 0) {
    fatal("testinfo file parse error: %s", testinfo_strerror(-i));
  }

  if (test_file) {
    if (!input_file || !input_file[0]) input_file = DEFAULT_INPUT_FILE_NAME;
    if (working_dir && working_dir[0]) {
      snprintf(input_path, sizeof(input_path), "%s/%s", working_dir, input_file);
    } else {
      snprintf(input_path, sizeof(input_path), "%s", input_file);
    }
  }

  if (corr_file) {
    if (!output_file || !output_file[0]) output_file = DEFAULT_OUTPUT_FILE_NAME;
    if (working_dir && working_dir[0]) {
      snprintf(output_path, sizeof(output_path), "%s/%s", working_dir, output_file);
    } else {
      snprintf(output_path, sizeof(output_path), "%s", output_file);
    }
  }

  if (info_file && tinfo.check_stderr > 0) {
    error_file = DEFAULT_ERROR_FILE_NAME;
    if (working_dir && working_dir[0]) {
      snprintf(error_path, sizeof(error_path), "%s/%s", working_dir, error_file);
    } else {
      snprintf(error_path, sizeof(error_path), "%s", error_file);
    }
  }

  if (!(tsk = task_New())) fatal("cannot create task");
  task_SetQuietFlag(tsk);
  task_pnAddArgs(tsk, argc, argv);
  task_pnAddArgs(tsk, tinfo.cmd_argc, tinfo.cmd_argv);
  task_SetPathAsArg0(tsk);
  if (working_dir) task_SetWorkingDir(tsk, working_dir);
  if (test_file) {
    if (copy_file(NULL, test_file, working_dir, input_file, -1, -1) < 0)
      fatal("copy failed");
    if (use_stdin) task_SetRedir(tsk, 0, TSR_FILE, input_path, TSK_READ);
  } else {
    if (stdin_file) task_SetRedir(tsk, 0, TSR_FILE, stdin_file, TSK_READ);
  }
  if (corr_file) {
    if (use_stdout) task_SetRedir(tsk, 1, TSR_FILE, output_path, TSK_REWRITE, TSK_FULL_RW);
  } else {
    if (stdout_file)
      task_SetRedir(tsk, 1, TSR_FILE, stdout_file, TSK_REWRITE, TSK_FULL_RW);
  }
  if (info_file && tinfo.check_stderr > 0) {
    task_SetRedir(tsk, 2, TSR_FILE, error_path, TSK_REWRITE, TSK_FULL_RW);
  } else {
    if (stderr_file)
      task_SetRedir(tsk, 2, TSR_FILE, stderr_file, TSK_REWRITE, TSK_FULL_RW);
  }
  if (clear_env_flag) task_ClearEnv(tsk);
  for (i = 0; i < env_vars.u; i++)
    task_PutEnv(tsk, env_vars.v[i]);
  for (i = 0; i < tinfo.env_u; ++i) {
    task_PutEnv(tsk, tinfo.env_v[i]);
  }
  if (time_limit_millis > 0)
    if (task_SetMaxTimeMillis(tsk, time_limit_millis) < 0)
      fatal("--time-limit-millis is not supported");
  if (time_limit > 0) task_SetMaxTime(tsk, time_limit);
  if (real_time_limit > 0) task_SetMaxRealTime(tsk, real_time_limit);
  if (kill_signal)
    if (task_SetKillSignal(tsk, kill_signal) < 0)
      fatal("invalid value for --kill-signal option");
  if (no_core_dump) task_DisableCoreDump(tsk);
  if (max_vm_size) task_SetVMSize(tsk, max_vm_size);
  if (max_stack_size) task_SetStackSize(tsk, max_stack_size);
  if (max_data_size) task_SetDataSize(tsk, max_data_size);
  if (memory_limit)
    if (task_EnableMemoryLimitError(tsk) < 0)
      fatal("--memory-limit is not supported");
  if (secure_exec)
    if (task_EnableSecureExec(tsk) < 0)
      fatal("--secure-exec is not supported");
  if (security_violation)
    if (task_EnableSecurityViolationError(tsk) < 0)
      fatal("--security-violation is not supported");

  task_PrintArgs(tsk);

  if (task_Start(tsk) < 0) {
    fprintf(stderr, "Status: CF\n"
            "Description: cannot start task: %s\n", task_GetErrorMessage(tsk));
    retcode = RUN_CHECK_FAILED;
    goto cleanup;
  }
  task_NewWait(tsk);
  if (memory_limit && task_IsMemoryLimit(tsk)) {
    if (all_tests <= 0) {
      fprintf(stderr, "Status: ML\n"
              "Description: memory limit exceeded\n");
    }
    retcode = RUN_MEM_LIMIT_ERR;
  } else if (security_violation && task_IsSecurityViolation(tsk)) {
    if (all_tests <= 0) {
      fprintf(stderr, "Status: SV\n"
              "Description: security violation\n");
    }
    retcode = RUN_SECURITY_ERR;
  } else if (task_IsTimeout(tsk)) {
    if (all_tests <= 0) {
      fprintf(stderr, "Status: TL\n"
              "Description: time limit exceeded\n");
    }
    retcode = RUN_TIME_LIMIT_ERR;
  } else if (task_IsAbnormal(tsk)
             && (!info_file || tinfo.exit_code <= 0 || task_Status(tsk) != TSK_EXITED
                 || task_ExitCode(tsk) != tinfo.exit_code)) {
    if (all_tests <= 0) {
      fprintf(stderr, "Status: RT\n");
      if (task_Status(tsk) == TSK_SIGNALED) {
        fprintf(stderr, "Signal: %d\n", task_TermSignal(tsk));
      } else {
        fprintf(stderr, "Exitcode: %d\n", task_ExitCode(tsk));
      }
      fprintf(stderr, "Description: run-time error\n");
    }
    retcode = RUN_RUN_TIME_ERR;
  } else {
    if (info_file && tinfo.check_stderr > 0) {
      if (copy_file(working_dir, error_file, NULL, corr_file, group, mode) < 0) {
        fprintf(stderr, "Status: PE\n");
      } else {
        if (quiet_flag <= 0 && all_tests <= 0) fprintf(stderr, "Status: OK\n");
        retcode = 0;
      }
    } else if (corr_file && update_corr > 0) {
      if (copy_file(working_dir, output_file, NULL, corr_file, group, mode) < 0) {
        fprintf(stderr, "Status: PE\n");
      } else {
        if (quiet_flag <= 0 && all_tests <= 0) fprintf(stderr, "Status: OK\n");
        retcode = 0;
      }
    } else {
      if (quiet_flag <= 0 && all_tests <= 0) fprintf(stderr, "Status: OK\n");
      retcode = 0;
    }
  }
  if (quiet_flag <= 0 && all_tests <= 0) {
    fprintf(stderr, "CPUTime: %ld\n", task_GetRunningTime(tsk));
    fprintf(stderr, "RealTime: %ld\n", task_GetRealTime(tsk));
    used_vm_size = task_GetMemoryUsed(tsk);
    if (used_vm_size > 0) {
      fprintf(stderr, "VMSize: %ld\n", used_vm_size);
    }
  }
  if (p_cpu_time) *p_cpu_time = task_GetRunningTime(tsk);
  if (p_real_time) *p_real_time = task_GetRealTime(tsk);

cleanup:
  task_Delete(tsk); tsk = NULL;
  if (input_path[0]) unlink(input_path);
  if (output_path[0]) unlink(output_path);
  if (error_path[0]) unlink(error_path);

  return retcode;
}