LabCodeHub öffentliche Ansicht
Anmelden
Küpper / ehc-tvwall öffentlich
Branch: main
ehc-tvwall / install.sh
Verlauf Rohdaten
R Rüdiger Küpper fix: v1.0.0
14518d05 vor 11 Tagen
install.sh 340 Zeilen · 12.7 KB · Shell
1
#!/usr/bin/env bash
2
# ===========================================================================
3
#  TV-Wall - Installation auf dem Raspberry Pi (Raspberry Pi OS / Raspbian)
4
#
5
#  Aufruf:   sudo ./install.sh [Optionen]
6
#
7
#  Optionen:
8
#    --no-ap        WLAN-Access-Point nicht einrichten
9
#    --ap-only      nur den Access Point einrichten
10
#    --config PFAD  andere Konfigurationsdatei verwenden
11
#    --yes          keine Rueckfragen stellen
12
# ===========================================================================
13
set -euo pipefail
14
15
SRC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
CONFIG_DST="/etc/tvwall/tvwall.conf"
17
BIN_DST="/usr/local/bin/tvwall"
18
UNIT_DST="/etc/systemd/system/tvwall.service"
19
DOC_DST="/opt/tvwall"
20
21
WITH_AP=1
22
AP_ONLY=0
23
ASSUME_YES=0
24
CONFIG_SRC=""
25
26
info()  { printf '\033[1;36m==>\033[0m %s\n' "$*"; }
27
ok()    { printf '\033[1;32m  ✓\033[0m %s\n' "$*"; }
28
warn()  { printf '\033[1;33m  !\033[0m %s\n' "$*"; }
29
die()   { printf '\033[1;31mFehler:\033[0m %s\n' "$*" >&2; exit 1; }
30
31
while [[ $# -gt 0 ]]; do
32
  case "$1" in
33
    --no-ap)   WITH_AP=0; shift ;;
34
    --ap-only) AP_ONLY=1; shift ;;
35
    --yes|-y)  ASSUME_YES=1; shift ;;
36
    --config)  CONFIG_SRC="${2:-}"; shift 2 ;;
37
    -h|--help) sed -n '2,15p' "$0"; exit 0 ;;
38
    *)         die "unbekannte Option: $1" ;;
39
  esac
40
done
41
42
[[ $EUID -eq 0 ]] || die "bitte mit sudo starten:  sudo $0"
43
44
# ---------------------------------------------------------------------------
45
# Konfiguration einlesen
46
# ---------------------------------------------------------------------------
47
if [[ -z "$CONFIG_SRC" ]]; then
48
  if [[ -f "$CONFIG_DST" ]]; then
49
    CONFIG_SRC="$CONFIG_DST"          # bestehende Installation: nicht ueberschreiben
50
  else
51
    CONFIG_SRC="$SRC_DIR/config/tvwall.conf"
52
  fi
53
fi
54
[[ -f "$CONFIG_SRC" ]] || die "Konfigurationsdatei nicht gefunden: $CONFIG_SRC"
55
56
set -a
57
# shellcheck disable=SC1090
58
. "$CONFIG_SRC"
59
set +a
60
61
AP_SSID="${AP_SSID:-TVWall-01}"
62
RUN_USER="${RUN_USER:-tvwall}"
63
STATE_DIR="${STATE_DIR:-/var/lib/tvwall}"
64
DISPLAY_MODE="${DISPLAY_MODE:-auto}"
65
HOSTNAME_NEW="${HOSTNAME:-tvwall-01}"
66
WEB_PORT="${WEB_PORT:-80}"
67
68
# --- Bildausgabe bestimmen --------------------------------------------------
69
# Der Desktop-Benutzer ist der einzige, der an die Sitzung herankommt.
70
detect_desktop_user() {
71
  local d u
72
  for d in /run/user/*; do
73
    [[ -d "$d" ]] || continue
74
    compgen -G "$d/wayland-*" >/dev/null 2>&1 || continue
75
    u="$(getent passwd "$(basename "$d")" | cut -d: -f1)"
76
    [[ -n "$u" ]] && { echo "$u"; return; }
77
  done
78
  u="$(sed -n 's/^autologin-user=//p' /etc/lightdm/lightdm.conf 2>/dev/null | head -1)"
79
  [[ -n "$u" ]] && { echo "$u"; return; }
80
  u="$(sed -n 's/.*--autologin \([^ ]*\).*/\1/p' \
81
       /etc/systemd/system/getty@tty1.service.d/autologin.conf 2>/dev/null | head -1)"
