iceoryx2_bb_posix/
process.rs

1// Copyright (c) 2023 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
13//! Provides an abstraction of [`Process`]es in a POSIX system.
14//!
15//! # Scheduler & Priorities
16//!
17//! The priority is independent of the scheduler and 0 is
18//! always the lowest and 255 always the highest priority. Internally, the scheduler dependent
19//! priority is mapped to the scheduler independent range from 0..255.
20//! A disadvantage can arise when the schedulers dependent priority range is either much more
21//! fine grained or coarse. But this should be negligible since most scheduler priorities have
22//! a range of about 50.
23//! The granularity of a [`Scheduler`] can be acquired with [`Scheduler::priority_granularity()`].
24//!
25//! # Examples
26//!
27//! ```no_run
28//! # extern crate iceoryx2_loggers;
29//!
30//! use iceoryx2_bb_posix::process::*;
31//! use iceoryx2_bb_posix::scheduler::*;
32//!
33//! let it_represents_my_process = Process::from_self();
34//! let it_represents_my_processes_parent = Process::from_parent();
35//! let mut process = Process::from_pid(ProcessId::new(123));
36//!
37//! process.set_scheduler(Scheduler::Fifo).expect("failed to set scheduler");
38//! process.set_priority(100).expect("failed to set priority");
39//!
40//! println!("pid: {:?}, scheduler: {:?}, prio: {}", process.id(),
41//!             process.get_scheduler().expect("failed to get scheduler"),
42//!             process.get_priority().expect("failed to get priority"));
43//! ```
44use core::fmt::Display;
45
46use iceoryx2_bb_elementary::enum_gen;
47use iceoryx2_bb_system_types::file_path::*;
48use iceoryx2_log::fail;
49use iceoryx2_pal_posix::posix::{errno::Errno, MemZeroedStruct};
50use iceoryx2_pal_posix::*;
51
52use crate::{
53    scheduler::{Scheduler, SchedulerConversionError},
54    signal::Signal,
55};
56
57#[derive(Debug, Clone, Copy, Eq, PartialEq)]
58pub enum ProcessExecutablePathError {
59    ContainsInvalidCharacters,
60    UnableToRead,
61}
62
63enum_gen! { ProcessSendSignalError
64  entry:
65    InsufficientPermissions,
66    UnknownProcessId,
67    UnknownError(i32)
68}
69
70enum_gen! { ProcessGetSchedulerError
71  entry:
72    InsufficientPermissions,
73    UnknownProcessId,
74    UnknownError(i32)
75
76  mapping:
77    SchedulerConversionError
78}
79
80enum_gen! { ProcessSetSchedulerError
81  entry:
82    InsufficientPermissions,
83    UnknownProcessId,
84    UnknownError(i32)
85
86  mapping:
87    SchedulerConversionError
88}
89
90enum_gen! {
91    /// The ProcessError enum is a generalization when one doesn't require the fine-grained error
92    /// handling enums. One can forward ProcessError as more generic return value when a method
93    /// returns a Process***Error.
94    /// On a higher level it is again convertable to [`crate::Error`].
95    ProcessError
96  generalization:
97    FailedToSetSchedulerSettings <= ProcessSetSchedulerError,
98    FailedToGetSchedulerSettings <= ProcessGetSchedulerError,
99    FailedToSendSignal <= ProcessSendSignalError
100}
101
102/// Trait to be able to convert integers into processes by interpreting their value as the
103/// process id
104pub trait ProcessExt {
105    fn as_process(&self) -> Process;
106}
107
108impl ProcessExt for posix::pid_t {
109    fn as_process(&self) -> Process {
110        Process::from_pid(ProcessId::new(*self))
111    }
112}
113
114/// Represents a process id.
115#[derive(Debug, Copy, Clone, PartialEq, Eq)]
116pub struct ProcessId(posix::pid_t);
117
118impl ProcessId {
119    /// Creates a new process id.
120    pub fn new(value: posix::pid_t) -> Self {
121        ProcessId(value)
122    }
123
124    /// Returns the underlying integer value of the process id
125    pub fn value(&self) -> posix::pid_t {
126        self.0
127    }
128}
129
130impl Display for ProcessId {
131    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
132        write!(f, "{}", self.0)
133    }
134}
135
136/// Represent a process in a POSIX system.
137#[derive(Debug)]
138pub struct Process {
139    pid: ProcessId,
140}
141
142impl Process {
143    /// Creates a process from a provided id. The process does not have to exists at the time of
144    /// creation. But all other methods may fail when the process does not exist.
145    pub fn from_pid(pid: ProcessId) -> Process {
146        Process { pid }
147    }
148
149    /// Create a process object from own process.
150    pub fn from_self() -> Process {
151        Process {
152            pid: unsafe { ProcessId::new(posix::getpid()) },
153        }
154    }
155
156    /// Create a process object from the parents process.
157    pub fn from_parent() -> Process {
158        Process {
159            pid: unsafe { ProcessId::new(posix::getppid()) },
160        }
161    }
162
163    /// Checks if the process is still alive
164    pub fn is_alive(&self) -> bool {
165        unsafe { posix::kill(self.pid.0, 0_i32) == 0 }
166    }
167
168    /// Returns the id of the process.
169    pub fn id(&self) -> ProcessId {
170        self.pid
171    }
172
173    /// Returns the executable path of the [`Process`].
174    pub fn executable(&self) -> Result<FilePath, ProcessExecutablePathError> {
175        let msg = "Unable to read executable path";
176        let mut buffer = [0u8; FilePath::max_len()];
177        let path_len =
178            unsafe { posix::proc_pidpath(self.pid.0, buffer.as_mut_ptr().cast(), buffer.len()) };
179        if path_len < 0 {
180            fail!(from self, with ProcessExecutablePathError::UnableToRead,
181                "{} since the name could not be acquired.", msg);
182        }
183
184        let path = fail!(from self,
185                            when FilePath::new(&buffer[..(path_len as usize)]),
186                            with ProcessExecutablePathError::ContainsInvalidCharacters,
187                            "{} since the acquired name contains invalid characters.", msg);
188
189        Ok(path)
190    }
191
192    /// Sends a signal to the process.
193    pub fn send_signal(&self, signal: Signal) -> Result<(), ProcessSendSignalError> {
194        if unsafe { posix::kill(self.pid.0, signal as i32) } == 0 {
195            return Ok(());
196        }
197
198        let msg = "Unable to send signal to process";
199        handle_errno!(ProcessSendSignalError, from self,
200            Errno::EPERM => (InsufficientPermissions, "{} due to insufficient permissions.", msg),
201            Errno::ESRCH => (UnknownProcessId, "{} since the process does not exist.", msg),
202            v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg,v)
203        );
204    }
205
206    /// Returns the priority of the process. 0 is the lowest and 255 the highest priority.
207    pub fn get_priority(&self) -> Result<u8, ProcessGetSchedulerError> {
208        let msg = "Unable to acquire priority of process";
209        let scheduler = fail!(from self, when self.get_scheduler(), "{} due to a failure while getting the current scheduler of the process.", msg);
210
211        let mut param = posix::sched_param::new_zeroed();
212        if unsafe { posix::sched_getparam(self.pid.0, &mut param) } == 0 {
213            return Ok(scheduler.get_priority_from(&param));
214        }
215
216        handle_errno!(ProcessGetSchedulerError, from self,
217            Errno::EPERM => (InsufficientPermissions, "{} due to insufficient permissions.", msg),
218            Errno::ESRCH => (UnknownProcessId, "{} since the process cannot be found on the system.", msg),
219            v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
220        );
221    }
222
223    /// Set the priority of the process. 0 is the lowest priority and 255 the highest.
224    pub fn set_priority(&mut self, priority: u8) -> Result<(), ProcessGetSchedulerError> {
225        let msg = "Unable to set process priority";
226        let scheduler = fail!(from self, when self.get_scheduler(), "{} due to a failure while acquiring the current process scheduler.", msg);
227        let mut param = posix::sched_param::new_zeroed();
228        param.sched_priority = scheduler.policy_specific_priority(priority);
229
230        if unsafe { posix::sched_setparam(self.pid.0, &param) } == 0 {
231            return Ok(());
232        }
233
234        handle_errno!(ProcessGetSchedulerError, from self,
235            Errno::EPERM => (InsufficientPermissions, "{} due to insufficient permissions.", msg),
236            Errno::ESRCH => (UnknownProcessId, "{} since the process cannot be found on the system.", msg),
237            v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
238        );
239    }
240
241    /// Returns the currently in use [`Scheduler`] by the process.
242    pub fn get_scheduler(&self) -> Result<Scheduler, ProcessGetSchedulerError> {
243        let msg = "Unable to acquire scheduler of process";
244        let v = unsafe { posix::sched_getscheduler(self.pid.0) };
245        if v != -1 {
246            return Ok(
247                fail!(from self, when Scheduler::from_int(v), "{} since the scheduler seems to be unknown.", msg),
248            );
249        }
250
251        handle_errno!(ProcessGetSchedulerError, from self,
252            Errno::EPERM => (InsufficientPermissions, "{} due to insufficient permissions.", msg),
253            Errno::ESRCH => (UnknownProcessId, "{} since the process cannot be found on the system.", msg),
254            v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg, v)
255        );
256    }
257
258    /// Sets a new [`Scheduler`] for the process and returns the formerly used [`Scheduler`]
259    /// on success.
260    pub fn set_scheduler(
261        &mut self,
262        scheduler: Scheduler,
263    ) -> Result<Scheduler, ProcessSetSchedulerError> {
264        let msg = "Unable to set scheduler of process";
265        let mut param = posix::sched_param::new_zeroed();
266        param.sched_priority = scheduler.policy_specific_priority(0);
267        let former_scheduler =
268            unsafe { posix::sched_setscheduler(self.pid.0, scheduler as i32, &param) };
269
270        if former_scheduler != -1 {
271            return Ok(fail!(from self, when Scheduler::from_int(former_scheduler),
272                    "The previous set up scheduler is not supported. New scheduler was successfully set but cannot reverted to previous scheduler."));
273        }
274
275        handle_errno!(ProcessSetSchedulerError, from self,
276            Errno::EPERM => (InsufficientPermissions, "{} due to insufficient permissions.", msg),
277            Errno::ESRCH => (UnknownProcessId, "{} since the process cannot be found on the system.", msg),
278            v => (UnknownError(v as i32), "{} since an unknown error occurred ({}).", msg,v)
279        );
280    }
281}