1 stable release
1.0.0 | May 12, 2022 |
---|
#565 in Unix APIs
37KB
455 lines
calloop-subprocess
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();
This document is kept up-to-date with cargo-rdme.
Dependencies
~6–19MB
~235K SLoC