82
  [[ -n "$u" && "$u" != "root" ]] && { echo "$u"; return; }
83
  getent passwd 1000 | cut -d: -f1
84
}
85
86
GRAPHICAL=0
87
[[ "$(systemctl get-default 2>/dev/null)" == "graphical.target" ]] && GRAPHICAL=1
88
pgrep -x labwc  >/dev/null 2>&1 && GRAPHICAL=1
89
pgrep -x wayfire >/dev/null 2>&1 && GRAPHICAL=1
90
pgrep -x Xorg   >/dev/null 2>&1 && GRAPHICAL=1
91
92
DISPLAY_MODE_CHANGED=0
93
# Eine bestehende Konfiguration wird nicht ueberschrieben - deshalb kann hier
94
# noch ein "drm" aus einer frueheren Installation stehen. Auf einem System mit
95
# Desktop fuehrt das zwangslaeufig zum schwarzen Bildschirm.
96
if [[ "$DISPLAY_MODE" == "drm" && $GRAPHICAL -eq 1 ]]; then
97
  echo
98
  warn "In der Konfiguration steht DISPLAY_MODE=\"drm\", auf diesem Pi laeuft aber"
99
  warn "ein Desktop. mpv kann den Bildschirm so nicht bekommen - er bliebe schwarz."
100
  if [[ $ASSUME_YES -eq 1 ]]; then
101
    DISPLAY_MODE="auto"; DISPLAY_MODE_CHANGED=1
102
  else
103
    read -r -p "    Auf DISPLAY_MODE=\"auto\" umstellen? [J/n] " answer
104
    if [[ ! "$answer" =~ ^[nN]$ ]]; then
105
      DISPLAY_MODE="auto"; DISPLAY_MODE_CHANGED=1
106
    fi
107
  fi
108
  echo
109
fi
110
111
EFFECTIVE_MODE="$DISPLAY_MODE"
112
if [[ "$EFFECTIVE_MODE" == "auto" ]]; then
113
  if [[ $GRAPHICAL -eq 1 ]]; then
114
    EFFECTIVE_MODE="desktop"
115
  else
116
    EFFECTIVE_MODE="drm"
117
  fi
118
fi
119
120
RUN_USER_CHANGED=0
121
if [[ "$EFFECTIVE_MODE" == "desktop" && "$RUN_USER" == "tvwall" ]]; then
122
  DESKTOP_USER="$(detect_desktop_user || true)"
123
  if [[ -n "$DESKTOP_USER" ]]; then
124
    RUN_USER="$DESKTOP_USER"
125
    RUN_USER_CHANGED=1
126
  fi
127
fi
128
129
info "Konfiguration: $CONFIG_SRC"
130
echo "    WLAN-Name (SSID) : $AP_SSID"
131
echo "    Hostname         : $HOSTNAME_NEW"
132
if [[ "$DISPLAY_MODE" == "auto" ]]; then
133
  echo "    Bildausgabe      : auto -> $EFFECTIVE_MODE"
134
else
135
  echo "    Bildausgabe      : $EFFECTIVE_MODE"
136
fi
137
if [[ $RUN_USER_CHANGED -eq 1 ]]; then
138
  echo "    Benutzer         : $RUN_USER   (Desktop-Benutzer, automatisch erkannt)"
139
else
140
  echo "    Benutzer         : $RUN_USER"
