#include #include #include #include #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 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); }