1use 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}