char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
f4a0f5b
main
1//! Linux specifics of the Unix backend. 2 3use std:: ffi:: CString ; 4use std:: io; 5use std:: os:: fd::{ AsFd , AsRawFd , FromRawFd , OwnedFd , RawFd }; 6use std:: os:: unix:: ffi:: OsStrExt ; 7 8use super :: reserve; 9 10pub ( super ) fn pipe () -> io:: Result <( OwnedFd , OwnedFd )> { 11let mut fds =[ 0 ; 2 ]; 12if unsafe { libc:: pipe2 ( fds. as_mut_ptr (), libc:: O_CLOEXEC ) } <0 { 13return Err ( io:: Error :: last_os_error ()); 14} 15Ok ( unsafe { ( OwnedFd :: from_raw_fd ( fds[ 0 ]), OwnedFd :: from_raw_fd ( fds[ 1 ])) }) 16} 17 18/// Returns the master and slave of a new terminal. 19pub ( super ) fn pty () -> io:: Result <( OwnedFd , OwnedFd )> { 20let fd =unsafe { libc:: posix_openpt ( libc:: O_RDWR | libc:: O_NOCTTY | libc:: O_CLOEXEC ) }; 21if fd <0 { 22return Err ( io:: Error :: last_os_error ()); 23} 24let master =unsafe { OwnedFd :: from_raw_fd ( fd) }; 25if unsafe { libc:: grantpt ( fd) } <0 ||unsafe { libc:: unlockpt ( fd) } <0 { 26return Err ( io:: Error :: last_os_error ()); 27} 28let fd =unsafe { 29 libc:: ioctl ( 30 fd, 31 libc:: TIOCGPTPEER , 32 libc:: O_RDWR | libc:: O_NOCTTY | libc:: O_CLOEXEC , 33) 34}; 35if fd <0 { 36return Err ( io:: Error :: last_os_error ()); 37} 38Ok (( master, unsafe { OwnedFd :: from_raw_fd ( fd) })) 39} 40 41/// The authorized executable, pinned by an `O_PATH` descriptor through exec. 42pub struct Executable { 43fd : OwnedFd , 44} 45impl Executable { 46pub fn open_at ( 47directory : & impl AsFd , 48name : & std:: ffi:: OsStr , 49) -> io:: Result <( Self , std:: fs:: Metadata )> { 50let label =CString :: new ( name. as_bytes ()) ?; 51// Follow the final link (multi-call/symlinked entries) while the parent 52// directory fd pins the lookup; the entry authorization covers the 53// lexical name, and argv[0] preserves it for multicall dispatch. 54let flags = libc:: O_PATH | libc:: O_CLOEXEC ; 55let fd =unsafe { libc:: openat ( directory. as_fd (). as_raw_fd (), label. as_ptr (), flags) }; 56if fd <0 { 57return Err ( io:: Error :: last_os_error ()); 58} 59let fd =unsafe { OwnedFd :: from_raw_fd ( fd) }; 60let metadata = std:: fs:: File :: from ( reserve ( fd. as_raw_fd (), 3 ) ?). metadata () ?; 61Ok (( Self { fd}, metadata)) 62} 63/// Moves the descriptor clear of every requested child descriptor. 64pub ( super ) fn reserve ( & self , minimum : RawFd ) -> io:: Result < Self > { 65Ok ( Self { 66fd : reserve ( self . fd . as_raw_fd (), minimum) ?, 67}) 68} 69/// Runs after fork; returns only on failure. 70pub ( super ) fn execute ( 71& self , 72argv : * const * const libc:: c_char , 73env : * const * const libc:: c_char , 74) -> io:: Error { 75let execute = ||unsafe { libc:: fexecve ( self . fd . as_raw_fd (), argv, env) }; 76execute (); 77let error = io:: Error :: last_os_error (); 78// Script interpreters need the pinned script fd after exec; binaries do not. 79if error. raw_os_error () ==Some ( libc:: ENOENT ) { 80if unsafe { libc:: fcntl ( self . fd . as_raw_fd (), libc:: F_SETFD , 0 ) } <0 { 81return io:: Error :: last_os_error (); 82} 83execute (); 84return io:: Error :: last_os_error (); 85} 86 error 87} 88} 89 90/// Taken before fork, so the child can tell whether its owner already died. 91pub ( super ) struct Owner ( libc:: pid_t ); 92impl Owner { 93pub fn new () ->Self { 94Self ( unsafe { libc:: getpid () }) 95} 96/// Orphaned children must not outlive an abruptly killed owner. 97pub fn bind ( & self ) -> io:: Result <()> { 98if unsafe { libc:: prctl ( libc:: PR_SET_PDEATHSIG , libc:: SIGKILL as u64 ) } <0 { 99return Err ( io:: Error :: last_os_error ()); 100} 101if unsafe { libc:: getppid () } !=self . 0 { 102return Err ( io:: Error :: new ( io:: ErrorKind :: NotConnected , "owner exited" )); 103} 104Ok (()) 105} 106} 107 108pub ( super ) struct Descriptors ; 109impl Descriptors { 110pub fn new () -> io:: Result < Self > { 111Ok ( Self ) 112} 113/// Marks every descriptor from 3 up CLOEXEC; spawn re-arms the declared 114/// ones. Marking rather than closing keeps the executable and exec-error 115/// pipe open until exec. 116pub fn restrict ( & mut self ) -> io:: Result <()> { 117let result =unsafe { 118 libc:: syscall ( 119 libc:: SYS_close_range , 1203u32 , 121 u32:: MAX , 122 libc:: CLOSE_RANGE_CLOEXEC , 123) 124}; 125if result ==0 { 126return Ok (()); 127} 128let error = io:: Error :: last_os_error (); 129if !matches! ( 130 error. raw_os_error (), 131Some ( libc:: ENOSYS | libc:: EINVAL | libc:: EPERM ) 132) { 133return Err ( error); 134} 135 136// RLIMIT_NOFILE can be lowered below existing descriptors, so scanning 137// only to that limit would leak them. getdents64 avoids allocation after 138// fork and visits exactly the child's open descriptors, including high FDs. 139let directory =unsafe { 140 libc:: open ( 141c"/proc/self/fd" . as_ptr (), 142 libc:: O_RDONLY | libc:: O_DIRECTORY | libc:: O_CLOEXEC , 143) 144}; 145if directory <0 { 146let error = io:: Error :: last_os_error (); 147return Err ( 148if matches! ( error. raw_os_error (), Some ( libc:: ENOENT | libc:: ENOTDIR )) { 149 io:: Error :: from_raw_os_error ( libc:: ENOTSUP ) 150} else { 151 error 152}, 153); 154} 155let directory =unsafe { OwnedFd :: from_raw_fd ( directory) }; 156# [ repr ( align ( 8 ))] 157struct Buffer ([ u8 ; 8192 ]); 158let mut buffer =Buffer ([ 0 ; 8192 ]); 159loop { 160let count =unsafe { 161 libc:: syscall ( 162 libc:: SYS_getdents64 , 163 directory. as_raw_fd (), 164 buffer. 0 . as_mut_ptr (), 165 buffer. 0 . len (), 166) 167}; 168if count <0 { 169let error = io:: Error :: last_os_error (); 170if error. kind () == io:: ErrorKind :: Interrupted { 171continue ; 172} 173return Err ( error); 174} 175if count ==0 { 176return Ok (()); 177} 178let mut entries =& buffer. 0 [ ..countas usize ]; 179while !entries. is_empty () { 180// linux_dirent64: ino(8), off(8), reclen(2), type(1), name. 181if entries. len () <20 { 182return Err ( io:: Error :: from_raw_os_error ( libc:: EIO )); 183} 184let length = u16:: from_ne_bytes ([ entries[ 16 ], entries[ 17 ]]) as usize ; 185if length <20 || length > entries. len () { 186return Err ( io:: Error :: from_raw_os_error ( libc:: EIO )); 187} 188let name =& entries[ 19 ..length]; 189let end = name 190. iter () 191. position ( |byte|* byte ==0 ) 192. ok_or_else ( || io:: Error :: from_raw_os_error ( libc:: EIO )) ?; 193let fd = name[ ..end]. iter (). try_fold ( 0i32 , |value, byte|{ 194if !byte. is_ascii_digit () { 195return None ; 196} 197 value. checked_mul ( 10 ) ?. checked_add (( byte -b'0' ) as i32 ) 198}); 199if let Some ( fd) = fd. filter ( |fd|* fd >=3 ) { 200let flags =unsafe { libc:: fcntl ( fd, libc:: F_GETFD ) }; 201if flags <0 202 ||unsafe { libc:: fcntl ( fd, libc:: F_SETFD , flags | libc:: FD_CLOEXEC ) } <0 203{ 204return Err ( io:: Error :: last_os_error ()); 205} 206} 207 entries =& entries[ length..]; 208} 209} 210} 211}