epics-tools-rs
Operational tooling for EPICS deployments — process supervisors, log/audit dumpers, config validators.
First tenant: procserv — a pure-Rust port of
epics-modules/procServ.
PTY-based process supervisor with a multi-client telnet console. Drop-in
flag compatibility with C procServ, so existing wrapper scripts and
systemd units do not need to be rewritten.
Unix-only (depends on forkpty(3) and POSIX signals). On non-Unix
targets the crate compiles to an empty library so workspace builds keep
succeeding; the procserv-rs binary is unavailable there.
Build
From the workspace root:
# binary: target/release/procserv-rs
The default features (procserv, procserv-bin) already pull in the
binary; no extra --features flag is needed. To use the library only
without the CLI dependencies (clap, tracing-subscriber):
[]
= { = "*", = false, = ["procserv"] }
Run
procserv-rs mirrors the C procServ flag surface. Common shapes:
# Foreground, port 4051, supervising a softIoc
# Daemonized with log/pid/info files
# Bind to all interfaces (default is localhost only)
# UNIX domain socket instead of TCP
Everything after -- is the child program and its argv.
Flags
| Flag | Description | Default |
|---|---|---|
-p, --port <PORT> |
TCP listen port | — |
--allow |
Bind 0.0.0.0 instead of 127.0.0.1 | off |
--unixpath <PATH> |
UNIX-domain socket path | — |
-f, --foreground |
Do not daemonize | off |
-L, --logfile <PATH> |
Log file | — |
--pidfile <PATH> |
PID file | — |
--info-file <PATH> |
PROCSERV_INFO info file |
— |
--holdoff <SEC> |
Hold-off between restarts | 15 |
-w, --wait |
Do not start child until first console request | off |
--chdir <DIR> |
chdir before exec'ing child |
— |
--name <NAME> |
Display name in banners | child basename |
--max-restarts <N> |
Max restarts inside --restart-window |
10 |
--restart-window <SEC> |
Sliding window for --max-restarts |
600 |
--kill-char <BYTE> |
Force-kill key (Ctrl-X = 24, 0 disables) | 24 |
--toggle-restart-char <BYTE> |
Toggle restart-mode key (Ctrl-T = 20) | 20 |
--logout-char <BYTE> |
Per-client logout key (Ctrl-] = 29) | 29 |
Connecting to the console
# or, for UNIX-socket listeners:
Multiple clients may connect simultaneously and share the child's stdout in a party-line fashion. Built-in keys:
Ctrl-X— force-kill the child (sendsSIGKILLby default).Ctrl-T— toggle restart mode (OnExit↔ off).Ctrl-R— manually restart the child when it is dead.Ctrl-]— log this client out (child stays running).
Logging
procserv-rs uses the tracing ecosystem with an EnvFilter:
RUST_LOG=debug
RUST_LOG=epics_tools_rs::procserv=trace
Use as a library
use ;
# async
The end-to-end test at tests/procserv_e2e.rs shows a complete config
literal with TCP listener, key bindings, restart policy and a
/bin/cat child — useful as a copy-paste starting point.
Architecture
The C → Rust mapping is documented at the crate root in
src/lib.rs. High-level points:
- Hub-and-spoke fan-out via a single supervisor task forwarding
per-connection mpsc messages, matching C
SendToAll's exclude-the-sender semantics withouttokio::sync::broadcastre-delivery. - Per-connection
readonlyflag instead of a master role. - Stateless command-key dispatch (no menu FSM); keys are still echoed to other connections.
- Narrow telnet usage — only
IAC WILL ECHOandIAC DO LINEMODEare negotiated, so the in-crate parser stays under ~80 LOC instead of vendoringlibtelnet.c.
License
See LICENSE.