]>
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 32
64 typedef struct _SList SList
;
70 typedef struct _NQList NQList
;
79 typedef struct _TOList TOList
;
88 #define MatchTypeQID 1
89 #define MatchTypeRelLineNr 2
91 typedef struct _MatchList MatchList
;
96 unsigned long rel_line_nr
;
101 GHashTable
*smtpd_debug_alloc
;
102 GHashTable
*qmgr_debug_alloc
;
103 GHashTable
*filter_debug_alloc
;
104 GHashTable
*smtpd_debug_free
;
105 GHashTable
*qmgr_debug_free
;
106 GHashTable
*filter_debug_free
;
109 // EPool: Entry related memory pools
117 typedef struct _EPool EPool
;
119 SList
*blocks
; // allocated usiing g_slice_alloc(EPOOL_BLOCK_SIZE)
120 SList
*mblocks
; // allocated use g_malloc
131 GHashTable
*filter_h
;
132 //GHashTable *track_h;
133 gzFile stream
[MAX_LOGFILES
];
136 time_t year
[MAX_LOGFILES
];
140 MatchList
*match_list
;
147 unsigned int exclude_greylist
:1;
148 unsigned int exclude_ndrs
:1;
151 typedef struct _SEntry SEntry
;
152 typedef struct _QEntry QEntry
;
153 typedef struct _FEntry FEntry
;
155 typedef struct _LogEntry LogEntry
;
156 typedef struct _LogList LogList
;
160 unsigned long linenr
;
166 LogEntry
*logs_last
; // pointer to last log (speedup add log)
169 // SEntry: Store SMTPD related logs
184 // time,rel_line_nr is used as cursor/ID
186 unsigned long rel_line_nr
;
188 //unsigned int external:1; // not from local host
189 unsigned int disconnect
:1;
190 unsigned int strmatch
:1;
194 // QEntry: Store Queue (qmgr, smtp, lmtp) related logs
213 unsigned int cleanup
:1;
214 unsigned int removed
:1;
215 unsigned int filtered
:1; // set when passed via lmtp to filter
216 unsigned int strmatch
:1;
219 // FEntry: Store filter (proxprox) related logs
226 char *logid
; // proxprox log id
232 unsigned int finished
:1;
233 unsigned int strmatch
:1;
237 void debug_error (char *msg
, const char *line
);
239 EPool
*epool_init (EPool
*ep
);
240 void epool_free (EPool
*ep
);
241 gpointer
epool_alloc (EPool
*ep
, int size
);
242 char *epool_strndup (EPool
*ep
, const char *s
, int len
);
243 char *epool_strndup0 (EPool
*ep
, const char *s
, int len
);
244 char *epool_strdup (EPool
*ep
, const char *s
);
246 void loglist_print (LogList
*loglist
);
247 void loglist_add (EPool
*ep
, LogList
*loglist
, const char *text
, int len
, unsigned long linenr
);
249 SEntry
*sentry_new (int pid
, time_t ltime
, unsigned long rel_line_nr
);
250 SEntry
*sentry_get (LParser
*parser
, int pid
, time_t ltime
, unsigned long rel_line_nr
);
251 void sentry_ref_add (SEntry
*sentry
, QEntry
*qentry
);
252 int sentry_ref_del (SEntry
*sentry
, QEntry
*qentry
);
253 void sentry_ref_finalize (LParser
*parser
, SEntry
*sentry
);
254 int sentry_ref_rem_unneeded (LParser
*parser
, SEntry
*sentry
);
255 void sentry_nqlist_add (SEntry
*sentry
, time_t ltime
, const char *from
, int from_len
,
256 const char *to
, int to_len
, char dstatus
);
257 void sentry_print (LParser
*parser
, SEntry
*sentry
);
258 void sentry_set_connect (SEntry
*sentry
, const char *connect
, int len
);
259 void sentry_free_noremove (SEntry
*sentry
);
260 void sentry_free (LParser
*parser
, SEntry
*sentry
);
261 void sentry_cleanup_hash (gpointer key
, gpointer value
, gpointer user_data
);
264 QEntry
*qentry_new (const char *qid
);
265 QEntry
*qentry_get (LParser
*parser
, const char *qid
);
266 void qentry_tolist_add (QEntry
*qentry
, time_t ltime
, char dstatus
, const char *to
,
267 int to_len
, const char *relay
, int relay_len
);
268 void qentry_set_from (QEntry
*qentry
, const char *from
, int len
);
269 void qentry_set_msgid (QEntry
*qentry
, const char *msgid
, int len
);
270 void qentry_set_client (QEntry
*qentry
, const char *client
, int len
);
271 void qentry_print (LParser
*parser
, QEntry
*qentry
);
272 void qentry_finalize (LParser
*parser
, QEntry
*qentry
);
273 void qentry_free_noremove (LParser
*parser
, QEntry
*qentry
);
274 void qentry_free (LParser
*parser
, QEntry
*qentry
);
275 void qentry_cleanup_hash (gpointer key
, gpointer value
, gpointer user_data
);
278 FEntry
*fentry_new (const char *logid
);
279 FEntry
*fentry_get (LParser
*parser
, const char *logid
);
280 void fentry_tolist_add (FEntry
*fentry
, char dstatus
, const char *to
,
281 int to_len
, const char *qid
, int qid_len
);
282 void fentry_free_noremove (LParser
*parser
, FEntry
*fentry
);
283 void fentry_free (LParser
*parser
, FEntry
*fentry
);
284 void fentry_cleanup_hash (gpointer key
, gpointer value
, gpointer user_data
);
286 LParser
*parser_new ();
287 void parser_free (LParser
*parser
);
289 //char *parser_track (LParser *parser, const char *qid, gboolean insert);
293 //#define LOGPATH "./log/"
294 #define LOGPATH "/var/log/"
295 //#define LOGPATH "/root/testlog/"
297 static const char *logfiles
[] = {
300 LOGPATH
"syslog.2.gz",
301 LOGPATH
"syslog.3.gz",
302 LOGPATH
"syslog.4.gz",
303 LOGPATH
"syslog.5.gz",
304 LOGPATH
"syslog.6.gz",
305 LOGPATH
"syslog.7.gz",
306 LOGPATH
"syslog.8.gz",
307 LOGPATH
"syslog.9.gz",
308 LOGPATH
"syslog.10.gz",
309 LOGPATH
"syslog.11.gz",
310 LOGPATH
"syslog.12.gz",
311 LOGPATH
"syslog.13.gz",
312 LOGPATH
"syslog.14.gz",
313 LOGPATH
"syslog.15.gz",
314 LOGPATH
"syslog.16.gz",
315 LOGPATH
"syslog.17.gz",
316 LOGPATH
"syslog.18.gz",
317 LOGPATH
"syslog.19.gz",
318 LOGPATH
"syslog.20.gz",
319 LOGPATH
"syslog.21.gz",
320 LOGPATH
"syslog.22.gz",
321 LOGPATH
"syslog.23.gz",
322 LOGPATH
"syslog.24.gz",
323 LOGPATH
"syslog.25.gz",
324 LOGPATH
"syslog.26.gz",
325 LOGPATH
"syslog.27.gz",
326 LOGPATH
"syslog.28.gz",
327 LOGPATH
"syslog.29.gz",
328 LOGPATH
"syslog.30.gz",
329 LOGPATH
"syslog.31.gz",
333 debug_error (char *msg
, const char *line
)
336 fprintf (stderr
, "ERROR: %s\n", msg
);
337 if (line
) fprintf (stderr
, "LINE: %s\n", line
);
346 epool_init (EPool
*ep
)
351 data
= g_slice_alloc0(EPOOL_BLOCK_SIZE
);
352 blocks
= (SList
*)data
;
355 ep
->allocated
+= EPOOL_BLOCK_SIZE
;
356 ep_allocated
+= EPOOL_BLOCK_SIZE
;
357 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
365 ep
->cpos
= sizeof (SList
);
371 epool_free (EPool
*ep
)
377 ep_allocated
-= ep
->allocated
;
378 printf ("MEM: %d\n", ep_allocated
);
396 g_slice_free1(EPOOL_BLOCK_SIZE
, data
);
401 epool_alloc (EPool
*ep
, int size
)
403 int rs
= (size
+ 3) & ~3;
404 int space
= EPOOL_BLOCK_SIZE
- sizeof (SList
) - ep
->cpos
;
407 if (size
> EPOOL_MAX_SIZE
) {
409 if (space
>= sizeof (SList
)) {
410 blocks
= ep
->blocks
->data
+ ep
->cpos
;
411 ep
->cpos
+= sizeof (SList
);
413 blocks
= (SList
*)epool_alloc (ep
, sizeof (SList
));
416 data
= g_malloc (size
);
418 ep
->allocated
+= size
;
419 ep_allocated
+= size
;
420 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
423 blocks
->next
= ep
->mblocks
;
425 ep
->mblocks
= blocks
;
429 } else if (space
>= rs
) {
430 data
= ep
->blocks
->data
+ ep
->cpos
;
436 SList
*blocks
= ep
->blocks
->data
+ ep
->cpos
;
438 data
= g_slice_alloc0 (EPOOL_BLOCK_SIZE
);
440 blocks
->next
= ep
->blocks
;
446 ep
->allocated
+= EPOOL_BLOCK_SIZE
;
447 ep_allocated
+= EPOOL_BLOCK_SIZE
;
448 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
456 epool_strndup (EPool
*ep
, const char *s
, int len
)
459 char *res
= epool_alloc (ep
, l
);
465 epool_strndup0 (EPool
*ep
, const char *s
, int len
)
467 char *res
= epool_alloc (ep
, len
+ 1);
468 strncpy (res
, s
, len
);
475 epool_strdup (EPool
*ep
, const char *s
)
477 int l
= strlen (s
) + 1;
478 char *res
= epool_alloc (ep
, l
);
484 loglist_print (LogList
*loglist
)
486 LogEntry
*log
= loglist
->log
;
488 printf ("L%08X %s", log
->linenr
, log
->text
);
495 loglist_add (EPool
*ep
, LogList
*loglist
, const char *text
, int len
, unsigned long linenr
)
500 if (len
!= strlen (text
)) {
501 debug_error ("string with wrong len", NULL
);
504 if (text
[len
] != 0) {
505 debug_error ("string is not null terminated", NULL
);
509 log
= epool_alloc (ep
, sizeof (LogEntry
));
511 log
->text
= epool_strndup (ep
, text
, len
);
512 log
->linenr
= linenr
;
515 if (loglist
->logs_last
) {
516 loglist
->logs_last
->next
= log
;
517 loglist
->logs_last
= log
;
520 loglist
->logs_last
= log
;
527 sentry_new (int pid
, time_t ltime
, unsigned long rel_line_nr
)
533 sentry
= (SEntry
*)g_slice_alloc0(EPOOL_BLOCK_SIZE
);
535 sentry
->ltime
= ltime
;
536 sentry
->rel_line_nr
= rel_line_nr
;
539 sentry
->ep
.allocated
+= EPOOL_BLOCK_SIZE
;
540 ep_allocated
+= EPOOL_BLOCK_SIZE
;
541 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
547 if ((se
= g_hash_table_lookup (smtpd_debug_alloc
, sentry
))) {
548 debug_error ("SEntry already alloced", NULL
);
550 g_hash_table_insert (smtpd_debug_alloc
, sentry
, sentry
);
555 cpos
= sizeof (SEntry
);
557 blocks
= (SList
*)((char *)sentry
+ cpos
);
559 cpos
+= sizeof (SList
);
561 blocks
->data
= sentry
;
564 sentry
->ep
.blocks
= blocks
;
565 sentry
->ep
.cpos
= cpos
;
571 sentry_get (LParser
*parser
, int pid
, time_t ltime
, unsigned long rel_line_nr
)
575 if ((sentry
= g_hash_table_lookup (parser
->smtpd_h
, &pid
))) {
579 if ((sentry
= sentry_new (pid
, ltime
, rel_line_nr
))) {
580 g_hash_table_insert (parser
->smtpd_h
, &sentry
->pid
, sentry
);
588 sentry_ref_add (SEntry
*sentry
, QEntry
*qentry
)
593 if (qentry
->smtpd
!= sentry
) {
594 debug_error ("qentry ref already set", NULL
);
601 if (l
->data
== qentry
) return;
605 l
= epool_alloc (&sentry
->ep
, sizeof (SList
));
607 qentry
->smtpd
= sentry
;
610 l
->next
= sentry
->refs
;
616 sentry_ref_del (SEntry
*sentry
, QEntry
*qentry
)
618 SList
*l
= sentry
->refs
;
621 if (!qentry
->smtpd
) {
622 debug_error ("qentry does not hav a qentry ref", NULL
);
629 QEntry
*qe
= (QEntry
*)l
->data
;
633 if (qe
&& qe
->cleanup
) count
++;
642 sentry_ref_finalize (LParser
*parser
, SEntry
*sentry
)
644 SList
*l
= sentry
->refs
;
650 QEntry
*qe
= l
->data
;
658 if (!qe
->removed
) continue;
660 FEntry
*fe
= qe
->filter
;
662 if (fe
&& !fe
->finished
) continue;
666 qentry_print (parser
, qe
);
671 qentry_free (parser
, qe
);
673 if (fe
) fentry_free (parser
, fe
);
677 if (!count
) sentry_free_noremove (sentry
);
681 sentry_ref_rem_unneeded (LParser
*parser
, SEntry
*sentry
)
683 SList
*l
= sentry
->refs
;
687 QEntry
*qe
= (QEntry
*)l
->data
;
692 qentry_free (parser
, qe
);
706 sentry_nqlist_add (SEntry
*sentry
, time_t ltime
, const char *from
, int from_len
,
707 const char *to
, int to_len
, char dstatus
)
709 NQList
*nq
= (NQList
*)epool_alloc (&sentry
->ep
, sizeof (NQList
));
711 nq
->from
= epool_strndup0 (&sentry
->ep
, from
, from_len
);
712 nq
->to
= epool_strndup0 (&sentry
->ep
, to
, to_len
);
713 nq
->dstatus
= dstatus
;
715 nq
->next
= sentry
->nqlist
;
721 sentry_print (LParser
*parser
, SEntry
*sentry
)
725 if (parser
->msgid
) return;
727 if (parser
->server
) {
728 if (!sentry
->connect
) return;
729 if (!strcasestr (sentry
->connect
, parser
->server
)) return;
732 MatchList
*match
= parser
->match_list
;
736 if (match
->mtype
== MatchTypeQID
) {
738 } else if (match
->mtype
== MatchTypeRelLineNr
) {
739 if (match
->ltime
== sentry
->ltime
&& match
->rel_line_nr
== sentry
->rel_line_nr
) {
744 g_error("implement me");
751 if (parser
->from
|| parser
->to
||
752 parser
->exclude_greylist
|| parser
->exclude_ndrs
) {
757 if (!*(parser
->from
)) {
758 if (*(nq
->from
)) nq
->dstatus
= 0;
759 } else if (!STRMATCH(parser
->from
, nq
->from
)) {
764 if (parser
->exclude_greylist
&& nq
->dstatus
== 'G') nq
->dstatus
= 0;
766 if (parser
->exclude_ndrs
&& nq
->from
&& !*nq
->from
) nq
->dstatus
= 0;
768 if (parser
->to
&& nq
->to
&& !STRMATCH(parser
->to
, nq
->to
)) {
772 if (nq
->dstatus
!= 0) found
= 1;
779 if (parser
->strmatch
&& !sentry
->strmatch
) return;
781 if (parser
->verbose
) {
785 printf ("CTIME: %08lX\n", parser
->ctime
);
787 if (sentry
->connect
) { printf ("CONNECT: %s\n", sentry
->connect
); }
788 //printf ("EXTERNAL: %d\n", sentry->external);
794 if (nq
->from
&& nq
->to
&& nq
->dstatus
) {
795 printf ("TO:%08lX:T%08lXL%08lX:%c: from <%s> to <%s>\n", nq
->ltime
,
796 sentry
->ltime
, sentry
->rel_line_nr
, nq
->dstatus
,
803 if (!parser
->verbose
) { fflush (stdout
); return; }
805 if (parser
->verbose
> 1) {
807 loglist_print (&sentry
->loglist
);
816 sentry_set_connect (SEntry
*sentry
, const char *connect
, int len
)
818 if (sentry
->connect
) {
820 if (strncmp (sentry
->connect
, connect
, len
)) {
821 debug_error ("duplicate connect", NULL
);
825 sentry
->connect
= epool_strndup0 (&sentry
->ep
, connect
, len
);
830 sentry_free_noremove (SEntry
*sentry
)
836 ep_allocated
-= sentry
->ep
.allocated
;
837 printf ("MEM: %d\n", ep_allocated
);
843 if ((se
= g_hash_table_lookup (smtpd_debug_free
, sentry
))) {
844 debug_error ("SEntry already freed", NULL
);
846 g_hash_table_insert (smtpd_debug_free
, sentry
, sentry
);
852 l
= sentry
->ep
.mblocks
;
859 l
= sentry
->ep
.blocks
;
863 g_slice_free1(EPOOL_BLOCK_SIZE
, data
);
868 sentry_free (LParser
*parser
, SEntry
*sentry
)
870 g_hash_table_remove (parser
->smtpd_h
, &sentry
->pid
);
872 sentry_free_noremove (sentry
);
876 sentry_cleanup_hash (gpointer key
,
881 LParser
*parser
= (LParser
*)user_data
;
883 sentry_print (parser
, se
);
884 sentry_free_noremove (se
);
889 sentry_debug_alloc (gpointer key
,
893 LParser
*parser
= (LParser
*)user_data
;
897 if ((fe
= g_hash_table_lookup (smtpd_debug_free
, se
))) {
901 printf ("FOUND ALLOCATED SENTRY:\n");
902 sentry_print (parser
, se
);
911 qentry_new (const char *qid
)
918 qentry
= (QEntry
*)g_slice_alloc0(EPOOL_BLOCK_SIZE
);
921 qentry
->ep
.allocated
+= EPOOL_BLOCK_SIZE
;
922 ep_allocated
+= EPOOL_BLOCK_SIZE
;
923 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
929 if ((qe
= g_hash_table_lookup (qmgr_debug_alloc
, qentry
))) {
930 debug_error ("QEntry already alloced", NULL
);
932 g_hash_table_insert (qmgr_debug_alloc
, qentry
, qentry
);
937 cpos
= sizeof (QEntry
);
939 blocks
= (SList
*)((char *)qentry
+ cpos
);
941 cpos
+= sizeof (SList
);
943 blocks
->data
= qentry
;
946 qentry
->qid
= qid_cp
= (char *)qentry
+ cpos
;
947 while ((*qid_cp
++ = *qid
++)) cpos
++;
949 cpos
= (cpos
+ 4) & ~3;
951 qentry
->ep
.blocks
= blocks
;
952 qentry
->ep
.cpos
= cpos
;;
958 qentry_tolist_add (QEntry
*qentry
, time_t ltime
, char dstatus
, const char *to
, int to_len
,
959 const char *relay
, int relay_len
)
961 TOList
*tl
= (TOList
*)epool_alloc (&qentry
->ep
, sizeof (TOList
));
963 tl
->to
= epool_strndup0 (&qentry
->ep
, to
, to_len
);
964 tl
->relay
= epool_strndup0 (&qentry
->ep
, relay
, relay_len
);
965 tl
->dstatus
= dstatus
;
967 tl
->next
= qentry
->tolist
;
973 qentry_set_from (QEntry
*qentry
, const char *from
, int len
)
977 if (strncmp (qentry
->from
, from
, len
)) {
978 debug_error ("duplicate from", NULL
);
982 qentry
->from
= epool_strndup0 (&qentry
->ep
, from
, len
);
987 qentry_set_msgid (QEntry
*qentry
, const char *msgid
, int len
)
991 if (strncmp (qentry
->msgid
, msgid
, len
)) {
992 debug_error ("duplicate msgid", NULL
);
996 qentry
->msgid
= epool_strndup0 (&qentry
->ep
, msgid
, len
);
1001 qentry_set_client (QEntry
*qentry
, const char *client
, int len
)
1003 if (qentry
->client
) {
1005 if (strncmp (qentry
->client
, client
, len
)) {
1006 debug_error ("duplicate client", NULL
);
1010 qentry
->client
= epool_strndup0 (&qentry
->ep
, client
, len
);
1015 qentry_print (LParser
*parser
, QEntry
*qentry
)
1018 SEntry
*se
= qentry
->smtpd
;
1019 FEntry
*fe
= qentry
->filter
;
1021 if (parser
->msgid
) {
1022 if (!qentry
->msgid
) return;
1023 if (strcasecmp (parser
->msgid
, qentry
->msgid
)) return;
1026 MatchList
*match
= parser
->match_list
;
1030 if (match
->mtype
== MatchTypeQID
) {
1031 if ((fe
&& !strcmp (fe
->logid
, match
->id
)) ||
1032 (!strcmp (qentry
->qid
, match
->id
))) {
1036 } else if (match
->mtype
== MatchTypeRelLineNr
) {
1037 if (se
&& match
->ltime
== se
->ltime
&& match
->rel_line_nr
== se
->rel_line_nr
) {
1042 g_error("implement me");
1044 match
= match
->next
;
1049 if (parser
->server
) {
1051 if (se
&& se
->connect
&& strcasestr (se
->connect
, parser
->server
)) found
= 1;
1052 if (qentry
->client
&& strcasestr (qentry
->client
, parser
->server
)) found
= 1;
1058 if (!qentry
->from
) return;
1059 if (!*(parser
->from
)) {
1060 if (*(qentry
->from
)) return;
1061 } else if (!STRMATCH(parser
->from
, qentry
->from
)) {
1065 if (parser
->exclude_ndrs
&& qentry
->from
&& !*qentry
->from
) return;
1069 tl
= qentry
->tolist
;
1072 if (parser
->to
&& !STRMATCH(parser
->to
, tl
->to
)) {
1082 if (parser
->strmatch
&&
1083 !(qentry
->strmatch
|| (se
&& se
->strmatch
) || (fe
&& fe
->strmatch
)))
1087 if (parser
->verbose
) {
1089 printf ("QENTRY: %s\n", qentry
->qid
);
1091 printf ("CTIME: %08lX\n", parser
->ctime
);
1092 printf ("SIZE: %u\n", qentry
->size
);
1094 if (qentry
->client
) { printf ("CLIENT: %s\n", qentry
->client
); }
1096 if (qentry
->msgid
) { printf ("MSGID: %s\n", qentry
->msgid
); }
1100 tl
= qentry
->tolist
;
1104 if (fe
&& tl
->dstatus
== '2') {
1107 if (fl
->to
&& !strcmp (tl
->to
, fl
->to
)) {
1119 dstatus
= fl
->dstatus
;
1123 dstatus
= tl
->dstatus
;
1127 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");
1135 if (!parser
->verbose
) { fflush (stdout
); return; }
1137 if (parser
->verbose
> 1) {
1139 if (se
&& se
->loglist
.log
) {
1141 loglist_print (&se
->loglist
);
1144 if (fe
&& fe
->loglist
.log
) {
1145 printf ("FILTER: %s\n", fe
->logid
);
1146 loglist_print (&fe
->loglist
);
1149 if (qentry
->loglist
.log
) {
1151 loglist_print (&qentry
->loglist
);
1163 qentry_get (LParser
*parser
, const char *qid
)
1167 if ((qentry
= g_hash_table_lookup (parser
->qmgr_h
, qid
))) {
1170 if ((qentry
= qentry_new (qid
))) {
1171 g_hash_table_insert (parser
->qmgr_h
, qentry
->qid
, qentry
);
1179 qentry_free_noremove (LParser
*parser
, QEntry
*qentry
)
1185 if ((se
= qentry
->smtpd
)) {
1186 if (sentry_ref_del (se
, qentry
) == 0) {
1187 if (se
->disconnect
) {
1188 sentry_free_noremove (se
);
1194 ep_allocated
-= qentry
->ep
.allocated
;
1195 printf ("MEM: %d\n", ep_allocated
);
1201 if ((qe
= g_hash_table_lookup (qmgr_debug_free
, qentry
))) {
1202 debug_error ("QEntry already freed", NULL
);
1204 g_hash_table_insert (qmgr_debug_free
, qentry
, qentry
);
1210 l
= qentry
->ep
.mblocks
;
1217 l
= qentry
->ep
.blocks
;
1221 g_slice_free1(EPOOL_BLOCK_SIZE
, data
);
1226 qentry_free (LParser
*parser
, QEntry
*qentry
)
1228 g_hash_table_remove (parser
->qmgr_h
, qentry
->qid
);
1230 qentry_free_noremove (parser
, qentry
);
1234 qentry_cleanup_hash (gpointer key
,
1239 LParser
*parser
= (LParser
*)user_data
;
1241 qentry_print (parser
, qe
);
1242 qentry_free_noremove (parser
, qe
);
1246 qentry_finalize (LParser
*parser
, QEntry
*qentry
)
1248 if (qentry
&& qentry
->removed
) {
1249 SEntry
*se
= qentry
->smtpd
;
1251 if (se
&& !se
->disconnect
) return;
1253 FEntry
*fe
= qentry
->filter
;
1255 if (fe
&& !fe
->finished
) return;
1257 qentry_print (parser
, qentry
);
1258 qentry_free (parser
, qentry
);
1260 if (fe
) fentry_free (parser
, fe
);
1267 fentry_new (const char *logid
)
1274 fentry
= (FEntry
*)g_slice_alloc0(EPOOL_BLOCK_SIZE
);
1277 fentry
->ep
.allocated
+= EPOOL_BLOCK_SIZE
;
1278 ep_allocated
+= EPOOL_BLOCK_SIZE
;
1279 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
1285 if ((fe
= g_hash_table_lookup (filter_debug_alloc
, fentry
))) {
1286 debug_error ("FEntry already alloced", NULL
);
1288 g_hash_table_insert (filter_debug_alloc
, fentry
, fentry
);
1293 cpos
= sizeof (FEntry
);
1295 blocks
= (SList
*)((char *)fentry
+ cpos
);
1297 cpos
+= sizeof (SList
);
1299 blocks
->data
= fentry
;
1300 blocks
->next
= NULL
;
1302 fentry
->logid
= logid_cp
= (char *)fentry
+ cpos
;
1303 while ((*logid_cp
++ = *logid
++)) cpos
++;
1304 cpos
= (cpos
+ 4) & ~3;
1306 fentry
->ep
.blocks
= blocks
;
1307 fentry
->ep
.cpos
= cpos
;;
1313 fentry_get (LParser
*parser
, const char *logid
)
1317 if ((fentry
= g_hash_table_lookup (parser
->filter_h
, logid
))) {
1320 if ((fentry
= fentry_new (logid
))) {
1321 g_hash_table_insert (parser
->filter_h
, fentry
->logid
, fentry
);
1329 fentry_tolist_add (FEntry
*fentry
, char dstatus
, const char *to
, int to_len
,
1330 const char *qid
, int qid_len
)
1332 TOList
*tl
= (TOList
*)epool_alloc (&fentry
->ep
, sizeof (TOList
));
1334 tl
->to
= epool_strndup0 (&fentry
->ep
, to
, to_len
);
1337 tl
->relay
= epool_strndup0 (&fentry
->ep
, qid
, qid_len
);
1341 tl
->dstatus
= dstatus
;
1342 tl
->next
= fentry
->tolist
;
1344 fentry
->tolist
= tl
;
1348 fentry_free_noremove (LParser
*parser
, FEntry
*fentry
)
1354 ep_allocated
-= fentry
->ep
.allocated
;
1355 printf ("MEM: %d\n", ep_allocated
);
1361 if ((fe
= g_hash_table_lookup (filter_debug_free
, fentry
))) {
1362 debug_error ("FEntry already freed", NULL
);
1364 g_hash_table_insert (filter_debug_free
, fentry
, fentry
);
1370 l
= fentry
->ep
.mblocks
;
1377 l
= fentry
->ep
.blocks
;
1381 g_slice_free1(EPOOL_BLOCK_SIZE
, data
);
1386 fentry_free (LParser
*parser
, FEntry
*fentry
)
1388 g_hash_table_remove (parser
->filter_h
, fentry
->logid
);
1390 fentry_free_noremove (parser
, fentry
);
1394 fentry_cleanup_hash (gpointer key
,
1399 LParser
*parser
= (LParser
*)user_data
;
1401 fentry_free_noremove (parser
, fe
);
1409 LParser
*parser
= g_malloc0 (sizeof (LParser
));
1414 epool_init (&parser
->ep
);
1416 if (!(parser
->smtpd_h
= g_hash_table_new (g_int_hash
, g_int_equal
))) {
1420 if (!(parser
->qmgr_h
= g_hash_table_new (g_str_hash
, g_str_equal
))) {
1424 if (!(parser
->filter_h
= g_hash_table_new (g_str_hash
, g_str_equal
))) {
1428 //if (!(parser->track_h = g_hash_table_new (g_str_hash, g_str_equal))) {
1432 for (i
= 0; i
< MAX_LOGFILES
; i
++) {
1433 gettimeofday(&tv
, NULL
);
1434 tv
.tv_sec
-= 3600*24*i
;
1435 ltime
= localtime (&tv
.tv_sec
);
1436 parser
->year
[i
] = ltime
->tm_year
+ 1900;
1443 parser_free (LParser
*parser
)
1447 for (i
= 0; i
< MAX_LOGFILES
; i
++) {
1448 if (parser
->stream
[i
]) gzclose (parser
->stream
[i
]);
1451 epool_free (&parser
->ep
);
1453 g_hash_table_destroy (parser
->smtpd_h
);
1454 g_hash_table_destroy (parser
->qmgr_h
);
1455 g_hash_table_destroy (parser
->filter_h
);
1456 //g_hash_table_destroy (parser->track_h);
1463 parser_track (LParser
*parser
, const char *qid
, gboolean insert
)
1467 if ((res
= g_hash_table_lookup (parser
->track_h
, qid
))) {
1470 if (insert
&& (res
= epool_strdup (&parser
->ep
, qid
))) {
1471 g_hash_table_insert (parser
->track_h
, res
, res
);
1479 static const int linebufsize
= 8192;
1480 static int cur_year
;
1481 static int cur_month
= 0;
1482 static int cal_mtod
[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
1485 mkgmtime (struct tm
*tm
)
1489 int year
= tm
->tm_year
+ 1900;
1490 int mon
= tm
->tm_mon
;
1492 res
= (year
- 1970) * 365 + cal_mtod
[mon
];
1494 // leap year corrections (gregorian calendar)
1495 if (mon
<= 1) year
-= 1;
1496 res
+= (year
- 1968) / 4;
1497 res
-= (year
- 1900) / 100;
1498 res
+= (year
- 1600) / 400;
1500 res
+= tm
->tm_mday
- 1;
1501 res
= res
*24 + tm
->tm_hour
;
1502 res
= res
*60 + tm
->tm_min
;
1503 res
= res
*60 + tm
->tm_sec
;
1509 parse_time (const char **text
, int len
)
1513 int year
= cur_year
;
1522 const char *line
= *text
;
1524 if (len
== (linebufsize
- 1)) {
1525 debug_error ("skipping long line data", line
);
1530 debug_error ("skipping short line data", line
);
1535 int csum
= (line
[0]<<16) + (line
[1]<<8) + line
[2];
1538 case 4874606: mon
= 0; break;
1539 case 4613474: mon
= 1; break;
1540 case 5071218: mon
= 2; break;
1541 case 4288626: mon
= 3; break;
1542 case 5071225: mon
= 4; break;
1543 case 4879726: mon
= 5; break;
1544 case 4879724: mon
= 6; break;
1545 case 4289895: mon
= 7; break;
1546 case 5465456: mon
= 8; break;
1547 case 5202804: mon
= 9; break;
1548 case 5140342: mon
= 10; break;
1549 case 4482403: mon
= 11; break;
1551 debug_error ("unable to parse month", line
);
1555 // year change heuristik
1556 if (cur_month
== 11 && mon
== 0) {
1559 if (mon
> cur_month
) cur_month
= mon
;
1561 ltime
= (year
- 1970)*365 + cal_mtod
[mon
];
1563 // leap year corrections (gregorian calendar)
1564 if (mon
<= 1) year
-= 1;
1565 ltime
+= (year
- 1968) / 4;
1566 ltime
-= (year
- 1900) / 100;
1567 ltime
+= (year
- 1600) / 400;
1569 const char *cpos
= line
+ 3;
1571 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
1573 debug_error ("missing spaces after month", line
);
1577 found
= 0; while (isdigit (*cpos
)) { mday
= mday
*10 + *cpos
- 48; cpos
++; found
++; }
1578 if (found
< 1 || found
> 2) {
1579 debug_error ("unable to parse day of month", line
);
1585 found
= 0; while (isspace (*cpos
)) { cpos
++; found
++; }
1587 debug_error ("missing spaces after day of month", line
);
1591 found
= 0; while (isdigit (*cpos
)) { hour
= hour
*10 + *cpos
- 48; cpos
++; found
++; }
1592 if (found
< 1 || found
> 2) {
1593 debug_error ("unable to parse hour", line
);
1601 debug_error ("missing collon after hour", line
);
1606 found
= 0; while (isdigit (*cpos
)) { min
= min
*10 + *cpos
- 48; cpos
++; found
++; }
1607 if (found
< 1 || found
> 2) {
1608 debug_error ("unable to parse minute", line
);
1616 debug_error ("missing collon after minute", line
);
1621 found
= 0; while (isdigit (*cpos
)) { sec
= sec
*10 + *cpos
- 48; cpos
++; found
++; }
1622 if (found
< 1 || found
> 2) {
1623 debug_error ("unable to parse second", line
);
1630 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
1632 debug_error ("missing spaces after time", line
);
1643 parser_count_files (LParser
*parser
)
1646 time_t start
= parser
->start
;
1647 char linebuf
[linebufsize
];
1651 for (i
= 0; i
< (MAX_LOGFILES
- 1); i
++) {
1652 cur_year
= parser
->year
[i
];
1655 if ((stream
= gzopen (logfiles
[i
], "r"))) {
1656 if ((line
= gzgets (stream
, linebuf
, linebufsize
))) {
1657 if (parse_time (&line
, strlen (line
)) < start
) {
1673 parse_qid (const char **text
, char *out
, char delim
, int maxlen
)
1680 while (isxdigit (*idx
)) { *copy
++ = *idx
++; found
++; if (found
> maxlen
) break; }
1682 if (found
> 1 && found
< maxlen
&&
1683 ((delim
&& (*idx
== delim
)) || (!delim
&& isspace (*idx
)))) {
1686 while (isspace (*idx
)) idx
++;
1694 print_usage (const char *name
)
1696 fprintf (stderr
, "usage: %s [OPTIONS] [OUTPUTFILENAME]\n", name
);
1697 fprintf (stderr
, "\t-f SENDER mails from SENDER\n");
1698 fprintf (stderr
, "\t-t RECIPIENT mails to RECIPIENT\n");
1699 fprintf (stderr
, "\t-h Server Server IP or Hostname\n");
1700 fprintf (stderr
, "\t-s START start time (YYYY-MM-DD HH:MM:SS)\n");
1701 fprintf (stderr
, "\t or seconds since epoch\n");
1702 fprintf (stderr
, "\t-e END end time (YYYY-MM-DD HH:MM:SS)\n");
1703 fprintf (stderr
, "\t or seconds since epoch\n");
1704 fprintf (stderr
, "\t-m MSGID message ID (exact match)\n");
1705 fprintf (stderr
, "\t-q QID queue ID (exact match)\n");
1706 fprintf (stderr
, "\t-x STRING search for strings\n");
1707 fprintf (stderr
, "\t-l LIMIT print max limit entries\n");
1708 fprintf (stderr
, "\t-v verbose output (no logs)\n");
1709 fprintf (stderr
, "\t-vv verbose output with logs\n");
1713 // gzgets is ways too slow, so we do it our own way
1716 mygzgetc (gzFile stream
)
1719 static char readbuf
[16384];
1720 static char *readend
= readbuf
+ sizeof (readbuf
);
1721 static char *readpos
= readbuf
+ sizeof (readbuf
);
1723 if (readpos
< readend
) return *readpos
++;
1725 if ((br
= gzread (stream
, readbuf
, sizeof (readbuf
))) <= 0) {
1729 readend
= readbuf
+ br
;
1736 mygzgets (gzFile stream
, char *line
, int bufsize
)
1742 while (--bufsize
> 0 && (c
= mygzgetc(stream
)) != -1) {
1747 if (c
== -1 && cpos
== line
)
1754 extern char *optarg
;
1755 extern int optind
, opterr
, optopt
;
1758 main (int argc
, char * const argv
[])
1760 char linebuf
[linebufsize
];
1762 char *uniqueid
= NULL
;
1770 unsigned long lines
= 0;
1771 unsigned long rel_line_nr
= 0;
1777 time_t ctime
, next_ctime
, start
, end
;
1783 smtpd_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1784 qmgr_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1785 filter_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1786 smtpd_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1787 qmgr_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1788 filter_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1791 if (!(parser
= parser_new ())) {
1792 fprintf (stderr
, "unable to alloc parser structure\n");
1796 while ((opt
= getopt (argc
, argv
, "f:t:s:e:h:m:q:x:l:I:vgn")) != -1) {
1798 parser
->from
= epool_strdup (&parser
->ep
, optarg
);
1799 } else if (opt
== 't') {
1800 parser
->to
= epool_strdup (&parser
->ep
, optarg
);
1801 } else if (opt
== 'v') {
1802 parser
->verbose
+= 1;
1803 } else if (opt
== 'g') {
1804 parser
->exclude_greylist
= 1;
1805 } else if (opt
== 'n') {
1806 parser
->exclude_ndrs
= 1;
1807 } else if (opt
== 'I') {
1809 } else if (opt
== 'h') {
1810 parser
->server
= epool_strdup (&parser
->ep
, optarg
);
1811 } else if (opt
== 'm') {
1812 parser
->msgid
= epool_strdup (&parser
->ep
, optarg
);
1813 } else if (opt
== 'q') {
1815 unsigned long rel_line_nr
;
1816 MatchList
*match
= (MatchList
*)epool_alloc(&parser
->ep
, sizeof(MatchList
));
1817 if (sscanf(optarg
, "T%08lXL%08lX", <ime
, &rel_line_nr
) == 2) {
1818 match
->mtype
= MatchTypeRelLineNr
;
1819 match
->ltime
= ltime
;
1820 match
->rel_line_nr
= rel_line_nr
;
1821 match
->next
= parser
->match_list
;
1822 parser
->match_list
= match
;
1824 match
->mtype
= MatchTypeQID
;
1825 match
->id
= epool_strdup(&parser
->ep
, optarg
);
1826 match
->next
= parser
->match_list
;
1827 parser
->match_list
= match
;
1829 } else if (opt
== 'x') {
1830 parser
->strmatch
= epool_strdup (&parser
->ep
, optarg
);
1831 } else if (opt
== 'l') {
1833 parser
->limit
= strtoul (optarg
, &l
, 0);
1834 if (!*optarg
|| *l
) {
1835 fprintf (stderr
, "unable to parse limit '%s'\n", optarg
);
1838 } else if (opt
== 's') {
1839 // use strptime to convert time
1842 if ((!(res
= strptime (optarg
, "%F %T", &tm
)) &&
1843 !(res
= strptime (optarg
, "%s", &tm
))) || *res
) {
1844 fprintf (stderr
, "unable to parse start time\n");
1847 parser
->start
= mkgmtime (&tm
);
1849 } else if (opt
== 'e') {
1852 if ((!(res
= strptime (optarg
, "%F %T", &tm
)) &&
1853 !(res
= strptime (optarg
, "%s", &tm
))) || *res
) {
1854 fprintf (stderr
, "unable to parse end time\n");
1857 parser
->end
= mkgmtime (&tm
);
1860 print_usage (argv
[0]);
1865 if (optind
< argc
) {
1867 if ((argc
- optind
) > 1) {
1868 print_usage (argv
[0]);
1872 char *tmpfn
= g_strdup_printf ("/tmp/.proxtrack-%08X.txt", getpid ());
1874 if ((stdout
= freopen (tmpfn
, "w", stdout
)) == NULL
) {
1875 perror ("unable to open output file");
1878 if (rename (tmpfn
, argv
[optind
]) != 0) {
1879 perror ("unable to open output file");
1885 // we use gmtime exerywhere to speedup things, this can cause problems
1886 // when daylight saving time changes
1888 gettimeofday(&tv
, NULL
);
1889 ltime
= localtime (&tv
.tv_sec
);
1891 if (!parser
->start
) {
1895 parser
->start
= mkgmtime (ltime
);
1898 ltime
= localtime (&tv
.tv_sec
);
1901 parser
->end
= mkgmtime (ltime
);
1904 if (parser
->end
< parser
->start
) {
1905 fprintf (stderr
, "end time before start time\n");
1910 if ((filecount
= parser_count_files (parser
)) <= 0) {
1911 fprintf (stderr
, "unable to access log files\n");
1916 printf ("# LogReader: %d %s\n", getpid(), uniqueid
);
1918 printf ("# LogReader: %d\n", getpid());
1921 printf ("# Query options\n");
1922 if (parser
->from
) printf ("# Sender: %s\n", parser
->from
);
1923 if (parser
->to
) printf ("# Recipient: %s\n", parser
->to
);
1924 if (parser
->server
) printf ("# Server: %s\n", parser
->server
);
1925 if (parser
->msgid
) printf ("# MsgID: %s\n", parser
->msgid
);
1927 MatchList
*match
= parser
->match_list
;
1929 if (match
->mtype
== MatchTypeQID
) {
1930 printf ("# QID: %s\n", match
->id
);
1931 } else if (match
->mtype
== MatchTypeRelLineNr
) {
1932 printf ("# QID: T%08lXL%08lX\n", match
->ltime
, match
->rel_line_nr
);
1934 g_error("internal error - unknown match type %d\n", match
->mtype
);
1936 match
= match
->next
;
1939 if (parser
->strmatch
) printf ("# Match: %s\n", parser
->strmatch
);
1941 strftime (linebuf
, 256, "%F %T", gmtime (&parser
->start
));
1942 printf ("# Start: %s (%lu)\n", linebuf
, parser
->start
);
1943 strftime (linebuf
, 256, "%F %T", gmtime (&parser
->end
));
1944 printf ("# END: %s (%lu)\n", linebuf
, parser
->end
);
1945 printf ("# End Query Options\n\n");
1949 start
= parser
->start
;
1953 for (i
= filecount
- 1; i
>= 0; i
--) {
1956 // syslog files does not conain years, so we must compute then
1957 // cur_year is the assumed start year
1960 cur_year
= parser
->year
[i
];
1963 if (!(stream
= (gpointer
) fopen (logfiles
[i
], "r"))) continue;
1965 if (!(stream
= (gpointer
) gzopen (logfiles
[i
], "r"))) continue;
1970 if (parser
->limit
&& (parser
->count
>= parser
->limit
)) {
1971 printf ("STATUS: aborted by limit (too many hits)\n");
1976 line
= fgets (linebuf
, linebufsize
, (FILE *)stream
);
1978 line
= mygzgets ((gzFile
)stream
, linebuf
, linebufsize
);
1983 int len
= strlen (line
);
1988 next_ctime
= parse_time (&cpos
, len
);
1994 if (next_ctime
!= ctime
) {
2002 if (ctime
< start
) continue;
2003 if (ctime
> end
) break;
2007 found
= 0; while (!isspace (*cpos
)) { cpos
++; found
= 1; }
2009 debug_error ("missing hostname", line
);
2013 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
2015 debug_error ("missing spaces after host", line
);
2019 if ((*cpos
== 'l') && !strncmp (cpos
, "last message repeated", 21)) {
2023 //printf ("LINE: %s\n", line);
2024 //const char *prog = cpos;
2027 found
= 0; while (*cpos
&& (*cpos
!= ':') && (*cpos
!= '[')) {
2028 csum_prog
= (csum_prog
<<8) + *cpos
;
2033 //idx1 = g_strndup (prog, found);
2034 //printf ("TEST:%s:%08X\n", idx1, csum_prog);
2039 found
= 0; while (isdigit (*cpos
)) {
2040 pid
= pid
*10 + *cpos
- 48;
2044 if (found
< 1 || found
> 15 || *cpos
!= ']') {
2045 debug_error ("unable to parse pid", line
);
2051 if (*cpos
++ != ':') {
2052 debug_error ("missing collon", line
);
2057 if (!isspace (*cpos
++)) {
2058 debug_error ("missing space after collon", line
);
2064 parser
->ctime
= ctime
;
2068 if (parser
->strmatch
&& STRMATCH(parser
->strmatch
, text
)) {
2072 if ((csum_prog
== 0x70726F78) ||// proxprox
2073 (csum_prog
== 0x6C746572)) { // pmg-smtp-filter
2075 if ((idx1
= parse_qid (&cpos
, qidbuf
, ':', 25))) {
2079 if (!(fe
= fentry_get (parser
, idx1
))) {
2083 loglist_add (&fe
->ep
, &fe
->loglist
, line
, len
, lines
);
2085 if (strmatch
) fe
->strmatch
= 1;
2087 //fixme: BCC, Notify?
2088 //fixme: parse virus info
2089 //fixme: parse spam score
2091 if ((*cpos
== 'a') && !strncmp (cpos
, "accept mail to <", 16)) {
2093 const char *to_s
, *to_e
;
2095 to_s
= cpos
= cpos
+ 16;
2097 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2099 if (*cpos
!= '>') continue;
2105 if ((*cpos
++ != ' ') || (*cpos
++ != '(')) continue;
2107 if (!(idx1
= parse_qid (&cpos
, qidbuf
, ')', 15))) continue;
2109 // parser_track (parser, idx1, 1);
2111 fentry_tolist_add (fe
, 'A', to_s
, to_e
- to_s
, idx1
, strlen (idx1
));
2113 } else if ((*cpos
== 'm') && !strncmp (cpos
, "moved mail for <", 16)) {
2114 const char *to_s
, *to_e
;
2116 to_s
= cpos
= cpos
+ 16;
2118 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2122 if (strncmp (cpos
, "> to ", 5)) continue;
2125 if (!strncmp (cpos
, "spam", 4)) {
2127 } else if (!strncmp (cpos
, "virus", 5)) {
2133 if (strncmp (cpos
, " quarantine - ", 14)) continue;
2136 if (!(idx1
= parse_qid (&cpos
, qidbuf
, 0, 25))) continue;
2138 fentry_tolist_add (fe
, 'Q', to_s
, to_e
- to_s
, idx1
, strlen (idx1
));
2140 } else if ((*cpos
== 'b') && !strncmp (cpos
, "block mail to <", 15)) {
2144 to_s
= cpos
= cpos
+ 15;
2146 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2148 if (*cpos
!= '>') continue;
2150 fentry_tolist_add (fe
, 'B', to_s
, cpos
- to_s
, NULL
, 0);
2152 } else if ((*cpos
== 'p') && !strncmp (cpos
, "processing time: ", 17)) {
2155 sscanf (cpos
, "%f", &fe
->ptime
);
2162 } else if (csum_prog
== 0x7265656E) { // postfix/postscreen
2167 debug_error ("no pid for postscreen", line
);
2172 if ((*text
== 'N') && !strncmp (text
, "NOQUEUE: reject: RCPT from ", 27)) {
2176 if (!(idx1
= strstr (cpos
, "; client ["))) continue;
2178 const char *client
= cpos
= idx1
+ 10;
2180 while (*cpos
&& (*cpos
!= ']')) { cpos
++; }
2182 const char *client_end
= cpos
;
2184 if (!(idx1
= strstr (cpos
, "; from=<"))) continue;
2186 const char *from
= cpos
= idx1
+ 8;
2188 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2191 if ((*cpos
== '>') && strncmp (cpos
, ">, to=<", 7)) continue;
2193 const char *to
= cpos
= cpos
+ 7;
2195 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2197 if (*cpos
!= '>') continue;
2199 if (!(se
= sentry_get (parser
, pid
, ctime
, rel_line_nr
))) {
2203 if (strmatch
) se
->strmatch
= 1;
2205 loglist_add (&se
->ep
, &se
->loglist
, line
, len
, lines
);
2207 sentry_nqlist_add (se
, ctime
, from
, idx1
- from
, to
, cpos
- to
, 'N');
2209 sentry_set_connect (se
, client
, client_end
- client
);
2211 g_hash_table_remove (parser
->smtpd_h
, &se
->pid
);
2215 sentry_print (parser
, se
);
2216 sentry_free (parser
, se
);
2219 } else if (csum_prog
== 0x716D6772) { // postfix/qmgr
2221 if ((idx2
= text
) && (idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2225 if (!(qe
= qentry_get (parser
, idx1
))) {
2229 if (strmatch
) qe
->strmatch
= 1;
2233 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
, lines
);
2235 if ((*idx2
== 'f') && !strncmp (idx2
, "from=<", 6)) {
2237 cpos
= idx2
= idx2
+ 6;
2238 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2241 debug_error ("unable to parse 'from' address", line
);
2245 qentry_set_from (qe
, idx2
, cpos
- idx2
);
2249 if ((*cpos
== ',') && !strncmp (cpos
, ", size=", 7)) {
2253 while (isdigit (*cpos
)) { size
= size
*10 + *cpos
++ - 48; }
2258 } else if ((*idx2
== 'r') && !strncmp (idx2
, "removed\n", 8)) {
2262 qentry_finalize (parser
, qe
);
2266 } else if ((csum_prog
== 0x736D7470) || //postfix/smtp
2267 (csum_prog
== 0x6C6D7470) || //postfix/lmtp
2268 (csum_prog
== 0x6F63616C) || //postfix/local
2269 (csum_prog
== 0x72726F72)) { //postfix/error
2271 int lmtp
= (csum_prog
== 0x6C6D7470);
2273 if ((cpos
= text
) && (idx1
= parse_qid (&cpos
, qidbuf
, ':', 15))) {
2277 if (!(qe
= qentry_get (parser
, idx1
))) {
2281 if (strmatch
) qe
->strmatch
= 1;
2285 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
, lines
);
2287 if (strncmp (cpos
, "to=<", 4)) continue;
2290 const char *to_s
, *to_e
;
2294 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2296 if (*cpos
!= '>') continue;
2302 if (!(cpos
= strstr (cpos
, ", relay="))) continue;
2305 const char *relay_s
, *relay_e
;
2309 while (*cpos
&& (*cpos
!= ',')) { cpos
++; }
2311 if (*cpos
!= ',') continue;
2315 if (!(idx1
= strstr (cpos
+ 1, ", dsn="))) continue;
2319 if (!isdigit (*cpos
)) continue;
2321 char dstatus
= *cpos
;
2323 qentry_tolist_add (qe
, ctime
, dstatus
, to_s
, to_e
- to_s
,
2324 relay_s
, relay_e
- relay_s
);
2326 if (!lmtp
) continue; // filter always uses lmtp
2328 if (!(idx1
= strstr (cpos
+ 1, "status=sent (250 2.")))
2331 cpos
= idx1
= idx1
+ 19;
2333 if (*cpos
== '5' && (idx1
= strstr (cpos
, "5.0 OK ("))) {
2334 cpos
= idx1
= idx1
+ 8;
2335 } else if (*cpos
== '7' && (idx1
= strstr (cpos
, "7.0 BLOCKED ("))) {
2336 cpos
= idx1
= idx1
+ 13;
2341 if (!(idx1
= parse_qid (&cpos
, qidbuf
, ')', 25)))
2348 if ((fe
= g_hash_table_lookup (parser
->filter_h
, idx1
))) {
2353 } else if (csum_prog
== 0x6D747064) { // postfix/smtpd
2357 debug_error ("no pid for smtpd", line
);
2361 if (!(se
= sentry_get (parser
, pid
, ctime
, rel_line_nr
))) {
2365 if (strmatch
) se
->strmatch
= 1;
2367 loglist_add (&se
->ep
, &se
->loglist
, line
, len
, lines
);
2369 if ((*text
== 'c') && !strncmp (text
, "connect from ", 13)) {
2371 cpos
= idx1
= text
+ 13;
2373 while (*idx1
&& !isspace (*idx1
)) { idx1
++; }
2375 sentry_set_connect (se
, cpos
, idx1
- cpos
);
2377 // fixme: do we need this?
2378 //if (strcmp (se->connect, "localhost[127.0.0.1]")) {
2379 // se->external = 1;
2382 } else if ((*text
== 'd') && !strncmp (text
, "disconnect from", 15)) {
2385 g_hash_table_remove (parser
->smtpd_h
, &se
->pid
);
2389 if (sentry_ref_rem_unneeded (parser
, se
) == 0) {
2390 sentry_print (parser
, se
);
2391 sentry_free (parser
, se
);
2393 sentry_ref_finalize (parser
, se
);
2396 } else if ((*text
== 'N') && !strncmp (text
, "NOQUEUE:", 8)) {
2400 // parse 'whatsup' (reject:)
2401 while (*cpos
&& (*cpos
!= ':')) { cpos
++; }
2402 if (*cpos
!= ':') continue;
2405 // parse '%s from %s:'
2406 while (*cpos
&& (*cpos
!= ':')) { cpos
++; }
2407 if (*cpos
!= ':') continue;
2411 while (*cpos
&& (*cpos
!= ';')) { cpos
++; }
2412 if (*cpos
!= ';') continue;
2416 *(char *)cpos
= 0; // dangerous hack: delimit string
2417 if (strstr (idx1
, ": Recipient address rejected: Service is unavailable (try later)")) {
2420 *(char *)cpos
= ';'; // dangerous hack: restore line
2422 if (!(idx1
= strstr (cpos
, "; from=<"))) continue;
2424 const char *from
= cpos
= idx1
+ 8;
2426 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2429 if ((*cpos
== '>') && strncmp (cpos
, "> to=<", 6)) continue;
2431 const char *to
= cpos
= cpos
+ 6;
2433 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2435 if (*cpos
!= '>') continue;
2437 sentry_nqlist_add (se
, ctime
, from
, idx1
- from
, to
, cpos
- to
, dstatus
);
2439 } else if ((idx2
= text
) && (idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2443 if ((qe
= qentry_get (parser
, idx1
))) {
2445 if (strmatch
) qe
->strmatch
= 1;
2447 sentry_ref_add (se
, qe
);
2449 if ((*idx2
== 'c') && !strncmp (idx2
, "client=", 7)) {
2450 cpos
= idx2
= idx2
+ 7;
2452 while (*cpos
&& !isspace (*cpos
)) { cpos
++; }
2454 qentry_set_client (qe
, idx2
, cpos
- idx2
);
2460 } else if (csum_prog
== 0x616E7570) { // postfix/cleanup
2465 if ((idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2466 if ((qe
= qentry_get (parser
, idx1
))) {
2468 if (strmatch
) qe
->strmatch
= 1;
2470 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
, lines
);
2472 if ((*idx2
== 'm') && !strncmp (idx2
, "message-id=", 11)) {
2474 cpos
= idx2
= idx2
+ 11;
2476 while (*cpos
&& !isspace(*cpos
)) { cpos
++; }
2478 qentry_set_msgid (qe
, idx2
, cpos
- idx2
);
2488 fclose ((FILE *)stream
);
2490 gzclose ((gzFile
)stream
);
2493 if (ctime
> end
) break;
2498 printf ("LINES: %d\n", lines
);
2499 printf ("MEM SMTPD entries: %d\n", g_hash_table_size (parser
->smtpd_h
));
2500 printf ("MEM QMGR entries: %d\n", g_hash_table_size (parser
->qmgr_h
));
2501 printf ("MEM FILTER entries: %d\n", g_hash_table_size (parser
->filter_h
));
2503 printf ("MEMDEB SMTPD entries: %d %d\n",
2504 g_hash_table_size (smtpd_debug_alloc
),
2505 g_hash_table_size (smtpd_debug_free
));
2506 printf ("MEMDEB QMGR entries: %d %d\n",
2507 g_hash_table_size (qmgr_debug_alloc
),
2508 g_hash_table_size (qmgr_debug_free
));
2509 printf ("MEMDEB FILTER entries: %d %d\n",
2510 g_hash_table_size (filter_debug_alloc
),
2511 g_hash_table_size (filter_debug_free
));
2514 g_hash_table_foreach (parser
->qmgr_h
, qentry_cleanup_hash
, parser
);
2515 g_hash_table_foreach (parser
->smtpd_h
, sentry_cleanup_hash
, parser
);
2516 g_hash_table_foreach (parser
->filter_h
, fentry_cleanup_hash
, parser
);
2519 printf ("MEMDEB SMTPD entries: %d %d\n",
2520 g_hash_table_size (smtpd_debug_alloc
),
2521 g_hash_table_size (smtpd_debug_free
));
2522 printf ("MEMDEB QMGR entries: %d %d\n",
2523 g_hash_table_size (qmgr_debug_alloc
),
2524 g_hash_table_size (qmgr_debug_free
));
2525 printf ("MEMDEB FILTER entries: %d %d\n",
2526 g_hash_table_size (filter_debug_alloc
),
2527 g_hash_table_size (filter_debug_free
));
2529 g_hash_table_foreach (smtpd_debug_alloc
, sentry_debug_alloc
, parser
);
2535 printf ("MEMMAX %d\n", ep_maxalloc
);
2538 parser_free (parser
);