syd/
err.rs

1//
2// Syd: rock-solid application kernel
3// src/err.rs: Error types and error handling code
4//
5// Copyright (c) 2024, 2025 Ali Polatel <alip@chesswob.org>
6//
7// SPDX-License-Identifier: GPL-3.0
8
9use std::{
10    array::TryFromSliceError,
11    env::VarError,
12    fmt, io,
13    net::AddrParseError,
14    num::{ParseIntError, TryFromIntError},
15    str::Utf8Error,
16};
17
18#[cfg(feature = "oci")]
19use libcgroups::common::AnyManagerError;
20#[cfg(feature = "oci")]
21use libcgroups::common::CreateCgroupSetupError;
22#[cfg(feature = "oci")]
23use libcontainer::error::LibcontainerError;
24#[cfg(feature = "oci")]
25use libcontainer::signal::SignalError;
26#[cfg(feature = "oci")]
27use libcontainer::utils::PathBufExtError;
28use nix::errno::Errno;
29use procfs::ProcError;
30use shellexpand::LookupError;
31#[cfg(feature = "oci")]
32use tracing::subscriber::SetGlobalDefaultError;
33
34use crate::{caps::errors::CapsError, elf::ElfError, libseccomp::error::SeccompError};
35
36/// Convenience type to use for functions returning a SydError.
37pub type SydResult<T> = std::result::Result<T, SydError>;
38
39/// A macro to create a SydError from the last errno.
40#[macro_export]
41macro_rules! lasterrno {
42    () => {
43        SydError::Nix(nix::errno::Errno::last())
44    };
45}
46
47/// Enum representing different types of errors
48pub enum SydError {
49    /// This error represents a network address parse error.
50    Addr(AddrParseError),
51    /// This error type represents a lexopt error.
52    Args(lexopt::Error),
53    /// This error type represents a capability error.
54    Caps(CapsError),
55    /// This error type represents an ELF parse error.
56    Elf(ElfError),
57    /// This error type represents an error in environment variable lookup in configuration.
58    Env(LookupError<VarError>),
59    /// This error type represents an `Errno`.
60    Nix(Errno),
61    /// This error type represents JSON errors.
62    Json(serde_json::Error),
63    /// This error type represents integer parse errors.
64    ParseInt(ParseIntError),
65    /// This error type represents integer parse errors.
66    TryInt(TryFromIntError),
67    /// This error type represents a slice conversion error.
68    TrySlice(TryFromSliceError),
69    /// This error type represents size parse errors.
70    ParseSize(parse_size::Error),
71    /// This error type represents a /proc filesystem error.
72    Proc(ProcError),
73    /// This error type represents a regular expression error.
74    Regex(regex::Error),
75    /// This error type represents Seccomp errors.
76    Scmp(SeccompError),
77    /// This error type represents UTF-8 errors.
78    Utf8(Utf8Error),
79    #[cfg(feature = "oci")]
80    /// This error type represents a Cgroup setup error.
81    CgSetup(CreateCgroupSetupError),
82    #[cfg(feature = "oci")]
83    /// This error type represents a miscallenous Cgroup error.
84    CgMisc(AnyManagerError),
85    #[cfg(feature = "oci")]
86    /// This error type represents container errors.
87    Cont(LibcontainerError),
88    #[cfg(feature = "oci")]
89    /// This error type represents a path canonicalization error.
90    Pext(PathBufExtError),
91    #[cfg(feature = "oci")]
92    /// This error type represents a tracing subscriber error.
93    SetTracing(SetGlobalDefaultError),
94    #[cfg(feature = "oci")]
95    /// This error type represents a signal parsing error.
96    Signal(SignalError<String>),
97    #[cfg(feature = "oci")]
98    /// This error type represents OCI-Spec errors.
99    Spec(oci_spec::OciSpecError),
100}
101
102impl SydError {
103    /// Converts a SydError to an Errno if applicable.
104    #[inline(always)]
105    pub fn errno(&self) -> Option<Errno> {
106        match self {
107            Self::Nix(errno) => Some(*errno),
108            Self::Proc(error) => proc_error_to_errno(error),
109            Self::Scmp(error) => error
110                .sysrawrc()
111                .map(|errno| errno.abs())
112                .map(Errno::from_raw),
113            _ => None,
114        }
115    }
116}
117
118impl fmt::Debug for SydError {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        match self {
121            Self::Addr(error) => write!(f, "AddrParseError: {error:?}"),
122            Self::Args(error) => write!(f, "ArgsParseError: {error:?}"),
123            Self::Caps(error) => write!(f, "CapsError: {error:?}"),
124            Self::Elf(error) => write!(f, "ElfError: {error:?}"),
125            Self::Env(error) => write!(f, "LookupError<VarError>: {error:?}"),
126            Self::Nix(errno) => write!(f, "NixError: {errno:?}"),
127            Self::Json(error) => write!(f, "JsonError: {error:?}"),
128            Self::ParseInt(error) => write!(f, "ParseIntError: {error:?}"),
129            Self::Scmp(error) => write!(f, "SeccompError: {error:?}"),
130            Self::TryInt(error) => write!(f, "TryFromIntError: {error:?}"),
131            Self::TrySlice(error) => write!(f, "TryFromSliceError: {error:?}"),
132            Self::ParseSize(error) => write!(f, "ParseSizeError: {error:?}"),
133            Self::Proc(error) => write!(f, "ProcError: {error:?}"),
134            Self::Regex(error) => write!(f, "RegexError: {error:?}"),
135            Self::Utf8(error) => write!(f, "Utf8Error: {error:?}"),
136            #[cfg(feature = "oci")]
137            Self::CgSetup(error) => write!(f, "CgroupSetupError: {error:?}"),
138            #[cfg(feature = "oci")]
139            Self::CgMisc(error) => write!(f, "AnyManagerError: {error:?}"),
140            #[cfg(feature = "oci")]
141            Self::Cont(error) => write!(f, "ContainerError: {error:?}"),
142            #[cfg(feature = "oci")]
143            Self::Pext(error) => write!(f, "PathBufExtError: {error:?}"),
144            #[cfg(feature = "oci")]
145            Self::SetTracing(error) => write!(f, "SetGlobalDefaultError: {error:?}"),
146            #[cfg(feature = "oci")]
147            Self::Signal(error) => write!(f, "SignalError: {error:?}"),
148            #[cfg(feature = "oci")]
149            Self::Spec(error) => write!(f, "OciSpecError: {error:?}"),
150        }
151    }
152}
153
154impl fmt::Display for SydError {
155    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156        match self {
157            Self::Addr(error) => write!(f, "AddrParseError: {error}"),
158            Self::Args(error) => write!(f, "ArgsParseError: {error}"),
159            Self::Caps(error) => write!(f, "CapsError: {error}"),
160            Self::Elf(error) => write!(f, "ElfError: {error}"),
161            Self::Env(error) => write!(f, "LookupError<VarError>: {error}"),
162            Self::Nix(errno) => write!(f, "NixError: {errno}"),
163            Self::Json(error) => write!(f, "JsonError: {error}"),
164            Self::ParseInt(error) => write!(f, "ParseIntError: {error}"),
165            Self::Scmp(error) => write!(f, "SeccompError: {error}"),
166            Self::TryInt(error) => write!(f, "TryFromIntError: {error}"),
167            Self::TrySlice(error) => write!(f, "TryFromSliceError: {error}"),
168            Self::ParseSize(error) => write!(f, "ParseSizeError: {error}"),
169            Self::Proc(error) => write!(f, "ProcError: {error}"),
170            Self::Regex(error) => write!(f, "RegexError: {error}"),
171            Self::Utf8(error) => write!(f, "Utf8Error: {error}"),
172            #[cfg(feature = "oci")]
173            Self::CgSetup(error) => write!(f, "CgroupSetupError: {error}"),
174            #[cfg(feature = "oci")]
175            Self::CgMisc(error) => write!(f, "AnyManagerError: {error}"),
176            #[cfg(feature = "oci")]
177            Self::Cont(error) => write!(f, "ContainerError: {error}"),
178            #[cfg(feature = "oci")]
179            Self::Pext(error) => write!(f, "PathBufExtError: {error}"),
180            #[cfg(feature = "oci")]
181            Self::SetTracing(error) => write!(f, "SetGlobalDefaultError: {error}"),
182            #[cfg(feature = "oci")]
183            Self::Signal(error) => write!(f, "SignalError: {error}"),
184            #[cfg(feature = "oci")]
185            Self::Spec(error) => write!(f, "OciSpecError: {error}"),
186        }
187    }
188}
189
190impl std::error::Error for SydError {
191    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
192        match self {
193            Self::Addr(error) => Some(error),
194            Self::Args(error) => Some(error),
195            Self::Nix(errno) => Some(errno),
196            Self::ParseInt(error) => Some(error),
197            Self::TryInt(error) => Some(error),
198            Self::TrySlice(error) => Some(error),
199            // TODO: Change to Some(error) when
200            // parse-size is upgraded, see Cargo.toml
201            Self::ParseSize(_error) => None,
202            Self::Proc(error) => Some(error),
203            Self::Regex(error) => Some(error),
204            Self::Utf8(error) => Some(error),
205            #[cfg(feature = "oci")]
206            Self::CgSetup(error) => Some(error),
207            #[cfg(feature = "oci")]
208            Self::CgMisc(error) => Some(error),
209            #[cfg(feature = "oci")]
210            Self::Cont(error) => Some(error),
211            #[cfg(feature = "oci")]
212            Self::Pext(error) => Some(error),
213            #[cfg(feature = "oci")]
214            Self::SetTracing(error) => Some(error),
215            #[cfg(feature = "oci")]
216            Self::Signal(error) => Some(error),
217            #[cfg(feature = "oci")]
218            Self::Spec(error) => Some(error),
219            _ => None,
220        }
221    }
222}
223
224// Conversions from std::io::Error to SydError.
225impl From<io::Error> for SydError {
226    fn from(err: io::Error) -> SydError {
227        SydError::Nix(err2no(&err))
228    }
229}
230
231// Conversions from AddrParseError to SydError.
232impl From<AddrParseError> for SydError {
233    fn from(err: AddrParseError) -> SydError {
234        SydError::Addr(err)
235    }
236}
237
238// Conversions from Utf8Error to SydError.
239impl From<Utf8Error> for SydError {
240    fn from(err: Utf8Error) -> SydError {
241        Self::Utf8(err)
242    }
243}
244
245// Conversions from ProcError to SydError.
246impl From<ProcError> for SydError {
247    fn from(err: ProcError) -> SydError {
248        Self::Proc(err)
249    }
250}
251
252// Conversions from regex::Error to SydError.
253impl From<regex::Error> for SydError {
254    fn from(err: regex::Error) -> SydError {
255        Self::Regex(err)
256    }
257}
258
259// Conversions from lexopt::Error to SydError.
260impl From<lexopt::Error> for SydError {
261    fn from(err: lexopt::Error) -> SydError {
262        Self::Args(err)
263    }
264}
265
266// Conversions from CapsError to SydError.
267impl From<CapsError> for SydError {
268    fn from(err: CapsError) -> SydError {
269        Self::Caps(err)
270    }
271}
272
273// Conversions from ElfError to SydError.
274impl From<ElfError> for SydError {
275    fn from(err: ElfError) -> SydError {
276        Self::Elf(err)
277    }
278}
279
280// Conversions from LookupError<VarError> to SydError.
281impl From<LookupError<VarError>> for SydError {
282    fn from(err: LookupError<VarError>) -> SydError {
283        Self::Env(err)
284    }
285}
286
287// Conversions from nix::errno::Errno to SydError.
288impl From<Errno> for SydError {
289    fn from(err: Errno) -> SydError {
290        Self::Nix(err)
291    }
292}
293
294// Conversions from serde_json::Error to SydError.
295impl From<serde_json::Error> for SydError {
296    fn from(err: serde_json::Error) -> SydError {
297        Self::Json(err)
298    }
299}
300
301// Conversions from AnyManagerError to SydError.
302#[cfg(feature = "oci")]
303impl From<AnyManagerError> for SydError {
304    fn from(err: AnyManagerError) -> SydError {
305        Self::CgMisc(err)
306    }
307}
308
309// Conversions from CreateCgroupSetupError to SydError.
310#[cfg(feature = "oci")]
311impl From<CreateCgroupSetupError> for SydError {
312    fn from(err: CreateCgroupSetupError) -> SydError {
313        Self::CgSetup(err)
314    }
315}
316
317// Conversions from LibcontainerError to SydError.
318#[cfg(feature = "oci")]
319impl From<LibcontainerError> for SydError {
320    fn from(err: LibcontainerError) -> SydError {
321        Self::Cont(err)
322    }
323}
324
325// Conversions from PathBufExtError to SydError.
326#[cfg(feature = "oci")]
327impl From<PathBufExtError> for SydError {
328    fn from(err: PathBufExtError) -> SydError {
329        Self::Pext(err)
330    }
331}
332
333// Conversions from SetGlobalDefaultError to SydError.
334#[cfg(feature = "oci")]
335impl From<SetGlobalDefaultError> for SydError {
336    fn from(err: SetGlobalDefaultError) -> SydError {
337        Self::SetTracing(err)
338    }
339}
340
341// Conversions from SignalError<String> to SydError.
342#[cfg(feature = "oci")]
343impl From<SignalError<String>> for SydError {
344    fn from(err: SignalError<String>) -> SydError {
345        Self::Signal(err)
346    }
347}
348
349// Conversions from OciSpecError to SydError.
350#[cfg(feature = "oci")]
351impl From<oci_spec::OciSpecError> for SydError {
352    fn from(err: oci_spec::OciSpecError) -> SydError {
353        Self::Spec(err)
354    }
355}
356
357// Conversions from ParseIntError to SydError.
358impl From<ParseIntError> for SydError {
359    fn from(err: ParseIntError) -> SydError {
360        Self::ParseInt(err)
361    }
362}
363
364// Conversions from TryFromIntError to SydError.
365impl From<TryFromIntError> for SydError {
366    fn from(err: TryFromIntError) -> SydError {
367        Self::TryInt(err)
368    }
369}
370
371// Conversions from TryFromSliceError to SydError.
372impl From<TryFromSliceError> for SydError {
373    fn from(err: TryFromSliceError) -> SydError {
374        Self::TrySlice(err)
375    }
376}
377
378// Conversions from parse_size::Error to SydError.
379impl From<parse_size::Error> for SydError {
380    fn from(err: parse_size::Error) -> SydError {
381        Self::ParseSize(err)
382    }
383}
384
385// Conversions from SeccompError to SydError.
386impl From<SeccompError> for SydError {
387    fn from(err: SeccompError) -> SydError {
388        Self::Scmp(err)
389    }
390}
391
392/// Convert a std::io::Error into a nix::Errno.
393#[inline(always)]
394pub fn err2no(err: &std::io::Error) -> Errno {
395    err.raw_os_error()
396        .map(Errno::from_raw)
397        .unwrap_or(Errno::ENOSYS)
398}
399
400/// Convert a std::io::Error reference into a nix::Errno.
401#[inline(always)]
402pub fn err2no_ref(err: &std::io::Error) -> Errno {
403    err.raw_os_error()
404        .map(Errno::from_raw)
405        .unwrap_or(Errno::ENOSYS)
406}
407
408/// Convert proc errno to nix errno.
409#[inline(always)]
410pub fn proc_error_to_errno(error: &ProcError) -> Option<Errno> {
411    match error {
412        ProcError::PermissionDenied(_) => Some(Errno::EACCES),
413        ProcError::NotFound(_) => Some(Errno::ESRCH),
414        ProcError::Io(error, _) => Some(err2no_ref(error)),
415        ProcError::Other(_) => None,
416        ProcError::Incomplete(_) => None,
417        ProcError::InternalError(_) => None,
418    }
419}