Skip to main content

rash/
pidfile.rs

1//! The pid file, removed when the guard is dropped.
2//!
3//! autossh registers an `atexit()` handler for this (autossh.c:1721-1727), which
4//! it then has to work around in `xerrlog()` because `_exit()` skips those on
5//! most systems. A `Drop` guard needs no such special case.
6
7use std::fs::{self, File, OpenOptions};
8use std::io::{self, Write};
9use std::path::{Path, PathBuf};
10use std::process;
11use std::time::SystemTime;
12
13#[derive(Debug)]
14pub struct PidFile {
15    path: PathBuf,
16}
17
18impl PidFile {
19    /// Write the current pid. Call this *after* any daemonising fork, so the
20    /// file records the daemon's pid rather than that of the process that
21    /// forked it.
22    pub fn create(path: &Path) -> io::Result<Self> {
23        let mut f = File::create(path)?;
24        writeln!(f, "{}", std::process::id())?;
25        f.flush()?;
26        Ok(Self {
27            path: path.to_path_buf(),
28        })
29    }
30
31    /// Bump the modification time so a watchdog can see rash is still alive.
32    ///
33    /// autossh only does this when built with `-DTOUCH_PIDFILE`, which is off in
34    /// the stock build; rash exposes it as `RASH_TOUCH_PIDFILE`.
35    pub fn touch(&self) -> io::Result<()> {
36        OpenOptions::new()
37            .write(true)
38            .open(&self.path)?
39            .set_modified(SystemTime::now())
40    }
41}
42
43impl Drop for PidFile {
44    /// Remove the file, but only while it is still ours.
45    ///
46    /// Nothing stops a second rash being pointed at the same path: it truncates
47    /// the file and writes its own pid, and without this check the first one to
48    /// exit would then delete the *survivor's* pid file, leaving a live
49    /// supervisor that no watchdog can find.
50    fn drop(&mut self) {
51        let ours = fs::read_to_string(&self.path)
52            .ok()
53            .and_then(|s| s.trim().parse::<u32>().ok())
54            .is_some_and(|pid| pid == process::id());
55        if ours {
56            let _ = fs::remove_file(&self.path);
57        }
58    }
59}