#[non_exhaustive]pub enum Error {
Config(ConfigError),
#[non_exhaustive] UsernsUnavailable {
blocker: Option<UsernsBlocker>,
source: Error,
},
NestedUsernsBudgetExhausted,
#[non_exhaustive] Setup {
step: SetupStep,
source: Error,
detail: Option<String>,
},
#[non_exhaustive] Spawn {
source: Error,
},
#[non_exhaustive] Wait {
source: Error,
},
SupervisorLost,
#[non_exhaustive] Signal {
source: Error,
},
IdentityMap(IdMapError),
#[non_exhaustive] Terminal {
source: Error,
},
TerminalsExhausted,
}Expand description
An error from the library itself.
Cage::run returns Err only when the library fails: invalid
configuration, an unsupported host, a failed setup step, or a failure to
spawn or wait. The sandboxed command’s own exit code is data, carried by
ExitStatus, and is never an Error.
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
Config(ConfigError)
The sandbox configuration was rejected at build time.
The host cannot create unprivileged user namespaces.
Produced when namespace creation is denied by the kernel. When the
probe in crate::host identifies the mechanism responsible, it is
carried in blocker.
NestedUsernsBudgetExhausted
The host’s user-namespace budget is exhausted, so the command could not enter the nested user namespace that locks the sandbox’s mount flags.
A user namespace is charged against user.max_user_namespaces at every
level up to the initial namespace, and a launch holds two: the sandbox’s
own, and the nested one the command enters. A host whose ceiling admits
the first and not the second reports this rather than a bare ENOSPC.
Distinct from UsernsUnavailable, which is a
host that permits no user namespace at all: here the sandbox was built
and only the second namespace was refused.
#[non_exhaustive]Setup
A sandbox setup step failed in the child process.
Fields
This variant is marked as non-exhaustive
detail: Option<String>What the step was operating on, when the step has a subject — for a
mount step, the mount it was assembling; for
Exec, the command path, and for a command
resolved by path lookup, the candidates the search tried.
An ENOENT from the exec step means either that the command itself
is absent or that its ELF interpreter is: a dynamically linked
binary whose loader is missing from the rootfs reports the same
errno as a missing binary. The detail names the command, not the
interpreter, so a path that plainly exists inside the rootfs points
at the second reading.
#[non_exhaustive]Spawn
The sandbox process could not be created.
Fields
This variant is marked as non-exhaustive
#[non_exhaustive]Wait
The sandbox process outcome could not be collected.
Fields
This variant is marked as non-exhaustive
SupervisorLost
The sandbox supervisor exited without reporting the command’s outcome.
The supervisor always reports the command’s wait status before it
exits; its silent disappearance means it was killed from outside or
exited abnormally, and the command’s outcome is unknown. A kill
requested through the handle is not this error: after
Running::kill, the outcome is reported as termination by
SIGKILL.
#[non_exhaustive]Signal
The sandbox could not be signaled through the handle.
Fields
This variant is marked as non-exhaustive
IdentityMap(IdMapError)
The identity-map delegate failed to establish the range map.
The delegate runs caller-side against the gated launch, so its own error is the diagnostic; the gated sandbox is torn down.
#[non_exhaustive]Terminal
A pseudoterminal operation failed: allocating one for the sandbox, or reading or setting its window size.
Fields
This variant is marked as non-exhaustive
TerminalsExhausted
The host has no free pseudoterminal to allocate.
Pseudoterminals are a bounded resource — /proc/sys/kernel/pty/max
states the ceiling — so a host running many sandboxes at once can
exhaust them. Distinct from Terminal because it is
the one allocation failure that says nothing is wrong with the request:
the same launch succeeds once something releases a terminal.
Implementations§
Source§impl Error
impl Error
Sourcepub fn shell_code(&self) -> u8
pub fn shell_code(&self) -> u8
The exit code a launcher reports when a launch fails, following the
conventions sh and timeout(1) established.
A caller whose whole purpose is to run one command inside a sandbox is a launcher, and a launcher’s own failures have to be distinguishable from the command’s exit codes. The conventions are:
| Code | Meaning |
|---|---|
| 127 | The command does not exist. |
| 126 | The command exists but could not be executed. |
| 125 | The launcher itself failed, for any other reason. |
The distinction between 127 and 126 comes from the errno the
Exec step failed with. An ENOENT there has two
readings — the command is absent, or its ELF interpreter is — and both
are reported as 127, since neither produced a runnable process.
This is what the fcage binary returns. A caller with its own
convention is free to map Error itself; this is the answer for one
that has none, and the one that makes a consumer behave like a shell.
124, timeout(1)’s “the deadline expired”, is not produced here: an
expired deadline is not an Error, it is a
Running::wait_timeout that returned no
status, so only the caller knows it happened.
§Example
use std::process::ExitCode;
use ferroday_cage::Cage;
fn launch(rootfs: &str) -> ExitCode {
match Cage::builder().command("/bin/true").rootfs(rootfs).build() {
Ok(_cage) => ExitCode::SUCCESS,
Err(error) => {
eprintln!("myapp: {error}");
ExitCode::from(error.shell_code())
}
}
}Trait Implementations§
Source§impl Error for Error
impl Error for Error
Source§fn source(&self) -> Option<&(dyn Error + 'static)>
fn source(&self) -> Option<&(dyn Error + 'static)>
The failure underneath, where there is one.
Every OS failure this type carries is an io::Error, so every one of
them is returned here. The variants that answer None carry nothing:
NestedUsernsBudgetExhausted,
SupervisorLost, and
TerminalsExhausted are conditions the
library recognized rather than syscalls that failed.
1.0.0 · Source§fn description(&self) -> &str
fn description(&self) -> &str
use the Display impl or to_string()
Source§impl From<ConfigError> for Error
impl From<ConfigError> for Error
Source§fn from(err: ConfigError) -> Self
fn from(err: ConfigError) -> Self
Source§impl From<Error> for DebianError
impl From<Error> for DebianError
Source§impl From<Error> for RelayError
impl From<Error> for RelayError
Source§fn from(error: Error) -> RelayError
fn from(error: Error) -> RelayError
Auto Trait Implementations§
impl !RefUnwindSafe for Error
impl !UnwindSafe for Error
impl Freeze for Error
impl Send for Error
impl Sync for Error
impl Unpin for Error
impl UnsafeUnpin for Error
Blanket Implementations§
Source§impl<T> AsErrorSource for Twhere
T: Error + 'static,
impl<T> AsErrorSource for Twhere
T: Error + 'static,
Source§fn as_error_source(&self) -> &(dyn Error + 'static)
fn as_error_source(&self) -> &(dyn Error + 'static)
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> FmtForward for T
impl<T> FmtForward for T
Source§fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
fn fmt_binary(self) -> FmtBinary<Self>where
Self: Binary,
self to use its Binary implementation when Debug-formatted.Source§fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
fn fmt_display(self) -> FmtDisplay<Self>where
Self: Display,
self to use its Display implementation when
Debug-formatted.Source§fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
fn fmt_lower_exp(self) -> FmtLowerExp<Self>where
Self: LowerExp,
self to use its LowerExp implementation when
Debug-formatted.Source§fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
fn fmt_lower_hex(self) -> FmtLowerHex<Self>where
Self: LowerHex,
self to use its LowerHex implementation when
Debug-formatted.Source§fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
fn fmt_octal(self) -> FmtOctal<Self>where
Self: Octal,
self to use its Octal implementation when Debug-formatted.Source§fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
fn fmt_pointer(self) -> FmtPointer<Self>where
Self: Pointer,
self to use its Pointer implementation when
Debug-formatted.Source§fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
fn fmt_upper_exp(self) -> FmtUpperExp<Self>where
Self: UpperExp,
self to use its UpperExp implementation when
Debug-formatted.Source§fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
fn fmt_upper_hex(self) -> FmtUpperHex<Self>where
Self: UpperHex,
self to use its UpperHex implementation when
Debug-formatted.Source§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
self, then passes self.deref() into the pipe function.impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
.tap_deref() only in debug builds, and is erased in release
builds.