Release Vulpes OS 2.7-preview.22
This commit is contained in:
parent
200d1fb235
commit
a138f97877
294 changed files with 515786 additions and 187 deletions
137
tundra/devices/beatles/gaia-session.py
Normal file
137
tundra/devices/beatles/gaia-session.py
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
os.umask(0o022)
|
||||
for name in ('gaia-audit.json', 'gaia-native.png', 'gecko.log', 'gaia-server.log', 'memory-samples.jsonl', 'keypad-request.json', 'keypad-response.json', 'manual-request.json', 'manual-response.json'):
|
||||
Path('/work',name).unlink(missing_ok=True)
|
||||
ROOT = Path('/opt/vulpes')
|
||||
PROFILE = Path('/work/gaia-profile')
|
||||
PROFILE.mkdir(exist_ok=True)
|
||||
os.chown(PROFILE,2000,2000)
|
||||
prefs = {
|
||||
'browser.shell.checkDefaultBrowser': False,
|
||||
'browser.aboutwelcome.enabled': False,
|
||||
'browser.startup.homepage_override.mstone': 'ignore',
|
||||
'browser.dom.window.dump.enabled': True,
|
||||
'datareporting.policy.dataSubmissionEnabled': False,
|
||||
'app.update.auto': False,
|
||||
'fission.autostart': False,
|
||||
'dom.ipc.processCount': 1,
|
||||
'dom.ipc.processPrelaunch.enabled': False,
|
||||
'gfx.webrender.software': True,
|
||||
'layers.acceleration.disabled': True,
|
||||
'layout.css.devPixelsPerPx': '1.0',
|
||||
'browser.cache.disk.enable': False,
|
||||
'browser.cache.memory.capacity': 8192,
|
||||
}
|
||||
(PROFILE/'user.js').write_text('\n'.join('user_pref(%s,%s);'%(json.dumps(k),json.dumps(v)) for k,v in prefs.items()))
|
||||
os.chown(PROFILE/'user.js',2000,2000)
|
||||
os.chmod(PROFILE/'user.js',0o600)
|
||||
saved = PROFILE/'vulpes-settings-v1.json'
|
||||
if not saved.exists():
|
||||
saved.write_text(json.dumps({'lockscreen.enabled':False,'vulpes.gaia-restoration.v1':True,'language.current':'fr'}))
|
||||
os.chown(saved,2000,2000)
|
||||
shutil.copy2(ROOT/'host/bootstrap/vulpes-autoconfig.js','/opt/firefox/defaults/pref/vulpes-autoconfig.js')
|
||||
config=(ROOT/'host/bootstrap/vulpes.cfg').read_text()
|
||||
config+="\nChromeUtils.importESModule('resource://vulpes/tests/BeatlesAudit.sys.mjs').install();\n"
|
||||
Path('/opt/firefox/vulpes.cfg').write_text(config)
|
||||
os.chmod('/opt/firefox/vulpes.cfg',0o644)
|
||||
for directory in ('logs','profiles'):
|
||||
(ROOT/directory).mkdir(exist_ok=True)
|
||||
os.chown(ROOT/directory,2000,2000)
|
||||
for path in (ROOT/'assets').iterdir():
|
||||
if path.is_file():os.chown(path,2000,2000)
|
||||
|
||||
|
||||
def unprivileged():
|
||||
Path('/proc/self/oom_score_adj').write_text('800')
|
||||
# Android's paranoid-network kernel requires inet even for private loopback.
|
||||
os.setgroups([3003])
|
||||
os.setgid(2000)
|
||||
os.setuid(2000)
|
||||
|
||||
|
||||
env=os.environ.copy()
|
||||
env.update(HOME='/work',TMPDIR='/tmp',DISPLAY=':0',MOZ_ENABLE_WAYLAND='0',
|
||||
VULPES_HOST_ROOT='/opt/firefox/vulpes/host',VULPES_WORKSPACE_ROOT='/opt/vulpes',
|
||||
VULPES_FULLSCREEN='1',VULPES_KEYPAD='1',VULPES_NATIVE_TOUCH='1',
|
||||
VULPES_DEVICE='beatles',VULPES_TUNDRA_STATUS='1',
|
||||
MOZ_CRASHREPORTER_DISABLE='1',FONTCONFIG_PATH='/etc/fonts')
|
||||
duration=1780 if os.environ.get("VULPES_TRIAL_MANUAL")=="1" else 160
|
||||
processes=[]
|
||||
handles=[]
|
||||
try:
|
||||
def start(command,log,drop=False):
|
||||
path=Path('/work/'+log)
|
||||
output=path.open('w');os.chmod(path,0o644);handles.append(output)
|
||||
proc=subprocess.Popen(command,env=env,stdout=output,stderr=subprocess.STDOUT,
|
||||
preexec_fn=unprivileged if drop else None)
|
||||
processes.append(proc)
|
||||
return proc
|
||||
xorg=start(['/usr/lib/xorg/Xorg',':0','vt1','-config','/etc/X11/xorg.conf',
|
||||
'-logfile','/work/Xorg.log','-nolisten','tcp','-ac','-noreset','-novtswitch','-sharevts','-nocursor'],'xorg-console.log')
|
||||
for _ in range(100):
|
||||
if xorg.poll() is not None:raise RuntimeError('Xorg failed')
|
||||
if Path('/tmp/.X11-unix/X0').exists():break
|
||||
time.sleep(.1)
|
||||
keypad=start(['/work/keypad'],'keypad.log')
|
||||
for _ in range(120):
|
||||
if keypad.poll() is not None:raise RuntimeError('Physical keypad bridge failed; inspect keypad.log')
|
||||
if 'Nokia keypad bridge ready' in Path('/work/keypad.log').read_text():break
|
||||
time.sleep(.1)
|
||||
else:raise RuntimeError('Physical keypad bridge startup timed out')
|
||||
cover=start(['/usr/bin/python3.11','/work/cover-display.py'],'cover-display.log')
|
||||
for _ in range(50):
|
||||
if cover.poll() is not None:raise RuntimeError('External display failed; inspect cover-display.log')
|
||||
if Path('/work/cover-ready').exists():break
|
||||
time.sleep(.1)
|
||||
else:raise RuntimeError('External display startup timed out')
|
||||
server=start(['/usr/bin/python3.11',str(ROOT/'tools/serve-gaia.py')],'gaia-server.log',True)
|
||||
import socket
|
||||
for _ in range(100):
|
||||
if server.poll() is not None:raise RuntimeError('Gaia server failed')
|
||||
try:
|
||||
with socket.create_connection(('127.0.0.1',8765),timeout=.2):break
|
||||
except OSError:time.sleep(.1)
|
||||
gecko=start(['/opt/firefox/firefox','-no-remote','-purgecaches','-profile',str(PROFILE),
|
||||
'-chrome','chrome://vulpes/content/shell.xhtml'],'gecko.log',True)
|
||||
begin=time.monotonic()
|
||||
last_key_sequence=0
|
||||
last_memory_sample=0
|
||||
with Path('/work/memory-samples.jsonl').open('w') as memory:
|
||||
while gecko.poll() is None and time.monotonic()-begin<duration:
|
||||
if cover.poll() is not None:raise RuntimeError('External display stopped')
|
||||
if keypad.poll() is not None:raise RuntimeError('Physical keypad bridge stopped')
|
||||
request=Path('/work/keypad-request.json')
|
||||
if request.exists():
|
||||
command=json.loads(request.read_text())
|
||||
sequence=command.get('sequence');processed=False
|
||||
if (isinstance(sequence,int) and last_key_sequence<sequence<=64 and
|
||||
command.get('key') in ('ArrowRight','ArrowLeft','ArrowUp','ArrowDown','Backspace','Enter','2','Escape','EndCall','Notifications','AudioVolumeUp','AudioVolumeDown')):
|
||||
subprocess.run(['/work/keypad','--inject-key',command['key']],env=env,check=True,timeout=5)
|
||||
last_key_sequence=sequence;processed=True
|
||||
elif (isinstance(sequence,int) and last_key_sequence<sequence<=64 and
|
||||
isinstance(command.get('keys'),str) and re.fullmatch(r'[0-9*#]{1,40}',command['keys'])):
|
||||
subprocess.run(['/work/keypad','--inject-keys',command['keys']],env=env,check=True,timeout=10)
|
||||
last_key_sequence=sequence;processed=True
|
||||
if processed:
|
||||
reply=Path('/work/keypad-response.tmp');reply.write_text(json.dumps({'sequence':sequence}));reply.replace('/work/keypad-response.json')
|
||||
sample={'seconds':round(time.monotonic()-begin,1),'meminfo':Path('/proc/meminfo').read_text()}
|
||||
if time.monotonic()-last_memory_sample>=2:
|
||||
memory.write(json.dumps(sample)+'\n');memory.flush();last_memory_sample=time.monotonic()
|
||||
time.sleep(.1)
|
||||
print('Gecko exit:',gecko.poll(),flush=True)
|
||||
finally:
|
||||
for proc in reversed(processes):
|
||||
if proc.poll() is None:proc.terminate()
|
||||
for proc in reversed(processes):
|
||||
try:proc.wait(timeout=3)
|
||||
except subprocess.TimeoutExpired:proc.kill();proc.wait()
|
||||
for handle in handles:handle.close()
|
||||
Loading…
Reference in a new issue