fork
Library for creating a new process detached from the controlling terminal (daemon)
0.10.0
6.1M downloads/mo
#4118 most downloaded on crates.io
immortal/fork
What this package is like to depend on
Last release 1 months ago
18 Jul 2026
Release timing varies
gaps range from 2 weeks to 1.3 years
Some releases are documented
notes for 11 of 35 stable releases
Nothing withdrawn
no release was ever pulled
7 years old
35 releases · first in 2019
10 releases in the last 12 months
see the full history below
Release timeline
35 releases · Oct 2019 to Jul 2026Releases
latest 35-
0.10.018 Jul 2026Release notes
Open source →Added
acquire_subreaper,release_subreaper, andis_subreaperlet a supervisor that is not PID 1 adopt orphaned descendants so thewait_any_*families can observe and reap their terminal state. Acquisition and release are idempotent. The role is process-global, is not inherited acrossfork, and is preserved acrossexec.- Linux support via
PR_SET_CHILD_SUBREAPER/PR_GET_CHILD_SUBREAPERand FreeBSD support viaPROC_REAP_ACQUIRE/PROC_REAP_RELEASE/PROC_REAP_STATUS. All three functions returnErrorKind::Unsupportedon other targets. - Standalone harness-free lifecycle contract covering state transitions,
forknon-inheritance,execpreservation, and orphan adoption.
Safety
- Platform system calls are isolated in a private module and every failure is
converted through
io::Error::last_os_error. - FreeBSD acquisition normalizes
EBUSY(already a reaper) to success, andis_subreaperexcludes PID 1's implicitREAPER_STATUS_REALINITrole so the query reflects only an explicitly acquired attribute, matching Linux. - Adoption observes and reaps orphaned descendants only; it does not enumerate or
signal still-running descendants that escape an owned process group, so
wait_any_*may report PIDs the supervisor did not spawn directly.
-
0.9.114 Jul 2026Release notes
Open source →Added
- Additive
ProcessGroupGuardfor fail-closed process-group owner loss, with a startup anchor, explicit disarm, bounded helper cleanup, descriptor isolation, running/stopped workload contracts, and an explicit session-escape boundary. - Signal-safe disarm over a CLOEXEC Unix socket with
MSG_NOSIGNAL, plus an adaptive bounded helper wait which avoids both fixed startup delay and busy polling. - Standalone single-threaded contracts which kill the owning broker with
SIGKILLbefore and after workload startup, proving that helpers and the workload disappear without Rust destructors, async cleanup, or inherited descriptors.
Safety
- Helper cleanup never signals a numeric PID after a terminal event was reaped or direct-child ownership can no longer be proven, preventing cleanup from targeting a reused PID.
- Workload
TERM,STOP,CONT, andKILLdelivery cannot disable the out-of-group helper. Invalid deadlines, failed exec, dead or stopped helpers, closed standard descriptors, and deliberate session escape have bounded regression contracts. - The API documents exclusive child-reaping ownership and immediate disarm after the final group member exits, because portable Unix cannot pin an empty numeric process-group ID against reuse.
- Descriptor scan bounds now use checked conversions across signed FreeBSD and
unsigned Linux
rlim_tdefinitions. - Process tests own and remove their temporary files, suppress core generation
for intentional
SIGABRTcases, and fail CI if an artifact or test process survives the suite.
Compatibility
- This release only adds public API. Existing
0.9.0callers remain source compatible.
- Additive
-
0.9.013 Jul 2026Release notes
Open source →Added
- Checked positive
ProcessIdandProcessGroupIdtypes. fork_process()andProcessForkfor a typed parent-side fork result.- Explicit process-group creation, joining, inspection, and signal delivery.
- Nonzero
Signalwith portable supervisor signal constants; signal zero is not representable. - Typed
ChildEvent::{Exited, Signalled, Stopped, Continued}collection through blocking and nonblocking child-specific and any-child wait functions. - Serial native lifecycle tests covering exit, stop/continue, signal delivery,
process groups, event draining, cleanup, and
EINTRretry. - Native FreeBSD lifecycle CI (latest release) alongside Linux and macOS.
- Owned close-on-exec
PipeandSocketPairprimitives for broker IPC and exec-status handshakes, including descriptor flag and data-flow tests. - Additive
PreparedCommandfork/exec API with precomputed arguments and environment, bounded startup handshakes, explicit process groups, collision- safe descriptor map/inherit/close actions, numeric identity transitions, and unintended-descriptor closure. - Public API compatibility CI against the published
0.8.0crate. - Additive checked double-fork daemon startup with a bounded readiness/failure channel, preserved descriptors, explicit stdio policy, exact startup stages, and residual cleanup ownership.
- Default child signal-state reset for prepared exec and checked daemon paths, with explicit opt-in inheritance.
- Regression tests pinning the exact portable
Signalconstant values and the behavioral compatibility of the pre-brokerfork/waitpid/waitpid_nohangpath, plus a runnableprocess_brokerexample.
- Checked positive
-
0.8.013 Jun 2026Release notes
Open source →Added
wait_any()andwait_any_nohang()- Add non-breaking wrappers aroundwaitpid(-1, ...)for supervisor-style process management.wait_any()blocks until any child terminates and returns(pid, status).wait_any_nohang()checks for any terminated child without blocking and returnsOk(Some((pid, status)))orOk(None).- Existing
waitpid()andwaitpid_nohang()signatures are unchanged.
- Checked daemon startup example - Added
checked_daemon_pattern.rs, demonstrating how to use a pre-fork pipe with the existing low-level primitives when the launching process must observe setup success or failure.
Improved
- Clarified supervisor identity guidance:
HashMapremains appropriate for tracking multiple live children, but long-lived restart/history state should use an application-owned monotonic id with PID as the current live lookup handle. Updated supervisor examples to show this pattern. - Documented the
daemon(false, false)startup pitfall: relative paths are resolved from/, and stdio/panic diagnostics are discarded through/dev/null. PID/log/config paths should generally be absolute, or startup should usenochdir = true,noclose = true, or a readiness pipe when appropriate.
Fixed
- Corrected README guidance for
close_fd()/close()onEINTR: the safe portable behavior is to callclose()once and treatEINTRas success, not to retry.
-
0.7.009 Mar 2026Release notes
Open source →Fixed
close()no longer retries onEINTR— Previously, the internalclose_retryhelper looped onEINTR, which is unsafe on all modern Unixes:- Linux:
close()always releases the fd before returningEINTR. Retrying can close an unrelated fd opened by another thread between attempts. - FreeBSD / macOS / other BSDs: The fd state after
close()+EINTRis unspecified per POSIX 2008+ (Austin Group defect 529), so retrying is equally dangerous. - The function has been renamed from
close_retrytoclose_onceand now callsclose()exactly once, treating bothEINTRandEBADFas success — the same approach used by Rust'sstd::fs::File::drop(), Go's runtime, and glibc internals. - Note:
open()anddup2()inredirect_stdio()still correctly retry onEINTR, as those calls do not release resources on interruption.
- Linux:
Improved
daemon()return value documentation — Made it prominent thatdaemon()only ever returnsOk(Fork::Child)orErr(...)to the caller;Ok(Fork::Parent(_))is never returned because both parent processes call_exit(0)internally. Added recommendedif letandmatchusage patterns to the doc comment. Updated#[must_use]message to reflect this guarantee.
Code Quality
- Added
test_daemon_never_returns_parentintegration test confirmingFork::Parentis unreachable - Renamed
test_close_retry_ok_and_ebadftotest_close_once_ok_and_ebadf - Replaced fragile
ttycommand string-matching intest_daemon_no_controlling_terminalwith portableopen("/dev/tty")check (works reliably across Linux, macOS, and BSDs) - Replaced fixed 100ms sleep in
test_getppid_after_parent_exitswith a retry loop (up to 1s), preventing flaky failures on slow CI systems - Updated tests README to reflect new and renamed tests
-
0.6.006 Dec 2025Release notes
Open source →Breaking Changes
getpgrp()signature changed - Now returnslibc::pid_tdirectly instead ofio::Result<libc::pid_t>getpgrp()always succeeds per POSIX specification and cannot fail- Migration guide:
- Change
getpgrp()?togetpgrp() - Change
getpgrp().expect("...")togetpgrp() - Change
match getpgrp() { Ok(pgid) => ... }tolet pgid = getpgrp();
- Change
- Rationale: Aligns with POSIX.1 specification and matches
getpid()/getppid()patterns - Verified on Linux, macOS, FreeBSD, OpenBSD per POSIX.1 specification
- Updated all tests and documentation to reflect this guarantee
Improved
- Enhanced documentation - Comprehensive improvements to library documentation
- Added "Common Patterns" section with practical examples:
- Process supervisor using HashMap with Fork
- Inter-process communication via pipes
- Daemon with PID file creation
- Added "Safety and Best Practices" guidelines
- Added detailed "Common Pitfalls and Safety Considerations" to
fork():- Mutexes and locks (deadlock risks)
- File descriptors (shared state issues)
- Signal handlers (inheritance behavior)
- Async-signal-safety between fork and exec
- Memory usage (copy-on-write behavior)
- Enhanced
Forkenum documentation with helper method examples - Added "Platform Compatibility" information
- Added "Common Patterns" section with practical examples:
- Test quality improvements
- Replaced deprecated
signal()withsigaction()in EINTR tests - More portable signal handling for cross-platform compatibility
- Renamed
test_getpgrp_returns_io_error_typetotest_getpgrp_returns_pid_type - Updated test README to reflect current test descriptions
- Replaced deprecated
Fixed
- Documentation warnings - Resolved doctest warnings about main function wrapping
- EINTR resilience -
close_fdandredirect_stdionow retry onEINTRforclose/open/dup2, preventing spurious failures under signal-heavy conditions on Linux, macOS, and BSD - Daemon exit safety - Replaced
std::process::exitin post-fork parents withlibc::_exitto avoid running non-async-signal-safe destructors, preventing undefined behavior betweenfork()andexec()
Code Quality
- Modernized C string handling - Replaced runtime
CString::new()allocations with compile-timec""string literals (Rust 2024 feature)chdir()now usesc"/"instead ofCString::new("/")redirect_stdio()now usesc"/dev/null"instead ofCString::new("/dev/null")- Benefits: Eliminated dead error handling code, zero runtime overhead, compile-time validation
- No API changes, fully backward compatible
- Enhanced code clarity - Added clarifying comments to
redirect_stdio()error handling logic explaining conditional cleanup of file descriptors - Comprehensive test coverage - Added 12 dedicated tests for
chdir()function (346 lines)- Tests idempotent behavior, process isolation, concurrent usage
- Validates modern
c""string literal implementation - Tests integration with
setsid()(daemon pattern) - Total test count increased from 107 to 119 tests
-
0.5.005 Dec 2025Release notes
Open source →Breaking Changes
waitpid()return type changed - Now returnsio::Result<libc::c_int>instead ofio::Result<()>- Returns the raw status code for inspection with
WIFEXITED,WEXITSTATUS,WIFSIGNALED,WTERMSIG, etc. - Migration: Change
waitpid(pid)?tolet status = waitpid(pid)?; assert!(WIFEXITED(status)); - Enables proper exit code checking and signal detection
- See updated examples in documentation
- Returns the raw status code for inspection with
Added
- Fork helper methods - Added convenience methods to
Forkenumis_parent()- Check if this is the parent processis_child()- Check if this is the child processchild_pid()- Get child PID if parent, otherwise None
- Hash trait -
Forknow derivesHash, enabling use inHashMapandHashSet- Useful for process supervisors and tracking multiple children
- Examples:
supervisor.rsandsupervisor_advanced.rs
- must_use attributes - Added
#[must_use]to critical functions to prevent accidental misusefork()- Must check if parent or childdaemon()- Must check daemon resultsetsid()- Must use session IDgetpgrp()- Must use process group ID
waitpid_nohang()function - Non-blocking variant ofwaitpid()- Returns
Ok(Some(status))if child has exited - Returns
Ok(None)if child is still running - Essential for process supervisors and event loops
- Enables polling patterns without blocking
- Includes 7 comprehensive tests
- Returns
- PID helper functions - Convenience wrappers for getting process IDs
getpid()- Get current process ID (always succeeds, hides unsafe)getppid()- Get parent process ID (always succeeds, hides unsafe)
- Status macro re-exports - Convenient access to status inspection macros
- Re-export
WIFEXITED,WEXITSTATUS,WIFSIGNALED,WTERMSIGfrom libc - Users can now
use fork::{waitpid, WIFEXITED, WEXITSTATUS}instead of separate libc import
- Re-export
- Comprehensive test suite - Added extensive tests covering critical edge cases
tests/waitpid_tests.rs- Exit codes, signals, error handling, and non-blocking waitstests/error_handling_tests.rs- Error paths and type verificationtests/pid_tests.rs- PID helper functions (getpid, getppid)tests/status_macro_tests.rs- Status macro re-exports
Improved
- Performance - Added
#[inline]hints to thin wrapper functions (chdir,setsid,getpgrp,getpid,getppid) - Documentation - Enhanced with comprehensive examples and safety considerations
- Added doc test for
setsid()- Session creation example - Added doc test for
getpgrp()- Process group query example - Added doc test for
getpid()- Current PID example - Added doc test for
getppid()- Parent PID example - Enhanced
fork()with safety considerations (file descriptors, mutexes, async-signal-safety, signals, memory) - Enhanced
waitpid()with status inspection examples - Added
waitpid_nohang()with polling patterns and process supervisor examples
- Added doc test for
- Daemon correctness -
daemon()now performs the full double-fork, exiting the intermediate session leader so only the daemon continues- Docs clarified the numbered double-fork stages
- Examples updated (
example_daemon.rs,example_touch_pid.rs) to reflect that only the daemon process returnsFork::Child
- waitpid robustness - Automatic retry on
EINTR(signal interruption)- Takes
pid_tinstead ofi32for better type safety - Returns raw status code enabling exit code inspection and signal detection
- Takes
- Code quality - Simplified
daemon()implementation using?operator consistently - Test coverage - Comprehensive coverage of all error paths and edge cases
- Error handling: Invalid PID (ECHILD), double-wait, session leader errors (EPERM)
- Exit codes: 0, 1, 42, 127, 255, and multiple code variations
- Signal termination: SIGKILL, SIGTERM, SIGABRT detection
- Status inspection: WIFEXITED vs WIFSIGNALED distinction
- Fork helper methods:
is_parent(),is_child(),child_pid() - io::Error type verification for all functions
- CI - GitHub Actions now run tests serially (
RUST_TEST_THREADS=1) and use the latest checkout action
Examples
- Added
supervisor.rs- Basic process supervisor example - Added
supervisor_advanced.rs- Production-ready supervisor with restart policies
Migration Guide (0.4.x → 0.5.0)
Before (0.4.x):
match fork() { Ok(Fork::Parent(child)) => { waitpid(child)?; // Just waits, no status } Ok(Fork::Child) => exit(0), Err(e) => eprintln!("Fork failed: {}", e), }After (0.5.0):
use libc::{WIFEXITED, WEXITSTATUS}; match fork() { Ok(Fork::Parent(child)) => { let status = waitpid(child)?; // Returns status code assert!(WIFEXITED(status), "Child should exit normally"); let exit_code = WEXITSTATUS(status); println!("Child exited with code: {}", exit_code); } Ok(Fork::Child) => exit(0), Err(e) => eprintln!("Fork failed: {}", e), } -
0.4.003 Nov 2025Release notes
Open source →Breaking Changes
- Improved error handling - All functions now return
io::Resultinstead ofResult<T, i32>fork()now returnsio::Result<Fork>(wasResult<Fork, i32>)daemon()now returnsio::Result<Fork>(wasResult<Fork, i32>)setsid()now returnsio::Result<libc::pid_t>(wasResult<libc::pid_t, i32>)getpgrp()now returnsio::Result<libc::pid_t>(wasResult<libc::pid_t, i32>)waitpid()now returnsio::Result<()>(wasResult<(), i32>)chdir()now returnsio::Result<()>(wasResult<libc::c_int, i32>)close_fd()now returnsio::Result<()>(wasResult<(), i32>)
Major Improvements
- Fixed file descriptor reuse bug (Issue #2)
- Added
redirect_stdio()function that redirects stdio to/dev/nullinstead of closing - Prevents silent file corruption when daemon opens files after stdio is closed
daemon()now usesredirect_stdio()instead ofclose_fd()- Matches industry standard implementations (libuv, systemd, BSD daemon(3))
- Added
Benefits
- Better error diagnostics - Errors now capture and preserve
errnovalues - Rich error messages - Error display shows descriptive text (e.g., "Permission denied") instead of
-1 - Rust idioms - Integrates seamlessly with
?operator,anyhow,thiserror, and other error handling crates - Type safety - Can match on
ErrorKindvariants for specific error handling - Debugging -
.raw_os_error()provides access to underlying errno when needed - Correctness - No more file descriptor reuse bugs that could corrupt data files
Added
Forkenum now derivesDebug,Clone,Copy,PartialEq,Eqfor better usabilityredirect_stdio()function - Safer alternative toclose_fd()- Comprehensive tests for stdio redirection (
tests/stdio_redirect_tests.rs)- Test demonstrating the fd reuse bug with
close_fd() - Tests verifying
redirect_stdio()prevents fd reuse - Tests confirming
daemon()uses correct behavior
- Test demonstrating the fd reuse bug with
Improved
- Simplified
close_fd()implementation using iterator pattern - Enhanced documentation with detailed error descriptions for all functions
- Updated all examples to use proper error handling patterns
- Added warnings to
close_fd()documentation about fd reuse risks
Security
- CRITICAL FIX:
daemon()no longer vulnerable to file descriptor reuse bugs- Previously, files opened after
daemon(false, false)could get fd 0, 1, or 2 - Any
println!,eprintln!, or panic would write to those files, corrupting them - Now stdio is redirected to
/dev/null, keeping fd 0,1,2 occupied - New files always get fd >= 3
- Previously, files opened after
- Improved error handling - All functions now return
-
0.3.126 Oct 2025Release notes
Open source →- Added comprehensive test coverage for
getpgrp()function- Unit tests in
src/lib.rs(test_getpgrp,test_getpgrp_in_parent) - Integration test
test_getpgrp_returns_process_groupintests/integration_tests.rs
- Unit tests in
- Added
coveragerecipe to.justfilefor generating coverage reports with grcov
- Added comprehensive test coverage for
-
0.3.019 Oct 2025Release notes
Open source →Changed
- Updated Rust edition from 2021 to 2024
- Applied edition 2024 formatting standards (alphabetical import ordering)
Added
- Integration tests directory - Added
tests/directory with comprehensive integration testsdaemon_tests.rs- 5 tests for daemon functionality (detached process, nochdir, process groups, command execution, no controlling terminal)fork_tests.rs- 7 tests for fork functionality (basic fork, parent-child communication, multiple children, environment inheritance, command execution, different PIDs, waitpid)integration_tests.rs- 5 tests for advanced patterns (double-fork daemon, setsid, chdir, process isolation, getpgrp)
Improved
- Significantly expanded test coverage from 1 to 13 comprehensive unit tests
- Added tests for all public API functions:
fork()- Multiple test scenarios including child executiondaemon()- Daemon pattern tested (double-fork with setsid)waitpid()- Proper parent-child synchronizationsetsid()- Session management and verificationgetpgrp()- Process group querieschdir()- Directory changes with verificationclose_fd()- File descriptor management
- Added real-world usage pattern tests:
- Classic double-fork daemon pattern
- Multiple sequential forks
- Command execution in child processes
- Improved test quality with proper cleanup and zombie process prevention
- Enhanced CI/CD integration with LLVM coverage instrumentation
- Total test count: 35 tests (13 unit + 17 integration + 5 doc tests)
Fixed
- Daemon tests now properly test the daemon pattern without calling
daemon()directly (which would callexit(0)and terminate the test runner)
Updated
- GitHub Actions: codecov/codecov-action from v4 to v5
-
0.2.019 Jul 2024 -
0.1.2301 Feb 2024Nothing published for this version
-
0.1.2206 Jun 2023Nothing published for this version
-
0.1.2112 Mar 2023Nothing published for this version
-
0.1.2023 Aug 2022Nothing published for this version
-
0.1.1911 Mar 2022Nothing published for this version
-
0.1.1824 Oct 2020Nothing published for this version
-
0.1.1720 Jul 2020Nothing published for this version
-
0.1.1620 Jul 2020Nothing published for this version
-
0.1.1522 Jun 2020Nothing published for this version
-
0.1.1402 May 2020Nothing published for this version
-
0.1.1317 Mar 2020Nothing published for this version
-
0.1.1221 Feb 2020Nothing published for this version
-
0.1.1121 Feb 2020Nothing published for this version
-
0.1.1002 Dec 2019Nothing published for this version
-
0.1.928 Oct 2019Nothing published for this version
-
0.1.827 Oct 2019Nothing published for this version
-
0.1.727 Oct 2019Nothing published for this version
-
0.1.626 Oct 2019Nothing published for this version
-
0.1.526 Oct 2019Nothing published for this version
-
0.1.425 Oct 2019Nothing published for this version
-
0.1.325 Oct 2019Nothing published for this version
-
0.1.223 Oct 2019Nothing published for this version
-
0.1.123 Oct 2019Nothing published for this version
-
0.1.023 Oct 2019Nothing published for this version