|
|
|
@ -18,8 +18,102 @@ static auto ensure = [](int res) -> void {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <class> inline constexpr bool always_false_v = false;
|
|
|
|
|
|
|
|
|
|
namespace sk {
|
|
|
|
|
runner::runner(runner_backend backend, const runner_config &config) : backend{backend}, timeout{static_cast<int>(config.timeout)} {}
|
|
|
|
|
execution_strategy::execution_strategy(const std::vector<std::string> &patterns) {
|
|
|
|
|
for (const auto &pattern : patterns) {
|
|
|
|
|
if (pattern == "{}") {
|
|
|
|
|
this->patterns.emplace_back(nullptr);
|
|
|
|
|
} else {
|
|
|
|
|
this->patterns.emplace_back(pattern);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
execution_strategy::execution_strategy(std::vector<pattern> patterns) : patterns(std::move(patterns)) {}
|
|
|
|
|
|
|
|
|
|
std::unique_ptr<execution_strategy> execution_strategy::create(const runner_strategy_config &config) {
|
|
|
|
|
return std::visit(
|
|
|
|
|
[](auto &&strategy) -> std::unique_ptr<execution_strategy> {
|
|
|
|
|
using T = std::decay_t<decltype(strategy)>;
|
|
|
|
|
if constexpr (std::is_same_v<T, sk::bubblewrap_config>) {
|
|
|
|
|
return std::make_unique<bubblewrap_execution_strategy>(strategy.args);
|
|
|
|
|
} else if constexpr (std::is_same_v<T, sk::docker_config>) {
|
|
|
|
|
return std::make_unique<docker_execution_strategy>(strategy.args, strategy.image);
|
|
|
|
|
} else {
|
|
|
|
|
static_assert(always_false_v<T>, "non-exhaustive visitor!");
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
config);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void execution_strategy::concat_patterns(std::vector<const char *> &args, const program &program) const {
|
|
|
|
|
for (const auto &pattern : patterns) {
|
|
|
|
|
std::visit(
|
|
|
|
|
[&args, &program](const auto &arg) {
|
|
|
|
|
if constexpr (std::is_same_v<std::string, std::decay_t<decltype(arg)>>) {
|
|
|
|
|
args.push_back(arg.c_str());
|
|
|
|
|
} else {
|
|
|
|
|
args.push_back(program.code.c_str());
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
pattern);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bubblewrap_execution_strategy::bubblewrap_execution_strategy(const std::vector<std::string> &patterns) : execution_strategy{patterns} {}
|
|
|
|
|
|
|
|
|
|
bubblewrap_execution_strategy::bubblewrap_execution_strategy(std::vector<pattern> patterns) : execution_strategy{std::move(patterns)} {}
|
|
|
|
|
|
|
|
|
|
std::vector<const char *> bubblewrap_execution_strategy::start(const program &program) {
|
|
|
|
|
std::vector<const char *> 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"};
|
|
|
|
|
concat_patterns(args, program);
|
|
|
|
|
return args;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void bubblewrap_execution_strategy::stop(const active_job &job) { ensure(kill(job.pid, SIGINT)); }
|
|
|
|
|
|
|
|
|
|
docker_execution_strategy::docker_execution_strategy(const std::vector<std::string> &patterns, std::string image) : execution_strategy{patterns}, image{std::move(image)} {}
|
|
|
|
|
|
|
|
|
|
docker_execution_strategy::docker_execution_strategy(std::vector<pattern> patterns, std::string image) : execution_strategy{std::move(patterns)}, image{std::move(image)} {}
|
|
|
|
|
|
|
|
|
|
std::vector<const char *> docker_execution_strategy::start(const program &program) {
|
|
|
|
|
std::vector<const char *> 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", image.c_str()};
|
|
|
|
|
concat_patterns(args, program);
|
|
|
|
|
return args;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void docker_execution_strategy::stop(const active_job &job) {
|
|
|
|
|
const char *const kill_args[] = {"docker", "kill", job.job_id.c_str(), nullptr};
|
|
|
|
|
pid_t kill_pid;
|
|
|
|
|
ensure(posix_spawnp(&kill_pid, kill_args[0], nullptr, nullptr, const_cast<char *const *>(kill_args), nullptr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
runner::runner(const runner_strategy_config &config, unsigned int timeout) : backend{execution_strategy::create(config)}, timeout{static_cast<int>(timeout)} {}
|
|
|
|
|
|
|
|
|
|
runner::runner(const runner &other) : backend{&*other.backend}, timeout{other.timeout} {}
|
|
|
|
|
|
|
|
|
|
runner::runner(runner &&other) noexcept : backend{std::move(other.backend)}, timeout{other.timeout} {}
|
|
|
|
|
|
|
|
|
|
runner &runner::operator=(const runner &other) {
|
|
|
|
|
if (this == &other) {
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
std::unique_ptr<execution_strategy> new_backend(&*other.backend);
|
|
|
|
|
backend = std::move(new_backend);
|
|
|
|
|
timeout = other.timeout;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
runner &runner::operator=(runner &&other) noexcept {
|
|
|
|
|
if (this == &other) {
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
backend = std::move(other.backend);
|
|
|
|
|
timeout = other.timeout;
|
|
|
|
|
return *this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
run_result runner::run_blocking(const program &program) {
|
|
|
|
|
// Open file descriptors ahead of time
|
|
|
|
@ -51,16 +145,12 @@ run_result runner::run_blocking(const program &program) {
|
|
|
|
|
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);
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
std::vector<const char *> args = backend->start(program);
|
|
|
|
|
args.push_back(nullptr);
|
|
|
|
|
pid_t pid;
|
|
|
|
|
bool killed = false;
|
|
|
|
|
int exit_code;
|
|
|
|
|
if (posix_spawnp(&pid, args[0], &actions, nullptr, const_cast<char *const *>(args), nullptr) != 0) {
|
|
|
|
|
if (posix_spawnp(&pid, args[0], &actions, nullptr, const_cast<char *const *>(args.data()), nullptr) != 0) {
|
|
|
|
|
throw std::system_error{errno, std::generic_category()};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -184,13 +274,29 @@ void runner::exit_active_jobs() {
|
|
|
|
|
active_jobs.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void runner::exit(const active_job &job) {
|
|
|
|
|
if (backend == runner_backend::Docker) {
|
|
|
|
|
const char *const kill_args[] = {"docker", "kill", job.job_id.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(job.pid, SIGINT));
|
|
|
|
|
void runner::exit(const active_job &job) { backend->stop(job); }
|
|
|
|
|
|
|
|
|
|
runner_list::runner_list(sk::runner_backend preferred_backend, const runner_config &config) {
|
|
|
|
|
for (const auto &strategy : config.strategies) {
|
|
|
|
|
runners.emplace(std::string{strategy.first}, runner{strategy.second, config.timeout});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
runner *runner_list::find_runner_for(const program &program) {
|
|
|
|
|
auto it = runners.find(program.image);
|
|
|
|
|
if (it != runners.end()) {
|
|
|
|
|
return &it->second;
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool runner_list::kill_active(const std::string &jobId) {
|
|
|
|
|
return std::any_of(runners.begin(), runners.end(), [&jobId](auto &runner) { return runner.second.kill_active(jobId); });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void runner_list::exit_active_jobs() {
|
|
|
|
|
for (auto &[name, runner] : runners) {
|
|
|
|
|
runner.exit_active_jobs();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|