kanit

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

cli.rs (4423B)


      1 use std::fs::{read_to_string, write};
      2 use std::os::unix::prelude::ExitStatusExt;
      3 use std::path::Path;
      4 use std::process;
      5 use std::process::{ExitCode, ExitStatus};
      6 
      7 use nix::errno::Errno;
      8 use nix::libc::pid_t;
      9 use nix::sched::{CloneFlags, unshare};
     10 use nix::sys::signal;
     11 use nix::sys::signal::{Signal, kill};
     12 use nix::sys::signalfd::{SfdFlags, SigSet, SignalFd};
     13 use nix::sys::stat::Mode;
     14 use nix::sys::wait::{WaitPidFlag, WaitStatus, waitpid};
     15 use nix::unistd::{Pid, mkdir};
     16 
     17 use kanit_common::error::{Context, Result};
     18 
     19 use crate::{Supervisor, spawn, spawn_restart};
     20 
     21 fn cgroup_signal(name: &str, signal: Signal) -> Result<()> {
     22     let procs_path = Path::new("/sys/fs/cgroup/system.slice")
     23         .join(name)
     24         .join("cgroup.procs");
     25 
     26     read_to_string(procs_path)
     27         .context("failed to read procs")?
     28         .split('\n')
     29         .filter_map(|pid| pid.parse::<u32>().ok())
     30         .try_for_each(|pid| {
     31             if pid == process::id() {
     32                 return Ok(());
     33             }
     34 
     35             let _ = kill(Pid::from_raw(pid as pid_t), signal);
     36 
     37             Ok(())
     38         })
     39 }
     40 
     41 pub fn handle_cli() -> ExitCode {
     42     match Supervisor::from_env() {
     43         Ok(mut cfg) => {
     44             let mut mask = SigSet::empty();
     45             mask.add(signal::SIGCHLD);
     46             mask.add(signal::SIGTERM);
     47             mask.thread_block().unwrap();
     48 
     49             let sfd = SignalFd::with_flags(&mask, SfdFlags::empty()).unwrap();
     50 
     51             if let Some(ref cgroup) = cfg.cgroup {
     52                 unshare(CloneFlags::CLONE_NEWCGROUP).expect("unshare cgroup");
     53 
     54                 let supervisor_cgroup = Path::new("/sys/fs/cgroup/system.slice").join(cgroup);
     55 
     56                 mkdir(
     57                     &supervisor_cgroup,
     58                     Mode::S_IRWXU | Mode::S_IRGRP | Mode::S_IRUSR,
     59                 )
     60                 .expect("create directory");
     61 
     62                 write(
     63                     supervisor_cgroup.join("cgroup.procs"),
     64                     process::id().to_string(),
     65                 )
     66                 .expect("write cgroup proc");
     67             }
     68 
     69             #[allow(clippy::zombie_processes)] // we use `waitpid` and not `.wait`
     70             let mut child = spawn(&cfg).expect("spawn child");
     71 
     72             loop {
     73                 match sfd.read_signal() {
     74                     Ok(Some(sig)) => match Signal::try_from(sig.ssi_signo as i32) {
     75                         Ok(Signal::SIGCHLD) => {
     76                             loop {
     77                                 let pid = waitpid(None, Some(WaitPidFlag::WNOHANG));
     78 
     79                                 match pid {
     80                                     Ok(WaitStatus::StillAlive) => break,
     81                                     Err(Errno::ECHILD) => break,
     82                                     Err(e) => {
     83                                         eprintln!("failed to waitpid: {e}");
     84                                         return ExitCode::FAILURE;
     85                                     }
     86                                     _ => {}
     87                                 }
     88                             }
     89 
     90                             if let Some(c) =
     91                                 spawn_restart(&mut cfg, ExitStatus::from_raw(sig.ssi_status), true)
     92                                     .expect("restart child")
     93                             {
     94                                 child = c;
     95                             } else {
     96                                 return ExitCode::SUCCESS;
     97                             }
     98                         }
     99                         Ok(Signal::SIGTERM) => {
    100                             child.kill().expect("kill child");
    101                             if let Some(attempts) = cfg.restart_attempts {
    102                                 cfg.restart_attempts = Some(attempts + 1);
    103                             }
    104                             if let Some(ref cgroup) = cfg.cgroup {
    105                                 cgroup_signal(cgroup.as_ref(), Signal::SIGKILL)
    106                                     .expect("kill child");
    107                             }
    108                         }
    109                         _ => {}
    110                     },
    111                     Ok(None) => unreachable!(),
    112                     Err(e) => {
    113                         eprintln!("{e}");
    114                         return ExitCode::FAILURE;
    115                     }
    116                 }
    117             }
    118         }
    119         Err(e) => {
    120             eprintln!("{e}");
    121             ExitCode::FAILURE
    122         }
    123     }
    124 }