supervisor.rs (2949B)
1 use std::fs::OpenOptions; 2 use std::os::unix::fs::chroot; 3 use std::os::unix::process::CommandExt; 4 use std::process::{Child, Command, ExitStatus, Stdio}; 5 use std::thread::sleep; 6 use std::time::Duration; 7 8 use nix::unistd::{Group, User}; 9 10 use kanit_common::error::{Context, Result}; 11 12 use crate::flags::{RestartPolicy, Supervisor}; 13 14 pub fn spawn_restart( 15 cfg: &mut Supervisor, 16 status: ExitStatus, 17 delay: bool, 18 ) -> Result<Option<Child>> { 19 match cfg.restart_policy.unwrap_or(RestartPolicy::Never) { 20 RestartPolicy::Never => return Ok(None), 21 RestartPolicy::OnFailure if status.success() => return Ok(None), 22 RestartPolicy::OnSuccess if !status.success() => return Ok(None), 23 _ => {} 24 } 25 26 if let Some(attempts) = cfg.restart_attempts { 27 if attempts == 0 { 28 return Ok(None); 29 } else { 30 cfg.restart_attempts = Some(attempts - 1); 31 } 32 } 33 34 if let Some(delay_sec) = cfg.restart_delay { 35 if delay { 36 sleep(Duration::from_secs(delay_sec)) 37 } 38 } 39 40 spawn(cfg).map(Some) 41 } 42 43 pub fn spawn(cfg: &Supervisor) -> Result<Child> { 44 let mut cmd = Command::new(&cfg.exec); 45 46 cmd.args(&cfg.args); 47 cmd.envs(cfg.env.iter().filter_map(|pair| pair.split_once('='))); 48 49 if let Some(ref dir) = cfg.pwd { 50 cmd.current_dir(dir); 51 } 52 53 if let Some(ref dir) = cfg.root { 54 let dir = dir.clone(); 55 56 // SAFETY: we only call async-signal-safe functions (chroot) 57 unsafe { 58 cmd.pre_exec(move || chroot(&dir)); 59 } 60 } 61 62 if let Some(ref user) = cfg.user { 63 let uid = user 64 .parse::<u32>() 65 .map(Some) 66 .unwrap_or_else(|_| { 67 User::from_name(user) 68 .map(|u| u.map(|u| u.uid.as_raw())) 69 .unwrap_or(None) 70 }) 71 .context("failed to parse/locate user")?; 72 73 cmd.uid(uid); 74 } 75 76 if let Some(ref group) = cfg.group { 77 let gid = group 78 .parse::<u32>() 79 .map(Some) 80 .unwrap_or_else(|_| { 81 Group::from_name(group) 82 .map(|g| g.map(|g| g.gid.as_raw())) 83 .unwrap_or(None) 84 }) 85 .context("failed to parse/locate group")?; 86 87 cmd.gid(gid); 88 } 89 90 if let Some(ref stdout) = cfg.stdout { 91 let f = OpenOptions::new() 92 .create(true) 93 .append(true) 94 .open(stdout) 95 .context("failed to open stdout")?; 96 97 cmd.stdout(f); 98 } else { 99 cmd.stdout(Stdio::null()); 100 } 101 102 if let Some(ref stderr) = cfg.stderr { 103 let f = OpenOptions::new() 104 .create(true) 105 .append(true) 106 .open(stderr) 107 .context("failed to open stderr")?; 108 109 cmd.stderr(f); 110 } else { 111 cmd.stderr(Stdio::null()); 112 } 113 114 cmd.spawn().context("failed to spawn child") 115 }