Add bounded aircraft control and verify current packaged runtime closure

This commit is contained in:
2026-09-19 13:31:51 +02:00
parent 4125018d04
commit a880e0485c
695 changed files with 79317 additions and 44 deletions
+72 -9
View File
@@ -5,6 +5,8 @@ import fcntl
import hashlib
import json
import os
import re
import stat
from pathlib import Path
import shutil
import signal
@@ -46,17 +48,68 @@ def sandbox(runtime, source, run):
'--chdir', '/tmp/runtime']
def engine_args(route):
PROJECTS = {
'core': ('2528466DE220D91C', '{AD62322D5213C440}Missions/ERR_Everon_Base.conf'),
'chat': ('FA804F411BDDF2C6', '{C1E20667DCE324ED}Missions/ERR_Everon.conf'),
}
ADDON_GUIDS = {
'core': PROJECTS['core'][0],
'ai': '5192138CB9D5B911',
'intel': 'D31039C7C53E348A',
'map': '9AAEE9041D79809E',
'chat': PROJECTS['chat'][0],
}
WORLD = '{B6C516F86302C679}Worlds/ERR_Everon.ent'
SYSTEMS = '{C50579A2EC48E8EF}Configs/Systems/MissionSystems.conf'
def engine_args(route, project='core'):
if route not in ('header', 'diag-world', 'vanilla-everon') or project not in PROJECTS:
raise ValueError('Unsupported diagnostic route or project')
if route == 'vanilla-everon' and project != 'core':
raise ValueError('vanilla-everon cannot be combined with --project chat')
args = ['/tmp/runtime/ArmaReforgerServer' + ('Diag' if route == 'diag-world' else ''),
'-profile', '/tmp/work/profile', '-noBackend', '-noSound', '-noSplash', '-noThrow',
'-bindIP', '127.0.0.1', '-bindPort', '2001', '-maxFPS', '30', '-logStats', '5000']
if route == 'vanilla-everon':
return args + ['-server', 'Missions/21_GM_Eden.conf']
args += ['-addonsDir', '/tmp/source/addons', '-addons', '2528466DE220D91C']
addon, header = PROJECTS[project]
args += ['-addonsDir', '/tmp/source/addons', '-addons', addon]
if route == 'header':
return args + ['-server', '{AD62322D5213C440}Missions/ERR_Everon_Base.conf']
return args + ['-server', '{B6C516F86302C679}Worlds/ERR_Everon.ent',
'-worldSystemsConfig', '{C50579A2EC48E8EF}Configs/Systems/MissionSystems.conf']
return args + ['-server', header]
return args + ['-server', WORLD, '-worldSystemsConfig', SYSTEMS]
def preflight_addons(source, route, project='core'):
"""Presence/identity checks only; native loading must validate RDB contents."""
engine_args(route, project) # also reject incompatible direct helper calls
if route == 'vanilla-everon':
return
addons = source / 'addons'
if addons.is_symlink() or not addons.is_dir():
raise ValueError('Expected a regular addons directory in diagnostic source')
closure = ('core',) if project == 'core' else tuple(ADDON_GUIDS)
for addon in closure:
folder = addons / addon
if folder.is_symlink() or not folder.is_dir():
raise ValueError('Missing or linked required addon directory: ' + addon)
for name in ('addon.gproj', 'resourceDatabase.rdb'):
path = folder / name
try:
info = path.lstat()
except FileNotFoundError as error:
raise ValueError('Missing required addon input: ' + str(path)) from error
if not stat.S_ISREG(info.st_mode) or info.st_size <= 0:
raise ValueError('Required addon input must be a nonempty regular file: ' + str(path))
project_file = folder / 'addon.gproj'
if project_file.stat().st_size > 65536:
raise ValueError('Unsupported oversized addon project: ' + str(project_file))
try:
definition = project_file.read_text(encoding='utf-8')
except UnicodeDecodeError as error:
raise ValueError('Addon project is not UTF-8: ' + str(project_file)) from error
if re.findall(r'\bGUID\s+"([A-Fa-f0-9]{16})"', definition) != [ADDON_GUIDS[addon]]:
raise ValueError('Required addon identity mismatch: ' + str(project_file))
def main():
@@ -65,8 +118,12 @@ def main():
parser.add_argument('--runtime', type=Path, default=Path('/tmp/erri-dedicated-runtime/server'))
parser.add_argument('--archive', type=Path, required=True)
parser.add_argument('--route', choices=['header', 'diag-world', 'vanilla-everon'], default='header')
parser.add_argument('--project', choices=['core', 'chat'], default='core',
help='Core base (default) or Chat with all five declared successor addons')
parser.add_argument('--seconds', type=int, default=90)
args = parser.parse_args()
if args.route == 'vanilla-everon' and args.project != 'core':
parser.error('vanilla-everon cannot be combined with --project chat')
if not 1 <= args.seconds <= 110:
parser.error('Runtime bound must be 1..110 seconds (plus at most 10 seconds owned cleanup)')
source, runtime, archive = args.source.resolve(), args.runtime.resolve(), args.archive.resolve()
@@ -76,10 +133,14 @@ def main():
parser.error('Source, archive and runtime must not overlap')
if archive.exists() or archive.is_symlink():
parser.error('Archive must be new')
try:
preflight_addons(source, args.route, args.project)
except (ValueError, OSError) as error:
parser.error(str(error))
origin = runtime.parent / 'runtime-origin.json'
if not origin.is_file() or json.loads(origin.read_text()).get('app_id') != 1874900:
parser.error('Stable private runtime origin missing')
binary = runtime / Path(engine_args(args.route)[0]).name
binary = runtime / Path(engine_args(args.route, args.project)[0]).name
with binary.open('rb') as stream:
elf = stream.read(20)
if binary.is_symlink() or len(elf) != 20 or elf[:6] != b'\x7fELF\x02\x01' or int.from_bytes(elf[18:20], 'little') != 62:
@@ -92,6 +153,7 @@ def main():
manifest(source)
frozen = archive / 'source'
shutil.copytree(source, frozen, ignore=shutil.ignore_patterns('.git', '__pycache__', 'evidence', 'generated'))
preflight_addons(frozen, args.route, args.project)
initial = manifest(frozen)
(archive / 'source-manifest.json').write_text(json.dumps(initial, indent=2)+'\n')
base = sandbox(runtime, frozen, archive)
@@ -102,11 +164,12 @@ def main():
if preflight.returncode:
print('Isolation refused; no server launched:', archive)
return 2
command = base + ['/usr/bin/python3', '-c', PROBE, netns] + engine_args(args.route)
record = {'command': command, 'engine': engine_args(args.route), 'seconds':args.seconds,
command = base + ['/usr/bin/python3', '-c', PROBE, netns] + engine_args(args.route, args.project)
record = {'command': command, 'engine': engine_args(args.route, args.project), 'seconds':args.seconds,
'route': args.route, 'project': None if args.route == 'vanilla-everon' else args.project,
'binarySHA256':hashlib.sha256(binary.read_bytes()).hexdigest(),
'serverLaunched':False,'realPlayers':0,'multiplayerProven':False,
'scope':'vanilla Everon control; no successor loaded' if args.route == 'vanilla-everon' else 'successor empty-server startup'}
'scope':'vanilla Everon control; no successor loaded' if args.route == 'vanilla-everon' else f'successor {args.project} empty-server startup'}
(archive / 'command.json').write_text(json.dumps(record, indent=2)+'\n')
lock_fd = os.open('/tmp/erri-dedicated-native-diagnostic.lock', os.O_WRONLY | os.O_CREAT | os.O_NOFOLLOW, 0o600)
with os.fdopen(lock_fd, 'a') as lock: