diff --git a/check_inet_iface_curl.sh b/check_inet_iface_curl.sh index d415295..8ddb8e1 100755 --- a/check_inet_iface_curl.sh +++ b/check_inet_iface_curl.sh @@ -1,71 +1,252 @@ #!/usr/bin/env bash +set -euo pipefail -# Testet ein Interface mit curl gegen drei HTTP/HTTPS-Ziele. -# Es gilt erst als offline, wenn alle drei Ziele fehlschlagen. -# Erst der zweite Fehlversuch erzeugt eine Fehlermeldung. +# Testet ein Interface mit curl gegen mehrere HTTP/HTTPS-Ziele. +# Online, sobald mindestens EIN Ziel funktioniert. +# Offline erst, wenn ALLE Ziele fehlschlagen. +# Erst der zweite Offline-Fall in Folge erzeugt Meldung + ggf. Aktion. # -# Aufruf: check_inet_iface_curl.sh +# Usage: +# check_inet_iface_curl.sh [--service NAME | --reboot] [--state-dir DIR] +# [--max-time SEC] [-h|--help] -IFACE="$1" +IFACE="${1:-}" +shift || true -# Drei Ziele - alles HTTP/HTTPS, damit echte TCP-Verbindungen getestet werden. -TARGETS=("https://1.1.1.1" "https://8.8.8.8" "https://www.debian.org" "https//oopen.de") +# Ziele - echte TCP/HTTPS-Tests +TARGETS=( + "https://oopen.de" + "https://1.1.1.1" + "https://8.8.8.8" + "https://www.debian.org" +) -CURL_BIN="$(which curl)" -DATE_BIN="$(which date)" +CURL_BIN="$(command -v curl)" +DATE_BIN="$(command -v date)" STATE_DIR="/run/check_inet" -STATE_FILE="$STATE_DIR/$IFACE.fail" +MAX_TIME=8 +SERVICE="" +DO_REBOOT=0 -mkdir -p "$STATE_DIR" +# terminal detection +IS_TTY=0 +if [[ -t 1 || -t 2 ]]; then IS_TTY=1; fi -if [ -z "$IFACE" ]; then - echo "" - echo " Usage: $0 " >&2 - echo "" - exit 2 +# colors (TTY only) +if [[ $IS_TTY -eq 1 ]]; then + RED=$'\e[31m'; GREEN=$'\e[32m'; YELLOW=$'\e[33m'; BOLD=$'\e[1m'; RESET=$'\e[0m' +else + RED=""; GREEN=""; YELLOW=""; BOLD=""; RESET="" fi -# ---------------------------------------------------------- -# Funktion: Testet ein HTTP(S)-Ziel mit Interface-Bindung -# ---------------------------------------------------------- -test_target() { - local target="$1" +usage() { + cat <<'USAGE' - # -4 erzwingt IPv4, --interface bindet Source-Interface, - # --max-time 8 begrenzt Timeout, -s silent, -o /dev/null keine Ausgabe - $CURL_BIN -4 --interface "$IFACE" --max-time 8 -s -o /dev/null "$target" - return $? +check_inet_iface_curl.sh - prüft Konnektivität per curl über ein bestimmtes Interface. + +Usage: + check_inet_iface_curl.sh [--restart-service NAME | --reboot] [--state-dir DIR] + [--max-time SEC] [-h|--help] + +Optionen: + --restart-service NAME systemd-Service bei 2x OFFLINE in Folge neu starten + --reboot bei 2x OFFLINE in Folge System rebooten + --state-dir DIR State-Verzeichnis (Default: /run/check_inet) + --max-time SEC curl Timeout pro Ziel (Default: 8) + -h, --help Hilfe + +Verhalten: + - Im Terminal: grün "OK" bei Erfolg, rot "FAILED" bei 2x OFFLINE + Details. + - Als Cron (kein TTY): bei Erfolg keine Ausgabe; bei 2x OFFLINE Meldung auf stderr + (damit Cron eine Mail erzeugen kann). + +Exit-Codes: + 0 = online / OK (oder 1. Offline nur gemerkt) + 1 = 2x offline in Folge (Fehler + ggf. Aktion) + 3 = Aktion fehlgeschlagen + +USAGE } -# ---------------------------------------------------------- -# Tests durchführen -# ---------------------------------------------------------- +clean_up() { -FAILED=0 + # Perform program exit housekeeping + blank_line + exit $1 +} -for TG in "${TARGETS[@]}"; do - test_target "$TG" - if [ $? -ne 0 ]; then - FAILED=$((FAILED + 1)) - fi + +log_info() { + if [[ $IS_TTY -eq 1 ]] ; then + echo -e "$*"; + fi +} +log_err() { echo -e "$*" >&2; } + +die() { log_err "${RED}${BOLD}ERROR:${RESET} $*"; exit 64; } +blank_line() { + if [[ $IS_TTY -eq 1 ]] ; then + echo "" + fi +} + +# ----------------------------- +# args +# ----------------------------- +while [[ $# -gt 0 ]]; do + case "$1" in + --restart-service) + [[ $# -ge 2 ]] || die "--restart-service braucht ein Argument" + SERVICE="$2" + shift 2 + ;; + --reboot) + DO_REBOOT=1 + shift + ;; + --state-dir) + [[ $# -ge 2 ]] || die "--state-dir braucht ein Argument" + STATE_DIR="$2" + shift 2 + ;; + --max-time) + [[ $# -ge 2 ]] || die "--max-time braucht ein Argument" + MAX_TIME="$2" + shift 2 + ;; + -h|--help) + usage + clean_up 0 + ;; + *) + die "Unbekannte Option: $1 (verwende -h)" + ;; + esac done -# Wenn mindestens EIN Ziel funktioniert → online -if [ $FAILED -lt 3 ]; then - [ -f "$STATE_FILE" ] && rm -f "$STATE_FILE" - exit 0 +if [[ -z "$IFACE" ]]; then + usage >&2 + clean_up 2 fi -# Wenn ALLE drei fehlschlagen → offline -if [ -f "$STATE_FILE" ]; then - TS="$($DATE_BIN '+%F %T')" - echo "" - echo "$TS - Interface $IFACE: TCP/HTTP nicht erreichbar (curl-Test; Targets: ${TARGETS[*]})!" >&2 - echo "" - exit 1 -else - # Erster Fehlschlag — noch keine Meldung - touch "$STATE_FILE" - exit 0 +if [[ -n "$SERVICE" && $DO_REBOOT -eq 1 ]]; then + die "--restart-service und --reboot schließen sich aus" fi + +STATE_FILE="$STATE_DIR/$IFACE.fail" +mkdir -p "$STATE_DIR" + +# lock gegen parallele cron-runs +LOCK_FD=9 +LOCK_FILE="$STATE_FILE.lock" +exec {LOCK_FD}>"$LOCK_FILE" || true +if command -v flock >/dev/null 2>&1; then + flock -n "$LOCK_FD" || clean_up 0 +fi + +# ------------------------------------------ +# Funktion: Testet ein HTTP(S)-Ziel +# ------------------------------------------ +test_target() { + local target="$1" + # -4 erzwingt IPv4, --interface bindet Source-Interface, + # --max-time begrenzt Timeout, -s silent, -o /dev/null keine Ausgabe + "$CURL_BIN" -4 --interface "$IFACE" --max-time "$MAX_TIME" -s -o /dev/null "$target" +} + +# ------------------------------------------ +# Tests +# ------------------------------------------ +TOTAL="${#TARGETS[@]}" +FAILED=0 +blank_line || true +log_info " Interface: \033[17G$IFACE" +log_info " Targets (${TOTAL}):\033[17G${TARGETS[*]}" +log_info " curl:\033[17G$CURL_BIN max-time: ${MAX_TIME}s" +blank_line || true + +for TG in "${TARGETS[@]}"; do + if test_target "$TG"; then + log_info " ${GREEN}OK${RESET} $TG" + break; + else + FAILED=$((FAILED + 1)) + log_info " ${YELLOW}FAIL${RESET} $TG" + fi +done + +# Wenn mindestens EIN Ziel funktioniert -> online +if [[ $FAILED -lt $TOTAL ]]; then + [[ -f "$STATE_FILE" ]] && rm -f "$STATE_FILE" + if [[ $IS_TTY -eq 1 ]]; then + echo "" + echo -e " ${GREEN}${BOLD}OK${RESET} - Interface ${IFACE}: mindestens ein Ziel erreichbar." + fi + clean_up 0 +fi + +# Alle Ziele fail -> offline +# Erst beim zweiten Mal melden/handeln +TS="$("$DATE_BIN" '+%F %T')" +if [[ -f "$STATE_FILE" ]]; then + if [[ $IS_TTY -eq 1 ]]; then + log_err "" + log_err " ${RED}${BOLD}FAILED${RESET} - ${TS} - Interface ${IFACE}: TCP/HTTP(S) Verbindungen nicht erreichbar! + (curl; Targets: ${TARGETS[*]})" + log_err "" + else + log_err "" + log_err " ${TS} - Interface ${IFACE}: TCP/HTTP(S) Verbindungen nicht erreichbar!" + fi + + # Aktion + if [[ -n "$SERVICE" ]]; then + + log_info " ${YELLOW}Restart Service ${BOLD}$SERVICE${RESET}" + log_info "" + + if systemctl restart "$SERVICE" >/dev/null 2>&1; then + if [[ $IS_TTY -eq 1 ]] ; then + log_err " ${GREEN}Service neugestartet:${RESET} $SERVICE" + else + log_err " ${TS} - Service neugestartet: $SERVICE" + log_err "" + fi + clean_up 1 + else + if [[ $IS_TTY -eq 1 ]] ; then + log_err " ${RED}Service-Restart fehlgeschlagen:${RESET} $SERVICE" + else + log_err "" + log_err " ${TS} - Service-Restart fehlgeschlagen: $SERVICE" + log_err "" + fi + clean_up 3 + fi + elif [[ $DO_REBOOT -eq 1 ]]; then + if [[ $IS_TTY -eq 1 ]]; then + log_err " ${YELLOW}${BOLD}Resboot machine NOW!${RESET}" + log_err "" + else + log_err " ${TS} - Resboot machine NOW!" + log_err "" + fi + + /sbin/reboot >/dev/null 2>&1 || { log_err "${RED}Reboot fehlgeschlagen.${RESET}"; clean_up 3; } + clean_up 1 + fi + + #clean_up 1 +else + # Erster Offline-Fall -> noch keine Meldung + touch "$STATE_FILE" + if [[ $IS_TTY -eq 1 ]]; then + log_info "" + log_info " ${YELLOW}${BOLD}WARN${RESET} - Interface ${IFACE} offline (das erste mal - noch keine Aktion/Meldung)." + else + log_err " ${TS} - Interface ${IFACE} offline (first time - no action started)" + fi + clean_up 0 +fi + diff --git a/check_inet_iface_ping.sh b/check_inet_iface_ping.sh index 1be782a..2e5dd16 100755 --- a/check_inet_iface_ping.sh +++ b/check_inet_iface_ping.sh @@ -1,66 +1,251 @@ #!/usr/bin/env bash -# Prüft ein Interface gegen drei Internet-Ziele. -# Es wird nur dann ein Fehler gemeldet, wenn alle drei Ziele nicht erreichbar sind. -# Zudem erfolgt die Meldung erst beim zweiten aufeinanderfolgenden Offline-Test. +set -euo pipefail + +# Testet ein Interface mit curl gegen mehrere HTTP/HTTPS-Ziele. +# Online, sobald mindestens EIN Ziel funktioniert. +# Offline erst, wenn ALLE Ziele fehlschlagen. +# Erst der zweite Offline-Fall in Folge erzeugt Meldung + ggf. Aktion. # -# Aufruf: check_inet_iface.sh +# Usage: +# check_inet_iface_curl.sh [--service NAME | --reboot] [--state-dir DIR] +# [--max-time SEC] [-h|--help] -IFACE="$1" +IFACE="${1:-}" +shift || true -# Drei Ziele, beliebig anpassbar (IPs oder Hostnames) -TARGETS=("1.1.1.1" "8.8.8.8" "www.debian.org" "oopen.de") +# Ziele - echte TCP/HTTPS-Tests +TARGETS=( + "oopen.de" + "1.1.1.1" + "8.8.8.8" + "www.debian.org" +) -PING_BIN="/bin/ping" -TIMEOUT_BIN="/usr/bin/timeout" -DATE_BIN="/bin/date" +PING_BIN="$(command -v ping)" +DATE_BIN="$(command -v date)" +TIMEOUT_BIN="$(command -v timeout)" -STATE_DIR="/var/run/check_inet" -STATE_FILE="$STATE_DIR/$IFACE.fail" +STATE_DIR="/run/check_inet" +MAX_TIME=8 +SERVICE="" +DO_REBOOT=0 -mkdir -p "$STATE_DIR" +# terminal detection +IS_TTY=0 +if [[ -t 1 || -t 2 ]]; then IS_TTY=1; fi -if [ -z "$IFACE" ]; then - echo "Usage: $0 " >&2 - exit 2 +# colors (TTY only) +if [[ $IS_TTY -eq 1 ]]; then + RED=$'\e[31m'; GREEN=$'\e[32m'; YELLOW=$'\e[33m'; BOLD=$'\e[1m'; RESET=$'\e[0m' +else + RED=""; GREEN=""; YELLOW=""; BOLD=""; RESET="" fi -# ---------------------------------------------------------- -# Funktion: Teste ein Ziel über das Interface -# ---------------------------------------------------------- -test_target() { - local target="$1" - $TIMEOUT_BIN 10 $PING_BIN -I "$IFACE" -c 2 -W 2 "$target" >/dev/null 2>&1 - return $? +usage() { + cat <<'USAGE' + +check_inet_iface_curl.sh - prüft Konnektivität per curl über ein bestimmtes Interface. + +Usage: + check_inet_iface_curl.sh [--restart-service NAME | --reboot] [--state-dir DIR] + [--max-time SEC] [-h|--help] + +Optionen: + --restart-service NAME systemd-Service bei 2x OFFLINE in Folge neu starten + --reboot bei 2x OFFLINE in Folge System rebooten + --state-dir DIR State-Verzeichnis (Default: /run/check_inet) + --max-time SEC curl Timeout pro Ziel (Default: 8) + -h, --help Hilfe + +Verhalten: + - Im Terminal: grün "OK" bei Erfolg, rot "FAILED" bei 2x OFFLINE + Details. + - Als Cron (kein TTY): bei Erfolg keine Ausgabe; bei 2x OFFLINE Meldung auf stderr + (damit Cron eine Mail erzeugen kann). + +Exit-Codes: + 0 = online / OK (oder 1. Offline nur gemerkt) + 1 = 2x offline in Folge (Fehler + ggf. Aktion) + 3 = Aktion fehlgeschlagen + +USAGE } -# ---------------------------------------------------------- -# Alle drei Ziele testen -# ---------------------------------------------------------- +clean_up() { -FAILED=0 + # Perform program exit housekeeping + blank_line + exit $1 +} -for TG in "${TARGETS[@]}"; do - test_target "$TG" - if [ $? -ne 0 ]; then - FAILED=$((FAILED + 1)) - fi + +log_info() { + if [[ $IS_TTY -eq 1 ]] ; then + echo -e "$*"; + fi +} +log_err() { echo -e "$*" >&2; } + +die() { log_err "${RED}${BOLD}ERROR:${RESET} $*"; exit 64; } +blank_line() { + if [[ $IS_TTY -eq 1 ]] ; then + echo "" + fi +} + +# ----------------------------- +# args +# ----------------------------- +while [[ $# -gt 0 ]]; do + case "$1" in + --restart-service) + [[ $# -ge 2 ]] || die "--restart-service braucht ein Argument" + SERVICE="$2" + shift 2 + ;; + --reboot) + DO_REBOOT=1 + shift + ;; + --state-dir) + [[ $# -ge 2 ]] || die "--state-dir braucht ein Argument" + STATE_DIR="$2" + shift 2 + ;; + --max-time) + [[ $# -ge 2 ]] || die "--max-time braucht ein Argument" + MAX_TIME="$2" + shift 2 + ;; + -h|--help) + usage + clean_up 0 + ;; + *) + die "Unbekannte Option: $1 (verwende -h)" + ;; + esac done -# Wenn mindestens ein Ziel erreichbar ist → online -if [ $FAILED -lt 3 ]; then - # Erfolg: State zurücksetzen - [ -f "$STATE_FILE" ] && rm -f "$STATE_FILE" - exit 0 +if [[ -z "$IFACE" ]]; then + usage >&2 + clean_up 2 fi -# Wenn alle Ziele fehlschlagen → offline -if [ -f "$STATE_FILE" ]; then - # Zweiter Fehlschlag → Meldung - TS="$($DATE_BIN '+%F %T')" - echo "$TS - Interface $IFACE: Internet nicht erreichbar (Targets: ${TARGETS[*]})!" >&2 - exit 1 -else - # Erster Fehlschlag - Still, aber State setzen - touch "$STATE_FILE" - exit 0 +if [[ -n "$SERVICE" && $DO_REBOOT -eq 1 ]]; then + die "--restart-service und --reboot schließen sich aus" fi + +STATE_FILE="$STATE_DIR/$IFACE.fail" +mkdir -p "$STATE_DIR" + +# lock gegen parallele cron-runs +LOCK_FD=9 +LOCK_FILE="$STATE_FILE.lock" +exec {LOCK_FD}>"$LOCK_FILE" || true +if command -v flock >/dev/null 2>&1; then + flock -n "$LOCK_FD" || clean_up 0 +fi + +# ------------------------------------------ +# Funktion: Testet ein HTTP(S)-Ziel +# ------------------------------------------ +test_target() { + local target="$1" + $TIMEOUT_BIN $MAX_TIME $PING_BIN -I "$IFACE" -c 2 -W 2 "$target" >/dev/null 2>&1 +} + +# ------------------------------------------ +# Tests +# ------------------------------------------ +TOTAL="${#TARGETS[@]}" +FAILED=0 +blank_line || true +log_info " Interface: \033[17G$IFACE" +log_info " Targets (${TOTAL}):\033[17G${TARGETS[*]}" +log_info " ping:\033[17G$PING_BIN timeout: ${MAX_TIME}s" +blank_line || true + +for TG in "${TARGETS[@]}"; do + if test_target "$TG"; then + log_info " ${GREEN}OK${RESET} $TG" + break; + else + FAILED=$((FAILED + 1)) + log_info " ${YELLOW}FAIL${RESET} $TG" + fi +done + +# Wenn mindestens EIN Ziel funktioniert -> online +if [[ $FAILED -lt $TOTAL ]]; then + [[ -f "$STATE_FILE" ]] && rm -f "$STATE_FILE" + if [[ $IS_TTY -eq 1 ]]; then + echo "" + echo -e " ${GREEN}${BOLD}OK${RESET} - Interface ${IFACE}: mindestens ein Ziel erreichbar." + fi + clean_up 0 +fi + +# Alle Ziele fail -> offline +# Erst beim zweiten Mal melden/handeln +TS="$("$DATE_BIN" '+%F %T')" +if [[ -f "$STATE_FILE" ]]; then + if [[ $IS_TTY -eq 1 ]]; then + log_err "" + log_err " ${RED}${BOLD}FAILED${RESET} - ${TS} - Interface ${IFACE}: TCP/HTTP(S) Verbindungen nicht erreichbar! + (curl; Targets: ${TARGETS[*]})" + log_err "" + else + log_err "" + log_err " ${TS} - Interface ${IFACE}: PING Verbindungen nicht erreichbar!" + fi + + # Aktion + if [[ -n "$SERVICE" ]]; then + + log_info " ${YELLOW}Restart Service ${BOLD}$SERVICE${RESET}" + log_info "" + + if systemctl restart "$SERVICE" >/dev/null 2>&1; then + if [[ $IS_TTY -eq 1 ]] ; then + log_err " ${GREEN}Service neugestartet:${RESET} $SERVICE" + else + log_err " ${TS} - Service neugestartet: $SERVICE" + log_err "" + fi + clean_up 1 + else + if [[ $IS_TTY -eq 1 ]] ; then + log_err " ${RED}Service-Restart fehlgeschlagen:${RESET} $SERVICE" + else + log_err "" + log_err " ${TS} - Service-Restart fehlgeschlagen: $SERVICE" + log_err "" + fi + clean_up 3 + fi + elif [[ $DO_REBOOT -eq 1 ]]; then + if [[ $IS_TTY -eq 1 ]]; then + log_err " ${YELLOW}${BOLD}Resboot machine NOW!${RESET}" + log_err "" + else + log_err " ${TS} - Resboot machine NOW!" + log_err "" + fi + + /sbin/reboot >/dev/null 2>&1 || { log_err "${RED}Reboot fehlgeschlagen.${RESET}"; clean_up 3; } + clean_up 1 + fi + + #clean_up 1 +else + # Erster Offline-Fall -> noch keine Meldung + touch "$STATE_FILE" + if [[ $IS_TTY -eq 1 ]]; then + log_info "" + log_info " ${YELLOW}${BOLD}WARN${RESET} - Interface ${IFACE} offline (das erste mal - noch keine Aktion/Meldung)." + else + log_err " ${TS} - Interface ${IFACE} offline (first time - no action started)" + fi + clean_up 0 +fi +