summaryrefslogtreecommitdiff
path: root/competition/process.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'competition/process.cpp')
-rw-r--r--competition/process.cpp136
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);
+}