]>
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
90 typedef struct _EPool EPool
;
92 SList
*blocks
; // allocated usiing g_slice_alloc(EPOOL_BLOCK_SIZE)
93 SList
*mblocks
; // allocated use g_malloc
104 GHashTable
*filter_h
;
105 //GHashTable *track_h;
106 gzFile
*stream
[MAX_LOGFILES
];
109 time_t year
[MAX_LOGFILES
];
120 unsigned int exclude_greylist
:1;
121 unsigned int exclude_ndrs
:1;
124 typedef struct _SEntry SEntry
;
125 typedef struct _QEntry QEntry
;
126 typedef struct _FEntry FEntry
;
128 typedef struct _LogEntry LogEntry
;
129 typedef struct _LogList LogList
;
138 LogEntry
*logs_last
; // pointer to last log (speedup add log)
141 // SEntry: Store SMTPD related logs
156 //unsigned int external:1; // not from local host
157 unsigned int disconnect
:1;
158 unsigned int strmatch
:1;
162 // QEntry: Store Queue (qmgr, smtp, lmtp) related logs
181 unsigned int cleanup
:1;
182 unsigned int removed
:1;
183 unsigned int filtered
:1; // set when passed via lmtp to filter
184 unsigned int strmatch
:1;
187 // FEntry: Store filter (proxprox) related logs
194 char *logid
; // proxprox log id
200 unsigned int finished
:1;
201 unsigned int strmatch
:1;
205 void debug_error (char *msg
, const char *line
);
206 SList
*slist_remove (SList
*list
, gpointer data
);
208 EPool
*epool_init (EPool
*ep
);
209 void epool_free (EPool
*ep
);
210 gpointer
epool_alloc (EPool
*ep
, int size
);
211 char *epool_strndup (EPool
*ep
, const char *s
, int len
);
212 char *epool_strndup0 (EPool
*ep
, const char *s
, int len
);
213 char *epool_strdup (EPool
*ep
, const char *s
);
215 void loglist_print (LogList
*loglist
);
216 void loglist_add (EPool
*ep
, LogList
*loglist
, const char *text
, int len
);
218 SEntry
*sentry_new (int pid
);
219 SEntry
*sentry_get (LParser
*parser
, int pid
);
220 void sentry_ref_add (SEntry
*sentry
, QEntry
*qentry
);
221 int sentry_ref_del (SEntry
*sentry
, QEntry
*qentry
);
222 void sentry_ref_finalize (LParser
*parser
, SEntry
*sentry
);
223 int sentry_ref_rem_unneeded (LParser
*parser
, SEntry
*sentry
);
224 void sentry_nqlist_add (SEntry
*sentry
, time_t ltime
, const char *from
, int from_len
,
225 const char *to
, int to_len
, char dstatus
);
226 void sentry_print (LParser
*parser
, SEntry
*sentry
);
227 void sentry_set_connect (SEntry
*sentry
, const char *connect
, int len
);
228 void sentry_free_noremove (SEntry
*sentry
);
229 void sentry_free (LParser
*parser
, SEntry
*sentry
);
230 void sentry_cleanup_hash (gpointer key
, gpointer value
, gpointer user_data
);
233 QEntry
*qentry_new (const char *qid
);
234 QEntry
*qentry_get (LParser
*parser
, const char *qid
);
235 void qentry_tolist_add (QEntry
*qentry
, time_t ltime
, char dstatus
, const char *to
,
236 int to_len
, const char *relay
, int relay_len
);
237 void qentry_set_from (QEntry
*qentry
, const char *from
, int len
);
238 void qentry_set_msgid (QEntry
*qentry
, const char *msgid
, int len
);
239 void qentry_set_client (QEntry
*qentry
, const char *client
, int len
);
240 void qentry_print (LParser
*parser
, QEntry
*qentry
);
241 void qentry_finalize (LParser
*parser
, QEntry
*qentry
);
242 void qentry_free_noremove (LParser
*parser
, QEntry
*qentry
);
243 void qentry_free (LParser
*parser
, QEntry
*qentry
);
244 void qentry_cleanup_hash (gpointer key
, gpointer value
, gpointer user_data
);
247 FEntry
*fentry_new (const char *logid
);
248 FEntry
*fentry_get (LParser
*parser
, const char *logid
);
249 void fentry_tolist_add (FEntry
*fentry
, char dstatus
, const char *to
,
250 int to_len
, const char *qid
, int qid_len
);
251 void fentry_free_noremove (LParser
*parser
, FEntry
*fentry
);
252 void fentry_free (LParser
*parser
, FEntry
*fentry
);
253 void fentry_cleanup_hash (gpointer key
, gpointer value
, gpointer user_data
);
255 LParser
*parser_new ();
256 void parser_free (LParser
*parser
);
258 //char *parser_track (LParser *parser, const char *qid, gboolean insert);
262 //#define LOGPATH "./log/"
263 #define LOGPATH "/var/log/"
264 //#define LOGPATH "/var/log5/"
266 static const char *logfiles
[] = {
269 LOGPATH
"syslog.2.gz",
270 LOGPATH
"syslog.3.gz",
271 LOGPATH
"syslog.4.gz",
272 LOGPATH
"syslog.5.gz",
273 LOGPATH
"syslog.6.gz",
274 LOGPATH
"syslog.7.gz",
275 LOGPATH
"syslog.8.gz",
276 LOGPATH
"syslog.9.gz",
277 LOGPATH
"syslog.10.gz",
278 LOGPATH
"syslog.11.gz",
279 LOGPATH
"syslog.12.gz",
280 LOGPATH
"syslog.13.gz",
281 LOGPATH
"syslog.14.gz",
282 LOGPATH
"syslog.15.gz",
283 LOGPATH
"syslog.16.gz",
284 LOGPATH
"syslog.17.gz",
285 LOGPATH
"syslog.18.gz",
286 LOGPATH
"syslog.19.gz",
287 LOGPATH
"syslog.20.gz",
288 LOGPATH
"syslog.21.gz",
289 LOGPATH
"syslog.22.gz",
290 LOGPATH
"syslog.23.gz",
291 LOGPATH
"syslog.24.gz",
292 LOGPATH
"syslog.25.gz",
293 LOGPATH
"syslog.26.gz",
294 LOGPATH
"syslog.27.gz",
295 LOGPATH
"syslog.28.gz",
296 LOGPATH
"syslog.29.gz",
297 LOGPATH
"syslog.30.gz",
298 LOGPATH
"syslog.31.gz",
302 debug_error (char *msg
, const char *line
)
305 fprintf (stderr
, "ERROR: %s\n", msg
);
306 if (line
) fprintf (stderr
, "LINE: %s\n", line
);
315 slist_remove (SList
*list
, gpointer data
)
321 if (l
->data
== data
) {
340 epool_init (EPool
*ep
)
345 data
= g_slice_alloc0(EPOOL_BLOCK_SIZE
);
346 blocks
= (SList
*)data
;
349 ep
->allocated
+= EPOOL_BLOCK_SIZE
;
350 ep_allocated
+= EPOOL_BLOCK_SIZE
;
351 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
359 ep
->cpos
= sizeof (SList
);
365 epool_free (EPool
*ep
)
371 ep_allocated
-= ep
->allocated
;
372 printf ("MEM: %d\n", ep_allocated
);
390 g_slice_free1(EPOOL_BLOCK_SIZE
, data
);
395 epool_alloc (EPool
*ep
, int size
)
397 int rs
= (size
+ 3) & ~3;
398 int space
= EPOOL_BLOCK_SIZE
- sizeof (SList
) - ep
->cpos
;
401 if (size
> EPOOL_MAX_SIZE
) {
403 if (space
>= sizeof (SList
)) {
404 blocks
= ep
->blocks
->data
+ ep
->cpos
;
405 ep
->cpos
+= sizeof (SList
);
407 blocks
= (SList
*)epool_alloc (ep
, sizeof (SList
));
410 data
= g_malloc (size
);
412 ep
->allocated
+= size
;
413 ep_allocated
+= size
;
414 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
417 blocks
->next
= ep
->mblocks
;
419 ep
->mblocks
= blocks
;
423 } else if (space
>= rs
) {
424 data
= ep
->blocks
->data
+ ep
->cpos
;
430 SList
*blocks
= ep
->blocks
->data
+ ep
->cpos
;
432 data
= g_slice_alloc0 (EPOOL_BLOCK_SIZE
);
434 blocks
->next
= ep
->blocks
;
440 ep
->allocated
+= EPOOL_BLOCK_SIZE
;
441 ep_allocated
+= EPOOL_BLOCK_SIZE
;
442 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
450 epool_strndup (EPool
*ep
, const char *s
, int len
)
453 char *res
= epool_alloc (ep
, l
);
459 epool_strndup0 (EPool
*ep
, const char *s
, int len
)
461 char *res
= epool_alloc (ep
, len
+ 1);
462 strncpy (res
, s
, len
);
469 epool_strdup (EPool
*ep
, const char *s
)
471 int l
= strlen (s
) + 1;
472 char *res
= epool_alloc (ep
, l
);
478 loglist_print (LogList
*loglist
)
480 LogEntry
*log
= loglist
->log
;
482 printf ("%s", log
->text
);
489 loglist_add (EPool
*ep
, LogList
*loglist
, const char *text
, int len
)
494 if (len
!= strlen (text
)) {
495 debug_error ("string with wrong len", NULL
);
498 if (text
[len
] != 0) {
499 debug_error ("string is not null terminated", NULL
);
503 log
= epool_alloc (ep
, sizeof (LogEntry
));
505 log
->text
= epool_strndup (ep
, text
, len
);
508 if (loglist
->logs_last
) {
509 loglist
->logs_last
->next
= log
;
510 loglist
->logs_last
= log
;
513 loglist
->logs_last
= log
;
526 sentry
= (SEntry
*)g_slice_alloc0(EPOOL_BLOCK_SIZE
);
530 sentry
->ep
.allocated
+= EPOOL_BLOCK_SIZE
;
531 ep_allocated
+= EPOOL_BLOCK_SIZE
;
532 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
538 if ((se
= g_hash_table_lookup (smtpd_debug_alloc
, sentry
))) {
539 debug_error ("SEntry already alloced", NULL
);
541 g_hash_table_insert (smtpd_debug_alloc
, sentry
, sentry
);
546 cpos
= sizeof (SEntry
);
548 blocks
= (SList
*)((char *)sentry
+ cpos
);
550 cpos
+= sizeof (SList
);
552 blocks
->data
= sentry
;
555 sentry
->ep
.blocks
= blocks
;
556 sentry
->ep
.cpos
= cpos
;
562 sentry_get (LParser
*parser
, int pid
)
566 if ((sentry
= g_hash_table_lookup (parser
->smtpd_h
, &pid
))) {
570 if ((sentry
= sentry_new (pid
))) {
571 g_hash_table_insert (parser
->smtpd_h
, &sentry
->pid
, sentry
);
579 sentry_ref_add (SEntry
*sentry
, QEntry
*qentry
)
584 if (qentry
->smtpd
!= sentry
) {
585 debug_error ("qentry ref already set", NULL
);
592 if (l
->data
== qentry
) return;
596 l
= epool_alloc (&sentry
->ep
, sizeof (SList
));
598 qentry
->smtpd
= sentry
;
601 l
->next
= sentry
->refs
;
607 sentry_ref_del (SEntry
*sentry
, QEntry
*qentry
)
609 SList
*l
= sentry
->refs
;
612 if (!qentry
->smtpd
) {
613 debug_error ("qentry does not hav a qentry ref", NULL
);
620 QEntry
*qe
= (QEntry
*)l
->data
;
624 if (qe
&& qe
->cleanup
) count
++;
633 sentry_ref_finalize (LParser
*parser
, SEntry
*sentry
)
635 SList
*l
= sentry
->refs
;
641 QEntry
*qe
= l
->data
;
649 if (!qe
->removed
) continue;
651 FEntry
*fe
= qe
->filter
;
653 if (fe
&& !fe
->finished
) continue;
657 qentry_print (parser
, qe
);
662 qentry_free (parser
, qe
);
664 if (fe
) fentry_free (parser
, fe
);
668 if (!count
) sentry_free_noremove (sentry
);
672 sentry_ref_rem_unneeded (LParser
*parser
, SEntry
*sentry
)
674 SList
*l
= sentry
->refs
;
678 QEntry
*qe
= (QEntry
*)l
->data
;
683 qentry_free (parser
, qe
);
697 sentry_nqlist_add (SEntry
*sentry
, time_t ltime
, const char *from
, int from_len
,
698 const char *to
, int to_len
, char dstatus
)
700 NQList
*nq
= (NQList
*)epool_alloc (&sentry
->ep
, sizeof (NQList
));
702 nq
->from
= epool_strndup0 (&sentry
->ep
, from
, from_len
);
703 nq
->to
= epool_strndup0 (&sentry
->ep
, to
, to_len
);
704 nq
->dstatus
= dstatus
;
706 nq
->next
= sentry
->nqlist
;
712 sentry_print (LParser
*parser
, SEntry
*sentry
)
716 if (parser
->msgid
|| parser
->qid
) return;
718 if (parser
->server
) {
719 if (!sentry
->connect
) return;
720 if (!strcasestr (sentry
->connect
, parser
->server
)) return;
723 if (parser
->from
|| parser
->to
||
724 parser
->exclude_greylist
|| parser
->exclude_ndrs
) {
729 if (!*(parser
->from
)) {
730 if (*(nq
->from
)) nq
->dstatus
= 0;
731 } else if (!STRMATCH(parser
->from
, nq
->from
)) {
736 if (parser
->exclude_greylist
&& nq
->dstatus
== 'G') nq
->dstatus
= 0;
738 if (parser
->exclude_ndrs
&& nq
->from
&& !*nq
->from
) nq
->dstatus
= 0;
740 if (parser
->to
&& nq
->to
&& !STRMATCH(parser
->to
, nq
->to
)) {
744 if (nq
->dstatus
!= 0) found
= 1;
751 if (parser
->strmatch
&& !sentry
->strmatch
) return;
753 if (parser
->verbose
) {
757 printf ("CTIME: %08lX\n", parser
->ctime
);
759 if (sentry
->connect
) { printf ("CONNECT: %s\n", sentry
->connect
); }
760 //printf ("EXTERNAL: %d\n", sentry->external);
766 if (nq
->from
&& nq
->to
&& nq
->dstatus
) {
767 printf ("TO:%08lX:00000000000:%c: from <%s> to <%s>\n", nq
->ltime
, nq
->dstatus
, nq
->from
, nq
->to
);
773 if (parser
->verbose
) {
775 loglist_print (&sentry
->loglist
);
783 sentry_set_connect (SEntry
*sentry
, const char *connect
, int len
)
785 if (sentry
->connect
) {
787 if (strncmp (sentry
->connect
, connect
, len
)) {
788 debug_error ("duplicate connect", NULL
);
792 sentry
->connect
= epool_strndup0 (&sentry
->ep
, connect
, len
);
797 sentry_free_noremove (SEntry
*sentry
)
803 ep_allocated
-= sentry
->ep
.allocated
;
804 printf ("MEM: %d\n", ep_allocated
);
810 if ((se
= g_hash_table_lookup (smtpd_debug_free
, sentry
))) {
811 debug_error ("SEntry already freed", NULL
);
813 g_hash_table_insert (smtpd_debug_free
, sentry
, sentry
);
819 l
= sentry
->ep
.mblocks
;
826 l
= sentry
->ep
.blocks
;
830 g_slice_free1(EPOOL_BLOCK_SIZE
, data
);
835 sentry_free (LParser
*parser
, SEntry
*sentry
)
837 g_hash_table_remove (parser
->smtpd_h
, &sentry
->pid
);
839 sentry_free_noremove (sentry
);
843 sentry_cleanup_hash (gpointer key
,
848 LParser
*parser
= (LParser
*)user_data
;
850 sentry_print (parser
, se
);
851 sentry_free_noremove (se
);
856 sentry_debug_alloc (gpointer key
,
860 LParser
*parser
= (LParser
*)user_data
;
864 if ((fe
= g_hash_table_lookup (smtpd_debug_free
, se
))) {
868 printf ("FOUND ALLOCATED SENTRY:\n");
869 sentry_print (parser
, se
);
878 qentry_new (const char *qid
)
885 qentry
= (QEntry
*)g_slice_alloc0(EPOOL_BLOCK_SIZE
);
888 qentry
->ep
.allocated
+= EPOOL_BLOCK_SIZE
;
889 ep_allocated
+= EPOOL_BLOCK_SIZE
;
890 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
896 if ((qe
= g_hash_table_lookup (qmgr_debug_alloc
, qentry
))) {
897 debug_error ("QEntry already alloced", NULL
);
899 g_hash_table_insert (qmgr_debug_alloc
, qentry
, qentry
);
904 cpos
= sizeof (QEntry
);
906 blocks
= (SList
*)((char *)qentry
+ cpos
);
908 cpos
+= sizeof (SList
);
910 blocks
->data
= qentry
;
913 qentry
->qid
= qid_cp
= (char *)qentry
+ cpos
;
914 while ((*qid_cp
++ = *qid
++)) cpos
++;
916 cpos
= (cpos
+ 4) & ~3;
918 qentry
->ep
.blocks
= blocks
;
919 qentry
->ep
.cpos
= cpos
;;
925 qentry_tolist_add (QEntry
*qentry
, time_t ltime
, char dstatus
, const char *to
, int to_len
,
926 const char *relay
, int relay_len
)
928 TOList
*tl
= (TOList
*)epool_alloc (&qentry
->ep
, sizeof (TOList
));
930 tl
->to
= epool_strndup0 (&qentry
->ep
, to
, to_len
);
931 tl
->relay
= epool_strndup0 (&qentry
->ep
, relay
, relay_len
);
932 tl
->dstatus
= dstatus
;
934 tl
->next
= qentry
->tolist
;
940 qentry_set_from (QEntry
*qentry
, const char *from
, int len
)
944 if (strncmp (qentry
->from
, from
, len
)) {
945 debug_error ("duplicate from", NULL
);
949 qentry
->from
= epool_strndup0 (&qentry
->ep
, from
, len
);
954 qentry_set_msgid (QEntry
*qentry
, const char *msgid
, int len
)
958 if (strncmp (qentry
->msgid
, msgid
, len
)) {
959 debug_error ("duplicate msgid", NULL
);
963 qentry
->msgid
= epool_strndup0 (&qentry
->ep
, msgid
, len
);
968 qentry_set_client (QEntry
*qentry
, const char *client
, int len
)
970 if (qentry
->client
) {
972 if (strncmp (qentry
->client
, client
, len
)) {
973 debug_error ("duplicate client", NULL
);
977 qentry
->client
= epool_strndup0 (&qentry
->ep
, client
, len
);
982 qentry_print (LParser
*parser
, QEntry
*qentry
)
985 SEntry
*se
= qentry
->smtpd
;
986 FEntry
*fe
= qentry
->filter
;
989 if (!qentry
->msgid
) return;
990 if (strcasecmp (parser
->msgid
, qentry
->msgid
)) return;
995 if (fe
&& !strcmp (fe
->logid
, parser
->qid
)) found
= 1;
996 if (!strcmp (qentry
->qid
, parser
->qid
)) found
= 1;
1001 if (parser
->server
) {
1003 if (se
&& se
->connect
&& strcasestr (se
->connect
, parser
->server
)) found
= 1;
1004 if (qentry
->client
&& strcasestr (qentry
->client
, parser
->server
)) found
= 1;
1010 if (!qentry
->from
) return;
1011 if (!*(parser
->from
)) {
1012 if (*(qentry
->from
)) return;
1013 } else if (!STRMATCH(parser
->from
, qentry
->from
)) {
1017 if (parser
->exclude_ndrs
&& qentry
->from
&& !*qentry
->from
) return;
1021 tl
= qentry
->tolist
;
1024 if (parser
->to
&& !STRMATCH(parser
->to
, tl
->to
)) {
1034 if (parser
->strmatch
&&
1035 !(qentry
->strmatch
|| (se
&& se
->strmatch
) || (fe
&& fe
->strmatch
)))
1039 if (parser
->verbose
) {
1041 printf ("QENTRY: %s\n", qentry
->qid
);
1043 printf ("CTIME: %08lX\n", parser
->ctime
);
1044 printf ("SIZE: %u\n", qentry
->size
);
1046 if (qentry
->client
) { printf ("CLIENT: %s\n", qentry
->client
); }
1048 if (qentry
->msgid
) { printf ("MSGID: %s\n", qentry
->msgid
); }
1052 tl
= qentry
->tolist
;
1056 if (fe
&& tl
->dstatus
== '2') {
1059 if (fl
->to
&& !strcmp (tl
->to
, fl
->to
)) {
1071 dstatus
= fl
->dstatus
;
1075 dstatus
= tl
->dstatus
;
1079 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");
1087 if (!parser
->verbose
) { fflush (stdout
); return; }
1091 if (se
->loglist
.log
) {
1093 loglist_print (&se
->loglist
);
1098 if (fe
->loglist
.log
) {
1099 printf ("FILTER: %s\n", fe
->logid
);
1100 loglist_print (&fe
->loglist
);
1104 if (qentry
->loglist
.log
) {
1106 loglist_print (&qentry
->loglist
);
1117 qentry_get (LParser
*parser
, const char *qid
)
1121 if ((qentry
= g_hash_table_lookup (parser
->qmgr_h
, qid
))) {
1124 if ((qentry
= qentry_new (qid
))) {
1125 g_hash_table_insert (parser
->qmgr_h
, qentry
->qid
, qentry
);
1133 qentry_free_noremove (LParser
*parser
, QEntry
*qentry
)
1139 if ((se
= qentry
->smtpd
)) {
1140 if (sentry_ref_del (se
, qentry
) == 0) {
1141 if (se
->disconnect
) {
1142 sentry_free_noremove (se
);
1148 ep_allocated
-= qentry
->ep
.allocated
;
1149 printf ("MEM: %d\n", ep_allocated
);
1155 if ((qe
= g_hash_table_lookup (qmgr_debug_free
, qentry
))) {
1156 debug_error ("QEntry already freed", NULL
);
1158 g_hash_table_insert (qmgr_debug_free
, qentry
, qentry
);
1164 l
= qentry
->ep
.mblocks
;
1171 l
= qentry
->ep
.blocks
;
1175 g_slice_free1(EPOOL_BLOCK_SIZE
, data
);
1180 qentry_free (LParser
*parser
, QEntry
*qentry
)
1182 g_hash_table_remove (parser
->qmgr_h
, qentry
->qid
);
1184 qentry_free_noremove (parser
, qentry
);
1188 qentry_cleanup_hash (gpointer key
,
1193 LParser
*parser
= (LParser
*)user_data
;
1195 qentry_print (parser
, qe
);
1196 qentry_free_noremove (parser
, qe
);
1200 qentry_finalize (LParser
*parser
, QEntry
*qentry
)
1202 if (qentry
&& qentry
->removed
) {
1203 SEntry
*se
= qentry
->smtpd
;
1205 if (se
&& !se
->disconnect
) return;
1207 FEntry
*fe
= qentry
->filter
;
1209 if (fe
&& !fe
->finished
) return;
1211 qentry_print (parser
, qentry
);
1212 qentry_free (parser
, qentry
);
1214 if (fe
) fentry_free (parser
, fe
);
1221 fentry_new (const char *logid
)
1228 fentry
= (FEntry
*)g_slice_alloc0(EPOOL_BLOCK_SIZE
);
1231 fentry
->ep
.allocated
+= EPOOL_BLOCK_SIZE
;
1232 ep_allocated
+= EPOOL_BLOCK_SIZE
;
1233 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
1239 if ((fe
= g_hash_table_lookup (filter_debug_alloc
, fentry
))) {
1240 debug_error ("FEntry already alloced", NULL
);
1242 g_hash_table_insert (filter_debug_alloc
, fentry
, fentry
);
1247 cpos
= sizeof (FEntry
);
1249 blocks
= (SList
*)((char *)fentry
+ cpos
);
1251 cpos
+= sizeof (SList
);
1253 blocks
->data
= fentry
;
1254 blocks
->next
= NULL
;
1256 fentry
->logid
= logid_cp
= (char *)fentry
+ cpos
;
1257 while ((*logid_cp
++ = *logid
++)) cpos
++;
1258 cpos
= (cpos
+ 4) & ~3;
1260 fentry
->ep
.blocks
= blocks
;
1261 fentry
->ep
.cpos
= cpos
;;
1267 fentry_get (LParser
*parser
, const char *logid
)
1271 if ((fentry
= g_hash_table_lookup (parser
->filter_h
, logid
))) {
1274 if ((fentry
= fentry_new (logid
))) {
1275 g_hash_table_insert (parser
->filter_h
, fentry
->logid
, fentry
);
1283 fentry_tolist_add (FEntry
*fentry
, char dstatus
, const char *to
, int to_len
,
1284 const char *qid
, int qid_len
)
1286 TOList
*tl
= (TOList
*)epool_alloc (&fentry
->ep
, sizeof (TOList
));
1288 tl
->to
= epool_strndup0 (&fentry
->ep
, to
, to_len
);
1291 tl
->relay
= epool_strndup0 (&fentry
->ep
, qid
, qid_len
);
1295 tl
->dstatus
= dstatus
;
1296 tl
->next
= fentry
->tolist
;
1298 fentry
->tolist
= tl
;
1302 fentry_free_noremove (LParser
*parser
, FEntry
*fentry
)
1308 ep_allocated
-= fentry
->ep
.allocated
;
1309 printf ("MEM: %d\n", ep_allocated
);
1315 if ((fe
= g_hash_table_lookup (filter_debug_free
, fentry
))) {
1316 debug_error ("FEntry already freed", NULL
);
1318 g_hash_table_insert (filter_debug_free
, fentry
, fentry
);
1324 l
= fentry
->ep
.mblocks
;
1331 l
= fentry
->ep
.blocks
;
1335 g_slice_free1(EPOOL_BLOCK_SIZE
, data
);
1340 fentry_free (LParser
*parser
, FEntry
*fentry
)
1342 g_hash_table_remove (parser
->filter_h
, fentry
->logid
);
1344 fentry_free_noremove (parser
, fentry
);
1348 fentry_cleanup_hash (gpointer key
,
1353 LParser
*parser
= (LParser
*)user_data
;
1355 fentry_free_noremove (parser
, fe
);
1363 LParser
*parser
= g_malloc0 (sizeof (LParser
));
1368 epool_init (&parser
->ep
);
1370 if (!(parser
->smtpd_h
= g_hash_table_new (g_int_hash
, g_int_equal
))) {
1374 if (!(parser
->qmgr_h
= g_hash_table_new (g_str_hash
, g_str_equal
))) {
1378 if (!(parser
->filter_h
= g_hash_table_new (g_str_hash
, g_str_equal
))) {
1382 //if (!(parser->track_h = g_hash_table_new (g_str_hash, g_str_equal))) {
1386 for (i
= 0; i
<= MAX_LOGFILES
; i
++) {
1387 gettimeofday(&tv
, NULL
);
1388 tv
.tv_sec
-= 3600*24*i
;
1389 ltime
= localtime (&tv
.tv_sec
);
1390 parser
->year
[i
] = ltime
->tm_year
+ 1900;
1397 parser_free (LParser
*parser
)
1401 for (i
= 0; i
< MAX_LOGFILES
; i
++) {
1402 if (parser
->stream
[i
]) gzclose (parser
->stream
[i
]);
1405 epool_free (&parser
->ep
);
1407 g_hash_table_destroy (parser
->smtpd_h
);
1408 g_hash_table_destroy (parser
->qmgr_h
);
1409 g_hash_table_destroy (parser
->filter_h
);
1410 //g_hash_table_destroy (parser->track_h);
1417 parser_track (LParser
*parser
, const char *qid
, gboolean insert
)
1421 if ((res
= g_hash_table_lookup (parser
->track_h
, qid
))) {
1424 if (insert
&& (res
= epool_strdup (&parser
->ep
, qid
))) {
1425 g_hash_table_insert (parser
->track_h
, res
, res
);
1433 static const int linebufsize
= 8192;
1434 static int cur_year
;
1435 static int cur_month
= 0;
1436 static int cal_mtod
[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
1439 mkgmtime (struct tm
*tm
)
1443 int year
= tm
->tm_year
+ 1900;
1444 int mon
= tm
->tm_mon
;
1446 res
= (year
- 1970) * 365 + cal_mtod
[mon
];
1448 // leap year corrections (gregorian calendar)
1449 if (mon
<= 1) year
-= 1;
1450 res
+= (year
- 1968) / 4;
1451 res
-= (year
- 1900) / 100;
1452 res
+= (year
- 1600) / 400;
1454 res
+= tm
->tm_mday
- 1;
1455 res
= res
*24 + tm
->tm_hour
;
1456 res
= res
*60 + tm
->tm_min
;
1457 res
= res
*60 + tm
->tm_sec
;
1463 parse_time (const char **text
, int len
)
1467 int year
= cur_year
;
1476 const char *line
= *text
;
1478 if (len
== (linebufsize
- 1)) {
1479 debug_error ("skipping long line data", line
);
1484 debug_error ("skipping short line data", line
);
1489 int csum
= (line
[0]<<16) + (line
[1]<<8) + line
[2];
1492 case 4874606: mon
= 0; break;
1493 case 4613474: mon
= 1; break;
1494 case 5071218: mon
= 2; break;
1495 case 4288626: mon
= 3; break;
1496 case 5071225: mon
= 4; break;
1497 case 4879726: mon
= 5; break;
1498 case 4879724: mon
= 6; break;
1499 case 4289895: mon
= 7; break;
1500 case 5465456: mon
= 8; break;
1501 case 5202804: mon
= 9; break;
1502 case 5140342: mon
= 10; break;
1503 case 4482403: mon
= 11; break;
1505 debug_error ("unable to parse month", line
);
1509 // year change heuristik
1510 if (cur_month
== 11 && mon
== 0) {
1513 if (mon
> cur_month
) cur_month
= mon
;
1515 ltime
= (year
- 1970)*365 + cal_mtod
[mon
];
1517 // leap year corrections (gregorian calendar)
1518 if (mon
<= 1) year
-= 1;
1519 ltime
+= (year
- 1968) / 4;
1520 ltime
-= (year
- 1900) / 100;
1521 ltime
+= (year
- 1600) / 400;
1523 const char *cpos
= line
+ 3;
1525 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
1527 debug_error ("missing spaces after month", line
);
1531 found
= 0; while (isdigit (*cpos
)) { mday
= mday
*10 + *cpos
- 48; cpos
++; found
++; }
1532 if (found
< 1 || found
> 2) {
1533 debug_error ("unable to parse day of month", line
);
1539 found
= 0; while (isspace (*cpos
)) { cpos
++; found
++; }
1541 debug_error ("missing spaces after day of month", line
);
1545 found
= 0; while (isdigit (*cpos
)) { hour
= hour
*10 + *cpos
- 48; cpos
++; found
++; }
1546 if (found
< 1 || found
> 2) {
1547 debug_error ("unable to parse hour", line
);
1555 debug_error ("missing collon after hour", line
);
1560 found
= 0; while (isdigit (*cpos
)) { min
= min
*10 + *cpos
- 48; cpos
++; found
++; }
1561 if (found
< 1 || found
> 2) {
1562 debug_error ("unable to parse minute", line
);
1570 debug_error ("missing collon after minute", line
);
1575 found
= 0; while (isdigit (*cpos
)) { sec
= sec
*10 + *cpos
- 48; cpos
++; found
++; }
1576 if (found
< 1 || found
> 2) {
1577 debug_error ("unable to parse second", line
);
1584 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
1586 debug_error ("missing spaces after time", line
);
1597 parser_count_files (LParser
*parser
)
1600 time_t start
= parser
->start
;
1601 char linebuf
[linebufsize
];
1605 for (i
= 0; i
< MAX_LOGFILES
; i
++) {
1606 cur_year
= parser
->year
[i
];
1609 if ((stream
= gzopen (logfiles
[i
], "r"))) {
1610 if ((line
= gzgets (stream
, linebuf
, linebufsize
))) {
1611 if (parse_time (&line
, strlen (line
)) < start
) {
1627 parse_qid (const char **text
, char *out
, char delim
, int maxlen
)
1634 while (isxdigit (*idx
)) { *copy
++ = *idx
++; found
++; if (found
> maxlen
) break; }
1636 if (found
> 1 && found
< maxlen
&&
1637 ((delim
&& (*idx
== delim
)) || (!delim
&& isspace (*idx
)))) {
1640 while (isspace (*idx
)) idx
++;
1648 print_usage (const char *name
)
1650 fprintf (stderr
, "usage: %s [OPTIONS] [OUTPUTFILENAME]\n", name
);
1651 fprintf (stderr
, "\t-f SENDER mails from SENDER\n");
1652 fprintf (stderr
, "\t-t RECIPIENT mails to RECIPIENT\n");
1653 fprintf (stderr
, "\t-h Server Server IP or Hostname\n");
1654 fprintf (stderr
, "\t-s START start time (YYYY-MM-DD HH:MM:SS)\n");
1655 fprintf (stderr
, "\t or seconds since epoch\n");
1656 fprintf (stderr
, "\t-e END end time (YYYY-MM-DD HH:MM:SS)\n");
1657 fprintf (stderr
, "\t or seconds since epoch\n");
1658 fprintf (stderr
, "\t-m MSGID message ID (exact match)\n");
1659 fprintf (stderr
, "\t-q QID queue ID (exact match)\n");
1660 fprintf (stderr
, "\t-x STRING search for strings\n");
1661 fprintf (stderr
, "\t-l LIMIT print max limit entries\n");
1662 fprintf (stderr
, "\t-v verbose output\n");
1666 // gzgets is ways too slow, so we do it our own way
1669 mygzgetc (gzFile stream
)
1672 static char readbuf
[16384];
1673 static char *readend
= readbuf
+ sizeof (readbuf
);
1674 static char *readpos
= readbuf
+ sizeof (readbuf
);
1676 if (readpos
< readend
) return *readpos
++;
1678 if ((br
= gzread (stream
, readbuf
, sizeof (readbuf
))) <= 0) {
1682 readend
= readbuf
+ br
;
1689 mygzgets (gzFile stream
, char *line
, int bufsize
)
1695 while (--bufsize
> 0 && (c
= mygzgetc(stream
)) != -1) {
1700 if (c
== -1 && cpos
== line
)
1707 extern char *optarg
;
1708 extern int optind
, opterr
, optopt
;
1711 main (int argc
, char * const argv
[])
1713 char linebuf
[linebufsize
];
1715 char *uniqueid
= NULL
;
1729 time_t ctime
, start
, end
;
1735 smtpd_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1736 qmgr_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1737 filter_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1738 smtpd_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1739 qmgr_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1740 filter_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1743 if (!(parser
= parser_new ())) {
1744 fprintf (stderr
, "unable to alloc parser structure\n");
1748 while ((opt
= getopt (argc
, argv
, "f:t:s:e:h:m:q:x:l:I:vgn")) != -1) {
1750 parser
->from
= epool_strdup (&parser
->ep
, optarg
);
1751 } else if (opt
== 't') {
1752 parser
->to
= epool_strdup (&parser
->ep
, optarg
);
1753 } else if (opt
== 'v') {
1754 parser
->verbose
= 1;
1755 } else if (opt
== 'g') {
1756 parser
->exclude_greylist
= 1;
1757 } else if (opt
== 'n') {
1758 parser
->exclude_ndrs
= 1;
1759 } else if (opt
== 'I') {
1761 } else if (opt
== 'h') {
1762 parser
->server
= epool_strdup (&parser
->ep
, optarg
);
1763 } else if (opt
== 'm') {
1764 parser
->msgid
= epool_strdup (&parser
->ep
, optarg
);
1765 } else if (opt
== 'q') {
1766 parser
->qid
= epool_strdup (&parser
->ep
, optarg
);
1767 } else if (opt
== 'x') {
1768 parser
->strmatch
= epool_strdup (&parser
->ep
, optarg
);
1769 } else if (opt
== 'l') {
1771 parser
->limit
= strtoul (optarg
, &l
, 0);
1772 if (!*optarg
|| *l
) {
1773 fprintf (stderr
, "unable to parse limit '%s'\n", optarg
);
1776 } else if (opt
== 's') {
1777 // use strptime to convert time
1780 if ((!(res
= strptime (optarg
, "%F %T", &tm
)) &&
1781 !(res
= strptime (optarg
, "%s", &tm
))) || *res
) {
1782 fprintf (stderr
, "unable to parse start time\n");
1785 parser
->start
= mkgmtime (&tm
);
1787 } else if (opt
== 'e') {
1790 if ((!(res
= strptime (optarg
, "%F %T", &tm
)) &&
1791 !(res
= strptime (optarg
, "%s", &tm
))) || *res
) {
1792 fprintf (stderr
, "unable to parse end time\n");
1795 parser
->end
= mkgmtime (&tm
);
1798 print_usage (argv
[0]);
1803 if (optind
< argc
) {
1805 if ((argc
- optind
) > 1) {
1806 print_usage (argv
[0]);
1810 char *tmpfn
= g_strdup_printf ("/tmp/.proxtrack-%08X.txt", getpid ());
1812 if ((stdout
= freopen (tmpfn
, "w", stdout
)) == NULL
) {
1813 perror ("unable to open output file");
1816 if (rename (tmpfn
, argv
[optind
]) != 0) {
1817 perror ("unable to open output file");
1823 // we use gmtime exerywhere to speedup things, this can cause problems
1824 // when daylight saving time changes
1826 gettimeofday(&tv
, NULL
);
1827 ltime
= localtime (&tv
.tv_sec
);
1829 if (!parser
->start
) {
1833 parser
->start
= mkgmtime (ltime
);
1836 ltime
= localtime (&tv
.tv_sec
);
1839 parser
->end
= mkgmtime (ltime
);
1842 if (parser
->end
< parser
->start
) {
1843 fprintf (stderr
, "end time before start time\n");
1848 if ((filecount
= parser_count_files (parser
)) <= 0) {
1849 fprintf (stderr
, "unable to access log files\n");
1854 printf ("# LogReader: %d %s\n", getpid(), uniqueid
);
1856 printf ("# LogReader: %d\n", getpid());
1859 printf ("# Query options\n");
1860 if (parser
->from
) printf ("# Sender: %s\n", parser
->from
);
1861 if (parser
->to
) printf ("# Recipient: %s\n", parser
->to
);
1862 if (parser
->server
) printf ("# Server: %s\n", parser
->server
);
1863 if (parser
->msgid
) printf ("# MsgID: %s\n", parser
->msgid
);
1864 if (parser
->qid
) printf ("# QID: %s\n", parser
->qid
);
1865 if (parser
->strmatch
) printf ("# Match: %s\n", parser
->strmatch
);
1867 strftime (linebuf
, 256, "%F %T", gmtime (&parser
->start
));
1868 printf ("# Start: %s (%lu)\n", linebuf
, parser
->start
);
1869 strftime (linebuf
, 256, "%F %T", gmtime (&parser
->end
));
1870 printf ("# END: %s (%lu)\n", linebuf
, parser
->end
);
1871 printf ("# End Query Options\n\n");
1875 start
= parser
->start
;
1879 for (i
= filecount
- 1; i
>= 0; i
--) {
1882 // syslog files does not conain years, so we must compute then
1883 // cur_year is the assumed start year
1886 cur_year
= parser
->year
[i
];
1889 if (!(stream
= (gpointer
) fopen (logfiles
[i
], "r"))) continue;
1891 if (!(stream
= (gpointer
) gzopen (logfiles
[i
], "r"))) continue;
1896 if (parser
->limit
&& (parser
->count
>= parser
->limit
)) {
1897 printf ("STATUS: aborted by limit (too many hits)\n");
1902 line
= fgets (linebuf
, linebufsize
, (FILE *)stream
);
1904 line
= mygzgets ((gzFile
)stream
, linebuf
, linebufsize
);
1909 int len
= strlen (line
);
1913 if (!(ctime
= parse_time (&cpos
, len
))) {
1917 if (ctime
< start
) continue;
1918 if (ctime
> end
) break;
1922 found
= 0; while (!isspace (*cpos
)) { cpos
++; found
= 1; }
1924 debug_error ("missing hostname", line
);
1928 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
1930 debug_error ("missing spaces after host", line
);
1934 if ((*cpos
== 'l') && !strncmp (cpos
, "last message repeated", 21)) {
1938 //printf ("LINE: %s\n", line);
1939 //const char prog = cpos;
1942 found
= 0; while (*cpos
&& (*cpos
!= ':') && (*cpos
!= '[')) {
1943 csum_prog
= (csum_prog
<<8) + *cpos
;
1948 //idx1 = g_strndup (prog, found);
1949 //printf ("TEST:%s:%08X\n", idx1, csum_prog);
1954 found
= 0; while (isdigit (*cpos
)) {
1955 pid
= pid
*10 + *cpos
- 48;
1959 if (found
< 1 || found
> 15 || *cpos
!= ']') {
1960 debug_error ("unable to parse pid", line
);
1966 if (*cpos
++ != ':') {
1967 debug_error ("missing collon", line
);
1972 if (!isspace (*cpos
++)) {
1973 debug_error ("missing space after collon", line
);
1979 parser
->ctime
= ctime
;
1983 if (parser
->strmatch
&& STRMATCH(parser
->strmatch
, text
)) {
1987 if (csum_prog
== 0x70726F78) { // proxprox
1989 if ((idx1
= parse_qid (&cpos
, qidbuf
, ':', 25))) {
1993 if (!(fe
= fentry_get (parser
, idx1
))) {
1997 loglist_add (&fe
->ep
, &fe
->loglist
, line
, len
);
1999 if (strmatch
) fe
->strmatch
= 1;
2001 //fixme: BCC, Notify?
2002 //fixme: parse virus info
2003 //fixme: parse spam score
2005 if ((*cpos
== 'a') && !strncmp (cpos
, "accept mail to <", 16)) {
2007 const char *to_s
, *to_e
;
2009 to_s
= cpos
= cpos
+ 16;
2011 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2013 if (*cpos
!= '>') continue;
2019 if ((*cpos
++ != ' ') || (*cpos
++ != '(')) continue;
2021 if (!(idx1
= parse_qid (&cpos
, qidbuf
, ')', 15))) continue;
2023 // parser_track (parser, idx1, 1);
2025 fentry_tolist_add (fe
, 'A', to_s
, to_e
- to_s
, idx1
, strlen (idx1
));
2027 } else if ((*cpos
== 'm') && !strncmp (cpos
, "moved mail for <", 16)) {
2028 const char *to_s
, *to_e
;
2030 to_s
= cpos
= cpos
+ 16;
2032 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2036 if (strncmp (cpos
, "> to ", 5)) continue;
2039 if (!strncmp (cpos
, "spam", 4)) {
2041 } else if (!strncmp (cpos
, "virus", 5)) {
2047 if (strncmp (cpos
, " quarantine - ", 14)) continue;
2050 if (!(idx1
= parse_qid (&cpos
, qidbuf
, 0, 25))) continue;
2052 fentry_tolist_add (fe
, 'Q', to_s
, to_e
- to_s
, idx1
, strlen (idx1
));
2054 } else if ((*cpos
== 'b') && !strncmp (cpos
, "block mail to <", 15)) {
2058 to_s
= cpos
= cpos
+ 15;
2060 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2062 if (*cpos
!= '>') continue;
2064 fentry_tolist_add (fe
, 'B', to_s
, cpos
- to_s
, NULL
, 0);
2066 } else if ((*cpos
== 'p') && !strncmp (cpos
, "processing time: ", 17)) {
2069 sscanf (cpos
, "%f", &fe
->ptime
);
2076 } else if (csum_prog
== 0x7265656E) { // postfix/postscreen
2081 debug_error ("no pid for postscreen", line
);
2086 if ((*text
== 'N') && !strncmp (text
, "NOQUEUE: reject: RCPT from ", 27)) {
2090 if (!(idx1
= strstr (cpos
, "; client ["))) continue;
2092 const char *client
= cpos
= idx1
+ 10;
2094 while (*cpos
&& (*cpos
!= ']')) { cpos
++; }
2096 const char *client_end
= cpos
;
2098 if (!(idx1
= strstr (cpos
, "; from=<"))) continue;
2100 const char *from
= cpos
= idx1
+ 8;
2102 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2105 if ((*cpos
== '>') && strncmp (cpos
, ">, to=<", 7)) continue;
2107 const char *to
= cpos
= cpos
+ 7;
2109 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2111 if (*cpos
!= '>') continue;
2113 if (!(se
= sentry_get (parser
, pid
))) {
2117 if (strmatch
) se
->strmatch
= 1;
2119 loglist_add (&se
->ep
, &se
->loglist
, line
, len
);
2121 sentry_nqlist_add (se
, ctime
, from
, idx1
- from
, to
, cpos
- to
, 'N');
2123 sentry_set_connect (se
, client
, client_end
- client
);
2125 g_hash_table_remove (parser
->smtpd_h
, &se
->pid
);
2129 sentry_print (parser
, se
);
2130 sentry_free (parser
, se
);
2133 } else if (csum_prog
== 0x716D6772) { // postfix/qmgr
2135 if ((idx2
= text
) && (idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2139 if (!(qe
= qentry_get (parser
, idx1
))) {
2143 if (strmatch
) qe
->strmatch
= 1;
2147 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
);
2149 if ((*idx2
== 'f') && !strncmp (idx2
, "from=<", 6)) {
2151 cpos
= idx2
= idx2
+ 6;
2152 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2155 debug_error ("unable to parse 'from' address", line
);
2159 qentry_set_from (qe
, idx2
, cpos
- idx2
);
2163 if ((*cpos
== ',') && !strncmp (cpos
, ", size=", 7)) {
2167 while (isdigit (*cpos
)) { size
= size
*10 + *cpos
++ - 48; }
2172 } else if ((*idx2
== 'r') && !strncmp (idx2
, "removed\n", 8)) {
2176 qentry_finalize (parser
, qe
);
2180 } else if ((csum_prog
== 0x736D7470) || //postfix/smtp
2181 (csum_prog
== 0x6C6D7470)) { //postfix/lmtp
2183 int lmtp
= (csum_prog
== 0x6C6D7470);
2185 if ((cpos
= text
) && (idx1
= parse_qid (&cpos
, qidbuf
, ':', 15))) {
2189 if (!(qe
= qentry_get (parser
, idx1
))) {
2193 if (strmatch
) qe
->strmatch
= 1;
2197 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
);
2199 if (strncmp (cpos
, "to=<", 4)) continue;
2202 const char *to_s
, *to_e
;
2206 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2208 if (*cpos
!= '>') continue;
2214 if (!(cpos
= strstr (cpos
, ", relay="))) continue;
2217 const char *relay_s
, *relay_e
;
2221 while (*cpos
&& (*cpos
!= ',')) { cpos
++; }
2223 if (*cpos
!= ',') continue;
2227 if (!(idx1
= strstr (cpos
+ 1, ", dsn="))) continue;
2231 if (!isdigit (*cpos
)) continue;
2233 char dstatus
= *cpos
;
2235 qentry_tolist_add (qe
, ctime
, dstatus
, to_s
, to_e
- to_s
,
2236 relay_s
, relay_e
- relay_s
);
2238 if (!lmtp
) continue; // filter always uses lmtp
2240 if (!(idx1
= strstr (cpos
+ 1, "status=sent (250 2.")))
2243 cpos
= idx1
= idx1
+ 19;
2245 if (*cpos
== '5' && (idx1
= strstr (cpos
, "5.0 OK ("))) {
2246 cpos
= idx1
= idx1
+ 8;
2247 } else if (*cpos
== '7' && (idx1
= strstr (cpos
, "7.0 BLOCKED ("))) {
2248 cpos
= idx1
= idx1
+ 13;
2253 if (!(idx1
= parse_qid (&cpos
, qidbuf
, ')', 25)))
2260 if ((fe
= g_hash_table_lookup (parser
->filter_h
, idx1
))) {
2265 } else if (csum_prog
== 0x6D747064) { // postfix/smtpd
2269 debug_error ("no pid for smtpd", line
);
2273 if (!(se
= sentry_get (parser
, pid
))) {
2277 if (strmatch
) se
->strmatch
= 1;
2279 loglist_add (&se
->ep
, &se
->loglist
, line
, len
);
2281 if ((*text
== 'c') && !strncmp (text
, "connect from ", 13)) {
2283 cpos
= idx1
= text
+ 13;
2285 while (*idx1
&& !isspace (*idx1
)) { idx1
++; }
2287 sentry_set_connect (se
, cpos
, idx1
- cpos
);
2289 // fixme: do we need this?
2290 //if (strcmp (se->connect, "localhost[127.0.0.1]")) {
2291 // se->external = 1;
2294 } else if ((*text
== 'd') && !strncmp (text
, "disconnect from", 15)) {
2297 g_hash_table_remove (parser
->smtpd_h
, &se
->pid
);
2301 if (sentry_ref_rem_unneeded (parser
, se
) == 0) {
2302 sentry_print (parser
, se
);
2303 sentry_free (parser
, se
);
2305 sentry_ref_finalize (parser
, se
);
2308 } else if ((*text
== 'N') && !strncmp (text
, "NOQUEUE:", 8)) {
2312 // parse 'whatsup' (reject:)
2313 while (*cpos
&& (*cpos
!= ':')) { cpos
++; }
2314 if (*cpos
!= ':') continue;
2317 // parse '%s from %s:'
2318 while (*cpos
&& (*cpos
!= ':')) { cpos
++; }
2319 if (*cpos
!= ':') continue;
2323 while (*cpos
&& (*cpos
!= ';')) { cpos
++; }
2324 if (*cpos
!= ';') continue;
2328 *(char *)cpos
= 0; // dangerous hack: delimit string
2329 if (strstr (idx1
, ": Recipient address rejected: Service is unavailable (try later)")) {
2332 *(char *)cpos
= ';'; // dangerous hack: restore line
2334 if (!(idx1
= strstr (cpos
, "; from=<"))) continue;
2336 const char *from
= cpos
= idx1
+ 8;
2338 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2341 if ((*cpos
== '>') && strncmp (cpos
, "> to=<", 6)) continue;
2343 const char *to
= cpos
= cpos
+ 6;
2345 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2347 if (*cpos
!= '>') continue;
2349 sentry_nqlist_add (se
, ctime
, from
, idx1
- from
, to
, cpos
- to
, dstatus
);
2351 } else if ((idx2
= text
) && (idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2355 if ((qe
= qentry_get (parser
, idx1
))) {
2357 if (strmatch
) qe
->strmatch
= 1;
2359 sentry_ref_add (se
, qe
);
2361 if ((*idx2
== 'c') && !strncmp (idx2
, "client=", 7)) {
2362 cpos
= idx2
= idx2
+ 7;
2364 while (*cpos
&& !isspace (*cpos
)) { cpos
++; }
2366 qentry_set_client (qe
, idx2
, cpos
- idx2
);
2372 } else if (csum_prog
== 0x616E7570) { // postfix/cleanup
2377 if ((idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2378 if ((qe
= qentry_get (parser
, idx1
))) {
2380 if (strmatch
) qe
->strmatch
= 1;
2382 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
);
2384 if ((*idx2
== 'm') && !strncmp (idx2
, "message-id=", 11)) {
2386 cpos
= idx2
= idx2
+ 11;
2388 while (*cpos
&& !isspace(*cpos
)) { cpos
++; }
2390 qentry_set_msgid (qe
, idx2
, cpos
- idx2
);
2400 fclose ((FILE *)stream
);
2402 gzclose ((gzFile
)stream
);
2405 if (ctime
> end
) break;
2410 printf ("LINES: %d\n", lines
);
2411 printf ("MEM SMTPD entries: %d\n", g_hash_table_size (parser
->smtpd_h
));
2412 printf ("MEM QMGR entries: %d\n", g_hash_table_size (parser
->qmgr_h
));
2413 printf ("MEM FILTER entries: %d\n", g_hash_table_size (parser
->filter_h
));
2415 printf ("MEMDEB SMTPD entries: %d %d\n",
2416 g_hash_table_size (smtpd_debug_alloc
),
2417 g_hash_table_size (smtpd_debug_free
));
2418 printf ("MEMDEB QMGR entries: %d %d\n",
2419 g_hash_table_size (qmgr_debug_alloc
),
2420 g_hash_table_size (qmgr_debug_free
));
2421 printf ("MEMDEB FILTER entries: %d %d\n",
2422 g_hash_table_size (filter_debug_alloc
),
2423 g_hash_table_size (filter_debug_free
));
2426 g_hash_table_foreach (parser
->qmgr_h
, qentry_cleanup_hash
, parser
);
2427 g_hash_table_foreach (parser
->smtpd_h
, sentry_cleanup_hash
, parser
);
2428 g_hash_table_foreach (parser
->filter_h
, fentry_cleanup_hash
, parser
);
2431 printf ("MEMDEB SMTPD entries: %d %d\n",
2432 g_hash_table_size (smtpd_debug_alloc
),
2433 g_hash_table_size (smtpd_debug_free
));
2434 printf ("MEMDEB QMGR entries: %d %d\n",
2435 g_hash_table_size (qmgr_debug_alloc
),
2436 g_hash_table_size (qmgr_debug_free
));
2437 printf ("MEMDEB FILTER entries: %d %d\n",
2438 g_hash_table_size (filter_debug_alloc
),
2439 g_hash_table_size (filter_debug_free
));
2441 g_hash_table_foreach (smtpd_debug_alloc
, sentry_debug_alloc
, parser
);
2447 printf ("MEMMAX %d\n", ep_maxalloc
);
2450 parser_free (parser
);