S2OJ/judger/uoj_judger/run/run_interaction.cpp
Masco Skray 96d4a3ecf7 style(judger,web): move code out from subfolder "1"
Due to historical reasons, the code is in subfolder "1".
With SVN removal, we place the code back and remove the annoying "1" folder.
2019-06-14 23:34:41 +08:00

264 lines
5.9 KiB
C++

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <exception>
#include <system_error>
#include <thread>
#include <unistd.h>
#include <asm/unistd.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <sys/user.h>
#include <fcntl.h>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <argp.h>
#include "uoj_env.h"
using namespace std;
struct RunResult {
int result;
int ust;
int usm;
int exit_code;
RunResult(int _result, int _ust = -1, int _usm = -1, int _exit_code = -1)
: result(_result), ust(_ust), usm(_usm), exit_code(_exit_code) {
if (result != RS_AC) {
ust = -1, usm = -1;
}
}
};
struct PipeConfig {
int from, to;
int from_fd, to_fd;
string saving_file_name; // empty for none
PipeConfig() {
}
PipeConfig(string str) {
if (sscanf(str.c_str(), "%d:%d-%d:%d", &from, &from_fd, &to, &to_fd) != 4) {
throw invalid_argument("bad init str for PipeConfig");
}
from -= 1;
to -= 1;
}
};
struct RunInteractionConfig {
vector<string> cmds;
vector<PipeConfig> pipes;
};
struct RunCmdData {
string cmd;
pid_t pid;
vector<int> ipipes, opipes;
};
struct PipeData {
PipeConfig config;
int ipipefd[2], opipefd[2];
thread io_thread;
exception_ptr eptr;
};
class RunInteraction {
private:
vector<RunCmdData> cmds;
vector<PipeData> pipes;
void prepare_fd() { // me
for (int i = 0; i < (int)pipes.size(); i++) {
close(pipes[i].ipipefd[1]);
close(pipes[i].opipefd[0]);
}
}
void prepare_fd_for_cmd(int id) {
freopen("/dev/null", "r", stdin);
freopen("/dev/null", "w", stdout);
freopen("/dev/null", "w", stderr);
for (int i = 0; i < (int)pipes.size(); i++) {
if (pipes[i].config.from == id) {
dup2(pipes[i].ipipefd[1], 128 + pipes[i].ipipefd[1]);
}
if (pipes[i].config.to == id) {
dup2(pipes[i].opipefd[0], 128 + pipes[i].opipefd[0]);
}
close(pipes[i].ipipefd[0]);
close(pipes[i].ipipefd[1]);
close(pipes[i].opipefd[0]);
close(pipes[i].opipefd[1]);
}
for (int i = 0; i < (int)pipes.size(); i++) {
if (pipes[i].config.from == id) {
dup2(128 + pipes[i].ipipefd[1], pipes[i].config.from_fd);
close(128 + pipes[i].ipipefd[1]);
}
if (pipes[i].config.to == id) {
dup2(128 + pipes[i].opipefd[0], pipes[i].config.to_fd);
close(128 + pipes[i].opipefd[0]);
}
}
}
void wait_pipe_io(int pipe_id) {
FILE *sf = NULL;
if (!pipes[pipe_id].config.saving_file_name.empty())
sf = fopen(pipes[pipe_id].config.saving_file_name.c_str(), "w");
int ifd = pipes[pipe_id].ipipefd[0];
int ofd = pipes[pipe_id].opipefd[1];
FILE *inf = fdopen(ifd, "r");
FILE *ouf = fdopen(ofd, "w");
try {
pipes[pipe_id].eptr = nullptr;
const int L = 4096;
char buf[L];
while (true) {
int c = fgetc(inf);
if (c == EOF) {
if (errno) {
throw system_error(errno, system_category());
}
break;
}
if (fputc(c, ouf) == EOF) {
throw system_error(errno, system_category());
}
fflush(ouf);
if (fputc(c, sf) == EOF) {
throw system_error(errno, system_category());
}
}
} catch (exception &e) {
cerr << e.what() << endl;
pipes[pipe_id].eptr = current_exception();
}
fclose(sf);
fclose(inf);
fclose(ouf);
}
public:
RunInteraction(const RunInteractionConfig &config) {
cmds.resize(config.cmds.size());
for (int i = 0; i < (int)config.cmds.size(); i++) {
cmds[i].cmd = config.cmds[i];
}
pipes.resize(config.pipes.size());
for (int i = 0; i < (int)config.pipes.size(); i++) {
pipes[i].config = config.pipes[i];
cmds[pipes[i].config.from].opipes.push_back(i);
cmds[pipes[i].config.to].ipipes.push_back(i);
}
for (int i = 0; i < (int)pipes.size(); i++) {
if (pipe(pipes[i].ipipefd) == -1 || pipe(pipes[i].opipefd) == -1) {
throw system_error(errno, system_category());
}
}
for (int i = 0; i < (int)cmds.size(); i++) {
cmds[i].pid = fork();
if (cmds[i].pid == 0) {
prepare_fd_for_cmd(i);
system(cmds[i].cmd.c_str());
exit(0);
} else if (cmds[i].pid == -1) {
throw system_error(errno, system_category());
}
}
prepare_fd();
for (int i = 0; i < (int)pipes.size(); i++) {
pipes[i].io_thread = thread(&RunInteraction::wait_pipe_io, this, i);
}
}
void join() {
int status;
while (wait(&status) > 0);
for (int i = 0; i < (int)pipes.size(); i++) {
pipes[i].io_thread.join();
}
}
};
error_t run_interaction_argp_parse_opt (int key, char *arg, struct argp_state *state) {
RunInteractionConfig *config = (RunInteractionConfig*)state->input;
try {
switch (key) {
case 'p':
config->pipes.push_back(PipeConfig(arg));
break;
case 's':
if (config->pipes.empty()) {
argp_usage(state);
}
config->pipes.back().saving_file_name = arg;
break;
case ARGP_KEY_ARG:
config->cmds.push_back(arg);
break;
case ARGP_KEY_END:
if (state->arg_num == 0) {
argp_usage(state);
}
break;
default:
return ARGP_ERR_UNKNOWN;
}
} catch (exception &e) {
argp_usage(state);
}
return 0;
}
RunInteractionConfig parse_args(int argc, char **argv) {
argp_option run_interaction_argp_options[] = {
{"add-pipe" , 'p', "PIPE" , 0, "Add a pipe <from>:<fd>-<to>:<fd> (fd < 128)" , 1},
{"save-pipe" , 's', "FILE" , 0, "Set last pipe saving file" , 2},
{0}
};
char run_interaction_argp_args_doc[] = "cmd1 cmd2 ...";
char run_interaction_argp_doc[] = "run_interaction: a tool to run multiple commands with interaction";
argp run_interaction_argp = {
run_interaction_argp_options,
run_interaction_argp_parse_opt,
run_interaction_argp_args_doc,
run_interaction_argp_doc
};
RunInteractionConfig config;
argp_parse(&run_interaction_argp, argc, argv, ARGP_NO_ARGS | ARGP_IN_ORDER, 0, &config);
return config;
}
int main(int argc, char **argv) {
signal(SIGPIPE, SIG_IGN);
RunInteraction ri(parse_args(argc, argv));
ri.join();
return 0;
}