Skip to main content

Container

Struct Container 

Source
pub struct Container { /* private fields */ }
Expand description

A Container is a configuration of how a process shall be spawned. It can, but doesn’t have to, include Linux namespace configuration.

NOTE: Configuring resource limits via cgroups is not yet supported.

Implementations§

Source§

impl Container

Source

pub fn new() -> Self

Creates a new Container that inherits everything from the parent process.

Source

pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Self
where K: AsRef<OsStr>, V: AsRef<OsStr>,

Inserts or updates an environment variable mapping.

Note that environment variable names are case-insensitive (but case-preserving) on Windows, and case-sensitive on all other platforms.

§Examples

Basic usage:

use reverie_process::Container;

let container = Container::new().env("PATH", "/bin");
Source

pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self
where I: IntoIterator<Item = (K, V)>, K: AsRef<OsStr>, V: AsRef<OsStr>,

Adds or updates multiple environment variable mappings.

§Examples

Basic usage:

use std::collections::HashMap;
use std::env;

use reverie_process::Container;
use reverie_process::Stdio;

let filtered_env: HashMap<String, String> = env::vars()
    .filter(|&(ref k, _)| k == "TERM" || k == "TZ" || k == "LANG" || k == "PATH")
    .collect();

let container = Container::new()
    .stdin(Stdio::null())
    .stdout(Stdio::inherit())
    .env_clear()
    .envs(&filtered_env);
Source

pub fn env_remove<K: AsRef<OsStr>>(&mut self, key: K) -> &mut Self

Removes an environment variable mapping.

§Examples

Basic usage:

use reverie_process::Container;

let container = Container::new().env_remove("PATH");
Source

pub fn env_clear(&mut self) -> &mut Self

Clears the entire environment map for the child process.

§Examples

Basic usage:

use reverie_process::Container;

let container = Container::new().env_clear();
Source

pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Self

Sets the working directory for the child process.

§Interaction with chroot

The working directory is set after the chroot is performed (if a chroot directory is specified). Thus, the path given is relative to the chroot directory. Otherwise, if no chroot directory is specified, the working directory is relative to the current working directory of the parent process at the time the child process is spawned.

§Platform-specific behavior

If the program path is relative (e.g., "./script.sh"), it’s ambiguous whether it should be interpreted relative to the parent’s working directory or relative to current_dir. The behavior in this case is platform specific and unstable, and it’s recommended to use canonicalize to get an absolute program path instead.

§Examples

Basic usage:

use reverie_process::Container;

let container = Container::new().current_dir("/bin");
Source

pub fn stdin<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self

Sets configuration for the child process’s standard input (stdin) handle.

Defaults to Stdio::inherit when used with spawn or status, and defaults to Stdio::piped when used with output.

§Examples

Basic usage:

use reverie_process::Container;
use reverie_process::Stdio;

let container = Container::new().stdin(Stdio::null());
Source

pub fn stdout<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self

Sets configuration for the child process’s standard output (stdout) handle.

Defaults to Stdio::inherit when used with spawn or status, and defaults to Stdio::piped when used with output.

§Examples

Basic usage:

use reverie_process::Container;
use reverie_process::Stdio;

let container = Container::new().stdout(Stdio::null());
Source

pub fn stderr<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self

Sets configuration for the child process’s standard error (stderr) handle.

Defaults to Stdio::inherit when used with spawn or status, and defaults to Stdio::piped when used with output.

§Examples

Basic usage:

use reverie_process::Container;
use reverie_process::Stdio;

let container = Container::new().stderr(Stdio::null());
Source

pub fn chroot<P: AsRef<Path>>(&mut self, chroot: P) -> &mut Self

Changes the root directory of the calling process to the specified path. This directory will be inherited by all child processes of the calling process.

Note that changing the root directory may cause the program to not be found. As such, the program path should be relative to this directory.

Source

pub fn unshare(&mut self, namespace: Namespace) -> &mut Self

Unshares parts of the process execution context that are normally shared with the parent process. This is useful for executing the child process in a new namespace.

Source

pub fn get_current_dir(&self) -> Option<&Path>

Returns the working directory for the child process.

This returns None if the working directory will not be changed.

Source

pub fn get_envs(&self) -> impl Iterator<Item = (&OsStr, Option<&OsStr>)>

Returns an iterator of the environment variables that will be set when the process is spawned. Note that this does not include any environment variables inherited from the parent process.

Source

pub fn get_captured_envs(&self) -> BTreeMap<OsString, OsString>

Returns a mapping of all environment variables that the new child process will inherit.

Source

pub fn get_env<K: AsRef<OsStr>>(&self, env: K) -> Option<Cow<'_, OsStr>>

Gets an environment variable. If the child process is to inherit this environment variable from the current process, then this returns the current process’s environment variable unless it is to be overridden.

Source

pub fn map_uid(&mut self, inside_uid: uid_t, outside_uid: uid_t) -> &mut Self

Maps one user ID to another.

Implies Namespace::USER.

§Example

