use std::process::ExitStatus;
use nix::{
sys::signal::{self, Signal},
unistd::Pid,
};
use tempfile::TempDir;
use tokio::process::Child;
use crate::MicrosandboxResult;
pub struct ProcessHandle {
pid: u32,
sandbox_name: String,
child: Child,
detached: bool,
_file_mounts_staging: Option<TempDir>,
}
impl ProcessHandle {
pub(crate) fn new(
pid: u32,
sandbox_name: String,
child: Child,
file_mounts_staging: Option<TempDir>,
) -> Self {
Self {
pid,
sandbox_name,
child,
detached: false,
_file_mounts_staging: file_mounts_staging,
}
}
pub fn pid(&self) -> u32 {
self.pid
}
pub fn sandbox_name(&self) -> &str {
&self.sandbox_name
}
pub fn kill(&self) -> MicrosandboxResult<()> {
tracing::debug!(pid = self.pid, sandbox = %self.sandbox_name, "sending SIGKILL");
signal::kill(Pid::from_raw(self.pid as i32), Signal::SIGKILL)?;
Ok(())
}
pub fn drain(&self) -> MicrosandboxResult<()> {
tracing::debug!(pid = self.pid, sandbox = %self.sandbox_name, "sending SIGUSR1 (drain)");
signal::kill(Pid::from_raw(self.pid as i32), Signal::SIGUSR1)?;
Ok(())
}
pub async fn wait(&mut self) -> MicrosandboxResult<ExitStatus> {
tracing::debug!(pid = self.pid, sandbox = %self.sandbox_name, "waiting for exit");
let status = self.child.wait().await?;
tracing::debug!(pid = self.pid, ?status, "process exited");
Ok(status)
}
pub fn try_wait(&mut self) -> MicrosandboxResult<Option<ExitStatus>> {
Ok(self.child.try_wait()?)
}
pub fn disarm(&mut self) {
self.detached = true;
if let Some(td) = self._file_mounts_staging.take() {
let _ = td.keep();
}
}
}
impl Drop for ProcessHandle {
fn drop(&mut self) {
if self.detached {
return;
}
if let Ok(None) = self.child.try_wait()
&& let Some(pid) = self.child.id()
{
tracing::debug!(pid, sandbox = %self.sandbox_name, "drop: sending SIGTERM safety net");
let _ = signal::kill(Pid::from_raw(pid as i32), Signal::SIGTERM);
}
}
}