NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
crates.io · #4217 most downloaded on crates.io
Library for creating a new process detached from the controlling terminal (daemon)
Last release 2 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
Full Changelog : 0.9.1...0.10.0
Full Changelog: 0.9.1...0.10.0
acquire_subreaper, release_subreaper, and is_subreaper let a supervisor
that is not PID 1 adopt orphaned descendants so the wait_any_* families can
observe and reap their terminal state. Acquisition and release are idempotent.
The role is process-global, is not inherited across fork, and is preserved
across exec.PR_SET_CHILD_SUBREAPER/PR_GET_CHILD_SUBREAPER and FreeBSD
support via PROC_REAP_ACQUIRE/PROC_REAP_RELEASE/PROC_REAP_STATUS. All
three functions return ErrorKind::Unsupported on other targets.fork
non-inheritance, exec preservation, and orphan adoption.io::Error::last_os_error.EBUSY (already a reaper) to success, and
is_subreaper excludes PID 1's implicit REAPER_STATUS_REALINIT role so the
query reflects only an explicitly acquired attribute, matching Linux.wait_any_* may report PIDs the supervisor did not spawn directly.One column per quarter.
Full Changelog : 0.9.0...0.9.1
Full Changelog: 0.9.0...0.9.1
ProcessGroupGuard for 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.MSG_NOSIGNAL, plus an
adaptive bounded helper wait which avoids both fixed startup delay and busy
polling.SIGKILL before and after workload startup, proving that helpers and the
workload disappear without Rust destructors, async cleanup, or inherited
descriptors.TERM, STOP, CONT, and KILL delivery 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.rlim_t definitions.SIGABRT cases, and fail CI if an artifact or test process
survives the suite.0.9.0 callers remain source
compatible.Full Changelog : 0.8.0...0.9.0
Full Changelog: 0.8.0...0.9.0
ProcessId and ProcessGroupId types.fork_process() and ProcessFork for a typed parent-side fork result.Signal with portable supervisor signal constants; signal zero is
not representable.ChildEvent::{Exited, Signalled, Stopped, Continued} collection through
blocking and nonblocking child-specific and any-child wait functions.EINTR retry.Pipe and SocketPair primitives for broker IPC and
exec-status handshakes, including descriptor flag and data-flow tests.PreparedCommand fork/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.0.8.0 crate.Signal constant values and the
behavioral compatibility of the pre-broker fork/waitpid/waitpid_nohang
path, plus a runnable process_broker example.Full Changelog : 0.7.0...0.8.0
Full Changelog: 0.7.0...0.8.0
wait_any() and wait_any_nohang() - Add non-breaking wrappers around
waitpid(-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
returns Ok(Some((pid, status))) or Ok(None).waitpid() and waitpid_nohang() signatures are unchanged.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.HashMap remains 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.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 use nochdir = true, noclose = true, or a readiness pipe when
appropriate.close_fd()/close() on EINTR: the safe
portable behavior is to call close() once and treat EINTR as success,
not to retry.Full Changelog : 0.6.0...0.7.0
Full Changelog: 0.6.0...0.7.0
close() no longer retries on EINTR — Previously, the internal close_retry helper
looped on EINTR, which is unsafe on all modern Unixes:
close() always releases the fd before returning EINTR.
Retrying can close an unrelated fd opened by another thread between attempts.close() + EINTR is
unspecified per POSIX 2008+ (Austin Group defect 529), so retrying is equally dangerous.close_retry to close_once and now calls close()
exactly once, treating both EINTR and EBADF as success — the same approach used by
Rust's std::fs::File::drop(), Go's runtime, and glibc internals.open() and dup2() in redirect_stdio() still correctly retry on EINTR,
as those calls do not release resources on interruption.daemon() return value documentation — Made it prominent that daemon() only ever
returns Ok(Fork::Child) or Err(...) to the caller; Ok(Fork::Parent(_)) is never
returned because both parent processes call _exit(0) internally. Added recommended
if let and match usage patterns to the doc comment. Updated #[must_use] message
to reflect this guarantee.test_daemon_never_returns_parent integration test confirming Fork::Parent is unreachabletest_close_retry_ok_and_ebadf to test_close_once_ok_and_ebadftty command string-matching in test_daemon_no_controlling_terminal with
portable open("/dev/tty") check (works reliably across Linux, macOS, and BSDs)test_getppid_after_parent_exits with a retry loop (up to 1s),
preventing flaky failures on slow CI systemsFull Changelog : 0.5.0...0.6.0
Full Changelog: 0.5.0...0.6.0
getpgrp() signature changed - Now returns libc::pid_t directly instead of io::Result<libc::pid_t>
getpgrp() always succeeds per POSIX specification and cannot failgetpgrp()? to getpgrp()getpgrp().expect("...") to getpgrp()match getpgrp() { Ok(pgid) => ... } to let pgid = getpgrp();getpid()/getppid() patternsfork():
Fork enum documentation with helper method examplessignal() with sigaction() in EINTR teststest_getpgrp_returns_io_error_type to test_getpgrp_returns_pid_typeclose_fd and redirect_stdio now retry on EINTR for close/open/dup2, preventing spurious failures under signal-heavy conditions on Linux, macOS, and BSDstd::process::exit in post-fork parents with libc::_exit to avoid running non-async-signal-safe destructors, preventing undefined behavior between fork() and exec()CString::new() allocations with compile-time c"" string literals (Rust 2024 feature)
chdir() now uses c"/" instead of CString::new("/")redirect_stdio() now uses c"/dev/null" instead of CString::new("/dev/null")redirect_stdio() error handling logic explaining conditional cleanup of file descriptorschdir() function (346 lines)
c"" string literal implementationsetsid() (daemon pattern)`waitpid()` return type changed - Now returns io::Result instead of io::Result<()>
waitpid() return type changed - Now returns io::Result<libc::c_int> instead of io::Result<()>
WIFEXITED, WEXITSTATUS, WIFSIGNALED, WTERMSIG, etc.waitpid(pid)? to let status = waitpid(pid)?; assert!(WIFEXITED(status));Fork enum
is_parent() - Check if this is the parent processis_child() - Check if this is the child processchild_pid() - Get child PID if parent, otherwise NoneFork now derives Hash, enabling use in HashMap and HashSet
supervisor.rs and supervisor_advanced.rs#[must_use] to critical functions to prevent accidental misuse
fork() - Must check if parent or childdaemon() - Must check daemon resultsetsid() - Must use session IDgetpgrp() - Must use process group IDwaitpid_nohang() function - Non-blocking variant of waitpid()
Ok(Some(status)) if child has exitedOk(None) if child is still runninggetpid() - Get current process ID (always succeeds, hides unsafe)getppid() - Get parent process ID (always succeeds, hides unsafe)WIFEXITED, WEXITSTATUS, WIFSIGNALED, WTERMSIG from libcuse fork::{waitpid, WIFEXITED, WEXITSTATUS} instead of separate libc importtests/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#[inline] hints to thin wrapper functions (chdir, setsid, getpgrp, getpid, getppid)setsid() - Session creation examplegetpgrp() - Process group query examplegetpid() - Current PID examplegetppid() - Parent PID examplefork() with safety considerations (file descriptors, mutexes, async-signal-safety, signals, memory)waitpid() with status inspection exampleswaitpid_nohang() with polling patterns and process supervisor examplesdaemon() now performs the full double-fork, exiting the intermediate session leader so only the daemon continues
example_daemon.rs, example_touch_pid.rs) to reflect that only the daemon process returns Fork::ChildEINTR (signal interruption)
pid_t instead of i32 for better type safetydaemon() implementation using ? operator consistentlyis_parent(), is_child(), child_pid()RUST_TEST_THREADS=1) and use the latest checkout actionsupervisor.rs - Basic process supervisor examplesupervisor_advanced.rs - Production-ready supervisor with restart policiesmatch fork() {
Ok(Fork::Parent(child)) => {
waitpid(child)?; // Just waits, no status
}
Ok(Fork::Child) => exit(0),
Err(e) => eprintln!("Fork failed: {}", e),
}
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),
}
CRITICAL FIX: daemon() no longer vulnerable to file descriptor reuse bugs
io::Result instead of Result<T, i32>
fork() now returns io::Result<Fork> (was Result<Fork, i32>)daemon() now returns io::Result<Fork> (was Result<Fork, i32>)setsid() now returns io::Result<libc::pid_t> (was Result<libc::pid_t, i32>)getpgrp() now returns io::Result<libc::pid_t> (was Result<libc::pid_t, i32>)waitpid() now returns io::Result<()> (was Result<(), i32>)chdir() now returns io::Result<()> (was Result<libc::c_int, i32>)close_fd() now returns io::Result<()> (was Result<(), i32>)redirect_stdio() function that redirects stdio to /dev/null instead of closingdaemon() now uses redirect_stdio() instead of close_fd()errno values-1? operator, anyhow, thiserror, and other error handling cratesErrorKind variants for specific error handling.raw_os_error() provides access to underlying errno when neededFork enum now derives Debug, Clone, Copy, PartialEq, Eq for better usabilityredirect_stdio() function - Safer alternative to close_fd()tests/stdio_redirect_tests.rs)
close_fd()redirect_stdio() prevents fd reusedaemon() uses correct behaviorclose_fd() implementation using iterator patternclose_fd() documentation about fd reuse risksdaemon() no longer vulnerable to file descriptor reuse bugs
daemon(false, false) could get fd 0, 1, or 2println!, eprintln!, or panic would write to those files, corrupting them/dev/null, keeping fd 0,1,2 occupiedAdded comprehensive test coverage for getpgrp() function
getpgrp() function
src/lib.rs (test_getpgrp, test_getpgrp_in_parent)test_getpgrp_returns_process_group in tests/integration_tests.rscoverage recipe to .justfile for generating coverage reports with grcovUpdated Rust edition from 2021 to 2024
tests/ directory with comprehensive integration tests
daemon_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)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 managementdaemon() directly
(which would call exit(0) and terminate the test runner)* Added waitpid(pid: i32)
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →