Enhance mailtrace script: add sender host and IP filters, improve progress display, and update usage instructions
This commit is contained in:
+116
-3
@@ -83,6 +83,8 @@ FROM_FILTER=""
|
||||
TO_FILTER=""
|
||||
QID_FILTER=""
|
||||
MSGID_FILTER=""
|
||||
SENDER_HOST_FILTER=""
|
||||
SENDER_IP_FILTER=""
|
||||
|
||||
SINCE_PREFIX=""
|
||||
UNTIL_PREFIX=""
|
||||
@@ -92,7 +94,7 @@ NO_NOTE=0 # suppress note lines in block output when TLS unknown
|
||||
|
||||
DEDUP=0
|
||||
DEDUP_PREFER="final" # final|amavis|first|last
|
||||
FINAL_ONLY=0
|
||||
FINAL_ONLY=1
|
||||
|
||||
SASL_ONLY=0
|
||||
NO_SASL=0
|
||||
@@ -100,6 +102,10 @@ INBOUND_ONLY=0
|
||||
OUTBOUND_ONLY=0
|
||||
|
||||
DEBUG=0
|
||||
SHOW_PROGRESS=0
|
||||
if [[ -t 2 ]]; then
|
||||
SHOW_PROGRESS=1
|
||||
fi
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
@@ -116,6 +122,8 @@ Filter:
|
||||
--to-addr <substr> match nur in to
|
||||
--qid <substr> match Queue-ID (Postfix Queue-ID / "qid")
|
||||
--message-id <substr> match Header Message-ID (cleanup: message-id=<...>)
|
||||
--sender-host <substr> match absendenden Client-Host (client/orig_client)
|
||||
--sender-ip <substr> match absendende Client-IP (clientip/orig_clientip; IPv4/IPv6)
|
||||
|
||||
Status-Filter:
|
||||
--success nur erfolgreiche Endzustellungen (status=success)
|
||||
@@ -130,7 +138,11 @@ AUTH / Richtung:
|
||||
Dedup (ein Eintrag pro Empfänger):
|
||||
--dedup dedupliziere pro (message-id,to). Fallback: (qid,to) falls msgid fehlt
|
||||
--dedup-prefer <mode> final|amavis|first|last (Default: final)
|
||||
--final-only nur endgültige Zustellung ausgeben (remote SMTP oder lokales LMTP), keine Amavis-Handoffs
|
||||
|
||||
Umfang:
|
||||
(default) nur endgültige Zustellungen ausgeben (remote SMTP oder lokales LMTP), keine Amavis-Handoffs
|
||||
--final-only wie Default; nur finale Zustellungen anzeigen
|
||||
--all-outcomes alle SMTP/LMTP-Outcomes anzeigen, inkl. Amavis-Handoffs
|
||||
|
||||
TLS/Notizen:
|
||||
--no-note im Blockformat keine Hinweiszeile ausgeben, wenn TLS nicht korrelierbar ist
|
||||
@@ -138,12 +150,38 @@ TLS/Notizen:
|
||||
Debug:
|
||||
--debug ergänzt Debug-Details zur Heuristik/Klassifikation
|
||||
|
||||
Fortschritt:
|
||||
Bei interaktivem stderr zeigt mailtrace.sh waehrend der Verarbeitung einen
|
||||
kleinen Spinner an. Der Spinner wird vor jeder Ausgabezeile entfernt.
|
||||
|
||||
Input / Zeitraum:
|
||||
--follow tail -F /var/log/mail.log (live)
|
||||
--all-logs /var/log/mail.log* inkl. .gz (historisch)
|
||||
--log-file <pfad> einzelne Logdatei (auch .gz); nicht kombinierbar mit --follow/--all-logs
|
||||
--since <prefix> Timestamp prefix-match (z.B. 2026-01-13T14:47)
|
||||
--until <prefix> stoppe sobald ts >= prefix (ISO8601 string compare)
|
||||
|
||||
Beispiele:
|
||||
# Finale Zustellungen eines absendenden Hosts (Default)
|
||||
mailtrace.sh --sender-host web0.warenform.de
|
||||
|
||||
# Finale Zustellungen einer IPv4-Adresse
|
||||
mailtrace.sh --sender-ip 83.223.85.164
|
||||
|
||||
# Finale Zustellungen einer IPv6-Adresse
|
||||
mailtrace.sh --sender-ip 2a01:30:0:13:286:96ff:fe4a:6eee
|
||||
|
||||
# Nur erfolgreiche finale Zustellungen dieses Hosts
|
||||
mailtrace.sh --sender-host web0.warenform.de --success
|
||||
|
||||
# Nur fehlgeschlagene Zustellungen einer IP in alten und rotierten Logs
|
||||
mailtrace.sh --all-logs --sender-ip 83.223.85.164 --fail
|
||||
|
||||
# Voller Verlauf inkl. Amavis-Handoffs und Reinject-Zustellungen
|
||||
mailtrace.sh --sender-host web0.warenform.de --all-outcomes
|
||||
|
||||
# Kompakte Ausgabe fuer einen Zeitraum
|
||||
mailtrace.sh --sender-host web0.warenform.de --since 2026-01-13T14 --one-line
|
||||
USAGE
|
||||
}
|
||||
|
||||
@@ -160,6 +198,8 @@ while [[ $# -gt 0 ]]; do
|
||||
--to-addr) TO_FILTER="${2:-}"; shift ;;
|
||||
--qid) QID_FILTER="${2:-}"; shift ;;
|
||||
--message-id) MSGID_FILTER="${2:-}"; shift ;;
|
||||
--sender-host) SENDER_HOST_FILTER="${2:-}"; shift ;;
|
||||
--sender-ip) SENDER_IP_FILTER="${2:-}"; shift ;;
|
||||
--since) SINCE_PREFIX="${2:-}"; shift ;;
|
||||
--until) UNTIL_PREFIX="${2:-}"; shift ;;
|
||||
--one-line) FORMAT="one-line" ;;
|
||||
@@ -168,6 +208,7 @@ while [[ $# -gt 0 ]]; do
|
||||
--dedup) DEDUP=1 ;;
|
||||
--dedup-prefer) DEDUP_PREFER="${2:-}"; shift ;;
|
||||
--final-only) FINAL_ONLY=1 ;;
|
||||
--all-outcomes) FINAL_ONLY=0 ;;
|
||||
--sasl-only) SASL_ONLY=1 ;;
|
||||
--no-sasl) NO_SASL=1 ;;
|
||||
--inbound-only) INBOUND_ONLY=1 ;;
|
||||
@@ -231,6 +272,8 @@ fi
|
||||
-v to_filter="$TO_FILTER" \
|
||||
-v qid_filter="$QID_FILTER" \
|
||||
-v msgid_filter="$MSGID_FILTER" \
|
||||
-v sender_host_filter="$SENDER_HOST_FILTER" \
|
||||
-v sender_ip_filter="$SENDER_IP_FILTER" \
|
||||
-v since_prefix="$SINCE_PREFIX" \
|
||||
-v until_prefix="$UNTIL_PREFIX" \
|
||||
-v out_format="$FORMAT" \
|
||||
@@ -243,7 +286,8 @@ fi
|
||||
-v no_sasl="$NO_SASL" \
|
||||
-v inbound_only="$INBOUND_ONLY" \
|
||||
-v outbound_only="$OUTBOUND_ONLY" \
|
||||
-v debug_mode="$DEBUG" '
|
||||
-v debug_mode="$DEBUG" \
|
||||
-v show_progress="$SHOW_PROGRESS" '
|
||||
###############################################################################
|
||||
# Helferfunktionen
|
||||
###############################################################################
|
||||
@@ -291,6 +335,7 @@ BEGIN {
|
||||
maxw = 0
|
||||
for (i=1; i in labels; i++) { w = length(labels[i] ":"); if (w > maxw) maxw = w }
|
||||
LABEL_W = maxw
|
||||
progress_tick()
|
||||
}
|
||||
function pl(label, value) { printf "%-*s %s\n", LABEL_W, label ":", value }
|
||||
|
||||
@@ -368,10 +413,67 @@ function is_inbound(sasl, orig_ip, src) {
|
||||
return 0
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# Fortschrittsanzeige auf stderr
|
||||
###############################################################################
|
||||
function progress_tick( now, ch) {
|
||||
if (show_progress != 1) return
|
||||
|
||||
now = systime()
|
||||
if (PROGRESS_LAST != "" && now - PROGRESS_LAST < 2) return
|
||||
|
||||
PROGRESS_LAST = now
|
||||
PROGRESS_IDX = (PROGRESS_IDX % 4) + 1
|
||||
ch = substr("|/-\\", PROGRESS_IDX, 1)
|
||||
printf "\r%s", ch > "/dev/stderr"
|
||||
fflush("/dev/stderr")
|
||||
PROGRESS_DRAWN = 1
|
||||
}
|
||||
|
||||
function progress_clear() {
|
||||
if (show_progress != 1 || PROGRESS_DRAWN != 1) return
|
||||
printf "\r \r" > "/dev/stderr"
|
||||
fflush("/dev/stderr")
|
||||
PROGRESS_DRAWN = 0
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# Ausgabe
|
||||
###############################################################################
|
||||
function count_record(k) {
|
||||
STAT_TOTAL++
|
||||
if (REC_status[k] == "success") STAT_SUCCESS++
|
||||
if (REC_status[k] == "failed") STAT_FAILED++
|
||||
}
|
||||
|
||||
function emit_stats( width) {
|
||||
progress_clear()
|
||||
|
||||
if (out_format == "json") {
|
||||
printf "{\"type\":\"stats\",\"total\":%d,\"success\":%d,\"failed\":%d}\n", STAT_TOTAL+0, STAT_SUCCESS+0, STAT_FAILED+0
|
||||
return
|
||||
}
|
||||
|
||||
if (out_format == "one-line") {
|
||||
printf "stats\ttotal=%d\tsuccess=%d\tfailed=%d\n", STAT_TOTAL+0, STAT_SUCCESS+0, STAT_FAILED+0
|
||||
return
|
||||
}
|
||||
|
||||
width = length(STAT_TOTAL+0)
|
||||
if (length(STAT_SUCCESS+0) > width) width = length(STAT_SUCCESS+0)
|
||||
if (length(STAT_FAILED+0) > width) width = length(STAT_FAILED+0)
|
||||
|
||||
print ""
|
||||
print "Statistics:"
|
||||
printf " total: %*d\n", width, STAT_TOTAL+0
|
||||
printf " success: %*d\n", width, STAT_SUCCESS+0
|
||||
printf " failed: %*d\n", width, STAT_FAILED+0
|
||||
}
|
||||
|
||||
function emit_record(k, tls_part) {
|
||||
progress_clear()
|
||||
count_record(k)
|
||||
|
||||
if (out_format == "one-line") {
|
||||
tls_part = ""
|
||||
if (REC_tls[k] != "" && REC_tls[k] != "-") {
|
||||
@@ -498,12 +600,15 @@ END {
|
||||
}
|
||||
for (i=1; i<=n; i++) emit_record(KEYS[i])
|
||||
}
|
||||
emit_stats()
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# Hauptparser: pro Logzeile State sammeln, bei Zustell-Outcome ausgeben
|
||||
###############################################################################
|
||||
{
|
||||
progress_tick()
|
||||
|
||||
ts = $1
|
||||
|
||||
# Zeitraumfilter (prefix match; ISO8601)
|
||||
@@ -631,6 +736,14 @@ END {
|
||||
orig_client = (mid != "" && EXTCLIENT_BY_MSGID[mid] ? EXTCLIENT_BY_MSGID[mid] : (ORIGCLIENT[qid]?ORIGCLIENT[qid]:"-"))
|
||||
orig_clientip = (mid != "" && EXTCLIENTIP_BY_MSGID[mid] ? EXTCLIENTIP_BY_MSGID[mid] : (ORIGCLIENTIP[qid]?ORIGCLIENTIP[qid]:"-"))
|
||||
|
||||
# Absender-Host/IP Filter: direkter client plus rekonstruierter Ursprung.
|
||||
if (sender_host_filter != "" &&
|
||||
index(client, sender_host_filter)==0 &&
|
||||
index(orig_client, sender_host_filter)==0) next
|
||||
if (sender_ip_filter != "" &&
|
||||
index(clientip, sender_ip_filter)==0 &&
|
||||
index(orig_clientip, sender_ip_filter)==0) next
|
||||
|
||||
# TLS: qid oder msgid-Vererbung
|
||||
tls = (TLS[qid] ? TLS[qid] : "")
|
||||
tls_proto = (TLS_P[qid] ? TLS_P[qid] : "")
|
||||
|
||||
Reference in New Issue
Block a user