Drei zusammenhaengende Vereinfachungen aus der Bedienung heraus:
1. CMK_IMAGE_SOURCE bestimmt, woher ein fehlendes Image kommt:
auto erst Registry, dann Downloadportal (Vorgabe)
registry nur Registry
download nur Tarball
"auto" ist die sinnvolle Vorgabe, weil Registries nur die juengsten
Patchstaende vorhalten - genau die aeltere Version, die eine
Migration braucht, liegt dann nur noch im Archiv. Der Wechsel
geschieht ohne Rueckfrage.
2. "image get" holt ueber diesen Weg, ohne dass Edition und Version
erneut abgefragt werden. Im Menue fragt "Image holen" entsprechend
nur noch nach dem Bezugsweg; der eingestellte ist gekennzeichnet.
Der vollstaendige Assistent bleibt als eigener Punkt daneben, fuer
den Fall, dass Edition oder Version geaendert werden sollen.
3. "up" beschafft ein lokal fehlendes Image selbst, bevor compose
startet. Vorher lief compose in einen nackten Pull, der bei einer nur
im Downloadportal vorhandenen Version scheitern musste - genau der
Fall mit 2.4.0p9. Mit "up --no-fetch" bleibt das aus.
Durchgespielt mit einer absichtlich nicht existierenden Version: up
erkennt das fehlende Image, versucht die Registry, weicht auf das
Downloadportal aus, bricht nach beiden Fehlschlaegen ab - und ruft
compose gar nicht erst auf. Mit --no-fetch geht es direkt zu compose.
Nebenbei: im Auswahlmenue erschien der eingestellte Bezugsweg doppelt
(einmal als "wie eingestellt", einmal unter seinem Namen) - zwei
Eintraege mit derselben Kennung. Jetzt wird der eingestellte nur noch
gekennzeichnet.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
511 lines
19 KiB
Bash
511 lines
19 KiB
Bash
#!/usr/bin/env bash
|
|
# =====================================================================
|
|
# common.sh - gemeinsame Funktionen fuer cmk-manage.sh
|
|
# Wird per "source" eingebunden, nicht direkt ausgefuehrt.
|
|
# =====================================================================
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Ausgabe / Logging
|
|
# ---------------------------------------------------------------------
|
|
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
|
|
C_RED=$'\033[31m'; C_GRN=$'\033[32m'; C_YEL=$'\033[33m'
|
|
C_BLU=$'\033[34m'; C_BLD=$'\033[1m'; C_RST=$'\033[0m'
|
|
else
|
|
C_RED=; C_GRN=; C_YEL=; C_BLU=; C_BLD=; C_RST=
|
|
fi
|
|
|
|
log() { printf '%s[ %s ]%s %s\n' "$C_BLU" "INFO" "$C_RST" "$*"; }
|
|
ok() { printf '%s[ OK ]%s %s\n' "$C_GRN" "$C_RST" "$*"; }
|
|
warn() { printf '%s[ WARN ]%s %s\n' "$C_YEL" "$C_RST" "$*" >&2; }
|
|
err() { printf '%s[FEHLER]%s %s\n' "$C_RED" "$C_RST" "$*" >&2; }
|
|
die() { err "$*"; exit 1; }
|
|
debug() { [ -n "${VERBOSE:-}" ] && printf ' > %s\n' "$*" >&2 || true; }
|
|
|
|
# ---------------------------------------------------------------------
|
|
# .env laden
|
|
# ---------------------------------------------------------------------
|
|
load_env() {
|
|
local envfile="${1:-$PROJECT_DIR/.env}"
|
|
if [ -f "$envfile" ]; then
|
|
set -a
|
|
# shellcheck disable=SC1090
|
|
. "$envfile"
|
|
set +a
|
|
ENV_FILE="$envfile"
|
|
else
|
|
warn "Keine .env gefunden ($envfile) - es gelten die Standardwerte."
|
|
ENV_FILE=""
|
|
fi
|
|
# Rueckfallwerte, falls ein Schluessel in der .env fehlt.
|
|
# Die .env bleibt die massgebliche Konfiguration - hier steht nur,
|
|
# was gilt, wenn dort nichts eingetragen ist.
|
|
CMK_CONTAINER_NAME="${CMK_CONTAINER_NAME:-checkmk}"
|
|
NPM_CONTAINER_NAME="${NPM_CONTAINER_NAME:-nginx-proxy-manager}"
|
|
CMK_SITE_ID="${CMK_SITE_ID:-cmk}"
|
|
CMK_IMAGE="${CMK_IMAGE:-docker.io/checkmk/check-mk-raw:2.3.0-latest}"
|
|
NPM_ENABLED="${NPM_ENABLED:-on}"
|
|
BACKUP_AUTO="${BACKUP_AUTO:-off}"
|
|
CMK_IMAGE_SOURCE="${CMK_IMAGE_SOURCE:-auto}"
|
|
PROXY_FORWARD_HOST="${PROXY_FORWARD_HOST:-127.0.0.1}"
|
|
PROXY_FORWARD_PORT="${PROXY_FORWARD_PORT:-5000}"
|
|
NPM_ADMIN_PORT="${NPM_ADMIN_PORT:-81}"
|
|
# Leer = automatisch aus dem Admin-Port ableiten
|
|
NPM_ADMIN_URL="${NPM_ADMIN_URL:-http://127.0.0.1:$NPM_ADMIN_PORT}"
|
|
BACKUP_DIR="$(abs_path "${BACKUP_DIR:-$PROJECT_DIR/backups}")"
|
|
IMAGE_DIR="$(abs_path "${IMAGE_DIR:-$PROJECT_DIR/images}")"
|
|
}
|
|
|
|
# relativen Pfad (./foo) am Projektverzeichnis ausrichten
|
|
abs_path() {
|
|
case "$1" in
|
|
/*) printf '%s\n' "$1" ;;
|
|
*) printf '%s\n' "$PROJECT_DIR/${1#./}" ;;
|
|
esac
|
|
}
|
|
|
|
# .env-Wert dauerhaft setzen
|
|
# env_set KEY WERT [allow_empty]
|
|
env_set() {
|
|
local key="$1" val="$2" allow_empty="${3:-}" f="${ENV_FILE:-$PROJECT_DIR/.env}"
|
|
[ -f "$f" ] || die "Keine .env vorhanden - kann '$key' nicht setzen."
|
|
# mehrzeilige Werte wuerden die .env zerstoeren
|
|
case "$val" in
|
|
*$'\n'*) die "Ungueltiger Wert fuer '$key' (mehrzeilig)." ;;
|
|
esac
|
|
[ -n "$val" ] || [ -n "$allow_empty" ] || die "Leerer Wert fuer '$key' - abgebrochen."
|
|
# Werte mit Leerzeichen in Anfuehrungszeichen setzen
|
|
case "$val" in
|
|
*" "*) case "$val" in \"*\") : ;; *) val="\"$val\"" ;; esac ;;
|
|
esac
|
|
if grep -qE "^[#[:space:]]*${key}=" "$f"; then
|
|
sed -i -E "s|^[#[:space:]]*${key}=.*|${key}=${val}|" "$f"
|
|
else
|
|
printf '%s=%s\n' "$key" "$val" >>"$f"
|
|
fi
|
|
export "$key=$val"
|
|
}
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Container-Runtime erkennen (docker / podman)
|
|
# ---------------------------------------------------------------------
|
|
detect_runtime() {
|
|
if [ -n "${CMK_RUNTIME:-}" ] && command -v "$CMK_RUNTIME" >/dev/null 2>&1; then
|
|
RUNTIME="$CMK_RUNTIME"
|
|
elif [ -n "${CMK_RUNTIME:-}" ]; then
|
|
# Falsch gesetzter Wert darf das Werkzeug nicht unbenutzbar machen
|
|
warn "CMK_RUNTIME='$CMK_RUNTIME' ist nicht installiert - suche selbst.
|
|
Korrigieren mit: cmk-manage.sh config set CMK_RUNTIME auto"
|
|
CMK_RUNTIME=""
|
|
detect_runtime
|
|
return
|
|
elif command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then
|
|
RUNTIME=docker
|
|
elif command -v podman >/dev/null 2>&1; then
|
|
RUNTIME=podman
|
|
elif command -v docker >/dev/null 2>&1; then
|
|
RUNTIME=docker # vorhanden, aber Daemon evtl. gestoppt
|
|
else
|
|
die "Weder docker noch podman gefunden. Bitte zuerst ./install.sh ausfuehren."
|
|
fi
|
|
debug "Runtime: $RUNTIME"
|
|
}
|
|
|
|
# Compose-Kommando ermitteln (als Array COMPOSE_CMD)
|
|
detect_compose() {
|
|
COMPOSE_CMD=()
|
|
if [ "$RUNTIME" = docker ]; then
|
|
if docker compose version >/dev/null 2>&1; then
|
|
COMPOSE_CMD=(docker compose)
|
|
elif command -v docker-compose >/dev/null 2>&1; then
|
|
COMPOSE_CMD=(docker-compose)
|
|
fi
|
|
else
|
|
if command -v podman-compose >/dev/null 2>&1; then
|
|
COMPOSE_CMD=(podman-compose)
|
|
elif podman compose version >/dev/null 2>&1; then
|
|
COMPOSE_CMD=(podman compose)
|
|
fi
|
|
fi
|
|
[ ${#COMPOSE_CMD[@]} -gt 0 ] || die "Kein Compose-Kommando gefunden (docker compose / podman-compose)."
|
|
debug "Compose: ${COMPOSE_CMD[*]}"
|
|
}
|
|
|
|
compose() {
|
|
detect_compose
|
|
( cd "$PROJECT_DIR" && "${COMPOSE_CMD[@]}" "$@" )
|
|
}
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Container-Helfer
|
|
# ---------------------------------------------------------------------
|
|
# Ist der NGINX Proxy Manager eingeschaltet? (on/true/1/yes = ja)
|
|
npm_enabled() {
|
|
case "$(printf '%s' "${NPM_ENABLED:-on}" | tr 'A-Z' 'a-z')" in
|
|
on|true|1|yes|ja|y) return 0 ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
container_exists() { $RUNTIME container inspect "$1" >/dev/null 2>&1; }
|
|
container_running() { [ "$($RUNTIME container inspect -f '{{.State.Running}}' "$1" 2>/dev/null)" = "true" ]; }
|
|
|
|
require_cmk_running() {
|
|
container_exists "$CMK_CONTAINER_NAME" \
|
|
|| die "Container '$CMK_CONTAINER_NAME' existiert nicht. Zuerst: $0 up"
|
|
container_running "$CMK_CONTAINER_NAME" \
|
|
|| die "Container '$CMK_CONTAINER_NAME' laeuft nicht. Zuerst: $0 up"
|
|
}
|
|
|
|
# Kommando im Checkmk-Container als root ausfuehren
|
|
ct() { $RUNTIME exec "$CMK_CONTAINER_NAME" "$@"; }
|
|
# ... mit stdin
|
|
cti() { $RUNTIME exec -i "$CMK_CONTAINER_NAME" "$@"; }
|
|
|
|
# ---------------------------------------------------------------------
|
|
# OMD-Helfer (im Container)
|
|
# ---------------------------------------------------------------------
|
|
cmk_sites() {
|
|
ct sh -c 'ls -1 /omd/sites 2>/dev/null' | sed '/^$/d' || true
|
|
}
|
|
|
|
cmk_site_exists() {
|
|
( set +o pipefail; cmk_sites | grep -qxF "$1" )
|
|
}
|
|
|
|
cmk_site_version() {
|
|
ct omd version -b "$1" 2>/dev/null | tr -d '\r' || true
|
|
}
|
|
|
|
# 0 = laeuft komplett, 1 = teilweise, 2 = gestoppt
|
|
cmk_site_state() {
|
|
local rc=0
|
|
ct omd status --bare "$1" >/dev/null 2>&1 || rc=$?
|
|
echo "$rc"
|
|
}
|
|
|
|
cmk_site_state_text() {
|
|
case "$(cmk_site_state "$1")" in
|
|
0) echo "laeuft" ;;
|
|
1) echo "teilweise" ;;
|
|
*) echo "gestoppt" ;;
|
|
esac
|
|
}
|
|
|
|
cmk_versions_installed() {
|
|
ct omd versions -b 2>/dev/null | tr -d '\r' || ct sh -c 'ls -1 /omd/versions | grep -v "^default$"' 2>/dev/null || true
|
|
}
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Diverses
|
|
# ---------------------------------------------------------------------
|
|
human_size() {
|
|
local b="${1:-0}"
|
|
if [ "$b" -ge 1073741824 ] 2>/dev/null; then awk -v b="$b" 'BEGIN{printf "%.1f GiB", b/1073741824}'
|
|
elif [ "$b" -ge 1048576 ] 2>/dev/null; then awk -v b="$b" 'BEGIN{printf "%.1f MiB", b/1048576}'
|
|
elif [ "$b" -ge 1024 ] 2>/dev/null; then awk -v b="$b" 'BEGIN{printf "%.1f KiB", b/1024}'
|
|
else echo "${b} B"; fi
|
|
}
|
|
|
|
file_size() { stat -c %s "$1" 2>/dev/null || echo 0; }
|
|
|
|
confirm() {
|
|
[ -n "${ASSUME_YES:-}" ] && return 0
|
|
local prompt="${1:-Fortfahren?} [j/N] " a
|
|
read -r -p "$prompt" a </dev/tty || return 1
|
|
case "$a" in [jJyY]*) return 0 ;; *) return 1 ;; esac
|
|
}
|
|
|
|
primary_ip() {
|
|
ip -4 route get 1.1.1.1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="src"){print $(i+1);exit}}' \
|
|
|| hostname -I 2>/dev/null | awk '{print $1}'
|
|
}
|
|
|
|
# =====================================================================
|
|
# TUI-Helfer (dialog bevorzugt, sonst whiptail) - ncurses wie im mc
|
|
# =====================================================================
|
|
UI_BACKTITLE="Checkmk Stack Manager - Container-Runtime: ${RUNTIME:-?}"
|
|
|
|
# Groesse des Terminals ermitteln (mit Rueckfallwerten)
|
|
ui_term_size() {
|
|
UI_ROWS="$(tput lines 2>/dev/null || true)"
|
|
UI_COLS="$(tput cols 2>/dev/null || true)"
|
|
case "$UI_ROWS" in ''|*[!0-9]*) UI_ROWS=24 ;; esac
|
|
case "$UI_COLS" in ''|*[!0-9]*) UI_COLS=80 ;; esac
|
|
if [ "$UI_ROWS" -lt 10 ]; then UI_ROWS=24; fi
|
|
if [ "$UI_COLS" -lt 40 ]; then UI_COLS=80; fi
|
|
return 0
|
|
}
|
|
|
|
# Fenstergroesse aus Textlaenge, Anzahl der Eintraege und Terminalgroesse
|
|
# berechnen. Vorher waren die Werte fest - bei langem Text blieb dann nur
|
|
# eine Zeile fuer die Liste uebrig.
|
|
#
|
|
# ACHTUNG: hier bewusst mit if-Bloecken statt "[ ... ] && VAR=...".
|
|
# Letzteres liefert 1, wenn die Bedingung nicht zutrifft, und beendet
|
|
# unter "set -e" die Funktion bzw. das ganze Skript.
|
|
#
|
|
# ui_calc TEXT FELDER_JE_EINTRAG EINTRAEGE...
|
|
ui_calc() {
|
|
local text="$1" per="$2"; shift 2
|
|
ui_term_size
|
|
|
|
local tl maxw
|
|
tl="$(printf '%s\n' "$text" | wc -l)"
|
|
maxw="$(printf '%s\n' "$text" | awk '{ if (length($0) > m) m = length($0) } END { print m+0 }')"
|
|
|
|
# breiteste Zeile der Liste (Kennung und Beschriftung stehen nebeneinander)
|
|
local -a args=("$@")
|
|
local i=0 w itemw=0 n=0
|
|
while [ "$i" -lt "${#args[@]}" ]; do
|
|
w=$(( ${#args[$i]} + 4 ))
|
|
if [ $((i+1)) -lt "${#args[@]}" ]; then
|
|
w=$(( w + ${#args[$((i+1))]} ))
|
|
fi
|
|
if [ "$w" -gt "$itemw" ]; then itemw="$w"; fi
|
|
i=$(( i + per )); n=$(( n + 1 ))
|
|
done
|
|
|
|
# Breite: der breitere von Text und Liste, plus Rahmen
|
|
if [ "$maxw" -gt "$itemw" ]; then UI_W=$(( maxw + 10 )); else UI_W=$(( itemw + 10 )); fi
|
|
if [ "$UI_W" -lt 60 ]; then UI_W=60; fi
|
|
if [ "$UI_W" -gt $(( UI_COLS - 4 )) ]; then UI_W=$(( UI_COLS - 4 )); fi
|
|
|
|
# Listenhoehe: moeglichst alle Eintraege zeigen, ohne aus dem Bild zu laufen
|
|
local avail=$(( UI_ROWS - tl - 9 ))
|
|
if [ "$avail" -lt 3 ]; then avail=3; fi
|
|
if [ "$n" -lt "$avail" ]; then UI_MH="$n"; else UI_MH="$avail"; fi
|
|
if [ "$UI_MH" -lt 1 ]; then UI_MH=1; fi
|
|
|
|
UI_H=$(( tl + UI_MH + 8 ))
|
|
if [ "$UI_H" -gt $(( UI_ROWS - 2 )) ]; then UI_H=$(( UI_ROWS - 2 )); fi
|
|
if [ "$UI_H" -lt 10 ]; then UI_H=10; fi
|
|
return 0
|
|
}
|
|
|
|
# Hoehe fuer Fenster ohne Liste (Meldung, Abfrage, Eingabe)
|
|
ui_calc_text() { # text zusatzzeilen
|
|
local text="$1" extra="${2:-7}"
|
|
ui_term_size
|
|
local tl maxw
|
|
tl="$(printf '%s\n' "$text" | wc -l)"
|
|
maxw="$(printf '%s\n' "$text" | awk '{ if (length($0) > m) m = length($0) } END { print m+0 }')"
|
|
UI_W=$(( maxw + 10 ))
|
|
if [ "$UI_W" -lt 60 ]; then UI_W=60; fi
|
|
if [ "$UI_W" -gt $(( UI_COLS - 4 )) ]; then UI_W=$(( UI_COLS - 4 )); fi
|
|
UI_H=$(( tl + extra ))
|
|
if [ "$UI_H" -gt $(( UI_ROWS - 2 )) ]; then UI_H=$(( UI_ROWS - 2 )); fi
|
|
if [ "$UI_H" -lt 8 ]; then UI_H=8; fi
|
|
return 0
|
|
}
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Rahmenzeichen in PuTTY & Co.
|
|
#
|
|
# Ohne UTF-8-Locale zeichnen dialog/whiptail die Rahmen ueber die alte
|
|
# VT100-Umschaltung "ESC(0". Terminals, die im UTF-8-Modus laufen -
|
|
# PuTTY in der Vorgabe -, ignorieren diese Umschaltung und zeigen
|
|
# stattdessen die rohen Buchstaben q, x, l, m.
|
|
#
|
|
# Nachgemessen: bei LC_ALL=C schickt whiptail eine ESC(0-Umschaltung und
|
|
# 91 rohe "q"; mit UTF-8-Locale keine einzige. Es genuegt, LC_CTYPE zu
|
|
# setzen - Sortierung und Zahlenformat bleiben damit unberuehrt.
|
|
# Zwingt ncurses zu echten Unicode-Rahmenzeichen.
|
|
#
|
|
# Auch mit UTF-8-Locale benutzt ncurses die VT100-Umschaltung ESC(0,
|
|
# solange das terminfo sie anbietet (bei TERM=xterm der Fall). Terminals
|
|
# im UTF-8-Modus - PuTTY - machen die nicht mit und zeigen q, x, l, m.
|
|
# Gemessen mit dialog unter LANG=C.UTF-8, TERM=xterm:
|
|
# ohne Schalter : 38 ESC(0, 0 Rahmenbytes, 182 rohe q
|
|
# mit Schalter : 0 ESC(0, 220 Rahmenbytes, 0 rohe q
|
|
# whiptail (slang) braucht ihn nicht, es zeichnet bei UTF-8-Locale schon
|
|
# von sich aus Unicode.
|
|
ui_use_unicode_acs() {
|
|
# CMK_TUI_ACS=1 erzwingt umgekehrt das alte Verhalten - fuer
|
|
# Terminals, die ESC(0 koennen, aber kein UTF-8.
|
|
if [ -n "${CMK_TUI_ACS:-}" ]; then
|
|
unset NCURSES_NO_UTF8_ACS 2>/dev/null || true
|
|
return 0
|
|
fi
|
|
export NCURSES_NO_UTF8_ACS=1
|
|
return 0
|
|
}
|
|
|
|
ui_fix_locale() {
|
|
[ -n "${UI_LOCALE_DONE:-}" ] && return 0
|
|
UI_LOCALE_DONE=1
|
|
UI_LOCALE_NOTE=""
|
|
case "${LC_ALL:-${LC_CTYPE:-${LANG:-}}}" in
|
|
*UTF-8*|*utf-8*|*UTF8*|*utf8*) ui_use_unicode_acs; return 0 ;;
|
|
esac
|
|
local l
|
|
for l in C.UTF-8 C.utf8 en_US.UTF-8 en_US.utf8 de_DE.UTF-8 de_DE.utf8; do
|
|
if ( set +o pipefail; locale -a 2>/dev/null | grep -qix "$l" ); then
|
|
# LC_ALL sticht jede einzelne Kategorie aus - solange es auf
|
|
# einem Nicht-UTF-8-Wert steht, bliebe LC_CTYPE wirkungslos.
|
|
if [ -n "${LC_ALL:-}" ]; then unset LC_ALL; fi
|
|
export LC_CTYPE="$l"
|
|
UI_LOCALE_NOTE="LC_CTYPE auf $l gesetzt (war nicht UTF-8)"
|
|
ui_use_unicode_acs
|
|
return 0
|
|
fi
|
|
done
|
|
# Keine UTF-8-Locale vorhanden - dann bleibt nur ASCII
|
|
UI_ASCII=1
|
|
UI_LOCALE_NOTE="keine UTF-8-Locale gefunden - einfache Rahmen"
|
|
return 1
|
|
}
|
|
|
|
# Zusaetzliche Argumente, wenn nur ASCII-Rahmen moeglich sind
|
|
ui_ascii_args() {
|
|
UI_EXTRA=()
|
|
case "${CMK_TUI_ASCII:-}${UI_ASCII:-}" in
|
|
'') return 0 ;;
|
|
esac
|
|
if [ "${UI_BIN:-}" = dialog ] && dialog --ascii-lines --print-version >/dev/null 2>&1; then
|
|
UI_EXTRA=(--ascii-lines)
|
|
elif [ -z "${UI_ASCII_WARNED:-}" ]; then
|
|
UI_ASCII_WARNED=1
|
|
warn "Einfache Rahmen kann nur 'dialog' zeichnen - 'whiptail' kennt
|
|
die Option nicht. Installieren mit:
|
|
dnf install -y dialog (RHEL/Rocky/Alma)
|
|
apt-get install -y dialog (Debian/Ubuntu)"
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
ui_init() {
|
|
ui_fix_locale || true
|
|
|
|
# Geometrie IMMER setzen - sonst laeuft das Skript unter "set -u" auf
|
|
# eine unbound variable, wenn UI_BIN von aussen vorgegeben wurde.
|
|
UI_H="${UI_H:-22}"; UI_W="${UI_W:-78}"; UI_MH="${UI_MH:-13}"
|
|
|
|
if [ -z "${UI_BIN:-}" ]; then
|
|
if command -v dialog >/dev/null 2>&1; then UI_BIN=dialog
|
|
elif command -v whiptail >/dev/null 2>&1; then UI_BIN=whiptail
|
|
else UI_BIN=""; fi
|
|
export DIALOGRC="${DIALOGRC:-}"
|
|
fi
|
|
|
|
# Einfache Rahmen koennen nur mit dialog gezeichnet werden. Ist das
|
|
# gewuenscht und dialog vorhanden, wird es dafuer vorgezogen.
|
|
if [ -n "${CMK_TUI_ASCII:-}${UI_ASCII:-}" ] && command -v dialog >/dev/null 2>&1; then
|
|
UI_BIN=dialog
|
|
fi
|
|
ui_ascii_args
|
|
}
|
|
|
|
ui_available() { ui_init; [ -n "$UI_BIN" ]; }
|
|
|
|
ui_require() {
|
|
ui_available || die "Fuer die TUI wird 'dialog' oder 'whiptail' benoetigt.
|
|
Debian/Ubuntu : apt-get install -y dialog
|
|
RHEL/Rocky : dnf install -y dialog"
|
|
UI_BACKTITLE="Checkmk Stack Manager - Runtime: $RUNTIME"
|
|
}
|
|
|
|
# =====================================================================
|
|
# Alle Dialoge laufen ueber genau drei Engstellen.
|
|
#
|
|
# Damit stimmt die Fenstergroesse automatisch - auch fuer Dialoge, die
|
|
# spaeter dazukommen. NEUE FENSTER BITTE IMMER UEBER DIESE DREI BAUEN
|
|
# und niemals "$UI_BIN" direkt aufrufen; "cmk-manage.sh selftest"
|
|
# prueft das nach.
|
|
#
|
|
# _ui_list Listenfenster (menu, checklist, radiolist) -> ui_calc
|
|
# _ui_box Textfenster (msgbox, yesno, inputbox, ...) -> ui_calc_text
|
|
# _ui_file Dateianzeige (textbox) -> volles Bild
|
|
# =====================================================================
|
|
|
|
# _ui_list WIDGET TITEL TEXT FELDER_JE_EINTRAG EINTRAEGE...
|
|
_ui_list() {
|
|
ui_init
|
|
local widget="$1" title="$2" text="$3" per="$4"; shift 4
|
|
ui_calc "$text" "$per" "$@"
|
|
"$UI_BIN" ${UI_EXTRA[@]+"${UI_EXTRA[@]}"} --backtitle "$UI_BACKTITLE" --title "$title" \
|
|
"$widget" "$text" "$UI_H" "$UI_W" "$UI_MH" "$@" 3>&1 1>&2 2>&3
|
|
}
|
|
|
|
# _ui_box WIDGET TITEL TEXT ZUSATZZEILEN [weitere Argumente]
|
|
# Widgets ohne Rueckgabe (msgbox, yesno, infobox) liefern nur den Status.
|
|
_ui_box() {
|
|
ui_init
|
|
local widget="$1" title="$2" text="$3" extra="$4"; shift 4
|
|
ui_calc_text "$text" "$extra"
|
|
case "$widget" in
|
|
--inputbox|--passwordbox)
|
|
"$UI_BIN" ${UI_EXTRA[@]+"${UI_EXTRA[@]}"} --backtitle "$UI_BACKTITLE" --title "$title" \
|
|
"$widget" "$text" "$UI_H" "$UI_W" "$@" 3>&1 1>&2 2>&3 ;;
|
|
*)
|
|
"$UI_BIN" ${UI_EXTRA[@]+"${UI_EXTRA[@]}"} --backtitle "$UI_BACKTITLE" --title "$title" \
|
|
"$widget" "$text" "$UI_H" "$UI_W" "$@" ;;
|
|
esac
|
|
}
|
|
|
|
# _ui_file TITEL DATEI - nutzt das Terminal so weit wie moeglich aus
|
|
_ui_file() {
|
|
ui_init
|
|
ui_term_size
|
|
"$UI_BIN" ${UI_EXTRA[@]+"${UI_EXTRA[@]}"} --backtitle "$UI_BACKTITLE" --title "$1" \
|
|
--textbox "$2" $(( UI_ROWS - 2 )) $(( UI_COLS - 4 ))
|
|
}
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Die eigentlichen Dialoge - alle nur noch duenne Huellen
|
|
# ---------------------------------------------------------------------
|
|
ui_menu() { local t="$1" x="$2"; shift 2; _ui_list --menu "$t" "$x" 2 "$@"; }
|
|
ui_checklist() { local t="$1" x="$2"; shift 2; _ui_list --checklist "$t" "$x" 3 "$@"; }
|
|
ui_radiolist() { local t="$1" x="$2"; shift 2; _ui_list --radiolist "$t" "$x" 3 "$@"; }
|
|
|
|
ui_input() { _ui_box --inputbox "$1" "$2" 10 "${3:-}"; }
|
|
ui_password() { _ui_box --passwordbox "$1" "$2" 10; }
|
|
ui_msg() { _ui_box --msgbox "$1" "$2" 7; }
|
|
ui_yesno() { _ui_box --yesno "$1" "$2" 8; }
|
|
ui_info() { _ui_box --infobox "$1" "$2" 5; }
|
|
ui_text() { _ui_file "$1" "$2"; }
|
|
|
|
# Kommando ausfuehren, Ausgabe live in Datei, danach in Textbox anzeigen
|
|
ui_run() { # title cmd...
|
|
ui_init
|
|
local title="$1"; shift
|
|
local logf; logf="$(mktemp)"
|
|
ui_info "$title" "Bitte warten ...
|
|
|
|
$*"
|
|
if "$@" >"$logf" 2>&1; then
|
|
ui_text "$title - erfolgreich" "$logf"
|
|
rm -f "$logf"; return 0
|
|
else
|
|
ui_text "$title - FEHLGESCHLAGEN" "$logf"
|
|
rm -f "$logf"; return 1
|
|
fi
|
|
}
|
|
|
|
# ---------------------------------------------------------------------
|
|
# Dateiauswahl im Midnight-Commander-Stil (funktioniert auch mit whiptail)
|
|
# ---------------------------------------------------------------------
|
|
ui_pick_file() { # startdir [glob] -> Dateipfad auf stdout
|
|
ui_init
|
|
local dir="${1:-$PWD}" glob="${2:-*}" sel
|
|
dir="$(cd "$dir" 2>/dev/null && pwd || echo "$PWD")"
|
|
while :; do
|
|
local -a items=()
|
|
items+=(".." "<Verzeichnis hoeher>")
|
|
local d f
|
|
while IFS= read -r d; do
|
|
[ -n "$d" ] && items+=("$d/" "<DIR>")
|
|
done < <(find "$dir" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' 2>/dev/null | sort)
|
|
while IFS= read -r f; do
|
|
[ -n "$f" ] || continue
|
|
items+=("$f" "$(human_size "$(file_size "$dir/$f")")")
|
|
done < <(find "$dir" -mindepth 1 -maxdepth 1 -type f -name "$glob" -printf '%f\n' 2>/dev/null | sort)
|
|
|
|
sel="$(ui_menu "Datei waehlen" "Verzeichnis: $dir\nFilter: $glob" "${items[@]}")" || return 1
|
|
case "$sel" in
|
|
"..") dir="$(dirname "$dir")" ;;
|
|
*/) dir="$dir/${sel%/}" ;;
|
|
*) printf '%s\n' "$dir/$sel"; return 0 ;;
|
|
esac
|
|
done
|
|
}
|