Skip to content

feat(zshell): obscured input, root command selection, runtime prompt (login primitives) - #61

Draft
swoisz wants to merge 8 commits into
mainfrom
feature/zshell-login-primitives
Draft

swoisz wants to merge 8 commits into
mainfrom
feature/zshell-login-primitives

Conversation

@swoisz

@swoisz swoisz commented Sep 18, 2026

Copy link
Copy Markdown
Collaborator

Closes #59.

Adds the four primitives Zephyr's login sample composes, with the same signatures and return codes as upstream include/zephyr/shell/shell.h:

API Kconfig
shell_obscure_set() -- echo *, returns previous state CONFIG_ZSHELL_START_OBSCURED
shell_set_root_cmd() -- typed input dispatches beneath one root; help and completion see only its subtree; NULL restores the tree CONFIG_ZSHELL_CMD_ROOT
shell_prompt_change() -- runtime prompt CONFIG_ZSHELL_PROMPT_CHANGE (default y), CONFIG_ZSHELL_PROMPT_BUFF_SIZE (2..40, default 20)
shell_history_purge() -- drop the entry holding a password --

The shell supplies mechanics only; the application owns the credential check. examples/shell_demo gains login/logout modeled on samples/subsys/shell/shell_module/src/login_cmd.c; idf.py -DLOGIN=y build starts it locked.

Divergences (all documented on the declaration)

  • shell_prompt_change() blocks on the output mutex instead of trylocking, so -EBUSY is never returned.
  • shell_obscure_set() writes a plain bool; upstream sets an atomic flag. Call from the shell thread.
  • shell_history_purge(sh) takes the shell, since Boreas embeds the history rather than pointing at it. Replaces upstream's z_shell_history_purge(sh->history).
  • CONFIG_ZSHELL_CMD_ROOT is applied once, by the first shell_init(), since the selection is shared by every instance. A later shell_init() must not re-lock a shell that has already logged in.

Notes

  • Every path that puts cmd_buff on the terminal now goes through shell_write_cmd(), so obscuring covers insert, delete, cursor moves, history recall, and completed text -- not just the echo of the typed key.
  • argv arrays grow by one slot: a selected root occupies argv[0] ahead of the typed tokens.
  • struct shell_ctx grows by the prompt buffer when PROMPT_CHANGE is on.
  • .gitignore now covers build*/ so per-variant build dirs (build_login/, build_linux/) stay untracked.

Test

  • New capture-transport harness drives shell_process_char() with no shell thread, checking both terminal bytes and dispatch.
  • Covered: obscured insert/delete/mid-line insert/completion, shell_obscure_set() previous-state contract, valid/invalid root selection, dispatch beneath the root, sibling roots and help unreachable while a root is selected (with a positive control once cleared), completion scoped to the root, prompt change bounds, history purge.
  • linux host: 239/239 pass (idf.py --preview set-target linux), including the 17 new shell tests.
  • esp32s3: builds clean with -Werror; earlier on-target run of this suite passed before the final review-fix commit.
  • clang-format: clean.
  • Not covered: CONFIG_ZSHELL_START_OBSCURED=y startup and the PROMPT_CHANGE=n / HISTORY=n fallbacks, which need a second test config.

🤖 Generated with Claude Code

swoisz and others added 8 commits September 4, 2026 21:49
Route every cmd_buff-to-terminal write through shell_write_cmd() and
every prompt print through shell_print_prompt(), replacing six copies of
the default_prompt lookup. shell_process_char() becomes internal API so
the host tests can drive line editing without the shell thread.

No behavior change. Prep for obscured input and runtime prompt changes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Hand-built struct shell with a write-capturing transport and no thread,
plus a two-command root tree. Covers insert echo, backspace, subcommand
dispatch and unknown-command handling.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Zephyr-compatible primitives for application login flows (#59):

- shell_obscure_set(): echo '*' for every cmd_buff write; returns the
  previous state. CONFIG_ZSHELL_START_OBSCURED sets the initial state.
- shell_set_root_cmd(): one selection shared by all instances, as
  upstream. Dispatch and tab completion place the root at argv[0] so
  typed input can only walk beneath it; help is itself a root command
  and therefore unreachable while a root is selected. NULL restores the
  tree, an unknown name returns -EINVAL. CONFIG_ZSHELL_CMD_ROOT (string)
  applies at init and warns if it names no command.
- shell_prompt_change(): bounded by CONFIG_ZSHELL_PROMPT_BUFF_SIZE under
  CONFIG_ZSHELL_PROMPT_CHANGE (default y, 20); -EINVAL on NULL/oversize,
  -EPERM when disabled, as upstream.
- shell_history_purge(): takes the shell because history is embedded
  here rather than pointed to; no-op without CONFIG_ZSHELL_HISTORY.

shell_root_cmd_find() replaces the three private root lookups.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…urge

Ten cases from #59: obscure toggle return value, obscured insert/delete
and mid-line rewrite, default-off state, valid/invalid root selection,
dispatch beneath the root, rejection of sibling roots and help, clearing
the root, root-aware completion, prompt bounds, and purge.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`login <password>` purges history, clears obscuring, restores the full
tree and prompt; `logout` reverses it. `idf.py -DLOGIN=y build` appends
sdkconfig.login (START_OBSCURED=y, CMD_ROOT="login") so the demo boots
locked. The command-line SDKCONFIG_DEFAULTS override cannot be used here
because the project CMake sets the list itself.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- Tab completion echoed completed text in clear while obscured; route
  the two insert writes through shell_write_cmd() and test it.
- shell_prompt_change() copies under wr_mtx so it cannot interleave with
  an in-progress write; @note the -EBUSY divergence from upstream.
- Apply CONFIG_ZSHELL_CMD_ROOT once: the selection is shared, so a second
  shell_init() after a login must not relock the first instance.
- shell_make_argv_root() replaces the duplicated root-at-argv[0] shape in
  dispatch and completion; cmd_help uses shell_root_cmd_find().
- Doxygen: @PARAM on the new APIs, accurate help/completion wording,
  history purge threading note; root lookup helpers move to internal API.
- Drop the dead CONFIG_ZSHELL_PROMPT_BUFF_SIZE fallback define.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ading

- Register the builtins in the test harness so `help` exists; assert it
  lands on the root handler while a root is selected and lists the tree
  once cleared (previously passed vacuously because help was unregistered).
- @note on shell_obscure_set(): plain bool, not upstream's atomic flag.
- Extend the shell_prompt_change() note: the prompt is read outside the
  lock when printed, so call from the shell thread.
- Drop the redundant `default n` on ZSHELL_START_OBSCURED.
- .gitignore build*/ so variant build dirs stay untracked.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.

zshell: add Zephyr-compatible obscured input and root command selection

1 participant