Process file descriptors (`pidfd`) for Linux ============================================ Process file descriptors (`pidfd`) provide a race-free way to manage processes on Linux, maintaining a persistent reference to a process using a file descriptor rather than a numeric process ID (PID) that could be reused after the process exits. This crate only works on Linux; if you need support for other platforms, or for older Linux kernels, see [async-process](https://crates.io/crates/async-process). `async-pidfd` provides Rust support for pidfd, and supports managing processes both synchronously (via the `PidFd` type) and asynchronously (via the `AsyncPidFd` type). Sync - `PidFd` -------------- The `PidFd` type manages processes synchronously. Use `PidFd::from_pid` to construct a `PidFd` from a process ID, such as from [`Child::id`](https://doc.rust-lang.org/std/process/struct.Child.html#method.id) in the standard library. (Note that the portable `Child::id` function returns process IDs as `u32`, rather than as a `libc::pid_t`, necessitating a cast.) ```rust use std::os::unix::process::ExitStatusExt; use std::process::{Command, ExitStatus}; use async_pidfd::PidFd; fn main() -> std::io::Result<()> { let child = Command::new("/bin/true").spawn()?; let pidfd = PidFd::from_pid(child.id() as libc::pid_t)?; let status = pidfd.wait()?.status(); assert_eq!(status.code(), Some(0)); let child = Command::new("/bin/sh").arg("-c").arg("kill -9 $$").spawn()?; let pidfd = PidFd::from_pid(child.id() as libc::pid_t)?; let status = pidfd.wait()?.status(); assert_eq!(status.signal(), Some(9)); Ok(()) } ``` `PidFd::wait` returns information about an exited process via the `ExitInfo` structure. `ExitInfo` includes a `libc::siginfo_t` indicating how the process exited (including the exit code if it exited normally, or the signal if it was killed by a signal), and a `libc::rusage` describing the resource usage of the process and its children. `libc::siginfo_t` has complex semantics; to get a [`std::process::ExitStatus`](https://doc.rust-lang.org/std/process/struct.ExitStatus.html) instead, you can call `.status()` on an `ExitInfo`. Note that while opening the PID for an arbitrary process can potentially race with the exit of that process, opening the PID for a child process that you have not yet waited on is safe, as the process ID will not get reused until you wait on the process (or block `SIGCHLD`). If you only want to use the synchronous `PidFd` type, you can use `async-pidfd` with `default-features = false` in `Cargo.toml` to remove async-related dependencies. Async - `AsyncPidFd` -------------------- The `AsyncPidFd` type manages processes asynchronously, based on the [`async-io`](https://docs.rs/async-io/) crate by Stjepan Glavina. `async-io` provides an `Async` wrapper that makes it easy to turn any synchronous type based on a file descriptor into an asynchronous type; the resulting asynchronous code uses `epoll` to wait for all the file descriptors concurrently. `AsyncPidFd` wraps an `Async` and provides the same API as `PidFd`, but with an `async` version of the `wait` function. ```rust use std::os::unix::process::ExitStatusExt; use std::process::{Command, ExitStatus}; use async_pidfd::AsyncPidFd; use futures_lite::future; async fn async_spawn_and_status(cmd: &mut Command) -> std::io::Result { let child = cmd.spawn()?; let pidfd = AsyncPidFd::from_pid(child.id() as libc::pid_t)?; Ok(pidfd.wait().await?.status()) } fn main() -> std::io::Result<()> { future::block_on(async { let (status1, status2) = future::try_join( async_spawn_and_status(&mut Command::new("/bin/true")), async_spawn_and_status(&mut Command::new("/bin/false")), ) .await?; assert_eq!(status1.code(), Some(0)); assert_eq!(status2.code(), Some(1)); Ok(()) }) } ```