|
|
|
@ -5,14 +5,25 @@
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <poll.h>
|
|
|
|
|
#include <spawn.h>
|
|
|
|
|
#include <sys/timerfd.h>
|
|
|
|
|
#include <system_error>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <wait.h>
|
|
|
|
|
|
|
|
|
|
static constexpr int TIMEOUT_SECONDS = 2;
|
|
|
|
|
|
|
|
|
|
// Define a helper to throw a system error if a syscall fails
|
|
|
|
|
static auto ensure = [](int res) -> void {
|
|
|
|
|
if (res == -1) {
|
|
|
|
|
throw std::system_error{errno, std::generic_category()};
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
namespace sk {
|
|
|
|
|
runner::runner(runner_backend backend) : backend{backend} {}
|
|
|
|
|
|
|
|
|
|
run_result runner::run_blocking(const program &program) {
|
|
|
|
|
// Open file descriptors ahead of time
|
|
|
|
|
int in_pipe[2];
|
|
|
|
|
int out_pipe[2];
|
|
|
|
|
int err_pipe[2];
|
|
|
|
@ -20,6 +31,17 @@ run_result runner::run_blocking(const program &program) {
|
|
|
|
|
throw std::system_error{errno, std::generic_category()};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a timer that will be polled when the program runs too long
|
|
|
|
|
int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
|
|
|
|
|
if (timerfd == -1) {
|
|
|
|
|
throw std::system_error{errno, std::generic_category()};
|
|
|
|
|
}
|
|
|
|
|
itimerspec timer{};
|
|
|
|
|
timer.it_value.tv_sec = TIMEOUT_SECONDS;
|
|
|
|
|
if (timerfd_settime(timerfd, 0, &timer, nullptr) == -1) {
|
|
|
|
|
throw std::system_error{errno, std::generic_category()};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Avoid blocking on stdin, and being interrupted if the pipe is closed prematurely
|
|
|
|
|
int flags = fcntl(in_pipe[1], F_GETFL, 0);
|
|
|
|
|
fcntl(in_pipe[1], F_SETFL, flags | O_NONBLOCK);
|
|
|
|
@ -29,19 +51,21 @@ run_result runner::run_blocking(const program &program) {
|
|
|
|
|
posix_spawn_file_actions_addclose(&actions, in_pipe[1]);
|
|
|
|
|
posix_spawn_file_actions_addclose(&actions, out_pipe[0]);
|
|
|
|
|
posix_spawn_file_actions_addclose(&actions, err_pipe[0]);
|
|
|
|
|
posix_spawn_file_actions_addclose(&actions, timerfd);
|
|
|
|
|
posix_spawn_file_actions_adddup2(&actions, in_pipe[0], STDIN_FILENO);
|
|
|
|
|
posix_spawn_file_actions_adddup2(&actions, out_pipe[1], STDOUT_FILENO);
|
|
|
|
|
posix_spawn_file_actions_adddup2(&actions, err_pipe[1], STDERR_FILENO);
|
|
|
|
|
posix_spawn_file_actions_addclose(&actions, in_pipe[0]);
|
|
|
|
|
posix_spawn_file_actions_addclose(&actions, out_pipe[1]);
|
|
|
|
|
posix_spawn_file_actions_addclose(&actions, err_pipe[1]);
|
|
|
|
|
const char *const docker_args[] = {"docker", "run", "--rm", "-i", "--pull=never", "--cap-drop=ALL", "--network=none", "--memory=64m", "--memory-swap=64m", "--pids-limit=128", program.image.c_str(), nullptr};
|
|
|
|
|
const char *const bwrap_args[] = {"bwrap", "--ro-bind", "/usr", "/usr", "--dir", "/tmp", "--dir", "/var", "--proc", "/proc", "--dev", "/dev", "--symlink", "usr/lib", "/lib", "--symlink", "usr/lib64", "/lib64", "--symlink", "usr/bin", "/bin", "--symlink", "usr/sbin", "/sbin", "--unshare-all", "/bin/sh", nullptr};
|
|
|
|
|
const char *const docker_args[] = {"docker", "run", "--rm", "-i", "--name", program.name.c_str(), "--pull=never", "--cap-drop=ALL", "--network=none", "--memory=64m", "--memory-swap=64m", "--pids-limit=128", program.image.c_str(), nullptr};
|
|
|
|
|
const char *const bwrap_args[] = {"bwrap", "--ro-bind", "/usr", "/usr", "--dir", "/tmp", "--dir", "/var", "--proc", "/proc", "--dev", "/dev", "--symlink", "usr/lib", "/lib", "--symlink", "usr/lib64", "/lib64", "--symlink", "usr/bin", "/bin", "--symlink", "usr/sbin", "/sbin", "--unshare-all", "--die-with-parent", "/bin/sh", nullptr};
|
|
|
|
|
const char *const *args = docker_args;
|
|
|
|
|
if (backend == runner_backend::BubbleWrap) {
|
|
|
|
|
args = bwrap_args;
|
|
|
|
|
}
|
|
|
|
|
pid_t pid;
|
|
|
|
|
bool killed = false;
|
|
|
|
|
int exit_code;
|
|
|
|
|
if (posix_spawnp(&pid, args[0], &actions, nullptr, const_cast<char *const *>(args), nullptr) != 0) {
|
|
|
|
|
throw std::system_error{errno, std::generic_category()};
|
|
|
|
@ -58,7 +82,7 @@ run_result runner::run_blocking(const program &program) {
|
|
|
|
|
std::array<char, 1024> buffer{};
|
|
|
|
|
std::string out;
|
|
|
|
|
std::string err;
|
|
|
|
|
std::array<pollfd, 3> plist = {pollfd{in_pipe[1], POLLOUT | POLLHUP, 0}, pollfd{out_pipe[0], POLLIN, 0}, pollfd{err_pipe[0], POLLIN, 0}};
|
|
|
|
|
std::array<pollfd, 4> plist = {pollfd{in_pipe[1], POLLOUT | POLLHUP, 0}, pollfd{out_pipe[0], POLLIN, 0}, pollfd{err_pipe[0], POLLIN, 0}, pollfd{timerfd, POLLIN, 0}};
|
|
|
|
|
pollfd *pfds = plist.data();
|
|
|
|
|
nfds_t nfds = plist.size();
|
|
|
|
|
|
|
|
|
@ -94,8 +118,8 @@ run_result runner::run_blocking(const program &program) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Poll stdout and stderr
|
|
|
|
|
for (nfds_t i = nfds - 2; i < nfds; ++i) {
|
|
|
|
|
// Poll stdout, stderr and the timer
|
|
|
|
|
for (nfds_t i = nfds - 3; i < nfds; ++i) {
|
|
|
|
|
if (pfds[i].revents & POLLIN) {
|
|
|
|
|
ssize_t bytes_read = read(pfds[i].fd, buffer.data(), buffer.size());
|
|
|
|
|
if (bytes_read == -1) {
|
|
|
|
@ -103,6 +127,15 @@ run_result runner::run_blocking(const program &program) {
|
|
|
|
|
}
|
|
|
|
|
if (pfds[i].fd == out_pipe[0]) {
|
|
|
|
|
out.append(buffer.data(), bytes_read);
|
|
|
|
|
} else if (pfds[i].fd == timerfd) {
|
|
|
|
|
if (backend == runner_backend::Docker) {
|
|
|
|
|
const char *const kill_args[] = {"docker", "kill", program.name.c_str(), nullptr};
|
|
|
|
|
pid_t kill_pid;
|
|
|
|
|
ensure(posix_spawnp(&kill_pid, kill_args[0], nullptr, nullptr, const_cast<char *const *>(kill_args), nullptr));
|
|
|
|
|
} else {
|
|
|
|
|
ensure(kill(pid, SIGINT));
|
|
|
|
|
}
|
|
|
|
|
killed = true;
|
|
|
|
|
} else {
|
|
|
|
|
err.append(buffer.data(), bytes_read);
|
|
|
|
|
}
|
|
|
|
@ -119,8 +152,9 @@ run_result runner::run_blocking(const program &program) {
|
|
|
|
|
waitpid(pid, &exit_code, 0);
|
|
|
|
|
close(out_pipe[0]);
|
|
|
|
|
close(err_pipe[0]);
|
|
|
|
|
close(timerfd);
|
|
|
|
|
|
|
|
|
|
posix_spawn_file_actions_destroy(&actions);
|
|
|
|
|
return run_result{out, err};
|
|
|
|
|
return run_result{out, err, killed ? 124 : exit_code};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|