1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
//! # Why is this crate useful? //! //! By default, any file descriptors opened by a process are inherited by any of that process's //! children. This can cause major bugs and security issues, but this behavior can't be changed //! without massively breaking backwards compatibility. //! //! Rust hides most of these problems by setting the close-on-exec flag on all file descriptors //! opened by the standard library (which causes them to *not* be inherited by child processes). //! However, in some scenarios it may be necessary to have a way to close all open file //! descriptors. //! //! ## Scenarios where this is helpful //! //! - Writing set-UID programs (which may not be able to fully trust their environment; for //! example, `sudo` closes all open file descriptors when it starts as a security measure) //! - Spawning processes while interacting with FFI code that *doesn't* set the close-on-exec flag //! on file descriptors it opens (the functionality offered by this crate is the ONLY way to //! safely do this) //! - On some platforms (notably, macOS/iOS), Rust isn't always able to set the close-on-exec flag //! *atomically*, which creates race conditions if one thread is e.g. opening sockets while //! another thread is spawning processes. This crate may be useful in helping to avoid those //! race conditions. //! //! # Example usage //! //! Here is a short program that uses `close_fds` to close all file descriptors (except the ones //! in a specified list) in a child process before launching it: //! //! ``` //! use std::process::Command; //! use std::os::unix::prelude::*; //! //! // Add any file descriptors that should stay open here //! let mut keep_fds = []; //! // ALWAYS sort the slice! It will give significant performance improvements. //! keep_fds.sort_unstable(); //! //! let mut cmd = Command::new("true"); //! //! // ... //! // Set up `cmd` here //! // ... //! //! unsafe { //! cmd.pre_exec(move || { //! // On macOS/iOS, just set them as close-on-exec (some sources indicate closing them //! // directly may cause problems) //! #[cfg(any(target_os = "macos", target_os = "ios"))] //! close_fds::set_fds_cloexec(3, &keep_fds); //! #[cfg(not(any(target_os = "macos", target_os = "ios")))] //! close_fds::close_open_fds(3, &keep_fds); //! //! Ok(()) //! }); //! } //! //! // Launch the child process //! //! cmd.status().unwrap(); //! ``` //! //! # Builders vs. helper functions //! //! [`close_open_fds()`], [`set_fds_cloexec()`], [`iter_open_fds()`], etc. provide simple ways to //! close, set the close-on-exec flag on, or iterate over all open file descriptors. //! //! However, for more advanced use cases, there are also "builders" ([`CloseFdsBuilder`], //! [`FdIterBuilder`]) which can be used to further customize aspects of the actions that will be //! taken. The documentation of each helper function describes how the same task could be performed //! using one of the builders. //! //! # Async-signal-safety //! //! ## Background //! //! An async-signal-safe function is one that is safe to call from a signal handler (many functions //! in the system libc are not!). In a multi-threaded program, when running in the child after a //! `fork()` (such as in a closure registered with //! `std::os::unix::process::CommandExt::pre_exec()`, it is only safe to call async-signal-safe //! functions. See `signal-safety(7)` and `fork(2)` for more information. //! //! ## Async-signal-safety in this crate //! //! **TL;DR**: The functions in this crate are async-signal-safe on Linux, macOS/iOS, the BSDs, and //! Solaris/Illumos. They *should* also be async-signal-safe on other \*nix-like OSes. //! //! Since the functions in this crate are most useful in the child process after a `fork()`, this //! crate tries to make all of them async-signal-safe. However, many of the optimizations that this //! crate performs in order to be efficient would not be possible by simply calling functions that //! POSIX requires to be async-signal-safe. //! //! As a result, this crate assumes that the following functions are async-signal-safe (in addition //! to the ones required by POSIX): //! //! - `closefrom()` on the BSDs //! - The `close_range()` syscall on Linux and FreeBSD //! - `sysctl()` on FreeBSD //! - `getdtablecount()` on OpenBSD //! - `getdirentries()`/`getdents()` (whichever is available) on Linux, NetBSD, FreeBSD, macOS/iOS, //! and Solaris/Illumos //! - `sysconf(_SC_OPEN_MAX)` on all OSes //! //! All of these except for `sysconf()` are implemented as system calls (or thin wrappers around //! other system calls) on whichever OS(es) they are present on. As a result, they should be //! async-signal-safe, even though they are not explicitly documented as such. //! //! `sysconf()` is not guaranteed to be async-signal-safe. However, on Linux, macOS/iOS, the BSDs, //! and Solaris/Illumos, `sysconf(_SC_OPEN_MAX)` is implemented in terms of //! `getrlimit(RLIMIT_NOFILE)`. On those platforms, `getrlimit()` is a system call, so //! `sysconf(_SC_OPEN_MAX)` (and thus, the functions in this crate) should be async-signal-safe. #![no_std] mod closefds; mod iterfds; mod sys; mod util; pub use closefds::*; pub use iterfds::*;