]>
git.proxmox.com Git - pmg-log-tracker.git/blob - pmg-log-tracker.c
3 (C) 2007-2017 Proxmox Server Solutions GmbH, All Rights Reserved
7 See http://www.proxmox.com for more information
21 #include <sys/types.h>
26 We assume the syslog files belong to one host, i.e. we do not
27 consider the hostname at all
31 REGEX: we do not use posix regular expressions (libc implementation
32 is too slow). fnmatch() is also slow.
33 Future versions may use our own implementation of a DFA. But currently we
34 just use strstr (no regex support at all)
37 //#define STRMATCH(pattern, string) (fnmatch (pattern, string, FNM_CASEFOLD) == 0)
38 #define STRMATCH(pattern, string) (strcasestr (string, pattern) != NULL)
41 //#define EPOOL_BLOCK_SIZE 512
42 //#define EPOOL_MAX_SIZE 128
43 #define EPOOL_BLOCK_SIZE 2048
44 #define EPOOL_MAX_SIZE 128
45 #define MAX_LOGFILES 31
49 typedef struct _SList SList
;
55 typedef struct _NQList NQList
;
64 typedef struct _TOList TOList
;
74 GHashTable
*smtpd_debug_alloc
;
75 GHashTable
*qmgr_debug_alloc
;
76 GHashTable
*filter_debug_alloc
;
77 GHashTable
*smtpd_debug_free
;
78 GHashTable
*qmgr_debug_free
;
79 GHashTable
*filter_debug_free
;
82 // EPool: Entry related memory pools
84 GMemChunk
*mem_chunk_epool
;
91 typedef struct _EPool EPool
;
93 SList
*blocks
; // allocated usiing mem_chunk_epool
94 SList
*mblocks
; // allocated use g_malloc
105 GHashTable
*filter_h
;
106 //GHashTable *track_h;
107 gzFile
*stream
[MAX_LOGFILES
];
110 time_t year
[MAX_LOGFILES
];
121 unsigned int exclude_greylist
:1;
122 unsigned int exclude_ndrs
:1;
125 typedef struct _SEntry SEntry
;
126 typedef struct _QEntry QEntry
;
127 typedef struct _FEntry FEntry
;
129 typedef struct _LogEntry LogEntry
;
130 typedef struct _LogList LogList
;
139 LogEntry
*logs_last
; // pointer to last log (speedup add log)
142 // SEntry: Store SMTPD related logs
157 //unsigned int external:1; // not from local host
158 unsigned int disconnect
:1;
159 unsigned int strmatch
:1;
163 // QEntry: Store Queue (qmgr, smtp, lmtp) related logs
182 unsigned int cleanup
:1;
183 unsigned int removed
:1;
184 unsigned int filtered
:1; // set when passed via lmtp to filter
185 unsigned int strmatch
:1;
188 // FEntry: Store filter (proxprox) related logs
195 char *logid
; // proxprox log id
201 unsigned int finished
:1;
202 unsigned int strmatch
:1;
206 void debug_error (char *msg
, const char *line
);
207 SList
*slist_remove (SList
*list
, gpointer data
);
209 EPool
*epool_init (EPool
*ep
);
210 void epool_free (EPool
*ep
);
211 gpointer
epool_alloc (EPool
*ep
, int size
);
212 char *epool_strndup (EPool
*ep
, const char *s
, int len
);
213 char *epool_strndup0 (EPool
*ep
, const char *s
, int len
);
214 char *epool_strdup (EPool
*ep
, const char *s
);
216 void loglist_print (LogList
*loglist
);
217 void loglist_add (EPool
*ep
, LogList
*loglist
, const char *text
, int len
);
219 SEntry
*sentry_new (int pid
);
220 SEntry
*sentry_get (LParser
*parser
, int pid
);
221 void sentry_ref_add (SEntry
*sentry
, QEntry
*qentry
);
222 int sentry_ref_del (SEntry
*sentry
, QEntry
*qentry
);
223 void sentry_ref_finalize (LParser
*parser
, SEntry
*sentry
);
224 int sentry_ref_rem_unneeded (LParser
*parser
, SEntry
*sentry
);
225 void sentry_nqlist_add (SEntry
*sentry
, time_t ltime
, const char *from
, int from_len
,
226 const char *to
, int to_len
, char dstatus
);
227 void sentry_print (LParser
*parser
, SEntry
*sentry
);
228 void sentry_set_connect (SEntry
*sentry
, const char *connect
, int len
);
229 void sentry_free_noremove (SEntry
*sentry
);
230 void sentry_free (LParser
*parser
, SEntry
*sentry
);
231 void sentry_cleanup_hash (gpointer key
, gpointer value
, gpointer user_data
);
234 QEntry
*qentry_new (const char *qid
);
235 QEntry
*qentry_get (LParser
*parser
, const char *qid
);
236 void qentry_tolist_add (QEntry
*qentry
, time_t ltime
, char dstatus
, const char *to
,
237 int to_len
, const char *relay
, int relay_len
);
238 void qentry_set_from (QEntry
*qentry
, const char *from
, int len
);
239 void qentry_set_msgid (QEntry
*qentry
, const char *msgid
, int len
);
240 void qentry_set_client (QEntry
*qentry
, const char *client
, int len
);
241 void qentry_print (LParser
*parser
, QEntry
*qentry
);
242 void qentry_finalize (LParser
*parser
, QEntry
*qentry
);
243 void qentry_free_noremove (LParser
*parser
, QEntry
*qentry
);
244 void qentry_free (LParser
*parser
, QEntry
*qentry
);
245 void qentry_cleanup_hash (gpointer key
, gpointer value
, gpointer user_data
);
248 FEntry
*fentry_new (const char *logid
);
249 FEntry
*fentry_get (LParser
*parser
, const char *logid
);
250 void fentry_tolist_add (FEntry
*fentry
, char dstatus
, const char *to
,
251 int to_len
, const char *qid
, int qid_len
);
252 void fentry_free_noremove (LParser
*parser
, FEntry
*fentry
);
253 void fentry_free (LParser
*parser
, FEntry
*fentry
);
254 void fentry_cleanup_hash (gpointer key
, gpointer value
, gpointer user_data
);
256 LParser
*parser_new ();
257 void parser_free (LParser
*parser
);
259 //char *parser_track (LParser *parser, const char *qid, gboolean insert);
263 //#define LOGPATH "./log/"
264 #define LOGPATH "/var/log/"
265 //#define LOGPATH "/var/log5/"
267 static const char *logfiles
[] = {
270 LOGPATH
"syslog.2.gz",
271 LOGPATH
"syslog.3.gz",
272 LOGPATH
"syslog.4.gz",
273 LOGPATH
"syslog.5.gz",
274 LOGPATH
"syslog.6.gz",
275 LOGPATH
"syslog.7.gz",
276 LOGPATH
"syslog.8.gz",
277 LOGPATH
"syslog.9.gz",
278 LOGPATH
"syslog.10.gz",
279 LOGPATH
"syslog.11.gz",
280 LOGPATH
"syslog.12.gz",
281 LOGPATH
"syslog.13.gz",
282 LOGPATH
"syslog.14.gz",
283 LOGPATH
"syslog.15.gz",
284 LOGPATH
"syslog.16.gz",
285 LOGPATH
"syslog.17.gz",
286 LOGPATH
"syslog.18.gz",
287 LOGPATH
"syslog.19.gz",
288 LOGPATH
"syslog.20.gz",
289 LOGPATH
"syslog.21.gz",
290 LOGPATH
"syslog.22.gz",
291 LOGPATH
"syslog.23.gz",
292 LOGPATH
"syslog.24.gz",
293 LOGPATH
"syslog.25.gz",
294 LOGPATH
"syslog.26.gz",
295 LOGPATH
"syslog.27.gz",
296 LOGPATH
"syslog.28.gz",
297 LOGPATH
"syslog.29.gz",
298 LOGPATH
"syslog.30.gz",
299 LOGPATH
"syslog.31.gz",
303 debug_error (char *msg
, const char *line
)
306 fprintf (stderr
, "ERROR: %s\n", msg
);
307 if (line
) fprintf (stderr
, "LINE: %s\n", line
);
316 slist_remove (SList
*list
, gpointer data
)
322 if (l
->data
== data
) {
341 epool_init (EPool
*ep
)
346 data
= g_mem_chunk_alloc0 (mem_chunk_epool
);
347 blocks
= (SList
*)data
;
350 ep
->allocated
+= EPOOL_BLOCK_SIZE
;
351 ep_allocated
+= EPOOL_BLOCK_SIZE
;
352 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
360 ep
->cpos
= sizeof (SList
);
366 epool_free (EPool
*ep
)
372 ep_allocated
-= ep
->allocated
;
373 printf ("MEM: %d\n", ep_allocated
);
391 g_mem_chunk_free (mem_chunk_epool
, data
);
396 epool_alloc (EPool
*ep
, int size
)
398 int rs
= (size
+ 3) & ~3;
399 int space
= EPOOL_BLOCK_SIZE
- sizeof (SList
) - ep
->cpos
;
402 if (size
> EPOOL_MAX_SIZE
) {
404 if (space
>= sizeof (SList
)) {
405 blocks
= ep
->blocks
->data
+ ep
->cpos
;
406 ep
->cpos
+= sizeof (SList
);
408 blocks
= (SList
*)epool_alloc (ep
, sizeof (SList
));
411 data
= g_malloc (size
);
413 ep
->allocated
+= size
;
414 ep_allocated
+= size
;
415 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
418 blocks
->next
= ep
->mblocks
;
420 ep
->mblocks
= blocks
;
424 } else if (space
>= rs
) {
425 data
= ep
->blocks
->data
+ ep
->cpos
;
431 SList
*blocks
= ep
->blocks
->data
+ ep
->cpos
;
433 data
= g_mem_chunk_alloc0 (mem_chunk_epool
);
435 blocks
->next
= ep
->blocks
;
441 ep
->allocated
+= EPOOL_BLOCK_SIZE
;
442 ep_allocated
+= EPOOL_BLOCK_SIZE
;
443 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
451 epool_strndup (EPool
*ep
, const char *s
, int len
)
454 char *res
= epool_alloc (ep
, l
);
460 epool_strndup0 (EPool
*ep
, const char *s
, int len
)
462 char *res
= epool_alloc (ep
, len
+ 1);
463 strncpy (res
, s
, len
);
470 epool_strdup (EPool
*ep
, const char *s
)
472 int l
= strlen (s
) + 1;
473 char *res
= epool_alloc (ep
, l
);
479 loglist_print (LogList
*loglist
)
481 LogEntry
*log
= loglist
->log
;
483 printf ("%s", log
->text
);
490 loglist_add (EPool
*ep
, LogList
*loglist
, const char *text
, int len
)
495 if (len
!= strlen (text
)) {
496 debug_error ("string with wrong len", NULL
);
499 if (text
[len
] != 0) {
500 debug_error ("string is not null terminated", NULL
);
504 log
= epool_alloc (ep
, sizeof (LogEntry
));
506 log
->text
= epool_strndup (ep
, text
, len
);
509 if (loglist
->logs_last
) {
510 loglist
->logs_last
->next
= log
;
511 loglist
->logs_last
= log
;
514 loglist
->logs_last
= log
;
527 sentry
= (SEntry
*)g_mem_chunk_alloc0 (mem_chunk_epool
);
531 sentry
->ep
.allocated
+= EPOOL_BLOCK_SIZE
;
532 ep_allocated
+= EPOOL_BLOCK_SIZE
;
533 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
539 if ((se
= g_hash_table_lookup (smtpd_debug_alloc
, sentry
))) {
540 debug_error ("SEntry already alloced", NULL
);
542 g_hash_table_insert (smtpd_debug_alloc
, sentry
, sentry
);
547 cpos
= sizeof (SEntry
);
549 blocks
= (SList
*)((char *)sentry
+ cpos
);
551 cpos
+= sizeof (SList
);
553 blocks
->data
= sentry
;
556 sentry
->ep
.blocks
= blocks
;
557 sentry
->ep
.cpos
= cpos
;
563 sentry_get (LParser
*parser
, int pid
)
567 if ((sentry
= g_hash_table_lookup (parser
->smtpd_h
, &pid
))) {
571 if ((sentry
= sentry_new (pid
))) {
572 g_hash_table_insert (parser
->smtpd_h
, &sentry
->pid
, sentry
);
580 sentry_ref_add (SEntry
*sentry
, QEntry
*qentry
)
585 if (qentry
->smtpd
!= sentry
) {
586 debug_error ("qentry ref already set", NULL
);
593 if (l
->data
== qentry
) return;
597 l
= epool_alloc (&sentry
->ep
, sizeof (SList
));
599 qentry
->smtpd
= sentry
;
602 l
->next
= sentry
->refs
;
608 sentry_ref_del (SEntry
*sentry
, QEntry
*qentry
)
610 SList
*l
= sentry
->refs
;
613 if (!qentry
->smtpd
) {
614 debug_error ("qentry does not hav a qentry ref", NULL
);
621 QEntry
*qe
= (QEntry
*)l
->data
;
625 if (qe
&& qe
->cleanup
) count
++;
634 sentry_ref_finalize (LParser
*parser
, SEntry
*sentry
)
636 SList
*l
= sentry
->refs
;
642 QEntry
*qe
= l
->data
;
650 if (!qe
->removed
) continue;
652 FEntry
*fe
= qe
->filter
;
654 if (fe
&& !fe
->finished
) continue;
658 qentry_print (parser
, qe
);
663 qentry_free (parser
, qe
);
665 if (fe
) fentry_free (parser
, fe
);
669 if (!count
) sentry_free_noremove (sentry
);
673 sentry_ref_rem_unneeded (LParser
*parser
, SEntry
*sentry
)
675 SList
*l
= sentry
->refs
;
679 QEntry
*qe
= (QEntry
*)l
->data
;
684 qentry_free (parser
, qe
);
698 sentry_nqlist_add (SEntry
*sentry
, time_t ltime
, const char *from
, int from_len
,
699 const char *to
, int to_len
, char dstatus
)
701 NQList
*nq
= (NQList
*)epool_alloc (&sentry
->ep
, sizeof (NQList
));
703 nq
->from
= epool_strndup0 (&sentry
->ep
, from
, from_len
);
704 nq
->to
= epool_strndup0 (&sentry
->ep
, to
, to_len
);
705 nq
->dstatus
= dstatus
;
707 nq
->next
= sentry
->nqlist
;
713 sentry_print (LParser
*parser
, SEntry
*sentry
)
717 if (parser
->msgid
|| parser
->qid
) return;
719 if (parser
->server
) {
720 if (!sentry
->connect
) return;
721 if (!strcasestr (sentry
->connect
, parser
->server
)) return;
724 if (parser
->from
|| parser
->to
||
725 parser
->exclude_greylist
|| parser
->exclude_ndrs
) {
730 if (!*(parser
->from
)) {
731 if (*(nq
->from
)) nq
->dstatus
= 0;
732 } else if (!STRMATCH(parser
->from
, nq
->from
)) {
737 if (parser
->exclude_greylist
&& nq
->dstatus
== 'G') nq
->dstatus
= 0;
739 if (parser
->exclude_ndrs
&& nq
->from
&& !*nq
->from
) nq
->dstatus
= 0;
741 if (parser
->to
&& nq
->to
&& !STRMATCH(parser
->to
, nq
->to
)) {
745 if (nq
->dstatus
!= 0) found
= 1;
752 if (parser
->strmatch
&& !sentry
->strmatch
) return;
754 if (parser
->verbose
) {
758 printf ("CTIME: %08lX\n", parser
->ctime
);
760 if (sentry
->connect
) { printf ("CONNECT: %s\n", sentry
->connect
); }
761 //printf ("EXTERNAL: %d\n", sentry->external);
767 if (nq
->from
&& nq
->to
&& nq
->dstatus
) {
768 printf ("TO:%08lX:00000000000:%c: from <%s> to <%s>\n", nq
->ltime
, nq
->dstatus
, nq
->from
, nq
->to
);
774 if (parser
->verbose
) {
776 loglist_print (&sentry
->loglist
);
784 sentry_set_connect (SEntry
*sentry
, const char *connect
, int len
)
786 if (sentry
->connect
) {
788 if (strncmp (sentry
->connect
, connect
, len
)) {
789 debug_error ("duplicate connect", NULL
);
793 sentry
->connect
= epool_strndup0 (&sentry
->ep
, connect
, len
);
798 sentry_free_noremove (SEntry
*sentry
)
804 ep_allocated
-= sentry
->ep
.allocated
;
805 printf ("MEM: %d\n", ep_allocated
);
811 if ((se
= g_hash_table_lookup (smtpd_debug_free
, sentry
))) {
812 debug_error ("SEntry already freed", NULL
);
814 g_hash_table_insert (smtpd_debug_free
, sentry
, sentry
);
820 l
= sentry
->ep
.mblocks
;
827 l
= sentry
->ep
.blocks
;
831 g_mem_chunk_free (mem_chunk_epool
, data
);
836 sentry_free (LParser
*parser
, SEntry
*sentry
)
838 g_hash_table_remove (parser
->smtpd_h
, &sentry
->pid
);
840 sentry_free_noremove (sentry
);
844 sentry_cleanup_hash (gpointer key
,
849 LParser
*parser
= (LParser
*)user_data
;
851 sentry_print (parser
, se
);
852 sentry_free_noremove (se
);
857 sentry_debug_alloc (gpointer key
,
861 LParser
*parser
= (LParser
*)user_data
;
865 if ((fe
= g_hash_table_lookup (smtpd_debug_free
, se
))) {
869 printf ("FOUND ALLOCATED SENTRY:\n");
870 sentry_print (parser
, se
);
879 qentry_new (const char *qid
)
886 qentry
= (QEntry
*)g_mem_chunk_alloc0 (mem_chunk_epool
);
889 qentry
->ep
.allocated
+= EPOOL_BLOCK_SIZE
;
890 ep_allocated
+= EPOOL_BLOCK_SIZE
;
891 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
897 if ((qe
= g_hash_table_lookup (qmgr_debug_alloc
, qentry
))) {
898 debug_error ("QEntry already alloced", NULL
);
900 g_hash_table_insert (qmgr_debug_alloc
, qentry
, qentry
);
905 cpos
= sizeof (QEntry
);
907 blocks
= (SList
*)((char *)qentry
+ cpos
);
909 cpos
+= sizeof (SList
);
911 blocks
->data
= qentry
;
914 qentry
->qid
= qid_cp
= (char *)qentry
+ cpos
;
915 while ((*qid_cp
++ = *qid
++)) cpos
++;
917 cpos
= (cpos
+ 4) & ~3;
919 qentry
->ep
.blocks
= blocks
;
920 qentry
->ep
.cpos
= cpos
;;
926 qentry_tolist_add (QEntry
*qentry
, time_t ltime
, char dstatus
, const char *to
, int to_len
,
927 const char *relay
, int relay_len
)
929 TOList
*tl
= (TOList
*)epool_alloc (&qentry
->ep
, sizeof (TOList
));
931 tl
->to
= epool_strndup0 (&qentry
->ep
, to
, to_len
);
932 tl
->relay
= epool_strndup0 (&qentry
->ep
, relay
, relay_len
);
933 tl
->dstatus
= dstatus
;
935 tl
->next
= qentry
->tolist
;
941 qentry_set_from (QEntry
*qentry
, const char *from
, int len
)
945 if (strncmp (qentry
->from
, from
, len
)) {
946 debug_error ("duplicate from", NULL
);
950 qentry
->from
= epool_strndup0 (&qentry
->ep
, from
, len
);
955 qentry_set_msgid (QEntry
*qentry
, const char *msgid
, int len
)
959 if (strncmp (qentry
->msgid
, msgid
, len
)) {
960 debug_error ("duplicate msgid", NULL
);
964 qentry
->msgid
= epool_strndup0 (&qentry
->ep
, msgid
, len
);
969 qentry_set_client (QEntry
*qentry
, const char *client
, int len
)
971 if (qentry
->client
) {
973 if (strncmp (qentry
->client
, client
, len
)) {
974 debug_error ("duplicate client", NULL
);
978 qentry
->client
= epool_strndup0 (&qentry
->ep
, client
, len
);
983 qentry_print (LParser
*parser
, QEntry
*qentry
)
986 SEntry
*se
= qentry
->smtpd
;
987 FEntry
*fe
= qentry
->filter
;
990 if (!qentry
->msgid
) return;
991 if (strcasecmp (parser
->msgid
, qentry
->msgid
)) return;
996 if (fe
&& !strcmp (fe
->logid
, parser
->qid
)) found
= 1;
997 if (!strcmp (qentry
->qid
, parser
->qid
)) found
= 1;
1002 if (parser
->server
) {
1004 if (se
&& se
->connect
&& strcasestr (se
->connect
, parser
->server
)) found
= 1;
1005 if (qentry
->client
&& strcasestr (qentry
->client
, parser
->server
)) found
= 1;
1011 if (!qentry
->from
) return;
1012 if (!*(parser
->from
)) {
1013 if (*(qentry
->from
)) return;
1014 } else if (!STRMATCH(parser
->from
, qentry
->from
)) {
1018 if (parser
->exclude_ndrs
&& qentry
->from
&& !*qentry
->from
) return;
1022 tl
= qentry
->tolist
;
1025 if (parser
->to
&& !STRMATCH(parser
->to
, tl
->to
)) {
1035 if (parser
->strmatch
&&
1036 !(qentry
->strmatch
|| (se
&& se
->strmatch
) || (fe
&& fe
->strmatch
)))
1040 if (parser
->verbose
) {
1042 printf ("QENTRY: %s\n", qentry
->qid
);
1044 printf ("CTIME: %08lX\n", parser
->ctime
);
1045 printf ("SIZE: %u\n", qentry
->size
);
1047 if (qentry
->client
) { printf ("CLIENT: %s\n", qentry
->client
); }
1049 if (qentry
->msgid
) { printf ("MSGID: %s\n", qentry
->msgid
); }
1053 tl
= qentry
->tolist
;
1057 if (fe
&& tl
->dstatus
== '2') {
1060 if (fl
->to
&& !strcmp (tl
->to
, fl
->to
)) {
1072 dstatus
= fl
->dstatus
;
1076 dstatus
= tl
->dstatus
;
1080 printf ("TO:%08lX:%s:%c: from <%s> to <%s> (%s)\n", tl
->ltime
, qentry
->qid
, dstatus
, qentry
->from
? qentry
->from
: "", to
? to
: "", relay
? relay
: "none");
1088 if (!parser
->verbose
) { fflush (stdout
); return; }
1092 if (se
->loglist
.log
) {
1094 loglist_print (&se
->loglist
);
1099 if (fe
->loglist
.log
) {
1100 printf ("FILTER: %s\n", fe
->logid
);
1101 loglist_print (&fe
->loglist
);
1105 if (qentry
->loglist
.log
) {
1107 loglist_print (&qentry
->loglist
);
1118 qentry_get (LParser
*parser
, const char *qid
)
1122 if ((qentry
= g_hash_table_lookup (parser
->qmgr_h
, qid
))) {
1125 if ((qentry
= qentry_new (qid
))) {
1126 g_hash_table_insert (parser
->qmgr_h
, qentry
->qid
, qentry
);
1134 qentry_free_noremove (LParser
*parser
, QEntry
*qentry
)
1140 if ((se
= qentry
->smtpd
)) {
1141 if (sentry_ref_del (se
, qentry
) == 0) {
1142 if (se
->disconnect
) {
1143 sentry_free_noremove (se
);
1149 ep_allocated
-= qentry
->ep
.allocated
;
1150 printf ("MEM: %d\n", ep_allocated
);
1156 if ((qe
= g_hash_table_lookup (qmgr_debug_free
, qentry
))) {
1157 debug_error ("QEntry already freed", NULL
);
1159 g_hash_table_insert (qmgr_debug_free
, qentry
, qentry
);
1165 l
= qentry
->ep
.mblocks
;
1172 l
= qentry
->ep
.blocks
;
1176 g_mem_chunk_free (mem_chunk_epool
, data
);
1181 qentry_free (LParser
*parser
, QEntry
*qentry
)
1183 g_hash_table_remove (parser
->qmgr_h
, qentry
->qid
);
1185 qentry_free_noremove (parser
, qentry
);
1189 qentry_cleanup_hash (gpointer key
,
1194 LParser
*parser
= (LParser
*)user_data
;
1196 qentry_print (parser
, qe
);
1197 qentry_free_noremove (parser
, qe
);
1201 qentry_finalize (LParser
*parser
, QEntry
*qentry
)
1203 if (qentry
&& qentry
->removed
) {
1204 SEntry
*se
= qentry
->smtpd
;
1206 if (se
&& !se
->disconnect
) return;
1208 FEntry
*fe
= qentry
->filter
;
1210 if (fe
&& !fe
->finished
) return;
1212 qentry_print (parser
, qentry
);
1213 qentry_free (parser
, qentry
);
1215 if (fe
) fentry_free (parser
, fe
);
1222 fentry_new (const char *logid
)
1229 fentry
= (FEntry
*)g_mem_chunk_alloc0 (mem_chunk_epool
);
1232 fentry
->ep
.allocated
+= EPOOL_BLOCK_SIZE
;
1233 ep_allocated
+= EPOOL_BLOCK_SIZE
;
1234 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
1240 if ((fe
= g_hash_table_lookup (filter_debug_alloc
, fentry
))) {
1241 debug_error ("FEntry already alloced", NULL
);
1243 g_hash_table_insert (filter_debug_alloc
, fentry
, fentry
);
1248 cpos
= sizeof (FEntry
);
1250 blocks
= (SList
*)((char *)fentry
+ cpos
);
1252 cpos
+= sizeof (SList
);
1254 blocks
->data
= fentry
;
1255 blocks
->next
= NULL
;
1257 fentry
->logid
= logid_cp
= (char *)fentry
+ cpos
;
1258 while ((*logid_cp
++ = *logid
++)) cpos
++;
1259 cpos
= (cpos
+ 4) & ~3;
1261 fentry
->ep
.blocks
= blocks
;
1262 fentry
->ep
.cpos
= cpos
;;
1268 fentry_get (LParser
*parser
, const char *logid
)
1272 if ((fentry
= g_hash_table_lookup (parser
->filter_h
, logid
))) {
1275 if ((fentry
= fentry_new (logid
))) {
1276 g_hash_table_insert (parser
->filter_h
, fentry
->logid
, fentry
);
1284 fentry_tolist_add (FEntry
*fentry
, char dstatus
, const char *to
, int to_len
,
1285 const char *qid
, int qid_len
)
1287 TOList
*tl
= (TOList
*)epool_alloc (&fentry
->ep
, sizeof (TOList
));
1289 tl
->to
= epool_strndup0 (&fentry
->ep
, to
, to_len
);
1292 tl
->relay
= epool_strndup0 (&fentry
->ep
, qid
, qid_len
);
1296 tl
->dstatus
= dstatus
;
1297 tl
->next
= fentry
->tolist
;
1299 fentry
->tolist
= tl
;
1303 fentry_free_noremove (LParser
*parser
, FEntry
*fentry
)
1309 ep_allocated
-= fentry
->ep
.allocated
;
1310 printf ("MEM: %d\n", ep_allocated
);
1316 if ((fe
= g_hash_table_lookup (filter_debug_free
, fentry
))) {
1317 debug_error ("FEntry already freed", NULL
);
1319 g_hash_table_insert (filter_debug_free
, fentry
, fentry
);
1325 l
= fentry
->ep
.mblocks
;
1332 l
= fentry
->ep
.blocks
;
1336 g_mem_chunk_free (mem_chunk_epool
, data
);
1341 fentry_free (LParser
*parser
, FEntry
*fentry
)
1343 g_hash_table_remove (parser
->filter_h
, fentry
->logid
);
1345 fentry_free_noremove (parser
, fentry
);
1349 fentry_cleanup_hash (gpointer key
,
1354 LParser
*parser
= (LParser
*)user_data
;
1356 fentry_free_noremove (parser
, fe
);
1364 LParser
*parser
= g_malloc0 (sizeof (LParser
));
1369 epool_init (&parser
->ep
);
1371 if (!(parser
->smtpd_h
= g_hash_table_new (g_int_hash
, g_int_equal
))) {
1375 if (!(parser
->qmgr_h
= g_hash_table_new (g_str_hash
, g_str_equal
))) {
1379 if (!(parser
->filter_h
= g_hash_table_new (g_str_hash
, g_str_equal
))) {
1383 //if (!(parser->track_h = g_hash_table_new (g_str_hash, g_str_equal))) {
1387 for (i
= 0; i
<= MAX_LOGFILES
; i
++) {
1388 gettimeofday(&tv
, NULL
);
1389 tv
.tv_sec
-= 3600*24*i
;
1390 ltime
= localtime (&tv
.tv_sec
);
1391 parser
->year
[i
] = ltime
->tm_year
+ 1900;
1398 parser_free (LParser
*parser
)
1402 for (i
= 0; i
< MAX_LOGFILES
; i
++) {
1403 if (parser
->stream
[i
]) gzclose (parser
->stream
[i
]);
1406 epool_free (&parser
->ep
);
1408 g_hash_table_destroy (parser
->smtpd_h
);
1409 g_hash_table_destroy (parser
->qmgr_h
);
1410 g_hash_table_destroy (parser
->filter_h
);
1411 //g_hash_table_destroy (parser->track_h);
1418 parser_track (LParser
*parser
, const char *qid
, gboolean insert
)
1422 if ((res
= g_hash_table_lookup (parser
->track_h
, qid
))) {
1425 if (insert
&& (res
= epool_strdup (&parser
->ep
, qid
))) {
1426 g_hash_table_insert (parser
->track_h
, res
, res
);
1434 static const int linebufsize
= 8192;
1435 static int cur_year
;
1436 static int cur_month
= 0;
1437 static int cal_mtod
[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
1440 mkgmtime (struct tm
*tm
)
1444 int year
= tm
->tm_year
+ 1900;
1445 int mon
= tm
->tm_mon
;
1447 res
= (year
- 1970) * 365 + cal_mtod
[mon
];
1449 // leap year corrections (gregorian calendar)
1450 if (mon
<= 1) year
-= 1;
1451 res
+= (year
- 1968) / 4;
1452 res
-= (year
- 1900) / 100;
1453 res
+= (year
- 1600) / 400;
1455 res
+= tm
->tm_mday
- 1;
1456 res
= res
*24 + tm
->tm_hour
;
1457 res
= res
*60 + tm
->tm_min
;
1458 res
= res
*60 + tm
->tm_sec
;
1464 parse_time (const char **text
, int len
)
1468 int year
= cur_year
;
1477 const char *line
= *text
;
1479 if (len
== (linebufsize
- 1)) {
1480 debug_error ("skipping long line data", line
);
1485 debug_error ("skipping short line data", line
);
1490 int csum
= (line
[0]<<16) + (line
[1]<<8) + line
[2];
1493 case 4874606: mon
= 0; break;
1494 case 4613474: mon
= 1; break;
1495 case 5071218: mon
= 2; break;
1496 case 4288626: mon
= 3; break;
1497 case 5071225: mon
= 4; break;
1498 case 4879726: mon
= 5; break;
1499 case 4879724: mon
= 6; break;
1500 case 4289895: mon
= 7; break;
1501 case 5465456: mon
= 8; break;
1502 case 5202804: mon
= 9; break;
1503 case 5140342: mon
= 10; break;
1504 case 4482403: mon
= 11; break;
1506 debug_error ("unable to parse month", line
);
1510 // year change heuristik
1511 if (cur_month
== 11 && mon
== 0) {
1514 if (mon
> cur_month
) cur_month
= mon
;
1516 ltime
= (year
- 1970)*365 + cal_mtod
[mon
];
1518 // leap year corrections (gregorian calendar)
1519 if (mon
<= 1) year
-= 1;
1520 ltime
+= (year
- 1968) / 4;
1521 ltime
-= (year
- 1900) / 100;
1522 ltime
+= (year
- 1600) / 400;
1524 const char *cpos
= line
+ 3;
1526 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
1528 debug_error ("missing spaces after month", line
);
1532 found
= 0; while (isdigit (*cpos
)) { mday
= mday
*10 + *cpos
- 48; cpos
++; found
++; }
1533 if (found
< 1 || found
> 2) {
1534 debug_error ("unable to parse day of month", line
);
1540 found
= 0; while (isspace (*cpos
)) { cpos
++; found
++; }
1542 debug_error ("missing spaces after day of month", line
);
1546 found
= 0; while (isdigit (*cpos
)) { hour
= hour
*10 + *cpos
- 48; cpos
++; found
++; }
1547 if (found
< 1 || found
> 2) {
1548 debug_error ("unable to parse hour", line
);
1556 debug_error ("missing collon after hour", line
);
1561 found
= 0; while (isdigit (*cpos
)) { min
= min
*10 + *cpos
- 48; cpos
++; found
++; }
1562 if (found
< 1 || found
> 2) {
1563 debug_error ("unable to parse minute", line
);
1571 debug_error ("missing collon after minute", line
);
1576 found
= 0; while (isdigit (*cpos
)) { sec
= sec
*10 + *cpos
- 48; cpos
++; found
++; }
1577 if (found
< 1 || found
> 2) {
1578 debug_error ("unable to parse second", line
);
1585 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
1587 debug_error ("missing spaces after time", line
);
1598 parser_count_files (LParser
*parser
)
1601 time_t start
= parser
->start
;
1602 char linebuf
[linebufsize
];
1606 for (i
= 0; i
< MAX_LOGFILES
; i
++) {
1607 cur_year
= parser
->year
[i
];
1610 if ((stream
= gzopen (logfiles
[i
], "r"))) {
1611 if ((line
= gzgets (stream
, linebuf
, linebufsize
))) {
1612 if (parse_time (&line
, strlen (line
)) < start
) {
1628 parse_qid (const char **text
, char *out
, char delim
, int maxlen
)
1635 while (isxdigit (*idx
)) { *copy
++ = *idx
++; found
++; if (found
> maxlen
) break; }
1637 if (found
> 1 && found
< maxlen
&&
1638 ((delim
&& (*idx
== delim
)) || (!delim
&& isspace (*idx
)))) {
1641 while (isspace (*idx
)) idx
++;
1649 print_usage (const char *name
)
1651 fprintf (stderr
, "usage: %s [OPTIONS] [OUTPUTFILENAME]\n", name
);
1652 fprintf (stderr
, "\t-f SENDER mails from SENDER\n");
1653 fprintf (stderr
, "\t-t RECIPIENT mails to RECIPIENT\n");
1654 fprintf (stderr
, "\t-h Server Server IP or Hostname\n");
1655 fprintf (stderr
, "\t-s START start time (YYYY-MM-DD HH:MM:SS)\n");
1656 fprintf (stderr
, "\t or seconds since epoch\n");
1657 fprintf (stderr
, "\t-e END end time (YYYY-MM-DD HH:MM:SS)\n");
1658 fprintf (stderr
, "\t or seconds since epoch\n");
1659 fprintf (stderr
, "\t-m MSGID message ID (exact match)\n");
1660 fprintf (stderr
, "\t-q QID queue ID (exact match)\n");
1661 fprintf (stderr
, "\t-x STRING search for strings\n");
1662 fprintf (stderr
, "\t-l LIMIT print max limit entries\n");
1663 fprintf (stderr
, "\t-v verbose output\n");
1667 // gzgets is ways too slow, so we do it our own way
1670 mygzgetc (gzFile stream
)
1673 static char readbuf
[16384];
1674 static char *readend
= readbuf
+ sizeof (readbuf
);
1675 static char *readpos
= readbuf
+ sizeof (readbuf
);
1677 if (readpos
< readend
) return *readpos
++;
1679 if ((br
= gzread (stream
, readbuf
, sizeof (readbuf
))) <= 0) {
1683 readend
= readbuf
+ br
;
1690 mygzgets (gzFile stream
, char *line
, int bufsize
)
1696 while (--bufsize
> 0 && (c
= mygzgetc(stream
)) != -1) {
1701 if (c
== -1 && cpos
== line
)
1708 extern char *optarg
;
1709 extern int optind
, opterr
, optopt
;
1712 main (int argc
, char * const argv
[])
1714 char linebuf
[linebufsize
];
1716 char *uniqueid
= NULL
;
1731 time_t ctime
, start
, end
;
1737 smtpd_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1738 qmgr_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1739 filter_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1740 smtpd_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1741 qmgr_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1742 filter_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1745 if (!(mem_chunk_epool
= g_mem_chunk_new ("EPOOL", EPOOL_BLOCK_SIZE
,
1746 1000*EPOOL_BLOCK_SIZE
,
1747 G_ALLOC_AND_FREE
))) {
1748 fprintf (stderr
, "unable to initialize epool\n");
1752 if (!(parser
= parser_new ())) {
1753 fprintf (stderr
, "unable to alloc parser structure\n");
1757 while ((opt
= getopt (argc
, argv
, "f:t:s:e:h:m:q:x:l:I:vgn")) != -1) {
1759 parser
->from
= epool_strdup (&parser
->ep
, optarg
);
1760 } else if (opt
== 't') {
1761 parser
->to
= epool_strdup (&parser
->ep
, optarg
);
1762 } else if (opt
== 'v') {
1763 parser
->verbose
= 1;
1764 } else if (opt
== 'g') {
1765 parser
->exclude_greylist
= 1;
1766 } else if (opt
== 'n') {
1767 parser
->exclude_ndrs
= 1;
1768 } else if (opt
== 'I') {
1770 } else if (opt
== 'h') {
1771 parser
->server
= epool_strdup (&parser
->ep
, optarg
);
1772 } else if (opt
== 'm') {
1773 parser
->msgid
= epool_strdup (&parser
->ep
, optarg
);
1774 } else if (opt
== 'q') {
1775 parser
->qid
= epool_strdup (&parser
->ep
, optarg
);
1776 } else if (opt
== 'x') {
1777 parser
->strmatch
= epool_strdup (&parser
->ep
, optarg
);
1778 } else if (opt
== 'l') {
1780 parser
->limit
= strtoul (optarg
, &l
, 0);
1781 if (!*optarg
|| *l
) {
1782 fprintf (stderr
, "unable to parse limit '%s'\n", optarg
);
1785 } else if (opt
== 's') {
1786 // use strptime to convert time
1789 if ((!(res
= strptime (optarg
, "%F %T", &tm
)) &&
1790 !(res
= strptime (optarg
, "%s", &tm
))) || *res
) {
1791 fprintf (stderr
, "unable to parse start time\n");
1794 parser
->start
= mkgmtime (&tm
);
1796 } else if (opt
== 'e') {
1799 if ((!(res
= strptime (optarg
, "%F %T", &tm
)) &&
1800 !(res
= strptime (optarg
, "%s", &tm
))) || *res
) {
1801 fprintf (stderr
, "unable to parse end time\n");
1804 parser
->end
= mkgmtime (&tm
);
1807 print_usage (argv
[0]);
1812 if (optind
< argc
) {
1814 if ((argc
- optind
) > 1) {
1815 print_usage (argv
[0]);
1819 char *tmpfn
= g_strdup_printf ("/tmp/.proxtrack-%08X.txt", getpid ());
1821 if ((stdout
= freopen (tmpfn
, "w", stdout
)) == NULL
) {
1822 perror ("unable to open output file");
1825 if (rename (tmpfn
, argv
[optind
]) != 0) {
1826 perror ("unable to open output file");
1832 // we use gmtime exerywhere to speedup things, this can cause problems
1833 // when daylight saving time changes
1835 gettimeofday(&tv
, NULL
);
1836 ltime
= localtime (&tv
.tv_sec
);
1838 if (!parser
->start
) {
1842 parser
->start
= mkgmtime (ltime
);
1845 ltime
= localtime (&tv
.tv_sec
);
1848 parser
->end
= mkgmtime (ltime
);
1851 if (parser
->end
< parser
->start
) {
1852 fprintf (stderr
, "end time before start time\n");
1857 if ((filecount
= parser_count_files (parser
)) <= 0) {
1858 fprintf (stderr
, "unable to access log files\n");
1863 printf ("# LogReader: %d %s\n", getpid(), uniqueid
);
1865 printf ("# LogReader: %d\n", getpid());
1868 printf ("# Query options\n");
1869 if (parser
->from
) printf ("# Sender: %s\n", parser
->from
);
1870 if (parser
->to
) printf ("# Recipient: %s\n", parser
->to
);
1871 if (parser
->server
) printf ("# Server: %s\n", parser
->server
);
1872 if (parser
->msgid
) printf ("# MsgID: %s\n", parser
->msgid
);
1873 if (parser
->qid
) printf ("# QID: %s\n", parser
->qid
);
1874 if (parser
->strmatch
) printf ("# Match: %s\n", parser
->strmatch
);
1876 strftime (linebuf
, 256, "%F %T", gmtime (&parser
->start
));
1877 printf ("# Start: %s (%lu)\n", linebuf
, parser
->start
);
1878 strftime (linebuf
, 256, "%F %T", gmtime (&parser
->end
));
1879 printf ("# END: %s (%lu)\n", linebuf
, parser
->end
);
1880 printf ("# End Query Options\n\n");
1884 start
= parser
->start
;
1888 for (i
= filecount
- 1; i
>= 0; i
--) {
1891 // syslog files does not conain years, so we must compute then
1892 // cur_year is the assumed start year
1895 cur_year
= parser
->year
[i
];
1898 if (!(stream
= (gpointer
) fopen (logfiles
[i
], "r"))) continue;
1900 if (!(stream
= (gpointer
) gzopen (logfiles
[i
], "r"))) continue;
1905 if (parser
->limit
&& (parser
->count
>= parser
->limit
)) {
1906 printf ("STATUS: aborted by limit (too many hits)\n");
1911 line
= fgets (linebuf
, linebufsize
, (FILE *)stream
);
1913 line
= mygzgets ((gzFile
)stream
, linebuf
, linebufsize
);
1918 int len
= strlen (line
);
1922 if (!(ctime
= parse_time (&cpos
, len
))) {
1926 if (ctime
< start
) continue;
1927 if (ctime
> end
) break;
1931 found
= 0; while (!isspace (*cpos
)) { cpos
++; found
= 1; }
1933 debug_error ("missing hostname", line
);
1937 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
1939 debug_error ("missing spaces after host", line
);
1943 if ((*cpos
== 'l') && !strncmp (cpos
, "last message repeated", 21)) {
1947 //printf ("LINE: %s\n", line);
1952 found
= 0; while (*cpos
&& (*cpos
!= ':') && (*cpos
!= '[')) {
1953 csum_prog
= (csum_prog
<<8) + *cpos
;
1958 //idx1 = g_strndup (prog, found);
1959 //printf ("TEST:%s:%08X\n", idx1, csum_prog);
1964 found
= 0; while (isdigit (*cpos
)) {
1965 pid
= pid
*10 + *cpos
- 48;
1969 if (found
< 1 || found
> 15 || *cpos
!= ']') {
1970 debug_error ("unable to parse pid", line
);
1976 if (*cpos
++ != ':') {
1977 debug_error ("missing collon", line
);
1982 if (!isspace (*cpos
++)) {
1983 debug_error ("missing space after collon", line
);
1989 parser
->ctime
= ctime
;
1993 if (parser
->strmatch
&& STRMATCH(parser
->strmatch
, text
)) {
1997 if (csum_prog
== 0x70726F78) { // proxprox
1999 if ((idx1
= parse_qid (&cpos
, qidbuf
, ':', 25))) {
2003 if (!(fe
= fentry_get (parser
, idx1
))) {
2007 loglist_add (&fe
->ep
, &fe
->loglist
, line
, len
);
2009 if (strmatch
) fe
->strmatch
= 1;
2011 //fixme: BCC, Notify?
2012 //fixme: parse virus info
2013 //fixme: parse spam score
2015 if ((*cpos
== 'a') && !strncmp (cpos
, "accept mail to <", 16)) {
2017 const char *to_s
, *to_e
;
2019 to_s
= cpos
= cpos
+ 16;
2021 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2023 if (*cpos
!= '>') continue;
2029 if ((*cpos
++ != ' ') || (*cpos
++ != '(')) continue;
2031 if (!(idx1
= parse_qid (&cpos
, qidbuf
, ')', 15))) continue;
2033 // parser_track (parser, idx1, 1);
2035 fentry_tolist_add (fe
, 'A', to_s
, to_e
- to_s
, idx1
, strlen (idx1
));
2037 } else if ((*cpos
== 'm') && !strncmp (cpos
, "moved mail for <", 16)) {
2038 const char *to_s
, *to_e
;
2040 to_s
= cpos
= cpos
+ 16;
2042 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2046 if (strncmp (cpos
, "> to ", 5)) continue;
2049 if (!strncmp (cpos
, "spam", 4)) {
2051 } else if (!strncmp (cpos
, "virus", 5)) {
2057 if (strncmp (cpos
, " quarantine - ", 14)) continue;
2060 if (!(idx1
= parse_qid (&cpos
, qidbuf
, 0, 25))) continue;
2062 fentry_tolist_add (fe
, 'Q', to_s
, to_e
- to_s
, idx1
, strlen (idx1
));
2064 } else if ((*cpos
== 'b') && !strncmp (cpos
, "block mail to <", 15)) {
2068 to_s
= cpos
= cpos
+ 15;
2070 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2072 if (*cpos
!= '>') continue;
2074 fentry_tolist_add (fe
, 'B', to_s
, cpos
- to_s
, NULL
, 0);
2076 } else if ((*cpos
== 'p') && !strncmp (cpos
, "processing time: ", 17)) {
2079 sscanf (cpos
, "%f", &fe
->ptime
);
2086 } else if (csum_prog
== 0x7265656E) { // postfix/postscreen
2091 debug_error ("no pid for postscreen", line
);
2096 if ((*text
== 'N') && !strncmp (text
, "NOQUEUE: reject: RCPT from ", 27)) {
2100 if (!(idx1
= strstr (cpos
, "; client ["))) continue;
2102 const char *client
= cpos
= idx1
+ 10;
2104 while (*cpos
&& (*cpos
!= ']')) { cpos
++; }
2106 const char *client_end
= cpos
;
2108 if (!(idx1
= strstr (cpos
, "; from=<"))) continue;
2110 const char *from
= cpos
= idx1
+ 8;
2112 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2115 if ((*cpos
== '>') && strncmp (cpos
, ">, to=<", 7)) continue;
2117 const char *to
= cpos
= cpos
+ 7;
2119 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2121 if (*cpos
!= '>') continue;
2123 if (!(se
= sentry_get (parser
, pid
))) {
2127 if (strmatch
) se
->strmatch
= 1;
2129 loglist_add (&se
->ep
, &se
->loglist
, line
, len
);
2131 sentry_nqlist_add (se
, ctime
, from
, idx1
- from
, to
, cpos
- to
, 'N');
2133 sentry_set_connect (se
, client
, client_end
- client
);
2135 g_hash_table_remove (parser
->smtpd_h
, &se
->pid
);
2139 sentry_print (parser
, se
);
2140 sentry_free (parser
, se
);
2143 } else if (csum_prog
== 0x716D6772) { // postfix/qmgr
2145 if ((idx2
= text
) && (idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2149 if (!(qe
= qentry_get (parser
, idx1
))) {
2153 if (strmatch
) qe
->strmatch
= 1;
2157 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
);
2159 if ((*idx2
== 'f') && !strncmp (idx2
, "from=<", 6)) {
2161 cpos
= idx2
= idx2
+ 6;
2162 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2165 debug_error ("unable to parse 'from' address", line
);
2169 qentry_set_from (qe
, idx2
, cpos
- idx2
);
2173 if ((*cpos
== ',') && !strncmp (cpos
, ", size=", 7)) {
2177 while (isdigit (*cpos
)) { size
= size
*10 + *cpos
++ - 48; }
2182 } else if ((*idx2
== 'r') && !strncmp (idx2
, "removed\n", 8)) {
2186 qentry_finalize (parser
, qe
);
2190 } else if ((csum_prog
== 0x736D7470) || //postfix/smtp
2191 (csum_prog
== 0x6C6D7470)) { //postfix/lmtp
2193 int lmtp
= (csum_prog
== 0x6C6D7470);
2195 if ((cpos
= text
) && (idx1
= parse_qid (&cpos
, qidbuf
, ':', 15))) {
2199 if (!(qe
= qentry_get (parser
, idx1
))) {
2203 if (strmatch
) qe
->strmatch
= 1;
2207 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
);
2209 if (strncmp (cpos
, "to=<", 4)) continue;
2212 const char *to_s
, *to_e
;
2216 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2218 if (*cpos
!= '>') continue;
2224 if (!(cpos
= strstr (cpos
, ", relay="))) continue;
2227 const char *relay_s
, *relay_e
;
2231 while (*cpos
&& (*cpos
!= ',')) { cpos
++; }
2233 if (*cpos
!= ',') continue;
2237 if (!(idx1
= strstr (cpos
+ 1, ", dsn="))) continue;
2241 if (!isdigit (*cpos
)) continue;
2243 char dstatus
= *cpos
;
2245 qentry_tolist_add (qe
, ctime
, dstatus
, to_s
, to_e
- to_s
,
2246 relay_s
, relay_e
- relay_s
);
2248 if (!lmtp
) continue; // filter always uses lmtp
2250 if (!(idx1
= strstr (cpos
+ 1, "status=sent (250 2.")))
2253 cpos
= idx1
= idx1
+ 19;
2255 if (*cpos
== '5' && (idx1
= strstr (cpos
, "5.0 OK ("))) {
2256 cpos
= idx1
= idx1
+ 8;
2257 } else if (*cpos
== '7' && (idx1
= strstr (cpos
, "7.0 BLOCKED ("))) {
2258 cpos
= idx1
= idx1
+ 13;
2263 if (!(idx1
= parse_qid (&cpos
, qidbuf
, ')', 25)))
2270 if ((fe
= g_hash_table_lookup (parser
->filter_h
, idx1
))) {
2275 } else if (csum_prog
== 0x6D747064) { // postfix/smtpd
2279 debug_error ("no pid for smtpd", line
);
2283 if (!(se
= sentry_get (parser
, pid
))) {
2287 if (strmatch
) se
->strmatch
= 1;
2289 loglist_add (&se
->ep
, &se
->loglist
, line
, len
);
2291 if ((*text
== 'c') && !strncmp (text
, "connect from ", 13)) {
2293 cpos
= idx1
= text
+ 13;
2295 while (*idx1
&& !isspace (*idx1
)) { idx1
++; }
2297 sentry_set_connect (se
, cpos
, idx1
- cpos
);
2299 // fixme: do we need this?
2300 //if (strcmp (se->connect, "localhost[127.0.0.1]")) {
2301 // se->external = 1;
2304 } else if ((*text
== 'd') && !strncmp (text
, "disconnect from", 15)) {
2307 g_hash_table_remove (parser
->smtpd_h
, &se
->pid
);
2311 if (sentry_ref_rem_unneeded (parser
, se
) == 0) {
2312 sentry_print (parser
, se
);
2313 sentry_free (parser
, se
);
2315 sentry_ref_finalize (parser
, se
);
2318 } else if ((*text
== 'N') && !strncmp (text
, "NOQUEUE:", 8)) {
2322 // parse 'whatsup' (reject:)
2323 while (*cpos
&& (*cpos
!= ':')) { cpos
++; }
2324 if (*cpos
!= ':') continue;
2327 // parse '%s from %s:'
2328 while (*cpos
&& (*cpos
!= ':')) { cpos
++; }
2329 if (*cpos
!= ':') continue;
2333 while (*cpos
&& (*cpos
!= ';')) { cpos
++; }
2334 if (*cpos
!= ';') continue;
2338 *(char *)cpos
= 0; // dangerous hack: delimit string
2339 if (strstr (idx1
, ": Recipient address rejected: Service is unavailable (try later)")) {
2342 *(char *)cpos
= ';'; // dangerous hack: restore line
2344 if (!(idx1
= strstr (cpos
, "; from=<"))) continue;
2346 const char *from
= cpos
= idx1
+ 8;
2348 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2351 if ((*cpos
== '>') && strncmp (cpos
, "> to=<", 6)) continue;
2353 const char *to
= cpos
= cpos
+ 6;
2355 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2357 if (*cpos
!= '>') continue;
2359 sentry_nqlist_add (se
, ctime
, from
, idx1
- from
, to
, cpos
- to
, dstatus
);
2361 } else if ((idx2
= text
) && (idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2365 if ((qe
= qentry_get (parser
, idx1
))) {
2367 if (strmatch
) qe
->strmatch
= 1;
2369 sentry_ref_add (se
, qe
);
2371 if ((*idx2
== 'c') && !strncmp (idx2
, "client=", 7)) {
2372 cpos
= idx2
= idx2
+ 7;
2374 while (*cpos
&& !isspace (*cpos
)) { cpos
++; }
2376 qentry_set_client (qe
, idx2
, cpos
- idx2
);
2382 } else if (csum_prog
== 0x616E7570) { // postfix/cleanup
2387 if ((idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2388 if ((qe
= qentry_get (parser
, idx1
))) {
2390 if (strmatch
) qe
->strmatch
= 1;
2392 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
);
2394 if ((*idx2
== 'm') && !strncmp (idx2
, "message-id=", 11)) {
2396 cpos
= idx2
= idx2
+ 11;
2398 while (*cpos
&& !isspace(*cpos
)) { cpos
++; }
2400 qentry_set_msgid (qe
, idx2
, cpos
- idx2
);
2410 fclose ((FILE *)stream
);
2412 gzclose ((gzFile
)stream
);
2415 if (ctime
> end
) break;
2420 printf ("LINES: %d\n", lines
);
2421 printf ("MEM SMTPD entries: %d\n", g_hash_table_size (parser
->smtpd_h
));
2422 printf ("MEM QMGR entries: %d\n", g_hash_table_size (parser
->qmgr_h
));
2423 printf ("MEM FILTER entries: %d\n", g_hash_table_size (parser
->filter_h
));
2425 printf ("MEMDEB SMTPD entries: %d %d\n",
2426 g_hash_table_size (smtpd_debug_alloc
),
2427 g_hash_table_size (smtpd_debug_free
));
2428 printf ("MEMDEB QMGR entries: %d %d\n",
2429 g_hash_table_size (qmgr_debug_alloc
),
2430 g_hash_table_size (qmgr_debug_free
));
2431 printf ("MEMDEB FILTER entries: %d %d\n",
2432 g_hash_table_size (filter_debug_alloc
),
2433 g_hash_table_size (filter_debug_free
));
2436 g_hash_table_foreach (parser
->qmgr_h
, qentry_cleanup_hash
, parser
);
2437 g_hash_table_foreach (parser
->smtpd_h
, sentry_cleanup_hash
, parser
);
2438 g_hash_table_foreach (parser
->filter_h
, fentry_cleanup_hash
, parser
);
2441 printf ("MEMDEB SMTPD entries: %d %d\n",
2442 g_hash_table_size (smtpd_debug_alloc
),
2443 g_hash_table_size (smtpd_debug_free
));
2444 printf ("MEMDEB QMGR entries: %d %d\n",
2445 g_hash_table_size (qmgr_debug_alloc
),
2446 g_hash_table_size (qmgr_debug_free
));
2447 printf ("MEMDEB FILTER entries: %d %d\n",
2448 g_hash_table_size (filter_debug_alloc
),
2449 g_hash_table_size (filter_debug_free
));
2451 g_hash_table_foreach (smtpd_debug_alloc
, sentry_debug_alloc
, parser
);
2457 printf ("MEMMAX %d\n", ep_maxalloc
);
2460 parser_free (parser
);