141
fi
142
echo "    Medienordner     : $STATE_DIR/media"
143
echo
144
145
if [[ $AP_ONLY -eq 1 ]]; then
146
  exec "$SRC_DIR/scripts/setup-ap.sh" "$CONFIG_SRC"
147
fi
148
149
# ---------------------------------------------------------------------------
150
# Pakete
151
# ---------------------------------------------------------------------------
152
if command -v apt-get >/dev/null; then
153
  MISSING=()
154
  command -v mpv    >/dev/null || MISSING+=(mpv)
155
  command -v ffmpeg >/dev/null || MISSING+=(ffmpeg)
156
  if [[ ${#MISSING[@]} -gt 0 ]]; then
157
    info "Installiere Pakete: ${MISSING[*]}"
158
    DEBIAN_FRONTEND=noninteractive apt-get update -qq
159
    DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends "${MISSING[@]}"
160
  fi
161
  ok "mpv und ffmpeg vorhanden"
162
else
163
  warn "kein apt-get gefunden - mpv und ffmpeg bitte selbst installieren"
164
fi
165
166
# ---------------------------------------------------------------------------
167
# Programm installieren
168
# ---------------------------------------------------------------------------
169
info "Installiere Programm nach $BIN_DST"
170
ARCH="$(uname -m)"
171
case "$ARCH" in
172
  aarch64|arm64) PREBUILT="$SRC_DIR/build/tvwall-linux-arm64" ;;
173
  armv7l|armv6l) PREBUILT="$SRC_DIR/build/tvwall-linux-arm" ;;
174
  x86_64)        PREBUILT="$SRC_DIR/build/tvwall-linux-amd64" ;;
175
  *)             PREBUILT="" ;;
176
esac
177
178
if [[ -n "$PREBUILT" && -f "$PREBUILT" ]]; then
179
  install -m 0755 "$PREBUILT" "$BIN_DST"
180
  ok "fertiges Programm verwendet ($ARCH)"
181
elif command -v go >/dev/null && [[ -f "$SRC_DIR/go.mod" ]]; then
182
  info "Baue aus dem Quelltext (das dauert einen Moment)"
183
  ( cd "$SRC_DIR" && go build -trimpath -ldflags "-s -w" -o /tmp/tvwall.build ./cmd/tvwall )
184
  install -m 0755 /tmp/tvwall.build "$BIN_DST" && rm -f /tmp/tvwall.build
185
  ok "aus Quelltext gebaut"
186
elif [[ -x "$BIN_DST" ]]; then
187
  # Erneuter Aufruf aus /opt/tvwall, nur um die Konfiguration zu uebernehmen.
188
  ok "vorhandenes Programm beibehalten ($("$BIN_DST" -version 2>/dev/null || echo unbekannt))"
189
else
190
  die "kein passendes Programm gefunden ($ARCH) und Go ist nicht installiert.
191
       Auf dem Entwicklungsrechner 'make dist' ausfuehren und das Paket hierher kopieren."
192
fi
193
194
# ---------------------------------------------------------------------------
195
# Benutzer und Verzeichnisse
196
# ---------------------------------------------------------------------------
197
if ! id -u "$RUN_USER" >/dev/null 2>&1; then
198
  info "Lege Benutzer $RUN_USER an"
199
  useradd --system --create-home --home-dir "/home/$RUN_USER" --shell /usr/sbin/nologin "$RUN_USER"
200
fi
201
for grp in video render audio input tty; do
202
  getent group "$grp" >/dev/null && usermod -aG "$grp" "$RUN_USER" || true
203
done
204
ok "Benutzer $RUN_USER eingerichtet"
205
206
install -d -m 0775 -o "$RUN_USER" -g "$RUN_USER" "$STATE_DIR" "$STATE_DIR/media" "$STATE_DIR/thumbs"
207
chown -R "$RUN_USER":"$RUN_USER" "$STATE_DIR" 2>/dev/null || true
208
ok "Medienordner: $STATE_DIR/media"
209
210
install -d -m 0755 /etc/tvwall
211
if [[ -f "$CONFIG_DST" && "$CONFIG_SRC" != "$CONFIG_DST" ]]; then
212
  install -m 0644 "$CONFIG_SRC" "$CONFIG_DST.neu"
