From a43846ed210b7fd77a81f2743de17ed0b2768fac Mon Sep 17 00:00:00 2001 From: alice Date: Mon, 10 Aug 2026 18:20:32 +0000 Subject: [PATCH 1/2] vms-21a: STARTUP.COM becomes a real STDRV phased startup driver Core mechanism, oracle-pinned to the Alpha V8.4 boot capture (docs/design-boot-faithful.md sec3.2/3.3): - A real [SYS0.SYSCOMMON.SYS$STARTUP] directory (ovmx_layout.h VMS_SYS_STARTUP) carries SYS$STARTUP:VMS$PHASES.DAT (the nine OBSERVED phase names verbatim: INITIAL DEVICES PRECONFIG CONFIG BASEENVIRON LPBEGIN LPMAIN LPBETA END) and SYS$STARTUP:VMS$VMS.DAT (an OVMX-DEFINED component-registration format, Rule 8 -- only the VSI file NAME is observed, the line format inside is OVMX's own invention). - SYS$STARTUP is now the measured multi-value SEARCH LIST logical (DEFINE/SYSTEM SYS$STARTUP dir,SYS$MANAGER -- exactly what vms-420 built DEFINE/SYSTEM multi-value support for). OVMX flattens the first element the same way lnm_defaults.c already flattens SYS$MANAGER (straight off SYS$SYSDEVICE rather than via a rooted SYS$SYSROOT), and STARTUP.COM's own comment flags that divergence from the oracle's literal "SYS$SYSROOT:[SYS$STARTUP]" text. - STARTUP.COM is now a GOSUB-based phase driver (RUN_PHASES): reads VMS$PHASES.DAT, and for each phase runs (a) every component VMS$VMS.DAT registers for it (RUN_COMPONENTS -- none registered yet, so every phase currently runs empty, which is honest per the design doc's "empty phases run empty" target) and (b) the one site file this project's reading of the OpenVMS System Manager's Manual assigns to that phase: SYCONFIG.COM at CONFIG, SYLOGICALS.COM at BASEENVIRON, SYSTARTUP_VMS.COM (unchanged content, only its call site moved) at LPMAIN. Verified end-to-end against a local build of DCL.EXE (process- scope logicals, since SYSTEM-scope needs the real executive) before wiring into the shipped file -- OPEN/READ/GOSUB/F$ELEMENT/EOF-via- $STATUS all confirmed correct. - SYS$MANAGER:OVMX.CONF and SYLOGICALS.CONF deleted: both verified to have ZERO code readers on main (OVMX.CONF was never referenced by any constant; SYLOGICALS.CONF's only reference, VMS_LNM_CONF_PATH, was itself dead and is removed). A real VMS site file is a DCL command procedure, not a KEY=VALUE config file, per design-boot-faithful.md sec2.4. NODE_NAME already maps to SCSNODE (vms-b6a7, unchanged by this commit); VERSION_LIMIT/DEFAULT_PROTECTION had no live OVMX SYSGEN parameter or SET command to map to, so they are dropped rather than ported to a home that doesn't exist yet. - SYCONFIG.COM and SYLOGICALS.COM are new, minimal, OVMX-authored site files (tools/ovmx_kit_pack.c already listed both as seed-once filenames, anticipating this work). ovmx_banner.h and test_identity_ssot.sh updated to point at SYLOGICALS.COM instead of the deleted .CONF. Dockerfile.bootable/CI wiring for the new directory and a QEMU boot proof follow in a subsequent commit on this branch. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../SYS0/SYSCOMMON/SYS$STARTUP/VMS$PHASES.DAT | 9 ++ .../SYS0/SYSCOMMON/SYS$STARTUP/VMS$VMS.DAT | 24 ++++ .../vms/SYS0/SYSCOMMON/SYSMGR/OVMX.CONF | 17 --- .../vms/SYS0/SYSCOMMON/SYSMGR/STARTUP.COM | 129 ++++++++++++++++-- .../vms/SYS0/SYSCOMMON/SYSMGR/SYCONFIG.COM | 23 ++++ .../vms/SYS0/SYSCOMMON/SYSMGR/SYLOGICALS.COM | 37 +++++ .../vms/SYS0/SYSCOMMON/SYSMGR/SYLOGICALS.CONF | 35 ----- src/libvms/include/ovmx_banner.h | 6 +- src/libvms/include/ovmx_layout.h | 53 ++++++- tests/integration/test_identity_ssot.sh | 8 +- 10 files changed, 265 insertions(+), 76 deletions(-) create mode 100644 distro/rootfs/vms/SYS0/SYSCOMMON/SYS$STARTUP/VMS$PHASES.DAT create mode 100644 distro/rootfs/vms/SYS0/SYSCOMMON/SYS$STARTUP/VMS$VMS.DAT delete mode 100644 distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/OVMX.CONF create mode 100644 distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/SYCONFIG.COM create mode 100644 distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/SYLOGICALS.COM delete mode 100644 distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/SYLOGICALS.CONF diff --git a/distro/rootfs/vms/SYS0/SYSCOMMON/SYS$STARTUP/VMS$PHASES.DAT b/distro/rootfs/vms/SYS0/SYSCOMMON/SYS$STARTUP/VMS$PHASES.DAT new file mode 100644 index 000000000..8ace64eee --- /dev/null +++ b/distro/rootfs/vms/SYS0/SYSCOMMON/SYS$STARTUP/VMS$PHASES.DAT @@ -0,0 +1,9 @@ +INITIAL +DEVICES +PRECONFIG +CONFIG +BASEENVIRON +LPBEGIN +LPMAIN +LPBETA +END diff --git a/distro/rootfs/vms/SYS0/SYSCOMMON/SYS$STARTUP/VMS$VMS.DAT b/distro/rootfs/vms/SYS0/SYSCOMMON/SYS$STARTUP/VMS$VMS.DAT new file mode 100644 index 000000000..74df24e34 --- /dev/null +++ b/distro/rootfs/vms/SYS0/SYSCOMMON/SYS$STARTUP/VMS$VMS.DAT @@ -0,0 +1,24 @@ +! +! VMS$VMS.DAT - STDRV component registration database +! +! OVMX-DEFINED FORMAT (CLAUDE.md Rule 8): only the FILE NAME is observed +! from the OpenVMS Alpha V8.4 oracle (docs/design-boot-faithful.md sec 3.3, +! DIRECTORY SYS$STARTUP:*.DAT). VSI's internal byte layout for this file was +! never read and is not reproduced here -- this line format is an OVMX +! invention, parsed by SYS$MANAGER:STARTUP.COM's RUN_COMPONENTS subroutine. +! +! One component per line: +! +! phase-name procedure-filespec +! +! separated by whitespace. "phase-name" must exactly match one of the nine +! names in SYS$STARTUP:VMS$PHASES.DAT. STARTUP.COM runs "procedure-filespec" +! (with @) when its phase comes up in the driver loop. Lines beginning with +! "!" and blank lines are comments/ignored. +! +! OVMX registers no components here yet -- there is no OVMX-authored +! per-phase driver procedure to name. An empty phase runs empty: STARTUP.COM +! prints nothing for a phase with no registered component, which is honest +! (docs/design-boot-faithful.md sec4 target #4) rather than inventing a +! component to demonstrate the mechanism against. +! diff --git a/distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/OVMX.CONF b/distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/OVMX.CONF deleted file mode 100644 index b40d85571..000000000 --- a/distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/OVMX.CONF +++ /dev/null @@ -1,17 +0,0 @@ -# OVMX Configuration File -# OpenVMS-Compatible Linux Distribution - -# VMS root directory (where SYS$SYSTEM etc. live) -VMS_ROOT=/vms - -# System node name -NODE_NAME=OVMX - -# Default file version limit (0 = unlimited) -VERSION_LIMIT=0 - -# Default protection for new files (SOGW) -DEFAULT_PROTECTION=S:RWED,O:RWED,G:RE,W: - -# Enable VMS-style file versioning -FILE_VERSIONING=1 diff --git a/distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/STARTUP.COM b/distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/STARTUP.COM index df0e3f3fc..ae2472600 100644 --- a/distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/STARTUP.COM +++ b/distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/STARTUP.COM @@ -1,4 +1,4 @@ -$! STARTUP.COM - OVMX System Startup Command Procedure +$! STARTUP.COM - OVMX System Startup Command Procedure (STDRV) $! $! Runs after kernel modules and system disk mount. There is no logical $! name daemon to wait for: on VMS the logical name tables are @@ -16,9 +16,9 @@ $! 3. provisions the accounts' home directories and the system tree's $! ownership; $! 4. execs DCL.EXE on THIS FILE, in the SAME process. $! -$! So this procedure -- and SYSTARTUP_VMS.COM after it -- runs under SYSTEM's -$! identity as the executive recorded it, which is what OpenVMS does: STARTUP -$! runs under username SYSTEM. +$! So this procedure -- and everything it invokes below -- runs under +$! SYSTEM's identity as the executive recorded it, which is what OpenVMS +$! does: STARTUP runs under username SYSTEM. $! $! PID 1 DOES NOT READ SYSUAF.DAT and must not start. It is statically linked, $! so when it did, it carried two hand-rolled 512-byte parsers of its own -- @@ -27,13 +27,59 @@ $! line limit could leave the system with no readable SYSTEM record and no $! boot. Account provisioning and system identity are system management, and $! system management belongs on this path. $! +$! -------------------------------------------------------------------- +$! THIS PROCEDURE IS STDRV -- A PHASED STARTUP DRIVER (vms-21a, +$! docs/design-boot-faithful.md sec3.2/3.3), not a flat script. It: +$! +$! 1. defines SYS$STARTUP (below) and the other system-wide logicals; +$! 2. reads the nine startup PHASES from SYS$STARTUP:VMS$PHASES.DAT +$! (OVMX-authored; the phase NAMES are OBSERVED OpenVMS Alpha V8.4 +$! boot behavior, CLAUDE.md Rule 8 -- a data file naming phases was +$! read, no VSI procedure content was); +$! 3. for each phase, in order, runs every component procedure the +$! OVMX-DEFINED database SYS$STARTUP:VMS$VMS.DAT registers for it +$! (RUN_COMPONENTS, below -- only the FILE NAME is observed; the line +$! format inside it is an OVMX invention, labeled at its own point of +$! definition in ovmx_layout.h). A phase with nothing registered runs +$! empty and prints nothing -- that is honest, not a bug; +$! 4. at the CONFIG phase, additionally runs SYS$MANAGER:SYCONFIG.COM; +$! at BASEENVIRON, SYS$MANAGER:SYLOGICALS.COM; at LPMAIN, +$! SYS$MANAGER:SYSTARTUP_VMS.COM -- the three documented OpenVMS +$! site-customization files (VSI OpenVMS System Manager's Manual, +$! "Customizing Startup with Site-Specific Files"). This phase +$! assignment is this project's own reading of that public +$! documentation, not an oracle capture -- flagged here rather than +$! silently presented as measured. +$! +$! This REPLACES SYS$MANAGER:OVMX.CONF and SYLOGICALS.CONF (both deleted, +$! vms-21a, docs/design-boot-faithful.md sec2.4): a Unix KEY=VALUE config +$! file is not how VMS configures a boot. NODE_NAME lives in the SYSGEN +$! parameter SCSNODE, already read at boot by read_boot_parameters() +$! (vms-b6a7); VERSION_LIMIT and DEFAULT_PROTECTION are SYSGEN parameters / +$! SET commands a site now places in SYCONFIG.COM, once OVMX has a SYSGEN +$! parameter or SET command for them -- neither exists yet, so neither +$! config key had anywhere honest to go and both are simply gone, not +$! ported (OVMX.CONF had ZERO code readers on main; verified again here). +$! $ SET NOON $! $! Display system time $ SHOW TIME $! -$! Define system-wide logical names -$ DEFINE/SYSTEM SYS$STARTUP SYS$MANAGER +$! Define system-wide logical names. +$! +$! SYS$STARTUP is the measured SEARCH LIST (docs/design-boot-faithful.md +$! sec3.2): "SYS$SYSROOT:[SYS$STARTUP]" then "SYS$MANAGER" -- a multi-value +$! logical, exactly what vms-420 built DEFINE/SYSTEM support for. OVMX does +$! not model SYS$SYSROOT as reaching SYSCOMMON on its own (lnm_defaults.c's +$! lnm_setup_defaults() seeds SYS$MANAGER the same flattened way, straight +$! off SYS$SYSDEVICE, rather than via SYS$SYSROOT:[SYSMGR]), so the first +$! element here is OVMX's flattened equivalent of the real directory +$! (ovmx_layout.h VMS_SYS_STARTUP) rather than the oracle's literal +$! "SYS$SYSROOT:[SYS$STARTUP]" text -- SHOW LOGICAL SYS$STARTUP will show +$! the OVMX path, not the VSI one, and that divergence is intentional and +$! documented here rather than silent. +$ DEFINE/SYSTEM SYS$STARTUP SYS$SYSDEVICE:[SYS0.SYSCOMMON.SYS$STARTUP],SYS$MANAGER $ DEFINE/SYSTEM SYS$LOGIN SYS$MANAGER $! $! SYS$UPDATE -- [SYS0.SYSCOMMON.SYSUPD], the standard VMS home for @@ -45,15 +91,68 @@ $! adding to that baseline table. $ DEFINE/SYSTEM SYS$UPDATE SYS$SYSDEVICE:[SYS0.SYSCOMMON.SYSUPD] $! $! Login banners are NOT set here -- LOGINOUT reads SYS$ANNOUNCE and -$! SYS$WELCOME, which a manager defines in SYS$MANAGER:SYLOGICALS.CONF. -$! The startup banner itself is displayed by STARTUP.EXE from the system -$! identity module, so there is exactly one place that knows the version. +$! SYS$WELCOME, which a manager defines in SYS$MANAGER:SYLOGICALS.COM (run +$! at the BASEENVIRON phase, below). The startup banner itself is +$! displayed by STARTUP.EXE from the system identity module, so there is +$! exactly one place that knows the version. $! -$! Site-specific startup. This is where services are started, and the -$! only place: STARTUP.EXE (PID 1) starts none of its own. Invoked -$! unconditionally and last, after the system logical names exist -- if -$! the procedure is missing, DCL says so rather than skipping it -$! silently. -$ @SYS$MANAGER:SYSTARTUP_VMS.COM +$! Run the phase driver. Every startup phase after this line -- component +$! procedures and the three site files -- happens inside RUN_PHASES. +$ GOSUB RUN_PHASES $! $ EXIT +$! +$! ====================================================================== +$! RUN_PHASES - STDRV's phase loop. +$! +$! Reads SYS$STARTUP:VMS$PHASES.DAT one phase name per line (READ signals +$! end-of-file as SS$_ENDOFFILE, decimal 2160 -- checked via $STATUS since +$! this DCL has no READ/END_OF_FILE= qualifier). For each phase: run its +$! registered components (RUN_COMPONENTS), then the one site file (if any) +$! documented for that phase. +$! ====================================================================== +$ RUN_PHASES: +$ OPEN/READ PHASE_FILE SYS$STARTUP:VMS$PHASES.DAT +$ PHASE_LOOP: +$ READ PHASE_FILE PHASE_NAME +$ IF $STATUS .EQ. 2160 THEN GOTO PHASE_LOOP_DONE +$ GOSUB RUN_COMPONENTS +$ IF PHASE_NAME .EQS. "CONFIG" THEN @SYS$MANAGER:SYCONFIG.COM +$ IF PHASE_NAME .EQS. "BASEENVIRON" THEN @SYS$MANAGER:SYLOGICALS.COM +$ IF PHASE_NAME .EQS. "LPMAIN" THEN GOSUB RUN_SITE_STARTUP +$ GOTO PHASE_LOOP +$ PHASE_LOOP_DONE: +$ CLOSE PHASE_FILE +$ RETURN +$! +$! RUN_SITE_STARTUP - the LPMAIN-phase site-specific commands (the role +$! SYSTARTUP_VMS.COM already had before vms-21a; unchanged here, only its +$! call site moved into the phase driver). See SYSTARTUP_VMS.COM's own +$! header for why this text is not oracle-pinned. +$ RUN_SITE_STARTUP: +$ WRITE SYS$OUTPUT "The OVMX system is now executing the site-specific startup commands." +$ @SYS$MANAGER:SYSTARTUP_VMS.COM +$ RETURN +$! +$! RUN_COMPONENTS - run every SYS$STARTUP:VMS$VMS.DAT component registered +$! for the CURRENT phase (PHASE_NAME, set by RUN_PHASES above). See +$! SYS$STARTUP:VMS$VMS.DAT's own header for the OVMX-defined line format +$! (Rule 8: this format is OVMX's invention, not VMS-authentic). A phase +$! with no matching line runs nothing and prints nothing -- an empty phase +$! is honest, not a shortcut. +$ RUN_COMPONENTS: +$ OPEN/READ COMPONENT_FILE SYS$STARTUP:VMS$VMS.DAT +$ COMPONENT_LOOP: +$ READ COMPONENT_FILE COMPONENT_LINE +$ IF $STATUS .EQ. 2160 THEN GOTO COMPONENT_LOOP_DONE +$ IF COMPONENT_LINE .EQS. "" THEN GOTO COMPONENT_LOOP +$ COMPONENT_FIRST_CHAR = F$EXTRACT(0,1,COMPONENT_LINE) +$ IF COMPONENT_FIRST_CHAR .EQS. "!" THEN GOTO COMPONENT_LOOP +$ COMPONENT_PHASE = F$ELEMENT(0," ",'COMPONENT_LINE') +$ IF COMPONENT_PHASE .NES. PHASE_NAME THEN GOTO COMPONENT_LOOP +$ COMPONENT_PROC = F$ELEMENT(1," ",'COMPONENT_LINE') +$ IF COMPONENT_PROC .NES. "" THEN @'COMPONENT_PROC' +$ GOTO COMPONENT_LOOP +$ COMPONENT_LOOP_DONE: +$ CLOSE COMPONENT_FILE +$ RETURN diff --git a/distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/SYCONFIG.COM b/distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/SYCONFIG.COM new file mode 100644 index 000000000..b4efaf75d --- /dev/null +++ b/distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/SYCONFIG.COM @@ -0,0 +1,23 @@ +$! SYCONFIG.COM - Site-specific device/configuration startup command +$! procedure (vms-21a). +$! +$! Invoked by SYS$MANAGER:STARTUP.COM's phase driver (STDRV) during the +$! CONFIG phase, before SYLOGICALS.COM and well before SYSTARTUP_VMS.COM's +$! site-specific commands (VSI OpenVMS System Manager's Manual, +$! "Customizing Startup with Site-Specific Files" -- SYCONFIG.COM is the +$! documented home for device and controller configuration commands, run +$! early enough that later startup phases see the configured devices). +$! Edits on the system disk persist across reboots and survive a PRODUCT +$! INSTALL upgrade (seed-once, vms-2c9 -- see +$! tools/ovmx_kit_pack.c is_seed_once_filename()). +$! +$! THIS PROCEDURE CONFIGURES NO DEVICE, AND THAT IS DELIBERATE. OVMX has no +$! device-configuration facility yet (no SET DEVICE-style controller setup) +$! -- a worked example here would name a command that does not exist on +$! OVMX, which would fail on every boot and teach the reader that the +$! failure is normal. Same honesty rule SYSTARTUP_VMS.COM's own header +$! documents for service startup. +$! +$ SET NOON +$! +$ EXIT diff --git a/distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/SYLOGICALS.COM b/distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/SYLOGICALS.COM new file mode 100644 index 000000000..d0c921e3c --- /dev/null +++ b/distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/SYLOGICALS.COM @@ -0,0 +1,37 @@ +$! SYLOGICALS.COM - Site-specific logical name definitions (vms-21a). +$! +$! Invoked by SYS$MANAGER:STARTUP.COM's phase driver (STDRV) during the +$! BASEENVIRON phase -- after SYCONFIG.COM has configured devices and after +$! STARTUP.COM's own system logical names (SYS$STARTUP, SYS$LOGIN, +$! SYS$UPDATE) exist, but before SYSTARTUP_VMS.COM starts any site service +$! (VSI OpenVMS System Manager's Manual, "Customizing Startup with +$! Site-Specific Files" -- SYLOGICALS.COM is the documented home for a +$! site's own DEFINE/SYSTEM commands, run early enough that every later +$! startup phase and every login session sees them). +$! +$! This REPLACES the old SYS$MANAGER:SYLOGICALS.CONF (deleted, vms-21a): a +$! real VMS site file is a command procedure a manager edits with DCL +$! DEFINE commands, never a "TABLE NAME VALUE" config file for a daemon to +$! parse. Edits on the system disk persist across reboots and survive a +$! PRODUCT INSTALL upgrade (seed-once, vms-2c9 -- see +$! tools/ovmx_kit_pack.c is_seed_once_filename()). +$! +$ SET NOON +$! +$! --------------------------------------------------------------------- +$! Login banners (LOGINOUT, src/libvms/include/ovmx_banner.h). +$! +$! SYS$ANNOUNCE is displayed BEFORE the "Username:" prompt. +$! SYS$WELCOME is displayed AFTER a successful login. +$! +$! Both are left UNDEFINED here, as OpenVMS ships them: with no SYS$WELCOME +$! defined, the built-in OVMX banner is displayed, and with no SYS$ANNOUNCE +$! nothing is displayed before the prompt. Uncomment to override. +$! +$! An equivalence string beginning with "@" names a FILE whose contents are +$! displayed -- use that for a multi-line site banner. +$! +$! $ DEFINE/SYSTEM SYS$ANNOUNCE "Unauthorized access is prohibited." +$! $ DEFINE/SYSTEM SYS$WELCOME "@SYS$MANAGER:WELCOME.TXT" +$! +$ EXIT diff --git a/distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/SYLOGICALS.CONF b/distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/SYLOGICALS.CONF deleted file mode 100644 index 03bc99866..000000000 --- a/distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/SYLOGICALS.CONF +++ /dev/null @@ -1,35 +0,0 @@ -# System Logical Name Definitions -# Format: TABLE LOGICAL_NAME EQUIVALENCE [ATTRIBUTES] -# Loaded by LNM daemon and init script on startup - -SYSTEM SYS$SYSTEM /vms/sys$system -SYSTEM SYS$LIBRARY /vms/sys$library -SYSTEM SYS$MANAGER /vms/sys$manager -SYSTEM SYS$HELP /vms/sys$help -SYSTEM SYS$SCRATCH /tmp -SYSTEM SYS$NODE OVMX -SYSTEM TT /dev/tty -SYSTEM SYS$DISK / -SYSTEM DECW$DISPLAY /dev/null -SYSTEM SYS$OUTPUT /dev/tty -SYSTEM SYS$INPUT /dev/tty -SYSTEM SYS$ERROR /dev/tty -SYSTEM SYS$COMMAND /dev/tty -SYSTEM SYS$SYSROOT /vms -SYSTEM SYS$SYSDEVICE / - -# --------------------------------------------------------------------- -# Login banners (LOGINOUT). -# -# SYS$ANNOUNCE is displayed BEFORE the "Username:" prompt. -# SYS$WELCOME is displayed AFTER a successful login. -# -# Both are left UNDEFINED here, as OpenVMS ships them: with no SYS$WELCOME -# the built-in OVMX banner is displayed, and with no SYS$ANNOUNCE nothing -# is displayed before the prompt. Uncomment to override. -# -# An equivalence string beginning with '@' names a FILE whose contents are -# displayed -- use that for a multi-line banner. -# -# SYSTEM SYS$ANNOUNCE Unauthorized access is prohibited. -# SYSTEM SYS$WELCOME @SYS$MANAGER:WELCOME.TXT diff --git a/src/libvms/include/ovmx_banner.h b/src/libvms/include/ovmx_banner.h index b8fc1b1f6..5e93668e4 100644 --- a/src/libvms/include/ovmx_banner.h +++ b/src/libvms/include/ovmx_banner.h @@ -2,8 +2,10 @@ * ovmx_banner.h - SYS$ANNOUNCE / SYS$WELCOME banner resolution (INV-1) * * On OpenVMS the login banners are NOT compiled into LOGINOUT -- they are - * system logical names a manager defines at boot (SYS$MANAGER:SYLOGICALS.COM, - * OVMX: SYS$MANAGER:SYLOGICALS.CONF): + * system logical names a manager defines at boot + * (SYS$MANAGER:SYLOGICALS.COM, same on OVMX since vms-21a -- SYLOGICALS.COM + * is a real DCL site procedure, not the SYLOGICALS.CONF config-file LARP it + * replaced): * * SYS$ANNOUNCE displayed BEFORE the "Username:" prompt. Undefined by * default -- nothing is printed. diff --git a/src/libvms/include/ovmx_layout.h b/src/libvms/include/ovmx_layout.h index 335323ef9..173b1de81 100644 --- a/src/libvms/include/ovmx_layout.h +++ b/src/libvms/include/ovmx_layout.h @@ -16,6 +16,9 @@ * [SYS0.SYSCOMMON.SYSLIB] SYS$LIBRARY/SYS$SHARE — libraries * [SYS0.SYSCOMMON.SYSMGR] SYS$MANAGER — admin configs * [SYS0.SYSCOMMON.SYSHLP] SYS$HELP — help files + * [SYS0.SYSCOMMON.SYS$STARTUP] SYS$STARTUP — the startup driver's phase + * and component data files (vms-21a, + * docs/design-boot-faithful.md §3.2/§3.3) * [USERS] User home directories * [SYSTMP] Scratch / temporary files */ @@ -62,6 +65,19 @@ #define VMS_SYSLIB SYSDISK_DEVICE ":[SYS0.SYSCOMMON.SYSLIB]" #define VMS_SYSMGR SYSDISK_DEVICE ":[SYS0.SYSCOMMON.SYSMGR]" #define VMS_SYSHLP SYSDISK_DEVICE ":[SYS0.SYSCOMMON.SYSHLP]" +/* + * SYS$STARTUP -- the startup driver's home directory (vms-21a). Measured + * (docs/design-boot-faithful.md §3.2): the real logical is the SEARCH LIST + * "SYS$SYSROOT:[SYS$STARTUP]" then "SYS$MANAGER". OVMX does not model + * SYS$SYSROOT as a rooted logical that itself reaches SYSCOMMON (it flattens + * every SYSCOMMON subdirectory directly off SYS$SYSDEVICE -- see + * lnm_setup_defaults()'s own SYS$MANAGER, which is seeded the same way + * instead of via SYS$SYSROOT:[SYSMGR]), so the first search-list element is + * this flattened path rather than the oracle's literal "SYS$SYSROOT: + * [SYS$STARTUP]" text -- STARTUP.COM's own DEFINE/SYSTEM documents this at + * the point it runs. + */ +#define VMS_SYS_STARTUP SYSDISK_DEVICE ":[SYS0.SYSCOMMON.SYS$STARTUP]" #define VMS_USERS SYSDISK_DEVICE ":[USERS]" #define VMS_SYSTMP SYSDISK_DEVICE ":[SYSTMP]" @@ -79,12 +95,39 @@ * starts none of its own -- see the NOTE ON SERVICES in * src/ovmx_init/ovmx_init.c (vms-47b). */ #define VMS_SYSTARTUP_PATH "SYS$MANAGER:SYSTARTUP_VMS.COM" +/* + * SYCONFIG.COM / SYLOGICALS.COM (vms-21a) -- the other two documented + * OpenVMS site-customization startup procedures (VSI OpenVMS System + * Manager's Manual, "Customizing Startup"), invoked by STARTUP.COM's phase + * driver at CONFIG and BASEENVIRON respectively -- see STARTUP.COM's own + * comments for the exact call sites. These REPLACE the Unix-shell-flavored + * SYS$MANAGER:OVMX.CONF and SYS$MANAGER:SYLOGICALS.CONF (deleted, zero + * readers, docs/design-boot-faithful.md §2.4): a real VMS site file is a + * command PROCEDURE a manager edits with DCL commands in it, never a + * KEY=VALUE config file. Both are seed-once on upgrade (vms-2c9, + * tools/ovmx_kit_pack.c is_seed_once_filename()). + */ +#define VMS_SYCONFIG_PATH "SYS$MANAGER:SYCONFIG.COM" +#define VMS_SYLOGICALS_PATH "SYS$MANAGER:SYLOGICALS.COM" #define VMS_HELPLIB_PATH "SYS$HELP:HELPLIB.HLP" -/* Reserved: zero readers as of vms-a4b (its only reader, lnm_daemon.c, - * was deleted). Not wired to anything until the executive-resident - * logical name work lands — see vms-ln0 (gated, operator sign-off - * pending) and vms-d37. Do not add a reader without that ruling. */ -#define VMS_LNM_CONF_PATH "SYS$MANAGER:SYLOGICALS.CONF" +/* + * STDRV's phase-driver data files, under SYS$STARTUP: (vms-21a). + * + * VMS$PHASES.DAT -- the nine phase names, VERBATIM as measured + * (docs/design-boot-faithful.md §3.3, Alpha 8.4 oracle capture): a plain + * list of names, one per line, is a DATA FILE under Rule 8 -- reading it + * during the lab capture was permitted and its content is OBSERVED, not + * invented. + * + * VMS$VMS.DAT -- the phase -> component-procedure registration. Only the + * FILE NAME is observed (§3.3's DIRECTORY listing); VSI's internal byte + * format was never read and is not reproduced. The line format STARTUP.COM + * parses ("PHASE-NAME procedure-filespec", one component per line) is an + * OVMX INVENTION, labeled as such at every place that reads or writes it + * (Rule 8) -- never presented as VMS-authentic. + */ +#define VMS_PHASES_DAT_PATH "SYS$STARTUP:VMS$PHASES.DAT" +#define VMS_COMPONENTS_DAT_PATH "SYS$STARTUP:VMS$VMS.DAT" #define VMS_SSH_HOST_KEY "SYS$MANAGER:SSH_HOST_RSA_KEY" /* ------------------------------------------------------------------ */ diff --git a/tests/integration/test_identity_ssot.sh b/tests/integration/test_identity_ssot.sh index 177931268..a1972353b 100755 --- a/tests/integration/test_identity_ssot.sh +++ b/tests/integration/test_identity_ssot.sh @@ -29,7 +29,10 @@ SSOT="src/libvms/include/ovmx_identity.h" # DATA (e.g. the software-version field of a captured SCS START packet), # not OVMX's own identity. They must match the specimen, not our version. # -# distro/**/STARTUP.COM and SYLOGICALS.CONF are scanned as text below. +# distro/**/STARTUP.COM and SYLOGICALS.COM are scanned as text below. +# (SYLOGICALS.CONF -- the Unix-config-file LARP -- was deleted by vms-21a; +# SYLOGICALS.COM, a real DCL site procedure, is where a banner override now +# lives, so it is the file this gate checks instead.) echo "INV-1 identity SSOT gate: scanning for hardcoded version literals" @@ -57,7 +60,8 @@ fi # --- 2. Boot-time DCL/config must not bake in a banner version ------ for f in "$SRC_ROOT/distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/STARTUP.COM" \ - "$SRC_ROOT/distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/SYLOGICALS.CONF"; do + "$SRC_ROOT/distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/SYLOGICALS.COM" \ + "$SRC_ROOT/distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/SYCONFIG.COM"; do [ -f "$f" ] || continue bad=$(grep -nE '(OpenVMS V|OVMX V)[0-9]' "$f" | grep -v '^[0-9]*:[[:space:]]*#' || true) if [ -n "$bad" ]; then From 9276f9f93bac675814bea73bf18b429868c0612a Mon Sep 17 00:00:00 2001 From: alice Date: Mon, 10 Aug 2026 18:24:52 +0000 Subject: [PATCH 2/2] vms-21a: wire SYS$STARTUP into the bootable image + add the phase-driver boot proof Dockerfile.bootable: the FAT initramfs, kit-stage, and mastered distrib image all now carry [SYS0.SYSCOMMON.SYS$STARTUP] (VMS$PHASES.DAT, VMS$VMS.DAT) alongside the existing SYSEXE/SYSLIB/SYSMGR/SYSHLP/SYSUPD directories -- without this copy STARTUP.COM's new phase-driver OPENs fail %RMS-E-FNF on every boot. Added build-time gates on the mastered distrib image: VMS$PHASES.DAT/VMS$VMS.DAT/SYCONFIG.COM/SYLOGICALS.COM must be present, and OVMX.CONF/SYLOGICALS.CONF must NOT be -- a regression that recreates either deleted file now fails the build, not just a runtime check. tests/qemu/test_startup_phase_driver.sh (new): boots the pre-installed distribution disk, logs in over the console, and proves against the REAL running system (not a build-time inspection) that SHOW LOGICAL SYS$STARTUP reports the measured multi-value search list (SYS$MANAGER as its second element -- vms-420's multi-value DEFINE/SYSTEM actually ran at boot), SYS$STARTUP:/SYS$MANAGER: carry the new data/site files, and DIRECTORY SYS$MANAGER:OVMX.CONF finds nothing on the booted, mounted disk. Also checks the pre-login boot console for the LPMAIN-phase site announcement and the absence of %RMS-E-FNF/ %DCL-E-OPENIN during boot (scoped to the boot-only log segment, so the later intentional OVMX.CONF absence-check can't be mistaken for a regression). Registered in ci.yml's persistent-boot job, right after the existing distrib-boot test it reuses the built image from. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/ci.yml | 16 ++ distro/Dockerfile.bootable | 30 ++- tests/qemu/test_startup_phase_driver.sh | 267 ++++++++++++++++++++++++ 3 files changed, 310 insertions(+), 3 deletions(-) create mode 100755 tests/qemu/test_startup_phase_driver.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d8893377c..66c1bb6b4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -102,6 +102,7 @@ jobs: - 'tests/qemu/test_release_e2e.sh' - 'tests/qemu/test_executive_integral.sh' - 'tests/qemu/test_distrib_boot.sh' + - 'tests/qemu/test_startup_phase_driver.sh' - 'tests/qemu/test_boot_scsnode_hostname_e2e.sh' - 'tests/qemu/run_boot_scsnode_hostname_e2e.sh' - 'tests/uat/**' @@ -379,6 +380,21 @@ jobs: ovmx-boot:latest \ /test.sh + # vms-21a. Reuses the image built above (no extra build). Boots the same + # pre-installed distribution disk and, over a real login session, proves + # STARTUP.COM is now a real STDRV phase driver: SHOW LOGICAL SYS$STARTUP + # names the measured multi-value search list, SYS$STARTUP:/SYS$MANAGER: + # carry the phase-driver's data files and the new site files, and + # SYS$MANAGER:OVMX.CONF is verified absent from the booted, mounted + # system disk (not merely deleted from the source tree). + - name: STARTUP.COM phase driver — SYS$STARTUP search list, OVMX.CONF gone + run: | + docker run --rm \ + -v ${{ github.workspace }}/tests/qemu/test_startup_phase_driver.sh:/test.sh:ro \ + --entrypoint bash \ + ovmx-boot:latest \ + /test.sh + # ----------------------------------------------------------------------- # cut-release-reproducible (vms-d73, epic vms-a84 RELEASE ENGINEERING) # diff --git a/distro/Dockerfile.bootable b/distro/Dockerfile.bootable index ac0e6621b..957800b60 100644 --- a/distro/Dockerfile.bootable +++ b/distro/Dockerfile.bootable @@ -203,6 +203,7 @@ RUN mkdir -p /initramfs-fat/dev /initramfs-fat/proc /initramfs-fat/sys \ /initramfs-fat/vms/SYS0/SYSCOMMON/SYSMGR \ /initramfs-fat/vms/SYS0/SYSCOMMON/SYSHLP \ /initramfs-fat/vms/SYS0/SYSCOMMON/SYSUPD \ + "/initramfs-fat/vms/SYS0/SYSCOMMON/SYS\$STARTUP" \ /initramfs-fat/vms/USERS \ /initramfs-fat/vms/SYSTMP && \ # OVMX binaries → SYSEXE (STARTUP.EXE is also /init). @@ -254,6 +255,11 @@ RUN mkdir -p /initramfs-fat/dev /initramfs-fat/proc /initramfs-fat/sys \ cp src/kernel/vmsfs/vmsfs.ko /initramfs-fat/lib/modules/ && \ # System disk config files → VMS-standard locations cp -r distro/rootfs/vms/SYS0/SYSCOMMON/SYSMGR/* /initramfs-fat/vms/SYS0/SYSCOMMON/SYSMGR/ && \ + # SYS$STARTUP: (vms-21a) -- STDRV's phase/component data files + # (VMS$PHASES.DAT, VMS$VMS.DAT). STARTUP.COM's phase driver opens these + # by filespec at boot; without this copy every OPEN fails %RMS-E-FNF and + # every phase (and every site file the driver invokes) silently never runs. + cp -r "distro/rootfs/vms/SYS0/SYSCOMMON/SYS\$STARTUP/"* "/initramfs-fat/vms/SYS0/SYSCOMMON/SYS\$STARTUP/" && \ cp distro/rootfs/vms/SYS0/SYSCOMMON/SYSEXE/SYSUAF.DAT /initramfs-fat/vms/SYS0/SYSCOMMON/SYSEXE/ && \ cp distro/rootfs/vms/SYS0/SYSCOMMON/SYSEXE/RIGHTSLIST.DAT /initramfs-fat/vms/SYS0/SYSCOMMON/SYSEXE/ 2>/dev/null ; \ # OVMXVMSSYS.PAR (vms-d34): the initial SYSGEN parameter file, seeded @@ -342,15 +348,18 @@ RUN mkdir -p /initramfs-fat/dev /initramfs-fat/proc /initramfs-fat/sys \ # ovmx_kit_pack (tools/ovmx_kit_pack.c) is factory BUILD tooling -- same # class as vmsfs_master above: never shipped on the media, plain host name, # never run at boot. It treats the direct children of its staging directory -# as top-level VMS directories, so a scratch tree carrying only the four +# as top-level VMS directories, so a scratch tree carrying only the five # component directories (not SYSUPD, which holds PARTS -- a separate, # already-a-kit-shaped layered product, not the OS payload) is assembled -# here before packing. -RUN mkdir -p /kit-stage/SYSEXE /kit-stage/SYSLIB /kit-stage/SYSMGR /kit-stage/SYSHLP && \ +# here before packing. SYS$STARTUP (vms-21a) ships so a PRODUCT INSTALL +# upgrade carries STDRV's phase/component data files, same as any other +# system directory. +RUN mkdir -p /kit-stage/SYSEXE /kit-stage/SYSLIB /kit-stage/SYSMGR /kit-stage/SYSHLP "/kit-stage/SYS\$STARTUP" && \ cp -r /initramfs-fat/vms/SYS0/SYSCOMMON/SYSEXE/. /kit-stage/SYSEXE/ && \ cp -r /initramfs-fat/vms/SYS0/SYSCOMMON/SYSLIB/. /kit-stage/SYSLIB/ && \ cp -r /initramfs-fat/vms/SYS0/SYSCOMMON/SYSMGR/. /kit-stage/SYSMGR/ && \ cp -r /initramfs-fat/vms/SYS0/SYSCOMMON/SYSHLP/. /kit-stage/SYSHLP/ 2>/dev/null ; \ + cp -r "/initramfs-fat/vms/SYS0/SYSCOMMON/SYS\$STARTUP/." "/kit-stage/SYS\$STARTUP/" && \ /src/build-static/bin/ovmx_kit_pack pack /boot/ovmx-os.kit /kit-stage "X86VMS VMS" && \ /src/build-static/bin/ovmx_kit_pack list /boot/ovmx-os.kit && \ # Ground-source gate: the kit manifest must actually name the images the @@ -445,6 +454,21 @@ RUN /src/build-static/bin/vmsfs_master master \ done && \ [ -f /tmp/distrib-verify/SYS0/SYSCOMMON/SYSMGR/STARTUP.COM ] || { echo "FAIL: mastered image missing SYS\$MANAGER:STARTUP.COM"; exit 1; } && \ echo "OK: ovmx-distrib.img carries SYS\$MANAGER:STARTUP.COM" && \ + # vms-21a: STDRV's phase-driver data files must be on the mastered disk, + # or every OPEN in STARTUP.COM's phase driver fails %RMS-E-FNF and no + # phase (and no site file the driver invokes) ever runs. + [ -f "/tmp/distrib-verify/SYS0/SYSCOMMON/SYS\$STARTUP/VMS\$PHASES.DAT" ] || { echo "FAIL: mastered image missing SYS\$STARTUP:VMS\$PHASES.DAT"; exit 1; } && \ + [ -f "/tmp/distrib-verify/SYS0/SYSCOMMON/SYS\$STARTUP/VMS\$VMS.DAT" ] || { echo "FAIL: mastered image missing SYS\$STARTUP:VMS\$VMS.DAT"; exit 1; } && \ + echo "OK: ovmx-distrib.img carries SYS\$STARTUP:VMS\$PHASES.DAT and VMS\$VMS.DAT" && \ + [ -f /tmp/distrib-verify/SYS0/SYSCOMMON/SYSMGR/SYCONFIG.COM ] || { echo "FAIL: mastered image missing SYS\$MANAGER:SYCONFIG.COM"; exit 1; } && \ + [ -f /tmp/distrib-verify/SYS0/SYSCOMMON/SYSMGR/SYLOGICALS.COM ] || { echo "FAIL: mastered image missing SYS\$MANAGER:SYLOGICALS.COM"; exit 1; } && \ + echo "OK: ovmx-distrib.img carries SYS\$MANAGER:SYCONFIG.COM and SYLOGICALS.COM" && \ + # The LARP config files this work deletes must never come back on the + # mastered image -- a regression that recreates distro/rootfs/.../ + # OVMX.CONF would silently ship it again without this gate. + [ ! -f /tmp/distrib-verify/SYS0/SYSCOMMON/SYSMGR/OVMX.CONF ] || { echo "FAIL: mastered image carries deleted SYS\$MANAGER:OVMX.CONF"; exit 1; } && \ + [ ! -f /tmp/distrib-verify/SYS0/SYSCOMMON/SYSMGR/SYLOGICALS.CONF ] || { echo "FAIL: mastered image carries deleted SYS\$MANAGER:SYLOGICALS.CONF"; exit 1; } && \ + echo "OK: ovmx-distrib.img carries neither OVMX.CONF nor SYLOGICALS.CONF" && \ # The SYSTEM-writable persistent directories must be baked in: PID 1 no # longer creates them (vms-2f0), so if the mastering source ever loses them # SYS$SCRATCH:/SYS$LOGIN: writes fail at runtime (%RMS-E-DNF). Assert both diff --git a/tests/qemu/test_startup_phase_driver.sh b/tests/qemu/test_startup_phase_driver.sh new file mode 100755 index 000000000..8b70ff40c --- /dev/null +++ b/tests/qemu/test_startup_phase_driver.sh @@ -0,0 +1,267 @@ +#!/bin/bash +# test_startup_phase_driver.sh - STARTUP.COM is a real STDRV phase driver +# over a real SYS$STARTUP search list, and OVMX.CONF is gone (vms-21a). +# +# Runs inside the ovmx-boot Docker image (has QEMU + kernel + the SLIM +# initramfs + /boot/ovmx-distrib.img, the mastered distribution system disk). +# +# WHAT THIS PROVES (docs/design-boot-faithful.md §3.2/§3.3, target shape #4): +# +# Boots the pre-installed distribution disk (same shape as +# test_distrib_boot.sh) and, once logged in, drives real DCL commands +# against the REAL executive/vmsfs (not a build-time inspection) to show: +# +# - SHOW LOGICAL SYS$STARTUP reports the measured multi-value search +# list (two equivalence strings, the second literally "SYS$MANAGER") -- +# proof DEFINE/SYSTEM's vms-420 multi-value path is what actually ran +# at boot, not just testable in isolation. +# - DIRECTORY SYS$STARTUP:*.DAT lists VMS$PHASES.DAT and VMS$VMS.DAT -- +# the phase driver's data files are really on the booted system disk. +# - DIRECTORY SYS$MANAGER:*.COM lists SYCONFIG.COM and SYLOGICALS.COM -- +# the new site files STARTUP.COM's phase driver invokes exist. +# - DIRECTORY SYS$MANAGER:OVMX.CONF reports zero files -- the deleted +# config file is not merely absent from the source tree, it is absent +# from the booted, mounted volume a session actually sees. +# +# The boot console log (captured from the same boot, before login) is also +# checked for the LPMAIN-phase site announcement line and for the absence +# of any %RMS-E-FNF / %DCL-E-OPENIN error, which would mean the phase +# driver's own OPEN calls failed silently on this disk. +# +# Usage: +# docker build -f distro/Dockerfile.bootable -t ovmx-boot . +# docker run --rm -v $PWD/tests/qemu/test_startup_phase_driver.sh:/test.sh:ro \ +# --entrypoint bash ovmx-boot /test.sh +# +# Exit code 0 = all checks pass, 1 = failures. + +set -uo pipefail + +TIMEOUT=90 +DISTRIB_IMG=/boot/ovmx-distrib.img +DISK="/tmp/test-phase-driver-sysdisk.img" +KERNEL=/boot/vmlinuz +SLIM_INITRD=/boot/initramfs-ovmx-slim.cpio.gz +ARCH=$(uname -m) + +PASS=0 +FAIL=0 +TOTAL=0 + +if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then + QEMU=qemu-system-aarch64 + MACHINE="-machine virt -cpu cortex-a57" + CONSOLE="console=ttyAMA0" +else + QEMU=qemu-system-x86_64 + MACHINE="" + CONSOLE="console=ttyS0" +fi + +record() { + local desc="$1" rc="$2" + TOTAL=$((TOTAL + 1)) + if [ "$rc" -eq 0 ]; then + echo " PASS: $desc" + PASS=$((PASS + 1)) + else + echo " FAIL: $desc" + FAIL=$((FAIL + 1)) + fi +} + +echo "=== OVMX STARTUP.COM Phase Driver Test (vms-21a) ===" +echo "Architecture: $ARCH QEMU: $QEMU" +echo "Kernel: $KERNEL Slim initrd: $SLIM_INITRD Distribution image: $DISTRIB_IMG" +echo "" + +if [ -f "$DISTRIB_IMG" ]; then + record "Mastered distribution image present in the built image" 0 +else + record "Mastered distribution image present in the built image" 1 + echo "FATAL: $DISTRIB_IMG missing — the mastering stage did not run" + exit 1 +fi + +rm -f "$DISK" +cp "$DISTRIB_IMG" "$DISK" + +CONSOLE_LOG="/tmp/test-phase-driver-console.log" +FIFO="/tmp/test-phase-driver-console.in" + +boot() { + rm -f "$CONSOLE_LOG" "$FIFO" + mkfifo "$FIFO" + # shellcheck disable=SC2086 + timeout "$TIMEOUT" $QEMU $MACHINE \ + -kernel "$KERNEL" \ + -initrd "$SLIM_INITRD" \ + -nographic \ + -append "$CONSOLE loglevel=3 quiet" \ + -m 256M \ + -smp 1 \ + -nic none \ + -nodefaults \ + -serial stdio \ + -drive file="$DISK",format=raw,if=virtio \ + -no-reboot \ + <"$FIFO" >"$CONSOLE_LOG" 2>&1 & + QEMU_PID=$! + exec 4>"$FIFO" +} + +cleanup() { + exec 4>&- 2>/dev/null || true + kill "$QEMU_PID" 2>/dev/null || true + wait "$QEMU_PID" 2>/dev/null || true + rm -f "$FIFO" +} + +send() { printf '%s\r' "$1" >&4; } + +wait_for() { + local pattern="$1" limit="${2:-30}" since="${3:-0}" waited=0 + while [ "$waited" -lt "$((limit * 4))" ]; do + if tail -c "+$((since + 1))" "$CONSOLE_LOG" 2>/dev/null | grep -qF "$pattern"; then + return 0 + fi + if ! kill -0 "$QEMU_PID" 2>/dev/null; then + return 1 + fi + sleep 0.25 + waited=$((waited + 1)) + done + return 1 +} + +boot +trap cleanup EXIT + +OK=1 + +if wait_for '%OVMX-I-EXEC' 60; then rc=0; else rc=1; OK=0; fi +record "executive attached" "$rc" + +if [ "$OK" -eq 1 ]; then + if wait_for 'Username:' 60; then rc=0; else rc=1; OK=0; fi + record "reaches the login prompt (phase driver ran to completion)" "$rc" +fi + +# Snapshot the BOOT-ONLY portion of the console (everything up to the first +# login prompt) before any interactive command runs below -- a later +# DIRECTORY of a deliberately-absent file is expected to print "0 files" and +# must never be mistaken for a boot-time OPEN failure. +BOOT_LOG="" +if [ "$OK" -eq 1 ]; then + BOOT_LOG=$(cat "$CONSOLE_LOG") +fi + +if [ "$OK" -eq 1 ]; then + LOGIN_OFFSET=$(wc -c <"$CONSOLE_LOG") + send 'SYSTEM' + if wait_for 'Password:' 30 "$LOGIN_OFFSET"; then rc=0; else rc=1; OK=0; fi + record "password prompt appears" "$rc" +fi + +if [ "$OK" -eq 1 ]; then + send 'MANAGER' + if wait_for 'Welcome to OVMX' 30 "$LOGIN_OFFSET"; then rc=0; else rc=1; OK=0; fi + record "SYSTEM login succeeds" "$rc" +fi + +# --- SHOW LOGICAL SYS$STARTUP: the measured multi-value search list -------- +if [ "$OK" -eq 1 ]; then + CMD_OFFSET=$(wc -c <"$CONSOLE_LOG") + send 'SHOW LOGICAL SYS$STARTUP' + if wait_for '$ ' 15 "$CMD_OFFSET"; then rc=0; else rc=1; fi + record "SHOW LOGICAL SYS\$STARTUP returns" "$rc" + + if [ "$rc" -eq 0 ]; then + SEG=$(tail -c "+$((CMD_OFFSET + 1))" "$CONSOLE_LOG" | tr -d '\r') + if printf '%s' "$SEG" | grep -qF 'SYS$STARTUP'; then r1=0; else r1=1; fi + record "SHOW LOGICAL SYS\$STARTUP names the logical" "$r1" + if printf '%s' "$SEG" | grep -qF 'SYS$MANAGER'; then r2=0; else r2=1; fi + record "SYS\$STARTUP search list carries SYS\$MANAGER as an element (vms-420 multi-value)" "$r2" + fi +fi + +# --- DIRECTORY SYS$STARTUP:*.DAT: the phase driver's data files ------------ +if [ "$OK" -eq 1 ]; then + CMD_OFFSET=$(wc -c <"$CONSOLE_LOG") + send 'DIRECTORY SYS$STARTUP:*.DAT' + if wait_for '$ ' 15 "$CMD_OFFSET"; then rc=0; else rc=1; fi + record "DIRECTORY SYS\$STARTUP:*.DAT returns" "$rc" + + if [ "$rc" -eq 0 ]; then + SEG=$(tail -c "+$((CMD_OFFSET + 1))" "$CONSOLE_LOG" | tr -d '\r') + if printf '%s' "$SEG" | grep -qF 'VMS$PHASES.DAT'; then r1=0; else r1=1; fi + record "SYS\$STARTUP: carries VMS\$PHASES.DAT on the booted disk" "$r1" + if printf '%s' "$SEG" | grep -qF 'VMS$VMS.DAT'; then r2=0; else r2=1; fi + record "SYS\$STARTUP: carries VMS\$VMS.DAT on the booted disk" "$r2" + fi +fi + +# --- DIRECTORY SYS$MANAGER:*.COM: the new site files ------------------------ +if [ "$OK" -eq 1 ]; then + CMD_OFFSET=$(wc -c <"$CONSOLE_LOG") + send 'DIRECTORY SYS$MANAGER:*.COM' + if wait_for '$ ' 15 "$CMD_OFFSET"; then rc=0; else rc=1; fi + record "DIRECTORY SYS\$MANAGER:*.COM returns" "$rc" + + if [ "$rc" -eq 0 ]; then + SEG=$(tail -c "+$((CMD_OFFSET + 1))" "$CONSOLE_LOG" | tr -d '\r') + if printf '%s' "$SEG" | grep -qF 'SYCONFIG.COM'; then r1=0; else r1=1; fi + record "SYS\$MANAGER: carries SYCONFIG.COM" "$r1" + if printf '%s' "$SEG" | grep -qF 'SYLOGICALS.COM'; then r2=0; else r2=1; fi + record "SYS\$MANAGER: carries SYLOGICALS.COM" "$r2" + fi +fi + +# --- DIRECTORY SYS$MANAGER:OVMX.CONF: must find nothing --------------------- +if [ "$OK" -eq 1 ]; then + CMD_OFFSET=$(wc -c <"$CONSOLE_LOG") + send 'DIRECTORY SYS$MANAGER:OVMX.CONF' + if wait_for '$ ' 15 "$CMD_OFFSET"; then rc=0; else rc=1; fi + record "DIRECTORY SYS\$MANAGER:OVMX.CONF returns" "$rc" + + if [ "$rc" -eq 0 ]; then + SEG=$(tail -c "+$((CMD_OFFSET + 1))" "$CONSOLE_LOG" | tr -d '\r') + if printf '%s' "$SEG" | grep -qF 'Total of 0 file'; then r1=0; else r1=1; fi + record "OVMX.CONF does not exist on the booted, mounted system disk" "$r1" + fi +fi + +cleanup +trap - EXIT +echo "" + +# --- Boot console: the phase driver actually ran, and did not fail an OPEN -- +# Checked against BOOT_LOG (captured above, before any interactive DIRECTORY +# command that legitimately expects a "0 files" no-match) so a later, +# intentional absence-check can never be mistaken for a boot-time failure. +echo "--- Boot console checks (from the same boot, pre-login) ---" +if printf '%s' "$BOOT_LOG" | grep -qF 'The OVMX system is now executing the site-specific startup commands.'; then + rc=0 +else + rc=1 +fi +record "LPMAIN-phase site announcement appears (SYSTARTUP_VMS.COM ran from inside the phase driver)" "$rc" + +if printf '%s' "$BOOT_LOG" | grep -qF '%RMS-E-FNF'; then rc=1; else rc=0; fi +record "no %RMS-E-FNF during boot (phase driver's OPENs all found their files)" "$rc" + +if printf '%s' "$BOOT_LOG" | grep -qF '%DCL-E-OPENIN'; then rc=1; else rc=0; fi +record "no %DCL-E-OPENIN during boot" "$rc" + +echo "" +echo "==========================================" +echo " RESULTS: $PASS/$TOTAL checks passed, $FAIL failed" +echo "==========================================" + +if [ "$FAIL" -ne 0 ]; then + echo "" + echo "--- Full console log ---" + cat "$CONSOLE_LOG" +fi + +[ "$FAIL" -eq 0 ]