use std::ffi::{c_char, c_int, c_void};
use std::mem;
use crate::error::{bail, bail_errno, Error, Result};
#[cfg_attr(debug_assertions, alloc_counter::no_alloc)]
pub unsafe fn get_topmost_stack_pointer(stack: &mut [u8]) -> *mut c_void {
let top_addr = stack.as_mut_ptr().add(stack.len()) as *mut c_void;
let top_addr = top_addr as usize & !0xf;
top_addr as *mut c_void
}
#[cfg_attr(debug_assertions, alloc_counter::no_alloc)]
pub fn socket_pair() -> Result<(c_int, c_int)> {
let mut socket_fds = [0; 2];
let 0.. = (unsafe {
libc::socketpair(
libc::AF_UNIX,
libc::SOCK_STREAM | libc::SOCK_CLOEXEC,
0,
socket_fds.as_mut_ptr(),
)
}) else {
bail_errno!("failed to create socketpair");
};
let [a, b] = socket_fds;
Ok((a, b))
}
#[cfg_attr(debug_assertions, alloc_counter::no_alloc)]
pub unsafe fn socket_send<T: Copy>(sock_fd: c_int, value: T) -> Result<()> {
let size = mem::size_of::<T>();
let result = libc::write(sock_fd, (&value) as *const T as *const c_void, size);
if result == -1 {
return Err(Error::last_os_error().cause("failed to send to socket"));
}
if (result as usize) != size {
return Err(Error::new().cause("failed to send socket message in single write call"));
}
Ok(())
}
#[cfg_attr(debug_assertions, alloc_counter::no_alloc)]
pub unsafe fn socket_recv<T: Copy>(sock_fd: c_int) -> Result<T> {
let size = mem::size_of::<T>();
let mut output_slot = mem::MaybeUninit::<T>::uninit();
let result = libc::read(
sock_fd,
output_slot.as_mut_ptr().cast(),
mem::size_of::<T>(),
);
if result == -1 {
return Err(Error::last_os_error().cause("failed to receive from socket"));
};
if result == 0 {
return Err(Error::new().cause("reached EOF while receiving from socket"));
}
if (result as usize) != size {
return Err(Error::new().cause("failed to receive socket message in single read call"));
}
Ok(output_slot.assume_init())
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExitStatus {
Code(c_int),
Signal(c_int),
}
impl ExitStatus {
pub fn from_wait_status(wait_status: c_int) -> Result<ExitStatus> {
if libc::WIFEXITED(wait_status) {
return Ok(ExitStatus::Code(libc::WEXITSTATUS(wait_status)));
}
if libc::WIFSIGNALED(wait_status) {
return Ok(ExitStatus::Signal(libc::WTERMSIG(wait_status)));
}
bail!("invalid wait status")
}
pub fn success(&self) -> bool {
matches!(self, ExitStatus::Code(0))
}
}
#[cfg_attr(debug_assertions, alloc_counter::no_alloc)]
pub unsafe fn waitpid(pid: c_int) -> Result<ExitStatus> {
let mut status: c_int = 0;
let 0.. = (unsafe { libc::waitpid(pid, &mut status as *mut c_int, 0) }) else {
bail_errno!("waitpid failed");
};
ExitStatus::from_wait_status(status)
}
#[cfg_attr(debug_assertions, alloc_counter::no_alloc)]
pub fn stat(path: *const c_char) -> Result<libc::stat> {
let mut stat_buf = mem::MaybeUninit::<libc::stat>::uninit();
let 0.. = (unsafe { libc::stat(path, stat_buf.as_mut_ptr()) }) else {
bail_errno!("failed to stat file");
};
Ok(unsafe { stat_buf.assume_init() })
}
#[cfg_attr(debug_assertions, alloc_counter::no_alloc)]
pub fn mkdirp(path: *const c_char) -> Result<()> {
let mkdir_ignoring_eexist = |path: *const c_char| -> Result<()> {
let mkdir_result = unsafe { libc::mkdir(path, 0o755) };
if mkdir_result == -1 {
let err = Error::last_os_error();
if err.errno != libc::EEXIST {
return Err(err.cause("failed to create directory"));
}
};
Ok(())
};
let path_len = unsafe { libc::strlen(path) };
if path_len > libc::PATH_MAX as usize {
bail!("mkdirp() path is longer than PATH_MAX");
}
let mut buf = [b'\0'; libc::PATH_MAX as usize + 1];
buf[..path_len + 1].copy_from_slice(unsafe { std::slice::from_raw_parts(path, path_len + 1) });
for i in 1..buf.len() {
if buf[i] == b'\0' {
break;
}
if buf[i] != b'/' {
continue;
}
buf[i] = b'\0';
mkdir_ignoring_eexist(buf.as_ptr())?;
buf[i] = b'/';
}
mkdir_ignoring_eexist(buf.as_ptr().cast())?;
Ok(())
}