213
  warn "$CONFIG_DST existiert bereits - neue Fassung liegt als $CONFIG_DST.neu daneben"
214
elif [[ ! -f "$CONFIG_DST" ]]; then
215
  install -m 0644 "$CONFIG_SRC" "$CONFIG_DST"
216
  ok "Konfiguration nach $CONFIG_DST kopiert"
217
fi
218
219
if [[ $RUN_USER_CHANGED -eq 1 && -f "$CONFIG_DST" ]]; then
220
  sed -i "s|^RUN_USER=.*|RUN_USER=\"$RUN_USER\"|" "$CONFIG_DST"
221
  ok "RUN_USER in $CONFIG_DST auf '$RUN_USER' gesetzt (Desktop-Benutzer)"
222
fi
223
if [[ $DISPLAY_MODE_CHANGED -eq 1 && -f "$CONFIG_DST" ]]; then
224
  sed -i "s|^DISPLAY_MODE=.*|DISPLAY_MODE=\"auto\"|" "$CONFIG_DST"
225
  ok "DISPLAY_MODE in $CONFIG_DST auf 'auto' gesetzt"
226
fi
227
228
install -d -m 0755 "$DOC_DST"
229
[[ -f "$SRC_DIR/README.md" ]] && install -m 0644 "$SRC_DIR/README.md" "$DOC_DST/README.md"
230
if [[ -d "$SRC_DIR/scripts" ]]; then
231
  install -d -m 0755 "$DOC_DST/scripts"
232
  install -m 0755 "$SRC_DIR"/scripts/* "$DOC_DST/scripts/"
233
fi
234
# Installer und Vorlagen mitnehmen, damit spaetere Aenderungen ohne das
235
# Quellpaket auskommen:  sudo /opt/tvwall/install.sh --no-ap
236
if [[ "$SRC_DIR" != "$DOC_DST" ]]; then
237
  install -m 0755 "$SRC_DIR/install.sh" "$DOC_DST/install.sh"
238
  install -d -m 0755 "$DOC_DST/systemd" "$DOC_DST/config"
239
  install -m 0644 "$SRC_DIR"/systemd/*.in "$DOC_DST/systemd/"
240
  install -m 0644 "$SRC_DIR"/config/tvwall.conf "$DOC_DST/config/tvwall.conf"
241
fi
242
243
# ---------------------------------------------------------------------------
244
# systemd-Dienst
245
# ---------------------------------------------------------------------------
246
info "Richte systemd-Dienst ein"
247
if [[ "$EFFECTIVE_MODE" == "desktop" ]]; then
248
  TEMPLATE="$SRC_DIR/systemd/tvwall-desktop.service.in"
249
  RUN_UID="$(id -u "$RUN_USER")"
250
  if [[ "$RUN_USER" == "tvwall" ]]; then
251
    warn "Bildausgabe im Desktop, aber der Dienst laeuft als 'tvwall'."
252
    warn "Nur der Benutzer, dem die Desktop-Sitzung gehoert, kommt an den Bildschirm."
253
    warn "Bitte RUN_USER in $CONFIG_DST auf den Desktop-Benutzer setzen (meist 'pi')."
254
  fi
255
else
256
  TEMPLATE="$SRC_DIR/systemd/tvwall-drm.service.in"
257
  RUN_UID=""
258
fi
259
[[ -f "$TEMPLATE" ]] || die "Vorlage fehlt: $TEMPLATE"
260
261
sed -e "s|@RUN_USER@|$RUN_USER|g" \
262
    -e "s|@RUN_UID@|$RUN_UID|g" \
263
    -e "s|@CONFIG@|$CONFIG_DST|g" \
264
    "$TEMPLATE" > "$UNIT_DST"
265
chmod 0644 "$UNIT_DST"
266
267
if [[ "$EFFECTIVE_MODE" != "desktop" ]]; then
268
  # Auf der Konsole soll kein Login-Prompt um den Bildschirm streiten.
269
  systemctl disable --now getty@tty1.service >/dev/null 2>&1 || true
270
else
271
  # Im Desktop-Betrieb wird tty1 gebraucht - eine frueher im Konsolenmodus
272
  # abgeschaltete Konsole hier wieder einschalten.
273
  if ! systemctl is-enabled --quiet getty@tty1.service 2>/dev/null; then
274
    systemctl enable --now getty@tty1.service >/dev/null 2>&1 || true
275
    ok "Konsole auf tty1 wieder aktiviert"
276
  fi
277
fi
278
279
systemctl daemon-reload
280
systemctl enable tvwall.service >/dev/null
281
ok "Dienst aktiviert (startet ab jetzt bei jedem Einschalten mit)"
282
283
# ---------------------------------------------------------------------------
284
# Hostname
285
# ---------------------------------------------------------------------------
286
if [[ -n "$HOSTNAME_NEW" && "$(hostname)" != "$HOSTNAME_NEW" ]]; then
287
  info "Setze Hostname auf $HOSTNAME_NEW"
288
  hostnamectl set-hostname "$HOSTNAME_NEW" || true
289
  sed -i "s/^127\.0\.1\.1.*/127.0.1.1\t$HOSTNAME_NEW/" /etc/hosts 2>/dev/null || true
