atagen

roundabout

public
Star0
Watch0
fdad58f8133a77a20c44d0e1472cb73a5128ef2c
1
ssh://brewery@pls.lobotomise.me:33/atagen/roundabout.git
src/client.rsRust92 lines2.4 KB
1
use std::{
2
    fs::{self, File, OpenOptions},
3
    io,
4
    os::unix::{fs::OpenOptionsExt, net::UnixStream, process::CommandExt},
5
    process::{Command, Stdio},
6
    thread,
7
    time::Duration,
8
};
9
10
use crate::paths;
11
12
pub struct Flock(File);
13
14
impl Flock {
15
    pub fn exclusive(file: File) -> io::Result<Self> {
16
        use std::os::fd::AsRawFd;
17
        if unsafe { libc::flock(file.as_raw_fd(), libc::LOCK_EX) } != 0 {
18
            return Err(io::Error::last_os_error());
19
        }
20
        Ok(Self(file))
21
    }
22
23
    pub fn try_exclusive(file: File) -> Option<Self> {
24
        use std::os::fd::AsRawFd;
25
        (unsafe { libc::flock(file.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) } == 0)
26
            .then_some(Self(file))
27
    }
28
}
29
30
impl Drop for Flock {
31
    fn drop(&mut self) {
32
        use std::os::fd::AsRawFd;
33
        unsafe { libc::flock(self.0.as_raw_fd(), libc::LOCK_UN) };
34
    }
35
}
36
37
pub fn spawn_lock() -> io::Result<Flock> {
38
    let file = OpenOptions::new()
39
        .create(true)
40
        .truncate(false)
41
        .write(true)
42
        .mode(0o600)
43
        .open(paths::spawn_lock())?;
44
    Flock::exclusive(file)
45
}
46
47
pub fn connect() -> Option<UnixStream> {
48
    UnixStream::connect(paths::socket()).ok()
49
}
50
51
pub fn connect_or_spawn() -> io::Result<UnixStream> {
52
    if let Some(s) = connect() {
53
        return Ok(s);
54
    }
55
    let _lock = spawn_lock()?;
56
    if let Some(s) = connect() {
57
        return Ok(s);
58
    }
59
    let _ = fs::remove_file(paths::socket());
60
    let log = OpenOptions::new()
61
        .create(true)
62
        .append(true)
63
        .open(paths::state_dir().join("daemon.log"))?;
64
    let mut cmd = Command::new(std::env::current_exe()?);
65
    cmd.arg("daemon")
66
        .stdin(Stdio::null())
67
        .stdout(Stdio::null())
68
        .stderr(log);
69
    unsafe {
70
        cmd.pre_exec(|| {
71
            libc::setsid();
72
            Ok(())
73
        });
74
    }
75
    let mut child = cmd.spawn()?;
76
    for _ in 0..200 {
77
        if let Some(s) = connect() {
78
            let _ = child.wait();
79
            return Ok(s);
80
        }
81
        if let Ok(Some(status)) = child.try_wait()
82
            && !status.success()
83
        {
84
            return Err(io::Error::other(format!(
85
                "daemon died on startup ({status}), see {}",
86
                paths::state_dir().join("daemon.log").display()
87
            )));
88
        }
89
        thread::sleep(Duration::from_millis(10));
90
    }
91
    Err(io::Error::other("daemon never showed up"))
92
}