Skip to content

vms-dec: kill boot-console newline spam (operator's echoed RETURNs) + type-during-boot regression gate - #850

Merged
baron-3dl merged 2 commits into
mainfrom
work/vms-dec
Aug 28, 2026
Merged

baron-3dl merged 2 commits into
mainfrom
work/vms-dec

Conversation

@baron-3dl

Copy link
Copy Markdown
Contributor

The bug (operator-reported, x86_64 + VAX)

The boot-to-login console showed obnoxious blank-line newline spam scattered through the boot output. Root cause is not the software emitting newlines — the boot output is clean. It is the operator's own RETURN keystrokes echoing.

tools/vms_login.c console_login() has an OPA0: "wake-on-RETURN" (vms-2213): it prints nothing and blocks until the operator presses RETURN before showing Username:. During the slow boot no prompt is visible, so an operator naturally mashes RETURN several times. The console tty is cooked/ECHO, so the kernel echoes each RETURN at type-time as a blank line, mixed into the boot narration. The existing tcflush() discards the leftover keystrokes but cannot un-print the blank lines the tty already echoed.

Why the "obvious" fix (ECHO-off in LOGINOUT) is insufficient — proven interactively

I first scoped ECHO-off to console_login()'s wake window. Interactive proof showed it caught almost none of the spam: the console log proves JOB_CONTROL forks LOGINOUT only at STARTUP.COM's tail (the final %RUN-S-PROC_ID line), so by the time console_login() runs the operator has already mashed RETURN throughout the long boot and the tty has already echoed every one. Longest echoed blank-line run: 16 before, still 8 with LOGINOUT-only ECHO-off.

PID 1 owns the console for the entire boot, so the echo must be silenced there. boot_console_disable_echo() (src/ovmx_init/ovmx_init.c) clears ECHO on /dev/console at the start of the flagless boot (and, on the conversational path, right after SYSBOOT> hands over — SYSBOOT> itself must keep echoing). termios is per-tty device state shared across the exec/fork chain to the same console, so it persists until LOGINOUT re-enables ECHO right before Username: (console_login() now forces ECHO on at the prompt). ECHO gates input echo only; boot output is untouched.

Also removes a redundant $ SHOW TIME from STARTUP.COM (it emitted a stray bare date line mid-boot; the banner already prints the date; the oracle boot capture shows none).

Interactive proof (x86_64, real vms.ko over qemu-system-x86_64), 15 RETURNs mashed during boot

BEFORE (origin/main) — longest blank-line run = 16:

%OVMX-I-MOUNTED, system disk DKA0: mounted
                       <-- 8 echoed blank lines here
%OVMX-I-SCSNODE, node name OVMX set from SYS$SYSTEM:OVMXVMSSYS.PAR
%STDRV-I-STARTUP, OpenVMX startup begun at 28-AUG-2026 16:26:37.01
                       <-- more echoed blanks
%OVMX-I-EXEC, system identity SYSTEM [1,4] established by the executive
                       <-- more echoed blanks
  28-AUG-2026 16:26:38.73        <-- stray SHOW TIME date line
The OVMX system is now executing the site-specific startup commands.
...
Username: SYSTEM

AFTER (this PR) — longest blank-line run = 1 (the two legit boot blanks), despite the same 15 RETURNs:

%OVMX-I-EXEC, VMS executive attached on /dev/vms

    OpenVMX V0.5-7 - OpenVMS-compatible
    28-AUG-2026 16:34:18.48

%OVMX-I-SYSDISK, mounting system disk DKA0:
%OVMX-I-MOUNTED, system disk DKA0: mounted
%OVMX-I-SCSNODE, node name OVMX set from SYS$SYSTEM:OVMXVMSSYS.PAR
%STDRV-I-STARTUP, OpenVMX startup begun at 28-AUG-2026 16:34:20.69
%OVMX-I-EXEC, system identity SYSTEM [1,4] established by the executive
The OVMX system is now executing the site-specific startup commands.
%INSTALL-I-ADDED, DECC$SHR.EXE installed
... (INSTALL lines) ...
%RUN-S-PROC_ID, identification of created process is 10000003
Username: SYSTEM

No echoed blank lines; no stray date line; Username: clean; SYSTEM logs in and the typed username echoes (ECHO restored).

Regression coverage CI structurally lacked

Every existing boot harness waits for Username: and only then feeds input, so none provokes the echo. New tests/qemu/test_console_boot_no_newline_spam.sh boots the real runtime, types RETURNs DURING the boot (before the prompt), and asserts no run of echoed blank lines precedes Username: while login still works. It measures the longest consecutive blank-line run in the pre-Username: segment:

  • BEFORE (origin/main): run=16 -> RED
  • LOGINOUT-only ECHO-off: run=8 -> RED (correctly still red; partial fix)
  • This PR: run=1 -> GREEN (6/6)

Wired as ctest console_boot_no_newline_spam_e2e and a ci.yml console-newline-spam-e2e job (same OVMX_QEMU_FULL_E2E=1 opt-in convention as dcl-acceptance-e2e; SKIP treated as hard failure).

Both arches

The fix is entirely in shared userland: VAX LOGINOUT cross-compiles from the same tools/vms_login.c (tools/cross-vax/build-loginout-vax.sh) and VAX STARTUP.EXE/PID 1 from the same src/ovmx_init/ovmx_init.c (tools/cross-vax/build-ovmx-init-vax.sh). open("/dev/console") + termios ECHO are POSIX, available under NetBSD-vax. No new TU, so no VAX build-enum updates. x86_64 verified interactively; VAX applies identically by construction.

Regression check

test_job_control_console.sh on the fixed image: login + every console-behavior assertion passes (36/1). The one failure (OPERATOR.LOG missing '^%OVMX-I-VMSFS, ') is identical on origin/main (verified) — a pre-existing, unrelated kmsg-bridge/VMSFS assertion (likely stale post vms-165 vmsfs retirement), not touched by this change.

Follow-up (noted, not shipped here)

Removing the wake entirely (faithful oracle behavior: straight to Username:) risks a Username:/STARTUP.COM-tail interleave and re-exposing the empty-username machine-gun the tcflush guards. Left as a follow-up rung; this PR keeps the wake and kills the spam.

🤖 Generated with Claude Code

baron-3dl and others added 2 commits August 28, 2026 16:23
…ssion gate

The boot-to-login console showed blank-line "newline spam" scattered through
the boot output on both x86_64 and VAX. Root cause is NOT the software
emitting newlines -- it is the OPERATOR'S OWN RETURN KEYSTROKES ECHOING.

tools/vms_login.c console_login() has an OPA0: "wake-on-RETURN" (vms-2213):
it prints nothing and blocks until the operator presses RETURN before showing
"Username:". During the slow boot no prompt is visible, so the operator
naturally hammers RETURN. The console tty is in cooked/ECHO mode, so the
kernel echoes each RETURN at type-time as a blank line, mixed into the boot
output. The existing tcflush() discards the leftover keystrokes but cannot
un-print the blank lines the tty already echoed -- that residual is the spam.

Fix: clear ECHO on STDIN for the duration of the wake window and restore it
just before the real "Username:" prompt (after the tcflush). ECHO gates INPUT
echo only, so boot OUTPUT is untouched and the typed username still echoes
normally -- but the wake keystrokes mashed during boot no longer print.

The wake's original banner-ordering rationale is now stale (vms-1fb moved the
banner to print from PID 1 before run_startup()); removing the wake entirely
is the faithful end state but risks a Username:/STARTUP.COM-tail interleave and
re-exposing the empty-username machine-gun the tcflush guards, so it is left as
a follow-up. This rung keeps the wake but silences the echo -- the guaranteed
fix for the reported spam.

