Skip to content

swick/pidfd-util-rs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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:

[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)

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

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.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages