diff options
| author | Ruben Herold <ruben@puettmann.net> | 2026-08-13 09:04:28 +0200 |
|---|---|---|
| committer | Ruben Herold <ruben@puettmann.net> | 2026-08-13 09:04:28 +0200 |
| commit | 6396d9aa43063569dfc9be2daac27f89a643252f (patch) | |
| tree | fe0fdcec40e9314a002bcf73869f86cb064c9a5c /python | |
| parent | 82c8f97d7ea5d49694b6256a388ba17c8a0cd58e (diff) | |
| download | vyos-1x-6396d9aa43063569dfc9be2daac27f89a643252f.tar.gz vyos-1x-6396d9aa43063569dfc9be2daac27f89a643252f.zip | |
utils: T9185: ask_yes_no() fails fast on non-tty instead of silent default
jestabro (and dmbaturin previously, on #5362) pointed out that silently
returning `default` on a non-TTY stdin still violates ask_yes_no()'s
interactive-only contract, even though it stops the CPU-spin. Per T9185,
sageframe's original incident confirms why: a caller that forgets an
explicit non-interactive guard gets no error and no visible hang, just
a process quietly pegging a core for days.
Raise EOFError immediately instead, so a missing guard fails loudly and
fast rather than either spinning or silently succeeding. Existing callers
that are meant to run non-interactively already check no_prompt/-y before
calling this (config_mgmt.py, backend.py) and are unaffected.
Diffstat (limited to 'python')
| -rw-r--r-- | python/vyos/utils/io.py | 16 |
1 files changed, 13 insertions, 3 deletions
diff --git a/python/vyos/utils/io.py b/python/vyos/utils/io.py index 360fc1ffb..58ceb3476 100644 --- a/python/vyos/utils/io.py +++ b/python/vyos/utils/io.py @@ -57,13 +57,23 @@ def ask_input(question, default='', numeric_only=False, valid_responses=[], def ask_yes_no(question, default=False) -> bool: """Ask a yes/no question via input() and return their answer. - If stdin is not a TTY (e.g. non-interactive scripts), returns - default without prompting. + Raises EOFError immediately if stdin is not a TTY (e.g. non-interactive + scripts), instead of ever calling input(). A silent default here has + caused real incidents (T9185): a caller that forgets to guard an + interactive-only prompt gets no error and no visible hang, just a + process quietly pegging a core. Callers that are meant to run + non-interactively must check for that explicitly (see the no_prompt + parameter used across config_mgmt.py/backend.py) rather than rely on + this function to fall back to a default. """ from sys import stdin, stdout if not stdin.isatty(): - return default + raise EOFError( + f'Cannot prompt "{question}" ([Y/n] default={default}): ' + 'stdin is not a TTY. Pass an explicit non-interactive option ' + '(e.g. no_prompt/-y) instead of relying on ask_yes_no().' + ) default_msg = "[Y/n]" if default else "[y/N]" while True: try: |