This is can be used to gain CAP_SYS_ADMIN privileges in the user namespace by mapping the root user inside the container to the current user outside of the container.

use reverie_process::Container;

let container = Container::new().map_uid(1, unsafe { libc::getuid() });
§Implementation

This modifies /proc/{pid}/uid_map where {pid} is the PID of the child process. See user_namespaces(7) for more details.

Source

pub fn map_uid_range( &mut self, starting_inside_uid: uid_t, starting_outside_uid: uid_t, count: u32, ) -> &mut Self

Maps potentially many user IDs inside the new user namespace to user IDs outside of the user namespace.

Implies Namespace::USER.

§Implementation

This modifies /proc/{pid}/uid_map where {pid} is the PID of the child process. See user_namespaces(7) for more details.

Source

pub fn map_root(&mut self) -> &mut Self

Convience function for mapping root (inside the container) to the current user ID (outside the container). This is useful for gaining new capabilities inside the container, such as being able to mount file systems.

Implies Namespace::USER.

This is the same as:

use reverie_process::Container;

let container = Container::new()
    .map_uid(0, unsafe { libc::geteuid() })
    .map_gid(0, unsafe { libc::getegid() });
Source

pub fn map_gid(&mut self, inside_gid: gid_t, outside_gid: gid_t) -> &mut Self

Maps one group ID to another.

Implies Namespace::USER.

§Implementation

This modifies /proc/{pid}/gid_map where {pid} is the PID of the child process. See user_namespaces(7) for more details.

Source

pub fn map_gid_range( &mut self, starting_inside_gid: gid_t, starting_outside_gid: gid_t, count: u32, ) -> &mut Self

Maps potentially many group IDs inside the new user namespace to group IDs outside of the user namespace.

Implies Namespace::USER.

§Implementation

This modifies /proc/{pid}/gid_map where {pid} is the PID of the child process. See user_namespaces(7) for more details.

Source

pub fn hostname<S: Into<OsString>>(&mut self, hostname: S) -> &mut Self

Sets the hostname of the container.

Implies Namespace::UTS, which requires CAP_SYS_ADMIN.

use reverie_process::Container;

let container = Container::new().map_root().hostname("foobar.local");
Source

pub fn domainname<S: Into<OsString>>(&mut self, domainname: S) -> &mut Self

Sets the domain name of the container.

Implies Namespace::UTS, which requires CAP_SYS_ADMIN.

§Example
use reverie_process::Container;

let container = Container::new().map_root().domainname("foobar");
Source

pub fn get_hostname(&self) -> Option<&OsStr>

Gets the hostname of the container.

Source

pub fn get_domainname(&self) -> Option<&OsStr>

Gets the domainname of the container.

Source

pub fn mount(&mut self, mount: Mount) -> &mut Self

Adds a file system to be mounted. Note that these are mounted in the same order as given.

Implies Namespace::MOUNT. Note that Namespace::USER should also have been set and map_uid should have been called in order to gain the privileges required to mount.

Source

pub fn mounts<I>(&mut self, mounts: I) -> &mut Self
where I: IntoIterator<Item = Mount>,

Adds multiple mounts.

Source

pub fn local_networking_only(&mut self) -> &mut Self

Sets up the container to have local networking only. This will prevent any network communication to the outside world.

Implies Namespace::NETWORK and Namespace::MOUNT.

This also causes a fresh /sys to be mounted to avoid seeing the host network interfaces in /sys/class/net.

Source

pub fn seccomp(&mut self, filter: Filter) -> &mut Self

Sets the seccomp filter. The filter is loaded immediately before execve and after all pre_exec callbacks have been executed. Thus, you will still be able to call filtered syscalls from pre_exec callbacks.

Source

pub fn seccomp_notify(&mut self) -> &mut Self

Indicates that we want to listen for seccomp events using seccomp_unotify(2).

If this is set, the seccomp listener file descriptor will be accessible via the Child.

Source

pub fn pty(&mut self, child: PtyChild) -> &mut Self

Sets the controlling pseudoterminal for the child process).

In the child process, this has the effect of:

  1. Creating a new session (with setsid()).
  2. Using an ioctl to set the controlling terminal.
  3. Setting this file descriptor as the stdio streams.

NOTE: Since this modifies the stdio streams, calling this will reset Self::stdin, Self::stdout, and Self::stderr back to Stdio::inherit().

Source

pub fn affinity(&mut self, affinity: usize) -> &mut Self

Sets the CPU to which the child threads/processes will be pinned.

Source

pub fn run<F, T>(&mut self, f: F) -> Result<T, RunError>
where F: FnMut() -> T, T: Serialize + DeserializeOwned,

Runs a function in a new process with the specified namespaces unshared. This blocks until the function itself returns and the process has exited.

§Safety
  • This should be called early on in the life of a process, before any other threads are created. This reduces the chance that any global resources (like the Tokio runtime) have been created yet.

  • Memory allocated in the parent must not be freed in the child, especially if using jemalloc where a separate thread does deallocations.

