kanit

Toy init system
Log | Files | Refs | README | LICENSE

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 }