#!/usr/bin/python3
"""Install hardware updates. Runs as root, reached only through polkit.

This is the privileged half, kept as small as it can be. It takes a list of
things to install, checks each one against what the detector actually found on
this machine, and refuses anything else. The caller is an unprivileged settings
page: it may ask for the wrong thing, and it must not be able to turn "install
my graphics driver" into "install any package on the system".
"""
from __future__ import annotations

import json
import os
import re
import subprocess
import sys

DETECTOR = "/usr/bin/motel-hardware-updates"

# Package names only. Anything with a shell character, an option-looking
# leading dash, or a path separator is rejected before apt ever sees it.
SAFE_PACKAGE = re.compile(r"^[a-z0-9][a-z0-9.+-]*$")


def fail(message: str) -> int:
    print(f"error: {message}", file=sys.stderr)
    return 1


def available() -> dict:
    """Ask the detector what is genuinely on offer for this machine."""
    result = subprocess.run(
        [DETECTOR, "--json"], capture_output=True, text=True,
        timeout=120, check=False,
    )
    if result.returncode != 0:
        return {"updates": []}
    try:
        return json.loads(result.stdout)
    except ValueError:
        return {"updates": []}


def main(argv: list[str]) -> int:
    if os.geteuid() != 0:
        return fail("this helper must run as root, through pkexec")

    requested = set(argv[1:])
    if not requested:
        return fail("nothing requested")

    offered = available()["updates"]
    offered_ids = {item["id"] for item in offered}

    unknown = requested - offered_ids
    if unknown:
        # Not a warning. If the caller asked for something this machine was
        # never offered, the request is wrong or hostile, and neither is a
        # reason to install it.
        return fail(f"not offered for this machine: {', '.join(sorted(unknown))}")

    packages: list[str] = []
    firmware_wanted = False

    for item in offered:
        if item["id"] not in requested:
            continue
        if item["kind"] == "firmware":
            firmware_wanted = True
            continue
        for package in item["packages"]:
            if not SAFE_PACKAGE.match(package):
                return fail(f"refusing suspicious package name: {package!r}")
            packages.append(package)

    status = 0

    if packages:
        print(f"Installing: {' '.join(packages)}", flush=True)
        env = dict(os.environ, DEBIAN_FRONTEND="noninteractive")
        # "--" so a package name can never be read as an option, even though
        # the regular expression above already forbids a leading dash.
        result = subprocess.run(
            ["apt-get", "install", "-y", "--no-install-recommends", "--"] + packages,
            env=env, check=False,
        )
        status |= result.returncode

    if firmware_wanted:
        print("Installing firmware updates", flush=True)
        # --no-reboot-check: the settings page tells the user a restart is
        # needed; fwupd prompting for one mid-install has nowhere to prompt.
        result = subprocess.run(
            ["fwupdmgr", "update", "-y", "--no-reboot-check"], check=False,
        )
        # 2 means "nothing to do", which is not a failure.
        if result.returncode not in (0, 2):
            status |= result.returncode

    return status


if __name__ == "__main__":
    sys.exit(main(sys.argv))
