[][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, so readdir() seems to be safe to call in practice after fork().

  • /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 when exec() occurs. (The standard library CommandExt interface does not drop closures before exec()).

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");