1use core::fmt;
10
11use serde::Deserialize;
12use serde::Serialize;
13use syscalls::Errno;
14
15#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
18#[repr(u32)]
19pub enum Context {
20 Unknown,
22 Affinity,
24 Clone,
26 Tty,
28 Stdio,
30 ResetSignals,
32 MapUid,
34 MapGid,
36 Hostname,
38 Domainname,
40 Chroot,
42 Chdir,
44 Mount,
46 Network,
48 PreExec,
50 Seccomp,
52 Exec,
54}
55
56impl Context {
57 pub fn as_str(&self) -> &'static str {
59 match self {
60 Self::Unknown => "Unknown failure",
61 Self::Affinity => "setting cpu affinity failed",
62 Self::Clone => "clone failed",
63 Self::Tty => "Setting the controlling tty failed",
64 Self::Stdio => "Setting up stdio file descriptors failed",
65 Self::ResetSignals => "Reseting signal handlers failed",
66 Self::MapUid => "Setting UID map failed",
67 Self::MapGid => "Setting GID map failed",
68 Self::Hostname => "Setting hostname failed",
69 Self::Domainname => "Setting domainname failed",
70 Self::Chroot => "chroot failed",
71 Self::Chdir => "chdir failed",
72 Self::Mount => "mount failed",
73 Self::Network => "network configuration failed",
74 Self::PreExec => "pre_exec callback(s) failed",
75 Self::Seccomp => "failed to install seccomp filter",
76 Self::Exec => "execvp failed",
77 }
78 }
79}
80
81impl fmt::Display for Context {
82 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83 fmt::Write::write_str(f, self.as_str())
84 }
85}
86
87#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
90pub struct Error {
91 errno: Errno,
92 context: Context,
93}
94
95impl Error {
96 pub fn new(errno: Errno, context: Context) -> Self {
98 Self { errno, context }
99 }
100
101 pub fn result<S>(value: S, context: Context) -> Result<S, Self>
104 where
105 S: syscalls::ErrnoSentinel + PartialEq<S>,
106 {
107 Errno::result(value).map_err(|err| Self::new(err, context))
108 }
109
110 pub fn errno(&self) -> Errno {
112 self.errno
113 }
114
115 pub fn context(&self) -> Context {
117 self.context
118 }
119}
120
121impl fmt::Display for Error {
122 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
123 writeln!(f, "{}: {}", self.context, self.errno)
124 }
125}
126
127impl std::error::Error for Error {}
128
129impl From<Errno> for Error {
130 fn from(err: Errno) -> Self {
131 Self::new(err, Context::Unknown)
132 }
133}
134
135impl From<Error> for Errno {
136 fn from(err: Error) -> Errno {
137 err.errno
138 }
139}
140
141impl From<Error> for std::io::Error {
142 fn from(err: Error) -> Self {
143 std::io::Error::from(err.errno)
144 }
145}
146
147impl From<[u8; 8]> for Error {
148 fn from(bytes: [u8; 8]) -> Self {
151 debug_assert_eq!(core::mem::size_of::<Self>(), 8);
152 unsafe { core::mem::transmute(bytes) }
153 }
154}
155
156impl From<Error> for [u8; 8] {
157 fn from(error: Error) -> Self {
160 debug_assert_eq!(core::mem::size_of::<Self>(), 8);
161 unsafe { core::mem::transmute(error) }
162 }
163}
164
165pub(super) trait AddContext<T> {
166 fn context(self, context: Context) -> Result<T, Error>;
167}
168
169impl<T> AddContext<T> for Result<T, Errno> {
170 fn context(self, context: Context) -> Result<T, Error> {
171 self.map_err(move |errno| Error::new(errno, context))
172 }
173}
174
175impl<T> AddContext<T> for Result<T, nix::errno::Errno> {
176 fn context(self, context: Context) -> Result<T, Error> {
177 self.map_err(move |errno| Error::new(Errno::new(errno as i32), context))
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
186 fn to_bytes() {
187 let bytes: [u8; 8] = Error::new(Errno::ENOENT, Context::Exec).into();
188 assert_eq!(Error::from(bytes), Error::new(Errno::ENOENT, Context::Exec));
189 }
190}