char/avril

(wip) why'd you have to go and make things so complicaaaaated?

git clone https://git.t4t.associates/char/avril

Charlotte Somrearrange libs into lib and extensions into extsf4a0f5b

main
4.3 KiB140 linesraw
1//! macOS specifics of the Unix backend.
2
3use std::io;
4use std::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd};
5use std::path::Path;
6
7fn close_on_exec(pair: (OwnedFd, OwnedFd)) -> io::Result<(OwnedFd, OwnedFd)> {
8    // Not atomic, but this backend's own children close foreign descriptors.
9    for fd in [&pair.0, &pair.1] {
10        if unsafe { libc::fcntl(fd.as_raw_fd(), libc::F_SETFD, libc::FD_CLOEXEC) } < 0 {
11            return Err(io::Error::last_os_error());
12        }
13    }
14    Ok(pair)
15}
16
17pub(super) fn pipe() -> io::Result<(OwnedFd, OwnedFd)> {
18    let mut fds = [0; 2];
19    if unsafe { libc::pipe(fds.as_mut_ptr()) } < 0 {
20        return Err(io::Error::last_os_error());
21    }
22    close_on_exec(unsafe { (OwnedFd::from_raw_fd(fds[0]), OwnedFd::from_raw_fd(fds[1])) })
23}
24
25/// Returns the master and slave of a new terminal.
26pub(super) fn pty() -> io::Result<(OwnedFd, OwnedFd)> {
27    let mut master = -1;
28    let mut slave = -1;
29    if unsafe {
30        libc::openpty(
31            &mut master,
32            &mut slave,
33            std::ptr::null_mut(),
34            std::ptr::null_mut(),
35            std::ptr::null_mut(),
36        )
37    } < 0
38    {
39        return Err(io::Error::last_os_error());
40    }
41    close_on_exec(unsafe { (OwnedFd::from_raw_fd(master), OwnedFd::from_raw_fd(slave)) })
42}
43
44/// The authorized executable. macOS has no `fexecve`, so it executes the
45/// authorized path.
46pub struct Executable;
47impl Executable {
48    pub fn open(target: &Path) -> io::Result<(Self, std::fs::Metadata)> {
49        Ok((Self, std::fs::metadata(target)?))
50    }
51    pub(super) fn reserve(&self, _minimum: RawFd) -> io::Result<Self> {
52        Ok(Self)
53    }
54    /// Runs after fork; returns only on failure. argv[0] is the authorized
55    /// path.
56    pub(super) fn execute(
57        &self,
58        argv: *const *const libc::c_char,
59        env: *const *const libc::c_char,
60    ) -> io::Error {
61        unsafe {
62            libc::execve(*argv, argv, env);
63        }
64        io::Error::last_os_error()
65    }
66}
67
68/// macOS has no parent-death signal: an abruptly killed owner orphans its
69/// children, and only orderly exits clean up.
70pub(super) struct Owner;
71impl Owner {
72    pub fn new() -> Self {
73        Self
74    }
75    pub fn bind(&self) -> io::Result<()> {
76        Ok(())
77    }
78}
79
80pub(super) struct Descriptors(Vec<libc::proc_fdinfo>);
81impl Descriptors {
82    /// Sizes the descriptor listing before fork, so the child need not
83    /// allocate.
84    pub fn new() -> io::Result<Self> {
85        let size = unsafe {
86            libc::proc_pidinfo(
87                libc::getpid(),
88                libc::PROC_PIDLISTFDS,
89                0,
90                std::ptr::null_mut(),
91                0,
92            )
93        };
94        if size <= 0 {
95            return Err(io::Error::last_os_error());
96        }
97        // Headroom for descriptors other threads open before fork.
98        let count = size as usize / std::mem::size_of::<libc::proc_fdinfo>() * 2 + 64;
99        Ok(Self(Vec::with_capacity(count)))
100    }
101    /// Marks every descriptor from 3 up CLOEXEC; spawn re-arms the declared
102    /// ones. macOS has neither close_range nor procfs, but lists a process's
103    /// descriptors directly, including any above a lowered RLIMIT_NOFILE.
104    pub fn restrict(&mut self) -> io::Result<()> {
105        let capacity =
106            (self.0.capacity() * std::mem::size_of::<libc::proc_fdinfo>()) as libc::c_int;
107        let size = unsafe {
108            libc::proc_pidinfo(
109                libc::getpid(),
110                libc::PROC_PIDLISTFDS,
111                0,
112                self.0.as_mut_ptr().cast(),
113                capacity,
114            )
115        };
116        if size <= 0 {
117            return Err(io::Error::last_os_error());
118        }
119        // A full buffer may have been truncated; fail closed.
120        if size >= capacity {
121            return Err(io::Error::from_raw_os_error(libc::EMFILE));
122        }
123        unsafe {
124            self.0
125                .set_len(size as usize / std::mem::size_of::<libc::proc_fdinfo>());
126        }
127        for entry in &self.0 {
128            let fd = entry.proc_fd;
129            if fd < 3 {
130                continue;
131            }
132            let flags = unsafe { libc::fcntl(fd, libc::F_GETFD) };
133            if flags < 0 || unsafe { libc::fcntl(fd, libc::F_SETFD, flags | libc::FD_CLOEXEC) } < 0
134            {
135                return Err(io::Error::last_os_error());
136            }
137        }
138        Ok(())
139    }
140}