#!/usr/bin/python3 ''' sagator's logwatch script (c) 2003-2005,2016-2018 Jan ONDREJ (SAL) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. ''' from __future__ import print_function import sys, re, os, time def kilomega(x, v='B'): if x>=1024*1024: return "%1.4f M%s" % (x/1024.0/1024.0, v) elif x>=1024: return "%1.4f k%s" % (x/1024.0, v) else: return "%1.4f %s " % (x, v) def hodminsec(t): return "%d:%02d:%02d" % (t/60/60, t/60%60, t%60) class a: connect = 0 count,bytes = 0, 0 time = 0 found = {} found_bytes = {} clean,clean_bytes = 0, 0 infected,infected_bytes = 0, 0 spam,spam_bytes = 0, 0 errors = 0 erra = {} warnings = 0 warna = {} def spamstring(s): return (s[:4]=='SPAM') or (s[-5:]=='&SPAM') try: DATE_RANGE = os.environ['LOGWATCH_DATE_RANGE'] except KeyError: DATE_RANGE = 'today' # regular expressions base = '^ *([0-9]+): ' reg_conn = re.compile(base+ 'Connection from: ').search reg_stats = re.compile(base+ 'STATS: ([0-9.]+) seconds, ([0-9]+) bytes, status: (.*)$').search if DATE_RANGE=='yesterday': searchdate = time.strftime("%a %b %e ..:..:.. %Y", time.localtime(time.time()-86400)) elif DATE_RANGE=='today': searchdate = time.strftime("%a %b %e ..:..:.. %Y") elif DATE_RANGE=='all': searchdate = '... ... .. ..:..:.. ....' reg_aconn = re.compile(base+'Connection from: ').search reg_conn = re.compile(base+'Connection from: .* at ('+searchdate+')').search reg_exit = re.compile(base+'Child exited ... pid,status: .([0-9]*),').search reg_err = re.compile(base+'([-a-zA-Z_()]*): *(ERROR|WARNING)[: ]*(.*)$').search reg_policy = re.compile(base+ 'checkpolicy: '+searchdate+' .* action=(.*)$').search pids = {} policy_results = {} while True: line = sys.stdin.readline() # EOF? if not line: break # add a new PID into DB rconn = reg_aconn(line) if rconn: reg1 = reg_conn(line) if reg1: a.connect += 1 pids[reg1.group(1)] = reg1.group(2) else: # It is a new connection from this pid, but not for date range # If in pids, remove it. try: del pids[rconn.group(1)] except KeyError: pass # remove PID from DB if exited reg2 = reg_exit(line) if reg2: try: del pids[reg2.group(2)] except: pass reg = reg_err(line) if reg: # is this PID in DB? if reg.group(1) not in pids: continue # is it an ERROR or WARNING? if reg.group(3)=="ERROR": # error a.errors += 1 try: a.erra[reg.group(4)] += 1 except KeyError: a.erra[reg.group(4)] = 1 else: # warning a.warnings += 1 try: a.warna[reg.group(4)] += 1 except KeyError: a.warna[reg.group(4)] = 1 reg = reg_stats(line) if reg: # is this PID in DB? if reg.group(1) not in pids: continue a.count += 1 a.time += float(reg.group(2)) bytes = reg.group(3) name = reg.group(4) a.bytes += int(bytes) if (len(name)>80) and (not spamstring(name)): # error? a.errors += 1 try: a.erra[name] += 1 except KeyError: a.erra[name] = 1 elif name=='CLEAN': a.clean += 1 a.clean_bytes += int(bytes) else: if spamstring(name): a.spam += 1 a.spam_bytes += int(bytes) else: a.infected += 1 a.infected_bytes += int(bytes) try: a.found[name] += 1 a.found_bytes[name] += int(bytes) except KeyError: a.found[name] = 1 a.found_bytes[name] = int(bytes) # policy processing reg = reg_policy(line) if reg: policy_result = reg.group(2) if policy_result.startswith("defer_if_permit Greylisted for"): policy_result = "defer_if_permit Greylisted" if policy_result in policy_results: policy_results[policy_result] += 1 else: policy_results[policy_result] = 1 #print '\n--------------------- SAGATOR -----------------------\n' if a.count>0: print(' Email count Bytes') print('Total: %6d %12s' % \ (a.count,kilomega(a.bytes))) if a.bytes==0: a.bytes = 0.00001 print('Clean: %6d [%3d%%] %12s [%3d%%]' % \ (a.clean, a.clean*100/a.count, kilomega(a.clean_bytes), a.clean_bytes*100/a.bytes)) print('Viruses: %6d [%3d%%] %12s [%3d%%]' % \ (a.infected, a.infected*100/a.count, kilomega(a.infected_bytes), a.infected_bytes*100/a.bytes)) print('Spams: %6d [%3d%%] %12s [%3d%%]' % \ (a.spam, a.spam*100/a.count, kilomega(a.spam_bytes), a.spam_bytes*100/a.bytes)) print('') print('Consumed time: %8s' % (hodminsec(a.time))) print('Connections: %6d' % (a.connect)) keys = list(a.found.keys()) if keys: keys.sort(key=lambda x: (a.found[x], a.found_bytes[x], x), reverse=True) print('') for k in keys: print('%6d [%12s]: %s' % (a.found[k],kilomega(a.found_bytes[k]),k)) if a.errors>0: print('\nErrors: %d' % a.errors) for k, v in list(a.erra.items()): print("%5d: %s" % (v, k)) if a.warnings>0: print('\nWarnings: %d' % a.warnings) for k, v in list(a.warna.items()): print("%5d: %s" % (v, k)) if policy_results: print('\nPolicy filter results:') total = sum(policy_results.values()) for k, v in sorted(list(policy_results.items()), key=lambda x: x[1], reverse=True): print("%6d [%3d%%]: %s" % (v, v*100/total, k)) #print '\n--------------------- SAGATOR -----------------------\n'