pub struct Command { /* private fields */ }
Expand description
Main class for running processes. Works in the spirit of builder pattern.
Implementations§
Source§impl Command
impl Command
Sourcepub fn new<S: AsRef<OsStr>>(program: S) -> Command
pub fn new<S: AsRef<OsStr>>(program: S) -> Command
Constructs a new Command
for launching the program at
path program
, with the following default configuration:
- No arguments to the program
- Inherit the current process’s environment
- Inherit the current process’s working directory
- Inherit stdin/stdout/stderr for
spawn
orstatus
, but create pipes foroutput
Builder methods are provided to change these defaults and otherwise configure the process.
Sourcepub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Command
pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Command
Add an argument to pass to the program.
Sourcepub fn args<S: AsRef<OsStr>>(&mut self, args: &[S]) -> &mut Command
pub fn args<S: AsRef<OsStr>>(&mut self, args: &[S]) -> &mut Command
Add multiple arguments to pass to the program.
Sourcepub fn env<K, V>(&mut self, key: K, val: V) -> &mut Command
pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Command
Inserts or updates an environment variable mapping.
Sourcepub fn envs<I, K, V>(&mut self, vars: I) -> &mut Command
pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Command
Inserts or updates multiple environment variable mappings.
Sourcepub fn env_remove<K: AsRef<OsStr>>(&mut self, key: K) -> &mut Command
pub fn env_remove<K: AsRef<OsStr>>(&mut self, key: K) -> &mut Command
Removes an environment variable mapping.
Sourcepub fn env_clear(&mut self) -> &mut Command
pub fn env_clear(&mut self) -> &mut Command
Clears the entire environment map for the child process.
Sourcepub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Command
pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Command
Sets the working directory for the child process.
Note: in case of chroot
or pivot_root
the working directory is
always set to something inside the new root. Algorithm is following:
- If path is set to absolute path, current dir is this path inside the chroot
- Check if chroot dir is prefix of
env::current_dir()
. If it is set current directory to the suffix. Otherwise set current directory to the new root dir. - If
current_dir
is specified (and relative) set working directory to the value (i.e. relative to the dir set in #2)
The pivot_root
is treated just the same as chroot
. I.e. we will
not try to set working directory inside the old_root
, unless path
inside is set explicitly by this method.
At the end of the day, the cmd.current_dir(env::current_dir())
is
not no-op if using chroot/pivot_root.
Sourcepub fn stdin(&mut self, cfg: Stdio) -> &mut Command
pub fn stdin(&mut self, cfg: Stdio) -> &mut Command
Configuration for the child process’s stdin handle (file descriptor 0).
Sourcepub fn stdout(&mut self, cfg: Stdio) -> &mut Command
pub fn stdout(&mut self, cfg: Stdio) -> &mut Command
Configuration for the child process’s stdout handle (file descriptor 1).
Sourcepub fn stderr(&mut self, cfg: Stdio) -> &mut Command
pub fn stderr(&mut self, cfg: Stdio) -> &mut Command
Configuration for the child process’s stderr handle (file descriptor 2).
Sourcepub fn uid(&mut self, id: uid_t) -> &mut Command
pub fn uid(&mut self, id: uid_t) -> &mut Command
Set user id of the new process. Note that it works only for root process or if you also set up user namespace
Source§impl Command
impl Command
Sourcepub fn before_unfreeze(
&mut self,
f: impl FnMut(u32) -> Result<(), Box<dyn Error + Send + Sync + 'static>> + 'static,
) -> &mut Self
pub fn before_unfreeze( &mut self, f: impl FnMut(u32) -> Result<(), Box<dyn Error + Send + Sync + 'static>> + 'static, ) -> &mut Self
Set a callback to run when child is already forked but not yet run
When starting a child we sometimes need more setup from the parent, for example: to configure pid namespaces for the unprivileged process (child) by privileged process (parent).
This callback runs in parent process after all built-in setup is
done (setting uid namespaces). It always run before pre_exec
callback in child.
If callback returns error, process is shut down.
Each invocation replaces callback, so there is only one of them can be called.
Sourcepub unsafe fn pre_exec(
&mut self,
f: impl Fn() -> Result<()> + Send + Sync + 'static,
) -> &mut Self
pub unsafe fn pre_exec( &mut self, f: impl Fn() -> Result<()> + Send + Sync + 'static, ) -> &mut Self
Set a callback to run in the child before calling exec
The callback is executed right before execve
system calls.
All other modifications of the environment are already applied
at this moment. It always run after before_unfreeze
in parent.
Warning this callback must not do any memory (de)allocations,
use mutexes, otherwise process may crash or deadlock. Only bare
syscalls are allowed (use libc
crate).
The closure is allowed to return an I/O error whose OS error code will be communicated back to the parent and returned as an error from when the spawn was requested.
Note: unlike same method in stdlib, each invocation of this method replaces callback, so there is only one of them can be called.
Source§impl Command
impl Command
Sourcepub fn allow_daemonize(&mut self) -> &mut Command
pub fn allow_daemonize(&mut self) -> &mut Command
Allow child process to daemonize. By default we run equivalent of
set_parent_death_signal(SIGKILL)
. See the set_parent_death_signal
for better explanation.
Sourcepub fn set_parent_death_signal(&mut self, sig: Signal) -> &mut Command
pub fn set_parent_death_signal(&mut self, sig: Signal) -> &mut Command
Set a signal that is sent to a process when it’s parent is dead.
This is by default set to SIGKILL
. And you should keep it that way
unless you know what you are doing.
Particularly you should consider the following choices:
-
Instead of setting
PDEATHSIG
to some other signal, send signal yourself and wait until child gracefully finishes. -
Instead of daemonizing use
systemd
/upstart
/whatever system init script to run your service
Another issue with this option is that it works only with immediate child. To better control all descendant processes you may need the following:
-
The
prctl(PR_SET_CHILD_SUBREAPER..)
in parent which allows to “catch” descendant processes. -
The pid namespaces
The former is out of scope of this library. The latter works by
cmd.unshare(Namespace::Pid)
, but you may need to setup mount points
and other important things (which are out of scope too).
To reset this behavior use allow_daemonize()
.
Sourcepub fn chroot_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Command
pub fn chroot_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Command
Set chroot dir. Only absolute path is supported
This method has a non-standard security feature: even if current_dir
is unspecified we set it to the directory inside the new root dir.
see more details in the description of Command::current_dir
.
Note that if both chroot dir and pivot_root specified. The chroot dir is applied after pivot root. If chroot dir is relative it’s relative to either suffix of the current directory with stripped off pivot dir or the pivot dir itself (if old workdir is not prefixed by pivot dir)
§Panics
If directory is not absolute
Sourcepub fn pivot_root<A: AsRef<Path>, B: AsRef<Path>>(
&mut self,
new_root: A,
put_old: B,
unmount: bool,
) -> &mut Command
pub fn pivot_root<A: AsRef<Path>, B: AsRef<Path>>( &mut self, new_root: A, put_old: B, unmount: bool, ) -> &mut Command
Moves the root of the file system to the directory put_old
and
makes new_root
the new root file system. Also it’s optionally
unmount new_root
mount point after moving root (but it must exist
anyway).
The documentation says that put_old
must be underneath the
new_root
. Currently we have a restriction that both must be absolute
and new_root
be prefix of put_old
, but we may lift it later.
Warning if you don’t unshare the mount namespace you will get
moved filesystem root for all processes running in that namespace
including parent (currently running) process itself. If you don’t
run equivalent to mount --make-private
for the old root filesystem
and set unmount
to true, you may get unmounted filesystem for
running processes too.
See man 2 pivot
for further details
Note that if both chroot dir and pivot_root specified. The chroot dir is applied after pivot root.
§Panics
Panics if either path is not absolute or new_root is not a prefix of put_old.
Unshare given namespaces
Note: each namespace have some consequences on how new process will
work, some of them are described in the Namespace
type documentation.
Sourcepub fn set_namespace<F: AsRawFd>(
&mut self,
file: &F,
ns: Namespace,
) -> Result<&mut Command>
pub fn set_namespace<F: AsRawFd>( &mut self, file: &F, ns: Namespace, ) -> Result<&mut Command>
Reassociate child process with a namespace specified by a file descriptor
file
argument is an open file referring to a namespace
‘ns’ is a namespace type
See man 2 setns
for further details
Note: using unshare
and setns
for the same namespace is meaningless.
Sourcepub fn set_id_maps(
&mut self,
uid_map: Vec<UidMap>,
gid_map: Vec<GidMap>,
) -> &mut Command
pub fn set_id_maps( &mut self, uid_map: Vec<UidMap>, gid_map: Vec<GidMap>, ) -> &mut Command
Sets user id and group id mappings for new process
This automatically enables User
namespace. You should also set uid
and gid
with respective methods for the new process.
Note there are basically two ways to enable id maps:
- Write them directly
- Invoke a
newuidmap
,newgidmap
commands
First option works either if current process is root or if resulting map only contains current user in the mapping.
The library will not try to guess the behavior. By default it will
write directly. You need to call the set_id_map_commands
when you
want non-default behavior.
See man 7 user_namespaces
for more info
Sourcepub fn set_id_map_commands<A: AsRef<Path>, B: AsRef<Path>>(
&mut self,
newuidmap: A,
newgidmap: B,
) -> &mut Command
pub fn set_id_map_commands<A: AsRef<Path>, B: AsRef<Path>>( &mut self, newuidmap: A, newgidmap: B, ) -> &mut Command
Set path to command-line utilities for writing uid/gid maps
The utilities provided my obey same interface as newuidmap
and
newgidmap
from shadow
(or sometimes uidmap
) package. To get it
working you usually need to setup /etc/subuid
and /etc/subgid
files.
See man 1 newuidmap
, man 1 newgidmap
for details
This method is no-op unless set_id_maps
is called.
Sourcepub fn keep_sigmask(&mut self) -> &mut Command
pub fn keep_sigmask(&mut self) -> &mut Command
Keep signal mask intact after executing child, keeps also ignored signals
By default signal mask is empty and all signals are reset to the
SIG_DFL
value right before execve()
syscall.
This is only useful if started process is aware of the issue and sets sigmasks to some reasonable value. When used wisely it may avoid some race conditions when signal is sent after child is cloned but before child have been able to establish it’s state.
Sourcepub fn arg0<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Command
pub fn arg0<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Command
Set the argument zero for the process
By default argument zero is same as path to the program to run. You
may set it to a short name of the command or to something else to
pretend there is a symlink to a program (for example to run gzip
as
gunzip
).
Sourcepub fn make_group_leader(&mut self, make_group_leader: bool) -> &mut Command
pub fn make_group_leader(&mut self, make_group_leader: bool) -> &mut Command
Makes child process a group leader
If child process is being launched as a foreground job,
the child process group needs to be put into the foreground on
the controlling terminal using tcsetpgrp
. To request status
information from stopped child process you should call waitpid
with
WUNTRACED
flag. And then check status with WIFSTOPPED
macro.
After giving child process group access to the controlling terminal
you should send the SIGCONT signal to the child process group.
Sourcepub fn env_var_with_pid<K>(&mut self, key: K) -> &mut Command
pub fn env_var_with_pid<K>(&mut self, key: K) -> &mut Command
Inserts a magic environment variable that will contain pid of spawned process
This is usually needed to avoid accidental propagation of the environment variables targeted only at this specific process.
§Example
This is how you can encode systemd activation protocol:
cmd.env_var_with_pid("LISTEN_PID");
cmd.env("LISTEN_FDS", "1");
Sourcepub fn keep_caps<'x>(&mut self, caps: impl IntoIterator<Item = &'x Capability>)
pub fn keep_caps<'x>(&mut self, caps: impl IntoIterator<Item = &'x Capability>)
Drop all capabilities, but keep only ones set by this setter
This method sets three or four sets of capabilities:
- Permitted
- Inherited
- Effective
- Ambient (if supported)
This works both when uid changes (from 0 to other) and when it isn’t changed, but requires process to have all capabilities granted by this method.
This method replaces whole capability mask on each invocation
Source§impl Command
impl Command
Sourcepub fn file_descriptor(&mut self, target_fd: RawFd, cfg: Fd) -> &mut Command
pub fn file_descriptor(&mut self, target_fd: RawFd, cfg: Fd) -> &mut Command
Configuration for any other file descriptor (panics for fds < 3) use stdin/stdout/stderr for them
Rust creates file descriptors with CLOEXEC flag by default, so no descriptors are inherited except ones specifically configured here (and stdio which is inherited by default)
Sourcepub fn close_fds<A: Into<AnyRange>>(&mut self, range: A) -> &mut Command
pub fn close_fds<A: Into<AnyRange>>(&mut self, range: A) -> &mut Command
Close a range of file descriptors as soon as process forks
Subsequent calls to this method add additional range. Use reset_fds
to remove all the ranges.
File descriptors that never closed are:
- the stdio file descriptors
- descriptors configured using
file_descriptor
/file_descriptor_raw
methods - internal file descriptors used for parent child notification by unshare crate itself (they are guaranteed to have CLOEXEC)
You should avoid this method if possilble and rely on CLOEXEC to do the work. But sometimes it’s inevitable:
- If you need to ensure closing descriptors for security reasons
- If you have some bad library out of your control which doesn’t set CLOEXEC on owned the file descriptors
Ranges obey the following rules:
- Range like
..12
is transformed into3..12
- Range with undefined upper bound
3..
is capped at current ulimit for file descriptors at the moment of calling the method - The full range
..
is an alias to3..
- Multiple overlapping ranges are closed multiple times which is both harmless and useless
§Panics
Panics when can’t get rlimit for range without upper bound. Should never happen in practice.
Panics when lower range of fd is < 3 (stdio file descriptors)