diff options
Diffstat (limited to 'competition/process.cpp')
-rw-r--r-- | competition/process.cpp | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/competition/process.cpp b/competition/process.cpp new file mode 100644 index 0000000..a728401 --- /dev/null +++ b/competition/process.cpp @@ -0,0 +1,136 @@ +#include <iostream> +#include <cstdlib> +#include <fcntl.h> +#include <sys/wait.h> +#include "process.h" +#include "error.h" + + +Process::Process(const string_view execname) + : execname(execname) {} + +void Process::redirectStderr(const string_view fname) { + stderrRedirect = fname; +} + +void Process::run() { + int stderrfd = -1; + if (stderrRedirect) { + stderrfd = open(stderrRedirect->data(), O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (stderrfd < 0) { + perror("open"); + cout << endl << "ERROR: Cannot open player log file '" << *stderrRedirect << "'" << endl; + throw StopCompetitionError(); + } + } + + int pipefds[2]; + if (pipe(pipefds) < 0) { + perror("pipe"); + exit(1); + } + infd = pipefds[1]; + int child_in = pipefds[0]; + + if (pipe(pipefds) < 0) { + perror("pipe"); + exit(1); + } + outfd = pipefds[0]; + int child_out = pipefds[1]; + + pid = fork(); + if (pid < 0) { + perror("fork"); + exit(1); + } + + if (pid == 0) { + if (stderrRedirect) dup2(stderrfd, STDERR_FILENO); + dup2(child_in, STDIN_FILENO); + dup2(child_out, STDOUT_FILENO); + close(infd); + close(outfd); + + execlp(execname.data(), execname.data(), NULL); + cerr << endl << "ERROR: Error executing player file '" << execname << "'" << endl; + exit(255); + } + + if (stderrfd >= 0) close(stderrfd); + close(child_in); + close(child_out); +} + +void Process::wait() { + while (true) { + int status; + if (waitpid(pid, &status, 0) < 0) { + if (errno == EINTR) continue; + perror("waitpid"); + break; + } + if (WIFEXITED(status)) break; + } +} + +void Process::stop() { + if (pid != -1) kill(pid, SIGSTOP); +} + +void Process::unStop() { + if (pid != -1) kill(pid, SIGCONT); +} + +bool Process::writeLine(const string_view line) { + string str; + str.reserve(line.size() + 1); + str += line; + str += '\n'; + + size_t cursor = 0; + while (cursor < str.size()) { + ssize_t nw = write(infd, str.data() + cursor, str.size() - cursor); + if (nw < 0) { + if (errno == EINTR) continue; + perror("write"); + return false; + } + cursor += nw; + } + + return true; +} + +optional<string> Process::readLine() { + size_t idx = readBuf.find('\n'); + if (idx != string::npos) { + string res = readBuf.substr(0, idx); + readBuf = readBuf.substr(idx + 1); + return res; + } + + while (true) { + string s(1024, '\0'); + ssize_t nr = read(outfd, &s[0], s.size()); + if (nr < 0) { + if (errno == EINTR) continue; + perror("read"); + return nullopt; + } + s.resize(nr); + + idx = s.find('\n'); + if (idx != string::npos) { + string res = readBuf + s.substr(0, idx); + readBuf = s.substr(idx + 1); + return res; + } + + readBuf += s; + } +} + +void Process::terminate() { + if (pid != -1) kill(pid, SIGTERM); +} |