1use std::fs::{File, OpenOptions};
6use std::io::{ErrorKind, Read, Write};
7
8use std::fs::remove_file;
9use std::path::{Path, PathBuf};
10
11use cfg_if::cfg_if;
12
13cfg_if! {
14 if #[cfg(feature = "logging")] {
15 pub use log;
16 } else {
17 #[allow(unused_macros)]
18 mod log {
19 macro_rules! trace (($($tt:tt)*) => {{}});
20 macro_rules! debug (($($tt:tt)*) => {{}});
21 macro_rules! info (($($tt:tt)*) => {{}});
22 macro_rules! warn (($($tt:tt)*) => {{}});
23 macro_rules! error (($($tt:tt)*) => {{}});
24 pub(crate) use {debug, trace};
25 }
26 }
27}
28
29use crate::log::*;
30
31mod error;
32pub use error::*;
33
34cfg_if! {
36 if #[cfg(target_os="windows")] {
37 mod windows;
38 use windows as os_impl;
39 } else if #[cfg(any(target_os="freebsd", target_os="linux", target_os="macos"))] {
40 mod unix;
41 use crate::unix as os_impl;
42 } else {
43 compile_error!("shared_memory isnt implemented for this platform...");
44 }
45}
46
47#[derive(Clone, Default)]
48pub struct ShmemConf {
50 owner: bool,
51 os_id: Option<String>,
52 overwrite_flink: bool,
53 flink_path: Option<PathBuf>,
54 size: usize,
55 ext: os_impl::ShmemConfExt,
56}
57impl Drop for ShmemConf {
58 fn drop(&mut self) {
59 if self.owner {
61 if let Some(flink_path) = self.flink_path.as_ref() {
62 debug!("Deleting file link {}", flink_path.to_string_lossy());
63 let _ = remove_file(flink_path);
64 }
65 }
66 }
67}
68
69impl ShmemConf {
70 pub fn new() -> Self {
72 ShmemConf::default()
73 }
74 pub fn os_id<S: AsRef<str>>(mut self, os_id: S) -> Self {
78 self.os_id = Some(String::from(os_id.as_ref()));
79 self
80 }
81
82 pub fn force_create_flink(mut self) -> Self {
84 self.overwrite_flink = true;
85 self
86 }
87
88 pub fn flink<S: AsRef<Path>>(mut self, path: S) -> Self {
93 self.flink_path = Some(PathBuf::from(path.as_ref()));
94 self
95 }
96
97 pub fn size(mut self, size: usize) -> Self {
99 self.size = size;
100 self
101 }
102
103 pub fn create(mut self) -> Result<Shmem, ShmemError> {
105 if self.size == 0 {
106 return Err(ShmemError::MapSizeZero);
107 }
108
109 if let Some(ref flink_path) = self.flink_path {
110 if !self.overwrite_flink && flink_path.is_file() {
111 return Err(ShmemError::LinkExists);
112 }
113 }
114
115 let mapping = match self.os_id {
117 None => {
118 loop {
120 let cur_id = format!("/shmem_{:X}", rand::random::<u64>());
121 match os_impl::create_mapping(&cur_id, self.size) {
122 Err(ShmemError::MappingIdExists) => continue,
123 Ok(m) => break m,
124 Err(e) => {
125 return Err(e);
126 }
127 };
128 }
129 }
130 Some(ref specific_id) => os_impl::create_mapping(specific_id, self.size)?,
131 };
132 debug!("Created shared memory mapping '{}'", mapping.unique_id);
133
134 if let Some(ref flink_path) = self.flink_path {
136 debug!("Creating file link that points to mapping");
137 let mut open_options: OpenOptions = OpenOptions::new();
138 open_options.write(true);
139
140 if self.overwrite_flink {
141 open_options.create(true).truncate(true);
142 } else {
143 open_options.create_new(true);
144 }
145
146 match open_options.open(flink_path) {
147 Ok(mut f) => {
148 if let Err(e) = f.write(mapping.unique_id.as_bytes()) {
150 let _ = std::fs::remove_file(flink_path);
151 return Err(ShmemError::LinkWriteFailed(e));
152 }
153 }
154 Err(e) if e.kind() == ErrorKind::AlreadyExists => {
155 return Err(ShmemError::LinkExists)
156 }
157 Err(e) => return Err(ShmemError::LinkCreateFailed(e)),
158 }
159
160 debug!(
161 "Created file link '{}' with id '{}'",
162 flink_path.to_string_lossy(),
163 mapping.unique_id
164 );
165 }
166
167 self.owner = true;
168 self.size = mapping.map_size;
169
170 Ok(Shmem {
171 config: self,
172 mapping,
173 })
174 }
175
176 pub fn open(mut self) -> Result<Shmem, ShmemError> {
178 if self.flink_path.is_none() && self.os_id.is_none() {
180 debug!("Open called with no file link or unique id...");
181 return Err(ShmemError::NoLinkOrOsId);
182 }
183
184 let mut flink_uid = String::new();
185 let mut retry = 0;
186 loop {
187 let unique_id = if let Some(ref unique_id) = self.os_id {
188 retry = 5;
189 unique_id.as_str()
190 } else {
191 let flink_path = self.flink_path.as_ref().unwrap();
192 debug!(
193 "Open shared memory from file link {}",
194 flink_path.to_string_lossy()
195 );
196 let mut f = match File::open(flink_path) {
197 Ok(f) => f,
198 Err(e) => return Err(ShmemError::LinkOpenFailed(e)),
199 };
200 flink_uid.clear();
201 if let Err(e) = f.read_to_string(&mut flink_uid) {
202 return Err(ShmemError::LinkReadFailed(e));
203 }
204 flink_uid.as_str()
205 };
206
207 match os_impl::open_mapping(unique_id, self.size, &self.ext) {
208 Ok(m) => {
209 self.size = m.map_size;
210 self.owner = false;
211
212 return Ok(Shmem {
213 config: self,
214 mapping: m,
215 });
216 }
217 Err(ShmemError::MapOpenFailed(_)) if self.os_id.is_none() && retry < 5 => {
220 retry += 1;
221 std::thread::sleep(std::time::Duration::from_millis(50));
222 }
223 Err(e) => return Err(e),
224 }
225 }
226 }
227}
228
229pub struct Shmem {
231 config: ShmemConf,
232 mapping: os_impl::MapData,
233}
234#[allow(clippy::len_without_is_empty)]
235impl Shmem {
236 pub fn is_owner(&self) -> bool {
238 self.config.owner
239 }
240 pub fn set_owner(&mut self, is_owner: bool) -> bool {
244 self.mapping.set_owner(is_owner);
245
246 let prev_val = self.config.owner;
247 self.config.owner = is_owner;
248 prev_val
249 }
250 pub fn get_os_id(&self) -> &str {
252 self.mapping.unique_id.as_str()
253 }
254 pub fn get_flink_path(&self) -> Option<&PathBuf> {
256 self.config.flink_path.as_ref()
257 }
258 pub fn len(&self) -> usize {
260 self.mapping.map_size
261 }
262 pub fn as_ptr(&self) -> *mut u8 {
264 self.mapping.as_mut_ptr()
265 }
266 pub unsafe fn as_slice(&self) -> &[u8] {
270 std::slice::from_raw_parts(self.as_ptr(), self.len())
271 }
272 pub unsafe fn as_slice_mut(&mut self) -> &mut [u8] {
276 std::slice::from_raw_parts_mut(self.as_ptr(), self.len())
277 }
278}