Skip to main content

Crate fork

Crate fork 

Source
Expand description

Library for creating a new process detached from the controlling terminal (daemon).

§Quick Start

use fork::{daemon, Fork};
use std::process::Command;

if let Ok(Fork::Child) = daemon(false, false) {
    Command::new("sleep")
        .arg("3")
        .output()
        .expect("failed to execute process");
}

§Common Patterns

§Process Supervisor

Track multiple worker processes by durable worker id, with a PID lookup for wait results:

use fork::{fork, wait_any_nohang, Fork, WIFEXITED};
use std::collections::HashMap;

#[derive(Clone, Copy, Eq, Hash, PartialEq)]
struct WorkerId(u64);

struct Worker {
    id: WorkerId,
    pid: libc::pid_t,
    name: String,
}

let mut workers = HashMap::new();
let mut by_pid = HashMap::new();

// Spawn 3 workers
for i in 0..3 {
    let id = WorkerId(i);
    match fork()? {
        Fork::Parent(pid) => {
            workers.insert(
                id,
                Worker {
                    id,
                    pid,
                    name: format!("worker-{}", i),
                },
            );
            by_pid.insert(pid, id);
        }
        Fork::Child => {
            // Do work...
            std::thread::sleep(std::time::Duration::from_secs(5));
            std::process::exit(0);
        }
    }
}

// Monitor workers without blocking
while !workers.is_empty() {
    loop {
        match wait_any_nohang()? {
            Some((pid, status)) => {
                if let Some(id) = by_pid.remove(&pid) {
                    let worker = workers.remove(&id).expect("pid map points to worker");
                    if WIFEXITED(status) {
                        println!(
                            "{} (id {}, pid {}) exited",
                            worker.name, worker.id.0, worker.pid
                        );
                    }
                }
                if workers.is_empty() {
                    break;
                }
            }
            None => break,
        }
    }
    std::thread::sleep(std::time::Duration::from_millis(100));
}

§Inter-Process Communication (IPC) via Pipe

use fork::{fork, Fork};
use std::io::{Read, Write};
use std::os::unix::io::FromRawFd;

// Create pipe before forking
let mut pipe_fds = [0i32; 2];
unsafe { libc::pipe(pipe_fds.as_mut_ptr()) };

match fork()? {
    Fork::Parent(_child) => {
        unsafe { libc::close(pipe_fds[1]) };  // Close write end

        let mut reader = unsafe { std::fs::File::from_raw_fd(pipe_fds[0]) };
        let mut msg = String::new();
        reader.read_to_string(&mut msg)?;
        println!("Received: {}", msg);
    }
    Fork::Child => {
        unsafe { libc::close(pipe_fds[0]) };  // Close read end

        let mut writer = unsafe { std::fs::File::from_raw_fd(pipe_fds[1]) };
        writer.write_all(b"Hello from child!")?;
        std::process::exit(0);
    }
}

§Daemon with PID File

use fork::{daemon, Fork, getpid};
use std::fs::File;
use std::io::Write;

if let Ok(Fork::Child) = daemon(false, false) {
    // Write PID file
    let pid = getpid();
    // Use an absolute path: daemon(false, false) changes cwd to `/`.
    let mut file = File::create("/var/run/myapp.pid")?;
    writeln!(file, "{}", pid)?;

    // Run daemon logic...
    loop {
        // Do work
        std::thread::sleep(std::time::Duration::from_secs(60));
    }
}

§Safety and Best Practices

  • Always check fork result - Functions marked #[must_use] prevent accidents
  • Use waitpid() - Reap child processes to avoid zombies
  • Prefer redirect_stdio() - Safer than close_fd() for daemons
  • Fork early - Before creating threads, locks, or complex state
  • Close unused file descriptors - Prevent resource leaks in children
  • Use durable supervisor ids - Treat PIDs as live process handles, not historical identity, because operating systems reuse PIDs after reaping
  • Handle signals properly - Consider what happens in both processes

§Platform Compatibility

This library uses POSIX system calls and is designed for Unix-like systems:

  • Linux (all distributions)
  • macOS (10.5+, replacement for deprecated daemon(3))
  • FreeBSD, OpenBSD, NetBSD
  • Other POSIX-compliant systems

Windows is not supported as it lacks fork() system call.

Enums§

Fork
Fork result

Functions§

WEXITSTATUS
WIFEXITED
WIFSIGNALED
WTERMSIG
chdir
Change dir to / see chdir(2)
close_fd
Close file descriptors stdin, stdout, stderr
daemon
The daemon function is for programs wishing to detach themselves from the controlling terminal and run in the background as system daemons.
fork
Create a new child process see fork(2)
getpgrp
Get the process group ID of the current process see getpgrp(2)
getpid
Get the current process ID see getpid(2)
getppid
Get the parent process ID see getppid(2)
redirect_stdio
Redirect stdin, stdout, stderr to /dev/null
setsid
Create session and set process group ID see setsid(2)
wait_any
Wait for any child process to terminate see wait(2)
wait_any_nohang
Wait for any child process to terminate without blocking see wait(2)
waitpid
Wait for process to change status see wait(2)
waitpid_nohang
Wait for process to change status without blocking see wait(2)