StdioConfig
StdioConfig controls how standard streams (stdin, stdout, stderr) are configured for child processes.
Overview
When spawning a child process, each standard stream can be configured independently:
Inherit- The child inherits the parent’s stream. Output appears in the parent’s terminal. Useful for debugging.Null- The child’s stream is connected to/dev/null. All output is discarded. Suitable for background processes.Piped- The child’s stream is piped.ProcessManager::start()spawns reader threads that forward output totracing. This prevents pipe buffer deadlocks when a child produces significant output.
graph TD
classDef default fill: #1e1e1e, stroke: #333333, stroke-width: 1px, color: #ffffff
classDef inherit fill: #00a1e4, stroke: #ffffff, stroke-width: 2px, color: #ffffff
classDef null fill: #f5b700, stroke: #333333, stroke-width: 2px, color: #000000
classDef piped fill: #89fc00, stroke: #333333, stroke-width: 2px, color: #000000
classDef thread fill: #dc0073, stroke: #333333, stroke-width: 1px, color: #000000
Child["Child Process"]
Child -->|"Inherit"| Parent["Parent's terminal<br/><small>output visible</small>"]
Child -->|"Null"| DevNull[" /dev/null<br/><small>output discarded</small>"]
Child -->|"Piped"| Pipe["OS Pipe<br/><small>64KB buffer</small>"]
Pipe --> Reader["Reader Thread<br/><small>tracing::debug! / error!</small>"]
class Child inherit
class Parent inherit
class DevNull null
class Pipe piped
class Reader thread
Default
StdioConfig::Null - all streams are null by default. This is the safest default for background processes and services that should not pollute the parent’s output.
Serde
StdioConfig implements Serialize and Deserialize with #[serde(rename_all = "lowercase")], so it serializes as "inherit", "null", and "piped".
Why Reader Threads for Piped?
When StdioConfig::Piped is used, the OS creates a pipe with a fixed buffer size (typically 64KB). If the child writes more than the buffer can hold and nobody reads the pipe, the child blocks - causing a deadlock. ProcessManager::start() spawns dedicated reader threads that continuously read from the pipe and forward lines to tracing::debug! / tracing::error!, preventing this deadlock.
Usage
#![allow(unused)]
fn main() {
use process_manager::{ProcessConfig, StdioConfig};
// Background process - discard all output
let config = ProcessConfig::builder()
.command("daemon".to_string())
.stdin(StdioConfig::Null)
.stdout(StdioConfig::Null)
.stderr(StdioConfig::Null)
.build();
// Debug mode - inherit parent's streams
let config = ProcessConfig::builder()
.command("my-app".to_string())
.stdin(StdioConfig::Inherit)
.stdout(StdioConfig::Inherit)
.stderr(StdioConfig::Inherit)
.build();
// Capture output via tracing
let config = ProcessConfig::builder()
.command("my-app".to_string())
.stdout(StdioConfig::Piped)
.stderr(StdioConfig::Piped)
.build();
// Reader threads will forward stdout to tracing::debug!
// and stderr to tracing::error!
}