Library for creating a new process detached from the controlling terminal (daemon) on Unix-like systems.
- ✅ Minimal - Small, focused library for process forking and daemonization
- ✅ Safe - Comprehensive test coverage (35 tests: 13 unit + 17 integration + 5 doc)
- ✅ Well-documented - Extensive documentation with real-world examples
- ✅ Unix-first - Built specifically for Unix-like systems (Linux, macOS, BSD)
- ✅ Edition 2024 - Uses latest Rust edition features
- Minimal library to daemonize, fork, double-fork a process
- daemon(3) has been
deprecated in macOS 10.5. By using
forkandsetsidsyscalls, new methods can be created to achieve the same goal - Provides the building blocks for creating proper Unix daemons
Add fork to your Cargo.toml:
[dependencies]
fork = "0.3"Or use cargo-add:
cargo add forkuse fork::{daemon, Fork};
use std::process::Command;
fn main() {
if let Ok(Fork::Child) = daemon(false, false) {
// This code runs in the daemon process
Command::new("sleep")
.arg("300")
.output()
.expect("failed to execute process");
}
}use fork::{fork, Fork};
match fork() {
Ok(Fork::Parent(child)) => {
println!("Parent process, child PID: {}", child);
}
Ok(Fork::Child) => {
println!("Child process");
}
Err(_) => println!("Fork failed"),
}fork()- Creates a new child processdaemon(nochdir, noclose)- Creates a daemon using double-fork patternnochdir: iffalse, changes working directory to/noclose: iffalse, redirects stdin/stdout/stderr to/dev/null
setsid()- Creates a new session and sets the process group IDwaitpid(pid)- Waits for child process to change stategetpgrp()- Returns the process group IDchdir()- Changes current directory to/close_fd()- Closes stdin, stdout, and stderr
See the documentation for detailed usage.
When using daemon(false, false), it will change directory to / and close the standard file descriptors.
Test running:
$ cargo runUse ps to check the process:
$ ps -axo ppid,pid,pgid,sess,tty,tpgid,stat,uid,%mem,%cpu,command | egrep "myapp|sleep|PID"Output:
PPID PID PGID SESS TTY TPGID STAT UID %MEM %CPU COMMAND
1 48738 48737 0 ?? 0 S 501 0.0 0.0 target/debug/myapp
48738 48753 48737 0 ?? 0 S 501 0.0 0.0 sleep 300
Key points:
PPID == 1- Parent is init/systemd (orphaned process)TTY = ??- No controlling terminal- New
PGID = 48737- Own process group
Process hierarchy:
1 - root (init/systemd)
└── 48738 myapp PGID - 48737
└── 48753 sleep PGID - 48737
The daemon() function implements the classic double-fork pattern:
- First fork - Creates child process
- setsid() - Child becomes session leader
- Second fork - Grandchild is created (not a session leader)
- First child exits - Leaves grandchild orphaned
- Grandchild continues - As daemon (no controlling terminal)
This prevents the daemon from ever acquiring a controlling terminal.
The library has comprehensive test coverage:
- 13 unit tests in
src/lib.rs - 17 integration tests in
tests/directory - 5 documentation tests
Run tests:
cargo testSee tests/README.md for detailed information about integration tests.
This library is designed for Unix-like operating systems:
- ✅ Linux
- ✅ macOS
- ✅ FreeBSD
- ✅ NetBSD
- ✅ OpenBSD
- ❌ Windows (not supported)
See the examples/ directory for more usage examples:
example_daemon.rs- Daemon creationexample_pipe.rs- Fork with pipe communicationexample_touch_pid.rs- PID file creation
Run an example:
cargo run --example example_daemonContributions are welcome! Please ensure:
- All tests pass:
cargo test - Code is formatted:
cargo fmt - No clippy warnings:
cargo clippy -- -D warnings - Documentation is updated
BSD 3-Clause License - see LICENSE file for details.