use tracing::{debug, instrument};
use crate::config::{Config, Language};
use crate::isolate::{
IsolateAction, IsolateBox, IsolateCommand, resolve_command, run_batch, validate_mounts,
};
use crate::runner::ExecuteError;
use crate::types::{ExecutionResult, ResourceLimits};
#[instrument(skip(sandbox, config, input))]
pub async fn execute(
sandbox: &IsolateBox,
config: &Config,
language: &Language,
input: Option<&[u8]>,
limits: Option<&ResourceLimits>,
) -> Result<ExecutionResult, ExecuteError> {
let mut effective_limits = config.default_limits.clone();
if let Some(ref lang_limits) = language.run.limits {
effective_limits = effective_limits.with_overrides(lang_limits);
}
if let Some(user_limits) = limits {
effective_limits = effective_limits.with_overrides(user_limits);
}
let mut run_cmd = if let Some(ref compile_config) = language.compile {
let binary = &compile_config.output_name;
if !sandbox.file_exists(binary).await? {
return Err(ExecuteError::NotStarted(format!(
"binary '{}' not found in sandbox - was compilation run?",
binary
)));
}
Language::expand_command(&language.run.command, &compile_config.source_name, binary)
} else {
let source_name = language.source_name();
if !sandbox.file_exists(&source_name).await? {
return Err(ExecuteError::NotStarted(format!(
"source '{}' not found in sandbox - write source first",
source_name
)));
}
Language::expand_command(&language.run.command, &source_name, &source_name)
};
resolve_command(&mut run_cmd).map_err(ExecuteError::Isolate)?;
debug!(?run_cmd, "executing program");
validate_mounts(&language.run.mounts).map_err(ExecuteError::Isolate)?;
let memory_limit = effective_limits.memory_limit;
let mut command = IsolateCommand::new(config.isolate_binary(), sandbox.id())
.action(IsolateAction::Run)
.cgroup(config.cgroup)
.limits(effective_limits)
.working_dir("/box")
.env("PATH", &language.run.path)
.mounts(config.sandbox_mounts.iter().cloned())
.mounts(language.run.mounts.iter().cloned())
.command(run_cmd);
for (key, value) in &language.run.env {
command = command.env(key, value);
}
let mut result = run_batch(sandbox, command, input)
.await
.map_err(ExecuteError::Isolate)?;
if let Some(mem_limit) = memory_limit {
result.detect_memory_limit(mem_limit);
}
debug!(
status = ?result.status,
time = result.time,
memory = result.memory,
exit_code = ?result.exit_code,
"execution complete"
);
Ok(result)
}
#[instrument(skip(sandbox, config, source, input))]
pub async fn execute_interpreted(
sandbox: &IsolateBox,
config: &Config,
language: &Language,
source: &[u8],
input: Option<&[u8]>,
limits: Option<&ResourceLimits>,
) -> Result<ExecutionResult, ExecuteError> {
let source_name = language.source_name();
sandbox
.write_file(&source_name, source)
.await
.map_err(ExecuteError::Isolate)?;
debug!(source_name, "wrote source file for interpreted execution");
execute(sandbox, config, language, input, limits).await
}