stream_non_blocking/
stream_non_blocking.rs

1//! Example: Non-blocking stream reading
2//!
3//! This example demonstrates how to do non-blocking reads from the process stream,
4//! allowing your application to continue doing other work while checking for output.
5//!
6//! Note: This example uses SeccompProfile::Unrestricted because bash requires
7//! many syscalls for proper operation. For simple commands like `echo`, you can
8//! use more restrictive profiles like Minimal or Compute.
9
10use sandbox_rs::{SandboxBuilder, SeccompProfile, StreamChunk};
11use std::time::Duration;
12use tempfile::tempdir;
13
14fn main() -> sandbox_rs::Result<()> {
15    let tmp = tempdir().expect("Failed to create temp dir");
16
17    let mut sandbox = SandboxBuilder::new("non-blocking-example")
18        .memory_limit_str("256M")?
19        .cpu_limit_percent(50)
20        .seccomp_profile(SeccompProfile::Unrestricted)
21        .root(tmp.path())
22        .build()?;
23
24    println!("Running process with non-blocking stream reads...\n");
25
26    // Run a process that outputs slowly
27    let (result, stream) = sandbox.run_with_stream(
28        "/bin/bash",
29        &[
30            "-c",
31            "for i in {1..3}; do echo \"Message $i\"; sleep 0.1; done",
32        ],
33    )?;
34
35    println!("Non-blocking polling:");
36
37    let mut received_chunks = 0;
38    let mut polling_attempts = 0;
39    let mut final_exit_code = result.exit_code;
40
41    loop {
42        polling_attempts += 1;
43
44        // Try to read without blocking
45        match stream.try_recv()? {
46            Some(chunk) => match chunk {
47                StreamChunk::Stdout(line) => {
48                    println!("[STDOUT] {}", line);
49                    received_chunks += 1;
50                }
51                StreamChunk::Stderr(line) => {
52                    eprintln!("[STDERR] {}", line);
53                    received_chunks += 1;
54                }
55                StreamChunk::Exit {
56                    exit_code,
57                    signal: _,
58                } => {
59                    println!("Process exited with code: {}", exit_code);
60                    // Capture the real exit code from the stream
61                    final_exit_code = exit_code;
62                    break;
63                }
64            },
65            None => {
66                // No data available right now, we could do other work here
67                std::thread::sleep(Duration::from_millis(10));
68            }
69        }
70
71        // Safety limit to prevent infinite loop in case of issues
72        if polling_attempts > 10000 {
73            println!("Safety timeout reached");
74            break;
75        }
76    }
77
78    // Update result with the actual exit code from streaming
79    let mut result = result;
80    result.exit_code = final_exit_code;
81
82    println!("\nStatistics:");
83    println!("  Chunks received: {}", received_chunks);
84    println!("  Polling attempts: {}", polling_attempts);
85    println!("  Exit code: {}", result.exit_code);
86    println!("  Wall time: {} ms", result.wall_time_ms);
87
88    // Check for seccomp errors and return error if found
89    result.check_seccomp_error()?;
90
91    Ok(())
92}