shared_memory_extended/
lib.rs1use 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)]
48pub struct ShmemConf {
50 owner: bool,
51 writable: bool,
52 os_id: Option<String>,
53 overwrite_flink: bool,
54 flink_path: Option<PathBuf>,
55 size: usize,
56 ext: os_impl::ShmemConfExt,
57}
58
59impl Default for ShmemConf {
60 fn default() -> Self {
61 Self {
62 owner: false,
63 writable: true,
64 os_id: None,
65 overwrite_flink: false,
66 flink_path: None,
67 size: Default::default(),
68 ext: Default::default(),
69 }
70 }
71}
72
73impl Drop for ShmemConf {
74 fn drop(&mut self) {
75 if self.owner {
77 if let Some(flink_path) = self.flink_path.as_ref() {
78 debug!("Deleting file link {}", flink_path.to_string_lossy());
79 let _ = remove_file(flink_path);
80 }
81 }
82 }
83}
84
85impl ShmemConf {
86 pub fn new() -> Self {
88 ShmemConf::default()
89 }
90 pub fn os_id<S: AsRef<str>>(mut self, os_id: S) -> Self {
94 self.os_id = Some(String::from(os_id.as_ref()));
95 self
96 }
97
98 pub fn force_create_flink(mut self) -> Self {
100 self.overwrite_flink = true;
101 self
102 }
103
104 pub fn flink<S: AsRef<Path>>(mut self, path: S) -> Self {
109 self.flink_path = Some(PathBuf::from(path.as_ref()));
110 self
111 }
112
113 pub fn size(mut self, size: usize) -> Self {
115 self.size = size;
116 self
117 }
118
119 pub fn writable(mut self, writable: bool) -> Self {
123 self.writable = writable;
124 self
125 }
126
127 pub fn create(mut self) -> Result<Shmem, ShmemError> {
129 if self.size == 0 {
130 return Err(ShmemError::MapSizeZero);
131 }
132
133 if let Some(ref flink_path) = self.flink_path {
134 if !self.overwrite_flink && flink_path.is_file() {
135 return Err(ShmemError::LinkExists);
136 }
137 }
138
139 let mapping = match self.os_id {
141 None => {
142 loop {
144 let cur_id = format!("/shmem_{:X}", rand::random::<u64>());
145 match os_impl::create_mapping(&cur_id, self.size, self.writable) {
146 Err(ShmemError::MappingIdExists) => continue,
147 Ok(m) => break m,
148 Err(e) => {
149 return Err(e);
150 }
151 };
152 }
153 }
154 Some(ref specific_id) => {
155 os_impl::create_mapping(specific_id, self.size, self.writable)?
156 }
157 };
158 debug!("Created shared memory mapping '{}'", mapping.unique_id);
159
160 if let Some(ref flink_path) = self.flink_path {
162 debug!("Creating file link that points to mapping");
163 let mut open_options: OpenOptions = OpenOptions::new();
164 open_options.write(true);
165
166 if self.overwrite_flink {
167 open_options.create(true).truncate(true);
168 } else {
169 open_options.create_new(true);
170 }
171
172 match open_options.open(flink_path) {
173 Ok(mut f) => {
174 if let Err(e) = f.write(mapping.unique_id.as_bytes()) {
176 let _ = std::fs::remove_file(flink_path);
177 return Err(ShmemError::LinkWriteFailed(e));
178 }
179 }
180 Err(e) if e.kind() == ErrorKind::AlreadyExists => {
181 return Err(ShmemError::LinkExists)
182 }
183 Err(e) => return Err(ShmemError::LinkCreateFailed(e)),
184 }
185
186 debug!(
187 "Created file link '{}' with id '{}'",
188 flink_path.to_string_lossy(),
189 mapping.unique_id
190 );
191 }
192
193 self.owner = true;
194 self.size = mapping.map_size;
195
196 Ok(Shmem {
197 config: self,
198 mapping,
199 })
200 }
201
202 pub fn open(mut self) -> Result<Shmem, ShmemError> {
204 if self.flink_path.is_none() && self.os_id.is_none() {
206 debug!("Open called with no file link or unique id...");
207 return Err(ShmemError::NoLinkOrOsId);
208 }
209
210 let mut flink_uid = String::new();
211 let mut retry = 0;
212 loop {
213 let unique_id = if let Some(ref unique_id) = self.os_id {
214 retry = 5;
215 unique_id.as_str()
216 } else {
217 let flink_path = self.flink_path.as_ref().unwrap();
218 debug!(
219 "Open shared memory from file link {}",
220 flink_path.to_string_lossy()
221 );
222 let mut f = match File::open(flink_path) {
223 Ok(f) => f,
224 Err(e) => return Err(ShmemError::LinkOpenFailed(e)),
225 };
226 flink_uid.clear();
227 if let Err(e) = f.read_to_string(&mut flink_uid) {
228 return Err(ShmemError::LinkReadFailed(e));
229 }
230 flink_uid.as_str()
231 };
232
233 match os_impl::open_mapping(unique_id, self.size, &self.ext, self.writable) {
234 Ok(m) => {
235 self.size = m.map_size;
236 self.owner = false;
237
238 return Ok(Shmem {
239 config: self,
240 mapping: m,
241 });
242 }
243 Err(ShmemError::MapOpenFailed(_)) if self.os_id.is_none() && retry < 5 => {
246 retry += 1;
247 std::thread::sleep(std::time::Duration::from_millis(50));
248 }
249 Err(e) => return Err(e),
250 }
251 }
252 }
253}
254
255pub struct Shmem {
257 config: ShmemConf,
258 mapping: os_impl::MapData,
259}
260#[allow(clippy::len_without_is_empty)]
261impl Shmem {
262 pub fn is_owner(&self) -> bool {
264 self.config.owner
265 }
266 pub fn set_owner(&mut self, is_owner: bool) -> bool {
270 self.mapping.set_owner(is_owner);
271
272 let prev_val = self.config.owner;
273 self.config.owner = is_owner;
274 prev_val
275 }
276 pub fn get_os_id(&self) -> &str {
278 self.mapping.unique_id.as_str()
279 }
280 pub fn get_flink_path(&self) -> Option<&PathBuf> {
282 self.config.flink_path.as_ref()
283 }
284 pub fn len(&self) -> usize {
286 self.mapping.map_size
287 }
288 pub fn as_ptr(&self) -> *mut u8 {
290 self.mapping.as_mut_ptr()
291 }
292 pub unsafe fn as_slice(&self) -> &[u8] {
296 std::slice::from_raw_parts(self.as_ptr(), self.len())
297 }
298 pub unsafe fn as_slice_mut(&mut self) -> &mut [u8] {
302 std::slice::from_raw_parts_mut(self.as_ptr(), self.len())
303 }
304}