Capsa is experimental software. APIs may change without notice.
Skip to content

Console ​

The console provides raw serial port access. For command execution, use the agent instead.

When to Use the Console ​

  • Waiting for boot output
  • Interactive troubleshooting
  • VMs without agent support

Enabling Console Access ​

Enable the console on your VM builder:

rust,no_run
let vm = capsa::vm(boot)
    .console_enabled()
    .build()
    .await?;

let console = vm.console().await?;

Waiting for Boot ​

Use wait_for() to detect when the VM has booted:

rust,no_run
// Wait for login prompt
console.wait_for("login:", Duration::from_secs(30)).await?;

// Or wait for shell prompt
console.wait_for("# ", Duration::from_secs(30)).await?;

Writing to Console ​

Send text to the console:

rust,no_run
// Write a line (includes newline)
console.write_line("root").await?;

// Write raw bytes
console.write(b"password\n").await?;

Reading Output ​

Read available output without blocking:

rust,no_run
let output = console.read_available().await?;
println!("Console output: {}", output);

Control Signals ​

Send control characters:

rust,no_run
// Send Ctrl+C (interrupt)
console.send_interrupt().await?;

// Send Ctrl+D (EOF)
console.send_eof().await?;

Split Reader/Writer ​

For advanced async I/O, split the console:

rust,no_run
let (mut reader, mut writer) = console.into_split().await?;

// Now you can read and write concurrently
tokio::spawn(async move {
    let mut buf = [0u8; 1024];
    loop {
        let n = reader.read(&mut buf).await.unwrap();
        if n == 0 { break; }
        print!("{}", String::from_utf8_lossy(&buf[..n]));
    }
});

writer.write_all(b"ls /\n").await?;

Capturing Boot Failures ​

When a VM fails to boot, use the console to capture output:

rust,no_run
let vm = capsa::vm(boot)
    .console_enabled()
    .build()
    .await?;

let console = vm.console().await?;

// Try to wait for prompt
match console.wait_for("# ", Duration::from_secs(30)).await {
    Ok(_) => println!("Boot successful"),
    Err(e) => {
        // Capture whatever output we got
        let output = console.read_available().await?;
        eprintln!("Boot failed: {}", e);
        eprintln!("Console output:\n{}", output);
    }
}

Complete Example ​

rust,no_run
use capsa::boot::LinuxDirectBoot;
use std::time::Duration;

async fn boot_and_run() -> capsa::Result<()> {
    let boot = LinuxDirectBoot::new("./kernel", "./initrd");

    let vm = capsa::vm(boot)
        .cpus(2)
        .memory_mb(1024)
        .cmdline_arg("console", "ttyS0")
        .console_enabled()
        .build()
        .await?;

    let console = vm.console().await?;

    // Wait for login prompt
    console.wait_for("login:", Duration::from_secs(30)).await?;

    // Log in
    console.write_line("root").await?;
    console.wait_for("# ", Duration::from_secs(5)).await?;

    // Run a command manually
    console.write_line("uname -a").await?;

    // Read output
    tokio::time::sleep(Duration::from_millis(100)).await;
    let output = console.read_available().await?;
    println!("Output: {}", output);

    vm.shutdown().await?;
    Ok(())
}

Next Steps ​

Released under the MIT License.