VulpesOS/tundra/tools/inspect-native.py
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

70 lines
4 KiB
Python

#!/usr/bin/env python3
# SPDX-License-Identifier: MPL-2.0
"""Inspect a running native RAM trial. Execute inside its main root as root."""
import json
import os
from pathlib import Path
import subprocess
import time
def inspect():
if 'tundra.rescue=1' not in Path('/proc/cmdline').read_text():
raise RuntimeError('This inspector is for a native RAM trial')
result={'recordedAtUnix':time.time(),'uptime':Path('/proc/uptime').read_text().strip()}
commands={
'failedUnits':['systemctl','--failed','--no-legend','--no-pager'],
'activeUnits':['systemctl','is-active','tundra-hal','tundra-display','systemd-logind','user@32011'],
'displayRestarts':['systemctl','show','tundra-display','-p','NRestarts','--value'],
'kernel':['uname','-r'],
'halPid':['lxc-info','-n','tundra-hal','-pH'],
}
for name,command in commands.items():
process=subprocess.run(command,capture_output=True,text=True,timeout=10)
result[name]={'code':process.returncode,'stdout':process.stdout,'stderr':process.stderr}
kernel = subprocess.run(['dmesg'], capture_output=True, text=True, timeout=10)
result['kernelFaults'] = [line for line in kernel.stdout.splitlines()
if any(marker in line for marker in
('Internal error: Oops', 'Kernel panic', 'Kernel BUG', 'BUG:'))]
result['memoryKills'] = [line for line in kernel.stdout.splitlines()
if any(marker in line for marker in
('lowmemorykiller: Killing', 'Out of memory: Kill', 'oom-kill:', 'Killed process'))]
result['kernelLogReadable'] = kernel.returncode == 0
result['pid1']=Path('/proc/1/cmdline').read_text().replace('\0',' ')
result['identity']=Path('/etc/os-release').read_text()
result['mounts']=Path('/proc/1/mounts').read_text()
result['memory']=Path('/proc/meminfo').read_text()
pid=result['halPid']['stdout'].strip()
result['halCgroups']=Path('/proc',pid,'cgroup').read_text() if pid.isdigit() else None
result['halMemoryLimits']={str(p):p.read_text().strip() for p in Path('/sys/fs/cgroup').rglob('memory.max') if 'lxc.payload.tundra-hal' in str(p)}
result['gaia']=json.loads(Path('/opt/vulpes/logs/test.json').read_text())
processes=[]
for path in Path('/proc').glob('[0-9]*/cmdline'):
try:
command=path.read_text().replace('\0',' ').strip()
if not command:continue
status=dict(line.split(':',1) for line in (path.parent/'status').read_text().splitlines() if ':' in line)
processes.append({'pid':int(path.parent.name),'command':command,
'uid':status.get('Uid','').strip(),'seccomp':status.get('Seccomp','').strip(),
'capabilities':status.get('CapEff','').strip()})
except (OSError,ProcessLookupError):continue
result['processes']=processes
result['checks']={
'noKernelFaults':result['kernelLogReadable'] and not result['kernelFaults'],
'noMemoryKills':result['kernelLogReadable'] and not result['memoryKills'],
'tundraPid1':'--unit=tundra.target' in result['pid1'],
'noFailedUnits':result['failedUnits']['code']==0 and not result['failedUnits']['stdout'].strip(),
'servicesActive':result['activeUnits']['code']==0 and result['activeUnits']['stdout'].split()==['active']*4,
'noDisplayRestart':result['displayRestarts']['stdout'].strip()=='0',
'gaia':result['gaia']['passed'],
'halLegacyDeviceGuard':bool(result['halCgroups'] and ':devices:/hal' in result['halCgroups']),
'halMemoryBounded':'536870912' in result['halMemoryLimits'].values(),
'noAndroidAppRuntime':not any(p['command'].split()[0].rsplit('/',1)[-1] in ('zygote','zygote64','app_process','app_process64','phosh') for p in processes),
}
return result
if __name__=='__main__':
result=inspect()
print(json.dumps(result,indent=2))
raise SystemExit(0 if all(result['checks'].values()) else 1)