#![cfg(unix)]
use std::ffi::CString;
use std::io;
use std::ptr::NonNull;
pub struct ShmRegion {
name: CString,
ptr: NonNull<u8>,
len: usize,
created: bool,
}
impl ShmRegion {
pub fn open_or_create(name: &str, size: usize) -> io::Result<Self> {
let cname = CString::new(name)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "shm name has nul byte"))?;
let (fd, created) = unsafe {
let fd = libc::shm_open(
cname.as_ptr(),
libc::O_RDWR | libc::O_CREAT | libc::O_EXCL,
0o600,
);
if fd >= 0 {
(fd, true)
} else {
let err = io::Error::last_os_error();
if err.raw_os_error() != Some(libc::EEXIST) {
return Err(err);
}
let fd = libc::shm_open(cname.as_ptr(), libc::O_RDWR, 0o600);
if fd < 0 {
return Err(io::Error::last_os_error());
}
(fd, false)
}
};
if created {
let rc = unsafe { libc::ftruncate(fd, size as libc::off_t) };
if rc != 0 {
let err = io::Error::last_os_error();
unsafe { libc::close(fd) };
let _ = unsafe { libc::shm_unlink(cname.as_ptr()) };
return Err(err);
}
}
let ptr = unsafe {
libc::mmap(
std::ptr::null_mut(),
size,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED,
fd,
0,
)
};
unsafe { libc::close(fd) };
if ptr == libc::MAP_FAILED {
let err = io::Error::last_os_error();
if created {
let _ = unsafe { libc::shm_unlink(cname.as_ptr()) };
}
return Err(err);
}
let ptr = NonNull::new(ptr.cast::<u8>()).expect("mmap returned non-null on success");
Ok(Self {
name: cname,
ptr,
len: size,
created,
})
}
pub fn as_ptr(&self) -> *mut u8 {
self.ptr.as_ptr()
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn created(&self) -> bool {
self.created
}
pub fn unlink(&self) -> io::Result<()> {
let rc = unsafe { libc::shm_unlink(self.name.as_ptr()) };
if rc != 0 {
let err = io::Error::last_os_error();
if err.raw_os_error() == Some(libc::ENOENT) {
return Ok(());
}
return Err(err);
}
Ok(())
}
}
impl Drop for ShmRegion {
fn drop(&mut self) {
unsafe {
libc::munmap(self.ptr.as_ptr().cast(), self.len);
}
}
}
unsafe impl Send for ShmRegion {}
unsafe impl Sync for ShmRegion {}
pub fn ring_segment_name(fleet_name: &str, kind: u8) -> String {
let uid = unsafe { libc::geteuid() };
format!("/orbit-{fleet_name}-{kind}-{uid}")
}