File size: 2,885 Bytes
fda8632 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | use super::{BashJob, BashTable};
use crate::agent::web_tools::io_detail;
use std::ffi::CString;
use std::fs::File;
use std::os::fd::FromRawFd;
use std::os::unix::process::CommandExt;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use std::time::Instant;
unsafe extern "C" {
fn mkstemp(template: *mut std::ffi::c_char) -> i32;
}
pub(super) fn spawn_job(
table: &mut BashTable,
command: &str,
timeout: i32,
) -> Result<usize, String> {
let (path, file) = create_output_file()?;
let stdout = file.try_clone().map_err(|error| {
format!(
"failed to create temporary output file: {}",
io_detail(&error)
)
})?;
let stderr = file.try_clone().map_err(|error| {
format!(
"failed to create temporary output file: {}",
io_detail(&error)
)
})?;
drop(file);
let child = Command::new("/bin/sh")
.arg("-c")
.arg(command)
.stdin(Stdio::inherit())
.stdout(Stdio::from(stdout))
.stderr(Stdio::from(stderr))
.process_group(0)
.spawn()
.map_err(|error| format!("failed to fork: {}", io_detail(&error)))?;
let pid = child.id() as i32;
if table.next_id <= 0 {
table.next_id = 1;
}
let id = table.next_id;
table.next_id += 1;
table.jobs.insert(
0,
BashJob {
id,
pid,
path,
child: Some(child),
start: Instant::now(),
timeout_sec: f64::from(timeout),
observed_once: false,
exit_status: -1,
running: true,
timed_out: false,
},
);
Ok(0)
}
fn create_output_file() -> Result<(PathBuf, File), String> {
let mut tmpl = CString::new("/tmp/ds4_agent_output_XXXXXX")
.map_err(|_| "failed to create temporary output file: invalid template".to_string())?
.into_bytes_with_nul();
// SAFETY: [Category 8 — FFI] `tmpl` is an exclusive CString buffer ending
// in `XXXXXX\0`; `mkstemp` rewrites those six bytes in place and does not
// retain the pointer.
let fd = unsafe { mkstemp(tmpl.as_mut_ptr().cast()) };
if fd < 0 {
return Err(format!(
"failed to create temporary output file: {}",
io_detail(&std::io::Error::last_os_error())
));
}
let path = CString::from_vec_with_nul(tmpl)
.map_err(|_| "failed to create temporary output file: invalid path".to_string())?
.into_string()
.map_err(|_| "failed to create temporary output file: invalid path".to_string())?;
// SAFETY: [Category 13 — Library / unsafe contract] `fd` is a live exclusive
// descriptor from `mkstemp` (fd >= 0 checked). `File` takes ownership; no
// other close path exists.
Ok((PathBuf::from(path), unsafe { File::from_raw_fd(fd) }))
}
|