summaryrefslogtreecommitdiff
path: root/src/authentication.rs
blob: 5312792929ac6ca20c02e3cc09f6b1f55d9b9c61 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
use anyhow::{Context, Result};
use std::{
    io::Read,
    os::fd::AsRawFd,
    process::{Child, Command, Stdio},
    time::Duration,
};

pub struct TerminalAgent(Option<Child>);
impl TerminalAgent {
    pub fn start(interactive: bool) -> Result<Self> {
        if !interactive || unsafe { libc::geteuid() } == 0 {
            return Ok(Self(None));
        }
        let (mut reader, writer) = std::os::unix::net::UnixStream::pair()?;
        reader.set_read_timeout(Some(Duration::from_secs(5)))?;
        let fd = writer.as_raw_fd();
        // SAFETY: fd belongs to writer and remains open until after spawning the agent.
        unsafe {
            libc::fcntl(fd, libc::F_SETFD, 0);
        }
        let mut child = Command::new("/usr/bin/pkttyagent")
            .args([
                "--process",
                &std::process::id().to_string(),
                "--notify-fd",
                &fd.to_string(),
                "--fallback",
            ])
            .stdin(Stdio::inherit())
            .stdout(Stdio::inherit())
            .stderr(Stdio::inherit())
            .spawn()
            .context("Could not start terminal polkit agent; install polkit")?;
        drop(writer);
        let mut buffer = [0u8; 1];
        if let Err(error) = reader.read(&mut buffer) {
            let _ = child.kill();
            let _ = child.wait();
            return Err(error).context("Terminal authentication agent did not become ready");
        }
        Ok(Self(Some(child)))
    }
}
impl Drop for TerminalAgent {
    fn drop(&mut self) {
        if let Some(child) = &mut self.0 {
            let _ = child.kill();
            let _ = child.wait();
        }
    }
}