#! /usr/bin/python3
# SPDX-License-Identifier: LicenseRef-CMSD-1.0
# Copyright (c) 2026 CyberMind — Gérald Kerma <devel@cybermind.fr>
# Source-Disclosed License — All rights reserved except as expressly granted.
# See LICENCE-CMSD-1.0.md for terms.
"""
secubox-lxc-sans-sandbox <conteneur> <service>… — remet en marche un service systemd d'un conteneur sans privilèges.

POURQUOI : depuis le redémarrage du 2026-10-05, AppArmor (`lxc-container-default-cgns`) refuse les montages dont le bac à sable
systemd a besoin (PrivateTmp, ProtectSystem, PrivateDevices…) : le service échoue en `226/NAMESPACE` avant même de démarrer
(redis de PeerTube et du courrier, apache2 du webmail…). Le conteneur est déjà l'isolation : on retire le bac à sable du service
par une surcharge `10-lxc-sans-sandbox.conf` DANS le conteneur, puis on le démarre. Rien n'est touché sur l'hôte.

Usage : secubox-lxc-sans-sandbox <conteneur> <service> [<service>…]
Sortie : 0 si chaque service est actif à la fin ; 1 sinon ; 2 sur argument douteux (rien n'est alors exécuté).
"""
import re
import subprocess
import sys

_SERVICE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9@._-]{0,63}\Z")
_CONTENEUR = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,63}\Z")

SURCHARGE = """[Service]
PrivateDevices=false
PrivateTmp=false
PrivateUsers=false
ProtectControlGroups=false
ProtectKernelTunables=false
ProtectKernelModules=false
ProtectSystem=false
ProtectHome=false
RestrictNamespaces=false
ProtectClock=false
ProtectHostname=false
ProtectKernelLogs=false
ProtectProc=default
ProcSubset=all
ReadWritePaths=
ReadWriteDirectories=
ReadOnlyPaths=
InaccessiblePaths=
ExecPaths=
NoExecPaths=
TemporaryFileSystem=
BindPaths=
BindReadOnlyPaths=
"""


def surcharge():
    return SURCHARGE


def nom_valide(nom):
    return bool(isinstance(nom, str) and (_SERVICE.match(nom) or _CONTENEUR.match(nom)) and ".." not in nom)


def attacher(conteneur, argv, entree=None):
    """Exécute `argv` dans le conteneur (lxc-attach), la sortie standard éventuelle étant `entree`. Retourne le code."""
    p = subprocess.run(["lxc-attach", "-n", conteneur, "--", *argv], input=entree, text=True,
                       stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    return p.returncode


def reparer(conteneur, services, executer=attacher):
    if not _CONTENEUR.match(conteneur or "") or not services or not all(_SERVICE.match(s or "") for s in services):
        return 2
    for s in services:
        dossier = f"/etc/systemd/system/{s}.service.d"
        executer(conteneur, ["mkdir", "-p", dossier])
        executer(conteneur, ["sh", "-c", f"cat > {dossier}/10-lxc-sans-sandbox.conf"], SURCHARGE)
    executer(conteneur, ["systemctl", "daemon-reload"])
    mauvais = 0
    for s in services:
        executer(conteneur, ["systemctl", "reset-failed", s])
        executer(conteneur, ["systemctl", "enable", "--now", s])
        if executer(conteneur, ["systemctl", "is-active", "--quiet", s]) != 0:
            print(f"{conteneur}: {s} n'a pas démarré", file=sys.stderr)
            mauvais += 1
        else:
            print(f"{conteneur}: {s} actif")
    return 1 if mauvais else 0


def main(argv):
    if len(argv) < 3:
        print(__doc__.strip().splitlines()[1], file=sys.stderr)
        return 2
    return reparer(argv[1], argv[2:])


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