use crate::{Config, ErrorCode, LogLevel, Logger, seccomp};
use nix::libc;
use nix::sys::resource::{Resource, setrlimit};
use nix::unistd::{Gid, Uid, execve, setgid, setuid};
use std::ffi::CString;
use std::fs::File;
use std::os::fd::AsRawFd;
pub fn child_process(config: &Config, mut logger: Logger) -> Result<(), ErrorCode> {
if config.max_stack != -1 {
setrlimit(
Resource::RLIMIT_STACK,
config.max_stack as u64,
config.max_stack as u64,
)
.map_err(|_| ErrorCode::SetrlimitFailed)?;
}
if !config.memory_limit_check_only && config.max_memory != -1 {
setrlimit(
Resource::RLIMIT_AS,
(config.max_memory * 2) as u64,
(config.max_memory * 2) as u64,
)
.map_err(|_| ErrorCode::SetrlimitFailed)?;
}
if config.max_cpu_time != -1 {
setrlimit(
Resource::RLIMIT_CPU,
(config.max_cpu_time / 1000) as u64,
(config.max_cpu_time / 1000) as u64,
)
.map_err(|_| ErrorCode::SetrlimitFailed)?;
}
if config.max_process_number != -1 {
setrlimit(
Resource::RLIMIT_NPROC,
config.max_process_number as u64,
config.max_process_number as u64,
)
.map_err(|_| ErrorCode::SetrlimitFailed)?;
}
if config.max_output_size != -1 {
setrlimit(
Resource::RLIMIT_FSIZE,
config.max_output_size as u64,
config.max_output_size as u64,
)
.map_err(|_| ErrorCode::SetrlimitFailed)?;
}
let input_file = File::open(&config.input_path).map_err(|_| ErrorCode::Dup2Failed)?;
if unsafe { libc::dup2(input_file.as_raw_fd(), 0) } == -1 {
logger
.write(
LogLevel::Fatal,
file!(),
line!(),
format_args!("Error: Failed to redirect standard input."),
)
.map_err(|_| ErrorCode::Dup2Failed)?;
return Err(ErrorCode::Dup2Failed);
}
let output_file = File::create(&config.output_path).map_err(|_| ErrorCode::Dup2Failed)?;
if unsafe { libc::dup2(output_file.as_raw_fd(), 1) } == -1 {
logger
.write(
LogLevel::Fatal,
file!(),
line!(),
format_args!("Error: Failed to redirect standard output."),
)
.map_err(|_| ErrorCode::Dup2Failed)?;
return Err(ErrorCode::Dup2Failed);
}
let error_file = File::create(&config.error_path).map_err(|_| ErrorCode::Dup2Failed)?;
if unsafe { libc::dup2(error_file.as_raw_fd(), 2) } == -1 {
logger
.write(
LogLevel::Fatal,
file!(),
line!(),
format_args!("Error: Failed to redirect standard error."),
)
.map_err(|_| ErrorCode::Dup2Failed)?;
return Err(ErrorCode::Dup2Failed);
}
setgid(Gid::from_raw(config.gid)).map_err(|_| ErrorCode::SetuidFailed)?;
setuid(Uid::from_raw(config.uid)).map_err(|_| ErrorCode::SetuidFailed)?;
if let Some(rule_name) = &config.seccomp_rule_name {
seccomp::load_seccomp_rules(rule_name).map_err(|_| ErrorCode::LoadSeccompFailed)?;
}
let exe_path = CString::new(config.exe_path.clone()).unwrap();
let args: Vec<CString> = config
.args
.iter()
.map(|arg| CString::new(arg.as_str()).unwrap())
.collect();
let env: Vec<CString> = config
.env
.iter()
.map(|e| CString::new(e.as_str()).unwrap())
.collect();
execve(&exe_path, &args, &env).map_err(|_| ErrorCode::ExecveFailed)?;
Ok(())
}