static void do_loop(void) { unsigned char new_entry_name[EJ_PATH_MAX]; unsigned char out_entry_name[EJ_PATH_MAX]; unsigned char new_path[EJ_PATH_MAX]; unsigned char out_path[EJ_PATH_MAX]; int r; int serial = 0; while (1) { r = scan_dir(global->queue_dir, new_entry_name, sizeof(new_entry_name), 0); if (r < 0) { die("scan_dir failed on %s", global->queue_dir); /* FIXME: recover and continue */ } if (!r) { os_Sleep(global->sleep_time); continue; } snprintf(out_entry_name, sizeof(out_entry_name), "%s_%s", os_NodeName(), new_entry_name); snprintf(new_path, sizeof(new_path), "%s/dir/%s", global->queue_dir, new_entry_name); snprintf(out_path, sizeof(out_path), "%s/out/%s", global->queue_dir, out_entry_name); while (rename(new_path, out_path) < 0) { if (errno == ENOENT) { err("file %s is stolen?", new_path); out_path[0] = 0; os_Sleep(global->sleep_time); break; } if (errno == ENOTEMPTY || errno == EEXIST) { err("directory %s already exists", out_path); snprintf(out_entry_name, sizeof(out_entry_name), "%s_%d_%s", os_NodeName(), ++serial, new_entry_name); snprintf(out_path, sizeof(out_path), "%s/out/%s", global->queue_dir, out_entry_name); continue; } die("rename: %s -> %s failed: %s", new_path, out_path, strerror(errno)); } if (out_path[0]) { read_packet(out_path); remove_directory_recursively(out_path, 0); } } }
/*! Remove a directory recursively like <tt>rm -r</tt>. * * \return * 0 on success, -1 on failure * * \param error * On failure, \c error will be set to a newly allocated string * that contains the error message. * On success, or when out of memory, * \c error will be set to \c NULL. * * \param dirname * the directory to remove */ static int remove_directory_recursively(char **error, const char *dirname) { DIR *dir; struct dirent *entry; /* continue on errors and report only the first error that occured */ *error = NULL; dir = opendir(dirname); if (dir == NULL && *error == NULL) { *error = sprintf_alloc("Unable to read directory entries of \"%s\": %s.", dirname, strerror(errno)); } else { for (entry = readdir(dir); entry != NULL; entry = readdir(dir)) { char *name; if ( strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } name = sprintf_alloc("%s/%s", dirname, entry->d_name); if (name == NULL && *error == NULL) { *error = sprintf_alloc("Out of memory."); continue; } if (entry->d_type == DT_DIR) { char *sub_error; if (remove_directory_recursively(&sub_error, name) != 0 && *error == NULL) { *error = sub_error; } } else { if (unlink(name) != 0 && *error == NULL) { *error = sprintf_alloc("Unable to remove file \"%s\": %s.", name, strerror(errno)); } } free(name); } if (closedir(dir) != 0 && *error == NULL) { *error = sprintf_alloc("Unable to close directory \"%s\": %s.", dirname, strerror(errno)); } } if (rmdir(dirname) != 0) { if (*error == NULL) { *error = sprintf_alloc("Unable to remove directory \"%s\": %s.", dirname, strerror(errno)); } return -1; } /* all previous errors are irrelevant because rmdir() was successful */ *error = NULL; return 0; }
/*! Convert a TeX or LaTeX source to DVI or PDF. */ void texcaller_convert(char **result, size_t *result_size, char **info, const char *source, size_t source_size, const char *source_format, const char *result_format, int max_runs) { char *error; const char *cmd; const char *tmpdir; char *dir = NULL; char *dir_template = NULL; char *source_filename = NULL; char *aux_filename = NULL; char *log_filename = NULL; char *result_filename = NULL; char *aux = NULL; size_t aux_size = 0; char *aux_old = NULL; size_t aux_old_size = 0; int runs; *result = NULL; *result_size = 0; *info = NULL; /* check arguments */ if (strcmp(source_format, "TeX") == 0 && strcmp(result_format, "DVI") == 0) { cmd = "tex"; } else if (strcmp(source_format, "TeX") == 0 && strcmp(result_format, "PDF") == 0) { cmd = "pdftex"; } else if (strcmp(source_format, "LaTeX") == 0 && strcmp(result_format, "DVI") == 0) { cmd = "latex"; } else if (strcmp(source_format, "LaTeX") == 0 && strcmp(result_format, "PDF") == 0) { cmd = "pdflatex"; } else { *info = sprintf_alloc("Unable to convert from \"%s\" to \"%s\".", source_format, result_format); goto cleanup; } if (max_runs < 2) { *info = sprintf_alloc("Argument max_runs is %i, but must be >= 2.", max_runs); goto cleanup; } /* create temporary directory */ tmpdir = getenv("TMPDIR"); if (tmpdir == NULL || strcmp(tmpdir, "") == 0) { tmpdir = "/tmp"; } dir_template = sprintf_alloc("%s/texcaller-temp-XXXXXX", tmpdir); if (dir_template == NULL) { goto cleanup; } dir = mkdtemp(dir_template); if (dir == NULL) { *info = sprintf_alloc("Unable to create temporary directory from template \"%s\": %s.", dir_template, strerror(errno)); goto cleanup; } source_filename = sprintf_alloc("%s/texput.tex", dir); if (source_filename == NULL) { goto cleanup; } aux_filename = sprintf_alloc("%s/texput.aux", dir); if (aux_filename == NULL) { goto cleanup; } log_filename = sprintf_alloc("%s/texput.log", dir); if (log_filename == NULL) { goto cleanup; } if (strcmp(result_format, "DVI") == 0) { result_filename = sprintf_alloc("%s/texput.dvi", dir); } else { result_filename = sprintf_alloc("%s/texput.pdf", dir); } if (result_filename == NULL) { goto cleanup; } /* create source file */ if (write_file(&error, source_filename, source, source_size) != 0) { *info = error; goto cleanup; } /* run command as often as necessary */ for (runs = 1; runs <= max_runs; runs++) { pid_t pid; pid = fork(); if (pid == -1) { *info = sprintf_alloc("Unable to fork child process: %s.", strerror(errno)); goto cleanup; } /* child process */ if (pid == 0) { /* run command within the temporary directory */ if (chdir(dir) != 0) { exit(1); } /* prevent access to stdin, stdout and stderr */ fclose(stdin); fclose(stdout); fclose(stderr); /* execute command */ execlp(cmd, cmd, "-interaction=batchmode", "-halt-on-error", "-file-line-error", "-no-shell-escape", "texput.tex", NULL); } /* wait for child process */ for (;;) { int status; pid_t wpid = waitpid(pid, &status, 0); if (wpid == -1) { *info = sprintf_alloc("Unable to wait for child process: %s.", strerror(errno)); goto cleanup; } if (WIFSIGNALED(status)) { *info = sprintf_alloc("Command \"%s\" was terminated by signal %i.", cmd, (int)WTERMSIG(status)); goto cleanup; } if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { *info = sprintf_alloc("Command \"%s\" terminated with exit status %i.", cmd, (int)WEXITSTATUS(status)); goto cleanup; } if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { break; } } /* read new aux file, saving old one */ free(aux_old); aux_old = aux; aux_old_size = aux_size; read_file(&aux, &aux_size, &error, aux_filename); /* tolerate missing aux file */ free(error); /* check whether aux file stabilized, which is also true if there isn't and wasn't any aux file */ if (aux_size == aux_old_size && memcmp(aux, aux_old, aux_size) == 0) { read_file(result, result_size, &error, result_filename); if (*result == NULL) { *info = error; goto cleanup; } *info = sprintf_alloc("Generated %s (%lu bytes)" " from %s (%lu bytes) after %i runs.", result_format, (unsigned long)*result_size, source_format, (unsigned long)source_size, runs); goto cleanup; } } /* aux file didn't stabilize */ *info = sprintf_alloc("Output didn't stabilize after %i runs.", max_runs); goto cleanup; /* cleanup all used resources */ cleanup: if (log_filename != NULL) { char *log; size_t log_size; read_file(&log, &log_size, &error, log_filename); free(error); if (log != NULL) { if (*info == NULL) { *info = log; } else { char *info_old = *info; *info = sprintf_alloc("%s\n\n%s", info_old, log); free(info_old); free(log); } } } if (dir != NULL && remove_directory_recursively(&error, dir) != 0) { free(*result); *result = NULL; *result_size = 0; free(*info); *info = error; } free(dir_template); free(source_filename); free(aux_filename); free(log_filename); free(result_filename); free(aux); free(aux_old); }
static void read_packet(const unsigned char *dir_path) { unsigned char packet_conf_file[EJ_PATH_MAX]; FILE *f = 0; struct generic_section_config *packet_config = 0; struct nwrun_in_packet *packet = 0; unsigned char result_name[EJ_PATH_MAX]; unsigned char result_in_dir[EJ_PATH_MAX]; unsigned char result_dir_dir[EJ_PATH_MAX]; unsigned char contest_dir[EJ_PATH_MAX]; struct nwrun_out_packet result; unsigned char result_packet_path[EJ_PATH_MAX]; int clean_result_dir = 0; memset(&result, 0, sizeof(result)); snprintf(packet_conf_file,sizeof(packet_conf_file),"%s/packet.cfg",dir_path); packet_config = nwrun_in_packet_parse(packet_conf_file, &packet); if (!packet_config) goto cleanup; nwrun_in_packet_print(stderr, (const struct nwrun_in_packet *) packet_config); /* setup packet defaults */ if (packet->contest_id <= 0) { err("contest_id is not set"); goto cleanup; } if (packet->prob_id <= 0) { err("prob_id is not set"); goto cleanup; } if (packet->test_num <= 0) { err("test_num is not set"); goto cleanup; } if (packet->judge_id <= 0) { err("judge_id is not set"); goto cleanup; } if (!packet->program_name[0]) { err("program_name is not set"); goto cleanup; } if (!packet->test_file_name[0]) { err("test_file_name is not set"); goto cleanup; } if (!packet->input_file_name[0]) { snprintf(packet->input_file_name, sizeof(packet->input_file_name), "%s", DEFAULT_INPUT_FILE_NAME); } if (!packet->result_file_name[0]) { err("result_file_name is not set"); goto cleanup; } if (!packet->output_file_name[0]) { snprintf(packet->output_file_name, sizeof(packet->output_file_name), "%s", DEFAULT_OUTPUT_FILE_NAME); } if (!packet->error_file_name[0]) { snprintf(packet->error_file_name, sizeof(packet->error_file_name), "%s", DEFAULT_ERROR_FILE_NAME); } if (packet->max_output_file_size <= 0) { packet->max_output_file_size = DEFAULT_MAX_OUTPUT_FILE_SIZE; } if (packet->max_error_file_size <= 0) { packet->max_error_file_size = DEFAULT_MAX_ERROR_FILE_SIZE; } if (packet->time_limit_millis <= 0) { err("time_limit_millis is invalid (%d)", packet->time_limit_millis); goto cleanup; } /* create the output directory */ snprintf(result_name, sizeof(result_name), "%c%c%d%c%d%c%d%c%d%c%d", get_priority_code(packet->priority - 17), get_num_prefix(packet->contest_id), packet->contest_id, get_num_prefix(packet->run_id - 1), packet->run_id - 1, get_num_prefix(packet->prob_id), packet->prob_id, get_num_prefix(packet->test_num), packet->test_num, get_num_prefix(packet->judge_id), packet->judge_id); if (packet->use_contest_id_in_reply) { snprintf(contest_dir, sizeof(contest_dir), "%s/%06d", global->result_dir, packet->contest_id); if (make_all_dir(contest_dir, 0777) < 0) { goto cleanup; } snprintf(result_in_dir, sizeof(result_in_dir), "%s/in/%s_%s", contest_dir, os_NodeName(), result_name); snprintf(result_dir_dir, sizeof(result_dir_dir), "%s/dir/%s", contest_dir, result_name); } else { snprintf(result_in_dir, sizeof(result_in_dir), "%s/in/%s_%s", global->result_dir, os_NodeName(), result_name); snprintf(result_dir_dir, sizeof(result_dir_dir), "%s/dir/%s", global->result_dir, result_name); } if (make_dir(result_in_dir, 0777) < 0) { goto cleanup; } clean_result_dir = 1; // set default values snprintf(result.hostname, sizeof(result.hostname), "%s", os_NodeName()); result.contest_id = packet->contest_id; result.run_id = packet->run_id; result.prob_id = packet->prob_id; result.test_num = packet->test_num; result.judge_id = packet->judge_id; result.status = RUN_CHECK_FAILED; snprintf(result.comment, sizeof(result.comment), "Default status was not changed"); handle_packet(dir_path, packet, result_in_dir, &result); snprintf(result_packet_path, sizeof(result_packet_path), "%s/packet.cfg", result_in_dir); if (!(f = fopen(result_packet_path, "wb"))) { err("cannot open file %s: %s", result_packet_path, os_ErrorMsg()); goto cleanup; } nwrun_out_packet_print(f, &result); fclose(f); f = 0; nwrun_out_packet_print(stderr, &result); if (rename(result_in_dir, result_dir_dir) < 0) { err("rename: %s -> %s failed: %s", result_in_dir, result_dir_dir, os_ErrorMsg()); goto cleanup; } clean_result_dir = 0; cleanup: if (clean_result_dir) { remove_directory_recursively(result_in_dir, 0); } nwrun_in_packet_free(packet_config); if (f) fclose(f); }
static void handle_packet( const unsigned char *dir_path, const struct nwrun_in_packet *packet, const unsigned char *result_path, struct nwrun_out_packet *result) { unsigned char dst_program_path[EJ_PATH_MAX]; unsigned char src_program_path[EJ_PATH_MAX]; unsigned char dst_input_path[EJ_PATH_MAX]; unsigned char src_input_path[EJ_PATH_MAX]; unsigned char run_output_path[EJ_PATH_MAX]; unsigned char full_output_path[EJ_PATH_MAX]; unsigned char run_error_path[EJ_PATH_MAX]; //unsigned char log_file_path[EJ_PATH_MAX]; unsigned char error_file_path[EJ_PATH_MAX]; unsigned char result_file_path[EJ_PATH_MAX]; ssize_t error_file_size; ssize_t output_file_size; FILE *f = 0; int cur_status = RUN_OK; /* copy the executable */ snprintf(dst_program_path, sizeof(dst_program_path), "%s/%s", global->work_dir, packet->program_name); snprintf(src_program_path, sizeof(src_program_path), "%s/%s", dir_path, packet->program_name); if (fast_copy_file(src_program_path, dst_program_path) < 0) { snprintf(result->comment, sizeof(result->comment), "copy failed: %s -> %s", src_program_path, dst_program_path); goto cleanup; } /* copy the input file */ snprintf(dst_input_path, sizeof(dst_input_path), "%s/%s", global->work_dir, packet->input_file_name); snprintf(src_input_path, sizeof(src_input_path), "%s/%s", dir_path, packet->test_file_name); if (packet->enable_unix2dos > 0) { if (generic_copy_file(CONVERT, "", src_input_path, "", CONVERT, "", dst_input_path, "") < 0) { snprintf(result->comment, sizeof(result->comment), "unix2dos copy failed: %s -> %s", src_input_path, dst_input_path); goto cleanup; } } else { if (fast_copy_file(src_input_path, dst_input_path) < 0) { snprintf(result->comment, sizeof(result->comment), "copy failed: %s -> %s", src_input_path, dst_input_path); goto cleanup; } } if (packet->combined_stdin > 0) { snprintf(dst_input_path, sizeof(dst_input_path), "%s/%s.stdin", global->work_dir, packet->input_file_name); if (packet->enable_unix2dos > 0) { if (generic_copy_file(CONVERT, "", src_input_path, "", CONVERT, "", dst_input_path, "") < 0) { snprintf(result->comment, sizeof(result->comment), "unix2dos copy failed: %s -> %s", src_input_path, dst_input_path); goto cleanup; } } else { if (fast_copy_file(src_input_path, dst_input_path) < 0) { snprintf(result->comment, sizeof(result->comment), "copy failed: %s -> %s", src_input_path, dst_input_path); goto cleanup; } } } snprintf(run_output_path, sizeof(run_output_path), "%s/%s", global->work_dir, packet->output_file_name); if (packet->combined_stdout > 0) { snprintf(run_output_path, sizeof(run_output_path), "%s/%s.stdout", global->work_dir, packet->output_file_name); } snprintf(run_error_path, sizeof(run_error_path), "%s/%s", global->work_dir, packet->error_file_name); cur_status = run_program(packet, dst_program_path, dst_input_path, run_output_path, run_error_path, result); if (packet->combined_stdout > 0) { snprintf(full_output_path, sizeof(full_output_path), "%s/%s", global->work_dir, packet->output_file_name); concatenate_files(full_output_path, run_output_path); snprintf(run_output_path, sizeof(run_output_path), "%s", full_output_path); } info("Testing finished: CPU time = %d, real time = %d", result->cpu_time_millis, result->real_time_millis); /* copy the stderr output */ result->error_file_existed = 0; if (packet->ignore_stderr <= 0) { error_file_size = generic_file_size("", run_error_path, ""); if (error_file_size >= 0) { result->error_file_existed = 1; result->error_file_orig_size = error_file_size; if (error_file_size > packet->max_error_file_size) { result->error_file_truncated = 1; if (generic_truncate(run_error_path, packet->max_error_file_size) < 0) { snprintf(result->comment, sizeof(result->comment), "truncate failed: %s", run_error_path); goto cleanup; } if (!(f = fopen(run_error_path, "a"))) { snprintf(result->comment, sizeof(result->comment), "appending error file failed: %s", run_error_path); goto cleanup; } fprintf(f, "\n\nFile truncated!\n"); fclose(f); f = 0; result->error_file_size = generic_file_size("", run_error_path, ""); } else { result->error_file_truncated = 0; result->error_file_size = error_file_size; } if (packet->error_file_name[0]) { snprintf(error_file_path, sizeof(error_file_path), "%s/%s", result_path, packet->error_file_name); info("Copy: %s -> %s", run_error_path, error_file_path); if (fast_copy_file(run_error_path, error_file_path) < 0) { snprintf(result->comment, sizeof(result->comment), "copy failed: %s -> %s", run_error_path, error_file_path); goto cleanup; } } } } /* copy the program output */ info("Copying program output"); output_file_size = generic_file_size("", run_output_path, ""); if (output_file_size < 0) { if (!result->comment[0]) { snprintf(result->comment, sizeof(result->comment), "no output file"); } if (cur_status == RUN_OK) cur_status = RUN_PRESENTATION_ERR; result->status = cur_status; goto cleanup; } result->output_file_existed = 1; result->output_file_orig_size = output_file_size; if (output_file_size > packet->max_output_file_size) { result->output_file_too_big = 1; snprintf(result->comment, sizeof(result->comment), "output file is too big (%ld)", ((long) output_file_size)); if (cur_status == RUN_OK) cur_status = RUN_PRESENTATION_ERR; result->status = cur_status; goto cleanup; } snprintf(result_file_path, sizeof(result_file_path), "%s/%s", result_path, packet->result_file_name); info("Copy: %s -> %s", run_output_path, result_file_path); if (fast_copy_file(run_output_path, result_file_path) < 0) { snprintf(result->comment, sizeof(result->comment), "copy failed: %s -> %s", run_output_path, result_file_path); result->status = RUN_CHECK_FAILED; goto cleanup; } result->status = cur_status; cleanup: if (f) fclose(f); remove_directory_recursively(global->work_dir, 1); }
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; }