]> git.proxmox.com Git - pmg-log-tracker.git/blobdiff - pmg-log-tracker.c
minor type fixes
[pmg-log-tracker.git] / pmg-log-tracker.c
index f6f3b47c8a22dbd7174d1b1f5be0ee2f13b6e47d..e2827e142994afc89ecd8700ace03b16562e189e 100644 (file)
@@ -1,10 +1,25 @@
 /*
 
- (C) 2007-2017 Proxmox Server Solutions GmbH, All Rights Reserved
 (C) 2007-2017 Proxmox Server Solutions GmbH, All Rights Reserved
 
- Proxmox Mail Tracker
 Proxmox Mail Tracker
 
- See http://www.proxmox.com for more information
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Affero General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Affero General Public License for more details.
+
+  You should have received a copy of the GNU Affero General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+  Author: Dietmar Maurer <dietmar@proxmox.com>
+
+  See http://www.proxmox.com for more information
 
 */
 
@@ -19,6 +34,7 @@
 #include <time.h>
 #include <unistd.h>
 #include <sys/types.h>
+#include <stdint.h>
 #include <zlib.h>
 #include <fnmatch.h>
 
@@ -42,7 +58,7 @@
 //#define EPOOL_MAX_SIZE 128
 #define EPOOL_BLOCK_SIZE 2048
 #define EPOOL_MAX_SIZE 128
-#define MAX_LOGFILES 31
+#define MAX_LOGFILES 32
 //#define EPOOL_DEBUG 
 //#define DEBUG
 
@@ -70,6 +86,18 @@ struct _TOList {
   TOList *next;
 };
 
+#define MatchTypeQID 1
+#define MatchTypeRelLineNr 2
+
+typedef struct _MatchList MatchList;
+struct _MatchList {
+  unsigned int mtype;
+  char *id;
+  time_t ltime;
+  unsigned long rel_line_nr;
+  MatchList *next;
+};
+
 #ifdef DEBUG
   GHashTable *smtpd_debug_alloc;
   GHashTable *qmgr_debug_alloc;
