1use core::fmt::Debug;
45
46use iceoryx2_bb_posix::{
47 file_descriptor::{FileDescriptor, FileDescriptorBased},
48 file_descriptor_set::SynchronousMultiplexing,
49 process::ProcessId,
50 signal::FetchableSignal,
51 signal_set::FetchableSignalSet,
52 user::Uid,
53};
54use iceoryx2_log::{fail, fatal_panic};
55use iceoryx2_pal_os_api::linux;
56use iceoryx2_pal_posix::posix::{self};
57
58#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
60pub enum SignalFdCreationError {
61 PerProcessFileHandleLimitReached,
63 SystemWideFileHandleLimitReached,
65 InsufficientMemory,
67 UnableToMountInodeDevice,
69 UnknownError(i32),
71}
72
73impl core::fmt::Display for SignalFdCreationError {
74 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
75 write!(f, "SignalFdCreationError::{self:?}")
76 }
77}
78
79impl core::error::Error for SignalFdCreationError {}
80
81#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
83pub enum SignalFdReadError {
84 SystemBreaksReadContract,
86 Interrupt,
88 IOerror,
90 InsufficientResources,
92 InsufficientMemory,
94 UnknownError(i32),
96}
97
98impl core::fmt::Display for SignalFdReadError {
99 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
100 write!(f, "SignalFdReadError::{self:?}")
101 }
102}
103
104impl core::error::Error for SignalFdReadError {}
105
106#[derive(Debug)]
108pub struct SignalFdBuilder {
109 signal_set: FetchableSignalSet,
110 close_on_exec: bool,
111}
112
113impl SignalFdBuilder {
114 pub fn new(signal_set: FetchableSignalSet) -> Self {
117 Self {
118 signal_set,
119 close_on_exec: false,
120 }
121 }
122
123 pub fn set_close_on_exec(mut self, value: bool) -> Self {
126 self.close_on_exec = value;
127 self
128 }
129
130 pub fn create_non_blocking(self) -> Result<SignalFd, SignalFdCreationError> {
132 Ok(SignalFd {
133 file_descriptor: self.create(true)?,
134 })
135 }
136
137 pub fn create_blocking(self) -> Result<BlockingSignalFd, SignalFdCreationError> {
139 Ok(BlockingSignalFd {
140 file_descriptor: self.create(false)?,
141 })
142 }
143
144 fn create(self, is_non_blocking: bool) -> Result<FileDescriptor, SignalFdCreationError> {
145 let msg = "Unable to create SignalFd";
146 let mut flags = 0;
147 if self.close_on_exec {
148 flags |= linux::SFD_CLOEXEC;
149 }
150
151 if is_non_blocking {
152 flags |= linux::SFD_NONBLOCK;
153 }
154
155 let fd = unsafe { linux::signalfd(-1, self.signal_set.native_handle(), flags as _) };
156
157 if fd == -1 {
158 match posix::Errno::get() {
159 posix::Errno::EMFILE => {
160 fail!(from self,
161 with SignalFdCreationError::PerProcessFileHandleLimitReached,
162 "{msg} since the per process file descriptor limit is exceeded.");
163 }
164 posix::Errno::ENFILE => {
165 fail!(from self,
166 with SignalFdCreationError::SystemWideFileHandleLimitReached,
167 "{msg} since the system wide file descriptor limit is exceeded.");
168 }
169 posix::Errno::ENODEV => {
170 fail!(from self,
171 with SignalFdCreationError::UnableToMountInodeDevice,
172 "{msg} since anonymous inode device could not be mapped.");
173 }
174 posix::Errno::ENOMEM => {
175 fail!(from self,
176 with SignalFdCreationError::InsufficientMemory,
177 "{msg} due to insufficient memory.");
178 }
179 e => {
180 fail!(from self,
181 with SignalFdCreationError::UnknownError(e as i32),
182 "{msg} due to an unknown error {e:?}.");
183 }
184 }
185 }
186
187 let file_descriptor = match FileDescriptor::new(fd) {
188 Some(fd) => fd,
189 None => fatal_panic!(from self,
190 "This should never happen! {msg} since the signalfd returned a broken file descriptor (fd)."),
191 };
192
193 Ok(file_descriptor)
194 }
195}
196
197pub struct SignalInfo {
199 signal_info: linux::signalfd_siginfo,
200}
201
202impl Debug for SignalInfo {
203 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
204 write!(
205 f,
206 "SignalInfo {{ signal: {:?}, origin_pid: {}, origin_uid: {} }}",
207 self.signal(),
208 self.origin_pid(),
209 self.origin_uid()
210 )
211 }
212}
213
214impl SignalInfo {
215 pub fn signal(&self) -> FetchableSignal {
217 (self.signal_info.ssi_signo as i32).into()
218 }
219
220 pub fn origin_pid(&self) -> ProcessId {
222 ProcessId::new(self.signal_info.ssi_pid as _)
223 }
224
225 pub fn origin_uid(&self) -> Uid {
227 Uid::new_from_native(self.signal_info.ssi_uid)
228 }
229}
230
231#[derive(Debug)]
233pub struct SignalFd {
234 file_descriptor: FileDescriptor,
235}
236
237fn read_from_fd<T: Debug>(
238 this: &T,
239 fd: &FileDescriptor,
240) -> Result<Option<SignalInfo>, SignalFdReadError> {
241 let msg = "Unable to read signal from SignalFd";
242 let mut signal_info: linux::signalfd_siginfo = unsafe { core::mem::zeroed() };
243
244 let number_of_bytes = unsafe {
245 posix::read(
246 fd.native_handle(),
247 ((&mut signal_info) as *mut linux::signalfd_siginfo).cast(),
248 core::mem::size_of::<linux::signalfd_siginfo>(),
249 )
250 };
251
252 if number_of_bytes == core::mem::size_of::<linux::signalfd_siginfo>() as _ {
253 return Ok(Some(SignalInfo { signal_info }));
254 }
255
256 if number_of_bytes != -1 {
257 fail!(from this,
258 with SignalFdReadError::SystemBreaksReadContract,
259 "{msg} since only {number_of_bytes} bytes were read but {} bytes were expected. This breaks the contract with the system.",
260 core::mem::size_of::<linux::signalfd_siginfo>());
261 }
262
263 match posix::Errno::get() {
264 posix::Errno::EAGAIN => Ok(None),
265 posix::Errno::EINTR => {
266 fail!(from this,
267 with SignalFdReadError::Interrupt,
268 "{msg} since an interrupt signal was raised.");
269 }
270 posix::Errno::EIO => {
271 fail!(from this,
272 with SignalFdReadError::IOerror,
273 "{msg} due to an i/o error.");
274 }
275 posix::Errno::ENOBUFS => {
276 fail!(from this,
277 with SignalFdReadError::InsufficientResources,
278 "{msg} due insufficient resources.");
279 }
280 posix::Errno::ENOMEM => {
281 fail!(from this,
282 with SignalFdReadError::InsufficientMemory,
283 "{msg} due insufficient memory.");
284 }
285 e => {
286 fail!(from this,
287 with SignalFdReadError::UnknownError(e as _),
288 "{msg} due to an unknown error ({e:?}).");
289 }
290 }
291}
292
293impl SignalFd {
294 pub fn try_read(&self) -> Result<Option<SignalInfo>, SignalFdReadError> {
297 read_from_fd(self, &self.file_descriptor)
298 }
299}
300
301impl FileDescriptorBased for SignalFd {
302 fn file_descriptor(&self) -> &FileDescriptor {
303 &self.file_descriptor
304 }
305}
306
307impl SynchronousMultiplexing for SignalFd {}
308
309#[derive(Debug)]
311pub struct BlockingSignalFd {
312 file_descriptor: FileDescriptor,
313}
314
315impl BlockingSignalFd {
316 pub fn blocking_read(&self) -> Result<Option<SignalInfo>, SignalFdReadError> {
319 read_from_fd(self, &self.file_descriptor)
320 }
321}
322
323impl FileDescriptorBased for BlockingSignalFd {
324 fn file_descriptor(&self) -> &FileDescriptor {
325 &self.file_descriptor
326 }
327}
328
329impl SynchronousMultiplexing for BlockingSignalFd {}