413 lines
18 KiB
Bash
413 lines
18 KiB
Bash
#!/bin/bash
|
|
# Postfix Mail Summary Tool - PRODUCTION READY EDITION (v4.0.9)
|
|
|
|
set -euo pipefail
|
|
IFS=$'\n\t'
|
|
|
|
# ==============================================================================
|
|
# 1. HELP MENU (EXTENDED & DETAILED)
|
|
# ==============================================================================
|
|
show_help() {
|
|
cat << 'EOF'
|
|
Usage: mail-stats [OPTIONS]
|
|
|
|
FILTERING OPTIONS (Combine multiple filters to narrow down results):
|
|
-d, --date "MMM DD" Filter by exact date. Default is today.
|
|
Examples: -d "Sep 4" | -d "Oct 12"
|
|
Special: -d "" (Empty string searches ALL available dates)
|
|
--time "HH:MM" Filter by exact time or whole hour.
|
|
Examples: --time "14:30" (exact minute) | --time "14:" (entire hour)
|
|
-f, --from STRING Filter by sender email address or domain.
|
|
Examples: -f "admin@domain.com" | -f "paypal"
|
|
-t, --to STRING Filter by recipient email address or domain.
|
|
Examples: -t "user@local.sk" | -t "gmail.com"
|
|
-a, --ip STRING Filter by source/relay IP address (full or partial).
|
|
Examples: -a "192.168.1.50" | -a "10.0."
|
|
-s, --status STATUS Filter by delivery status (case-insensitive).
|
|
Examples: -s sent | -s deferred | -s reject | -s bounced | -s greylist
|
|
-i, --id QUEUE_ID Search for a specific Postfix Queue ID (Enables Detail View).
|
|
Example: -i 4T3bV22xLnz1t
|
|
--min-size BYTES Show only emails larger than specified BYTES.
|
|
Example: --min-size 10485760 (Find emails larger than ~10MB)
|
|
-R, --regex Treat filters (-f, -t, -a, -s) as Regular Expressions.
|
|
Example: -R -f "^admin.*@.*\.sk$"
|
|
|
|
OUTPUT & RUN MODE OPTIONS:
|
|
-l, --list NUMBER Number of latest records to display (1-1000). Default: 10.
|
|
Example: -l 50
|
|
-S, --summary Show aggregate statistics (Top IPs, Senders, Data usage) instead of list.
|
|
-c, --csv Output data in CSV format (disables colors for easy Excel import).
|
|
-w, --watch Live Watch Mode (tail -f). Runs continuously and shows new emails in real-time.
|
|
|
|
MISC OPTIONS:
|
|
--log-dir PATH Custom path to log files (default: /var/log).
|
|
-h, --help Display this detailed help message.
|
|
EOF
|
|
exit 0
|
|
}
|
|
|
|
# ==============================================================================
|
|
# 2. DEPENDENCY CHECK (FEATURE 2)
|
|
# ==============================================================================
|
|
for cmd in awk grep tail mktemp flock sort; do
|
|
if ! command -v "$cmd" &> /dev/null; then
|
|
echo "Error: Required command '$cmd' is not installed." >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
# ==============================================================================
|
|
# 3. ARGUMENT PARSING
|
|
# ==============================================================================
|
|
LIMIT=10; MAX_LIMIT=1000
|
|
FROM_FILTER=""; TO_FILTER=""; ID_FILTER=""; DATE_FILTER=""; IP_FILTER=""
|
|
STATUS_FILTER=""; TIME_FILTER=""; LOG_DIR="/var/log"; MIN_SIZE=0
|
|
DATE_SET=0; CSV_MODE=0; SUMMARY_MODE=0; WATCH_MODE=0; REGEX_MODE=0
|
|
CURRENT_MONTH=$(date +%m) # Required for New Year's Eve Bug fix
|
|
|
|
check_arg() {
|
|
if [[ "$2" -lt 1 ]]; then
|
|
echo "Error: Option '$1' requires an argument." >&2; exit 1
|
|
fi
|
|
}
|
|
|
|
while [[ "$#" -gt 0 ]]; do
|
|
case $1 in
|
|
-h|--help) show_help ;;
|
|
-l|--list) check_arg "$1" $#; if [[ "$2" =~ ^[0-9]+$ ]]; then LIMIT="$2"; fi; shift ;;
|
|
-f|--from) check_arg "$1" $#; FROM_FILTER="$2"; shift ;;
|
|
-t|--to) check_arg "$1" $#; TO_FILTER="$2"; shift ;;
|
|
-i|--id) check_arg "$1" $#; ID_FILTER="$2"; shift ;;
|
|
-a|--ip) check_arg "$1" $#; IP_FILTER="$2"; shift ;;
|
|
-s|--status) check_arg "$1" $#; STATUS_FILTER="$2"; shift ;;
|
|
--time) check_arg "$1" $#; TIME_FILTER="$2"; shift ;;
|
|
--log-dir) check_arg "$1" $#; LOG_DIR="$2"; shift ;;
|
|
--min-size) check_arg "$1" $#; if [[ "$2" =~ ^[0-9]+$ ]]; then MIN_SIZE="$2"; fi; shift ;;
|
|
-S|--summary) SUMMARY_MODE=1 ;;
|
|
-c|--csv) CSV_MODE=1 ;;
|
|
-w|--watch) WATCH_MODE=1 ;;
|
|
-R|--regex) REGEX_MODE=1 ;;
|
|
-d|--date) check_arg "$1" $#; DATE_FILTER="$2"; DATE_SET=1; shift ;;
|
|
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if (( LIMIT < 1 )); then LIMIT=1; fi
|
|
if (( LIMIT > MAX_LIMIT )); then LIMIT=$MAX_LIMIT; fi
|
|
|
|
if [ "$DATE_SET" -eq 0 ]; then
|
|
DATE_FILTER=$(date '+%b %e')
|
|
else
|
|
DATE_FILTER=$(echo "$DATE_FILTER" | sed -E 's/^([a-zA-Z]{3})[[:space:]]+([0-9])$/\1 \2/')
|
|
fi
|
|
readonly DATE_FILTER
|
|
|
|
DETAIL_VIEW=0
|
|
if [[ -n "$ID_FILTER" && "$ID_FILTER" != "NOQUEUE" ]]; then
|
|
DETAIL_VIEW=1
|
|
fi
|
|
|
|
export USE_COLOR=0
|
|
if [[ -t 1 ]]; then
|
|
USE_COLOR=1
|
|
fi
|
|
if [[ "$CSV_MODE" -eq 1 ]]; then
|
|
USE_COLOR=0
|
|
fi
|
|
export CSV_MODE
|
|
|
|
if [[ "$WATCH_MODE" -eq 1 && "$REGEX_MODE" -eq 1 ]]; then
|
|
echo "Security Warning: Regular Expressions (-R) are disabled in Watch Mode (-w) to prevent CPU locking (ReDoS)." >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$LOG_DIR" != "/var/log" && "$LOG_DIR" != "/var/log/mail" ]]; then
|
|
echo "Error: Unsafe log directory specified ($LOG_DIR). Only /var/log or /var/log/mail are allowed." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# ==============================================================================
|
|
# 4. RESOURCE LIMITS & LOCKFILES
|
|
# ==============================================================================
|
|
if [[ "$WATCH_MODE" -eq 1 ]]; then
|
|
LOCKFILE="${TMPDIR:-/tmp}/postfix_watch_${UID}.lock"
|
|
ulimit -v 500000
|
|
else
|
|
LOCKFILE="${TMPDIR:-/tmp}/postfix_summary_${UID}.lock"
|
|
ulimit -t 15
|
|
ulimit -v 500000
|
|
ulimit -f 102400
|
|
fi
|
|
|
|
exec 9>>"$LOCKFILE"
|
|
if ! flock -n 9; then
|
|
echo "Error: Another instance of this mode is already running." >&2
|
|
exit 1
|
|
fi
|
|
|
|
TMP_DATA=$(mktemp "${TMPDIR:-/tmp}/postfix_log.XXXXXX")
|
|
trap 'rm -f "$TMP_DATA" 2>/dev/null' EXIT
|
|
|
|
# ==============================================================================
|
|
# 5. LOG FILE DISCOVERY & PROCESSING ENGINE (FEATURE 1: SYSTEMD SUPPORT)
|
|
# ==============================================================================
|
|
if [ ! -d "$LOG_DIR" ]; then
|
|
echo "Error: Directory $LOG_DIR does not exist." >&2; exit 1
|
|
fi
|
|
|
|
get_log_stream() {
|
|
if [[ "$WATCH_MODE" -eq 1 ]]; then
|
|
ACTIVE_LOG=""
|
|
if [[ -f "$LOG_DIR/maillog" ]]; then ACTIVE_LOG="$LOG_DIR/maillog"; fi
|
|
if [[ -z "$ACTIVE_LOG" && -f "$LOG_DIR/mail.log" ]]; then ACTIVE_LOG="$LOG_DIR/mail.log"; fi
|
|
|
|
if [[ -n "$ACTIVE_LOG" ]]; then
|
|
echo "Starting Live Watch Mode on: $ACTIVE_LOG (Ctrl+C to stop)..." >&2
|
|
tail -n 100 -F "$ACTIVE_LOG"
|
|
elif command -v journalctl &> /dev/null; then
|
|
echo "Starting Live Watch Mode via systemd journal (Ctrl+C to stop)..." >&2
|
|
journalctl SYSLOG_FACILITY=2 -n 100 -f
|
|
else
|
|
echo "Error: No active log found to watch and journalctl is unavailable." >&2; exit 1
|
|
fi
|
|
else
|
|
LOG_FILES=()
|
|
while IFS= read -r -d '' file; do
|
|
LOG_FILES+=("$file")
|
|
done < <(find "$LOG_DIR" -maxdepth 1 \( -name 'maillog*' -o -name 'mail.log*' \) -type f -print0 2>/dev/null | sort -z -r)
|
|
|
|
# Fallback to journalctl if no log files are found
|
|
if [ ${#LOG_FILES[@]} -eq 0 ]; then
|
|
if command -v journalctl &> /dev/null; then
|
|
journalctl SYSLOG_FACILITY=2 --no-pager 2>/dev/null || true
|
|
return
|
|
else
|
|
echo "Error: No Postfix logs found in $LOG_DIR and journalctl is unavailable." >&2; exit 1
|
|
fi
|
|
fi
|
|
|
|
for log in "${LOG_FILES[@]}"; do
|
|
if [ ! -r "$log" ]; then continue; fi
|
|
|
|
if [[ "$log" =~ \.gz$ ]]; then
|
|
if command -v zcat &> /dev/null; then
|
|
if [[ -n "$DATE_FILTER" ]]; then
|
|
if ! zgrep -m 1 -q -e "$DATE_FILTER" -- "$log" 2>/dev/null; then continue; fi
|
|
fi
|
|
zcat -- "$log" 2>/dev/null
|
|
fi
|
|
else
|
|
if [[ -n "$DATE_FILTER" ]]; then
|
|
if ! grep -m 1 -q -e "$DATE_FILTER" -- "$log" 2>/dev/null; then continue; fi
|
|
fi
|
|
cat -- "$log" 2>/dev/null
|
|
fi
|
|
done
|
|
fi
|
|
}
|
|
|
|
# ==============================================================================
|
|
# 6. AWK PROCESSING LOGIC (FEATURE 4: NEW YEAR'S EVE BUG FIX)
|
|
# ==============================================================================
|
|
# shellcheck disable=SC2016
|
|
AWK_SCRIPT='
|
|
BEGIN {
|
|
split("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec", n);
|
|
for(i in n) { m[n[i]]=sprintf("%02d",i); months[n[i]]=1; }
|
|
db_count = 0;
|
|
}
|
|
function chk(val, filt, is_regex) {
|
|
if (filt == "") return 1;
|
|
if (is_regex == 1 || is_regex == "1") {
|
|
return (tolower(val) ~ tolower(filt));
|
|
}
|
|
return (index(tolower(val), tolower(filt)) > 0);
|
|
}
|
|
function sanitize(str) { gsub(/[^[:print:]]/, "", str); return str; }
|
|
!($1 in months) { next }
|
|
{ if (length($0) < 20 || length($0) > 4096) next; }
|
|
(w_mode == 0 && d_filt != "" && substr($0, 1, length(d_filt)) != d_filt) { next }
|
|
(tm_filt != "" && index($3, tm_filt) != 1) { next }
|
|
{
|
|
if (db_count > 100000) { split("", db_from); split("", db_time); split("", db_in_ip); split("", db_subj); split("", db_size); db_count = 0; }
|
|
qid = ""; for(i=1; i<=9; i++) if($i ~ /^[A-F0-9]+:$/) { qid=$i; gsub(/:/,"",qid); break; }
|
|
curr_t = $1"-"$2"_"$3;
|
|
if (qid != "" && !(qid in db_from)) { db_count++; }
|
|
|
|
if (qid != "" && index($0, "warning: header Subject:") > 0) {
|
|
if (match($0, /Subject: .* from /)) {
|
|
subj = substr($0, RSTART+9, RLENGTH-15); db_subj[qid] = sanitize(subj);
|
|
}
|
|
}
|
|
if (qid != "" && index($0, "size=") > 0) {
|
|
if (match($0, /size=[0-9]+/)) db_size[qid] = substr($0, RSTART+5, RLENGTH-5);
|
|
}
|
|
if (index($0, "amavis") > 0 && index($0, "queued_as:") > 0) {
|
|
if(match($0, /queued_as: [A-F0-9]+/)) {
|
|
aqid = substr($0, RSTART+11, RLENGTH-11);
|
|
if (match($0, /\[[0-9]{1,3}\.[0-9.]+/)) db_in_ip[aqid] = substr($0, RSTART+1, RLENGTH-1);
|
|
}
|
|
}
|
|
if (qid != "" && index($0, "client=") > 0) {
|
|
if (match($0, /\[[0-9]{1,3}\.[0-9.]+/)) {
|
|
cip = substr($0, RSTART+1, RLENGTH-1);
|
|
if (cip != "127.0.0.1" && db_in_ip[qid] == "") db_in_ip[qid] = cip;
|
|
}
|
|
db_time[qid] = curr_t;
|
|
}
|
|
if (qid != "" && index($0, "from=<") > 0) {
|
|
if (match($0, /from=<[^>]*>/)) db_from[qid] = substr($0, RSTART+6, RLENGTH-7);
|
|
}
|
|
if (qid != "" && index($0, "to=<") > 0 && index($0, "status=") > 0) {
|
|
if (index($0, "relay=127.0.0.1") > 0 || index($0, "10025") > 0) next;
|
|
match($0, /to=<[^>]*>/); s_to = substr($0, RSTART+4, RLENGTH-5);
|
|
match($0, /status=[a-z]+/); s_st = substr($0, RSTART+7, RLENGTH-7);
|
|
rip = ""; if (match($0, /relay=[^ ]+\[[0-9]{1,3}\.[0-9.]+/)) {
|
|
temp_m = substr($0, RSTART, RLENGTH); match(temp_m, /\[[0-9.]+/); rip = substr(temp_m, RSTART+1, RLENGTH-1);
|
|
}
|
|
final_ip = (rip != "" && rip != "127.0.0.1") ? rip : (db_in_ip[qid] != "" ? db_in_ip[qid] : "local");
|
|
s_re = "OK"; if (match($0, /\([^)]*\)/)) { s_re = substr($0, RSTART+1, RLENGTH-2); gsub(/ /,"_",s_re); }
|
|
s_f = (db_from[qid] != "") ? db_from[qid] : "-";
|
|
s_tm = (db_time[qid] != "") ? db_time[qid] : curr_t;
|
|
s_sz = (db_size[qid] != "") ? db_size[qid] : 0;
|
|
s_sub = (db_subj[qid] != "") ? db_subj[qid] : "-"; gsub(/ /,"_",s_sub);
|
|
|
|
if (s_sz >= m_sz && chk(s_f, f_filt, r_mode) && chk(s_to, t_filt, r_mode) && chk(qid, i_filt, r_mode) && chk(final_ip, ip_filt, r_mode) && chk(s_st, st_filt, r_mode)) {
|
|
|
|
# New Year Fix: Prefix sorting key with 0 (prev year) or 1 (curr year)
|
|
log_m = m[$1];
|
|
yr_prefix = (log_m > cur_m) ? "0" : "1";
|
|
sort_k = yr_prefix log_m sprintf("%02d", $2);
|
|
|
|
tm_tmp=$3; gsub(/:/,"",tm_tmp); sort_k = sort_k tm_tmp;
|
|
out = sort_k " " s_tm " " qid " " final_ip " " s_f " " s_to " " s_st " " s_sz " " s_sub " " s_re;
|
|
if (w_mode == 1) { print out; fflush(); } else { print out > tmp_file; }
|
|
}
|
|
}
|
|
if (index($0, "NOQUEUE: reject:") > 0 && (i_filt == "" || i_filt == "NOQUEUE")) {
|
|
match($0, /from=<[^>]*>/); f=(RLENGTH>0)?substr($0, RSTART+6, RLENGTH-7):"-";
|
|
match($0, /to=<[^>]*>/); t=(RLENGTH>0)?substr($0, RSTART+4, RLENGTH-5):"-";
|
|
if (match($0, /\[[0-9]{1,3}\.[0-9.]+/)) nip = substr($0, RSTART+1, RLENGTH-1); else nip = "unknown";
|
|
s_st = (index($0, "Greylisted") > 0) ? "GREYLIST" : "REJECT";
|
|
s_re = "Rejected"; if (match($0, /: [^;]+; /)) s_re = substr($0, RSTART+2, RLENGTH-4); gsub(/ /,"_",s_re);
|
|
|
|
log_m = m[$1];
|
|
yr_prefix = (log_m > cur_m) ? "0" : "1";
|
|
sort_k = yr_prefix log_m sprintf("%02d", $2);
|
|
|
|
tm_tmp=$3; gsub(/:/,"",tm_tmp); sort_k = sort_k tm_tmp;
|
|
|
|
if (chk(f, f_filt, r_mode) && chk(t, t_filt, r_mode) && chk(nip, ip_filt, r_mode) && chk(s_st, st_filt, r_mode)) {
|
|
out = sort_k " " curr_t " NOQUEUE " nip " " f " " t " " s_st " 0 - " s_re;
|
|
if (w_mode == 1) { print out; fflush(); } else { print out > tmp_file; }
|
|
}
|
|
}
|
|
}'
|
|
|
|
# ==============================================================================
|
|
# 7. EXECUTION & OUTPUT RENDERING (FEATURE 3: PAGER INTEGRATION)
|
|
# ==============================================================================
|
|
format_output() {
|
|
awk -v u_col="$USE_COLOR" -v csv="$CSV_MODE" -v d_view="$DETAIL_VIEW" '
|
|
function csv_safe(s) { if(s ~ /^[=+\-@]/) { s=" "s }; gsub(/"/,"\"\"",s); return s; }
|
|
function d_cut(e, m) { if(length(e)<=m)return e; return ".." substr(e, length(e)-m+2); }
|
|
BEGIN { b="\033[1;34m"; r="\033[0m"; g="\033[1;32m"; rd="\033[1;31m"; y="\033[1;33m"; gr="\033[0;90m"; c="\033[0;36m"; if(u_col!="1")b=r=g=rd=y=gr=c="";
|
|
if(csv==1 && d_view==0) print "DATE-TIME,QUEUE_ID,EXT_IP,SENDER,RECIPIENT,STATUS,SIZE_BYTES,SUBJECT,REASON"; }
|
|
{
|
|
tm=$2; gsub(/_/," ",tm); id=$3; ip=$4;
|
|
from=$5; to=$6; st=$7; sz=$8;
|
|
subj=$9; gsub(/_/," ",subj);
|
|
re=$10; gsub(/_/," ",re);
|
|
|
|
if (d_view == 1) {
|
|
printf "\n%s--- Detail for ID: %s ---%s\nDate: %s\nIP: %s\nFrom: %s\nTo: %s\nStatus: %s\nSize: %s Bytes\nSubject: %s\nReason: %s\n", b,id,r,tm,ip,from,to,st,sz,subj,re;
|
|
fflush();
|
|
} else if (csv == 1) {
|
|
printf "\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"\n", csv_safe(tm), csv_safe(id), csv_safe(ip), csv_safe(from), csv_safe(to), csv_safe(st), csv_safe(sz), csv_safe(subj), csv_safe(re);
|
|
fflush();
|
|
} else {
|
|
scol = (st == "sent") ? g : (st == "deferred" || st == "GREYLIST" ? y : rd);
|
|
s_re = (match(re, /[0-9]{3} [0-9]\.[0-9]\.[0-9]/)) ? substr(re, RSTART, RLENGTH) : substr(re, 1, 15);
|
|
printf "%-16s %s%-12s%s %s%-16s%s %-32s %-28s %s%-10s%s %s[%s]%s\n", tm, (id=="NOQUEUE"?rd:b), id, r, gr, sprintf("%.15s", ip), r, d_cut(from, 31), d_cut(to, 27), scol, st, r, (st=="sent"?c:scol), s_re, r;
|
|
fflush();
|
|
}
|
|
}'
|
|
}
|
|
|
|
# Wrapper function for the pager (less)
|
|
use_pager() {
|
|
if [[ -t 1 && "$CSV_MODE" -eq 0 && "$WATCH_MODE" -eq 0 ]]; then
|
|
if command -v less &> /dev/null; then
|
|
# -R preserves colors, -F exits if content fits on one screen, -X prevents clearing the screen on exit
|
|
less -R -F -X
|
|
else
|
|
cat
|
|
fi
|
|
else
|
|
cat
|
|
fi
|
|
}
|
|
|
|
if [[ "$WATCH_MODE" -eq 1 ]]; then
|
|
if [[ "$USE_COLOR" -eq 1 ]]; then
|
|
printf "\033[1m%-16s %-12s %-16s %-32s %-28s %-10s %s\033[0m\n" "DATE-TIME" "QUEUE_ID" "EXT_IP" "SENDER" "RECIPIENT" "STATUS" "REASON"
|
|
fi
|
|
get_log_stream | awk -v d_filt="$DATE_FILTER" -v tm_filt="$TIME_FILTER" -v f_filt="$FROM_FILTER" -v t_filt="$TO_FILTER" -v i_filt="$ID_FILTER" -v ip_filt="$IP_FILTER" -v st_filt="$STATUS_FILTER" -v m_sz="$MIN_SIZE" -v cur_m="$CURRENT_MONTH" -v w_mode=1 -v r_mode="$REGEX_MODE" -v tmp_file="$TMP_DATA" "$AWK_SCRIPT" | format_output
|
|
exit 0
|
|
fi
|
|
|
|
# Run normal processing
|
|
get_log_stream | awk -v d_filt="$DATE_FILTER" -v tm_filt="$TIME_FILTER" -v f_filt="$FROM_FILTER" -v t_filt="$TO_FILTER" -v i_filt="$ID_FILTER" -v ip_filt="$IP_FILTER" -v st_filt="$STATUS_FILTER" -v m_sz="$MIN_SIZE" -v cur_m="$CURRENT_MONTH" -v w_mode=0 -v r_mode="$REGEX_MODE" -v tmp_file="$TMP_DATA" "$AWK_SCRIPT"
|
|
|
|
if [[ "$SUMMARY_MODE" -eq 1 ]]; then
|
|
total_records=$(wc -l < "$TMP_DATA")
|
|
|
|
(
|
|
if [[ "$USE_COLOR" -eq 1 ]]; then printf "\033[1;36m"; fi
|
|
echo "=========================================================="
|
|
echo " SUMMARY STATISTICS (Total records: ${total_records})"
|
|
echo "=========================================================="
|
|
if [[ "$USE_COLOR" -eq 1 ]]; then printf "\033[0m"; fi
|
|
|
|
if [[ "$total_records" -eq 0 ]]; then
|
|
echo "No data matched your filters."
|
|
else
|
|
print_top() {
|
|
local col=$1; local title=$2; local max=$3
|
|
if [[ "$USE_COLOR" -eq 1 ]]; then
|
|
printf "\n\033[1;33m%s\033[0m\n" "$title"
|
|
else
|
|
printf "\n%s\n" "$title"
|
|
fi
|
|
awk "{print \$$col}" "$TMP_DATA" | sort | uniq -c | sort -nr | head -n "$max" | awk '{printf " %6d %s\n", $1, $2}'
|
|
}
|
|
|
|
print_top 7 "STATUS BREAKDOWN:" 10
|
|
print_top 4 "TOP 10 SOURCE IPs:" 10
|
|
print_top 5 "TOP 10 SENDERS:" 10
|
|
|
|
if [[ "$USE_COLOR" -eq 1 ]]; then
|
|
printf "\n\033[1;33mBANDWIDTH CONSUMPTION:\033[0m\n"
|
|
else
|
|
printf "\nBANDWIDTH CONSUMPTION:\n"
|
|
fi
|
|
awk '{sum+=$8} END {printf " Total Processed Size: %.2f MB\n", sum/1048576}' "$TMP_DATA"
|
|
fi
|
|
) | use_pager
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "$DETAIL_VIEW" -eq 1 ]]; then
|
|
if [ ! -s "$TMP_DATA" ]; then
|
|
echo "No records found for ID: $ID_FILTER"; exit 0
|
|
fi
|
|
sort -n "$TMP_DATA" | format_output | use_pager
|
|
else
|
|
(
|
|
if [[ "$USE_COLOR" -eq 1 && "$CSV_MODE" -eq 0 ]]; then
|
|
printf "\033[1m%-16s %-12s %-16s %-32s %-28s %-10s %s\033[0m\n" "DATE-TIME" "QUEUE_ID" "EXT_IP" "SENDER" "RECIPIENT" "STATUS" "REASON"
|
|
fi
|
|
sort -n "$TMP_DATA" | tail -n "$LIMIT" | format_output
|
|
) | use_pager
|
|
fi |