pidfd-util 0.1.0

Safe Rust wrapper for Linux process file descriptors (pidfd)
Documentation
# pidfd-util-rs

Safe Rust wrapper for Linux process file descriptors (pidfd).

This crate provides a safe, ergonomic interface to Linux's pidfd API, which represents processes as file descriptors. Unlike traditional PIDs, pidfds cannot be reused after a process exits, making them safe from PID reuse race conditions.

## Features

- **Safe process operations**: Send signals, wait for exit, query process information
- **PID reuse protection**: Pidfds remain valid and unique even after process termination
- **Modern kernel support**: Uses modern kernel APIs when available, falls back to older methods
- **Async support**: Optional async operations via the `async` feature (enabled by default)
- **Nightly compatibility**: Uses stdlib's `PidFd` on nightly, provides own implementation on stable

## Installation

Add this to your `Cargo.toml`:

```toml
[dependencies]
pidfd-util = { version = "0.1.0", git = "https://github.com/swick/pidfd-util-rs.git" }
```

## Obtaining a PidFd

The typical ways to obtain a pidfd include:

- **Using `clone3` with `CLONE_PIDFD`**: On nightly Rust, use `std::process::Command` with `create_pidfd(true)` and then call `into_pidfd()` on the child process.
- **Clearing the `CLOEXEC` flag and exec'ing**: Clear the close-on-exec flag using `libc::fcntl` with `libc::F_SETFD`, then exec the target process.
- **Passing via UNIX socket**: Use `sendmsg`/`recvmsg` on a UNIX socket to pass the file descriptor between processes. This is exposed in `std::os::unix::net::UnixStream::recv_vectored_with_ancillary`.

⚠️ **Warning**: While `PidFd::from_pid()` exists, its use is highly discouraged. There is a race condition where the process with the PID dies and a new process gets assigned the same recycled PID, causing the resulting pidfd to refer to the wrong process.

## Usage

### Basic Example (Nightly)

```rust
use pidfd_util::{PidFd, PidFdExt};
use std::os::linux::process::{CommandExt, ChildExt};
#![feature(linux_pidfd)]
use std::process::Command;

fn main() -> std::io::Result<()> {
    // Spawn a child process with pidfd support
    let mut child = Command::new("echo")
        .create_pidfd(true)
        .spawn()
        .expect("Failed to spawn child");

    // Get the pidfd for the child
    let pidfd = child
        .into_pidfd()
        .expect("Failed to retrieve pidfd");

    // Query process information
    let pid = pidfd.get_pid().expect("Failed to get the child PID");
    let creds = pidfd.get_creds().expect("Failed to get child credentials");
    println!("Process {} running as UID {}", pid, creds.euid);

    // Send a signal
    pidfd.send_signal(libc::SIGTERM).expect("Failed to send SIGTERM to child");

    // Wait for process to exit
    let status = pidfd.wait().expect("Failed to wait for child to exit");
    println!("Process exited with status: {:?}", status);

    Ok(())
}
```

### Async Example

```rust
use pidfd_util::{PidFd, PidFdExt, AsyncPidFd};

async fn example(pidfd: PidFd) -> std::io::Result<()> {
    let async_pidfd: AsyncPidFd = pidfd.try_into()?;

    // Asynchronously wait for the process to exit
    let status = async_pidfd.wait().await?;
    println!("Process exited with: {:?}", status);

    Ok(())
}
```

## Core Types

- **`PidFd`**: The main type representing a process file descriptor
- **`PidFdExt`**: Extension trait providing additional operations (get PID, credentials, namespaces, etc.)
- **`AsyncPidFd`**: Async wrapper for waiting on process exit (requires `async` feature)
- **`PidfdCreds`**: Process credential information (UID, GID variants)
- **`PidfdGetNamespace`**: Namespace types that can be queried

## Key Operations

### Process Information

- `get_pid()` - Get the process ID of the process referred to by the pidfd
- `get_ppid()` - Get the parent process ID
- `get_id()` - Get a unique, non-reusable process identifier (useful for logging)
- `get_creds()` - Get process credentials (UIDs/GIDs)
- `get_cgroupid()` - Get the cgroup ID

### Process Control

- `kill()` - Send SIGKILL to the process
- `send_signal()` - Send any signal to the process
- `wait()` - Wait for process exit (blocking)
- `try_wait()` - Check if process has exited (non-blocking)

### Advanced Operations

- `get_namespace()` - Get a file descriptor to a process namespace
- `set_namespace()` - Move the calling process into a namespace of the target process
- `get_remote_fd()` - Duplicate a file descriptor from another process
- `access_proc()` - Execute a function with PID reuse protection

## Kernel Requirements

- Basic pidfd support requires **Linux 5.3+**
- Some operations require newer kernels (automatically detected with fallback where possible)

## Cargo Features

- `async` (default): Enables `AsyncPidFd` for async process waiting
- `nightly`: Uses stdlib's `PidFd` implementation on nightly Rust

## License

This project is licensed under either of:

- MIT License
- Apache License, Version 2.0

at your option.