Also removes the redundant `$ SHOW TIME` from STARTUP.COM (it emitted a stray
bare date line mid-boot; the banner already prints the date; the oracle boot
capture shows none).

Regression coverage CI structurally lacked: every existing boot harness waits
for "Username:" and only THEN feeds input, so none provokes the echo. New
tests/qemu/test_console_boot_no_newline_spam.sh boots the real runtime, TYPES
RETURNs DURING the boot (before the prompt), and asserts no run of echoed
blank lines precedes "Username:" while login still works. Wired as ctest
console_boot_no_newline_spam_e2e and a ci.yml console-newline-spam-e2e job.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…UT re-enables at prompt

Interactive proof exposed that ECHO-off scoped to LOGINOUT's wake window
caught almost none of the spam: the boot console log shows JOB_CONTROL forks
LOGINOUT only at STARTUP.COM's tail (the final %RUN-S-PROC_ID line), so by the
time console_login() runs, the operator has already mashed RETURN throughout
the long boot and the cooked-mode console tty has already echoed every one of
those keystrokes as a blank line. Longest echoed blank-line run measured 16
before, and still 8 with the LOGINOUT-only ECHO-off -- not fixed.

PID 1 owns the console for the entire boot, so the echo must be silenced there.
boot_console_disable_echo() (src/ovmx_init/ovmx_init.c) clears ECHO on
/dev/console at the start of the flagless boot (and, on the conversational
path, right after SYSBOOT> hands over -- SYSBOOT> itself must keep echoing).
termios is per-tty device state shared across the exec/fork chain to the same
console, so it persists until LOGINOUT re-enables ECHO right before "Username:"
(console_login now forces ECHO on at the prompt instead of restoring a saved
state). ECHO gates input echo only; boot OUTPUT is untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@baron-3dl
baron-3dl merged commit 4787e58 into main Aug 28, 2026
5 checks passed
baron-3dl added a commit that referenced this pull request Aug 28, 2026
…ce parity, SHOW USERS de-fab (#858)

Bumps OVMX_PRODUCT_VERSION and os-release V0.5-7 -> V0.5-8. 14 commits since V0.5-7.

Distributed Lock Manager — the cross-node ladder tops out at a real GRANT:
  - #849 vms-e8f1/vms-17c  DLM rung-2 (H4): the first REAL cross-node $ENQ GRANT
                           — node B holds a lock on A's behalf for A's CSID,
                           $ENQ returns SS$_NORMAL over live SCS (no longer
                           INV-6 "grants nothing"). The distributed lock
                           manager now actually locks across nodes.
  - #845 vms-209          DLM harness H3 — a cross-node $ENQ reaches node B's
                           real executive
  - #843 vms-4bd0         DLM harness H2 — two real-executive QEMU nodes
                           complete the full VMS$VAXcluster join
  - #842 vms-534          DLM harness H1 — two real-executive nodes exchange
                           the 0x6007 HELLO over a socket netdev
  - #840 vms-4b6          DLM harness rung H0 — SCSD.EXE composes with a real
                           executive
  - #841 docs             Alpha cross-node DLM wire oracle — observed SCS
                           transport + documented GRANT semantics (Rule 8)

Alpha co-release parity — authenticated login + the shared SHOW battery:
  - #852 vms-f2c          Alpha DCL/SHOW acceptance parity — the same shared
                           11-command battery x86_64 runs, now on
                           qemu-system-alpha
  - #847                  vms.ko guards BG fork-inherit tracepoints behind
                           CONFIG_TRACEPOINTS (Alpha P0 modpost fix)

Authenticity (INV-6 de-fabrication):
  - #839 vms-6a1          SHOW USERS fails honestly when the executive is
                           absent — no more per-process fabricated user list

Boot console fidelity:
  - #850 vms-dec          kill boot-console newline spam (the operator's echoed
                           RETURNs) + a type-during-boot regression gate

