unshare/
error.rs

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/// Error runnning process
27///
28/// This type has very large number of options and it's enum only to be
29/// compact. Probably you shouldn't match on the error cases but just format
30/// it for user into string.
31#[derive(Debug)]
32pub enum Error {
33    /// Unknown nix error
34    ///
35    /// Frankly, this error should not happen when running process. We just
36    /// keep it here in case `nix` returns this error, which should not happen.
37    NixError, // Not sure it's possible, but it is here to convert from
38                 // nix::Error safer
39    /// Some invalid error code received from child application
40    UnknownError,
41    /// Error happened when we were trying to create pipe. The pipes used for
42    /// two purposes: (a) for the process's stdio (`Stdio::pipe()` or
43    /// `Stdio::null()`), (b) internally to wake up child process and return
44    /// error back to the parent.
45    // TODO(tailhook) should create pipe be split into PipeError and StdioError
46    CreatePipe(i32),
47    /// Error when forking/cloning process
48    Fork(i32),
49    /// Error when running execve() systemcall
50    Exec(i32),
51    /// Error when setting working directory specified by user
52    Chdir(i32),
53    /// Unable to set death signal (probably signal number invalid)
54    ParentDeathSignal(i32),
55    /// Error reading/writing through one of the two signal pipes
56    PipeError(i32),
57    /// Error waiting for process (for some functions only, for example
58    /// ``Command::status()``). It probably means someone already waited for
59    /// the process, for example it might be other thread, or signal handler.
60    WaitError(i32),
61    /// Error setting up stdio for process
62    StdioError(i32),
63    /// Could not set supplementary groups, group id  or user id for the
64    /// process
65    SetUser(i32),
66    /// Error changing root, it explains `chroot`, `pivot_root` system calls
67    /// and setting working directory inside new root. Also includes unmounting
68    /// old file system for pivot_root case.
69    ChangeRoot(i32),
70    /// Error setting uid or gid map. May be either problem running
71    /// `newuidmap`/`newgidmap` command or writing the mapping file directly
72    SetIdMap(i32),
73    /// Auxillary command failed
74    ///
75    /// There are two auxillary commands for now: `newuidmap` and `newgidmap`.
76    /// They run only when uid mappings (user namespaces) are enabled.
77    ///
78    /// Note that failing to run the binary results to `SedIdMap(sys_errno)`,
79    /// this error contains status code of command that was succesfullly
80    /// spawned.
81    AuxCommandExited(i32),
82    /// Auxillary command was killed by signal
83    ///
84    /// Similar to `AuxCommandExited` but when command was killed
85    AuxCommandKilled(i32),
86    /// Error when calling setpgid function
87    SetPGid(i32),
88    /// Error when calling setns syscall
89    SetNs(i32),
90    /// Error when calling capset syscall
91    CapSet(i32),
92    /// Before unfreeze callback error
93    BeforeUnfreeze(Box<dyn (::std::error::Error) + Send + Sync + 'static>),
94    /// Before exec callback error
95    PreExec(i32),
96}
97
98impl Error {
99    /// Similarly to `io::Error` returns bare error code
100    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                // May be OS knows error name better
162                write!(fmt, "{}: {}", self.title(),
163                    io::Error::from_raw_os_error(code))
164            } else {
165                // Format similar to that of std::io::Error
166                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            // no BeforeUnfreeze, because can't be in a child
264            c if c == C::PreExec as i32 => E::PreExec(errno),
265            _ => E::UnknownError,
266        }
267    }
268}