blob: 65ea492aa6c282ca6ae687b3da7adb3b8718f175 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
use super::Spawned;
use crate::Command;
use crate::group::Group;
use std::{
io,
process::{ExitStatus, Stdio},
};
use tokio::signal::unix::{SignalKind, signal};
pub struct Child {
group: Group,
proxy: Option<maka_network::proxy::Proxy>,
#[cfg(target_os = "linux")]
mount_cleanup: crate::command::Cleanup,
}
pub async fn spawn(plan: Command) -> Result<Spawned, maka_agent_facts::tools::ToolError> {
if !plan.cwd.is_absolute() || !plan.executable.is_absolute() {
return Err(crate::failed(
"pipe processes require captured absolute executable and cwd",
));
}
spawn_prepared(plan.prepare().await?).await
}
pub(crate) async fn spawn_prepared(
mut plan: crate::command::Prepared,
) -> Result<Spawned, maka_agent_facts::tools::ToolError> {
let spawned = (|| -> io::Result<_> {
// Subscribe before spawn/check so an immediate exit cannot lose its wake.
let changed = signal(SignalKind::child())?;
let child = plan
.unix()
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.process_group(0)
.kill_on_drop(true)
.spawn()?;
Ok((child, changed))
})();
let (mut child, changed) = match spawned {
Ok(spawned) => spawned,
Err(error) => {
plan.abort().await?;
return Err(crate::failed(error));
}
};
Ok(Spawned {
stdin: child.stdin.take().expect("piped stdin"),
stdout: child.stdout.take().expect("piped stdout"),
stderr: child.stderr.take().expect("piped stderr"),
child: Child {
group: Group::new(child, changed),
proxy: plan.take_proxy(),
#[cfg(target_os = "linux")]
mount_cleanup: plan.take_mount_lease().into(),
},
})
}
impl Child {
pub fn id(&self) -> u32 {
self.group.pid().as_raw_nonzero().get() as u32
}
pub fn terminate(&mut self) -> io::Result<()> {
self.group.terminate().map(|_| ())
}
pub(crate) fn interrupt(&mut self) -> io::Result<bool> {
self.group.interrupt()
}
pub async fn wait(&mut self) -> io::Result<ExitStatus> {
let status = self.group.wait().await?;
#[cfg(target_os = "linux")]
self.mount_cleanup.finish().await?;
if let Some(proxy) = &mut self.proxy {
proxy.close().await?;
}
Ok(status)
}
}