Initial Agentsd project commit
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
124
core/agentsd/src/syscall/process.rs
Normal file
124
core/agentsd/src/syscall/process.rs
Normal file
@@ -0,0 +1,124 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
use tokio::process::Command;
|
||||
|
||||
struct ManagedProcess {
|
||||
cmd: String,
|
||||
}
|
||||
|
||||
/// Process syscall: spawn and manage subprocesses.
|
||||
/// Uses the real OS PID as the identifier.
|
||||
pub struct ProcessSyscall {
|
||||
procs: Mutex<HashMap<u64, ManagedProcess>>,
|
||||
children: tokio::sync::Mutex<HashMap<u64, tokio::process::Child>>,
|
||||
}
|
||||
|
||||
impl ProcessSyscall {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
procs: Mutex::new(HashMap::new()),
|
||||
children: tokio::sync::Mutex::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn spawn(
|
||||
&self,
|
||||
cmd: &str,
|
||||
args: &[String],
|
||||
env: &HashMap<String, String>,
|
||||
cwd: &str,
|
||||
) -> Result<u64, String> {
|
||||
let mut command = Command::new(cmd);
|
||||
command.args(args);
|
||||
if !cwd.is_empty() {
|
||||
command.current_dir(cwd);
|
||||
}
|
||||
for (k, v) in env {
|
||||
command.env(k, v);
|
||||
}
|
||||
command
|
||||
.stdin(std::process::Stdio::piped())
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::piped());
|
||||
|
||||
let child = command.spawn().map_err(|e| e.to_string())?;
|
||||
let pid = child
|
||||
.id()
|
||||
.ok_or_else(|| "spawned process has no OS pid".to_string())? as u64;
|
||||
|
||||
self.procs.lock().unwrap().insert(
|
||||
pid,
|
||||
ManagedProcess {
|
||||
cmd: cmd.to_string(),
|
||||
},
|
||||
);
|
||||
self.children.lock().await.insert(pid, child);
|
||||
|
||||
tracing::info!("process spawned: pid={} cmd={}", pid, cmd);
|
||||
Ok(pid)
|
||||
}
|
||||
|
||||
pub async fn kill(&self, pid: u64) -> Result<(), String> {
|
||||
let mut child = {
|
||||
let mut children = self.children.lock().await;
|
||||
children.remove(&pid).ok_or_else(|| format!("pid {} not found", pid))?
|
||||
};
|
||||
|
||||
child.kill().await.map_err(|e| e.to_string())?;
|
||||
self.procs.lock().unwrap().remove(&pid);
|
||||
tracing::info!("process killed: pid={}", pid);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn wait(&self, pid: u64) -> Result<i32, String> {
|
||||
let mut child = {
|
||||
let mut children = self.children.lock().await;
|
||||
children.remove(&pid).ok_or_else(|| format!("pid {} not found", pid))?
|
||||
};
|
||||
self.procs.lock().unwrap().remove(&pid);
|
||||
let status = child.wait().await.map_err(|e| e.to_string())?;
|
||||
Ok(status.code().unwrap_or(-1))
|
||||
}
|
||||
|
||||
pub async fn write_stdin(&self, pid: u64, data: &[u8]) -> Result<(), String> {
|
||||
use tokio::io::AsyncWriteExt;
|
||||
let mut children = self.children.lock().await;
|
||||
if let Some(child) = children.get_mut(&pid) {
|
||||
if let Some(stdin) = child.stdin.as_mut() {
|
||||
stdin.write_all(data).await.map_err(|e| e.to_string())?;
|
||||
stdin.flush().await.map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err("stdin not available".to_string())
|
||||
}
|
||||
} else {
|
||||
Err(format!("pid {} not found", pid))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn read_stdout(&self, pid: u64, buf: &mut [u8]) -> Result<usize, String> {
|
||||
use tokio::io::AsyncReadExt;
|
||||
let mut children = self.children.lock().await;
|
||||
if let Some(child) = children.get_mut(&pid) {
|
||||
if let Some(stdout) = child.stdout.as_mut() {
|
||||
stdout.read(buf).await.map_err(|e| e.to_string())
|
||||
} else {
|
||||
Err("stdout not available".to_string())
|
||||
}
|
||||
} else {
|
||||
Err(format!("pid {} not found", pid))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn signal(&self, pid: u64, _signal: i32) -> Result<(), String> {
|
||||
self.kill(pid).await
|
||||
}
|
||||
|
||||
pub fn list(&self) -> Vec<(u64, String, String)> {
|
||||
let procs = self.procs.lock().unwrap();
|
||||
procs
|
||||
.iter()
|
||||
.map(|(pid, mp)| (*pid, mp.cmd.clone(), "running".to_string()))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user