-
Notifications
You must be signed in to change notification settings - Fork 1
Shell
popen-based shell-out + small-file reader / writer. The
systems-y bits that pure-Ruby would reach for IO.popen,
File.read, File.write to do. None of those lower cleanly
through spinel, so this module wraps the C calls behind a tiny API.
-
Read
/proc,/sys,/etc. Cheap and portable from any handler. -
Shell out to a CLI for one-off captures (e.g.
nvidia-smi,uptime,df -h). - Atomic file writes (rename-into-place) for state checkpoints.
What it isn't:
- Not a streaming pipe. Captures are bounded (~64 KiB) and the whole output materialises in memory before returning.
- Not a process-group manager. There's no
kill -9 <pgid>; if a command hangs, the worker hangs.
hosts = Tep::Shell.read("/etc/hosts")
load1 = Tep::Shell.read("/proc/loadavg").split(" ")[0]Tep::Shell.read(path) returns the whole file content as a String,
or "" on failure. There's an explicit duplication step inside —
the underlying FFI returns a pointer to a static buffer that the
next call would overwrite, so the result is safely usable after
the call returns. (If you're calling into Sock.sphttp_file_read
directly, dup with + "" yourself.)
Tep::Shell.read_limited(path, max_bytes) is the same but caps the
read at max_bytes — handy when you don't trust the source size.
Tep::Shell.write("/tmp/state.json", payload)Returns true on success. Writes are atomic: the content goes to
<path>.tmp.<pid> and then renames into place, so a concurrent
reader either sees the old content or the new content but never a
half-written file.
out = Tep::Shell.run("nvidia-smi --query-gpu=name --format=csv,noheader")
gpu = out.stripTep::Shell.run(cmd) runs the command via popen(3) (so it goes
through /bin/sh -c "cmd" — quoting matters), reads stdout to EOF,
and returns it. Standard error goes to the worker's stderr.
Tep::Shell.run_limited(cmd, max_bytes) caps the captured bytes —
use it when the command might produce a lot of output and you only
want the head.
get '/healthz' do
content_type 'application/json'
h = Tep.str_hash
h["uptime_seconds"] = Tep::Shell.read("/proc/uptime").split(" ")[0]
h["load_1m"] = Tep::Shell.read("/proc/loadavg").split(" ")[0]
h["hostname"] = Tep::Shell.read("/etc/hostname").strip
Tep::Json.from_str_hash(h)
endget '/gpu' do
cached = Tep::Shell.read("/tmp/gpu.cache")
if cached.length > 0
return cached
end
fresh = Tep::Shell.run("nvidia-smi --query-gpu=utilization.gpu,temperature.gpu --format=csv,noheader")
Tep::Shell.write("/tmp/gpu.cache", fresh)
fresh
endFor a real cache, layer a TTL on top via the file's mtime — or
move it into a Tep::Job background refresher.
-
runblocks until the command exits. A hung command (waiting for stdin, dead-locked, runaway) hangs the worker. Usetimeout 5 some-cmdat the shell level to bound, or move long shell-outs into aTep::Job. -
Quoting is
sh-level, not Ruby-level.Tep::Shell.run("echo $USER")expands$USERvia the shell. To pass a literal$, single-quote it. To avoid shell quoting entirely, write a small C helper and bind it via FFI; tep itself doesn't ship anexecv- shaped runner yet. -
Static-buffer aliasing. The low-level
Sock.sphttp_file_readandSock.sphttp_shell_capturereturn pointers into a shared static buffer. TheTep::Shell.read/runwrappers do the necessary+ ""dup before returning, but if you go behind the wrapper, two consecutive captures will clobber each other unless you dup. -
rundoes not propagate the exit code. If you care, parse$?from the output by including it in the command:Tep::Shell.run("nvidia-smi; echo EXIT:$?")and split onEXIT:.