Networking (layered-product promotion):
  - #851 vms-67f          TCP/IP Services promoted to a first-class layered
                           product — build plan + roadmap (1.0 blocker)

Docs / ledger:
  - #745                  Alpha C-RTL archive DECIDED — Option A (operator
                           ruling 2026-08-23)
  - #709 vms-da0          OpenVMS GCC port × OVMX faithful-surface gap analysis
  - #838                  reconcile the release block for the V0.5-7 tag
                           (INV-LEDGER)

QA'd under KVM boot-to-login on the workshop host with the full SHOW
acceptance battery VMS-faithful; boot console clean (no newline flood) and
SHOW USERS honest. Release notes are generated at tag time by
tools/gen_release_notes.py (INV-LEDGER single-ledger); the roadmap narrative
block + public site are the conductor's reconcile (INV-0/INV-LEDGER).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Aug 28, 2026
…(dark since #850)

The job name added today by #850 (vms-dec) contained an unquoted mid-value colon
('... before Username: (vms-dec)'), which is invalid YAML. GitHub Actions could
not parse ci.yml, so EVERY run of it — on main and every branch — completed with
ZERO jobs (no Build & Test, no harnesses, nothing); the only checks still firing
were the separate Ledger-drift and CodeQL workflows. Quoting the value restores
the whole workflow. Pre-existing project-wide breakage, surfaced while wiring the
H5 CI job (which could not run until ci.yml parsed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
baron-3dl added a commit that referenced this pull request Aug 28, 2026
… (block-then-grant on the requester) (#875)

* vms-6ca: DLM H5 executive core — requester-side GRANT RECEIVE + deferred-grant report

The cross-node contention semantics (#873) proved block-then-grant on a single
real /dev/vms. This adds the two executive halves the async-reply SCS wire needs:

- MASTER side: vms_lock_dlm_xnode_deq now REPORTS the queued cross-node waiter
  that a release flipped to granted (its requester CSID + original req_lkid +
  master handle + granted mode), through the fields a DEQ otherwise leaves 0, so
  the daemon can WIRE a deferred GRANT to the requester without a second probe.
  The master lock entry now carries the requester's own lock handle (req_lkid).

- REQUESTER side: VMS_DLM_OP_GRANT is implemented (was SS$_UNSUPPORTED). A GRANT
  / queued-reply the master sent back completes an executive-resident ORIGIN
  record — the requester-side proxy of the outstanding request, on its own list,
  never touched by the local lock manager. Its granted mode is set ONLY from what
  the master genuinely sent over SCS: a queued-reply (mode NL) leaves it pending,
  a deferred GRANT (mode EX) flips it NL->EX. GETLKI falls through to it, so the
  status flip is observable on the REQUESTER node. INV-6: genuine executive
  state, no fabricated grant.

BLKAST as a receive op stays SS$_UNSUPPORTED — the BLKAST wire is deferred
honestly on this rung (the block-then-grant round-trip is proven without it).

test_syssvc_dlm_xnode.c extended to prove both halves against a real /dev/vms
(x86_64 + Alpha LP64): the deferred-grant report on the block-then-grant $DEQ,
and the requester-side queued->granted origin flip. vms.ko builds clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* vms-6ca: DLM H5 SCS wire — queued-reply + deferred-GRANT delivery over the live VC

The scsd daemon now carries the async DLM replies over the real SCS wire, so the
block-then-grant completes on the REQUESTER node across the network — not just in
the master's local executive (#873).

Node B (master): the GRANT frame it sends back now carries the granted mode
honestly — a queued-reply carries NL (the requester stays genuinely pending), a
grant carries the granted mode — plus the master's lock handle, so the requester
can $DEQ the holder. On a cross-node $DEQ that flips a queued waiter to granted,
B reads the executive's deferred-grant report and WIRES an unprompted deferred
GRANT (SS$_NORMAL, the granted mode) to that requester (SCSD-I-DLMDEFER).

Node A (requester, armed by OVMX_DLM_H5): drives block-then-grant over the wire —
holds RESONE EX (#1), sends a second incompatible $ENQ (#2) that QUEUES on B,
dispatches B's queued-reply into its OWN executive so #2's origin record is
genuinely PENDING (GETLKI->NL, SCSD-I-DLMPEND), releases the holder (#1), and on
B's deferred GRANT dispatches it into its executive — the origin record FLIPS
NL->EX (GETLKI->EX, SCSD-I-DLMH5FLIP). The status flip is read from A's real
executive, driven only by what the master sent over SCS. INV-6: no fabricated
wire reply or grant; the BLKAST wire stays deferred (the holder releases on its
own). scsd builds clean; vmsscs unit tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* vms-6ca: DLM H5 two-node QEMU harness + CI job — block-then-grant over the wire

Extends the H4 two-node real-/dev/vms harness to H5. Both nodes boot vms.ko and
complete the VMS$VAXcluster join; node A is armed OVMX_DLM_ENQ=RESONE
OVMX_DLM_H5=1 and drives the block-then-grant sequence over the live SCS wire:
holds RESONE EX, a second incompatible $ENQ QUEUES on node B (B WIREs a
queued-reply -> A's requester origin record PENDING, SCSD-I-DLMPEND), A releases
the holder, node B WIREs the deferred GRANT off that real $DEQ (SCSD-I-DLMDEFER),
and A's origin record FLIPS NL->EX (SCSD-I-DLMH5FLIP granted_mode=EX).

H5 PASS asserts the status flip on the REQUESTER node A across the wire, plus B's
deferred-grant wire — read from the nodes' own SCSD logs, never fabricated
(INV-6). New Dockerfile.dlm-harness-h5 + init_dlm_h5.sh + run_dlm_harness_h5.sh;
new dlm-harness-h5 CI job (path-filtered on the DLM sources + harness files); the
existing dlm_harness filter already covers vms_lock.c + src/vmsscs/**. Shell +
YAML syntax validated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* vms-6ca: DLM H5 — record the async-reply wire + requester-side completion in the compat ledger

docs/compat/facilities/cluster-dlm.yaml (single-ledger source, INV-LEDGER): new
cluster-dlm$async-reply-wire item for the H5 rung — the master WIREs the
queued-reply + deferred GRANT over SCS, the requester-side GRANT RECEIVE completes
an executive-resident origin record, and the NL->EX flip is observed on the
requester. Notes the BLKAST wire deferred honestly and multi-peer deferred-grant
routing as a later rung. Summary updated; H5 evidence points at the two-node
harness. YAML validated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* vms-6ca: regenerate compat surface register for the H5 async-reply-wire item

Derived docs/compatibility-surface.md re-rendered from docs/compat/*.yaml via
tools/compat/render_compat.py (INV-LEDGER: yaml is source, md is generated).
407 items now catalogued.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci: quote the job name with a mid-value colon — repairs ci.yml parse (dark since #850)

The job name added today by #850 (vms-dec) contained an unquoted mid-value colon
('... before Username: (vms-dec)'), which is invalid YAML. GitHub Actions could
not parse ci.yml, so EVERY run of it — on main and every branch — completed with
ZERO jobs (no Build & Test, no harnesses, nothing); the only checks still firing
were the separate Ledger-drift and CodeQL workflows. Quoting the value restores
the whole workflow. Pre-existing project-wide breakage, surfaced while wiring the
H5 CI job (which could not run until ci.yml parsed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* vms-6ca: register the H5 client sender in the send-site census + repair H0's stale selftest op

Two fixes surfaced by re-enabling ci.yml:

1. scs_send_sites census: the new scsd_dlm_client_send_op() sender (node A's
   contending $ENQ + holder $DEQ) is added to the CHOKED half of the SEND SITE
   TABLE in scsd.c; the scsd_dlm_srv_msg_input entry now notes the deferred GRANT
   it also wires. test_scsd_send_sites: 109 checks, 0 failures.

2. DLM Harness H0 selftest: it drove a DEQ expecting SS$_UNSUPPORTED (2296), but
   rung 3 (#873, vms-904c) made DEQ real — a DEQ of an unknown handle now returns
   SS$_IVLOCKID (8484), so H0 had been red since #873 (hidden by the dark CI). The
   side-effect-free "executive reached, honest 2296" probe now uses BLKAST, whose
   receive op still honestly returns SS$_UNSUPPORTED (its wire is deferred, vms-6ca)
   and mutates no lock state. No run/init/ci grep change needed — still rc=2296 PASS.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <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.

1 participant