Skip to content

variants/linux: meshcorectl, a dependency-free client for the console - #11

Open
mmmorks wants to merge 1 commit into
pr/05-pty-consolefrom
pr/05b-meshcorectl
Open

variants/linux: meshcorectl, a dependency-free client for the console#11
mmmorks wants to merge 1 commit into
pr/05-pty-consolefrom
pr/05b-meshcorectl

Conversation

@mmmorks

@mmmorks mmmorks commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Summary

A client for the PTY console, for the cases meshcore-cli -r does not cover. Its repeater mode is fine interactively, but in one-shot form it exits 0 whatever the daemon answered, waits a fixed 0.3 s and reads once, and cannot take commands from stdin, so a deploy script cannot tell set freq 868 from ERR: bad value or from a daemon that never read the line. It is also a pipx install that pulls in the BLE stack, on a host that may be a Pi Zero with nothing but python3.

meshcorectl is one file on the standard library:

  • REPL with readline editing, history in ~/.meshcorectl_history, and Tab completion of the real command set (tables checked against the dispatch literals in CommonCLI.cpp and MyMesh.cpp; get and set have separate key pools).
  • One-shot (meshcorectl set name foo) and piped (printf 'ver\nneighbors\n' | meshcorectl) forms.
  • Exit 0 only if every command was actually run by the daemon. The echo is the proof: the daemon echoes a command byte by byte as it consumes it and only then replies, so reboot counts as run, and a later command in the same script that finds the console hung up fails with a message saying why.
  • Finds the console the way the daemon publishes it (/run/meshcored/console, then $XDG_RUNTIME_DIR/meshcore/console, then /tmp/meshcore-<uid>/console); -s PATH or MESHCORED_CONSOLE overrides.

Its docstring records the firmware behaviours it cannot fix (clock sync needs a sender timestamp the console passes as 0; region load is multi-line).

What changed

  • variants/linux/meshcorectl: the client.
  • README: installed alongside meshcored in §1; §5 shows it next to meshcore-cli; ## The control CLI gains the three ways to drive it and the exit-code contract.

How it was tested

Exercised against a stand-in console (a Python PTY driven like the firmware loop) on macOS and in the arm64 container: one-shot, piped, --help, a missing path, a non-console path, a command with no reply text, and a piped script that continues past reboot (exit 1 with the reason). Not yet run against a real node.

Dependencies

Stacked on the PTY console hardening PR (pr/05-pty-console). Review the last commit only. Optional: the console works with meshcore-cli without this.

Shared code touched

None.

meshcore-cli's repeater mode covers interactive use of the console well.
What it does not cover is scripting: its one-shot form exits 0 whatever
the daemon answered, waits a fixed 0.3 s and reads once, and has no way
to take commands from stdin -- so a deploy script cannot tell "set freq"
from "ERR: bad value" or from a daemon that never read the line. It is
also a pipx install that pulls in the BLE stack, on a host that may be a
Pi Zero with nothing but python3.

meshcorectl is one file on the Python standard library: a readline REPL
with history and Tab completion of the real command set (the tables are
checked against the dispatch literals in CommonCLI.cpp and MyMesh.cpp), a
one-shot form, and a piped form, exiting 0 only if every command was
actually run by the daemon. The echo is the proof: the daemon echoes a
command byte by byte as it consumes it and only then replies, so text
that starts with the echo is a reply and anything else is the reason
nothing ran. That makes `reboot` count as run, and a later command in the
same script, finding the console hung up, fail with a message that says
why. It resolves the console the same way the daemon publishes it
(/run/meshcored/console, then $XDG_RUNTIME_DIR/meshcore/console, then
/tmp/meshcore-<uid>/console); -s PATH or MESHCORED_CONSOLE overrides.

Exercised against a stand-in console (a Python PTY driven like the
firmware loop) on macOS and Linux: one-shot, piped, --help, a missing
path, a non-console path, a command with no reply text, and a piped
script that continues past reboot.

Reported no-reply is no longer asserted as "never read": a stall means the
echo has not come back yet, and the daemon may still run the command. The
four commands that take the console down (reboot, clkreboot, poweroff,
shutdown) are the exception in the other direction -- closing the master
vhangups the slave and flushes its input queue, so their echo can be lost
even though the command ran, and a hangup right after writing one is
success rather than failure.

Each command flushes the console's input queue before writing and locates
its echo anywhere in the reply rather than requiring it at the front, so a
reply that arrives after the previous command's idle gap (erase, start ota)
cannot be misread as this command's or reported against the wrong line.

An interrupt exits 130 instead of printing a traceback, and the console
descriptor is closed on every path.

Two table corrections: radio.fem.rxgain is not a `set` key (fem_rxgain is a
serializer key only, and CommonRadioPrefs dispatches just radio.rxgain), and
extra.sf is readable but not writable outside USE_LR2021, so it belongs in
the get-only pool.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant