#[allow(unused_macros)]
#[macro_use]
pub(crate) mod log {
macro_rules! trace (($($tt:tt)*) => {{}});
macro_rules! debug (($($tt:tt)*) => {{}});
macro_rules! info (($($tt:tt)*) => {{}});
macro_rules! warn (($($tt:tt)*) => {{}});
macro_rules! error (($($tt:tt)*) => {{}});
}
#[allow(unused_imports)]
use log::*;
use std::fs::{File, OpenOptions};
use std::io::{ErrorKind, Read, Write};
use std::fs::remove_file;
use std::path::{Path, PathBuf};
use ::cfg_if::*;
use ::fs2::FileExt;
mod error;
pub use error::*;
cfg_if! {
if #[cfg(target_os="windows")] {
mod windows;
use windows as os_impl;
} else if #[cfg(any(target_os="freebsd", target_os="linux", target_os="macos"))] {
mod unix;
use unix as os_impl;
} else {
compile_error!("shared_memory isnt implemented for this platform...");
}
}
pub struct ShmemConf {
owner: bool,
os_id: Option<String>,
overwrite_flink: bool,
flink_path: Option<PathBuf>,
size: usize,
}
impl Drop for ShmemConf {
fn drop(&mut self) {
if self.owner {
if let Some(flink_path) = self.flink_path.as_ref() {
debug!("Deleting file link {}", flink_path.to_string_lossy());
let _ = remove_file(flink_path);
}
}
}
}
#[allow(clippy::new_without_default)]
impl ShmemConf {
pub fn new() -> Self {
Self {
owner: false,
os_id: None,
overwrite_flink: false,
flink_path: None,
size: 0,
}
}
pub fn os_id<S: AsRef<str>>(mut self, os_id: S) -> Self {
self.os_id = Some(String::from(os_id.as_ref()));
self
}
pub fn force_create_flink(mut self) -> Self {
self.overwrite_flink = true;
self
}
pub fn flink<S: AsRef<Path>>(mut self, path: S) -> Self {
self.flink_path = Some(PathBuf::from(path.as_ref()));
self
}
pub fn size(mut self, size: usize) -> Self {
self.size = size;
self
}
pub fn create(mut self) -> Result<Shmem, ShmemError> {
if self.size == 0 {
return Err(ShmemError::MapSizeZero);
}
let fout = if let Some(ref flink_path) = self.flink_path {
debug!("Creating file link that points to mapping");
let mut open_options: OpenOptions = OpenOptions::new();
open_options.write(true);
if self.overwrite_flink {
open_options.create(true).truncate(true);
} else {
open_options.create_new(true);
}
match open_options.open(flink_path) {
Ok(f) => {
debug!("Created file link '{}'", flink_path.to_string_lossy());
if let Err(e) = f.try_lock_exclusive() {
return Err(ShmemError::LinkCreateFailed(e));
}
Some(f)
}
Err(e) if e.kind() == ErrorKind::AlreadyExists => {
return Err(ShmemError::LinkExists)
}
Err(e) => return Err(ShmemError::LinkCreateFailed(e)),
}
} else {
None
};
let mapping = match self.os_id {
None => {
loop {
let cur_id = format!("/shmem_{:X}", rand::random::<u64>());
match os_impl::create_mapping(&cur_id, self.size) {
Err(ShmemError::MappingIdExists) => continue,
Ok(m) => break m,
Err(e) => {
if fout.is_some() {
let _ = std::fs::remove_file(self.flink_path.as_ref().unwrap());
}
return Err(e);
}
};
}
}
Some(ref specific_id) => os_impl::create_mapping(specific_id, self.size)?,
};
debug!("Created shared memory mapping '{}'", mapping.unique_id);
if let Some(mut f) = fout {
debug!("Writing memory mapping id to flink");
if let Err(e) = f.write(mapping.unique_id.as_bytes()) {
let _ = std::fs::remove_file(self.flink_path.as_ref().unwrap());
return Err(ShmemError::LinkWriteFailed(e));
}
let _ = f.unlock();
}
self.owner = true;
self.size = mapping.map_size;
Ok(Shmem {
config: self,
mapping,
})
}
pub fn open(mut self) -> Result<Shmem, ShmemError> {
if self.flink_path.is_none() && self.os_id.is_none() {
debug!("Open called with no file link or unique id...");
return Err(ShmemError::NoLinkOrOsId);
}
if let Some(ref flink_path) = self.flink_path {
debug!(
"Open shared memory from file link {}",
flink_path.to_string_lossy()
);
let mut f = match File::open(flink_path) {
Ok(f) => {
f.lock_shared().unwrap();
f
}
Err(e) => return Err(ShmemError::LinkOpenFailed(e)),
};
let mut contents: Vec<u8> = Vec::new();
if let Err(e) = f.read_to_end(&mut contents) {
let _ = f.unlock();
return Err(ShmemError::LinkReadFailed(e));
}
let _ = f.unlock();
let link_os_id = match String::from_utf8(contents) {
Ok(s) => s,
Err(_) => return Err(ShmemError::LinkDoesNotExist),
};
if let Some(os_id) = self.os_id.as_ref() {
if *os_id != link_os_id {
return Err(ShmemError::FlinkInvalidOsId);
}
} else {
self.os_id = Some(link_os_id);
}
}
let os_id = match self.os_id.as_ref() {
Some(i) => i,
None => return Err(ShmemError::NoLinkOrOsId),
};
debug!("Openning shared memory id {}", os_id);
let mapping = os_impl::open_mapping(os_id, self.size)?;
self.size = mapping.map_size;
self.owner = false;
Ok(Shmem {
config: self,
mapping,
})
}
}
pub struct Shmem {
config: ShmemConf,
mapping: os_impl::MapData,
}
#[allow(clippy::len_without_is_empty)]
impl Shmem {
pub fn is_owner(&self) -> bool {
self.config.owner
}
pub fn set_owner(&mut self, is_owner: bool) -> bool {
self.mapping.set_owner(is_owner);
let prev_val = self.config.owner;
self.config.owner = is_owner;
prev_val
}
pub fn get_os_id(&self) -> &str {
self.mapping.unique_id.as_str()
}
pub fn get_flink_path(&self) -> Option<&PathBuf> {
self.config.flink_path.as_ref()
}
pub fn len(&self) -> usize {
self.mapping.map_size
}
pub fn as_ptr(&self) -> *mut u8 {
self.mapping.map_ptr
}
pub unsafe fn as_slice(&self) -> &[u8] {
std::slice::from_raw_parts(self.as_ptr(), self.len())
}
pub unsafe fn as_slice_mut(&mut self) -> &mut [u8] {
std::slice::from_raw_parts_mut(self.as_ptr(), self.len())
}
}