Proc-heim
Proc-heim is a library for running and managing short-lived and long-lived processes using asynchronous API. A new process can be created by running either command or script.
Features
Proc-heim internally uses tokio::process for executing processes and provides all its functionality plus additional features:
- spawning new processes via scripts (in different scripting languages) and any
Rusttypes, which implementsRunnabletrait, - flexible managing of all spawned processes using single facade, which can be easily shared by multiple threads/tasks,
- bi-directional, message-based communication between child and parent processes via standard IO streams or named pipes,
- collecting and querying logs produced by child processes (both running and completed).
For more detailed list of features see ProcessManagerHandle documentation.
API overview
Proc-heim library is divided into two modules: model and manager.
The first one defines types and traits used to describe commands, scripts and their settings, such as messaging and logging type, environment variables and working directory. The module contains a Runnable trait, which defines how to run a user-defined process. The library provides two implementation of this trait: Cmd and Script.
The manager module provides an API for spawning and managing child processes. The whole implementation relies on Actor model architecture. To start using the library, a client code needs to spawn a ProcessManager task, responsible for:
- creating new actors implementing some functionality (eg. reading messages from child process),
- forwarding messages sent between client code and other actors.
After spawning the ProcessManager task, a ProcessManagerHandle is being returned, which exposes an API for spawning and managing user-defined processes.
Examples
Spawning a new ProcessManager task
use ProcessManager;
use PathBuf;
let working_directory = from;
let handle = spawn?;
// now use the handle to spawn new processes and interact with them
Spawning a new process from command
use ProcessManager;
use PathBuf;
let working_directory = from;
let handle = spawn?;
let cmd = with_args;
let process_id = handle.spawn.await?;
// now use the process_id to interact with a process ...
Reading logs from a process
use ;
use ;
let working_directory = from;
let handle = spawn?;
let script = with_args_and_options;
let process_id = handle.spawn.await?;
// We are waiting for the process to exit in order to get all logs
handle.wait.await??;
let logs = handle
.get_logs_stdout
.await?;
assert_eq!;
assert_eq!;
let error_logs = handle
.get_logs_stderr
.await?;
assert_eq!;
assert_eq!;
Messaging with a process via named pipes
use TryStreamExt;
use ;
use PathBuf;
let working_directory = from;
let handle = spawn?;
let script = with_options;
// We can use "spawn_with_handle" instead of "spawn" to get "ProcessHandle",
// which mimics the "ProcessManagerHandle" API,
// but without having to pass the process ID to each method call.
let process_handle = handle.spawn_with_handle.await?;
process_handle.send_message.await?;
// We can send a next message without causing a deadlock here.
// This is possible because the response to the first message
// will be read by a dedicated Tokio task,
// spawned automatically by the Process Manager.
process_handle.send_message.await?;
let mut stream = process_handle.subscribe_message_string_stream.await?;
let msg = stream.try_next.await?.unwrap;
assert_eq!;
let msg = stream.try_next.await?.unwrap;
assert_eq!;
let result = process_handle.kill.await;
assert!;
For more examples, see integration tests.