Files

50 lines
1.8 KiB
Python

"""Shared plumbing for the root workers that unprivileged daemons lease over a pipe.
The daemon starts the worker with ``spawn``. EOF on the worker's stdin, a missed heartbeat or SIGTERM ends the
lease, and the worker then removes only what it created (interfaces it marked with its own alias).
"""
from __future__ import annotations
import fcntl
import os
import signal
import subprocess
import sys
import threading
from pathlib import Path
def spawn(unit: str, module: str, popen=subprocess.Popen):
from openpilot.common.basedir import BASEDIR
return popen(['sudo', '-n', 'systemd-run', '--quiet', '--pipe', '--wait', '--collect', f'--unit={unit}',
'--property=TimeoutStopSec=8', '--property=KillMode=control-group', '--working-directory=' + str(BASEDIR),
sys.executable, '-u', '-m', module], cwd=BASEDIR,
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, close_fds=True)
def owned(interface: str, alias: str) -> bool:
try:
return Path(f'/sys/class/net/{interface}/ifalias').read_text().strip() == alias
except FileNotFoundError:
return False
def claim(runtime: Path, name: str) -> int:
"""Root check, a private runtime directory and its lock; returns the lock's file descriptor."""
if os.geteuid() != 0:
raise RuntimeError(f'{name} requires network administration privileges')
os.umask(0o077)
runtime.mkdir(mode=0o700, exist_ok=True)
if runtime.is_symlink() or runtime.stat().st_uid != 0:
raise RuntimeError(f'Unsafe {name} runtime directory')
fd = os.open(runtime / 'lock', os.O_CREAT | os.O_RDWR | os.O_NOFOLLOW, 0o600)
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
return fd
def stop_event() -> threading.Event:
stop = threading.Event()
signal.signal(signal.SIGTERM, lambda *_: stop.set())
signal.signal(signal.SIGINT, lambda *_: stop.set())
return stop