Skip to main content

iceoryx2_cal/monitoring/
file_lock.rs

1// Copyright (c) 2024 Contributors to the Eclipse Foundation
2//
3// See the NOTICE file(s) distributed with this work for additional
4// information regarding copyright ownership.
5//
6// This program and the accompanying materials are made available under the
7// terms of the Apache Software License 2.0 which is available at
8// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license
9// which is available at https://opensource.org/licenses/MIT.
10//
11// SPDX-License-Identifier: Apache-2.0 OR MIT
12
13use alloc::format;
14use alloc::vec;
15use alloc::vec::Vec;
16use core::ptr::NonNull;
17
18use iceoryx2_bb_elementary_traits::non_null::NonNullCompat;
19use iceoryx2_bb_elementary_traits::testing::abandonable::Abandonable;
20use iceoryx2_bb_posix::file::Permission;
21use iceoryx2_bb_posix::process_state::ProcessGuardBuilder;
22use iceoryx2_bb_posix::process_state::ProcessMonitorOpenError;
23use iceoryx2_bb_posix::{
24    directory::{Directory, DirectoryOpenError, DirectoryReadError},
25    file::{File, FileRemoveError},
26    file_type::FileType,
27    process_state::{
28        ProcessCleaner, ProcessCleanerCreateError, ProcessGuard, ProcessGuardCreateError,
29        ProcessMonitor, ProcessMonitorCreateError, ProcessMonitorStateError, ProcessState,
30    },
31};
32use iceoryx2_bb_system_types::{file_name::FileName, path::Path};
33use iceoryx2_log::fail;
34
35use crate::{
36    monitoring::{MonitoringCreateCleanerError, MonitoringCreateMonitorError, State},
37    named_concept::{
38        NamedConcept, NamedConceptBuilder, NamedConceptConfiguration, NamedConceptDoesExistError,
39        NamedConceptListError, NamedConceptMgmt, NamedConceptRemoveError,
40    },
41};
42
43use super::{
44    Monitoring, MonitoringBuilder, MonitoringCleaner, MonitoringCreateTokenError,
45    MonitoringMonitor, MonitoringStateError, MonitoringToken,
46};
47
48#[cfg(not(feature = "dev_permissions"))]
49const GUARD_PERMISSIONS: Permission = Permission::OWNER_ALL;
50
51#[cfg(not(feature = "dev_permissions"))]
52const DIR_PERMISSIONS: Permission = Permission::OWNER_ALL
53    .const_bitor(Permission::GROUP_READ)
54    .const_bitor(Permission::GROUP_EXEC)
55    .const_bitor(Permission::OTHERS_READ)
56    .const_bitor(Permission::OTHERS_EXEC);
57
58#[cfg(feature = "dev_permissions")]
59const GUARD_PERMISSIONS: Permission = Permission::ALL;
60
61#[cfg(feature = "dev_permissions")]
62const DIR_PERMISSIONS: Permission = Permission::ALL;
63
64#[derive(Debug)]
65pub struct FileLockMonitoring {}
66
67impl NamedConceptMgmt for FileLockMonitoring {
68    type Configuration = Configuration;
69
70    fn list_cfg(
71        cfg: &Self::Configuration,
72    ) -> Result<Vec<FileName>, crate::named_concept::NamedConceptListError> {
73        let path = cfg.get_path_hint();
74        let origin = "FileLockMonitoring::list_cfg()";
75        let msg = format!("Unable to list all FileLockMonitoring instances in \"{path}\"");
76        let directory = match Directory::new(path) {
77            Ok(directory) => directory,
78            Err(DirectoryOpenError::InsufficientPermissions) => {
79                fail!(from origin, with NamedConceptListError::InsufficientPermissions,
80                    "{} due to insufficient permissions to read the directory.", msg);
81            }
82            Err(DirectoryOpenError::DoesNotExist) => {
83                return Ok(vec![]);
84            }
85            Err(v) => {
86                fail!(from origin, with NamedConceptListError::InternalError,
87                    "{} due to failure ({:?}) while reading the directory.", msg, v);
88            }
89        };
90
91        let entries = fail!(from origin,
92                            when directory.contents(),
93                            map DirectoryReadError::InsufficientPermissions => NamedConceptListError::InsufficientPermissions,
94                            unmatched NamedConceptListError::InternalError,
95                            "{} due to a failure while reading the directory contents.", msg);
96
97        Ok(entries
98            .iter()
99            .filter(|entry| {
100                let metadata = entry.metadata();
101                metadata.file_type() == FileType::File
102            })
103            .filter_map(|entry| cfg.extract_name_from_file(entry.name()))
104            .collect())
105    }
106
107    fn does_exist_cfg(
108        name: &FileName,
109        cfg: &Self::Configuration,
110    ) -> Result<bool, crate::named_concept::NamedConceptDoesExistError> {
111        let process_state_path = cfg.path_for(name);
112        let msg =
113            format!("Unable to check if the FileLockMonitoring \"{process_state_path}\" exists");
114        let origin = "FileLockMonitoring::does_exist_cfg()";
115
116        match File::does_exist(&process_state_path) {
117            Ok(v) => Ok(v),
118            Err(e) => {
119                fail!(from origin, with NamedConceptDoesExistError::InternalError,
120                    "{} due to an internal failure ({:?}).", msg, e);
121            }
122        }
123    }
124
125    unsafe fn remove_cfg(
126        name: &FileName,
127        cfg: &Self::Configuration,
128    ) -> Result<bool, crate::named_concept::NamedConceptRemoveError> {
129        let process_state_path = cfg.path_for(name);
130        let msg = format!("Unable to remove FileLockMonitoring \"{process_state_path}\"");
131        let origin = "FileLockMonitoring::remove_cfg()";
132        match unsafe { ProcessGuard::remove(&process_state_path) } {
133            Ok(v) => Ok(v),
134            Err(FileRemoveError::InsufficientPermissions) => {
135                fail!(from origin, with NamedConceptRemoveError::InsufficientPermissions,
136                        "{} due to insufficient permissions.", msg);
137            }
138            Err(v) => {
139                fail!(from origin, with NamedConceptRemoveError::InternalError,
140                        "{} due to an internal failure ({:?}).", msg, v);
141            }
142        }
143    }
144
145    fn remove_path_hint(
146        value: &Path,
147    ) -> Result<(), crate::named_concept::NamedConceptPathHintRemoveError> {
148        crate::named_concept::remove_path_hint(value)
149    }
150}
151
152#[derive(Debug)]
153pub struct Cleaner {
154    cleaner: ProcessCleaner,
155    name: FileName,
156}
157
158impl Abandonable for Cleaner {
159    unsafe fn abandon_in_place(mut this: NonNull<Self>) {
160        let this = unsafe { this.as_mut() };
161        unsafe { ProcessCleaner::abandon_in_place(NonNull::iox2_from_mut(&mut this.cleaner)) };
162    }
163}
164
165impl NamedConcept for Cleaner {
166    fn name(&self) -> &FileName {
167        &self.name
168    }
169}
170
171impl MonitoringCleaner for Cleaner {
172    fn relinquish(self) {
173        self.cleaner.abandon()
174    }
175}
176
177#[derive(Debug)]
178pub struct Token {
179    guard: ProcessGuard,
180    name: FileName,
181}
182
183impl Abandonable for Token {
184    unsafe fn abandon_in_place(mut this: NonNull<Self>) {
185        let this = unsafe { this.as_mut() };
186        unsafe { ProcessGuard::abandon_in_place(NonNull::iox2_from_mut(&mut this.guard)) };
187    }
188}
189
190impl NamedConcept for Token {
191    fn name(&self) -> &FileName {
192        &self.name
193    }
194}
195
196impl MonitoringToken for Token {}
197
198#[derive(Debug)]
199pub struct Monitor {
200    monitor: ProcessMonitor,
201    name: FileName,
202}
203
204impl NamedConcept for Monitor {
205    fn name(&self) -> &FileName {
206        &self.name
207    }
208}
209
210impl MonitoringMonitor for Monitor {
211    fn state(&self) -> Result<super::State, MonitoringStateError> {
212        let msg = "Unable to acquire monitor state";
213
214        match self.monitor.state() {
215            Ok(ProcessState::Alive) => Ok(State::Alive),
216            Ok(ProcessState::Dead) | Ok(ProcessState::CleaningUp) => Ok(State::Dead),
217            Ok(ProcessState::DoesNotExist) | Ok(ProcessState::Starting) => Ok(State::DoesNotExist),
218            Err(ProcessMonitorStateError::Interrupt)
219            | Err(ProcessMonitorStateError::ProcessMonitorOpenError(
220                ProcessMonitorOpenError::Interrupt,
221            )) => {
222                fail!(from self, with MonitoringStateError::Interrupt,
223                    "{} since an interrupt signal was received.", msg);
224            }
225            Err(ProcessMonitorStateError::ProcessMonitorOpenError(
226                ProcessMonitorOpenError::InsufficientPermissions,
227            )) => {
228                fail!(from self, with MonitoringStateError::InsufficientPermissions,
229                    "{} due to insufficient permissions.", msg);
230            }
231            Err(v) => {
232                fail!(from self, with MonitoringStateError::InternalError,
233                    "{} since an internal failure occurred ({:?}).", msg, v);
234            }
235        }
236    }
237}
238
239#[derive(Debug)]
240pub struct Builder {
241    name: FileName,
242    config: Configuration,
243}
244
245impl NamedConceptBuilder<FileLockMonitoring> for Builder {
246    fn new(name: &FileName) -> Self {
247        Self {
248            name: *name,
249            config: Configuration::default(),
250        }
251    }
252
253    fn config(mut self, config: &<FileLockMonitoring as NamedConceptMgmt>::Configuration) -> Self {
254        self.config = config.clone();
255        self
256    }
257}
258
259impl MonitoringBuilder<FileLockMonitoring> for Builder {
260    fn token(
261        self,
262    ) -> Result<<FileLockMonitoring as super::Monitoring>::Token, super::MonitoringCreateTokenError>
263    {
264        let msg = "Unable to create FileLockMonitoring token";
265        let process_state_path = self.config.path_for(&self.name);
266        match ProcessGuardBuilder::new()
267            .guard_permissions(GUARD_PERMISSIONS)
268            .directory_permissions(DIR_PERMISSIONS)
269            .create(&process_state_path)
270        {
271            Ok(guard) => Ok(Token {
272                guard,
273                name: self.name,
274            }),
275            Err(ProcessGuardCreateError::InsufficientPermissions) => {
276                fail!(from self, with MonitoringCreateTokenError::InsufficientPermissions,
277                    "{} due to insufficient permissions.", msg);
278            }
279            Err(ProcessGuardCreateError::AlreadyExists) => {
280                fail!(from self, with MonitoringCreateTokenError::AlreadyExists,
281                    "{} since it already exists.", msg);
282            }
283            Err(ProcessGuardCreateError::SystemCorrupted) => {
284                fail!(from self, with MonitoringCreateTokenError::SystemCorrupted,
285                    "{} since another process corrupted the process guard while it was created.", msg);
286            }
287            Err(v) => {
288                fail!(from self, with MonitoringCreateTokenError::InternalError,
289                    "{} due to an internal failure ({:?}).", msg, v);
290            }
291        }
292    }
293
294    fn monitor(
295        self,
296    ) -> Result<
297        <FileLockMonitoring as super::Monitoring>::Monitor,
298        super::MonitoringCreateMonitorError,
299    > {
300        let msg = "Unable to acquire monitor";
301        let process_state_path = self.config.path_for(&self.name);
302        match ProcessMonitor::new(&process_state_path) {
303            Ok(monitor) => Ok(Monitor {
304                monitor,
305                name: self.name,
306            }),
307            Err(ProcessMonitorCreateError::InvalidCleanerPathName) => {
308                fail!(from self, with MonitoringCreateMonitorError::ConceptNameNotSupportedOnPlatform,
309                    "{} since the concept name \"{}\" results in a concept name that is not supported on this platform.", msg, self.name);
310            }
311        }
312    }
313
314    fn cleaner(
315        self,
316    ) -> Result<<FileLockMonitoring as Monitoring>::Cleaner, super::MonitoringCreateCleanerError>
317    {
318        let msg = "Unable to acquire cleaner";
319        let process_state_path = self.config.path_for(&self.name);
320        match ProcessCleaner::new(&process_state_path) {
321            Ok(cleaner) => Ok(Cleaner {
322                cleaner,
323                name: self.name,
324            }),
325            Err(ProcessCleanerCreateError::Interrupt) => {
326                fail!(from self, with MonitoringCreateCleanerError::Interrupt,
327                    "{} since an interrupt signal was received.", msg);
328            }
329            Err(ProcessCleanerCreateError::ProcessIsStillAlive) => {
330                fail!(from self, with MonitoringCreateCleanerError::InstanceStillAlive,
331                    "{} since the instance is still alive.", msg);
332            }
333            Err(ProcessCleanerCreateError::OwnedByAnotherProcess) => {
334                fail!(from self, with MonitoringCreateCleanerError::AlreadyOwnedByAnotherInstance,
335                    "{} since another instance already acquired the cleaner.", msg);
336            }
337            Err(ProcessCleanerCreateError::ProcessIsBeingCleanedUpOrCrashedDuringCleanup) => {
338                fail!(from self, with MonitoringCreateCleanerError::IsBeingCleanedUpOrAnotherCleanerCrashedDuringCleanup,
339                    "{} since the process is currently being cleaned up (or crashed during cleanup).", msg);
340            }
341            Err(ProcessCleanerCreateError::DoesNotExist) => {
342                fail!(from self, with MonitoringCreateCleanerError::DoesNotExist,
343                    "{} since it does not exist.", msg);
344            }
345            Err(e) => {
346                fail!(from self, with MonitoringCreateCleanerError::InternalError,
347                    "{} due to an internal failure ({:?}).", msg, e);
348            }
349        }
350    }
351}
352
353impl crate::monitoring::Monitoring for FileLockMonitoring {
354    type Token = Token;
355    type Monitor = Monitor;
356    type Builder = Builder;
357    type Cleaner = Cleaner;
358}
359
360#[derive(Clone, PartialEq, Eq, Debug)]
361pub struct Configuration {
362    suffix: FileName,
363    prefix: FileName,
364    path_hint: Path,
365}
366
367impl Default for Configuration {
368    fn default() -> Self {
369        Self {
370            suffix: FileLockMonitoring::default_suffix(),
371            prefix: FileLockMonitoring::default_prefix(),
372            path_hint: FileLockMonitoring::default_path_hint(),
373        }
374    }
375}
376
377impl NamedConceptConfiguration for Configuration {
378    fn prefix(mut self, value: &FileName) -> Self {
379        self.prefix = *value;
380        self
381    }
382
383    fn get_prefix(&self) -> &FileName {
384        &self.prefix
385    }
386
387    fn suffix(mut self, value: &FileName) -> Self {
388        self.suffix = *value;
389        self
390    }
391
392    fn get_suffix(&self) -> &FileName {
393        &self.suffix
394    }
395
396    fn path_hint(mut self, value: &Path) -> Self {
397        self.path_hint = *value;
398        self
399    }
400
401    fn get_path_hint(&self) -> &Path {
402        &self.path_hint
403    }
404}