Files
StarPilot/jetlink_repo/jetlink/comma/root.py
T
Zeph Leggett de40ad2ff1 Jetlink d24328d
Vendor only the comma payload jetlink commits to (jetlink/, LICENSE,
pyproject.toml, README.md) with an include list, so the exclude list goes
and upstream's README stays as published; StarPilot's notes live in
JETLINK.md. The root helper now ships inside the package.

The adapter moves to API 3 in the next commit; until then Jetlink stays
off.

d24328d stands in for the jetlink release tag commit: re-run
tools/vendor/sync.py jetlink_repo <tag commit> --apply before merging and
update the revision in CREDITS.md and JETLINK.md with it.
2026-10-18 16:18:37 -05:00

64 lines
2.3 KiB
Python

"""
Copyright (c) 2026-, Zeph Leggett.
This file is part of jetlink and is licensed under the MIT License.
See the LICENSE file in the root directory for more details.
Root on the comma: jetlink/comma/jetlink-root.sh under sudo -n, and nothing
else. The script leaves its own record of the gadget and its network in
/dev/shm (jetlink-gadget, jetlink-net), so a False here only says the call
failed; the reason is in the log line and, for the gadget, in those files.
root.run('gadget', '--ios')
root.run('port', 'hold', timeout=root.PORT_TIMEOUT)
root.run('vm', 'apply')
Only AGNOS has the gadget stack, the port's lever and the sysctls, so
everywhere else run() is a quiet False and nothing is spawned.
"""
from __future__ import annotations
import logging
import os
import subprocess
from pathlib import Path
log = logging.getLogger('jetlink.comma')
AGNOS = os.path.isfile('/AGNOS')
# Next to this file, so the package alone is all the comma runs. A path
# through the <openpilot>/jetlink symlink is fine: bash only opens it.
SCRIPT = Path(__file__).with_name('jetlink-root.sh')
# sudo, configfs, and for iOS nmcli and dnsmasq
TIMEOUT = 30.0
# sudo and two echos take tens of ms. Short, because the owner lets the port go
# before it closes FunctionFS, inside manager's 5 s
PORT_TIMEOUT = 2.0
# port source: the kernel waits up to 2 s for the far end's PR_Swap
SWAP_TIMEOUT = 4.0
def run(*args: str, timeout: float = TIMEOUT) -> bool:
"""sudo -n bash SCRIPT *args, on AGNOS. True when it exits 0; otherwise one
log line, with the last thing the script wrote to stderr, and False. Off
AGNOS a False with no log line. Never raises."""
if not AGNOS:
return False
what = ' '.join((SCRIPT.name, *args))
try:
result = subprocess.run(['sudo', '-n', 'bash', str(SCRIPT), *args], stdout=subprocess.DEVNULL,
stderr=subprocess.PIPE, timeout=timeout, text=True, errors='replace')
except subprocess.TimeoutExpired:
log.error("jetlink: %s timed out after %.0f s", what, timeout)
return False
except Exception as e:
log.error("jetlink: %s did not run: %s", what, e)
return False
if result.returncode == 0:
return True
lines = (result.stderr or '').strip().splitlines()
log.error("jetlink: %s failed (exit %d): %s", what, result.returncode, lines[-1] if lines else 'no output')
return False