Expand description

Subprocess handling event source for the Calloop event loop.

Calloop is built around the concept of event sources eg. timers, channels, file descriptors themselves, which generate events and call a callback for them. This crate provides two kinds of event sources that let you manage subprocess:

  • a chain, which runs a series of commands and generates a success/failure event
  • a listener, which runs a single command and generates an event based on lines of output

Error handling

Errors in this crate are classified into two high-level kinds, as is the custom for Calloop.

LaunchError might be generated by an event source’s process_events() method when a critical error is encountered. This generally means that the event source itself can’t continue sensibly and should be removed from the event loop.

The ErrorEvent type is concerned with errors of the subprocess it’s managing, including the command not being found or requiring permissions that the process doesn’t have. These will be contained in the type of event that is given to the callback.

The main way that these are different is: if there’s a problem using whatever underlying mechanism we use to spawn a subprocess or get results back from it, then a LaunchError is produced then and there. But if the underlying mechanics for setting up a subprocess succeed, and it’s just that the command contains a typo and can’t be found, then it’s perfectly possible to generate an event for that. It will just happen to contain the IO error you’d expect when trying to run the command directly.

Releasing file descriptors

To avoid “holding on” to file descriptors (and just the memory associated with the event source itself), you must honour the calloop::PostAction returned from process_events() and remove the event source if requested.

This is automatically done for top level event sources ie. those added by insert_source(). If you use it as part of a composed event source, you must either manage reregistration yourself, or wrap the source with TransientSource.

Example

This runs ls -a and prints the events received.

use calloop::{EventLoop, LoopSignal};
use calloop_subproc::{Command, SubprocListen};

let ls_cmd = Command::new("ls").with_args(["-a"]);
let listener = SubprocListen::new(ls_cmd).unwrap();

let mut event_loop: EventLoop<LoopSignal> = EventLoop::try_new().unwrap();

event_loop
    .handle()
    .insert_source(listener, |event, _, stopper| {
        // What kind of event did we get this time?
        let msg = match event {
            // The subprocess was just started.
            calloop_subproc::ListenEvent::Start => "Subprocess started".to_owned(),
            // We got a line of output from the subprocess.
            calloop_subproc::ListenEvent::Line(line) => format!("Output: {}", line),
            // The subprocess ended.
            calloop_subproc::ListenEvent::End(res) => {
                // Since the subprocess ended, we want to stop the loop.
                stopper.stop();

                // Show why the subprocess ended.
                match res {
                    Ok(()) => "Subprocess completed".to_owned(),
                    Err(error) => format!("Subprocess error: {:?}", error),
                }
            }
        };

        // Print our formatted event.
        println!("{}", msg);

        // This callback must return true if the subprocess should be
        // killed. We want it to run to completion, so we return false.
        false
    })
    .unwrap();

event_loop
    .run(None, &mut event_loop.get_signal(), |_| {})
    .unwrap();

Structs

There’s a lot of common manipulation of what we think of as a command. This type factors out some of that, specifically:

An event source that processes a sequence of commands.

An event source that runs a subprocess and generates events for lines of output.

Enums

This is the error type that our event source’s callback might receive. If something went wrong, it could be for one of two reasons:

This is the error type that might be returned from the methods on the event source itself. In general they are more “critical” than ErrorEvent and mean that the event source is unable to continue.

The events generated by SubprocListen.