290
  grep -q "^127.0.1.1" /etc/hosts || printf '127.0.1.1\t%s\n' "$HOSTNAME_NEW" >> /etc/hosts
291
fi
292
command -v avahi-daemon >/dev/null && systemctl enable --now avahi-daemon >/dev/null 2>&1 || true
293
294
# ---------------------------------------------------------------------------
295
# Access Point
296
# ---------------------------------------------------------------------------
297
if [[ $WITH_AP -eq 1 ]]; then
298
  if [[ $ASSUME_YES -eq 0 ]]; then
299
    echo
300
    warn "Der Access Point belegt das WLAN des Pi."
301
    warn "Bist du gerade per WLAN verbunden, bricht diese Verbindung ab."
302
    read -r -p "    Access Point '$AP_SSID' jetzt einrichten? [j/N] " answer
303
    [[ "$answer" =~ ^[jJyY]$ ]] || WITH_AP=0
304
  fi
305
fi
306
if [[ $WITH_AP -eq 1 ]]; then
307
  "$SRC_DIR/scripts/setup-ap.sh" "$CONFIG_DST" || warn "Access Point konnte nicht eingerichtet werden - siehe Meldungen oben"
308
else
309
  info "Access Point uebersprungen (spaeter: sudo $DOC_DST/scripts/setup-ap.sh)"
310
fi
311
312
# ---------------------------------------------------------------------------
313
# Start
314
# ---------------------------------------------------------------------------
315
info "Starte Dienst"
316
systemctl restart tvwall.service
317
sleep 2
318
if systemctl is-active --quiet tvwall.service; then
319
  ok "tvwall laeuft"
320
else
321
  warn "Dienst laeuft nicht - Protokoll ansehen mit:  journalctl -u tvwall -n 50"
322
fi
323
324
PORT_SUFFIX=""
325
[[ "$WEB_PORT" != "80" ]] && PORT_SUFFIX=":$WEB_PORT"
326
cat <<EOF
327
328
===========================================================================
329
 Fertig.
330
331
   WLAN verbinden mit :  $AP_SSID
332
   Webinterface       :  http://${AP_IP:-192.168.4.1}$PORT_SUFFIX
333
                         http://$HOSTNAME_NEW.local$PORT_SUFFIX
334
   Medienordner       :  $STATE_DIR/media
335
336
   Protokoll ansehen  :  journalctl -u tvwall -f
337
   Dienst neu starten :  sudo systemctl restart tvwall
338
   Konfiguration      :  sudo nano $CONFIG_DST
339
===========================================================================
340
EOF