1use std::io;
2use std::fmt;
3use crate::status::ExitStatus;
4
5use nix;
6
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum ErrorCode {
10 CreatePipe = 1,
11 Fork = 2,
12 Exec = 3,
13 Chdir = 4,
14 ParentDeathSignal = 5,
15 PipeError = 6,
16 StdioError = 7,
17 SetUser = 8,
18 ChangeRoot = 9,
19 SetIdMap = 10,
20 SetPGid = 11,
21 SetNs = 12,
22 CapSet = 13,
23 PreExec = 14,
24}
25
26#[derive(Debug)]
32pub enum Error {
33 NixError, UnknownError,
41 CreatePipe(i32),
47 Fork(i32),
49 Exec(i32),
51 Chdir(i32),
53 ParentDeathSignal(i32),
55 PipeError(i32),
57 WaitError(i32),
61 StdioError(i32),
63 SetUser(i32),
66 ChangeRoot(i32),
70 SetIdMap(i32),
73 AuxCommandExited(i32),
82 AuxCommandKilled(i32),
86 SetPGid(i32),
88 SetNs(i32),
90 CapSet(i32),
92 BeforeUnfreeze(Box<dyn (::std::error::Error) + Send + Sync + 'static>),
94 PreExec(i32),
96}
97
98impl Error {
99 pub fn raw_os_error(&self) -> Option<i32> {
101 use self::Error::*;
102 match self {
103 &UnknownError => None,
104 &NixError => None,
105 &CreatePipe(x) => Some(x),
106 &Fork(x) => Some(x),
107 &Exec(x) => Some(x),
108 &Chdir(x) => Some(x),
109 &ParentDeathSignal(x) => Some(x),
110 &PipeError(x) => Some(x),
111 &WaitError(x) => Some(x),
112 &StdioError(x) => Some(x),
113 &SetUser(x) => Some(x),
114 &ChangeRoot(x) => Some(x),
115 &SetIdMap(x) => Some(x),
116 &AuxCommandExited(..) => None,
117 &AuxCommandKilled(..) => None,
118 &SetPGid(x) => Some(x),
119 &SetNs(x) => Some(x),
120 &CapSet(x) => Some(x),
121 &BeforeUnfreeze(..) => None,
122 &PreExec(x) => Some(x),
123 }
124 }
125}
126
127impl Error {
128 fn title(&self) -> &'static str {
129 use self::Error::*;
130 match self {
131 &UnknownError => "unexpected value received via signal pipe",
132 &NixError => "some unknown nix error",
133 &CreatePipe(_) => "can't create pipe",
134 &Fork(_) => "error when forking",
135 &Exec(_) => "error when executing",
136 &Chdir(_) => "error when setting working directory",
137 &ParentDeathSignal(_) => "error when death signal",
138 &PipeError(_) => "error in signalling pipe",
139 &WaitError(_) => "error in waiting for child",
140 &StdioError(_) => "error setting up stdio for child",
141 &SetUser(_) => "error setting user or groups",
142 &ChangeRoot(_) => "error changing root directory",
143 &SetIdMap(_) => "error setting uid/gid mappings",
144 &AuxCommandExited(_) => "aux command exited with non-zero code",
145 &AuxCommandKilled(_) => "aux command was killed by signal",
146 &SetPGid(_) => "error when calling setpgid",
147 &SetNs(_) => "error when calling setns",
148 &CapSet(_) => "error when setting capabilities",
149 &BeforeUnfreeze(_) => "error in before_unfreeze callback",
150 &PreExec(_) => "error in pre_exec callback",
151 }
152 }
153}
154
155impl fmt::Display for Error {
156 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
157 use crate::Error::*;
158 if let Some(code) = self.raw_os_error() {
159 let errno = nix::errno::from_i32(code);
160 if let nix::errno::Errno::UnknownErrno = errno {
161 write!(fmt, "{}: {}", self.title(),
163 io::Error::from_raw_os_error(code))
164 } else {
165 write!(fmt, "{}: {} (os error {})", self.title(),
167 errno.desc(), code)
168 }
169 } else {
170 match self {
171 BeforeUnfreeze(err) => {
172 write!(fmt, "{}: {}", self.title(), err)
173 }
174 _ => write!(fmt, "{}", self.title()),
175 }
176 }
177 }
178}
179
180#[inline]
181pub fn result<T, E: IntoError>(code: ErrorCode, r: Result<T, E>)
182 -> Result<T, Error>
183{
184 r.map_err(|e| e.into_error(code))
185}
186
187#[inline]
188pub fn cmd_result<E: IntoError>(def_code: ErrorCode, r: Result<ExitStatus, E>)
189 -> Result<(), Error>
190{
191 match r.map_err(|e| e.into_error(def_code))? {
192 ExitStatus::Exited(0) => Ok(()),
193 ExitStatus::Exited(x) => Err(Error::AuxCommandExited(x as i32)),
194 ExitStatus::Signaled(x, _) => Err(Error::AuxCommandKilled(x as i32)),
195 }
196}
197
198pub trait IntoError {
199 fn into_error(self, code: ErrorCode) -> Error;
200}
201
202impl IntoError for nix::Error {
203 fn into_error(self, code: ErrorCode) -> Error {
204 match self {
205 nix::Error::Sys(x) => code.wrap(x as i32),
206 _ => Error::NixError,
207 }
208 }
209}
210
211impl IntoError for io::Error {
212 fn into_error(self, code: ErrorCode) -> Error {
213 code.wrap(self.raw_os_error().unwrap_or(-1))
214 }
215}
216
217impl IntoError for Error {
218 fn into_error(self, code: ErrorCode) -> Error {
219 code.wrap(self.raw_os_error().unwrap_or(-1))
220 }
221}
222
223
224impl ErrorCode {
225 pub fn wrap(self, errno: i32) -> Error {
226 use self::ErrorCode as C;
227 use self::Error as E;
228 match self {
229 C::CreatePipe => E::CreatePipe(errno),
230 C::Fork => E::Fork(errno),
231 C::Exec => E::Exec(errno),
232 C::Chdir => E::Chdir(errno),
233 C::ParentDeathSignal => E::ParentDeathSignal(errno),
234 C::PipeError => E::PipeError(errno),
235 C::StdioError => E::StdioError(errno),
236 C::SetUser => E::SetUser(errno),
237 C::ChangeRoot => E::ChangeRoot(errno),
238 C::SetIdMap => E::SetIdMap(errno),
239 C::SetPGid => E::SetPGid(errno),
240 C::SetNs => E::SetNs(errno),
241 C::CapSet => E::CapSet(errno),
242 C::PreExec => E::PreExec(errno),
243 }
244 }
245 pub fn from_i32(code: i32, errno: i32) -> Error {
246 use self::ErrorCode as C;
247 use self::Error as E;
248 match code {
249 c if c == C::CreatePipe as i32 => E::CreatePipe(errno),
250 c if c == C::Fork as i32 => E::Fork(errno),
251 c if c == C::Exec as i32 => E::Exec(errno),
252 c if c == C::Chdir as i32 => E::Chdir(errno),
253 c if c == C::ParentDeathSignal as i32
254 => E::ParentDeathSignal(errno),
255 c if c == C::PipeError as i32 => E::PipeError(errno),
256 c if c == C::StdioError as i32 => E::StdioError(errno),
257 c if c == C::SetUser as i32 => E::SetUser(errno),
258 c if c == C::ChangeRoot as i32 => E::ChangeRoot(errno),
259 c if c == C::SetIdMap as i32 => E::SetIdMap(errno),
260 c if c == C::SetPGid as i32 => E::SetPGid(errno),
261 c if c == C::SetNs as i32 => E::SetNs(errno),
262 c if c == C::CapSet as i32 => E::CapSet(errno),
263 c if c == C::PreExec as i32 => E::PreExec(errno),
265 _ => E::UnknownError,
266 }
267 }
268}