Kernel Panic decoderen vanuit een QR-code foto

Gebruik

Het script kan als volgt worden uitgevoerd:

./decode-kernel-panic.py screenshot.png -o panic.log

De Code

#!/usr/bin/env python3
"""
Scan een Linux panic QR-code met ZXingReader en decomprimeer het crashlogboek.
./decode-kernel-panic.py screenshot.png -o panic.log
"""
import argparse
import json
from pathlib import Path
import subprocess
import sys
from urllib.parse import parse_qs, urlsplit
import zlib

def decode_panic_url(url):
    """Decodeer de z-parameter van de kernel: 17 decimale cijfers per 7 little-endian bytes.
    Format referentie: https://github.com/kdj0c/panic_report
    """
    parts = urlsplit(url)
    parameters = parse_qs(parts.query, keep_blank_values=True)
    if "z" not in parameters:
        parameters = parse_qs(parts.fragment.lstrip("?"), keep_blank_values=True)
    if "z" not in parameters:
        raise ValueError("QR-code URL bevat geen gecomprimeerde panic-data (z-parameter)")
    if len(parameters["z"]) != 1:
        raise ValueError("QR-code URL bevat meerdere z-parameters")

    digits = parameters["z"][0]
    if not digits or not digits.isascii() or not digits.isdecimal():
        raise ValueError("panic-data moet uitsluitend decimale cijfers bevatten")

    # Een laatste, gedeeltelijke groep gebruikt minder cijfers en bytes.
    byte_counts = {3: 1, 5: 2, 8: 3, 10: 4, 13: 5, 15: 6, 17: 7}
    compressed = bytearray()
    for offset in range(0, len(digits), 17):
        chunk = digits[offset:offset + 17]
        if len(chunk) not in byte_counts:
            raise ValueError("ongeldige lengte van gecodeerde panic-data")
        try:
            compressed.extend(int(chunk).to_bytes(byte_counts[len(chunk)], "little"))
        except OverflowError as exc:
            raise ValueError("gecodeerde panic-data bevat een chunk buiten bereik") from exc
    return zlib.decompress(compressed)

def scan_image(image):
    if not image.is_file():
        raise ValueError(f"afbeelding bestaat niet of is geen bestand: {image}")
    try:
        result = subprocess.run(
            ["ZXingReader", "-formats", "QRCode", "-json", str(image.resolve())],
            capture_output=True,
            text=True,
        )
    except FileNotFoundError as exc:
        raise ValueError("ZXingReader is vereist; installeer dit en voeg het toe aan PATH") from exc

    if result.returncode != 0:
        detail = result.stderr.strip() or "geen leesbare QR-code gevonden"
        raise ValueError(f"ZXingReader is mislukt: {detail}")

    if not result.stdout.strip():
        raise ValueError("geen leesbare QR-code gevonden")

    try:
        qr = json.loads(result.stdout)
    except json.JSONDecodeError as exc:
        raise ValueError("verwacht één QR-code in de JSON-output van ZXingReader") from exc

    if not isinstance(qr, dict) or not isinstance(qr.get("Text"), str):
        raise ValueError("ZXingReader retourneerde geen QR-code tekst")
    return qr["Text"]

def main():
    parser = argparse.ArgumentParser(
        description="Scan een Linux panic screenshot met ZXingReader en decomprimeer het logboek.",
        epilog="Vereist Python 3 en ZXingReader. Het decoderen vindt lokaal plaats zonder netwerktoegang.",
    )
    parser.add_argument("image", type=Path, help="screenshot of foto met de panic QR-code")
    parser.add_argument("-o", "--output", type=Path, help="schrijf het logboek naar een bestand in plaats van stdout")
    args = parser.parse_args()

    try:
        if args.output and args.output.resolve() == args.image.resolve():
            raise ValueError("output moet verschillen van de invoer-afbeelding")

        log = decode_panic_url(scan_image(args.image))
        if args.output:
            args.output.write_bytes(log)
        else:
            sys.stdout.buffer.write(log)
    except BrokenPipeError:
        # Sta commando's toe zoals `decode-kernel-panic screenshot.png | head`.
        import os
        os.dup2(os.open(os.devnull, os.O_WRONLY), sys.stdout.fileno())
        return 0
    except (OSError, ValueError, zlib.error) as exc:
        print(f"{parser.prog}: {exc}", file=sys.stderr)
        return 1
    return 0

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

Vereisten en Details

  • Softwarevereisten: Python 3 en ZXingReader.
  • Privacy: Het decoderen wordt lokaal uitgevoerd; er is geen netwerktoegang nodig.
  • Functionaliteit:
  • Het script extraheert de tekst uit een QR-code in een afbeelding.
  • Het decodeert de z-parameter uit de URL, waarbij 17 decimale cijfers worden omgezet naar 7 little-endian bytes.
  • De data wordt vervolgens gedecomprimeerd met behulp van zlib.