Files
2026-10-17 21:57:25 +08:00

107 lines
4.4 KiB
Plaintext

from stdint import *
import stdlib
import string
import sys
import memhub
import win32.win32console as win32cmd
import lib.VLog as VLog
import lib.VFs as VFs
import lib.VDirs as VDirs
import lib.VGuid as VGuid
import lib.VCache as VCache
import lib.VJson as VJson
import lib.VProc as VProc
import lib.VPe as VPe
import lib.VScan as VScan
import lib.VCheck as VCheck
import lib.VGuard as VGuard
import lib.VCli as VCli
# ============================================================
# VET - Viper Environment Tool (entry point)
#
# Modes (see README): ver = single-exe check (dir table + .PRVERSN section + `--version` prefix);
# scan = resident disk scanner (interval via --sleep, default 10s; stdout is JSON only);
# confirm-watch = hidden heartbeat watchdog subcommand, auto-spawned by scan + --need-confirm;
# it only does sleep then waits on stdin for {"ok": 1}, aborting the parent on failure.
#
# This file only: console code page -> Log init -> main pool -> inject each module's `_mbuddy`
# -> init argv -> dispatch subcommand. All implementations live in lib/.
# ============================================================
define CODE_PAGE = 65001
define POOL_SIZE = 16777216 # MemBuddy main pool 16 MiB
export def main() -> int:
win32cmd.SetConsoleOutputCP(CODE_PAGE)
win32cmd.SetConsoleCP(CODE_PAGE)
VLog.LogInit()
# MemBuddy pool: sys._init_argv and all module allocations depend on it
arena: bytes = stdlib.malloc(POOL_SIZE)
if arena is None:
VLog.LogStr("[VET] FATAL: memory pool allocation failed\n")
return 1
mb: memhub.MemBuddy* = memhub.MemBuddy(arena, POOL_SIZE)
if mb is None:
VLog.LogStr("[VET] FATAL: MemBuddy initialization failed\n")
return 1
# Inject the same pool into every module that declares _mbuddy; missing one makes its
# _mbuddy point at zeroed static storage, so all allocs return None and it silently degrades.
VFs._mbuddy = mb
VDirs._mbuddy = mb
VGuid._mbuddy = mb
VCache._mbuddy = mb
VJson._mbuddy = mb
VScan._mbuddy = mb
VCheck._mbuddy = mb
VGuard._mbuddy = mb
sys._mbuddy = mb
sys._init_argv()
if sys._argc < 2 or sys._argv[1] is None:
VCli.PrintUsage()
return 1
Cmd: str = sys._argv[1]
if string.strcmp(Cmd, "ver") == 0:
# ver does not parse flags (target fixed at _argv[2]): --verbose in argv enables detail logs
if VCli.HasVerbose() != 0:
VLog.SetVerbose(1)
return VPe.RunVer()
# `--search-paths` may omit `scan` and sit in the subcommand slot; it is a pure query
# (no scan, no cache IO) with identical semantics. Set here, merged later by ParseScanArgs.
if string.strcmp(Cmd, "--search-paths") == 0:
VCli.SetSearchPathsImplicit()
if string.strcmp(Cmd, "scan") == 0 or string.strcmp(Cmd, "--search-paths") == 0:
# Args are parsed here because the watchdog lifecycle spans all of RunScan, so
# --need-confirm/--need-confirm-timeout must be known before entering it.
if VCli.ParseScanArgs() != 0:
return 1
# --verbose takes effect right after parsing, before watchdog/summary logs, so all
# later VLog.Verbose* calls gate on it (default off = quiet).
VLog.SetVerbose(VCli.Verbose())
# --search-paths: build the searched dir table, print it line by line to stderr, exit.
# Placed before SpawnWatchdog since it needs no scan/cache and must not start the watchdog.
if VCli.SearchPaths() != 0:
return VScan.RunSearchPathsQuery()
# --need-confirm > 0: spawn a separate watchdog subprocess (decoupled from scanning).
# Failure is fatal: an explicit heartbeat request must not silently run unconfirmed.
if VCli.NeedConfirm() > 0:
if VGuard.SpawnWatchdog(VCli.NeedConfirm(), VCli.NeedConfirmTimeout()) == 0:
VLog.LogStr("Error: heartbeat confirm watchdog launch failed, scan aborted\n")
return 1
RcScan: int = VScan.RunScan()
VGuard.StopWatchdog() # Always reap the watchdog on normal exit (incl. --once) to avoid orphans
return RcScan
# Hidden internal subcommand: heartbeat watchdog (not in usage; spawned by the scan branch)
if string.strcmp(Cmd, "confirm-watch") == 0:
return VGuard.RunConfirmWatch()
VLog.LogFmtS1("Error: unknown subcommand %s\n", Cmd)
VCli.PrintUsage()
return 1