- Termin aus Mail: Mailtext oder .eml per Drag & Drop/Strg+V auf Terminplan oder Aufgaben, KI schlägt Aufgaben vor, Prüfung im Aufgaben-Panel; neues Notizfeld je Aufgabe (plan_todos.notiz) - Terminplan: Meilensteine ohne Aufgabenbezug je in eigener Zeile, "+ Neu"-Menü klappt neben schmaler Seitenleiste nach rechts, Leistungsphasen - Rollen & Rechte, Abwesenheiten-Seite, Benachrichtigungen, Rundgang - Glas-Darstellung, Logo als Favicon - Pi-Betrieb: PDF-Arbeit im Kindprozess, Wächter, Einrichtungs- und Prüfskript inkl. WLAN-Wächter Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
237 lines
9.2 KiB
Bash
Executable File
237 lines
9.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# pi-einrichten.sh — macht Ubuntu Server auf dem Raspberry Pi Zero 2 (512 MB)
|
|
# stabil für die Projektübersicht. Mehrfach ausführbar (prüft jeden Schritt).
|
|
#
|
|
# sudo bash Docker/pi-einrichten.sh # Standard
|
|
# sudo bash Docker/pi-einrichten.sh --snapd-aus # zusätzlich snapd abschalten
|
|
#
|
|
# Was es tut (Einzelheiten in CLAUDE.md, "Betrieb auf dem Pi"):
|
|
# 1. Hardware-Watchdog: hängt das System, startet der Pi selbst neu
|
|
# 2. zram: komprimierter Auslagerungsspeicher im RAM (statt SD-Karte)
|
|
# 3. Speichergrenzen für Container erlauben (cgroup memory)
|
|
# 4. gpu_mem=16 — kein Bildschirm, mehr RAM für Linux
|
|
# 5. Unnötige Dienste aus (multipathd, ModemManager, udisks2 …)
|
|
# 6. earlyoom: greift ein, BEVOR das System im Speichermangel festfriert
|
|
# 7. Systemprotokoll auf 50 MB begrenzen
|
|
# 8. Nächtlicher Neustart des Containers (3:30 Uhr)
|
|
# 9. Prüfung der Stromversorgung (Unterspannung)
|
|
# 10. WLAN: Energiesparen aus, Wächter verbindet neu und startet den Pi
|
|
# neu, wenn das Netz 10 Minuten lang weg bleibt
|
|
set -u
|
|
|
|
if [ "$(id -u)" -ne 0 ]; then echo "Bitte mit sudo ausführen."; exit 1; fi
|
|
|
|
SNAPD_AUS=0
|
|
[ "${1:-}" = "--snapd-aus" ] && SNAPD_AUS=1
|
|
|
|
NEUSTART_NOETIG=0
|
|
FIRMWARE=/boot/firmware
|
|
[ -d "$FIRMWARE" ] || FIRMWARE=/boot
|
|
ok() { echo " ✓ $*"; }
|
|
info() { echo " • $*"; }
|
|
warn() { echo " ! $*"; }
|
|
|
|
echo "== Projektübersicht: Pi einrichten =="
|
|
echo "System: $(. /etc/os-release; echo "$PRETTY_NAME"), $(uname -m), $(free -m | awk '/Mem:/{print $2}') MB RAM"
|
|
|
|
# 1. Hardware-Watchdog ------------------------------------------------------
|
|
echo "[1] Hardware-Watchdog"
|
|
mkdir -p /etc/systemd/system.conf.d
|
|
cat > /etc/systemd/system.conf.d/10-watchdog.conf <<'EOF'
|
|
# Hängt das System, löst der Watchdog des Pi nach 14 s einen Neustart aus
|
|
# (der bcm2835-Watchdog kann höchstens ~15 s).
|
|
[Manager]
|
|
RuntimeWatchdogSec=14s
|
|
RebootWatchdogSec=2min
|
|
EOF
|
|
if [ -e /dev/watchdog ]; then ok "Watchdog aktiv nach Neustart (/dev/watchdog vorhanden)"
|
|
else warn "/dev/watchdog fehlt — Kernelmodul bcm2835_wdt wird beim Start geladen"; echo bcm2835_wdt > /etc/modules-load.d/watchdog.conf; NEUSTART_NOETIG=1; fi
|
|
systemctl daemon-reexec
|
|
|
|
# 2. zram -------------------------------------------------------------------
|
|
echo "[2] zram (komprimierter Auslagerungsspeicher)"
|
|
apt-get update -qq
|
|
if apt-cache show systemd-zram-generator >/dev/null 2>&1; then
|
|
apt-get install -y -qq systemd-zram-generator >/dev/null
|
|
cat > /etc/systemd/zram-generator.conf <<'EOF'
|
|
[zram0]
|
|
zram-size = ram / 2
|
|
compression-algorithm = zstd
|
|
swap-priority = 100
|
|
EOF
|
|
systemctl daemon-reload
|
|
systemctl restart systemd-zram-setup@zram0.service 2>/dev/null || true
|
|
ok "systemd-zram-generator eingerichtet (halber RAM, zstd)"
|
|
else
|
|
apt-get install -y -qq zram-tools >/dev/null
|
|
printf 'ALGO=zstd\nPERCENT=50\nPRIORITY=100\n' > /etc/default/zramswap
|
|
systemctl restart zramswap 2>/dev/null || true
|
|
ok "zram-tools eingerichtet (halber RAM, zstd)"
|
|
fi
|
|
cat > /etc/sysctl.d/90-zram.conf <<'EOF'
|
|
# zram ist schnell: früh auslagern, Seiten einzeln holen
|
|
vm.swappiness=100
|
|
vm.page-cluster=0
|
|
vm.vfs_cache_pressure=200
|
|
EOF
|
|
sysctl -q --system
|
|
swapon --show | sed 's/^/ /'
|
|
|
|
# 3. Speichergrenzen für Container -------------------------------------------
|
|
echo "[3] Speichergrenzen für Container (cgroup memory)"
|
|
if grep -qw memory /sys/fs/cgroup/cgroup.controllers 2>/dev/null; then
|
|
ok "bereits verfügbar"
|
|
else
|
|
CMDLINE="$FIRMWARE/cmdline.txt"
|
|
if [ -f "$CMDLINE" ] && ! grep -q "cgroup_enable=memory" "$CMDLINE"; then
|
|
cp "$CMDLINE" "$CMDLINE.vor-projektuebersicht"
|
|
sed -i '1 s/$/ cgroup_enable=memory cgroup_memory=1/' "$CMDLINE"
|
|
ok "in $CMDLINE ergänzt (Sicherung: cmdline.txt.vor-projektuebersicht)"
|
|
NEUSTART_NOETIG=1
|
|
else
|
|
warn "nicht verfügbar — $CMDLINE prüfen"
|
|
fi
|
|
fi
|
|
|
|
# 4. GPU-Speicher -----------------------------------------------------------
|
|
echo "[4] GPU-Speicher"
|
|
CONFIG="$FIRMWARE/config.txt"
|
|
if [ -f "$CONFIG" ]; then
|
|
if grep -q "^gpu_mem=" "$CONFIG"; then sed -i 's/^gpu_mem=.*/gpu_mem=16/' "$CONFIG"
|
|
else printf '\n# Projektübersicht: kein Bildschirm, mehr RAM für Linux\ngpu_mem=16\n' >> "$CONFIG"; fi
|
|
ok "gpu_mem=16"
|
|
NEUSTART_NOETIG=1
|
|
else warn "$CONFIG nicht gefunden"; fi
|
|
|
|
# 5. Unnötige Dienste -------------------------------------------------------
|
|
echo "[5] Unnötige Dienste abschalten"
|
|
for d in multipathd multipathd.socket ModemManager udisks2 packagekit bluetooth avahi-daemon avahi-daemon.socket; do
|
|
if systemctl list-unit-files "$d.service" "$d" 2>/dev/null | grep -q "$d"; then
|
|
systemctl disable --now "$d" >/dev/null 2>&1 && ok "$d aus"
|
|
fi
|
|
done
|
|
if [ "$SNAPD_AUS" = 1 ]; then
|
|
if command -v snap >/dev/null && snap list docker >/dev/null 2>&1; then
|
|
warn "Docker ist als Snap installiert — snapd bleibt an"
|
|
else
|
|
systemctl disable --now snapd.service snapd.socket snapd.seeded.service >/dev/null 2>&1 && ok "snapd aus"
|
|
fi
|
|
else
|
|
info "snapd bleibt an (spart ~40 MB mit --snapd-aus)"
|
|
fi
|
|
|
|
# 6. earlyoom ---------------------------------------------------------------
|
|
echo "[6] earlyoom"
|
|
apt-get install -y -qq earlyoom >/dev/null
|
|
cat > /etc/default/earlyoom <<'EOF'
|
|
# Bei unter 5 % freiem RAM UND unter 10 % freiem Swap den größten Prozess
|
|
# beenden — nie SSH, Docker oder systemd. Der App-Container startet dann neu.
|
|
# Ohne innere Anführungszeichen: systemd reicht sie sonst wörtlich weiter.
|
|
EARLYOOM_ARGS="-m 5 -s 10 -r 3600 --avoid ^(sshd|dockerd|containerd|systemd|systemd-journal)$ --prefer ^node$"
|
|
EOF
|
|
systemctl enable --now earlyoom >/dev/null 2>&1
|
|
systemctl restart earlyoom
|
|
ok "aktiv"
|
|
|
|
# 7. Systemprotokoll --------------------------------------------------------
|
|
echo "[7] Systemprotokoll begrenzen"
|
|
mkdir -p /etc/systemd/journald.conf.d
|
|
printf '[Journal]\nSystemMaxUse=50M\n' > /etc/systemd/journald.conf.d/10-groesse.conf
|
|
systemctl restart systemd-journald
|
|
ok "höchstens 50 MB"
|
|
|
|
# 8. Nächtlicher Neustart ---------------------------------------------------
|
|
echo "[8] Nächtlicher Neustart des Containers"
|
|
cat > /etc/cron.d/projektuebersicht <<'EOF'
|
|
# Projektübersicht: frischer Speicher jede Nacht (gegen schleichende Lecks)
|
|
30 3 * * * root docker restart projektuebersicht >/dev/null 2>&1
|
|
EOF
|
|
systemctl enable docker >/dev/null 2>&1
|
|
ok "täglich 3:30 Uhr; Docker startet beim Booten"
|
|
|
|
# 9. Stromversorgung --------------------------------------------------------
|
|
echo "[9] Stromversorgung"
|
|
T=""
|
|
[ -r /sys/devices/platform/soc/soc:firmware/get_throttled ] && T=$(cat /sys/devices/platform/soc/soc:firmware/get_throttled)
|
|
command -v vcgencmd >/dev/null && T=$(vcgencmd get_throttled | cut -d= -f2)
|
|
if [ -z "$T" ]; then info "nicht auslesbar (Paket libraspberrypi-bin installieren)"
|
|
elif [ "$((T))" -eq 0 ]; then ok "keine Unterspannung seit dem Start ($T)"
|
|
else warn "Unterspannung/Drosselung erkannt ($T) — besseres Netzteil (5 V / 2,5 A) verwenden!"; fi
|
|
|
|
# 10. WLAN ------------------------------------------------------------------
|
|
# Befund 29.10.: Der Pi "stürzte" nicht ab, sondern verlor das WLAN
|
|
# (wlan0: Lost carrier) und kam danach nie wieder in das Netz
|
|
# (ASSOC-REJECT in Schleife) — erreichbar war er erst nach Stecker ziehen.
|
|
echo "[10] WLAN stabil halten"
|
|
apt-get install -y -qq iw >/dev/null 2>&1
|
|
cat > /etc/systemd/system/wlan-energiesparen-aus.service <<'EOF'
|
|
[Unit]
|
|
Description=WLAN-Energiesparen aus (Pi Zero 2 verliert sonst die Verbindung)
|
|
After=sys-subsystem-net-devices-wlan0.device
|
|
Wants=sys-subsystem-net-devices-wlan0.device
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
ExecStart=/usr/sbin/iw dev wlan0 set power_save off
|
|
RemainAfterExit=yes
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
cat > /usr/local/sbin/wlan-waechter <<'EOF'
|
|
#!/bin/sh
|
|
# Prüft jede Minute, ob das Gateway erreichbar ist. Ohne Netz: alle 2
|
|
# Minuten WLAN neu verbinden, nach 10 Minuten den Pi neu starten.
|
|
ZAEHLER=/run/wlan-waechter.zaehler
|
|
GW=$(ip route | awk '/^default/{print $3; exit}')
|
|
if [ -n "$GW" ] && ping -c 2 -W 3 "$GW" >/dev/null 2>&1; then
|
|
rm -f "$ZAEHLER"; exit 0
|
|
fi
|
|
N=$(( $(cat "$ZAEHLER" 2>/dev/null || echo 0) + 1 ))
|
|
echo "$N" > "$ZAEHLER"
|
|
logger -t wlan-waechter "kein Netz seit $N Minute(n)"
|
|
if [ "$N" -ge 10 ]; then
|
|
logger -t wlan-waechter "Neustart des Pi"; systemctl reboot
|
|
elif [ $((N % 2)) -eq 0 ]; then
|
|
logger -t wlan-waechter "WLAN neu verbinden"
|
|
wpa_cli -i wlan0 reassociate >/dev/null 2>&1
|
|
if [ "$N" -ge 4 ]; then
|
|
ip link set wlan0 down; sleep 2; ip link set wlan0 up
|
|
networkctl reconfigure wlan0 >/dev/null 2>&1
|
|
fi
|
|
fi
|
|
EOF
|
|
chmod 755 /usr/local/sbin/wlan-waechter
|
|
cat > /etc/systemd/system/wlan-waechter.service <<'EOF'
|
|
[Unit]
|
|
Description=WLAN-Wächter der Projektübersicht
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
ExecStart=/usr/local/sbin/wlan-waechter
|
|
EOF
|
|
cat > /etc/systemd/system/wlan-waechter.timer <<'EOF'
|
|
[Unit]
|
|
Description=WLAN-Wächter jede Minute
|
|
|
|
[Timer]
|
|
OnBootSec=3min
|
|
OnUnitActiveSec=1min
|
|
|
|
[Install]
|
|
WantedBy=timers.target
|
|
EOF
|
|
systemctl daemon-reload
|
|
systemctl enable --now wlan-energiesparen-aus.service >/dev/null 2>&1
|
|
systemctl enable --now wlan-waechter.timer >/dev/null 2>&1
|
|
if iw dev wlan0 get power_save 2>/dev/null | grep -q off; then ok "Energiesparen aus"; else warn "Energiesparen ließ sich nicht abschalten"; fi
|
|
ok "Wächter aktiv (neu verbinden ab 2 min, Neustart nach 10 min ohne Netz)"
|
|
|
|
echo
|
|
if [ "$NEUSTART_NOETIG" = 1 ]; then
|
|
echo "Fertig. Bitte den Pi jetzt neu starten: sudo reboot"
|
|
else
|
|
echo "Fertig."
|
|
fi
|
|
echo "Danach prüfen: bash Docker/pi-pruefen.sh"
|