1use core::ptr::NonNull;
71
72use alloc::vec;
73use alloc::vec::Vec;
74
75use iceoryx2_bb_concurrency::atomic::AtomicBool;
76use iceoryx2_bb_concurrency::atomic::Ordering;
77use iceoryx2_bb_container::semantic_string::*;
78use iceoryx2_bb_elementary::enum_gen;
79use iceoryx2_bb_system_types::file_name::*;
80use iceoryx2_bb_system_types::file_path::*;
81use iceoryx2_bb_system_types::path::*;
82use iceoryx2_log::{error, fail, fatal_panic, trace};
83use iceoryx2_pal_configuration::PATH_SEPARATOR;
84use iceoryx2_pal_posix::posix::errno::Errno;
85use iceoryx2_pal_posix::posix::POSIX_SUPPORT_ADVANCED_SIGNAL_HANDLING;
86use iceoryx2_pal_posix::posix::POSIX_SUPPORT_PERSISTENT_SHARED_MEMORY;
87use iceoryx2_pal_posix::*;
88
89pub use crate::access_mode::AccessMode;
90pub use crate::creation_mode::CreationMode;
91use crate::file::{FileStatError, FileTruncateError};
92use crate::file_descriptor::*;
93use crate::memory_lock::{MemoryLock, MemoryLockCreationError};
94use crate::memory_mapping::{
95 MappingBehavior, MemoryMapping, MemoryMappingBuilder, MemoryMappingCreationError,
96};
97pub use crate::permission::Permission;
98use crate::signal::SignalHandler;
99use crate::system_configuration::Limit;
100
101enum_gen! { SharedMemoryCreationError
102 entry:
103 SizeDoesNotFit,
104 InsufficientMemory,
105 InsufficientMemoryToBeMemoryLocked,
106 UnsupportedSizeOfZero,
107 InsufficientPermissions,
108 MappedRegionLimitReached,
109 PerProcessFileHandleLimitReached,
110 SystemWideFileHandleLimitReached,
111 NameTooLong,
112 InvalidName,
113 AlreadyExist,
114 DoesNotExist,
115 UnknownError(i32)
116 mapping:
117 FileTruncateError,
118 FileStatError,
119 MemoryLockCreationError,
120 SharedMemoryRemoveError,
121 MemoryMappingCreationError
122}
123
124enum_gen! { SharedMemoryRemoveError
125 entry:
126 InsufficientPermissions,
127 UnknownError(i32)
128}
129
130#[derive(Debug)]
132pub struct SharedMemoryBuilder {
133 name: FileName,
134 size: usize,
135 is_memory_locked: bool,
136 has_ownership: bool,
137 permission: Permission,
138 creation_mode: Option<CreationMode>,
139 zero_memory: bool,
140 access_mode: AccessMode,
141 mapping_offset: isize,
142 enforce_base_address: Option<u64>,
143}
144
145impl SharedMemoryBuilder {
146 pub fn new(name: &FileName) -> Self {
147 SharedMemoryBuilder {
148 name: *name,
149 size: 0,
150 is_memory_locked: false,
151 permission: Permission::OWNER_ALL,
152 access_mode: AccessMode::None,
153 has_ownership: true,
154 creation_mode: None,
155 zero_memory: true,
156 mapping_offset: 0,
157 enforce_base_address: None,
158 }
159 }
160
161 pub fn mapping_offset(mut self, value: isize) -> Self {
164 self.mapping_offset = value;
165 self
166 }
167
168 pub fn is_memory_locked(mut self, value: bool) -> Self {
171 self.is_memory_locked = value;
172 self
173 }
174
175 pub fn enforce_base_address(mut self, value: u64) -> Self {
178 self.enforce_base_address = Some(value);
179 self
180 }
181
182 pub fn open_existing(
184 mut self,
185 access_mode: AccessMode,
186 ) -> Result<SharedMemory, SharedMemoryCreationError> {
187 self.access_mode = access_mode;
188 Self::open(self)
189 }
190
191 fn create_memory_mapping(
192 file_descriptor: FileDescriptor,
193 config: &SharedMemoryBuilder,
194 ) -> Result<MemoryMapping, SharedMemoryCreationError> {
195 match MemoryMappingBuilder::from_file_descriptor(file_descriptor)
196 .mapping_behavior(MappingBehavior::Shared)
197 .initial_mapping_permission(config.access_mode.into())
198 .mapping_address_hint(config.enforce_base_address.unwrap_or(0) as usize)
199 .enforce_mapping_address_hint(config.enforce_base_address.is_some())
200 .offset(config.mapping_offset)
201 .size(config.size)
202 .create()
203 {
204 Ok(mapping) => Ok(mapping),
205 Err(e) => {
206 fail!(from config, with e.into(),
207 "Failed to create shared memory since the memory mapping failed ({e:?}).");
208 }
209 }
210 }
211
212 fn open(mut self) -> Result<SharedMemory, SharedMemoryCreationError> {
213 let msg = "Unable to open shared memory";
214 let fd = SharedMemory::shm_open(&self.name, &self)?;
215
216 let actual_shm_size = fail!(from self, when fd.metadata(),
217 "{} since a failure occurred while acquiring the file attributes.", msg)
218 .size();
219 self.size = actual_shm_size as usize;
220
221 let memory_mapping = Self::create_memory_mapping(fd, &self)?;
222
223 let shm = SharedMemory {
224 name: self.name,
225 has_ownership: AtomicBool::new(false),
226 memory_lock: None,
227 memory_mapping,
228 mapping_offset: self.mapping_offset,
229 };
230
231 trace!(from shm, "open");
232 Ok(shm)
233 }
234
235 pub fn creation_mode(mut self, creation_mode: CreationMode) -> SharedMemoryCreationBuilder {
237 self.access_mode = AccessMode::ReadWrite;
238 self.creation_mode = Some(creation_mode);
239 SharedMemoryCreationBuilder { config: self }
240 }
241}
242
243#[derive(Debug)]
244pub struct SharedMemoryCreationBuilder {
245 config: SharedMemoryBuilder,
246}
247
248impl SharedMemoryCreationBuilder {
249 pub fn permission(mut self, value: Permission) -> Self {
251 self.config.permission = value;
252 self
253 }
254
255 pub fn zero_memory(mut self, value: bool) -> Self {
261 self.config.zero_memory = value;
262 self
263 }
264
265 pub fn size(mut self, size: usize) -> Self {
267 self.config.size = size;
268 self
269 }
270
271 pub fn has_ownership(mut self, value: bool) -> Self {
275 self.config.has_ownership = value;
276 self
277 }
278
279 pub fn create(mut self) -> Result<SharedMemory, SharedMemoryCreationError> {
281 let msg = "Unable to create shared memory";
282
283 if self.config.size == 0 {
284 fail!(from self.config, with SharedMemoryCreationError::UnsupportedSizeOfZero,
285 "{msg} since a size of 0 is not supported for a shared memory object.");
286 }
287
288 let shm_created;
289 let mut fd = match self
290 .config
291 .creation_mode
292 .expect("CreationMode must be set on creation")
293 {
294 CreationMode::CreateExclusive => {
295 shm_created = true;
296 SharedMemory::shm_create(&self.config.name, &self.config)?
297 }
298 CreationMode::PurgeAndCreate => {
299 shm_created = true;
300 fail!(from self.config, when SharedMemory::shm_unlink(&self.config.name),
301 "Failed to remove already existing shared memory.");
302 SharedMemory::shm_create(&self.config.name, &self.config)?
303 }
304 CreationMode::OpenOrCreate => {
305 match SharedMemory::shm_open(&self.config.name, &self.config) {
306 Ok(fd) => {
307 shm_created = false;
308 self.config.has_ownership = false;
309 fd
310 }
311 Err(SharedMemoryCreationError::DoesNotExist) => {
312 shm_created = true;
313 match SharedMemory::shm_create(&self.config.name, &self.config) {
314 Ok(fd) => fd,
315 Err(SharedMemoryCreationError::AlreadyExist) => {
316 SharedMemory::shm_open(&self.config.name, &self.config)?
317 }
318 Err(e) => return Err(e),
319 }
320 }
321 Err(v) => return Err(v),
322 }
323 }
324 };
325
326 if !shm_created {
327 let actual_shm_size = fail!(from self.config, when fd.metadata(),
328 "{} since a failure occurred while acquiring the file attributes.", msg)
329 .size();
330 if self.config.size > actual_shm_size as usize {
331 fail!(from self.config, with SharedMemoryCreationError::SizeDoesNotFit,
332 "{} since the actual size {} is not equal to the configured size {}.", msg, actual_shm_size, self.config.size);
333 }
334
335 self.config.size = actual_shm_size as _;
336 let memory_mapping = SharedMemoryBuilder::create_memory_mapping(fd, &self.config)?;
337
338 let shm = SharedMemory {
339 name: self.config.name,
340 has_ownership: AtomicBool::new(self.config.has_ownership),
341 memory_lock: None,
342 memory_mapping,
343 mapping_offset: self.config.mapping_offset,
344 };
345
346 trace!(from shm, "open");
347 return Ok(shm);
348 }
349
350 fail!(from self.config, when fd.truncate(self.config.size), "{} since the shared memory truncation failed.", msg);
351
352 let actual_shm_size = fail!(from self.config, when fd.metadata(),
353 "{} since a failure occurred while acquiring the file attributes.", msg)
354 .size();
355 if (actual_shm_size as usize) < self.config.size {
356 fail!(from self.config, with SharedMemoryCreationError::SizeDoesNotFit,
357 "{} since the actual size {} is less than to the configured size {}.", msg, actual_shm_size, self.config.size);
358 }
359
360 self.config.size = actual_shm_size as _;
361 let memory_mapping = SharedMemoryBuilder::create_memory_mapping(fd, &self.config)?;
362
363 let mut shm = SharedMemory {
364 name: self.config.name,
365 has_ownership: AtomicBool::new(self.config.has_ownership),
366 memory_lock: None,
367 memory_mapping,
368 mapping_offset: self.config.mapping_offset,
369 };
370
371 if self.config.is_memory_locked {
372 shm.memory_lock = Some(
373 fail!(from self.config, when unsafe { MemoryLock::new(shm.memory_mapping.base_address().cast(), shm.memory_mapping.size()) },
374 "{} since the memory lock failed.", msg),
375 )
376 }
377
378 if self.config.zero_memory {
379 if POSIX_SUPPORT_ADVANCED_SIGNAL_HANDLING {
380 let memset_call = || unsafe {
381 posix::memset(
382 shm.memory_mapping.base_address_mut().cast(),
383 0,
384 self.config.size,
385 );
386 };
387 match SignalHandler::call_and_fetch(memset_call) {
388 None => (),
389 Some(v) => {
390 fail!(from self.config, with SharedMemoryCreationError::InsufficientMemory,
391 "{} since a signal {} was raised while zeroing the memory. Is enough memory available on the system?", msg, v);
392 }
393 }
394 } else {
395 unsafe {
396 posix::memset(
397 shm.memory_mapping.base_address_mut().cast(),
398 0,
399 self.config.size,
400 )
401 };
402 }
403 }
404
405 trace!(from shm, "create");
406 Ok(shm)
407 }
408}
409
410#[derive(Debug)]
412pub struct SharedMemory {
413 name: FileName,
414 has_ownership: AtomicBool,
415 memory_mapping: MemoryMapping,
416 memory_lock: Option<MemoryLock>,
417 mapping_offset: isize,
418}
419
420impl Drop for SharedMemory {
421 fn drop(&mut self) {
422 if self.has_ownership() {
423 match self.set_permission(Permission::OWNER_ALL) {
424 Ok(()) => match Self::shm_unlink(&self.name) {
425 Ok(_) => {
426 trace!(from self, "delete");
427 }
428 Err(_) => {
429 error!(from self, "Failed to cleanup shared memory.");
430 }
431 },
432 Err(e) => {
433 error!(from self, "Failed to cleanup shared memory since the permissions could not be adjusted ({:?}).", e);
434 }
435 }
436 }
437 }
438}
439
440impl SharedMemory {
441 pub fn does_exist(name: &FileName) -> bool {
443 let file_path =
444 FilePath::from_path_and_file(&Path::new(&[PATH_SEPARATOR; 1]).unwrap(), name).unwrap();
445 FileDescriptor::new(unsafe {
446 posix::shm_open(
447 file_path.as_c_str(),
448 AccessMode::Read.as_oflag(),
449 Permission::none().as_mode(),
450 )
451 })
452 .is_some()
453 }
454
455 pub fn mapping_offset(&self) -> isize {
457 self.mapping_offset
458 }
459
460 pub fn does_support_persistency() -> bool {
463 POSIX_SUPPORT_PERSISTENT_SHARED_MEMORY
464 }
465
466 pub fn has_ownership(&self) -> bool {
470 self.has_ownership.load(Ordering::Relaxed)
471 }
472
473 pub fn release_ownership(&self) {
476 self.has_ownership.store(false, Ordering::Relaxed)
477 }
478
479 pub fn acquire_ownership(&self) {
482 self.has_ownership.store(true, Ordering::Relaxed)
483 }
484
485 pub fn remove(name: &FileName) -> Result<bool, SharedMemoryRemoveError> {
487 match Self::shm_unlink(name) {
488 Ok(true) => {
489 trace!(from "SharedMemory::remove", "\"{}\"", name);
490 Ok(true)
491 }
492 Ok(false) => Ok(false),
493 Err(v) => Err(v),
494 }
495 }
496
497 pub fn list() -> Vec<FileName> {
499 let mut result = vec![];
500
501 let raw_shm_names = unsafe { posix::shm_list() };
502 for name in &raw_shm_names {
503 if let Ok(f) = unsafe { FileName::from_c_str(name.as_ptr() as *mut _) } {
504 result.push(f)
505 }
506 }
507
508 result
509 }
510
511 pub fn name(&self) -> &FileName {
513 &self.name
514 }
515
516 pub fn base_address(&self) -> NonNull<u8> {
520 match NonNull::new(self.memory_mapping.base_address().cast_mut()) {
521 Some(v) => v,
522 None => {
523 fatal_panic!(from self,
524 "This should never happen! A valid shared memory object should never contain a base address with null value.");
525 }
526 }
527 }
528
529 pub fn size(&self) -> usize {
531 self.memory_mapping.size()
532 }
533
534 pub fn as_slice(&self) -> &[u8] {
536 self.memory_mapping.as_slice()
537 }
538
539 pub fn as_mut_slice(&mut self) -> &mut [u8] {
541 self.memory_mapping.as_mut_slice()
542 }
543
544 fn shm_create(
545 name: &FileName,
546 config: &SharedMemoryBuilder,
547 ) -> Result<FileDescriptor, SharedMemoryCreationError> {
548 let file_path =
549 FilePath::from_path_and_file(&Path::new(&[PATH_SEPARATOR; 1]).unwrap(), name).unwrap();
550 let fd = FileDescriptor::new(unsafe {
551 posix::shm_open(
552 file_path.as_c_str(),
553 CreationMode::CreateExclusive.as_oflag() | config.access_mode.as_oflag(),
554 config.permission.as_mode(),
555 )
556 });
557
558 if let Some(v) = fd {
559 return Ok(v);
560 }
561
562 let msg = "Unable to create shared memory";
563 handle_errno!(SharedMemoryCreationError, from config,
564 Errno::EACCES => (InsufficientPermissions, "{} due to insufficient permissions.", msg),
565 Errno::EINVAL => (InvalidName, "{} since the provided name \"{}\" is invalid.", msg, name),
566 Errno::EEXIST => (AlreadyExist, "{} since it already exists.", msg),
567 Errno::EMFILE => (PerProcessFileHandleLimitReached, "{} since the per-process file handle limit was reached.", msg),
568 Errno::ENFILE => (SystemWideFileHandleLimitReached, "{} since the system-wide file handle limit was reached.", msg),
569 Errno::ENAMETOOLONG => (NameTooLong, "{} since the name exceeds the maximum supported length of {}.", msg, Limit::MaxFileNameLength.value() ),
570 v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
571 );
572 }
573
574 fn shm_open(
575 name: &FileName,
576 config: &SharedMemoryBuilder,
577 ) -> Result<FileDescriptor, SharedMemoryCreationError> {
578 let file_path =
579 FilePath::from_path_and_file(&Path::new(&[PATH_SEPARATOR; 1]).unwrap(), name).unwrap();
580 let fd = FileDescriptor::new(unsafe {
581 posix::shm_open(
582 file_path.as_c_str(),
583 config.access_mode.as_oflag(),
584 Permission::none().as_mode(),
585 )
586 });
587
588 if let Some(v) = fd {
589 return Ok(v);
590 }
591
592 let msg = "Unable to open shared memory";
593 handle_errno!(SharedMemoryCreationError, from config,
594 Errno::ENOENT => (DoesNotExist, "{} since the shared memory does not exist.", msg),
595 Errno::EACCES => (InsufficientPermissions, "{} due to insufficient permissions.", msg),
596 Errno::EINVAL => (InvalidName, "{} since the provided name \"{}\" is invalid.", msg, name),
597 Errno::EMFILE => (PerProcessFileHandleLimitReached, "{} since the per-process file handle limit was reached.", msg),
598 Errno::ENFILE => (SystemWideFileHandleLimitReached, "{} since the system-wide file handle limit was reached.", msg),
599 Errno::ENAMETOOLONG => (NameTooLong, "{} since the name exceeds the maximum supported length of {}.", msg, Limit::MaxFileNameLength.value() ),
600 v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
601 );
602 }
603
604 fn shm_unlink(name: &FileName) -> Result<bool, SharedMemoryRemoveError> {
605 let file_path =
606 FilePath::from_path_and_file(&Path::new(&[PATH_SEPARATOR; 1]).unwrap(), name).unwrap();
607 if unsafe { posix::shm_unlink(file_path.as_c_str()) } == 0 {
608 return Ok(true);
609 }
610
611 let msg = "Unable to remove shared memory device file";
612 let origin = "SharedMemory::unlink()";
613 match posix::Errno::get() {
614 posix::Errno::EACCES => {
615 fail!(from origin, with SharedMemoryRemoveError::InsufficientPermissions,
616 "{} \"{}\" due to insufficient permissions.", msg, name);
617 }
618 posix::Errno::ENOENT => Ok(false),
619 v => {
620 fail!(from origin, with SharedMemoryRemoveError::UnknownError(v as i32),
621 "{} \"{}\" since an unknown error occurred ({}).", msg, name, v);
622 }
623 }
624 }
625}
626
627impl FileDescriptorBased for SharedMemory {
628 fn file_descriptor(&self) -> &FileDescriptor {
629 self.memory_mapping
630 .file_descriptor()
631 .as_ref()
632 .expect("Memory mapping is based on a file descriptor")
633 }
634}
635
636impl FileDescriptorManagement for SharedMemory {}