Source

pub fn run_with_startup<P, C, F, O, S, T, D>( &mut self, timeout: Duration, parent_start: P, child_start: C, run: F, ) -> Result<(O, DeferredContainerRun<T>), StartupRunError>

Runs child setup and a parent readiness callback before installing the unchanged seccomp filter and entering the child workload.

child_start runs after namespace/filesystem setup, without creating a helper task. It returns child-local state and may transfer up to MAX_STARTUP_FDS owned descriptors through its context. parent_start runs in the original process, with the actual owned child and received descriptors, and must return only when its external resources are ready. Its returned owner stays in the parent. Only then may run consume the child state. Startup endpoint aliases close before seccomp is installed.

The positive, representable timeout gives the entire protocol one monotonic I/O deadline, including time spent in callbacks. It does not preempt arbitrary callback code or destructors. Failure cancels and reaps the owned child; actual cleanup errors remain errors. Kernel waits for an uninterruptible child still require outer process supervision.

Like Self::run, call this before starting other threads. No signal handler or other thread may reap this child; SIGCHLD auto-reaping is rejected before clone. Callbacks must obey the existing fork-safety rules, close unrelated inherited descriptors, and not fork workers in the child. This API does not prove capture completion or guest teardown.

Results are drained before wait, including large values. run returns a deferred cleanup value just as Self::run_with_deferred_drop does; the returned handle’s DeferredContainerRun::finalize_with_status checks the real terminal status before yielding the value.

Source

pub fn run_with_startup_owned<P, C, F, S, T, U>( &mut self, timeout: Duration, parent_start: &mut P, child_start: &mut C, run: &mut F, ) -> Result<OwnedDeferredContainerRun<T>, StartupOwnedFailure<T>>

Runs the existing startup protocol with retained child/result ownership.

Call before starting threads; no handler or other thread may reap this child. The callbacks are borrowed. Parent setup returns unit: retain initialized parent resources outside this call and pair them with every returned owner, including errors. This API cannot clean external state. Child callbacks obey the same fork-safety and no-child-worker contract as Self::run_with_startup. Namespace/filter/signal policy is unchanged.

Linux pidfd support is required and probed before clone. Startup uses one finite timeout, without preempting callbacks. Result acquisition then blocks draining the pipe before wait, with no workload deadline or value size cap. On read failure the same FD and partial bytes remain owned. Generic deserialization happens only after actual successful child wait. Explicit cleanup observation is bounded; implicit Drop can block and requires outer process supervision for uninterruptible/unknown cleanup.

Source

pub fn run_with_deferred_drop_owned<F, T, D>( &mut self, run: &mut F, ) -> Result<OwnedDeferredContainerRun<T>, StartupOwnedFailure<T>>
where F: FnMut() -> (T, D), T: Serialize,

Runs a deferred workload while retaining the original child/result owner.

This has the fork-safety requirements of Self::run: call before starting threads, with no handler or other thread reaping this child. The workload may create its own workers; it is not subject to the startup callbacks’ no-child-worker contract. The borrowed factory and any external parent resources must remain alive through every returned owner, including errors. Ownership here covers the direct child, not arbitrary descendants. A normal raw-clone callback return exits only its calling thread. Join worker threads before returning, or arrange an explicit group exit (for example _exit in D) if those workers must end with the callback.

The child publishes and closes its encoded result before dropping D. Linux pidfd support is required and probed before clone. After clone, failures retain the original wait, reader and exact partial bytes, with no implicit cleanup attempt. The workload may already have started before such a failure is detected. Call the retained owner’s explicit cancellation/observation methods with an absolute deadline; failed results stay failed even when the child later exits successfully. Atomic pidfd availability is validated after the initial drain: a real read failure is reported first; otherwise missing identity is a protocol refusal with complete encoded bytes and EOF retained.

Initial result acquisition blocks draining the pipe before wait, without a workload deadline or size cap. Explicit finalization bounds do not bound that acquisition or child serialization. Implicit owner Drop can block; callers requiring a hard bound need outer process supervision. Generic result decoding is available only after an actual successful child wait, including for an encoded container-setup refusal.

Source

pub fn run_with_deferred_drop<F, T, D>( &mut self, f: F, ) -> Result<DeferredContainerRun<T>, RunError>
where F: FnMut() -> (T, D), T: Serialize + DeserializeOwned,

Runs a function in a new process, publishes its result, and only then drops a child-owned cleanup value.

The returned handle owns the mandatory wait for the child. Callers may inspect the provisional value while doing independent work, but can only take ownership of it through DeferredContainerRun::finalize, which rejects an unsuccessful child exit. Dropping the handle still reaps the child, but yields no value.

A caller therefore cannot accidentally destructure the value away from the mandatory cleanup check:

ⓘ
use reverie_process::Container;
let (value, cleanup) = Container::new()
    .run_with_deferred_drop(|| (42, ()))
    .unwrap();

This has the same fork-safety requirements as Container::run.

Trait Implementations§

Source§

impl Default for Container

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.