Files
esn-support-bash-snippets/legacy/checkms/checkms.sh
T

253 lines
6.9 KiB
Bash
Executable File

#!/usr/bin/env bash
# checkms - Analyse von MS-Fehlercodes im Exim-Log + Code-Management
# Maintainer: ljp
set -euo pipefail
# --- Defaults ---
DATE="$(date +%Y-%m-%d)"
MINUTES=0
HOURS=0
CONF_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/checkms"
ADD_CODES=()
DEL_INDEXES=()
SET_USER=""
SHOW_CONF=0
# --- Funktionen ---
usage() {
cat <<USAGE
Usage: $0 [OPTIONS] [YYYY-MM-DD]
Analyse von MS-Fehlercodes im Exim-Log und Pflege der Codes.
Analyse-Optionen:
--minutes N Analysiert nur die letzten N Minuten
--hours N Analysiert nur die letzten N Stunden
--confdir PATH Pfad zum Konfigurationsordner (Default: ~/.config/checkms)
Konfig-Optionen:
--add-code REGEX Fügt einen Code/Regex zur Config hinzu (mehrfach möglich)
--del-code ID Löscht Code mit ID (siehe --show-config)
--set-user USER Setzt den SSH-User in der Config
--show-config Zeigt die aktuelle Config (JSON + tabellarisch) und beendet
--help Diese Hilfe
USAGE
}
init_conf() {
mkdir -p "$CONF_DIR"
read -rp "Bitte SSH-User für den Logger angeben: " SSHUSER
cat >"$CONF_FILE" <<JSON
{
"ssh_user": "$SSHUSER",
"codes": [
"S3150",
"S775",
"S7771[0-9]*"
]
}
JSON
echo "Konfigurationsdatei erstellt: $CONF_FILE"
}
require_jq_write() {
if ! command -v jq >/dev/null 2>&1; then
echo "Fehler: Für --add-code/--del-code/--set-user wird 'jq' benötigt." >&2
exit 1
fi
}
show_config() {
if [[ ! -f "$CONF_FILE" ]]; then
echo "Keine Config vorhanden. Sie wird beim nächsten Run erstellt."
exit 0
fi
# Roh-JSON
if command -v jq >/dev/null 2>&1; then
echo "RAW ($CONF_FILE):"
jq . "$CONF_FILE"
else
echo "RAW ($CONF_FILE):"
cat "$CONF_FILE"
fi
echo
# Tabellarisch
if command -v jq >/dev/null 2>&1; then
SSH_USER_SHOW="$(jq -r '.ssh_user' "$CONF_FILE")"
mapfile -t CODES_SHOW < <(jq -r '.codes[]' "$CONF_FILE")
else
SSH_USER_SHOW="$(grep -o '"ssh_user":[^,}]*' "$CONF_FILE" | cut -d'"' -f4)"
CODES_LINE="$(sed -n 's/.*"codes":[[]\(.*\)[]].*/\1/p' "$CONF_FILE")"
IFS=',' read -r -a CODES_SHOW <<<"$(echo "$CODES_LINE" | tr -d ' \t"')"
fi
echo "TABELLARISCHE ANSICHT:"
printf "%-12s %s\n" "SSH_USER" "${SSH_USER_SHOW:-<unset>}"
echo
printf "%-6s %s\n" "ID" "CODE"
printf "%-6s %s\n" "----" "-----------------------"
id=0
for code in "${CODES_SHOW[@]}"; do
printf "%-6s %s\n" "$id" "$code"
id=$((id+1))
done
exit 0
}
apply_add_codes() {
[[ ${#ADD_CODES[@]} -eq 0 ]] && return 0
require_jq_write
local tmp; tmp="$(mktemp)"
cp "$CONF_FILE" "$tmp"
for code in "${ADD_CODES[@]}"; do
jq --arg code "$code" '
(.codes | any(. == $code)) as $exists
| if $exists then . else (.codes += [$code]) end
' "$tmp" > "${tmp}.new"
mv "${tmp}.new" "$tmp"
done
mv "$tmp" "$CONF_FILE"
}
apply_del_indexes() {
[[ ${#DEL_INDEXES[@]} -eq 0 ]] && return 0
require_jq_write
local tmp; tmp="$(mktemp)"
cp "$CONF_FILE" "$tmp"
IFS=$'\n' read -r -d '' -a sorted <<<"$(printf "%s\n" "${DEL_INDEXES[@]}" | sort -rn; printf '\0')"
for idx in "${sorted[@]}"; do
[[ "$idx" =~ ^[0-9]+$ ]] || { echo "Warnung: ungültige ID '$idx'"; continue; }
jq --argjson i "$idx" '
if ($i >= 0 and $i < (.codes|length))
then .codes |= del(.[$i])
else .
end
' "$tmp" > "${tmp}.new"
mv "${tmp}.new" "$tmp"
done
mv "$tmp" "$CONF_FILE"
}
apply_set_user() {
[[ -z "$SET_USER" ]] && return 0
require_jq_write
local tmp; tmp="$(mktemp)"
jq --arg u "$SET_USER" '.ssh_user = $u' "$CONF_FILE" > "$tmp"
mv "$tmp" "$CONF_FILE"
}
# --- Argumente parsen ---
while [[ $# -gt 0 ]]; do
case "$1" in
--minutes) MINUTES="$2"; shift 2 ;;
--hours) HOURS="$2"; shift 2 ;;
--confdir) CONF_DIR="$2"; shift 2 ;;
--show-config) SHOW_CONF=1; shift ;;
--add-code) ADD_CODES+=("$2"); shift 2 ;;
--del-code) DEL_INDEXES+=("$2"); shift 2 ;;
--set-user) SET_USER="$2"; shift 2 ;;
--help) usage; exit 0 ;;
[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]) DATE="$1"; shift ;;
*) echo "Unbekannte Option: $1" >&2; usage; exit 1 ;;
esac
done
# --- Configpfade ---
CONF_FILE="${CONF_DIR}/checkms.conf"
[[ -f "$CONF_FILE" ]] || init_conf
# --- Reine Konfig-Operationen (frühzeitiger Exit!) ---
if [[ $SHOW_CONF -eq 1 && ${#ADD_CODES[@]} -eq 0 && ${#DEL_INDEXES[@]} -eq 0 && -z "$SET_USER" ]]; then
show_config
fi
# Wenn Codes/User geändert werden sollen, erst anwenden …
if [[ ${#ADD_CODES[@]} -gt 0 || ${#DEL_INDEXES[@]} -gt 0 || -n "$SET_USER" ]]; then
apply_add_codes
apply_del_indexes
apply_set_user
# Wenn nur Konfig bearbeitet wurde (keine Analyse-Flags/Datum), hier enden
if [[ $MINUTES -eq 0 && $HOURS -eq 0 && "$DATE" == "$(date +%Y-%m-%d)" ]]; then
# Optional: danach anzeigen
[[ $SHOW_CONF -eq 1 ]] && show_config
exit 0
fi
fi
# --- Config laden für Analyse ---
if command -v jq >/dev/null 2>&1; then
SSH_USER="$(jq -r '.ssh_user' "$CONF_FILE")"
CODES_REGEX="$(jq -r '.codes[]' "$CONF_FILE" | paste -sd'|' -)"
else
SSH_USER="$(grep -o '"ssh_user":[^,}]*' "$CONF_FILE" | cut -d'"' -f4)"
CODES_REGEX="$(
sed -n 's/.*"codes":[[]\(.*\)[]].*/\1/p' "$CONF_FILE" \
| tr -d ' \t"' | tr ',' '|'
)"
fi
[[ -n "${CODES_REGEX// }" ]] || CODES_REGEX="S3150|S775|S7771[0-9]*"
year="${DATE:0:4}"
month="${DATE:5:2}"
LOGFILE="${DATE}.log"
# --- Remote-Kommando ---
if type go >/dev/null 2>&1; then
REMOTE="go logger"
else
REMOTE="ssh ${SSH_USER}@logger.in.nessus.at"
fi
# --- Analyse (remote) ---
$REMOTE bash -s <<EOF
set -euo pipefail
cd /var/log/easyname/$year/$month
if [ -f "$LOGFILE" ]; then SRC="cat \"$LOGFILE\""
elif [ -f "$LOGFILE.gz" ]; then SRC="zcat \"$LOGFILE.gz\""
else
echo "Logfile $LOGFILE(.gz) nicht gefunden." >&2
exit 1
fi
echo "Analysiere $LOGFILE"
echo
printf "%-8s %-16s %-8s\n" "COUNT" "IP" "CODE"
printf "%-8s %-16s %-8s\n" "-----" "----------------" "------"
TF_CMD=""
if [ $MINUTES -gt 0 ]; then
START_TS=\$(date -d "-$MINUTES minutes" +"%b %_d %H:%M")
echo "Zeitraum: letzte $MINUTES Minuten (ab \$START_TS)"
TF_CMD="awk -v ts=\"\$START_TS\" '\$0 ~ ts,0'"
elif [ $HOURS -gt 0 ]; then
START_TS=\$(date -d "-$HOURS hours" +"%b %_d %H")
echo "Zeitraum: letzte $HOURS Stunden (ab \$START_TS)"
TF_CMD="awk -v ts=\"\$START_TS\" '\$0 ~ ts,0'"
fi
eval \$SRC \${TF_CMD:+| \$TF_CMD} \
| grep -E 'outlook|hotmail' \
| grep -E "$CODES_REGEX" \
| awk '
{
code="NA"; ip="NA";
for (i=1;i<=NF;i++) {
if (\$i ~ /\(S[0-9]+\)/) { t=\$i; gsub(/[()]/,"",t); code=t }
else if (\$i ~ /^S[0-9]+/) { code=\$i }
if (\$i ~ /^\[[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\]$/) {
ip=\$i; gsub(/[\[\]]/,"",ip)
}
}
print code, ip
}
' \
| sort | uniq -c | sort -nr \
| while read count code ip; do
printf "%-8s %-16s %-8s\n" "\$count" "\$ip" "\$code"
done
EOF