run_interaction.cpp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cstdlib>
  4. #include <exception>
  5. #include <system_error>
  6. #include <thread>
  7. #include <unistd.h>
  8. #include <asm/unistd.h>
  9. #include <sys/ptrace.h>
  10. #include <sys/wait.h>
  11. #include <sys/resource.h>
  12. #include <sys/user.h>
  13. #include <fcntl.h>
  14. #include <cstring>
  15. #include <string>
  16. #include <vector>
  17. #include <set>
  18. #include <argp.h>
  19. #include "uoj_env.h"
  20. using namespace std;
  21. struct RunResult {
  22. int result;
  23. int ust;
  24. int usm;
  25. int exit_code;
  26. RunResult(int _result, int _ust = -1, int _usm = -1, int _exit_code = -1)
  27. : result(_result), ust(_ust), usm(_usm), exit_code(_exit_code) {
  28. if (result != RS_AC) {
  29. ust = -1, usm = -1;
  30. }
  31. }
  32. };
  33. struct PipeConfig {
  34. int from, to;
  35. int from_fd, to_fd;
  36. string saving_file_name; // empty for none
  37. PipeConfig() {
  38. }
  39. PipeConfig(string str) {
  40. if (sscanf(str.c_str(), "%d:%d-%d:%d", &from, &from_fd, &to, &to_fd) != 4) {
  41. throw invalid_argument("bad init str for PipeConfig");
  42. }
  43. from -= 1;
  44. to -= 1;
  45. }
  46. };
  47. struct RunInteractionConfig {
  48. vector<string> cmds;
  49. vector<PipeConfig> pipes;
  50. };
  51. struct RunCmdData {
  52. string cmd;
  53. pid_t pid;
  54. vector<int> ipipes, opipes;
  55. };
  56. struct PipeData {
  57. PipeConfig config;
  58. int ipipefd[2], opipefd[2];
  59. thread io_thread;
  60. exception_ptr eptr;
  61. };
  62. class RunInteraction {
  63. private:
  64. vector<RunCmdData> cmds;
  65. vector<PipeData> pipes;
  66. void prepare_fd() { // me
  67. for (int i = 0; i < (int)pipes.size(); i++) {
  68. close(pipes[i].ipipefd[1]);
  69. close(pipes[i].opipefd[0]);
  70. }
  71. }
  72. void prepare_fd_for_cmd(int id) {
  73. freopen("/dev/null", "r", stdin);
  74. freopen("/dev/null", "w", stdout);
  75. freopen("/dev/null", "w", stderr);
  76. for (int i = 0; i < (int)pipes.size(); i++) {
  77. if (pipes[i].config.from == id) {
  78. dup2(pipes[i].ipipefd[1], 128 + pipes[i].ipipefd[1]);
  79. }
  80. if (pipes[i].config.to == id) {
  81. dup2(pipes[i].opipefd[0], 128 + pipes[i].opipefd[0]);
  82. }
  83. close(pipes[i].ipipefd[0]);
  84. close(pipes[i].ipipefd[1]);
  85. close(pipes[i].opipefd[0]);
  86. close(pipes[i].opipefd[1]);
  87. }
  88. for (int i = 0; i < (int)pipes.size(); i++) {
  89. if (pipes[i].config.from == id) {
  90. dup2(128 + pipes[i].ipipefd[1], pipes[i].config.from_fd);
  91. close(128 + pipes[i].ipipefd[1]);
  92. }
  93. if (pipes[i].config.to == id) {
  94. dup2(128 + pipes[i].opipefd[0], pipes[i].config.to_fd);
  95. close(128 + pipes[i].opipefd[0]);
  96. }
  97. }
  98. }
  99. void wait_pipe_io(int pipe_id) {
  100. FILE *sf = NULL;
  101. if (!pipes[pipe_id].config.saving_file_name.empty())
  102. sf = fopen(pipes[pipe_id].config.saving_file_name.c_str(), "w");
  103. int ifd = pipes[pipe_id].ipipefd[0];
  104. int ofd = pipes[pipe_id].opipefd[1];
  105. FILE *inf = fdopen(ifd, "r");
  106. FILE *ouf = fdopen(ofd, "w");
  107. try {
  108. pipes[pipe_id].eptr = nullptr;
  109. const int L = 4096;
  110. char buf[L];
  111. while (true) {
  112. int c = fgetc(inf);
  113. if (c == EOF) {
  114. if (errno) {
  115. throw system_error(errno, system_category());
  116. }
  117. break;
  118. }
  119. if (fputc(c, ouf) == EOF) {
  120. throw system_error(errno, system_category());
  121. }
  122. fflush(ouf);
  123. if (fputc(c, sf) == EOF) {
  124. throw system_error(errno, system_category());
  125. }
  126. }
  127. } catch (exception &e) {
  128. cerr << e.what() << endl;
  129. pipes[pipe_id].eptr = current_exception();
  130. }
  131. fclose(sf);
  132. fclose(inf);
  133. fclose(ouf);
  134. }
  135. public:
  136. RunInteraction(const RunInteractionConfig &config) {
  137. cmds.resize(config.cmds.size());
  138. for (int i = 0; i < (int)config.cmds.size(); i++) {
  139. cmds[i].cmd = config.cmds[i];
  140. }
  141. pipes.resize(config.pipes.size());
  142. for (int i = 0; i < (int)config.pipes.size(); i++) {
  143. pipes[i].config = config.pipes[i];
  144. cmds[pipes[i].config.from].opipes.push_back(i);
  145. cmds[pipes[i].config.to].ipipes.push_back(i);
  146. }
  147. for (int i = 0; i < (int)pipes.size(); i++) {
  148. if (pipe(pipes[i].ipipefd) == -1 || pipe(pipes[i].opipefd) == -1) {
  149. throw system_error(errno, system_category());
  150. }
  151. }
  152. for (int i = 0; i < (int)cmds.size(); i++) {
  153. cmds[i].pid = fork();
  154. if (cmds[i].pid == 0) {
  155. prepare_fd_for_cmd(i);
  156. system(cmds[i].cmd.c_str());
  157. exit(0);
  158. } else if (cmds[i].pid == -1) {
  159. throw system_error(errno, system_category());
  160. }
  161. }
  162. prepare_fd();
  163. for (int i = 0; i < (int)pipes.size(); i++) {
  164. pipes[i].io_thread = thread(&RunInteraction::wait_pipe_io, this, i);
  165. }
  166. }
  167. void join() {
  168. int status;
  169. while (wait(&status) > 0);
  170. for (int i = 0; i < (int)pipes.size(); i++) {
  171. pipes[i].io_thread.join();
  172. }
  173. }
  174. };
  175. error_t run_interaction_argp_parse_opt (int key, char *arg, struct argp_state *state) {
  176. RunInteractionConfig *config = (RunInteractionConfig*)state->input;
  177. try {
  178. switch (key) {
  179. case 'p':
  180. config->pipes.push_back(PipeConfig(arg));
  181. break;
  182. case 's':
  183. if (config->pipes.empty()) {
  184. argp_usage(state);
  185. }
  186. config->pipes.back().saving_file_name = arg;
  187. break;
  188. case ARGP_KEY_ARG:
  189. config->cmds.push_back(arg);
  190. break;
  191. case ARGP_KEY_END:
  192. if (state->arg_num == 0) {
  193. argp_usage(state);
  194. }
  195. break;
  196. default:
  197. return ARGP_ERR_UNKNOWN;
  198. }
  199. } catch (exception &e) {
  200. argp_usage(state);
  201. }
  202. return 0;
  203. }
  204. RunInteractionConfig parse_args(int argc, char **argv) {
  205. argp_option run_interaction_argp_options[] = {
  206. {"add-pipe" , 'p', "PIPE" , 0, "Add a pipe <from>:<fd>-<to>:<fd> (fd < 128)" , 1},
  207. {"save-pipe" , 's', "FILE" , 0, "Set last pipe saving file" , 2},
  208. {0}
  209. };
  210. char run_interaction_argp_args_doc[] = "cmd1 cmd2 ...";
  211. char run_interaction_argp_doc[] = "run_interaction: a tool to run multiple commands with interaction";
  212. argp run_interaction_argp = {
  213. run_interaction_argp_options,
  214. run_interaction_argp_parse_opt,
  215. run_interaction_argp_args_doc,
  216. run_interaction_argp_doc
  217. };
  218. RunInteractionConfig config;
  219. argp_parse(&run_interaction_argp, argc, argv, ARGP_NO_ARGS | ARGP_IN_ORDER, 0, &config);
  220. return config;
  221. }
  222. int main(int argc, char **argv) {
  223. signal(SIGPIPE, SIG_IGN);
  224. RunInteraction ri(parse_args(argc, argv));
  225. ri.join();
  226. return 0;
  227. }