#!/usr/bin/env bash
set -euo pipefail
CONTAINER_NAME="pihole"
UPSTREAM_URL="https://raw.githubusercontent.com/pimanDE/translate2german/master/translate2german.sh"
WORKDIR=""
KEEP_FILES=0
# -------------------------------------------------------------------------------------------------
# ab hier nichts mehr ändern
# -------------------------------------------------------------------------------------------------
usage() {
cat <<'EOF'
Usage:
run_translate2german_docker.sh [options]
Lädt das aktuelle translate2german-Upstream-Skript, konvertiert rpl->sed
und führt die konvertierte Version direkt im Pi-hole Docker-Container aus.
Optionen:
--container NAME Docker-Containername (Default: pihole)
--upstream-url URL Raw-URL des Upstream-Skripts
--workdir DIR Arbeitsverzeichnis (Default: temporär)
--keep-files Temporäre Dateien behalten
-h, --help Hilfe anzeigen
Beispiele:
./run_translate2german_docker.sh
./run_translate2german_docker.sh --container pihole6
./run_translate2german_docker.sh --container pihole \
--upstream-url https://raw.githubusercontent.com/pimanDE/translate2german/master/translate2german.sh
EOF
}
die() {
echo "ERROR: $*" >&2
exit 1
}
require_cmd() {
command -v "$1" >/dev/null 2>&1 || die "Befehl fehlt: $1"
}
check_docker_access() {
# root kann typischerweise immer auf den Docker-Daemon zugreifen.
if [[ "${EUID:-$(id -u)}" -eq 0 ]]; then
return 0
fi
local sock="/var/run/docker.sock"
if [[ -S "$sock" && -r "$sock" && -w "$sock" ]]; then
return 0
fi
# Finale Laufzeitprüfung gegen den Daemon, um Gruppen- und Socket-Setups abzudecken.
if docker info >/dev/null 2>&1; then
return 0
fi
die "Kein Docker-Zugriff für Benutzer $(id -un). Bitte als root ausführen oder Benutzer zur Docker-Gruppe hinzufügen."
}
while [[ $# -gt 0 ]]; do
case "$1" in
--container)
[[ $# -ge 2 ]] || die "--container benötigt einen Wert"
CONTAINER_NAME="$2"
shift 2
;;
--upstream-url)
[[ $# -ge 2 ]] || die "--upstream-url benötigt einen Wert"
UPSTREAM_URL="$2"
shift 2
;;
--workdir)
[[ $# -ge 2 ]] || die "--workdir benötigt einen Wert"
WORKDIR="$2"
shift 2
;;
--keep-files)
KEEP_FILES=1
shift
;;
-h|--help)
usage
exit 0
;;
*)
die "Unbekannte Option: $1 (siehe --help)"
;;
esac
done
require_cmd docker
require_cmd curl
require_cmd python3
require_cmd mktemp
check_docker_access
if [[ -z "$WORKDIR" ]]; then
WORKDIR="$(mktemp -d)"
CLEANUP_WORKDIR=1
else
mkdir -p "$WORKDIR"
CLEANUP_WORKDIR=0
fi
if [[ "$KEEP_FILES" -eq 1 ]]; then
CLEANUP_WORKDIR=0
fi
cleanup() {
if [[ "${CLEANUP_WORKDIR:-0}" -eq 1 ]]; then
rm -rf "$WORKDIR"
fi
}
trap cleanup EXIT
UPSTREAM_SCRIPT="$WORKDIR/translate2german.upstream.sh"
CONVERTED_SCRIPT="$WORKDIR/translate2german.converted.sh"
RUN_LOG="$WORKDIR/translate2german.run.log"
echo "[1/5] Prüfe Docker-Container ..."
docker inspect "$CONTAINER_NAME" >/dev/null 2>&1 || die "Container nicht gefunden: $CONTAINER_NAME"
RUNNING_STATE="$(docker inspect -f '{{.State.Running}}' "$CONTAINER_NAME" 2>/dev/null || true)"
[[ "$RUNNING_STATE" == "true" ]] || die "Container läuft nicht: $CONTAINER_NAME"
docker exec "$CONTAINER_NAME" sh -c 'true' >/dev/null 2>&1 || die "docker exec Test fehlgeschlagen für: $CONTAINER_NAME"
echo "[2/5] Lade Upstream-Skript ..."
curl -fsSL "$UPSTREAM_URL" -o "$UPSTREAM_SCRIPT" || die "Download fehlgeschlagen: $UPSTREAM_URL"
echo "[3/5] Konvertiere rpl -> sed ..."
python3 - "$UPSTREAM_SCRIPT" "$CONVERTED_SCRIPT" <<'PY'
import re
import shlex
import sys
src_path = sys.argv[1]
dst_path = sys.argv[2]
dep_block_start = re.compile(r"^\s*if\s+dpkg-query\s+-s\s+rpl\b")
sudo_prefix = re.compile(r"^(\s*)sudo\s+")
rpl_active = re.compile(r"^\s*rpl\s+--encoding\s+UTF-8\b")
in_dep_block = False
dep_if_depth = 0
converted_count = 0
unconverted_lines = []
def sed_escape(value: str) -> str:
return value.replace("\\", "\\\\").replace("&", r"\&").replace("|", r"\|")
def shell_single_quote(value: str) -> str:
return "'" + value.replace("'", "'\"'\"'") + "'"
with open(src_path, "r", encoding="utf-8") as fin, open(dst_path, "w", encoding="utf-8") as fout:
for lineno, raw_line in enumerate(fin, start=1):
line = raw_line.rstrip("\n")
if dep_block_start.match(line):
in_dep_block = True
dep_if_depth = 1
continue
if in_dep_block:
stripped = line.strip()
if stripped.startswith("if "):
dep_if_depth += 1
if stripped == "fi":
dep_if_depth -= 1
if dep_if_depth == 0:
in_dep_block = False
continue
no_sudo = sudo_prefix.sub(r"\1", line, count=1)
stripped = no_sudo.strip()
if stripped.startswith("#") or stripped == "":
fout.write(no_sudo + "\n")
continue
try:
parts = shlex.split(no_sudo, posix=True)
except ValueError:
fout.write(no_sudo + "\n")
continue
if len(parts) >= 6 and parts[0] == "rpl" and parts[1] == "--encoding" and parts[2] == "UTF-8":
src = parts[3]
dst = parts[4]
file_path = " ".join(parts[5:])
sed_expr = "s|{}|{}|g".format(sed_escape(src), sed_escape(dst))
converted_line = "sed -i {} {}".format(
shell_single_quote(sed_expr),
shell_single_quote(file_path),
)
fout.write(converted_line + "\n")
converted_count += 1
continue
fout.write(no_sudo + "\n")
with open(dst_path, "r", encoding="utf-8") as chk:
for lineno, raw_line in enumerate(chk, start=1):
if raw_line.lstrip().startswith("#"):
continue
if rpl_active.search(raw_line):
unconverted_lines.append(lineno)
if unconverted_lines:
print("ERROR: Unkonvertierte rpl-Zeilen in Ausgabe: {}".format(",".join(map(str, unconverted_lines))), file=sys.stderr)
sys.exit(2)
if converted_count == 0:
print("ERROR: Keine rpl-Befehle konvertiert. Upstream-Format evtl. geändert.", file=sys.stderr)
sys.exit(3)
print("Konvertiert: {} rpl-Befehle".format(converted_count))
PY
chmod +x "$CONVERTED_SCRIPT"
echo "[4/5] Führe konvertiertes Skript im Container aus ..."
if ! docker exec -i "$CONTAINER_NAME" sh -s < "$CONVERTED_SCRIPT" | tee "$RUN_LOG"; then
die "Ausführung im Container fehlgeschlagen. Log: $RUN_LOG"
fi
echo "[5/5] Fertig."
echo "Container: $CONTAINER_NAME"
echo "Upstream: $UPSTREAM_URL"
echo "Run-Log: $RUN_LOG"
if [[ "$KEEP_FILES" -eq 1 ]]; then
echo "Arbeitsverzeichnis behalten: $WORKDIR"
fi