//! Path simple: spawn directo, sin namespacing. use crate::env::{build_env, EnvSpec}; use crate::error::IncarnateError; use crate::pre_exec::{apply_unchecked, ChildSetup}; use crate::ChildStdio; use brahman_card::{Card, Payload}; use nix::unistd::Pid; use std::os::fd::FromRawFd; use std::os::unix::process::CommandExt; use std::process::{Command, Stdio}; pub fn incarnate_plain( card: &Card, env_spec: &EnvSpec, stdio: &ChildStdio, setup: &ChildSetup, ) -> Result { let (exec, argv, base_envp) = match &card.payload { Payload::Native { exec, argv, envp } => (exec.clone(), argv.clone(), envp.clone()), Payload::Legacy { exec, argv, .. } => (exec.clone(), argv.clone(), Vec::new()), _ => return Err(IncarnateError::NonExecutablePayload), }; let env = build_env(card, &base_envp, env_spec); let mut cmd = Command::new(&exec); cmd.args(&argv); cmd.env_clear(); for (k, v) in &env { cmd.env(k, v); } if let Some(fd) = stdio.stdin_fd { // SAFETY: el caller garantiza que `fd` está abierto y le // transfiere ownership al child. `Command` lo cierra tras spawn. cmd.stdin(unsafe { Stdio::from_raw_fd(fd) }); } if let Some(fd) = stdio.stdout_fd { cmd.stdout(unsafe { Stdio::from_raw_fd(fd) }); } if let Some(fd) = stdio.stderr_fd { cmd.stderr(unsafe { Stdio::from_raw_fd(fd) }); } if !setup.is_empty() { // Clone para que la closure sea 'static (Command::pre_exec lo exige). let ops = setup.ops.clone(); // SAFETY: pre_exec corre post-fork pre-exec. apply_unchecked sólo // hace syscalls async-signal-safe. unsafe { cmd.pre_exec(move || { let r = apply_unchecked(&ops); if r != 0 { Err(std::io::Error::from_raw_os_error(libc::EINVAL)) } else { Ok(()) } }); } } let child = cmd.spawn()?; Ok(Pid::from_raw(child.id() as i32)) }