]>
git.proxmox.com Git - pmg-log-tracker.git/blob - pmg-log-tracker.c
21145faf47d25415d8d0be14b6449cb1859c32a9
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
= (SList
*)((char *)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
= (char *)ep
->blocks
->data
+ ep
->cpos
;
436 SList
*blocks
= (SList
*)((char *)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
) {
783 printf ("SMTPD: T%08lXL%08lX\n", sentry
->ltime
, sentry
->rel_line_nr
);
785 printf ("CTIME: %08lX\n", parser
->ctime
);
787 if (sentry
->connect
) { printf ("CLIENT: %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
) {
1095 printf ("CLIENT: %s\n", qentry
->client
);
1096 } else if (se
&& se
->connect
) {
1097 printf ("CLIENT: %s\n", se
->connect
);
1100 if (qentry
->msgid
) { printf ("MSGID: %s\n", qentry
->msgid
); }
1104 tl
= qentry
->tolist
;
1108 if (fe
&& tl
->dstatus
== '2') {
1111 if (fl
->to
&& !strcmp (tl
->to
, fl
->to
)) {
1123 dstatus
= fl
->dstatus
;
1127 dstatus
= tl
->dstatus
;
1131 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");
1139 if (!parser
->verbose
) { fflush (stdout
); return; }
1141 if (parser
->verbose
> 1) {
1143 if (se
&& se
->loglist
.log
) {
1145 loglist_print (&se
->loglist
);
1148 if (fe
&& fe
->loglist
.log
) {
1149 printf ("FILTER: %s\n", fe
->logid
);
1150 loglist_print (&fe
->loglist
);
1153 if (qentry
->loglist
.log
) {
1155 loglist_print (&qentry
->loglist
);
1167 qentry_get (LParser
*parser
, const char *qid
)
1171 if ((qentry
= g_hash_table_lookup (parser
->qmgr_h
, qid
))) {
1174 if ((qentry
= qentry_new (qid
))) {
1175 g_hash_table_insert (parser
->qmgr_h
, qentry
->qid
, qentry
);
1183 qentry_free_noremove (LParser
*parser
, QEntry
*qentry
)
1189 if ((se
= qentry
->smtpd
)) {
1190 if (sentry_ref_del (se
, qentry
) == 0) {
1191 if (se
->disconnect
) {
1192 sentry_free_noremove (se
);
1198 ep_allocated
-= qentry
->ep
.allocated
;
1199 printf ("MEM: %d\n", ep_allocated
);
1205 if ((qe
= g_hash_table_lookup (qmgr_debug_free
, qentry
))) {
1206 debug_error ("QEntry already freed", NULL
);
1208 g_hash_table_insert (qmgr_debug_free
, qentry
, qentry
);
1214 l
= qentry
->ep
.mblocks
;
1221 l
= qentry
->ep
.blocks
;
1225 g_slice_free1(EPOOL_BLOCK_SIZE
, data
);
1230 qentry_free (LParser
*parser
, QEntry
*qentry
)
1232 g_hash_table_remove (parser
->qmgr_h
, qentry
->qid
);
1234 qentry_free_noremove (parser
, qentry
);
1238 qentry_cleanup_hash (gpointer key
,
1243 LParser
*parser
= (LParser
*)user_data
;
1245 qentry_print (parser
, qe
);
1246 qentry_free_noremove (parser
, qe
);
1250 qentry_finalize (LParser
*parser
, QEntry
*qentry
)
1252 if (qentry
&& qentry
->removed
) {
1253 SEntry
*se
= qentry
->smtpd
;
1255 if (se
&& !se
->disconnect
) return;
1257 FEntry
*fe
= qentry
->filter
;
1259 if (fe
&& !fe
->finished
) return;
1261 qentry_print (parser
, qentry
);
1262 qentry_free (parser
, qentry
);
1264 if (fe
) fentry_free (parser
, fe
);
1271 fentry_new (const char *logid
)
1278 fentry
= (FEntry
*)g_slice_alloc0(EPOOL_BLOCK_SIZE
);
1281 fentry
->ep
.allocated
+= EPOOL_BLOCK_SIZE
;
1282 ep_allocated
+= EPOOL_BLOCK_SIZE
;
1283 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
1289 if ((fe
= g_hash_table_lookup (filter_debug_alloc
, fentry
))) {
1290 debug_error ("FEntry already alloced", NULL
);
1292 g_hash_table_insert (filter_debug_alloc
, fentry
, fentry
);
1297 cpos
= sizeof (FEntry
);
1299 blocks
= (SList
*)((char *)fentry
+ cpos
);
1301 cpos
+= sizeof (SList
);
1303 blocks
->data
= fentry
;
1304 blocks
->next
= NULL
;
1306 fentry
->logid
= logid_cp
= (char *)fentry
+ cpos
;
1307 while ((*logid_cp
++ = *logid
++)) cpos
++;
1308 cpos
= (cpos
+ 4) & ~3;
1310 fentry
->ep
.blocks
= blocks
;
1311 fentry
->ep
.cpos
= cpos
;;
1317 fentry_get (LParser
*parser
, const char *logid
)
1321 if ((fentry
= g_hash_table_lookup (parser
->filter_h
, logid
))) {
1324 if ((fentry
= fentry_new (logid
))) {
1325 g_hash_table_insert (parser
->filter_h
, fentry
->logid
, fentry
);
1333 fentry_tolist_add (FEntry
*fentry
, char dstatus
, const char *to
, int to_len
,
1334 const char *qid
, int qid_len
)
1336 TOList
*tl
= (TOList
*)epool_alloc (&fentry
->ep
, sizeof (TOList
));
1338 tl
->to
= epool_strndup0 (&fentry
->ep
, to
, to_len
);
1341 tl
->relay
= epool_strndup0 (&fentry
->ep
, qid
, qid_len
);
1345 tl
->dstatus
= dstatus
;
1346 tl
->next
= fentry
->tolist
;
1348 fentry
->tolist
= tl
;
1352 fentry_free_noremove (LParser
*parser
, FEntry
*fentry
)
1358 ep_allocated
-= fentry
->ep
.allocated
;
1359 printf ("MEM: %d\n", ep_allocated
);
1365 if ((fe
= g_hash_table_lookup (filter_debug_free
, fentry
))) {
1366 debug_error ("FEntry already freed", NULL
);
1368 g_hash_table_insert (filter_debug_free
, fentry
, fentry
);
1374 l
= fentry
->ep
.mblocks
;
1381 l
= fentry
->ep
.blocks
;
1385 g_slice_free1(EPOOL_BLOCK_SIZE
, data
);
1390 fentry_free (LParser
*parser
, FEntry
*fentry
)
1392 g_hash_table_remove (parser
->filter_h
, fentry
->logid
);
1394 fentry_free_noremove (parser
, fentry
);
1398 fentry_cleanup_hash (gpointer key
,
1403 LParser
*parser
= (LParser
*)user_data
;
1405 fentry_free_noremove (parser
, fe
);
1413 LParser
*parser
= g_malloc0 (sizeof (LParser
));
1418 epool_init (&parser
->ep
);
1420 if (!(parser
->smtpd_h
= g_hash_table_new (g_int_hash
, g_int_equal
))) {
1424 if (!(parser
->qmgr_h
= g_hash_table_new (g_str_hash
, g_str_equal
))) {
1428 if (!(parser
->filter_h
= g_hash_table_new (g_str_hash
, g_str_equal
))) {
1432 //if (!(parser->track_h = g_hash_table_new (g_str_hash, g_str_equal))) {
1436 for (i
= 0; i
< MAX_LOGFILES
; i
++) {
1437 gettimeofday(&tv
, NULL
);
1438 tv
.tv_sec
-= 3600*24*i
;
1439 ltime
= localtime (&tv
.tv_sec
);
1440 parser
->year
[i
] = ltime
->tm_year
+ 1900;
1447 parser_free (LParser
*parser
)
1451 for (i
= 0; i
< MAX_LOGFILES
; i
++) {
1452 if (parser
->stream
[i
]) gzclose (parser
->stream
[i
]);
1455 epool_free (&parser
->ep
);
1457 g_hash_table_destroy (parser
->smtpd_h
);
1458 g_hash_table_destroy (parser
->qmgr_h
);
1459 g_hash_table_destroy (parser
->filter_h
);
1460 //g_hash_table_destroy (parser->track_h);
1467 parser_track (LParser
*parser
, const char *qid
, gboolean insert
)
1471 if ((res
= g_hash_table_lookup (parser
->track_h
, qid
))) {
1474 if (insert
&& (res
= epool_strdup (&parser
->ep
, qid
))) {
1475 g_hash_table_insert (parser
->track_h
, res
, res
);
1483 static const int linebufsize
= 8192;
1484 static int cur_year
;
1485 static int cur_month
= 0;
1486 static int cal_mtod
[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
1489 mkgmtime (struct tm
*tm
)
1493 int year
= tm
->tm_year
+ 1900;
1494 int mon
= tm
->tm_mon
;
1496 res
= (year
- 1970) * 365 + cal_mtod
[mon
];
1498 // leap year corrections (gregorian calendar)
1499 if (mon
<= 1) year
-= 1;
1500 res
+= (year
- 1968) / 4;
1501 res
-= (year
- 1900) / 100;
1502 res
+= (year
- 1600) / 400;
1504 res
+= tm
->tm_mday
- 1;
1505 res
= res
*24 + tm
->tm_hour
;
1506 res
= res
*60 + tm
->tm_min
;
1507 res
= res
*60 + tm
->tm_sec
;
1513 parse_time (const char **text
, int len
)
1517 int year
= cur_year
;
1526 const char *line
= *text
;
1528 if (len
== (linebufsize
- 1)) {
1529 debug_error ("skipping long line data", line
);
1534 debug_error ("skipping short line data", line
);
1539 int csum
= (line
[0]<<16) + (line
[1]<<8) + line
[2];
1542 case 4874606: mon
= 0; break;
1543 case 4613474: mon
= 1; break;
1544 case 5071218: mon
= 2; break;
1545 case 4288626: mon
= 3; break;
1546 case 5071225: mon
= 4; break;
1547 case 4879726: mon
= 5; break;
1548 case 4879724: mon
= 6; break;
1549 case 4289895: mon
= 7; break;
1550 case 5465456: mon
= 8; break;
1551 case 5202804: mon
= 9; break;
1552 case 5140342: mon
= 10; break;
1553 case 4482403: mon
= 11; break;
1555 debug_error ("unable to parse month", line
);
1559 // year change heuristik
1560 if (cur_month
== 11 && mon
== 0) {
1563 if (mon
> cur_month
) cur_month
= mon
;
1565 ltime
= (year
- 1970)*365 + cal_mtod
[mon
];
1567 // leap year corrections (gregorian calendar)
1568 if (mon
<= 1) year
-= 1;
1569 ltime
+= (year
- 1968) / 4;
1570 ltime
-= (year
- 1900) / 100;
1571 ltime
+= (year
- 1600) / 400;
1573 const char *cpos
= line
+ 3;
1575 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
1577 debug_error ("missing spaces after month", line
);
1581 found
= 0; while (isdigit (*cpos
)) { mday
= mday
*10 + *cpos
- 48; cpos
++; found
++; }
1582 if (found
< 1 || found
> 2) {
1583 debug_error ("unable to parse day of month", line
);
1589 found
= 0; while (isspace (*cpos
)) { cpos
++; found
++; }
1591 debug_error ("missing spaces after day of month", line
);
1595 found
= 0; while (isdigit (*cpos
)) { hour
= hour
*10 + *cpos
- 48; cpos
++; found
++; }
1596 if (found
< 1 || found
> 2) {
1597 debug_error ("unable to parse hour", line
);
1605 debug_error ("missing collon after hour", line
);
1610 found
= 0; while (isdigit (*cpos
)) { min
= min
*10 + *cpos
- 48; cpos
++; found
++; }
1611 if (found
< 1 || found
> 2) {
1612 debug_error ("unable to parse minute", line
);
1620 debug_error ("missing collon after minute", line
);
1625 found
= 0; while (isdigit (*cpos
)) { sec
= sec
*10 + *cpos
- 48; cpos
++; found
++; }
1626 if (found
< 1 || found
> 2) {
1627 debug_error ("unable to parse second", line
);
1634 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
1636 debug_error ("missing spaces after time", line
);
1647 parser_count_files (LParser
*parser
)
1650 time_t start
= parser
->start
;
1651 char linebuf
[linebufsize
];
1655 for (i
= 0; i
< (MAX_LOGFILES
- 1); i
++) {
1656 cur_year
= parser
->year
[i
];
1659 if ((stream
= gzopen (logfiles
[i
], "r"))) {
1660 if ((line
= gzgets (stream
, linebuf
, linebufsize
))) {
1661 if (parse_time (&line
, strlen (line
)) < start
) {
1677 parse_qid (const char **text
, char *out
, char delim
, int maxlen
)
1684 while (isxdigit (*idx
)) { *copy
++ = *idx
++; found
++; if (found
> maxlen
) break; }
1686 if (found
> 1 && found
< maxlen
&&
1687 ((delim
&& (*idx
== delim
)) || (!delim
&& isspace (*idx
)))) {
1690 while (isspace (*idx
)) idx
++;
1698 print_usage (const char *name
)
1700 fprintf (stderr
, "usage: %s [OPTIONS] [OUTPUTFILENAME]\n", name
);
1701 fprintf (stderr
, "\t-f SENDER mails from SENDER\n");
1702 fprintf (stderr
, "\t-t RECIPIENT mails to RECIPIENT\n");
1703 fprintf (stderr
, "\t-h Server Server IP or Hostname\n");
1704 fprintf (stderr
, "\t-s START start time (YYYY-MM-DD HH:MM:SS)\n");
1705 fprintf (stderr
, "\t or seconds since epoch\n");
1706 fprintf (stderr
, "\t-e END end time (YYYY-MM-DD HH:MM:SS)\n");
1707 fprintf (stderr
, "\t or seconds since epoch\n");
1708 fprintf (stderr
, "\t-m MSGID message ID (exact match)\n");
1709 fprintf (stderr
, "\t-q QID queue ID (exact match), can be\n");
1710 fprintf (stderr
, "\t specified multiple times.\n");
1711 fprintf (stderr
, "\t-x STRING search for strings\n");
1712 fprintf (stderr
, "\t-l LIMIT print max limit entries\n");
1713 fprintf (stderr
, "\t-g exclude greylist entries\n");
1714 fprintf (stderr
, "\t-n exclude NDR entries\n");
1715 fprintf (stderr
, "\t-v verbose output (no logs)\n");
1716 fprintf (stderr
, "\t-vv verbose output with logs\n");
1720 // gzgets is ways too slow, so we do it our own way
1723 mygzgetc (gzFile stream
)
1726 static char readbuf
[16384];
1727 static char *readend
= readbuf
+ sizeof (readbuf
);
1728 static char *readpos
= readbuf
+ sizeof (readbuf
);
1730 if (readpos
< readend
) return *readpos
++;
1732 if ((br
= gzread (stream
, readbuf
, sizeof (readbuf
))) <= 0) {
1736 readend
= readbuf
+ br
;
1743 mygzgets (gzFile stream
, char *line
, int bufsize
)
1749 while (--bufsize
> 0 && (c
= mygzgetc(stream
)) != -1) {
1754 if (c
== -1 && cpos
== line
)
1761 extern char *optarg
;
1762 extern int optind
, opterr
, optopt
;
1765 main (int argc
, char * const argv
[])
1767 char linebuf
[linebufsize
];
1769 char *inputfile
= NULL
;
1777 unsigned long lines
= 0;
1778 unsigned long rel_line_nr
= 0;
1784 time_t ctime
, next_ctime
, start
, end
;
1790 smtpd_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1791 qmgr_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1792 filter_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1793 smtpd_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1794 qmgr_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1795 filter_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1798 if (!(parser
= parser_new ())) {
1799 fprintf (stderr
, "unable to alloc parser structure\n");
1803 while ((opt
= getopt (argc
, argv
, "f:t:s:e:h:m:q:x:i:l:vgn")) != -1) {
1805 parser
->from
= epool_strdup (&parser
->ep
, optarg
);
1806 } else if (opt
== 't') {
1807 parser
->to
= epool_strdup (&parser
->ep
, optarg
);
1808 } else if (opt
== 'v') {
1809 parser
->verbose
+= 1;
1810 } else if (opt
== 'g') {
1811 parser
->exclude_greylist
= 1;
1812 } else if (opt
== 'n') {
1813 parser
->exclude_ndrs
= 1;
1814 } else if (opt
== 'h') {
1815 parser
->server
= epool_strdup (&parser
->ep
, optarg
);
1816 } else if (opt
== 'm') {
1817 parser
->msgid
= epool_strdup (&parser
->ep
, optarg
);
1818 } else if (opt
== 'q') {
1820 unsigned long rel_line_nr
;
1821 MatchList
*match
= (MatchList
*)epool_alloc(&parser
->ep
, sizeof(MatchList
));
1822 if (sscanf(optarg
, "T%08lXL%08lX", <ime
, &rel_line_nr
) == 2) {
1823 match
->mtype
= MatchTypeRelLineNr
;
1824 match
->ltime
= ltime
;
1825 match
->rel_line_nr
= rel_line_nr
;
1826 match
->next
= parser
->match_list
;
1827 parser
->match_list
= match
;
1829 match
->mtype
= MatchTypeQID
;
1830 match
->id
= epool_strdup(&parser
->ep
, optarg
);
1831 match
->next
= parser
->match_list
;
1832 parser
->match_list
= match
;
1834 } else if (opt
== 'x') {
1835 parser
->strmatch
= epool_strdup (&parser
->ep
, optarg
);
1836 } else if (opt
== 'i') {
1838 } else if (opt
== 'l') {
1840 parser
->limit
= strtoul (optarg
, &l
, 0);
1841 if (!*optarg
|| *l
) {
1842 fprintf (stderr
, "unable to parse limit '%s'\n", optarg
);
1845 } else if (opt
== 's') {
1846 // use strptime to convert time
1849 if ((!(res
= strptime (optarg
, "%F %T", &tm
)) &&
1850 !(res
= strptime (optarg
, "%s", &tm
))) || *res
) {
1851 fprintf (stderr
, "unable to parse start time\n");
1854 parser
->start
= mkgmtime (&tm
);
1856 } else if (opt
== 'e') {
1859 if ((!(res
= strptime (optarg
, "%F %T", &tm
)) &&
1860 !(res
= strptime (optarg
, "%s", &tm
))) || *res
) {
1861 fprintf (stderr
, "unable to parse end time\n");
1864 parser
->end
= mkgmtime (&tm
);
1867 print_usage (argv
[0]);
1872 if (optind
< argc
) {
1874 if ((argc
- optind
) > 1) {
1875 print_usage (argv
[0]);
1879 char *tmpfn
= g_strdup_printf ("/tmp/.proxtrack-%08X.txt", getpid ());
1881 if ((stdout
= freopen (tmpfn
, "w", stdout
)) == NULL
) {
1882 perror ("unable to open output file");
1885 if (rename (tmpfn
, argv
[optind
]) != 0) {
1886 perror ("unable to open output file");
1892 // we use gmtime exerywhere to speedup things, this can cause problems
1893 // when daylight saving time changes
1895 gettimeofday(&tv
, NULL
);
1896 ltime
= localtime (&tv
.tv_sec
);
1898 if (!parser
->start
) {
1902 parser
->start
= mkgmtime (ltime
);
1905 ltime
= localtime (&tv
.tv_sec
);
1908 parser
->end
= mkgmtime (ltime
);
1911 if (parser
->end
< parser
->start
) {
1912 fprintf (stderr
, "end time before start time\n");
1919 } else if ((filecount
= parser_count_files (parser
)) <= 0) {
1920 fprintf (stderr
, "unable to access log files\n");
1924 printf ("# LogReader: %d\n", getpid());
1926 printf ("# Query options\n");
1927 if (parser
->from
) printf ("# Sender: %s\n", parser
->from
);
1928 if (parser
->to
) printf ("# Recipient: %s\n", parser
->to
);
1929 if (parser
->server
) printf ("# Server: %s\n", parser
->server
);
1930 if (parser
->msgid
) printf ("# MsgID: %s\n", parser
->msgid
);
1932 MatchList
*match
= parser
->match_list
;
1934 if (match
->mtype
== MatchTypeQID
) {
1935 printf ("# QID: %s\n", match
->id
);
1936 } else if (match
->mtype
== MatchTypeRelLineNr
) {
1937 printf ("# QID: T%08lXL%08lX\n", match
->ltime
, match
->rel_line_nr
);
1939 g_error("internal error - unknown match type %d\n", match
->mtype
);
1941 match
= match
->next
;
1944 if (parser
->strmatch
) printf ("# Match: %s\n", parser
->strmatch
);
1946 strftime (linebuf
, 256, "%F %T", gmtime (&parser
->start
));
1947 printf ("# Start: %s (%lu)\n", linebuf
, parser
->start
);
1948 strftime (linebuf
, 256, "%F %T", gmtime (&parser
->end
));
1949 printf ("# END: %s (%lu)\n", linebuf
, parser
->end
);
1950 printf ("# End Query Options\n\n");
1954 start
= parser
->start
;
1958 for (i
= filecount
- 1; i
>= 0; i
--) {
1961 // syslog files does not conain years, so we must compute then
1962 // cur_year is the assumed start year
1965 cur_year
= parser
->year
[i
];
1968 if (inputfile
&& strlen(inputfile
) == 1 && *inputfile
== '-') {
1969 stream
= (gpointer
) stdin
;
1970 } else if (inputfile
) {
1971 if (!(stream
= (gpointer
) fopen (inputfile
, "r"))) {
1972 fprintf(stderr
, "unable to open log file\n");
1975 } else if (!(stream
= (gpointer
) fopen (logfiles
[i
], "r"))) continue;
1977 if (!(stream
= (gpointer
) gzopen (logfiles
[i
], "r"))) continue;
1982 if (parser
->limit
&& (parser
->count
>= parser
->limit
)) {
1983 printf ("STATUS: aborted by limit (too many hits)\n");
1988 line
= fgets (linebuf
, linebufsize
, (FILE *)stream
);
1990 line
= mygzgets ((gzFile
)stream
, linebuf
, linebufsize
);
1995 int len
= strlen (line
);
2000 next_ctime
= parse_time (&cpos
, len
);
2006 if (next_ctime
!= ctime
) {
2014 if (ctime
< start
) continue;
2015 if (ctime
> end
) break;
2019 found
= 0; while (!isspace (*cpos
)) { cpos
++; found
= 1; }
2021 debug_error ("missing hostname", line
);
2025 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
2027 debug_error ("missing spaces after host", line
);
2031 if ((*cpos
== 'l') && !strncmp (cpos
, "last message repeated", 21)) {
2035 //printf ("LINE: %s\n", line);
2036 //const char *prog = cpos;
2039 found
= 0; while (*cpos
&& (*cpos
!= ':') && (*cpos
!= '[')) {
2040 csum_prog
= (csum_prog
<<8) + *cpos
;
2045 //idx1 = g_strndup (prog, found);
2046 //printf ("TEST:%s:%08X\n", idx1, csum_prog);
2051 found
= 0; while (isdigit (*cpos
)) {
2052 pid
= pid
*10 + *cpos
- 48;
2056 if (found
< 1 || found
> 15 || *cpos
!= ']') {
2057 debug_error ("unable to parse pid", line
);
2063 if (*cpos
++ != ':') {
2064 debug_error ("missing collon", line
);
2069 if (!isspace (*cpos
++)) {
2070 debug_error ("missing space after collon", line
);
2076 parser
->ctime
= ctime
;
2080 if (parser
->strmatch
&& STRMATCH(parser
->strmatch
, text
)) {
2084 if ((csum_prog
== 0x70726F78) ||// proxprox
2085 (csum_prog
== 0x6C746572)) { // pmg-smtp-filter
2087 if ((idx1
= parse_qid (&cpos
, qidbuf
, ':', 25))) {
2091 if (!(fe
= fentry_get (parser
, idx1
))) {
2095 loglist_add (&fe
->ep
, &fe
->loglist
, line
, len
, lines
);
2097 if (strmatch
) fe
->strmatch
= 1;
2099 //fixme: BCC, Notify?
2100 //fixme: parse virus info
2101 //fixme: parse spam score
2103 if ((*cpos
== 'a') && !strncmp (cpos
, "accept mail to <", 16)) {
2105 const char *to_s
, *to_e
;
2107 to_s
= cpos
= cpos
+ 16;
2109 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2111 if (*cpos
!= '>') continue;
2117 if ((*cpos
++ != ' ') || (*cpos
++ != '(')) continue;
2119 if (!(idx1
= parse_qid (&cpos
, qidbuf
, ')', 15))) continue;
2121 // parser_track (parser, idx1, 1);
2123 fentry_tolist_add (fe
, 'A', to_s
, to_e
- to_s
, idx1
, strlen (idx1
));
2125 } else if ((*cpos
== 'm') && !strncmp (cpos
, "moved mail for <", 16)) {
2126 const char *to_s
, *to_e
;
2128 to_s
= cpos
= cpos
+ 16;
2130 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2134 if (strncmp (cpos
, "> to ", 5)) continue;
2137 if (!strncmp (cpos
, "spam", 4)) {
2139 } else if (!strncmp (cpos
, "virus", 5)) {
2145 if (strncmp (cpos
, " quarantine - ", 14)) continue;
2148 if (!(idx1
= parse_qid (&cpos
, qidbuf
, 0, 25))) continue;
2150 fentry_tolist_add (fe
, 'Q', to_s
, to_e
- to_s
, idx1
, strlen (idx1
));
2152 } else if ((*cpos
== 'b') && !strncmp (cpos
, "block mail to <", 15)) {
2156 to_s
= cpos
= cpos
+ 15;
2158 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2160 if (*cpos
!= '>') continue;
2162 fentry_tolist_add (fe
, 'B', to_s
, cpos
- to_s
, NULL
, 0);
2164 } else if ((*cpos
== 'p') && !strncmp (cpos
, "processing time: ", 17)) {
2167 sscanf (cpos
, "%f", &fe
->ptime
);
2174 } else if (csum_prog
== 0x7265656E) { // postfix/postscreen
2179 debug_error ("no pid for postscreen", line
);
2184 if ((*text
== 'N') && !strncmp (text
, "NOQUEUE: reject: RCPT from ", 27)) {
2188 if (!(idx1
= strstr (cpos
, "; client ["))) continue;
2190 const char *client
= cpos
= idx1
+ 10;
2192 while (*cpos
&& (*cpos
!= ']')) { cpos
++; }
2194 const char *client_end
= cpos
;
2196 if (!(idx1
= strstr (cpos
, "; from=<"))) continue;
2198 const char *from
= cpos
= idx1
+ 8;
2200 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2203 if ((*cpos
== '>') && strncmp (cpos
, ">, to=<", 7)) continue;
2205 const char *to
= cpos
= cpos
+ 7;
2207 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2209 if (*cpos
!= '>') continue;
2211 if (!(se
= sentry_get (parser
, pid
, ctime
, rel_line_nr
))) {
2215 if (strmatch
) se
->strmatch
= 1;
2217 loglist_add (&se
->ep
, &se
->loglist
, line
, len
, lines
);
2219 sentry_nqlist_add (se
, ctime
, from
, idx1
- from
, to
, cpos
- to
, 'N');
2221 sentry_set_connect (se
, client
, client_end
- client
);
2223 g_hash_table_remove (parser
->smtpd_h
, &se
->pid
);
2227 sentry_print (parser
, se
);
2228 sentry_free (parser
, se
);
2231 } else if (csum_prog
== 0x716D6772) { // postfix/qmgr
2233 if ((idx2
= text
) && (idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2237 if (!(qe
= qentry_get (parser
, idx1
))) {
2241 if (strmatch
) qe
->strmatch
= 1;
2245 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
, lines
);
2247 if ((*idx2
== 'f') && !strncmp (idx2
, "from=<", 6)) {
2249 cpos
= idx2
= idx2
+ 6;
2250 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2253 debug_error ("unable to parse 'from' address", line
);
2257 qentry_set_from (qe
, idx2
, cpos
- idx2
);
2261 if ((*cpos
== ',') && !strncmp (cpos
, ", size=", 7)) {
2265 while (isdigit (*cpos
)) { size
= size
*10 + *cpos
++ - 48; }
2270 } else if ((*idx2
== 'r') && !strncmp (idx2
, "removed\n", 8)) {
2274 qentry_finalize (parser
, qe
);
2278 } else if ((csum_prog
== 0x736D7470) || //postfix/smtp
2279 (csum_prog
== 0x6C6D7470) || //postfix/lmtp
2280 (csum_prog
== 0x6F63616C) || //postfix/local
2281 (csum_prog
== 0x72726F72)) { //postfix/error
2283 int lmtp
= (csum_prog
== 0x6C6D7470);
2285 if ((cpos
= text
) && (idx1
= parse_qid (&cpos
, qidbuf
, ':', 15))) {
2289 if (!(qe
= qentry_get (parser
, idx1
))) {
2293 if (strmatch
) qe
->strmatch
= 1;
2297 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
, lines
);
2299 if (strncmp (cpos
, "to=<", 4)) continue;
2302 const char *to_s
, *to_e
;
2306 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2308 if (*cpos
!= '>') continue;
2314 if (!(cpos
= strstr (cpos
, ", relay="))) continue;
2317 const char *relay_s
, *relay_e
;
2321 while (*cpos
&& (*cpos
!= ',')) { cpos
++; }
2323 if (*cpos
!= ',') continue;
2327 if (!(idx1
= strstr (cpos
+ 1, ", dsn="))) continue;
2331 if (!isdigit (*cpos
)) continue;
2333 char dstatus
= *cpos
;
2335 qentry_tolist_add (qe
, ctime
, dstatus
, to_s
, to_e
- to_s
,
2336 relay_s
, relay_e
- relay_s
);
2338 if (!lmtp
) continue; // filter always uses lmtp
2340 if (!(idx1
= strstr (cpos
+ 1, "status=sent (250 2.")))
2343 cpos
= idx1
= idx1
+ 19;
2345 if (*cpos
== '5' && (idx1
= strstr (cpos
, "5.0 OK ("))) {
2346 cpos
= idx1
= idx1
+ 8;
2347 } else if (*cpos
== '7' && (idx1
= strstr (cpos
, "7.0 BLOCKED ("))) {
2348 cpos
= idx1
= idx1
+ 13;
2353 if (!(idx1
= parse_qid (&cpos
, qidbuf
, ')', 25)))
2360 if ((fe
= g_hash_table_lookup (parser
->filter_h
, idx1
))) {
2365 } else if (csum_prog
== 0x6D747064) { // postfix/smtpd
2369 debug_error ("no pid for smtpd", line
);
2373 if (!(se
= sentry_get (parser
, pid
, ctime
, rel_line_nr
))) {
2377 if (strmatch
) se
->strmatch
= 1;
2379 loglist_add (&se
->ep
, &se
->loglist
, line
, len
, lines
);
2381 if ((*text
== 'c') && !strncmp (text
, "connect from ", 13)) {
2383 cpos
= idx1
= text
+ 13;
2385 while (*idx1
&& !isspace (*idx1
)) { idx1
++; }
2387 sentry_set_connect (se
, cpos
, idx1
- cpos
);
2389 // fixme: do we need this?
2390 //if (strcmp (se->connect, "localhost[127.0.0.1]")) {
2391 // se->external = 1;
2394 } else if ((*text
== 'd') && !strncmp (text
, "disconnect from", 15)) {
2397 g_hash_table_remove (parser
->smtpd_h
, &se
->pid
);
2401 if (sentry_ref_rem_unneeded (parser
, se
) == 0) {
2402 sentry_print (parser
, se
);
2403 sentry_free (parser
, se
);
2405 sentry_ref_finalize (parser
, se
);
2408 } else if ((*text
== 'N') && !strncmp (text
, "NOQUEUE:", 8)) {
2412 // parse 'whatsup' (reject:)
2413 while (*cpos
&& (*cpos
!= ':')) { cpos
++; }
2414 if (*cpos
!= ':') continue;
2417 // parse '%s from %s:'
2418 while (*cpos
&& (*cpos
!= ':')) { cpos
++; }
2419 if (*cpos
!= ':') continue;
2423 while (*cpos
&& (*cpos
!= ';')) { cpos
++; }
2424 if (*cpos
!= ';') continue;
2428 *(char *)cpos
= 0; // dangerous hack: delimit string
2429 if (strstr (idx1
, ": Recipient address rejected: Service is unavailable (try later)")) {
2432 *(char *)cpos
= ';'; // dangerous hack: restore line
2434 if (!(idx1
= strstr (cpos
, "; from=<"))) continue;
2436 const char *from
= cpos
= idx1
+ 8;
2438 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2441 if ((*cpos
== '>') && strncmp (cpos
, "> to=<", 6)) continue;
2443 const char *to
= cpos
= cpos
+ 6;
2445 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2447 if (*cpos
!= '>') continue;
2449 sentry_nqlist_add (se
, ctime
, from
, idx1
- from
, to
, cpos
- to
, dstatus
);
2451 } else if ((idx2
= text
) && (idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2455 if ((qe
= qentry_get (parser
, idx1
))) {
2457 if (strmatch
) qe
->strmatch
= 1;
2459 sentry_ref_add (se
, qe
);
2461 if ((*idx2
== 'c') && !strncmp (idx2
, "client=", 7)) {
2462 cpos
= idx2
= idx2
+ 7;
2464 while (*cpos
&& !isspace (*cpos
)) { cpos
++; }
2466 qentry_set_client (qe
, idx2
, cpos
- idx2
);
2472 } else if (csum_prog
== 0x616E7570) { // postfix/cleanup
2477 if ((idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2478 if ((qe
= qentry_get (parser
, idx1
))) {
2480 if (strmatch
) qe
->strmatch
= 1;
2482 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
, lines
);
2484 if ((*idx2
== 'm') && !strncmp (idx2
, "message-id=", 11)) {
2486 cpos
= idx2
= idx2
+ 11;
2488 while (*cpos
&& !isspace(*cpos
)) { cpos
++; }
2490 qentry_set_msgid (qe
, idx2
, cpos
- idx2
);
2500 fclose ((FILE *)stream
);
2502 gzclose ((gzFile
)stream
);
2505 if (ctime
> end
) break;
2510 printf ("LINES: %d\n", lines
);
2511 printf ("MEM SMTPD entries: %d\n", g_hash_table_size (parser
->smtpd_h
));
2512 printf ("MEM QMGR entries: %d\n", g_hash_table_size (parser
->qmgr_h
));
2513 printf ("MEM FILTER entries: %d\n", g_hash_table_size (parser
->filter_h
));
2515 printf ("MEMDEB SMTPD entries: %d %d\n",
2516 g_hash_table_size (smtpd_debug_alloc
),
2517 g_hash_table_size (smtpd_debug_free
));
2518 printf ("MEMDEB QMGR entries: %d %d\n",
2519 g_hash_table_size (qmgr_debug_alloc
),
2520 g_hash_table_size (qmgr_debug_free
));
2521 printf ("MEMDEB FILTER entries: %d %d\n",
2522 g_hash_table_size (filter_debug_alloc
),
2523 g_hash_table_size (filter_debug_free
));
2526 g_hash_table_foreach (parser
->qmgr_h
, qentry_cleanup_hash
, parser
);
2527 g_hash_table_foreach (parser
->smtpd_h
, sentry_cleanup_hash
, parser
);
2528 g_hash_table_foreach (parser
->filter_h
, fentry_cleanup_hash
, parser
);
2531 printf ("MEMDEB SMTPD entries: %d %d\n",
2532 g_hash_table_size (smtpd_debug_alloc
),
2533 g_hash_table_size (smtpd_debug_free
));
2534 printf ("MEMDEB QMGR entries: %d %d\n",
2535 g_hash_table_size (qmgr_debug_alloc
),
2536 g_hash_table_size (qmgr_debug_free
));
2537 printf ("MEMDEB FILTER entries: %d %d\n",
2538 g_hash_table_size (filter_debug_alloc
),
2539 g_hash_table_size (filter_debug_free
));
2541 g_hash_table_foreach (smtpd_debug_alloc
, sentry_debug_alloc
, parser
);
2547 printf ("MEMMAX %d\n", ep_maxalloc
);
2550 parser_free (parser
);