[−][src]Function closefds::close_fds_on_exec
pub fn close_fds_on_exec(
keep_fds: Vec<RawFd>
) -> Result<impl FnMut() -> Result<()>>
Create a closure that will set the FD_CLOEXEC
flag on all open file descriptors when called.
This function should be called before invoking fork()
as it may allocate memory. The
returned closure should be called after fork()
by the child process.
This function may fail to create the closure. Additionally, the closure may also fail when called. However, in no case will we fall back to an implementation that does not guarantee that all open file descriptors have been successfully processed (ie: We will not look up the max number of open file descriptors and then attempt to close all file descriptors up to that number as such an approach may fail to process some file descriptors if the max number of open file descriptors changes).
keep_fds
is a Vec
of file descriptors to ensure that the FD_CLOEXEC
flag is
not set on. FD_CLOEXEC
will be set on all other file descriptors.
Current Implementation
The current implementation opens either the /proc/self/fd/
directory (Linux) or /dev/fd/
directory (BSDs) in the parent process with opendir()
. readdir()
is used in the child
process to iterate over the entries in that directory and set the FD_CLOEXEC
flag as
appropriate.
Notes:
-
readdir()
is not async-signal-safe according to any standard. However, the process spawning code in both Python and Java work similarly, soreaddir()
seems to be safe to call in practice afterfork()
. -
/proc/self/fd/
or/dev/fd/
directories must be available. -
The returned closure needs to be dropped in the parent process in order to close the opened directory. However, it must not be dropped in the child process as doing so will call
free()
which may deadlock - all resources will instead be freed whenexec()
occurs. (The standard libraryCommandExt
interface does not drop closures beforeexec()
).
Future Implementations
A future version of this library may change the implementation for all supported operating systems or for specific operating systems. The likely reasons to do so would be to improve performance, to remove calls to non-async-signal-safe functions, or to remove the dependency on specific directories being available. However, any future implementation is guaranteed to still process all open file descriptors.
Example
The following example will spawn a child process while making sure that only STDIN, STDOUT, and STDERR are inherited.
Command::new("path/to/program") .pre_exec(close_fds_on_exec(vec![0, 1, 2])?) .spawn() .expect("Spawn Failed");