1use alloc::format;
139use core::fmt::Debug;
140
141pub use iceoryx2_bb_container::semantic_string::SemanticString;
142pub use iceoryx2_bb_system_types::file_path::FilePath;
143
144use iceoryx2_bb_concurrency::cell::Cell;
145use iceoryx2_bb_container::semantic_string::SemanticStringError;
146use iceoryx2_bb_elementary::enum_gen;
147use iceoryx2_log::{fail, trace};
148use iceoryx2_pal_posix::posix::{self, Errno, MemZeroedStruct};
149
150use crate::{
151 access_mode::AccessMode,
152 directory::{Directory, DirectoryAccessError, DirectoryCreateError},
153 file::{File, FileBuilder, FileCreationError, FileOpenError, FileRemoveError},
154 file_descriptor::{FileDescriptorBased, FileDescriptorManagement},
155 file_lock::LockType,
156 permission::Permission,
157 unix_datagram_socket::CreationMode,
158};
159
160#[derive(Debug, Clone, Copy, Eq, PartialEq)]
162pub enum ProcessState {
163 Alive,
164 Dead,
165 DoesNotExist,
166 Starting,
167 CleaningUp,
168}
169
170#[derive(Debug, Clone, Copy, Eq, PartialEq)]
172pub enum ProcessGuardCreateError {
173 InsufficientPermissions,
174 IsDirectory,
175 InvalidDirectory,
176 AlreadyExists,
177 NoSpaceLeft,
178 ReadOnlyFilesystem,
179 ContractViolation,
180 Interrupt,
181 InvalidCleanerPathName,
182 UnknownError(i32),
183}
184
185#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
186enum ProcessGuardLockError {
187 OwnedByAnotherProcess,
188 Interrupt,
189 UnknownError(i32),
190}
191
192enum_gen! {
193ProcessGuardRemoveError
195 entry:
196 InsufficientPermissions,
197 Interrupt,
198 OwnedByAnotherProcess,
199 InvalidCleanerPathName,
200 UnknownError(i32)
201 mapping:
202 FileRemoveError
203}
204
205#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
207pub enum ProcessMonitorCreateError {
208 InsufficientPermissions,
209 Interrupt,
210 IsDirectory,
211 InvalidCleanerPathName,
212 UnknownError,
213}
214
215enum_gen! {
216ProcessMonitorStateError
218 entry:
219 CorruptedState,
220 Interrupt,
221 UnknownError(i32)
222
223 mapping:
224 ProcessMonitorCreateError
225}
226
227#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
229pub enum ProcessCleanerCreateError {
230 ProcessIsStillAlive,
231 OwnedByAnotherProcess,
232 Interrupt,
233 FailedToAcquireLockState,
234 UnableToOpenStateFile,
235 UnableToOpenCleanerFile,
236 InvalidCleanerPathName,
237 DoesNotExist,
238 UnknownError,
239}
240
241#[derive(Debug)]
258pub struct ProcessGuardBuilder {
259 directory_permissions: Permission,
260 guard_permissions: Permission,
261}
262
263impl Default for ProcessGuardBuilder {
264 fn default() -> Self {
265 Self::new()
266 }
267}
268
269impl ProcessGuardBuilder {
270 pub fn new() -> Self {
272 Self {
273 directory_permissions: Permission::OWNER_ALL
274 | Permission::GROUP_READ
275 | Permission::GROUP_EXEC
276 | Permission::OTHERS_READ
277 | Permission::OTHERS_EXEC,
278 guard_permissions: Permission::OWNER_ALL,
279 }
280 }
281
282 pub fn directory_permissions(mut self, value: Permission) -> Self {
286 self.directory_permissions = value;
287 self
288 }
289
290 pub fn guard_permissions(mut self, value: Permission) -> Self {
292 self.guard_permissions = value;
293 self
294 }
295
296 pub fn create(self, path: &FilePath) -> Result<ProcessGuard, ProcessGuardCreateError> {
312 let origin = "ProcessGuard::new()";
313 let msg = format!("Unable to create new ProcessGuard with the file \"{path}\"");
314
315 let owner_lock_path = match generate_owner_lock_path(path) {
316 Ok(f) => f,
317 Err(e) => {
318 fail!(from origin, with ProcessGuardCreateError::InvalidCleanerPathName,
319 "{} since the corresponding owner_lock path name would be invalid ({:?}).", msg, e);
320 }
321 };
322
323 fail!(from origin, when Self::create_directory(path, self.directory_permissions),
324 "{} since the directory \"{}\" of the process guard could not be created", msg, path);
325
326 let owner_lock_file = fail!(from origin, when Self::create_file(&owner_lock_path, self.guard_permissions),
327 "{} since the owner_lock file \"{}\" could not be created.", msg, owner_lock_path);
328 let mut file = fail!(from origin, when Self::create_file(path, INIT_PERMISSION),
329 "{} since the state file \"{}\" could not be created.", msg, path);
330
331 match Self::lock_state_file(&file) {
332 Ok(()) => (),
333 Err(lock_error) => match lock_error {
334 ProcessGuardLockError::Interrupt => {
335 fail!(from origin, with ProcessGuardCreateError::Interrupt,
336 "{} since an interrupt signal was received while locking the file.", msg);
337 }
338 ProcessGuardLockError::OwnedByAnotherProcess => {
339 fail!(from origin, with ProcessGuardCreateError::ContractViolation,
340 "{} since the another process holds the lock of a process state that is in initialization.", msg);
341 }
342 ProcessGuardLockError::UnknownError(v) => {
343 fail!(from origin, with ProcessGuardCreateError::UnknownError(v),
344 "{} since an unknown failure occurred while locking the file ({:?}).", msg, v);
345 }
346 },
347 };
348
349 match file.set_permission(self.guard_permissions) {
350 Ok(_) => {
351 trace!(from "ProcessGuard::new()", "create process state \"{}\" for monitoring", path);
352 Ok(ProcessGuard {
353 file,
354 owner_lock_file,
355 })
356 }
357 Err(v) => {
358 fail!(from origin, with ProcessGuardCreateError::UnknownError(0),
359 "{} since the final permissions could not be applied due to an internal failure ({:?}).", msg, v);
360 }
361 }
362 }
363
364 fn create_directory(
365 path: &FilePath,
366 permissions: Permission,
367 ) -> Result<(), ProcessGuardCreateError> {
368 let origin = "ProcessGuard::create_directory()";
369 let msg = format!(
370 "Unable to create directory \"{}\" for new ProcessGuard state with the file \"{}\"",
371 path.path(),
372 path
373 );
374
375 let dir_path = path.path();
376
377 if dir_path.is_empty() {
378 return Ok(());
379 }
380
381 match Directory::does_exist(&dir_path) {
382 Ok(true) => Ok(()),
383 Ok(false) => match Directory::create(&dir_path, permissions) {
384 Ok(_) | Err(DirectoryCreateError::DirectoryAlreadyExists) => Ok(()),
385 Err(DirectoryCreateError::InsufficientPermissions) => {
386 fail!(from origin, with ProcessGuardCreateError::InsufficientPermissions,
387 "{} since the directory {} could not be created due to insufficient permissions.",
388 msg, dir_path);
389 }
390 Err(DirectoryCreateError::ReadOnlyFilesystem) => {
391 fail!(from origin, with ProcessGuardCreateError::ReadOnlyFilesystem,
392 "{} since the directory {} could not be created since it is located on an read-only file system.",
393 msg, dir_path);
394 }
395 Err(DirectoryCreateError::NoSpaceLeft) => {
396 fail!(from origin, with ProcessGuardCreateError::NoSpaceLeft,
397 "{} since the directory {} could not be created since there is no space left.",
398 msg, dir_path);
399 }
400 Err(v) => {
401 fail!(from origin, with ProcessGuardCreateError::NoSpaceLeft,
402 "{} since the directory {} could not be created due to an unknown failure ({:?}).",
403 msg, dir_path, v);
404 }
405 },
406 Err(DirectoryAccessError::InsufficientPermissions) => {
407 fail!(from origin, with ProcessGuardCreateError::InsufficientPermissions,
408 "{} since the directory {} could not be accessed due to insufficient permissions.",
409 msg, dir_path);
410 }
411 Err(DirectoryAccessError::PathPrefixIsNotADirectory) => {
412 fail!(from origin, with ProcessGuardCreateError::InvalidDirectory,
413 "{} since the directory {} is actually not a valid directory.", msg, dir_path);
414 }
415 Err(v) => {
416 fail!(from origin, with ProcessGuardCreateError::UnknownError(0),
417 "{} since an unknown failure occurred ({:?}) while checking if directory {} exists.",
418 msg, v, dir_path);
419 }
420 }
421 }
422
423 fn create_file(
424 path: &FilePath,
425 permission: Permission,
426 ) -> Result<File, ProcessGuardCreateError> {
427 let origin = "ProcessGuard::file()";
428 let msg = format!("Unable to create new ProcessGuard state file \"{path}\"");
429
430 match FileBuilder::new(path)
431 .has_ownership(true)
432 .creation_mode(CreationMode::CreateExclusive)
433 .permission(permission)
434 .create()
435 {
436 Ok(f) => Ok(f),
437 Err(FileCreationError::InsufficientPermissions) => {
438 fail!(from origin, with ProcessGuardCreateError::InsufficientPermissions,
439 "{} due to insufficient permissions.", msg);
440 }
441 Err(FileCreationError::FileAlreadyExists) => {
442 fail!(from origin, with ProcessGuardCreateError::AlreadyExists,
443 "{} since the underlying file already exists.", msg);
444 }
445 Err(FileCreationError::IsDirectory) => {
446 fail!(from origin, with ProcessGuardCreateError::IsDirectory,
447 "{} since the path is a directory.", msg);
448 }
449 Err(FileCreationError::NoSpaceLeft) => {
450 fail!(from origin, with ProcessGuardCreateError::NoSpaceLeft,
451 "{} since there is no space left on the device.", msg);
452 }
453 Err(FileCreationError::FilesytemIsReadOnly) => {
454 fail!(from origin, with ProcessGuardCreateError::ReadOnlyFilesystem,
455 "{} since the file system is read only.", msg);
456 }
457 Err(v) => {
458 fail!(from origin, with ProcessGuardCreateError::UnknownError(0),
459 "{} due to an internal failure ({:?}).", msg, v);
460 }
461 }
462 }
463
464 fn lock_state_file(file: &File) -> Result<(), ProcessGuardLockError> {
465 let msg = format!("Unable to lock process state file {file:?}");
466 let mut new_lock_state = posix::flock::new_zeroed();
467 new_lock_state.l_type = LockType::Write as _;
468 new_lock_state.l_whence = posix::SEEK_SET as _;
469
470 if unsafe {
471 posix::fcntl(
472 file.file_descriptor().native_handle(),
473 posix::F_SETLK,
474 &mut new_lock_state,
475 )
476 } != -1
477 {
478 return Ok(());
479 }
480
481 handle_errno!(ProcessGuardLockError, from "ProcessState::lock_state_file()",
482 Errno::EACCES => (OwnedByAnotherProcess, "{} since the lock is owned by another process.", msg),
483 Errno::EAGAIN => (OwnedByAnotherProcess, "{} since the lock is owned by another process.", msg),
484 Errno::EINTR => (Interrupt, "{} since an interrupt signal was received.", msg),
485 v => (UnknownError(v as i32), "{} due to an unknown failure (errno code: {}).", msg, v)
486 );
487 }
488}
489
490#[derive(Debug)]
509pub struct ProcessGuard {
510 file: File,
511 owner_lock_file: File,
512}
513
514const INIT_PERMISSION: Permission = Permission::OWNER_WRITE;
515const OWNER_LOCK_SUFFIX: &[u8] = b"_owner_lock";
516
517fn generate_owner_lock_path(path: &FilePath) -> Result<FilePath, SemanticStringError> {
518 let mut owner_lock_path = *path;
519 owner_lock_path.push_bytes(OWNER_LOCK_SUFFIX)?;
520 Ok(owner_lock_path)
521}
522
523impl ProcessGuard {
524 pub unsafe fn remove(file: &FilePath) -> Result<bool, FileRemoveError> {
533 let msg = "Unable to remove process guard resources";
534 let origin = "ProcessGuard::remove()";
535 let owner_lock_path = match generate_owner_lock_path(file) {
536 Ok(v) => v,
537 Err(e) => {
538 fail!(from origin,
539 with FileRemoveError::MaxSupportedPathLengthExceeded,
540 "{msg} since the owner lock path exceeds the maximum supported path length ({e:?})."
541 );
542 }
543 };
544
545 let mut result = match File::remove(file) {
546 Ok(v) => v,
547 Err(e) => {
548 fail!(from origin, with e,
549 "{msg} since the underlying file \"{file}\" could not be removed.");
550 }
551 };
552
553 result &= match File::remove(&owner_lock_path) {
554 Ok(v) => v,
555 Err(e) => {
556 fail!(from origin, with e,
557 "{msg} since the underlying owner lock file \"{owner_lock_path}\" could not be removed.");
558 }
559 };
560
561 Ok(result)
562 }
563
564 pub fn path(&self) -> &FilePath {
566 match self.file.path() {
567 Some(path) => path,
568 None => {
569 unreachable!()
570 }
571 }
572 }
573
574 pub(crate) fn staged_death(self) {
575 self.file.release_ownership();
576 self.owner_lock_file.release_ownership();
577 }
578}
579
580pub struct ProcessMonitor {
630 file: Cell<Option<File>>,
631 path: FilePath,
632 owner_lock_path: FilePath,
633}
634
635impl Debug for ProcessMonitor {
636 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
637 write!(
638 f,
639 "ProcessMonitor {{ file = {:?}, path = {:?}}}",
640 unsafe { &*self.file.as_ptr() },
641 self.path
642 )
643 }
644}
645
646impl ProcessMonitor {
647 pub fn new(path: &FilePath) -> Result<Self, ProcessMonitorCreateError> {
662 let msg = format!("Unable to open process monitor \"{path}\"");
663 let origin = "ProcessMonitor::new()";
664 let owner_lock_path = match generate_owner_lock_path(path) {
665 Ok(f) => f,
666 Err(e) => {
667 fail!(from origin, with ProcessMonitorCreateError::InvalidCleanerPathName,
668 "{} since the corresponding owner_lock path name would be invalid ({:?}).", msg, e);
669 }
670 };
671
672 let new_self = Self {
673 file: Cell::new(None),
674 path: *path,
675 owner_lock_path,
676 };
677
678 new_self.file.set(Self::open_file(&new_self.path)?);
679 Ok(new_self)
680 }
681
682 pub fn path(&self) -> &FilePath {
684 &self.path
685 }
686
687 pub fn state(&self) -> Result<ProcessState, ProcessMonitorStateError> {
722 let msg = "Unable to acquire ProcessState";
723 match unsafe { &*self.file.as_ptr() } {
724 Some(_) => self.read_state_from_file(),
725 None => match File::does_exist(&self.path) {
726 Ok(true) => {
727 self.file.set(Self::open_file(&self.path)?);
728 self.read_state_from_file()
729 }
730 Ok(false) => Ok(ProcessState::DoesNotExist),
731 Err(v) => {
732 fail!(from self, with ProcessMonitorStateError::UnknownError(0),
733 "{} since an unknown failure occurred while checking if the file exists ({:?}).", msg, v);
734 }
735 },
736 }
737 }
738
739 fn get_lock_state(file: &File) -> Result<i64, ProcessMonitorStateError> {
740 let msg = format!("Unable to acquire lock on file {file:?}");
741 let mut current_state = posix::flock::new_zeroed();
742 current_state.l_type = LockType::Write as _;
743
744 if unsafe {
745 posix::fcntl(
746 file.file_descriptor().native_handle(),
747 posix::F_GETLK,
748 &mut current_state,
749 )
750 } == -1
751 {
752 handle_errno!(ProcessMonitorStateError, from "ProcessMonitor::get_lock_state()",
753 Errno::EINTR => (Interrupt, "{} since an interrupt signal was received.", msg),
754 v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
755 )
756 }
757
758 Ok(current_state.l_type as _)
759 }
760
761 fn read_state_from_file(&self) -> Result<ProcessState, ProcessMonitorStateError> {
762 let file = match unsafe { &*self.file.as_ptr() } {
763 Some(ref f) => f,
764 None => return Ok(ProcessState::Starting),
765 };
766
767 let msg = format!("Unable to read state from file {file:?}");
768 let lock_state = fail!(from self, when Self::get_lock_state(file),
769 "{} since the lock state of the state file could not be acquired.", msg);
770
771 match lock_state as _ {
772 posix::F_WRLCK => Ok(ProcessState::Alive),
773 _ => match File::does_exist(&self.path) {
774 Ok(true) => match file.permission() {
775 Ok(INIT_PERMISSION) => Ok(ProcessState::Starting),
776 Err(_) | Ok(_) => {
777 self.file.set(None);
778 match Self::open_file(&self.owner_lock_path)? {
779 Some(f) => {
780 let lock_state = fail!(from self, when Self::get_lock_state(&f),
781 "{} since the lock state of the owner_lock file could not be acquired.", msg);
782 if lock_state == posix::F_WRLCK as _ {
783 return Ok(ProcessState::CleaningUp);
784 }
785
786 Ok(ProcessState::Dead)
787 }
788 None => match File::does_exist(&self.path) {
789 Ok(true) => {
790 fail!(from self, with ProcessMonitorStateError::CorruptedState,
791 "{} since the corresponding owner_lock file \"{}\" does not exist. This indicates a corrupted state.",
792 msg, self.owner_lock_path);
793 }
794 Ok(false) => {
795 self.file.set(None);
796 Ok(ProcessState::DoesNotExist)
797 }
798 Err(v) => {
799 fail!(from self, with ProcessMonitorStateError::UnknownError(0),
800 "{} since an unknown failure occurred while checking if the process state file exists ({:?}).", msg, v);
801 }
802 },
803 }
804 }
805 },
806 Ok(false) => {
807 self.file.set(None);
808 Ok(ProcessState::DoesNotExist)
809 }
810 Err(v) => {
811 fail!(from self, with ProcessMonitorStateError::UnknownError(0),
812 "{} since an unknown failure occurred while checking if the process state file exists ({:?}).", msg, v);
813 }
814 },
815 }
816 }
817
818 fn open_file(path: &FilePath) -> Result<Option<File>, ProcessMonitorCreateError> {
819 let origin = "ProcessMonitor::new()";
820 let msg = format!("Unable to open ProcessMonitor state file \"{path}\"");
821
822 match FileBuilder::new(path).open_existing(AccessMode::Write) {
823 Ok(f) => Ok(Some(f)),
824 Err(FileOpenError::FileDoesNotExist) => Ok(None),
825 Err(FileOpenError::IsDirectory) => {
826 fail!(from origin, with ProcessMonitorCreateError::IsDirectory,
827 "{} since the path is a directory.", msg);
828 }
829 Err(FileOpenError::InsufficientPermissions) => {
830 fail!(from origin, with ProcessMonitorCreateError::InsufficientPermissions,
831 "{} due to insufficient permissions.", msg);
832 }
833 Err(FileOpenError::Interrupt) => {
834 fail!(from origin, with ProcessMonitorCreateError::Interrupt,
835 "{} since an interrupt signal was received.", msg);
836 }
837 Err(v) => {
838 fail!(from origin, with ProcessMonitorCreateError::UnknownError,
839 "{} since an unknown failure occurred ({:?}).", msg, v);
840 }
841 }
842 }
843}
844
845#[derive(Debug)]
864pub struct ProcessCleaner {
865 file: File,
866 owner_lock_file: File,
867}
868
869impl ProcessCleaner {
870 pub fn new(path: &FilePath) -> Result<Self, ProcessCleanerCreateError> {
874 let msg = format!("Unable to instantiate ProcessCleaner \"{path}\"");
875 let origin = "ProcessCleaner::new()";
876 let owner_lock_path = match generate_owner_lock_path(path) {
877 Ok(f) => f,
878 Err(e) => {
879 fail!(from origin, with ProcessCleanerCreateError::InvalidCleanerPathName,
880 "{} since the corresponding owner_lock path name would be invalid ({:?}).", msg, e);
881 }
882 };
883
884 let owner_lock_file = match fail!(from origin, when ProcessMonitor::open_file(&owner_lock_path),
885 with ProcessCleanerCreateError::UnableToOpenCleanerFile,
886 "{} since the owner_lock file could not be opened.", msg)
887 {
888 Some(f) => f,
889 None => {
890 fail!(from origin, with ProcessCleanerCreateError::DoesNotExist,
891 "{} since the process owner_lock file does not exist.", msg);
892 }
893 };
894
895 let file = match fail!(from origin, when ProcessMonitor::open_file(path),
896 with ProcessCleanerCreateError::UnableToOpenStateFile,
897 "{} since the state file could not be opened.", msg)
898 {
899 Some(f) => f,
900 None => {
901 fail!(from origin, with ProcessCleanerCreateError::DoesNotExist,
902 "{} since the process state file does not exist.", msg);
903 }
904 };
905
906 let lock_state = fail!(from origin, when ProcessMonitor::get_lock_state(&file),
907 with ProcessCleanerCreateError::FailedToAcquireLockState,
908 "{} since the lock state could not be acquired.", msg);
909
910 if lock_state == posix::F_WRLCK as _ {
911 fail!(from origin, with ProcessCleanerCreateError::ProcessIsStillAlive,
912 "{} since the corresponding process is still alive.", msg);
913 }
914
915 match ProcessGuardBuilder::lock_state_file(&owner_lock_file) {
916 Ok(()) => {
917 file.acquire_ownership();
918 owner_lock_file.acquire_ownership();
919 Ok(Self {
920 file,
921 owner_lock_file,
922 })
923 }
924 Err(ProcessGuardLockError::OwnedByAnotherProcess) => {
925 fail!(from origin, with ProcessCleanerCreateError::OwnedByAnotherProcess,
926 "{} since another process already has instantiated a ProcessCleaner.", msg);
927 }
928 Err(ProcessGuardLockError::Interrupt) => {
929 fail!(from origin, with ProcessCleanerCreateError::Interrupt,
930 "{} since an interrupt signal was received.", msg);
931 }
932 Err(e) => {
933 fail!(from origin, with ProcessCleanerCreateError::UnknownError,
934 "{} due to an unknown failure ({:?}).", msg, e);
935 }
936 }
937 }
938
939 pub fn abandon(self) {
943 self.file.release_ownership();
944 self.owner_lock_file.release_ownership();
945 }
946}