@@ -110,9 +138,9 @@ typedef struct {
   time_t start;
   time_t end;
   time_t ctime;
+  MatchList *match_list;
   char *server;
   char *msgid;
-  char *qid;
   char *strmatch;
   unsigned long limit;
   unsigned long count;
@@ -130,6 +158,7 @@ typedef struct _LogList LogList;
 
 struct _LogEntry {
   const char *text;
+  unsigned long linenr;
   LogEntry *next;
 };
 
@@ -153,6 +182,10 @@ struct _SEntry {
 
   char *connect;
 
+ // time,rel_line_nr is used as cursor/ID
+  time_t ltime;
+  unsigned long rel_line_nr;
+
   //unsigned int external:1;        // not from local host
   unsigned int disconnect:1;
   unsigned int strmatch:1;
@@ -203,7 +236,6 @@ struct _FEntry {
 
 // Prototypes
 void      debug_error (char *msg, const char *line);
-SList    *slist_remove (SList *list, gpointer data);
 
 EPool    *epool_init (EPool *ep);
 void      epool_free (EPool *ep);
@@ -213,10 +245,10 @@ char     *epool_strndup0 (EPool *ep, const char *s, int len);
 char     *epool_strdup (EPool *ep, const char *s);
 
 void      loglist_print (LogList *loglist);
-void      loglist_add (EPool *ep, LogList *loglist, const char *text, int len);
+void      loglist_add (EPool *ep, LogList *loglist, const char *text, int len, unsigned long linenr);
 
-SEntry   *sentry_new (int pid);
-SEntry   *sentry_get (LParser *parser, int pid);
+SEntry   *sentry_new (int pid, time_t ltime, unsigned long rel_line_nr);
+SEntry   *sentry_get (LParser *parser, int pid, time_t ltime, unsigned long rel_line_nr);
 void      sentry_ref_add (SEntry *sentry, QEntry *qentry);
 int       sentry_ref_del (SEntry *sentry, QEntry *qentry);
 void      sentry_ref_finalize (LParser *parser, SEntry *sentry);
@@ -259,9 +291,21 @@ void      parser_free (LParser *parser);
 
 // Implementations
 
+// Checksum Macros
+#define PROXPROX               0xE0E4DEF0
+#define PMG_SMTP_FILTER                0x0A85A6B7
+#define POSTFIX_POSTSCREEN     0xD17E2019
+#define POSTFIX_QMGR           0x48465316
+#define POSTFIX_SMTP           0x4A466014
+#define POSTFIX_LMTP           0x43466014
+#define POSTFIX_LOCAL          0x484F05AF
+#define POSTFIX_ERROR          0x4B5E13AE
+#define POSTFIX_SMTPD          0x466014AE
+#define POSTFIX_CLEANUP                0x05A8BAC1
+
 //#define LOGPATH "./log/"
 #define LOGPATH "/var/log/"
-//#define LOGPATH "/var/log5/"
+//#define LOGPATH "/root/testlog/"
 
 static const char *logfiles[] = {
   LOGPATH "syslog",
@@ -311,31 +355,6 @@ debug_error (char *msg, const char *line)
 #endif
 }
 
-SList *
-slist_remove (SList *list, gpointer data)
-{
-  SList *p = NULL;
-  SList *l = list;
-
-  while (l) {
-    if (l->data == data) {
-      if (p)
-       p->next = l->next;
-      if (list == l)
-       list = list->next;
-
-      l->next = NULL;
-
-      break;
-    }
-
-    p = l;
-    l = l->next;
-  }
-
-  return list;
-}
-
 EPool *
 epool_init (EPool *ep)
 {
@@ -401,7 +420,7 @@ epool_alloc (EPool *ep, int size)
   if (size > EPOOL_MAX_SIZE) {
     SList *blocks;
     if (space >= sizeof (SList)) {
-      blocks = ep->blocks->data + ep->cpos;
+      blocks = (SList *)((char *)ep->blocks->data + ep->cpos);
       ep->cpos += sizeof (SList);
     } else {
       blocks = (SList *)epool_alloc (ep, sizeof (SList));
@@ -421,13 +440,13 @@ epool_alloc (EPool *ep, int size)
     return data;
 
   } else if (space >= rs) {
-    data = ep->blocks->data + ep->cpos;
+    data = (char *)ep->blocks->data + ep->cpos;
     ep->cpos += rs;
 
     return data;
 
   } else {
-    SList *blocks = ep->blocks->data + ep->cpos;
+    SList *blocks = (SList *)((char *)ep->blocks->data + ep->cpos);
 
     data = g_slice_alloc0 (EPOOL_BLOCK_SIZE);
     blocks->data = data;
@@ -479,14 +498,14 @@ loglist_print (LogList *loglist)
 {
   LogEntry *log = loglist->log;
   while (log) {
-    printf ("%s", log->text);
+    printf ("L%08lX %s", log->linenr, log->text);
     log = log->next;
   }
 }
 
 
 void
-loglist_add (EPool *ep, LogList *loglist, const char *text, int len)
+loglist_add (EPool *ep, LogList *loglist, const char *text, int len, unsigned long linenr)
 {
   LogEntry *log;
 
@@ -503,6 +522,7 @@ loglist_add (EPool *ep, LogList *loglist, const char *text, int len)
   log = epool_alloc (ep, sizeof (LogEntry));
 
   log->text = epool_strndup (ep, text, len);
+  log->linenr = linenr;
   log->next = NULL;
 
   if (loglist->logs_last) {
@@ -517,7 +537,7 @@ loglist_add (EPool *ep, LogList *loglist, const char *text, int len)
 }
 
 SEntry*
-sentry_new (int pid)
+sentry_new (int pid, time_t ltime, unsigned long rel_line_nr)
 {
   SEntry *sentry;
   SList *blocks;
@@ -525,6 +545,8 @@ sentry_new (int pid)
 
   sentry = (SEntry *)g_slice_alloc0(EPOOL_BLOCK_SIZE);
   sentry->pid = pid;
+  sentry->ltime = ltime;
+  sentry->rel_line_nr = rel_line_nr;
 
 #ifdef EPOOL_DEBUG
   sentry->ep.allocated += EPOOL_BLOCK_SIZE;
@@ -559,7 +581,7 @@ sentry_new (int pid)
 }
 
 SEntry *
-sentry_get (LParser *parser, int pid
+sentry_get (LParser *parser, int pid, time_t ltime, unsigned long rel_line_nr)
 {
   SEntry *sentry;
 
@@ -567,7 +589,7 @@ sentry_get (LParser *parser, int pid)
     return sentry;
   } else {
 
-    if ((sentry = sentry_new (pid))) {
+    if ((sentry = sentry_new (pid, ltime, rel_line_nr))) {
       g_hash_table_insert (parser->smtpd_h, &sentry->pid, sentry);
     }
 
@@ -713,13 +735,32 @@ sentry_print (LParser *parser, SEntry *sentry)
 {
   NQList *nq;
 
-  if (parser->msgid || parser->qid) return;
+  if (parser->msgid) return;
 
   if (parser->server) {
     if (!sentry->connect) return;
     if (!strcasestr (sentry->connect, parser->server)) return;
   }
 
+  MatchList *match = parser->match_list;
+  if (match) {
+    int found = 0;
+    while(match) {
+      if (match->mtype == MatchTypeQID) {
+       return;
+      } else if (match->mtype == MatchTypeRelLineNr) {
+       if (match->ltime == sentry->ltime && match->rel_line_nr == sentry->rel_line_nr) {
+         found = 1;
+         break;
+       }
+      } else {
+       g_error("implement me");
+      }
+      match = match->next;
+    }
+    if (!found) return;
+  }
+
   if (parser->from || parser->to || 
       parser->exclude_greylist || parser->exclude_ndrs) {
     nq = sentry->nqlist;
@@ -752,11 +793,11 @@ sentry_print (LParser *parser, SEntry *sentry)
 
   if (parser->verbose) {
 
-    printf ("SMTPD:\n");
+    printf ("SMTPD: T%08lXL%08lX\n", sentry->ltime, sentry->rel_line_nr);
 
     printf ("CTIME: %08lX\n", parser->ctime);
 
-    if (sentry->connect) { printf ("CONNECT: %s\n", sentry->connect); }
+    if (sentry->connect) { printf ("CLIENT: %s\n", sentry->connect); }
     //printf ("EXTERNAL: %d\n", sentry->external);
 
   }
@@ -764,18 +805,23 @@ sentry_print (LParser *parser, SEntry *sentry)
   nq = sentry->nqlist;
   while (nq) {
     if (nq->from && nq->to && nq->dstatus) {
-      printf ("TO:%08lX:00000000000:%c: from <%s> to <%s>\n", nq->ltime, nq->dstatus, nq->from, nq->to);
+      printf ("TO:%08lX:T%08lXL%08lX:%c: from <%s> to <%s>\n", nq->ltime,
+             sentry->ltime, sentry->rel_line_nr, nq->dstatus,
+             nq->from, nq->to);
       parser->count++;
     }
     nq = nq->next;
   }
 
-  if (parser->verbose) {
+  if (!parser->verbose)  { fflush (stdout); return; }
+
+  if (parser->verbose > 1) {
     printf ("LOGS:\n");
     loglist_print (&sentry->loglist);
-    printf ("\n");
   }
 
+  printf ("\n");
+
   fflush (stdout);
 }
 
@@ -990,12 +1036,27 @@ qentry_print (LParser *parser, QEntry *qentry)
     if (strcasecmp (parser->msgid, qentry->msgid)) return;
   }
 
-  if (parser->qid) {
+  MatchList *match = parser->match_list;
+  if (match) {
     int found = 0;
-    if (fe && !strcmp (fe->logid, parser->qid)) found = 1;
-    if (!strcmp (qentry->qid, parser->qid)) found = 1;
-
-    if (!found) return;    
+    while(match) {
+      if (match->mtype == MatchTypeQID) {
+       if ((fe && !strcmp (fe->logid, match->id)) ||
+           (!strcmp (qentry->qid, match->id))) {
+         found = 1;
+         break;
+       }
+      } else if (match->mtype == MatchTypeRelLineNr) {
+       if (se && match->ltime == se->ltime && match->rel_line_nr == se->rel_line_nr) {
+         found = 1;
+         break;
+       }
+      } else {
+       g_error("implement me");
+      }
+      match = match->next;
+    }
+    if (!found) return;
   }
 
   if (parser->server) {
@@ -1043,7 +1104,11 @@ qentry_print (LParser *parser, QEntry *qentry)
     printf ("CTIME: %08lX\n", parser->ctime);
     printf ("SIZE: %u\n", qentry->size);
 
-    if (qentry->client) { printf ("CLIENT: %s\n", qentry->client); }
+    if (qentry->client) {
+      printf ("CLIENT: %s\n", qentry->client);
+    } else if (se && se->connect) {
+      printf ("CLIENT: %s\n", se->connect);
+    }
 
     if (qentry->msgid) { printf ("MSGID: %s\n", qentry->msgid); }
 
@@ -1086,24 +1151,22 @@ qentry_print (LParser *parser, QEntry *qentry)
 
   if (!parser->verbose)  { fflush (stdout); return; }
 
-  if (se) {
+  if (parser->verbose > 1) {
 
-    if (se->loglist.log) {
+    if (se && se->loglist.log) {
       printf ("SMTP:\n");
       loglist_print (&se->loglist);
     }
-  }
 
-  if (fe) {
-    if (fe->loglist.log) {
-       printf ("FILTER: %s\n", fe->logid);
-       loglist_print (&fe->loglist);
-     }
-  }
+    if (fe && fe->loglist.log) {
+      printf ("FILTER: %s\n", fe->logid);
+      loglist_print (&fe->loglist);
+    }
 
-  if (qentry->loglist.log) {
-    printf ("QMGR:\n");
-    loglist_print (&qentry->loglist);
+    if (qentry->loglist.log) {
+      printf ("QMGR:\n");
+      loglist_print (&qentry->loglist);
+    }
   }
 
   printf ("\n");
@@ -1459,6 +1522,19 @@ mkgmtime (struct tm *tm)
   return res;
 }
 
+#define JAN (('J'<<16)|('a'<<8)|'n')
+#define FEB (('F'<<16)|('e'<<8)|'b')
+#define MAR (('M'<<16)|('a'<<8)|'r')
+#define APR (('A'<<16)|('p'<<8)|'r')
+#define MAY (('M'<<16)|('a'<<8)|'y')
+#define JUN (('J'<<16)|('u'<<8)|'n')
+#define JUL (('J'<<16)|('u'<<8)|'l')
+#define AUG (('A'<<16)|('u'<<8)|'g')
+#define SEP (('S'<<16)|('e'<<8)|'p')
+#define OCT (('O'<<16)|('c'<<8)|'t')
+#define NOV (('N'<<16)|('o'<<8)|'v')
+#define DEC (('D'<<16)|('e'<<8)|'c')
+
 time_t
 parse_time (const char **text, int len)
 {
@@ -1487,21 +1563,21 @@ parse_time (const char **text, int len)
 
   // parse month
   int csum = (line[0]<<16) + (line[1]<<8) + line[2];
-      
+
   switch (csum) {
-  case 4874606: mon = 0; break;
-  case 4613474: mon = 1; break;
-  case 5071218: mon = 2; break;
-  case 4288626: mon = 3; break;
-  case 5071225: mon = 4; break;
-  case 4879726: mon = 5; break;
-  case 4879724: mon = 6; break;
-  case 4289895: mon = 7; break;
-  case 5465456: mon = 8; break;
-  case 5202804: mon = 9; break;
-  case 5140342: mon = 10; break;
-  case 4482403: mon = 11; break;
-  default: 
+    case JAN: mon = 0; break;
+    case FEB: mon = 1; break;
+    case MAR: mon = 2; break;
+    case APR: mon = 3; break;
+    case MAY: mon = 4; break;
+    case JUN: mon = 5; break;
+    case JUL: mon = 6; break;
+    case AUG: mon = 7; break;
+    case SEP: mon = 8; break;
+    case OCT: mon = 9; break;
+    case NOV: mon = 10; break;
+    case DEC: mon = 11; break;
+  default:
     debug_error ("unable to parse month", line);
     return 0;    
   }
@@ -1528,7 +1604,7 @@ parse_time (const char **text, int len)
     return 0;
   }
 
-  found = 0; while (isdigit (*cpos)) { mday = mday*10 + *cpos - 48; cpos++; found++; }
+  found = 0; while (isdigit (*cpos)) { mday = mday*10 + *cpos - '0'; cpos++; found++; }
   if (found < 1 || found > 2) {
     debug_error ("unable to parse day of month", line);
     return 0;
@@ -1542,7 +1618,7 @@ parse_time (const char **text, int len)
     return 0;
   }
 
-  found = 0; while (isdigit (*cpos)) { hour = hour*10 + *cpos - 48; cpos++; found++; }
+  found = 0; while (isdigit (*cpos)) { hour = hour*10 + *cpos - '0'; cpos++; found++; }
   if (found < 1 || found > 2) {
     debug_error ("unable to parse hour", line);
     return 0;
@@ -1557,7 +1633,7 @@ parse_time (const char **text, int len)
   }
   cpos++;
 
-  found = 0; while (isdigit (*cpos)) { min = min*10 + *cpos - 48; cpos++; found++; }
+  found = 0; while (isdigit (*cpos)) { min = min*10 + *cpos - '0'; cpos++; found++; }
   if (found < 1 || found > 2) {
     debug_error ("unable to parse minute", line);
     return 0;
@@ -1572,7 +1648,7 @@ parse_time (const char **text, int len)
   }
   cpos++;
 
-  found = 0; while (isdigit (*cpos)) { sec = sec*10 + *cpos - 48; cpos++; found++; }
+  found = 0; while (isdigit (*cpos)) { sec = sec*10 + *cpos - '0'; cpos++; found++; }
   if (found < 1 || found > 2) {
     debug_error ("unable to parse second", line);
     return 0;
@@ -1602,7 +1678,7 @@ parser_count_files (LParser *parser)
   const char *line;
   gzFile stream;
 
-  for (i = 0; i < MAX_LOGFILES; i++) {
+  for (i = 0; i < (MAX_LOGFILES - 1); i++) {
     cur_year = parser->year[i];
     cur_month = 0;
 
@@ -1656,16 +1732,20 @@ print_usage (const char *name)
   fprintf (stderr, "\t-e END         end time (YYYY-MM-DD HH:MM:SS)\n");
   fprintf (stderr, "\t               or seconds since epoch\n");
   fprintf (stderr, "\t-m MSGID       message ID (exact match)\n");
-  fprintf (stderr, "\t-q QID         queue ID (exact match)\n");
+  fprintf (stderr, "\t-q QID         queue ID (exact match), can be\n");
+  fprintf (stderr, "\t               specified multiple times.\n");
   fprintf (stderr, "\t-x STRING      search for strings\n");
   fprintf (stderr, "\t-l LIMIT       print max limit entries\n");
-  fprintf (stderr, "\t-v             verbose output\n");
+  fprintf (stderr, "\t-g             exclude greylist entries\n");
+  fprintf (stderr, "\t-n             exclude NDR entries\n");
+  fprintf (stderr, "\t-v             verbose output (no logs)\n");
+  fprintf (stderr, "\t-vv            verbose output with logs\n");
 }
 
 
 // gzgets is ways too slow, so we do it our own way
 
-static int
+static char
 mygzgetc (gzFile stream)
 {
   int br;
@@ -1688,7 +1768,7 @@ mygzgetc (gzFile stream)
 static char *
 mygzgets (gzFile stream, char *line, int bufsize)
 {
-  int c=0;
+  char c=0;
   char *cpos;
 
   cpos = line;
@@ -1712,21 +1792,22 @@ main (int argc, char * const argv[])
 {
   char linebuf[linebufsize];
   char *line;
-  char *uniqueid = NULL;
+  char *inputfile = NULL;
 
   const char *text;
   const char *idx1;
   const char *idx2;
   const char *cpos;
   int found = 0;
-  int csum_prog;
-  int lines = 0;
+  uint32_t csum_prog;
+  unsigned long lines = 0;
+  unsigned long rel_line_nr = 0;
   char qidbuf[30];
   int i;
 
   struct tm *ltime;
   struct timeval tv;
-  time_t ctime, start, end;
+  time_t ctime, next_ctime, start, end;
 
   LParser *parser;
   int opt;
@@ -1745,27 +1826,41 @@ main (int argc, char * const argv[])
     exit (-1);
   }
 
-  while ((opt = getopt (argc, argv, "f:t:s:e:h:m:q:x:l:I:vgn")) != -1) {
+  while ((opt = getopt (argc, argv, "f:t:s:e:h:m:q:x:i:l:vgn")) != -1) {
     if (opt == 'f') {
       parser->from = epool_strdup (&parser->ep, optarg);
     } else if (opt == 't') {
       parser->to = epool_strdup (&parser->ep, optarg);
     } else if (opt == 'v') {
-      parser->verbose = 1;
+      parser->verbose += 1;
     } else if (opt == 'g') {
       parser->exclude_greylist = 1;
     } else if (opt == 'n') {
       parser->exclude_ndrs = 1;
-    } else if (opt == 'I') {
-      uniqueid = optarg;
     } else if (opt == 'h') {
       parser->server = epool_strdup (&parser->ep, optarg);
     } else if (opt == 'm') {
       parser->msgid = epool_strdup (&parser->ep, optarg);
     } else if (opt == 'q') {
-      parser->qid = epool_strdup (&parser->ep, optarg);
+      time_t ltime;
+      unsigned long rel_line_nr;
+      MatchList *match = (MatchList *)epool_alloc(&parser->ep, sizeof(MatchList));
+      if (sscanf(optarg, "T%08lXL%08lX", &ltime, &rel_line_nr) == 2) {
+       match->mtype = MatchTypeRelLineNr;
+       match->ltime = ltime;
+       match->rel_line_nr = rel_line_nr;
+       match->next = parser->match_list;
+       parser->match_list = match;
+      } else {
+       match->mtype = MatchTypeQID;
+       match->id = epool_strdup(&parser->ep, optarg);
+       match->next = parser->match_list;
+       parser->match_list = match;
+      }
     } else if (opt == 'x') {
       parser->strmatch = epool_strdup (&parser->ep, optarg);
+    } else if (opt == 'i') {
+      inputfile = optarg;
     } else if (opt == 'l') {
       char *l;
       parser->limit = strtoul (optarg, &l, 0);
@@ -1845,23 +1940,33 @@ main (int argc, char * const argv[])
   }
 
   int filecount;
-  if ((filecount = parser_count_files (parser)) <= 0) {
+  if (inputfile) {
+      filecount = 1;
+  } else if ((filecount = parser_count_files (parser)) <= 0) {
     fprintf (stderr, "unable to access log files\n");
     exit (-1);
   }
 
-  if (uniqueid) {
-    printf ("# LogReader: %d %s\n", getpid(), uniqueid);
-  } else {
-    printf ("# LogReader: %d\n", getpid());
-  }
+  printf ("# LogReader: %d\n", getpid());
 
   printf ("# Query options\n");
   if (parser->from) printf ("# Sender:    %s\n", parser->from);
   if (parser->to) printf ("# Recipient: %s\n", parser->to);
   if (parser->server) printf ("# Server:    %s\n", parser->server);
   if (parser->msgid) printf ("# MsgID:     %s\n", parser->msgid);
-  if (parser->qid) printf ("# QID:       %s\n", parser->qid);
+
+  MatchList *match = parser->match_list;
+  while (match) {
+    if (match->mtype == MatchTypeQID) {
+      printf ("# QID:       %s\n", match->id);
+    } else if (match->mtype == MatchTypeRelLineNr) {
+      printf ("# QID:       T%08lXL%08lX\n", match->ltime, match->rel_line_nr);
+    } else {
+      g_error("internal error - unknown match type %d\n", match->mtype);
+    }
+    match = match->next;
+  }
+
   if (parser->strmatch) printf ("# Match:     %s\n", parser->strmatch);
 
   strftime (linebuf, 256, "%F %T", gmtime (&parser->start));
@@ -1886,7 +1991,14 @@ main (int argc, char * const argv[])
     cur_year = parser->year[i];
 
     if (i <= 1) {
-      if (!(stream = (gpointer) fopen (logfiles[i], "r"))) continue;
+      if (inputfile && strlen(inputfile) == 1 && *inputfile == '-') {
+       stream = (gpointer) stdin;
+      } else if (inputfile) {
+       if (!(stream = (gpointer) fopen (inputfile, "r"))) {
+         fprintf(stderr, "unable to open log file\n");
+         exit (-1);
+       }
+      } else if (!(stream = (gpointer) fopen (logfiles[i], "r"))) continue;
     } else {
       if (!(stream = (gpointer) gzopen (logfiles[i], "r"))) continue;
     }
@@ -1910,9 +2022,20 @@ main (int argc, char * const argv[])
       int pid = 0;
 
       cpos = line;
-      if (!(ctime = parse_time (&cpos, len))) {
+
+      next_ctime = parse_time (&cpos, len);
+
+      if (!next_ctime) {
        continue;
-      } 
+      }
+
+      if (next_ctime != ctime) {
+       rel_line_nr = 0;
+      } else {
+       rel_line_nr++;
+      }
+
+      ctime = next_ctime;
 
       if (ctime < start) continue;
       if (ctime > end) break;
@@ -1936,23 +2059,23 @@ main (int argc, char * const argv[])
       }
 
       //printf ("LINE: %s\n", line);
-      //const char prog = cpos;
+      //const char *prog = cpos;
 
       csum_prog = 0;
       found = 0; while (*cpos && (*cpos != ':') && (*cpos != '[')) { 
-       csum_prog = (csum_prog <<8) + *cpos;
+       csum_prog = ((csum_prog << 8)|(csum_prog >> 24)) + *cpos;
        cpos++; 
        found++; 
       }
 
       //idx1 = g_strndup (prog, found);
       //printf ("TEST:%s:%08X\n", idx1, csum_prog);
-      //g_free (idx1);
+      //free (idx1);
 
       if (*cpos == '[') {
        cpos++;
        found = 0; while (isdigit (*cpos)) { 
-         pid = pid*10 + *cpos - 48; 
+         pid = pid*10 + *cpos - '0';
          cpos++; 
          found++; 
        }
@@ -1984,7 +2107,8 @@ main (int argc, char * const argv[])
        strmatch = 1;
       }
 
-      if (csum_prog == 0x70726F78) { // proxprox
+      if ((csum_prog == PROXPROX) ||
+         (csum_prog == PMG_SMTP_FILTER)) {
 
        if ((idx1 = parse_qid (&cpos, qidbuf, ':', 25))) {
 
@@ -1994,7 +2118,7 @@ main (int argc, char * const argv[])
            continue;
          }
 
-         loglist_add (&fe->ep, &fe->loglist, line, len);
+         loglist_add (&fe->ep, &fe->loglist, line, len, lines);
 
          if (strmatch) fe->strmatch = 1;
 
@@ -2073,7 +2197,7 @@ main (int argc, char * const argv[])
 
        }
 
-      } else if (csum_prog == 0x7265656E) { // postfix/postscreen
+      } else if (csum_prog == POSTFIX_POSTSCREEN) {
 
              SEntry *se;
 
@@ -2110,13 +2234,13 @@ main (int argc, char * const argv[])
          
                      if (*cpos != '>') continue;
 
-                     if (!(se = sentry_get (parser, pid))) {
+                     if (!(se = sentry_get (parser, pid, ctime, rel_line_nr))) {
                              continue;
                      }
 
                      if (strmatch) se->strmatch = 1;
 
-                     loglist_add (&se->ep, &se->loglist, line, len);
+                     loglist_add (&se->ep, &se->loglist, line, len, lines);
 
                      sentry_nqlist_add (se, ctime, from, idx1 - from, to, cpos - to, 'N');
 
@@ -2130,7 +2254,7 @@ main (int argc, char * const argv[])
                      sentry_free (parser, se);
              }
              
-      } else if (csum_prog == 0x716D6772) { // postfix/qmgr
+      } else if (csum_prog == POSTFIX_QMGR) {
 
        if ((idx2 = text) && (idx1 = parse_qid (&idx2, qidbuf, ':', 15))) {
 
@@ -2144,7 +2268,7 @@ main (int argc, char * const argv[])
 
          qe->cleanup = 1;      
 
-         loglist_add (&qe->ep, &qe->loglist, line, len);
+         loglist_add (&qe->ep, &qe->loglist, line, len, lines);
        
          if ((*idx2 == 'f') && !strncmp (idx2, "from=<", 6)) {
 
@@ -2164,7 +2288,7 @@ main (int argc, char * const argv[])
              int size = 0;
              cpos += 7;
 
-             while (isdigit (*cpos)) { size = size*10 + *cpos++ - 48; }
+             while (isdigit (*cpos)) { size = size*10 + *cpos++ - '0'; }
 
              qe->size = size;
            }
@@ -2177,10 +2301,12 @@ main (int argc, char * const argv[])
          }
        }
 
-      } else if ((csum_prog == 0x736D7470) || //postfix/smtp
-                (csum_prog == 0x6C6D7470)) {  //postfix/lmtp
+      } else if ((csum_prog == POSTFIX_SMTP) ||
+                (csum_prog == POSTFIX_LMTP) ||
+                (csum_prog == POSTFIX_LOCAL) ||
+                (csum_prog == POSTFIX_ERROR)) {
 
-       int lmtp = (csum_prog == 0x6C6D7470);
+       int lmtp = (csum_prog == POSTFIX_LMTP);
 
        if ((cpos = text) && (idx1 = parse_qid (&cpos, qidbuf, ':', 15))) {
 
@@ -2194,7 +2320,7 @@ main (int argc, char * const argv[])
 
          qe->cleanup = 1;      
 
-         loglist_add (&qe->ep, &qe->loglist, line, len);
+         loglist_add (&qe->ep, &qe->loglist, line, len, lines);
 
          if (strncmp (cpos, "to=<", 4)) continue;
          cpos += 4;
@@ -2262,7 +2388,7 @@ main (int argc, char * const argv[])
          }
        }
 
-      } else if (csum_prog == 0x6D747064) { // postfix/smtpd
+      } else if (csum_prog == POSTFIX_SMTPD) {
        SEntry *se;
 
        if (!pid) {
@@ -2270,13 +2396,13 @@ main (int argc, char * const argv[])
          continue;
        }
 
-       if (!(se = sentry_get (parser, pid))) {
+       if (!(se = sentry_get (parser, pid, ctime, rel_line_nr))) {
          continue;
        }
 
        if (strmatch) se->strmatch = 1;
 
-       loglist_add (&se->ep, &se->loglist, line, len);
+       loglist_add (&se->ep, &se->loglist, line, len, lines);
 
        if ((*text == 'c') && !strncmp (text, "connect from ", 13)) {
        
@@ -2369,7 +2495,7 @@ main (int argc, char * const argv[])
 
        }
  
-      } else if (csum_prog == 0x616E7570) { // postfix/cleanup
+      } else if (csum_prog == POSTFIX_CLEANUP) {
 
        QEntry *qe;
 
@@ -2379,7 +2505,7 @@ main (int argc, char * const argv[])
 
            if (strmatch) qe->strmatch = 1;
 
-           loglist_add (&qe->ep, &qe->loglist, line, len);
+           loglist_add (&qe->ep, &qe->loglist, line, len, lines);
 
            if ((*idx2 == 'm') && !strncmp (idx2, "message-id=", 11)) {