]>
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 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU Affero General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU Affero General Public License for more details.
17 You should have received a copy of the GNU Affero General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 Author: Dietmar Maurer <dietmar@proxmox.com>
22 See http://www.proxmox.com for more information
36 #include <sys/types.h>
41 We assume the syslog files belong to one host, i.e. we do not
42 consider the hostname at all
46 REGEX: we do not use posix regular expressions (libc implementation
47 is too slow). fnmatch() is also slow.
48 Future versions may use our own implementation of a DFA. But currently we
49 just use strstr (no regex support at all)
52 //#define STRMATCH(pattern, string) (fnmatch (pattern, string, FNM_CASEFOLD) == 0)
53 #define STRMATCH(pattern, string) (strcasestr (string, pattern) != NULL)
56 //#define EPOOL_BLOCK_SIZE 512
57 //#define EPOOL_MAX_SIZE 128
58 #define EPOOL_BLOCK_SIZE 2048
59 #define EPOOL_MAX_SIZE 128
60 #define MAX_LOGFILES 31
64 typedef struct _SList SList
;
70 typedef struct _NQList NQList
;
79 typedef struct _TOList TOList
;
89 GHashTable
*smtpd_debug_alloc
;
90 GHashTable
*qmgr_debug_alloc
;
91 GHashTable
*filter_debug_alloc
;
92 GHashTable
*smtpd_debug_free
;
93 GHashTable
*qmgr_debug_free
;
94 GHashTable
*filter_debug_free
;
97 // EPool: Entry related memory pools
105 typedef struct _EPool EPool
;
107 SList
*blocks
; // allocated usiing g_slice_alloc(EPOOL_BLOCK_SIZE)
108 SList
*mblocks
; // allocated use g_malloc
119 GHashTable
*filter_h
;
120 //GHashTable *track_h;
121 gzFile stream
[MAX_LOGFILES
];
124 time_t year
[MAX_LOGFILES
];
135 unsigned int exclude_greylist
:1;
136 unsigned int exclude_ndrs
:1;
139 typedef struct _SEntry SEntry
;
140 typedef struct _QEntry QEntry
;
141 typedef struct _FEntry FEntry
;
143 typedef struct _LogEntry LogEntry
;
144 typedef struct _LogList LogList
;
153 LogEntry
*logs_last
; // pointer to last log (speedup add log)
156 // SEntry: Store SMTPD related logs
171 //unsigned int external:1; // not from local host
172 unsigned int disconnect
:1;
173 unsigned int strmatch
:1;
177 // QEntry: Store Queue (qmgr, smtp, lmtp) related logs
196 unsigned int cleanup
:1;
197 unsigned int removed
:1;
198 unsigned int filtered
:1; // set when passed via lmtp to filter
199 unsigned int strmatch
:1;
202 // FEntry: Store filter (proxprox) related logs
209 char *logid
; // proxprox log id
215 unsigned int finished
:1;
216 unsigned int strmatch
:1;
220 void debug_error (char *msg
, const char *line
);
222 EPool
*epool_init (EPool
*ep
);
223 void epool_free (EPool
*ep
);
224 gpointer
epool_alloc (EPool
*ep
, int size
);
225 char *epool_strndup (EPool
*ep
, const char *s
, int len
);
226 char *epool_strndup0 (EPool
*ep
, const char *s
, int len
);
227 char *epool_strdup (EPool
*ep
, const char *s
);
229 void loglist_print (LogList
*loglist
);
230 void loglist_add (EPool
*ep
, LogList
*loglist
, const char *text
, int len
);
232 SEntry
*sentry_new (int pid
);
233 SEntry
*sentry_get (LParser
*parser
, int pid
);
234 void sentry_ref_add (SEntry
*sentry
, QEntry
*qentry
);
235 int sentry_ref_del (SEntry
*sentry
, QEntry
*qentry
);
236 void sentry_ref_finalize (LParser
*parser
, SEntry
*sentry
);
237 int sentry_ref_rem_unneeded (LParser
*parser
, SEntry
*sentry
);
238 void sentry_nqlist_add (SEntry
*sentry
, time_t ltime
, const char *from
, int from_len
,
239 const char *to
, int to_len
, char dstatus
);
240 void sentry_print (LParser
*parser
, SEntry
*sentry
);
241 void sentry_set_connect (SEntry
*sentry
, const char *connect
, int len
);
242 void sentry_free_noremove (SEntry
*sentry
);
243 void sentry_free (LParser
*parser
, SEntry
*sentry
);
244 void sentry_cleanup_hash (gpointer key
, gpointer value
, gpointer user_data
);
247 QEntry
*qentry_new (const char *qid
);
248 QEntry
*qentry_get (LParser
*parser
, const char *qid
);
249 void qentry_tolist_add (QEntry
*qentry
, time_t ltime
, char dstatus
, const char *to
,
250 int to_len
, const char *relay
, int relay_len
);
251 void qentry_set_from (QEntry
*qentry
, const char *from
, int len
);
252 void qentry_set_msgid (QEntry
*qentry
, const char *msgid
, int len
);
253 void qentry_set_client (QEntry
*qentry
, const char *client
, int len
);
254 void qentry_print (LParser
*parser
, QEntry
*qentry
);
255 void qentry_finalize (LParser
*parser
, QEntry
*qentry
);
256 void qentry_free_noremove (LParser
*parser
, QEntry
*qentry
);
257 void qentry_free (LParser
*parser
, QEntry
*qentry
);
258 void qentry_cleanup_hash (gpointer key
, gpointer value
, gpointer user_data
);
261 FEntry
*fentry_new (const char *logid
);
262 FEntry
*fentry_get (LParser
*parser
, const char *logid
);
263 void fentry_tolist_add (FEntry
*fentry
, char dstatus
, const char *to
,
264 int to_len
, const char *qid
, int qid_len
);
265 void fentry_free_noremove (LParser
*parser
, FEntry
*fentry
);
266 void fentry_free (LParser
*parser
, FEntry
*fentry
);
267 void fentry_cleanup_hash (gpointer key
, gpointer value
, gpointer user_data
);
269 LParser
*parser_new ();
270 void parser_free (LParser
*parser
);
272 //char *parser_track (LParser *parser, const char *qid, gboolean insert);
276 //#define LOGPATH "./log/"
277 #define LOGPATH "/var/log/"
278 //#define LOGPATH "/var/log5/"
280 static const char *logfiles
[] = {
283 LOGPATH
"syslog.2.gz",
284 LOGPATH
"syslog.3.gz",
285 LOGPATH
"syslog.4.gz",
286 LOGPATH
"syslog.5.gz",
287 LOGPATH
"syslog.6.gz",
288 LOGPATH
"syslog.7.gz",
289 LOGPATH
"syslog.8.gz",
290 LOGPATH
"syslog.9.gz",
291 LOGPATH
"syslog.10.gz",
292 LOGPATH
"syslog.11.gz",
293 LOGPATH
"syslog.12.gz",
294 LOGPATH
"syslog.13.gz",
295 LOGPATH
"syslog.14.gz",
296 LOGPATH
"syslog.15.gz",
297 LOGPATH
"syslog.16.gz",
298 LOGPATH
"syslog.17.gz",
299 LOGPATH
"syslog.18.gz",
300 LOGPATH
"syslog.19.gz",
301 LOGPATH
"syslog.20.gz",
302 LOGPATH
"syslog.21.gz",
303 LOGPATH
"syslog.22.gz",
304 LOGPATH
"syslog.23.gz",
305 LOGPATH
"syslog.24.gz",
306 LOGPATH
"syslog.25.gz",
307 LOGPATH
"syslog.26.gz",
308 LOGPATH
"syslog.27.gz",
309 LOGPATH
"syslog.28.gz",
310 LOGPATH
"syslog.29.gz",
311 LOGPATH
"syslog.30.gz",
312 LOGPATH
"syslog.31.gz",
316 debug_error (char *msg
, const char *line
)
319 fprintf (stderr
, "ERROR: %s\n", msg
);
320 if (line
) fprintf (stderr
, "LINE: %s\n", line
);
329 epool_init (EPool
*ep
)
334 data
= g_slice_alloc0(EPOOL_BLOCK_SIZE
);
335 blocks
= (SList
*)data
;
338 ep
->allocated
+= EPOOL_BLOCK_SIZE
;
339 ep_allocated
+= EPOOL_BLOCK_SIZE
;
340 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
348 ep
->cpos
= sizeof (SList
);
354 epool_free (EPool
*ep
)
360 ep_allocated
-= ep
->allocated
;
361 printf ("MEM: %d\n", ep_allocated
);
379 g_slice_free1(EPOOL_BLOCK_SIZE
, data
);
384 epool_alloc (EPool
*ep
, int size
)
386 int rs
= (size
+ 3) & ~3;
387 int space
= EPOOL_BLOCK_SIZE
- sizeof (SList
) - ep
->cpos
;
390 if (size
> EPOOL_MAX_SIZE
) {
392 if (space
>= sizeof (SList
)) {
393 blocks
= ep
->blocks
->data
+ ep
->cpos
;
394 ep
->cpos
+= sizeof (SList
);
396 blocks
= (SList
*)epool_alloc (ep
, sizeof (SList
));
399 data
= g_malloc (size
);
401 ep
->allocated
+= size
;
402 ep_allocated
+= size
;
403 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
406 blocks
->next
= ep
->mblocks
;
408 ep
->mblocks
= blocks
;
412 } else if (space
>= rs
) {
413 data
= ep
->blocks
->data
+ ep
->cpos
;
419 SList
*blocks
= ep
->blocks
->data
+ ep
->cpos
;
421 data
= g_slice_alloc0 (EPOOL_BLOCK_SIZE
);
423 blocks
->next
= ep
->blocks
;
429 ep
->allocated
+= EPOOL_BLOCK_SIZE
;
430 ep_allocated
+= EPOOL_BLOCK_SIZE
;
431 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
439 epool_strndup (EPool
*ep
, const char *s
, int len
)
442 char *res
= epool_alloc (ep
, l
);
448 epool_strndup0 (EPool
*ep
, const char *s
, int len
)
450 char *res
= epool_alloc (ep
, len
+ 1);
451 strncpy (res
, s
, len
);
458 epool_strdup (EPool
*ep
, const char *s
)
460 int l
= strlen (s
) + 1;
461 char *res
= epool_alloc (ep
, l
);
467 loglist_print (LogList
*loglist
)
469 LogEntry
*log
= loglist
->log
;
471 printf ("%s", log
->text
);
478 loglist_add (EPool
*ep
, LogList
*loglist
, const char *text
, int len
)
483 if (len
!= strlen (text
)) {
484 debug_error ("string with wrong len", NULL
);
487 if (text
[len
] != 0) {
488 debug_error ("string is not null terminated", NULL
);
492 log
= epool_alloc (ep
, sizeof (LogEntry
));
494 log
->text
= epool_strndup (ep
, text
, len
);
497 if (loglist
->logs_last
) {
498 loglist
->logs_last
->next
= log
;
499 loglist
->logs_last
= log
;
502 loglist
->logs_last
= log
;
515 sentry
= (SEntry
*)g_slice_alloc0(EPOOL_BLOCK_SIZE
);
519 sentry
->ep
.allocated
+= EPOOL_BLOCK_SIZE
;
520 ep_allocated
+= EPOOL_BLOCK_SIZE
;
521 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
527 if ((se
= g_hash_table_lookup (smtpd_debug_alloc
, sentry
))) {
528 debug_error ("SEntry already alloced", NULL
);
530 g_hash_table_insert (smtpd_debug_alloc
, sentry
, sentry
);
535 cpos
= sizeof (SEntry
);
537 blocks
= (SList
*)((char *)sentry
+ cpos
);
539 cpos
+= sizeof (SList
);
541 blocks
->data
= sentry
;
544 sentry
->ep
.blocks
= blocks
;
545 sentry
->ep
.cpos
= cpos
;
551 sentry_get (LParser
*parser
, int pid
)
555 if ((sentry
= g_hash_table_lookup (parser
->smtpd_h
, &pid
))) {
559 if ((sentry
= sentry_new (pid
))) {
560 g_hash_table_insert (parser
->smtpd_h
, &sentry
->pid
, sentry
);
568 sentry_ref_add (SEntry
*sentry
, QEntry
*qentry
)
573 if (qentry
->smtpd
!= sentry
) {
574 debug_error ("qentry ref already set", NULL
);
581 if (l
->data
== qentry
) return;
585 l
= epool_alloc (&sentry
->ep
, sizeof (SList
));
587 qentry
->smtpd
= sentry
;
590 l
->next
= sentry
->refs
;
596 sentry_ref_del (SEntry
*sentry
, QEntry
*qentry
)
598 SList
*l
= sentry
->refs
;
601 if (!qentry
->smtpd
) {
602 debug_error ("qentry does not hav a qentry ref", NULL
);
609 QEntry
*qe
= (QEntry
*)l
->data
;
613 if (qe
&& qe
->cleanup
) count
++;
622 sentry_ref_finalize (LParser
*parser
, SEntry
*sentry
)
624 SList
*l
= sentry
->refs
;
630 QEntry
*qe
= l
->data
;
638 if (!qe
->removed
) continue;
640 FEntry
*fe
= qe
->filter
;
642 if (fe
&& !fe
->finished
) continue;
646 qentry_print (parser
, qe
);
651 qentry_free (parser
, qe
);
653 if (fe
) fentry_free (parser
, fe
);
657 if (!count
) sentry_free_noremove (sentry
);
661 sentry_ref_rem_unneeded (LParser
*parser
, SEntry
*sentry
)
663 SList
*l
= sentry
->refs
;
667 QEntry
*qe
= (QEntry
*)l
->data
;
672 qentry_free (parser
, qe
);
686 sentry_nqlist_add (SEntry
*sentry
, time_t ltime
, const char *from
, int from_len
,
687 const char *to
, int to_len
, char dstatus
)
689 NQList
*nq
= (NQList
*)epool_alloc (&sentry
->ep
, sizeof (NQList
));
691 nq
->from
= epool_strndup0 (&sentry
->ep
, from
, from_len
);
692 nq
->to
= epool_strndup0 (&sentry
->ep
, to
, to_len
);
693 nq
->dstatus
= dstatus
;
695 nq
->next
= sentry
->nqlist
;
701 sentry_print (LParser
*parser
, SEntry
*sentry
)
705 if (parser
->msgid
|| parser
->qid
) return;
707 if (parser
->server
) {
708 if (!sentry
->connect
) return;
709 if (!strcasestr (sentry
->connect
, parser
->server
)) return;
712 if (parser
->from
|| parser
->to
||
713 parser
->exclude_greylist
|| parser
->exclude_ndrs
) {
718 if (!*(parser
->from
)) {
719 if (*(nq
->from
)) nq
->dstatus
= 0;
720 } else if (!STRMATCH(parser
->from
, nq
->from
)) {
725 if (parser
->exclude_greylist
&& nq
->dstatus
== 'G') nq
->dstatus
= 0;
727 if (parser
->exclude_ndrs
&& nq
->from
&& !*nq
->from
) nq
->dstatus
= 0;
729 if (parser
->to
&& nq
->to
&& !STRMATCH(parser
->to
, nq
->to
)) {
733 if (nq
->dstatus
!= 0) found
= 1;
740 if (parser
->strmatch
&& !sentry
->strmatch
) return;
742 if (parser
->verbose
) {
746 printf ("CTIME: %08lX\n", parser
->ctime
);
748 if (sentry
->connect
) { printf ("CONNECT: %s\n", sentry
->connect
); }
749 //printf ("EXTERNAL: %d\n", sentry->external);
755 if (nq
->from
&& nq
->to
&& nq
->dstatus
) {
756 printf ("TO:%08lX:00000000000:%c: from <%s> to <%s>\n", nq
->ltime
, nq
->dstatus
, nq
->from
, nq
->to
);
762 if (parser
->verbose
) {
764 loglist_print (&sentry
->loglist
);
772 sentry_set_connect (SEntry
*sentry
, const char *connect
, int len
)
774 if (sentry
->connect
) {
776 if (strncmp (sentry
->connect
, connect
, len
)) {
777 debug_error ("duplicate connect", NULL
);
781 sentry
->connect
= epool_strndup0 (&sentry
->ep
, connect
, len
);
786 sentry_free_noremove (SEntry
*sentry
)
792 ep_allocated
-= sentry
->ep
.allocated
;
793 printf ("MEM: %d\n", ep_allocated
);
799 if ((se
= g_hash_table_lookup (smtpd_debug_free
, sentry
))) {
800 debug_error ("SEntry already freed", NULL
);
802 g_hash_table_insert (smtpd_debug_free
, sentry
, sentry
);
808 l
= sentry
->ep
.mblocks
;
815 l
= sentry
->ep
.blocks
;
819 g_slice_free1(EPOOL_BLOCK_SIZE
, data
);
824 sentry_free (LParser
*parser
, SEntry
*sentry
)
826 g_hash_table_remove (parser
->smtpd_h
, &sentry
->pid
);
828 sentry_free_noremove (sentry
);
832 sentry_cleanup_hash (gpointer key
,
837 LParser
*parser
= (LParser
*)user_data
;
839 sentry_print (parser
, se
);
840 sentry_free_noremove (se
);
845 sentry_debug_alloc (gpointer key
,
849 LParser
*parser
= (LParser
*)user_data
;
853 if ((fe
= g_hash_table_lookup (smtpd_debug_free
, se
))) {
857 printf ("FOUND ALLOCATED SENTRY:\n");
858 sentry_print (parser
, se
);
867 qentry_new (const char *qid
)
874 qentry
= (QEntry
*)g_slice_alloc0(EPOOL_BLOCK_SIZE
);
877 qentry
->ep
.allocated
+= EPOOL_BLOCK_SIZE
;
878 ep_allocated
+= EPOOL_BLOCK_SIZE
;
879 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
885 if ((qe
= g_hash_table_lookup (qmgr_debug_alloc
, qentry
))) {
886 debug_error ("QEntry already alloced", NULL
);
888 g_hash_table_insert (qmgr_debug_alloc
, qentry
, qentry
);
893 cpos
= sizeof (QEntry
);
895 blocks
= (SList
*)((char *)qentry
+ cpos
);
897 cpos
+= sizeof (SList
);
899 blocks
->data
= qentry
;
902 qentry
->qid
= qid_cp
= (char *)qentry
+ cpos
;
903 while ((*qid_cp
++ = *qid
++)) cpos
++;
905 cpos
= (cpos
+ 4) & ~3;
907 qentry
->ep
.blocks
= blocks
;
908 qentry
->ep
.cpos
= cpos
;;
914 qentry_tolist_add (QEntry
*qentry
, time_t ltime
, char dstatus
, const char *to
, int to_len
,
915 const char *relay
, int relay_len
)
917 TOList
*tl
= (TOList
*)epool_alloc (&qentry
->ep
, sizeof (TOList
));
919 tl
->to
= epool_strndup0 (&qentry
->ep
, to
, to_len
);
920 tl
->relay
= epool_strndup0 (&qentry
->ep
, relay
, relay_len
);
921 tl
->dstatus
= dstatus
;
923 tl
->next
= qentry
->tolist
;
929 qentry_set_from (QEntry
*qentry
, const char *from
, int len
)
933 if (strncmp (qentry
->from
, from
, len
)) {
934 debug_error ("duplicate from", NULL
);
938 qentry
->from
= epool_strndup0 (&qentry
->ep
, from
, len
);
943 qentry_set_msgid (QEntry
*qentry
, const char *msgid
, int len
)
947 if (strncmp (qentry
->msgid
, msgid
, len
)) {
948 debug_error ("duplicate msgid", NULL
);
952 qentry
->msgid
= epool_strndup0 (&qentry
->ep
, msgid
, len
);
957 qentry_set_client (QEntry
*qentry
, const char *client
, int len
)
959 if (qentry
->client
) {
961 if (strncmp (qentry
->client
, client
, len
)) {
962 debug_error ("duplicate client", NULL
);
966 qentry
->client
= epool_strndup0 (&qentry
->ep
, client
, len
);
971 qentry_print (LParser
*parser
, QEntry
*qentry
)
974 SEntry
*se
= qentry
->smtpd
;
975 FEntry
*fe
= qentry
->filter
;
978 if (!qentry
->msgid
) return;
979 if (strcasecmp (parser
->msgid
, qentry
->msgid
)) return;
984 if (fe
&& !strcmp (fe
->logid
, parser
->qid
)) found
= 1;
985 if (!strcmp (qentry
->qid
, parser
->qid
)) found
= 1;
990 if (parser
->server
) {
992 if (se
&& se
->connect
&& strcasestr (se
->connect
, parser
->server
)) found
= 1;
993 if (qentry
->client
&& strcasestr (qentry
->client
, parser
->server
)) found
= 1;
999 if (!qentry
->from
) return;
1000 if (!*(parser
->from
)) {
1001 if (*(qentry
->from
)) return;
1002 } else if (!STRMATCH(parser
->from
, qentry
->from
)) {
1006 if (parser
->exclude_ndrs
&& qentry
->from
&& !*qentry
->from
) return;
1010 tl
= qentry
->tolist
;
1013 if (parser
->to
&& !STRMATCH(parser
->to
, tl
->to
)) {
1023 if (parser
->strmatch
&&
1024 !(qentry
->strmatch
|| (se
&& se
->strmatch
) || (fe
&& fe
->strmatch
)))
1028 if (parser
->verbose
) {
1030 printf ("QENTRY: %s\n", qentry
->qid
);
1032 printf ("CTIME: %08lX\n", parser
->ctime
);
1033 printf ("SIZE: %u\n", qentry
->size
);
1035 if (qentry
->client
) { printf ("CLIENT: %s\n", qentry
->client
); }
1037 if (qentry
->msgid
) { printf ("MSGID: %s\n", qentry
->msgid
); }
1041 tl
= qentry
->tolist
;
1045 if (fe
&& tl
->dstatus
== '2') {
1048 if (fl
->to
&& !strcmp (tl
->to
, fl
->to
)) {
1060 dstatus
= fl
->dstatus
;
1064 dstatus
= tl
->dstatus
;
1068 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");
1076 if (!parser
->verbose
) { fflush (stdout
); return; }
1080 if (se
->loglist
.log
) {
1082 loglist_print (&se
->loglist
);
1087 if (fe
->loglist
.log
) {
1088 printf ("FILTER: %s\n", fe
->logid
);
1089 loglist_print (&fe
->loglist
);
1093 if (qentry
->loglist
.log
) {
1095 loglist_print (&qentry
->loglist
);
1106 qentry_get (LParser
*parser
, const char *qid
)
1110 if ((qentry
= g_hash_table_lookup (parser
->qmgr_h
, qid
))) {
1113 if ((qentry
= qentry_new (qid
))) {
1114 g_hash_table_insert (parser
->qmgr_h
, qentry
->qid
, qentry
);
1122 qentry_free_noremove (LParser
*parser
, QEntry
*qentry
)
1128 if ((se
= qentry
->smtpd
)) {
1129 if (sentry_ref_del (se
, qentry
) == 0) {
1130 if (se
->disconnect
) {
1131 sentry_free_noremove (se
);
1137 ep_allocated
-= qentry
->ep
.allocated
;
1138 printf ("MEM: %d\n", ep_allocated
);
1144 if ((qe
= g_hash_table_lookup (qmgr_debug_free
, qentry
))) {
1145 debug_error ("QEntry already freed", NULL
);
1147 g_hash_table_insert (qmgr_debug_free
, qentry
, qentry
);
1153 l
= qentry
->ep
.mblocks
;
1160 l
= qentry
->ep
.blocks
;
1164 g_slice_free1(EPOOL_BLOCK_SIZE
, data
);
1169 qentry_free (LParser
*parser
, QEntry
*qentry
)
1171 g_hash_table_remove (parser
->qmgr_h
, qentry
->qid
);
1173 qentry_free_noremove (parser
, qentry
);
1177 qentry_cleanup_hash (gpointer key
,
1182 LParser
*parser
= (LParser
*)user_data
;
1184 qentry_print (parser
, qe
);
1185 qentry_free_noremove (parser
, qe
);
1189 qentry_finalize (LParser
*parser
, QEntry
*qentry
)
1191 if (qentry
&& qentry
->removed
) {
1192 SEntry
*se
= qentry
->smtpd
;
1194 if (se
&& !se
->disconnect
) return;
1196 FEntry
*fe
= qentry
->filter
;
1198 if (fe
&& !fe
->finished
) return;
1200 qentry_print (parser
, qentry
);
1201 qentry_free (parser
, qentry
);
1203 if (fe
) fentry_free (parser
, fe
);
1210 fentry_new (const char *logid
)
1217 fentry
= (FEntry
*)g_slice_alloc0(EPOOL_BLOCK_SIZE
);
1220 fentry
->ep
.allocated
+= EPOOL_BLOCK_SIZE
;
1221 ep_allocated
+= EPOOL_BLOCK_SIZE
;
1222 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
1228 if ((fe
= g_hash_table_lookup (filter_debug_alloc
, fentry
))) {
1229 debug_error ("FEntry already alloced", NULL
);
1231 g_hash_table_insert (filter_debug_alloc
, fentry
, fentry
);
1236 cpos
= sizeof (FEntry
);
1238 blocks
= (SList
*)((char *)fentry
+ cpos
);
1240 cpos
+= sizeof (SList
);
1242 blocks
->data
= fentry
;
1243 blocks
->next
= NULL
;
1245 fentry
->logid
= logid_cp
= (char *)fentry
+ cpos
;
1246 while ((*logid_cp
++ = *logid
++)) cpos
++;
1247 cpos
= (cpos
+ 4) & ~3;
1249 fentry
->ep
.blocks
= blocks
;
1250 fentry
->ep
.cpos
= cpos
;;
1256 fentry_get (LParser
*parser
, const char *logid
)
1260 if ((fentry
= g_hash_table_lookup (parser
->filter_h
, logid
))) {
1263 if ((fentry
= fentry_new (logid
))) {
1264 g_hash_table_insert (parser
->filter_h
, fentry
->logid
, fentry
);
1272 fentry_tolist_add (FEntry
*fentry
, char dstatus
, const char *to
, int to_len
,
1273 const char *qid
, int qid_len
)
1275 TOList
*tl
= (TOList
*)epool_alloc (&fentry
->ep
, sizeof (TOList
));
1277 tl
->to
= epool_strndup0 (&fentry
->ep
, to
, to_len
);
1280 tl
->relay
= epool_strndup0 (&fentry
->ep
, qid
, qid_len
);
1284 tl
->dstatus
= dstatus
;
1285 tl
->next
= fentry
->tolist
;
1287 fentry
->tolist
= tl
;
1291 fentry_free_noremove (LParser
*parser
, FEntry
*fentry
)
1297 ep_allocated
-= fentry
->ep
.allocated
;
1298 printf ("MEM: %d\n", ep_allocated
);
1304 if ((fe
= g_hash_table_lookup (filter_debug_free
, fentry
))) {
1305 debug_error ("FEntry already freed", NULL
);
1307 g_hash_table_insert (filter_debug_free
, fentry
, fentry
);
1313 l
= fentry
->ep
.mblocks
;
1320 l
= fentry
->ep
.blocks
;
1324 g_slice_free1(EPOOL_BLOCK_SIZE
, data
);
1329 fentry_free (LParser
*parser
, FEntry
*fentry
)
1331 g_hash_table_remove (parser
->filter_h
, fentry
->logid
);
1333 fentry_free_noremove (parser
, fentry
);
1337 fentry_cleanup_hash (gpointer key
,
1342 LParser
*parser
= (LParser
*)user_data
;
1344 fentry_free_noremove (parser
, fe
);
1352 LParser
*parser
= g_malloc0 (sizeof (LParser
));
1357 epool_init (&parser
->ep
);
1359 if (!(parser
->smtpd_h
= g_hash_table_new (g_int_hash
, g_int_equal
))) {
1363 if (!(parser
->qmgr_h
= g_hash_table_new (g_str_hash
, g_str_equal
))) {
1367 if (!(parser
->filter_h
= g_hash_table_new (g_str_hash
, g_str_equal
))) {
1371 //if (!(parser->track_h = g_hash_table_new (g_str_hash, g_str_equal))) {
1375 for (i
= 0; i
< MAX_LOGFILES
; i
++) {
1376 gettimeofday(&tv
, NULL
);
1377 tv
.tv_sec
-= 3600*24*i
;
1378 ltime
= localtime (&tv
.tv_sec
);
1379 parser
->year
[i
] = ltime
->tm_year
+ 1900;
1386 parser_free (LParser
*parser
)
1390 for (i
= 0; i
< MAX_LOGFILES
; i
++) {
1391 if (parser
->stream
[i
]) gzclose (parser
->stream
[i
]);
1394 epool_free (&parser
->ep
);
1396 g_hash_table_destroy (parser
->smtpd_h
);
1397 g_hash_table_destroy (parser
->qmgr_h
);
1398 g_hash_table_destroy (parser
->filter_h
);
1399 //g_hash_table_destroy (parser->track_h);
1406 parser_track (LParser
*parser
, const char *qid
, gboolean insert
)
1410 if ((res
= g_hash_table_lookup (parser
->track_h
, qid
))) {
1413 if (insert
&& (res
= epool_strdup (&parser
->ep
, qid
))) {
1414 g_hash_table_insert (parser
->track_h
, res
, res
);
1422 static const int linebufsize
= 8192;
1423 static int cur_year
;
1424 static int cur_month
= 0;
1425 static int cal_mtod
[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
1428 mkgmtime (struct tm
*tm
)
1432 int year
= tm
->tm_year
+ 1900;
1433 int mon
= tm
->tm_mon
;
1435 res
= (year
- 1970) * 365 + cal_mtod
[mon
];
1437 // leap year corrections (gregorian calendar)
1438 if (mon
<= 1) year
-= 1;
1439 res
+= (year
- 1968) / 4;
1440 res
-= (year
- 1900) / 100;
1441 res
+= (year
- 1600) / 400;
1443 res
+= tm
->tm_mday
- 1;
1444 res
= res
*24 + tm
->tm_hour
;
1445 res
= res
*60 + tm
->tm_min
;
1446 res
= res
*60 + tm
->tm_sec
;
1452 parse_time (const char **text
, int len
)
1456 int year
= cur_year
;
1465 const char *line
= *text
;
1467 if (len
== (linebufsize
- 1)) {
1468 debug_error ("skipping long line data", line
);
1473 debug_error ("skipping short line data", line
);
1478 int csum
= (line
[0]<<16) + (line
[1]<<8) + line
[2];
1481 case 4874606: mon
= 0; break;
1482 case 4613474: mon
= 1; break;
1483 case 5071218: mon
= 2; break;
1484 case 4288626: mon
= 3; break;
1485 case 5071225: mon
= 4; break;
1486 case 4879726: mon
= 5; break;
1487 case 4879724: mon
= 6; break;
1488 case 4289895: mon
= 7; break;
1489 case 5465456: mon
= 8; break;
1490 case 5202804: mon
= 9; break;
1491 case 5140342: mon
= 10; break;
1492 case 4482403: mon
= 11; break;
1494 debug_error ("unable to parse month", line
);
1498 // year change heuristik
1499 if (cur_month
== 11 && mon
== 0) {
1502 if (mon
> cur_month
) cur_month
= mon
;
1504 ltime
= (year
- 1970)*365 + cal_mtod
[mon
];
1506 // leap year corrections (gregorian calendar)
1507 if (mon
<= 1) year
-= 1;
1508 ltime
+= (year
- 1968) / 4;
1509 ltime
-= (year
- 1900) / 100;
1510 ltime
+= (year
- 1600) / 400;
1512 const char *cpos
= line
+ 3;
1514 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
1516 debug_error ("missing spaces after month", line
);
1520 found
= 0; while (isdigit (*cpos
)) { mday
= mday
*10 + *cpos
- 48; cpos
++; found
++; }
1521 if (found
< 1 || found
> 2) {
1522 debug_error ("unable to parse day of month", line
);
1528 found
= 0; while (isspace (*cpos
)) { cpos
++; found
++; }
1530 debug_error ("missing spaces after day of month", line
);
1534 found
= 0; while (isdigit (*cpos
)) { hour
= hour
*10 + *cpos
- 48; cpos
++; found
++; }
1535 if (found
< 1 || found
> 2) {
1536 debug_error ("unable to parse hour", line
);
1544 debug_error ("missing collon after hour", line
);
1549 found
= 0; while (isdigit (*cpos
)) { min
= min
*10 + *cpos
- 48; cpos
++; found
++; }
1550 if (found
< 1 || found
> 2) {
1551 debug_error ("unable to parse minute", line
);
1559 debug_error ("missing collon after minute", line
);
1564 found
= 0; while (isdigit (*cpos
)) { sec
= sec
*10 + *cpos
- 48; cpos
++; found
++; }
1565 if (found
< 1 || found
> 2) {
1566 debug_error ("unable to parse second", line
);
1573 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
1575 debug_error ("missing spaces after time", line
);
1586 parser_count_files (LParser
*parser
)
1589 time_t start
= parser
->start
;
1590 char linebuf
[linebufsize
];
1594 for (i
= 0; i
< MAX_LOGFILES
; i
++) {
1595 cur_year
= parser
->year
[i
];
1598 if ((stream
= gzopen (logfiles
[i
], "r"))) {
1599 if ((line
= gzgets (stream
, linebuf
, linebufsize
))) {
1600 if (parse_time (&line
, strlen (line
)) < start
) {
1616 parse_qid (const char **text
, char *out
, char delim
, int maxlen
)
1623 while (isxdigit (*idx
)) { *copy
++ = *idx
++; found
++; if (found
> maxlen
) break; }
1625 if (found
> 1 && found
< maxlen
&&
1626 ((delim
&& (*idx
== delim
)) || (!delim
&& isspace (*idx
)))) {
1629 while (isspace (*idx
)) idx
++;
1637 print_usage (const char *name
)
1639 fprintf (stderr
, "usage: %s [OPTIONS] [OUTPUTFILENAME]\n", name
);
1640 fprintf (stderr
, "\t-f SENDER mails from SENDER\n");
1641 fprintf (stderr
, "\t-t RECIPIENT mails to RECIPIENT\n");
1642 fprintf (stderr
, "\t-h Server Server IP or Hostname\n");
1643 fprintf (stderr
, "\t-s START start time (YYYY-MM-DD HH:MM:SS)\n");
1644 fprintf (stderr
, "\t or seconds since epoch\n");
1645 fprintf (stderr
, "\t-e END end time (YYYY-MM-DD HH:MM:SS)\n");
1646 fprintf (stderr
, "\t or seconds since epoch\n");
1647 fprintf (stderr
, "\t-m MSGID message ID (exact match)\n");
1648 fprintf (stderr
, "\t-q QID queue ID (exact match)\n");
1649 fprintf (stderr
, "\t-x STRING search for strings\n");
1650 fprintf (stderr
, "\t-l LIMIT print max limit entries\n");
1651 fprintf (stderr
, "\t-v verbose output\n");
1655 // gzgets is ways too slow, so we do it our own way
1658 mygzgetc (gzFile stream
)
1661 static char readbuf
[16384];
1662 static char *readend
= readbuf
+ sizeof (readbuf
);
1663 static char *readpos
= readbuf
+ sizeof (readbuf
);
1665 if (readpos
< readend
) return *readpos
++;
1667 if ((br
= gzread (stream
, readbuf
, sizeof (readbuf
))) <= 0) {
1671 readend
= readbuf
+ br
;
1678 mygzgets (gzFile stream
, char *line
, int bufsize
)
1684 while (--bufsize
> 0 && (c
= mygzgetc(stream
)) != -1) {
1689 if (c
== -1 && cpos
== line
)
1696 extern char *optarg
;
1697 extern int optind
, opterr
, optopt
;
1700 main (int argc
, char * const argv
[])
1702 char linebuf
[linebufsize
];
1704 char *uniqueid
= NULL
;
1718 time_t ctime
, start
, end
;
1724 smtpd_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1725 qmgr_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1726 filter_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1727 smtpd_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1728 qmgr_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1729 filter_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1732 if (!(parser
= parser_new ())) {
1733 fprintf (stderr
, "unable to alloc parser structure\n");
1737 while ((opt
= getopt (argc
, argv
, "f:t:s:e:h:m:q:x:l:I:vgn")) != -1) {
1739 parser
->from
= epool_strdup (&parser
->ep
, optarg
);
1740 } else if (opt
== 't') {
1741 parser
->to
= epool_strdup (&parser
->ep
, optarg
);
1742 } else if (opt
== 'v') {
1743 parser
->verbose
= 1;
1744 } else if (opt
== 'g') {
1745 parser
->exclude_greylist
= 1;
1746 } else if (opt
== 'n') {
1747 parser
->exclude_ndrs
= 1;
1748 } else if (opt
== 'I') {
1750 } else if (opt
== 'h') {
1751 parser
->server
= epool_strdup (&parser
->ep
, optarg
);
1752 } else if (opt
== 'm') {
1753 parser
->msgid
= epool_strdup (&parser
->ep
, optarg
);
1754 } else if (opt
== 'q') {
1755 parser
->qid
= epool_strdup (&parser
->ep
, optarg
);
1756 } else if (opt
== 'x') {
1757 parser
->strmatch
= epool_strdup (&parser
->ep
, optarg
);
1758 } else if (opt
== 'l') {
1760 parser
->limit
= strtoul (optarg
, &l
, 0);
1761 if (!*optarg
|| *l
) {
1762 fprintf (stderr
, "unable to parse limit '%s'\n", optarg
);
1765 } else if (opt
== 's') {
1766 // use strptime to convert time
1769 if ((!(res
= strptime (optarg
, "%F %T", &tm
)) &&
1770 !(res
= strptime (optarg
, "%s", &tm
))) || *res
) {
1771 fprintf (stderr
, "unable to parse start time\n");
1774 parser
->start
= mkgmtime (&tm
);
1776 } else if (opt
== 'e') {
1779 if ((!(res
= strptime (optarg
, "%F %T", &tm
)) &&
1780 !(res
= strptime (optarg
, "%s", &tm
))) || *res
) {
1781 fprintf (stderr
, "unable to parse end time\n");
1784 parser
->end
= mkgmtime (&tm
);
1787 print_usage (argv
[0]);
1792 if (optind
< argc
) {
1794 if ((argc
- optind
) > 1) {
1795 print_usage (argv
[0]);
1799 char *tmpfn
= g_strdup_printf ("/tmp/.proxtrack-%08X.txt", getpid ());
1801 if ((stdout
= freopen (tmpfn
, "w", stdout
)) == NULL
) {
1802 perror ("unable to open output file");
1805 if (rename (tmpfn
, argv
[optind
]) != 0) {
1806 perror ("unable to open output file");
1812 // we use gmtime exerywhere to speedup things, this can cause problems
1813 // when daylight saving time changes
1815 gettimeofday(&tv
, NULL
);
1816 ltime
= localtime (&tv
.tv_sec
);
1818 if (!parser
->start
) {
1822 parser
->start
= mkgmtime (ltime
);
1825 ltime
= localtime (&tv
.tv_sec
);
1828 parser
->end
= mkgmtime (ltime
);
1831 if (parser
->end
< parser
->start
) {
1832 fprintf (stderr
, "end time before start time\n");
1837 if ((filecount
= parser_count_files (parser
)) <= 0) {
1838 fprintf (stderr
, "unable to access log files\n");
1843 printf ("# LogReader: %d %s\n", getpid(), uniqueid
);
1845 printf ("# LogReader: %d\n", getpid());
1848 printf ("# Query options\n");
1849 if (parser
->from
) printf ("# Sender: %s\n", parser
->from
);
1850 if (parser
->to
) printf ("# Recipient: %s\n", parser
->to
);
1851 if (parser
->server
) printf ("# Server: %s\n", parser
->server
);
1852 if (parser
->msgid
) printf ("# MsgID: %s\n", parser
->msgid
);
1853 if (parser
->qid
) printf ("# QID: %s\n", parser
->qid
);
1854 if (parser
->strmatch
) printf ("# Match: %s\n", parser
->strmatch
);
1856 strftime (linebuf
, 256, "%F %T", gmtime (&parser
->start
));
1857 printf ("# Start: %s (%lu)\n", linebuf
, parser
->start
);
1858 strftime (linebuf
, 256, "%F %T", gmtime (&parser
->end
));
1859 printf ("# END: %s (%lu)\n", linebuf
, parser
->end
);
1860 printf ("# End Query Options\n\n");
1864 start
= parser
->start
;
1868 for (i
= filecount
- 1; i
>= 0; i
--) {
1871 // syslog files does not conain years, so we must compute then
1872 // cur_year is the assumed start year
1875 cur_year
= parser
->year
[i
];
1878 if (!(stream
= (gpointer
) fopen (logfiles
[i
], "r"))) continue;
1880 if (!(stream
= (gpointer
) gzopen (logfiles
[i
], "r"))) continue;
1885 if (parser
->limit
&& (parser
->count
>= parser
->limit
)) {
1886 printf ("STATUS: aborted by limit (too many hits)\n");
1891 line
= fgets (linebuf
, linebufsize
, (FILE *)stream
);
1893 line
= mygzgets ((gzFile
)stream
, linebuf
, linebufsize
);
1898 int len
= strlen (line
);
1902 if (!(ctime
= parse_time (&cpos
, len
))) {
1906 if (ctime
< start
) continue;
1907 if (ctime
> end
) break;
1911 found
= 0; while (!isspace (*cpos
)) { cpos
++; found
= 1; }
1913 debug_error ("missing hostname", line
);
1917 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
1919 debug_error ("missing spaces after host", line
);
1923 if ((*cpos
== 'l') && !strncmp (cpos
, "last message repeated", 21)) {
1927 //printf ("LINE: %s\n", line);
1928 //const char *prog = cpos;
1931 found
= 0; while (*cpos
&& (*cpos
!= ':') && (*cpos
!= '[')) {
1932 csum_prog
= (csum_prog
<<8) + *cpos
;
1937 //idx1 = g_strndup (prog, found);
1938 //printf ("TEST:%s:%08X\n", idx1, csum_prog);
1943 found
= 0; while (isdigit (*cpos
)) {
1944 pid
= pid
*10 + *cpos
- 48;
1948 if (found
< 1 || found
> 15 || *cpos
!= ']') {
1949 debug_error ("unable to parse pid", line
);
1955 if (*cpos
++ != ':') {
1956 debug_error ("missing collon", line
);
1961 if (!isspace (*cpos
++)) {
1962 debug_error ("missing space after collon", line
);
1968 parser
->ctime
= ctime
;
1972 if (parser
->strmatch
&& STRMATCH(parser
->strmatch
, text
)) {
1976 if (csum_prog
== 0x6C746572) { // pmg-smtp-filter
1978 if ((idx1
= parse_qid (&cpos
, qidbuf
, ':', 25))) {
1982 if (!(fe
= fentry_get (parser
, idx1
))) {
1986 loglist_add (&fe
->ep
, &fe
->loglist
, line
, len
);
1988 if (strmatch
) fe
->strmatch
= 1;
1990 //fixme: BCC, Notify?
1991 //fixme: parse virus info
1992 //fixme: parse spam score
1994 if ((*cpos
== 'a') && !strncmp (cpos
, "accept mail to <", 16)) {
1996 const char *to_s
, *to_e
;
1998 to_s
= cpos
= cpos
+ 16;
2000 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2002 if (*cpos
!= '>') continue;
2008 if ((*cpos
++ != ' ') || (*cpos
++ != '(')) continue;
2010 if (!(idx1
= parse_qid (&cpos
, qidbuf
, ')', 15))) continue;
2012 // parser_track (parser, idx1, 1);
2014 fentry_tolist_add (fe
, 'A', to_s
, to_e
- to_s
, idx1
, strlen (idx1
));
2016 } else if ((*cpos
== 'm') && !strncmp (cpos
, "moved mail for <", 16)) {
2017 const char *to_s
, *to_e
;
2019 to_s
= cpos
= cpos
+ 16;
2021 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2025 if (strncmp (cpos
, "> to ", 5)) continue;
2028 if (!strncmp (cpos
, "spam", 4)) {
2030 } else if (!strncmp (cpos
, "virus", 5)) {
2036 if (strncmp (cpos
, " quarantine - ", 14)) continue;
2039 if (!(idx1
= parse_qid (&cpos
, qidbuf
, 0, 25))) continue;
2041 fentry_tolist_add (fe
, 'Q', to_s
, to_e
- to_s
, idx1
, strlen (idx1
));
2043 } else if ((*cpos
== 'b') && !strncmp (cpos
, "block mail to <", 15)) {
2047 to_s
= cpos
= cpos
+ 15;
2049 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2051 if (*cpos
!= '>') continue;
2053 fentry_tolist_add (fe
, 'B', to_s
, cpos
- to_s
, NULL
, 0);
2055 } else if ((*cpos
== 'p') && !strncmp (cpos
, "processing time: ", 17)) {
2058 sscanf (cpos
, "%f", &fe
->ptime
);
2065 } else if (csum_prog
== 0x7265656E) { // postfix/postscreen
2070 debug_error ("no pid for postscreen", line
);
2075 if ((*text
== 'N') && !strncmp (text
, "NOQUEUE: reject: RCPT from ", 27)) {
2079 if (!(idx1
= strstr (cpos
, "; client ["))) continue;
2081 const char *client
= cpos
= idx1
+ 10;
2083 while (*cpos
&& (*cpos
!= ']')) { cpos
++; }
2085 const char *client_end
= cpos
;
2087 if (!(idx1
= strstr (cpos
, "; from=<"))) continue;
2089 const char *from
= cpos
= idx1
+ 8;
2091 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2094 if ((*cpos
== '>') && strncmp (cpos
, ">, to=<", 7)) continue;
2096 const char *to
= cpos
= cpos
+ 7;
2098 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2100 if (*cpos
!= '>') continue;
2102 if (!(se
= sentry_get (parser
, pid
))) {
2106 if (strmatch
) se
->strmatch
= 1;
2108 loglist_add (&se
->ep
, &se
->loglist
, line
, len
);
2110 sentry_nqlist_add (se
, ctime
, from
, idx1
- from
, to
, cpos
- to
, 'N');
2112 sentry_set_connect (se
, client
, client_end
- client
);
2114 g_hash_table_remove (parser
->smtpd_h
, &se
->pid
);
2118 sentry_print (parser
, se
);
2119 sentry_free (parser
, se
);
2122 } else if (csum_prog
== 0x716D6772) { // postfix/qmgr
2124 if ((idx2
= text
) && (idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2128 if (!(qe
= qentry_get (parser
, idx1
))) {
2132 if (strmatch
) qe
->strmatch
= 1;
2136 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
);
2138 if ((*idx2
== 'f') && !strncmp (idx2
, "from=<", 6)) {
2140 cpos
= idx2
= idx2
+ 6;
2141 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2144 debug_error ("unable to parse 'from' address", line
);
2148 qentry_set_from (qe
, idx2
, cpos
- idx2
);
2152 if ((*cpos
== ',') && !strncmp (cpos
, ", size=", 7)) {
2156 while (isdigit (*cpos
)) { size
= size
*10 + *cpos
++ - 48; }
2161 } else if ((*idx2
== 'r') && !strncmp (idx2
, "removed\n", 8)) {
2165 qentry_finalize (parser
, qe
);
2169 } else if ((csum_prog
== 0x736D7470) || //postfix/smtp
2170 (csum_prog
== 0x6C6D7470) || //postfix/lmtp
2171 (csum_prog
== 0x6F63616C) || //postfix/local
2172 (csum_prog
== 0x72726F72)) { //postfix/error
2174 int lmtp
= (csum_prog
== 0x6C6D7470);
2176 if ((cpos
= text
) && (idx1
= parse_qid (&cpos
, qidbuf
, ':', 15))) {
2180 if (!(qe
= qentry_get (parser
, idx1
))) {
2184 if (strmatch
) qe
->strmatch
= 1;
2188 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
);
2190 if (strncmp (cpos
, "to=<", 4)) continue;
2193 const char *to_s
, *to_e
;
2197 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2199 if (*cpos
!= '>') continue;
2205 if (!(cpos
= strstr (cpos
, ", relay="))) continue;
2208 const char *relay_s
, *relay_e
;
2212 while (*cpos
&& (*cpos
!= ',')) { cpos
++; }
2214 if (*cpos
!= ',') continue;
2218 if (!(idx1
= strstr (cpos
+ 1, ", dsn="))) continue;
2222 if (!isdigit (*cpos
)) continue;
2224 char dstatus
= *cpos
;
2226 qentry_tolist_add (qe
, ctime
, dstatus
, to_s
, to_e
- to_s
,
2227 relay_s
, relay_e
- relay_s
);
2229 if (!lmtp
) continue; // filter always uses lmtp
2231 if (!(idx1
= strstr (cpos
+ 1, "status=sent (250 2.")))
2234 cpos
= idx1
= idx1
+ 19;
2236 if (*cpos
== '5' && (idx1
= strstr (cpos
, "5.0 OK ("))) {
2237 cpos
= idx1
= idx1
+ 8;
2238 } else if (*cpos
== '7' && (idx1
= strstr (cpos
, "7.0 BLOCKED ("))) {
2239 cpos
= idx1
= idx1
+ 13;
2244 if (!(idx1
= parse_qid (&cpos
, qidbuf
, ')', 25)))
2251 if ((fe
= g_hash_table_lookup (parser
->filter_h
, idx1
))) {
2256 } else if (csum_prog
== 0x6D747064) { // postfix/smtpd
2260 debug_error ("no pid for smtpd", line
);
2264 if (!(se
= sentry_get (parser
, pid
))) {
2268 if (strmatch
) se
->strmatch
= 1;
2270 loglist_add (&se
->ep
, &se
->loglist
, line
, len
);
2272 if ((*text
== 'c') && !strncmp (text
, "connect from ", 13)) {
2274 cpos
= idx1
= text
+ 13;
2276 while (*idx1
&& !isspace (*idx1
)) { idx1
++; }
2278 sentry_set_connect (se
, cpos
, idx1
- cpos
);
2280 // fixme: do we need this?
2281 //if (strcmp (se->connect, "localhost[127.0.0.1]")) {
2282 // se->external = 1;
2285 } else if ((*text
== 'd') && !strncmp (text
, "disconnect from", 15)) {
2288 g_hash_table_remove (parser
->smtpd_h
, &se
->pid
);
2292 if (sentry_ref_rem_unneeded (parser
, se
) == 0) {
2293 sentry_print (parser
, se
);
2294 sentry_free (parser
, se
);
2296 sentry_ref_finalize (parser
, se
);
2299 } else if ((*text
== 'N') && !strncmp (text
, "NOQUEUE:", 8)) {
2303 // parse 'whatsup' (reject:)
2304 while (*cpos
&& (*cpos
!= ':')) { cpos
++; }
2305 if (*cpos
!= ':') continue;
2308 // parse '%s from %s:'
2309 while (*cpos
&& (*cpos
!= ':')) { cpos
++; }
2310 if (*cpos
!= ':') continue;
2314 while (*cpos
&& (*cpos
!= ';')) { cpos
++; }
2315 if (*cpos
!= ';') continue;
2319 *(char *)cpos
= 0; // dangerous hack: delimit string
2320 if (strstr (idx1
, ": Recipient address rejected: Service is unavailable (try later)")) {
2323 *(char *)cpos
= ';'; // dangerous hack: restore line
2325 if (!(idx1
= strstr (cpos
, "; from=<"))) continue;
2327 const char *from
= cpos
= idx1
+ 8;
2329 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2332 if ((*cpos
== '>') && strncmp (cpos
, "> to=<", 6)) continue;
2334 const char *to
= cpos
= cpos
+ 6;
2336 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2338 if (*cpos
!= '>') continue;
2340 sentry_nqlist_add (se
, ctime
, from
, idx1
- from
, to
, cpos
- to
, dstatus
);
2342 } else if ((idx2
= text
) && (idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2346 if ((qe
= qentry_get (parser
, idx1
))) {
2348 if (strmatch
) qe
->strmatch
= 1;
2350 sentry_ref_add (se
, qe
);
2352 if ((*idx2
== 'c') && !strncmp (idx2
, "client=", 7)) {
2353 cpos
= idx2
= idx2
+ 7;
2355 while (*cpos
&& !isspace (*cpos
)) { cpos
++; }
2357 qentry_set_client (qe
, idx2
, cpos
- idx2
);
2363 } else if (csum_prog
== 0x616E7570) { // postfix/cleanup
2368 if ((idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2369 if ((qe
= qentry_get (parser
, idx1
))) {
2371 if (strmatch
) qe
->strmatch
= 1;
2373 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
);
2375 if ((*idx2
== 'm') && !strncmp (idx2
, "message-id=", 11)) {
2377 cpos
= idx2
= idx2
+ 11;
2379 while (*cpos
&& !isspace(*cpos
)) { cpos
++; }
2381 qentry_set_msgid (qe
, idx2
, cpos
- idx2
);
2391 fclose ((FILE *)stream
);
2393 gzclose ((gzFile
)stream
);
2396 if (ctime
> end
) break;
2401 printf ("LINES: %d\n", lines
);
2402 printf ("MEM SMTPD entries: %d\n", g_hash_table_size (parser
->smtpd_h
));
2403 printf ("MEM QMGR entries: %d\n", g_hash_table_size (parser
->qmgr_h
));
2404 printf ("MEM FILTER entries: %d\n", g_hash_table_size (parser
->filter_h
));
2406 printf ("MEMDEB SMTPD entries: %d %d\n",
2407 g_hash_table_size (smtpd_debug_alloc
),
2408 g_hash_table_size (smtpd_debug_free
));
2409 printf ("MEMDEB QMGR entries: %d %d\n",
2410 g_hash_table_size (qmgr_debug_alloc
),
2411 g_hash_table_size (qmgr_debug_free
));
2412 printf ("MEMDEB FILTER entries: %d %d\n",
2413 g_hash_table_size (filter_debug_alloc
),
2414 g_hash_table_size (filter_debug_free
));
2417 g_hash_table_foreach (parser
->qmgr_h
, qentry_cleanup_hash
, parser
);
2418 g_hash_table_foreach (parser
->smtpd_h
, sentry_cleanup_hash
, parser
);
2419 g_hash_table_foreach (parser
->filter_h
, fentry_cleanup_hash
, parser
);
2422 printf ("MEMDEB SMTPD entries: %d %d\n",
2423 g_hash_table_size (smtpd_debug_alloc
),
2424 g_hash_table_size (smtpd_debug_free
));
2425 printf ("MEMDEB QMGR entries: %d %d\n",
2426 g_hash_table_size (qmgr_debug_alloc
),
2427 g_hash_table_size (qmgr_debug_free
));
2428 printf ("MEMDEB FILTER entries: %d %d\n",
2429 g_hash_table_size (filter_debug_alloc
),
2430 g_hash_table_size (filter_debug_free
));
2432 g_hash_table_foreach (smtpd_debug_alloc
, sentry_debug_alloc
, parser
);
2438 printf ("MEMMAX %d\n", ep_maxalloc
);
2441 parser_free (parser
);