]>
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
;
148 unsigned long linenr
;
154 LogEntry
*logs_last
; // pointer to last log (speedup add log)
157 // SEntry: Store SMTPD related logs
172 //unsigned int external:1; // not from local host
173 unsigned int disconnect
:1;
174 unsigned int strmatch
:1;
178 // QEntry: Store Queue (qmgr, smtp, lmtp) related logs
197 unsigned int cleanup
:1;
198 unsigned int removed
:1;
199 unsigned int filtered
:1; // set when passed via lmtp to filter
200 unsigned int strmatch
:1;
203 // FEntry: Store filter (proxprox) related logs
210 char *logid
; // proxprox log id
216 unsigned int finished
:1;
217 unsigned int strmatch
:1;
221 void debug_error (char *msg
, const char *line
);
223 EPool
*epool_init (EPool
*ep
);
224 void epool_free (EPool
*ep
);
225 gpointer
epool_alloc (EPool
*ep
, int size
);
226 char *epool_strndup (EPool
*ep
, const char *s
, int len
);
227 char *epool_strndup0 (EPool
*ep
, const char *s
, int len
);
228 char *epool_strdup (EPool
*ep
, const char *s
);
230 void loglist_print (LogList
*loglist
);
231 void loglist_add (EPool
*ep
, LogList
*loglist
, const char *text
, int len
, unsigned long linenr
);
233 SEntry
*sentry_new (int pid
);
234 SEntry
*sentry_get (LParser
*parser
, int pid
);
235 void sentry_ref_add (SEntry
*sentry
, QEntry
*qentry
);
236 int sentry_ref_del (SEntry
*sentry
, QEntry
*qentry
);
237 void sentry_ref_finalize (LParser
*parser
, SEntry
*sentry
);
238 int sentry_ref_rem_unneeded (LParser
*parser
, SEntry
*sentry
);
239 void sentry_nqlist_add (SEntry
*sentry
, time_t ltime
, const char *from
, int from_len
,
240 const char *to
, int to_len
, char dstatus
);
241 void sentry_print (LParser
*parser
, SEntry
*sentry
);
242 void sentry_set_connect (SEntry
*sentry
, const char *connect
, int len
);
243 void sentry_free_noremove (SEntry
*sentry
);
244 void sentry_free (LParser
*parser
, SEntry
*sentry
);
245 void sentry_cleanup_hash (gpointer key
, gpointer value
, gpointer user_data
);
248 QEntry
*qentry_new (const char *qid
);
249 QEntry
*qentry_get (LParser
*parser
, const char *qid
);
250 void qentry_tolist_add (QEntry
*qentry
, time_t ltime
, char dstatus
, const char *to
,
251 int to_len
, const char *relay
, int relay_len
);
252 void qentry_set_from (QEntry
*qentry
, const char *from
, int len
);
253 void qentry_set_msgid (QEntry
*qentry
, const char *msgid
, int len
);
254 void qentry_set_client (QEntry
*qentry
, const char *client
, int len
);
255 void qentry_print (LParser
*parser
, QEntry
*qentry
);
256 void qentry_finalize (LParser
*parser
, QEntry
*qentry
);
257 void qentry_free_noremove (LParser
*parser
, QEntry
*qentry
);
258 void qentry_free (LParser
*parser
, QEntry
*qentry
);
259 void qentry_cleanup_hash (gpointer key
, gpointer value
, gpointer user_data
);
262 FEntry
*fentry_new (const char *logid
);
263 FEntry
*fentry_get (LParser
*parser
, const char *logid
);
264 void fentry_tolist_add (FEntry
*fentry
, char dstatus
, const char *to
,
265 int to_len
, const char *qid
, int qid_len
);
266 void fentry_free_noremove (LParser
*parser
, FEntry
*fentry
);
267 void fentry_free (LParser
*parser
, FEntry
*fentry
);
268 void fentry_cleanup_hash (gpointer key
, gpointer value
, gpointer user_data
);
270 LParser
*parser_new ();
271 void parser_free (LParser
*parser
);
273 //char *parser_track (LParser *parser, const char *qid, gboolean insert);
277 //#define LOGPATH "./log/"
278 #define LOGPATH "/var/log/"
279 //#define LOGPATH "/root/testlog/"
281 static const char *logfiles
[] = {
284 LOGPATH
"syslog.2.gz",
285 LOGPATH
"syslog.3.gz",
286 LOGPATH
"syslog.4.gz",
287 LOGPATH
"syslog.5.gz",
288 LOGPATH
"syslog.6.gz",
289 LOGPATH
"syslog.7.gz",
290 LOGPATH
"syslog.8.gz",
291 LOGPATH
"syslog.9.gz",
292 LOGPATH
"syslog.10.gz",
293 LOGPATH
"syslog.11.gz",
294 LOGPATH
"syslog.12.gz",
295 LOGPATH
"syslog.13.gz",
296 LOGPATH
"syslog.14.gz",
297 LOGPATH
"syslog.15.gz",
298 LOGPATH
"syslog.16.gz",
299 LOGPATH
"syslog.17.gz",
300 LOGPATH
"syslog.18.gz",
301 LOGPATH
"syslog.19.gz",
302 LOGPATH
"syslog.20.gz",
303 LOGPATH
"syslog.21.gz",
304 LOGPATH
"syslog.22.gz",
305 LOGPATH
"syslog.23.gz",
306 LOGPATH
"syslog.24.gz",
307 LOGPATH
"syslog.25.gz",
308 LOGPATH
"syslog.26.gz",
309 LOGPATH
"syslog.27.gz",
310 LOGPATH
"syslog.28.gz",
311 LOGPATH
"syslog.29.gz",
312 LOGPATH
"syslog.30.gz",
313 LOGPATH
"syslog.31.gz",
317 debug_error (char *msg
, const char *line
)
320 fprintf (stderr
, "ERROR: %s\n", msg
);
321 if (line
) fprintf (stderr
, "LINE: %s\n", line
);
330 epool_init (EPool
*ep
)
335 data
= g_slice_alloc0(EPOOL_BLOCK_SIZE
);
336 blocks
= (SList
*)data
;
339 ep
->allocated
+= EPOOL_BLOCK_SIZE
;
340 ep_allocated
+= EPOOL_BLOCK_SIZE
;
341 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
349 ep
->cpos
= sizeof (SList
);
355 epool_free (EPool
*ep
)
361 ep_allocated
-= ep
->allocated
;
362 printf ("MEM: %d\n", ep_allocated
);
380 g_slice_free1(EPOOL_BLOCK_SIZE
, data
);
385 epool_alloc (EPool
*ep
, int size
)
387 int rs
= (size
+ 3) & ~3;
388 int space
= EPOOL_BLOCK_SIZE
- sizeof (SList
) - ep
->cpos
;
391 if (size
> EPOOL_MAX_SIZE
) {
393 if (space
>= sizeof (SList
)) {
394 blocks
= ep
->blocks
->data
+ ep
->cpos
;
395 ep
->cpos
+= sizeof (SList
);
397 blocks
= (SList
*)epool_alloc (ep
, sizeof (SList
));
400 data
= g_malloc (size
);
402 ep
->allocated
+= size
;
403 ep_allocated
+= size
;
404 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
407 blocks
->next
= ep
->mblocks
;
409 ep
->mblocks
= blocks
;
413 } else if (space
>= rs
) {
414 data
= ep
->blocks
->data
+ ep
->cpos
;
420 SList
*blocks
= ep
->blocks
->data
+ ep
->cpos
;
422 data
= g_slice_alloc0 (EPOOL_BLOCK_SIZE
);
424 blocks
->next
= ep
->blocks
;
430 ep
->allocated
+= EPOOL_BLOCK_SIZE
;
431 ep_allocated
+= EPOOL_BLOCK_SIZE
;
432 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
440 epool_strndup (EPool
*ep
, const char *s
, int len
)
443 char *res
= epool_alloc (ep
, l
);
449 epool_strndup0 (EPool
*ep
, const char *s
, int len
)
451 char *res
= epool_alloc (ep
, len
+ 1);
452 strncpy (res
, s
, len
);
459 epool_strdup (EPool
*ep
, const char *s
)
461 int l
= strlen (s
) + 1;
462 char *res
= epool_alloc (ep
, l
);
468 loglist_print (LogList
*loglist
)
470 LogEntry
*log
= loglist
->log
;
472 printf ("L%08X %s", log
->linenr
, log
->text
);
479 loglist_add (EPool
*ep
, LogList
*loglist
, const char *text
, int len
, unsigned long linenr
)
484 if (len
!= strlen (text
)) {
485 debug_error ("string with wrong len", NULL
);
488 if (text
[len
] != 0) {
489 debug_error ("string is not null terminated", NULL
);
493 log
= epool_alloc (ep
, sizeof (LogEntry
));
495 log
->text
= epool_strndup (ep
, text
, len
);
496 log
->linenr
= linenr
;
499 if (loglist
->logs_last
) {
500 loglist
->logs_last
->next
= log
;
501 loglist
->logs_last
= log
;
504 loglist
->logs_last
= log
;
517 sentry
= (SEntry
*)g_slice_alloc0(EPOOL_BLOCK_SIZE
);
521 sentry
->ep
.allocated
+= EPOOL_BLOCK_SIZE
;
522 ep_allocated
+= EPOOL_BLOCK_SIZE
;
523 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
529 if ((se
= g_hash_table_lookup (smtpd_debug_alloc
, sentry
))) {
530 debug_error ("SEntry already alloced", NULL
);
532 g_hash_table_insert (smtpd_debug_alloc
, sentry
, sentry
);
537 cpos
= sizeof (SEntry
);
539 blocks
= (SList
*)((char *)sentry
+ cpos
);
541 cpos
+= sizeof (SList
);
543 blocks
->data
= sentry
;
546 sentry
->ep
.blocks
= blocks
;
547 sentry
->ep
.cpos
= cpos
;
553 sentry_get (LParser
*parser
, int pid
)
557 if ((sentry
= g_hash_table_lookup (parser
->smtpd_h
, &pid
))) {
561 if ((sentry
= sentry_new (pid
))) {
562 g_hash_table_insert (parser
->smtpd_h
, &sentry
->pid
, sentry
);
570 sentry_ref_add (SEntry
*sentry
, QEntry
*qentry
)
575 if (qentry
->smtpd
!= sentry
) {
576 debug_error ("qentry ref already set", NULL
);
583 if (l
->data
== qentry
) return;
587 l
= epool_alloc (&sentry
->ep
, sizeof (SList
));
589 qentry
->smtpd
= sentry
;
592 l
->next
= sentry
->refs
;
598 sentry_ref_del (SEntry
*sentry
, QEntry
*qentry
)
600 SList
*l
= sentry
->refs
;
603 if (!qentry
->smtpd
) {
604 debug_error ("qentry does not hav a qentry ref", NULL
);
611 QEntry
*qe
= (QEntry
*)l
->data
;
615 if (qe
&& qe
->cleanup
) count
++;
624 sentry_ref_finalize (LParser
*parser
, SEntry
*sentry
)
626 SList
*l
= sentry
->refs
;
632 QEntry
*qe
= l
->data
;
640 if (!qe
->removed
) continue;
642 FEntry
*fe
= qe
->filter
;
644 if (fe
&& !fe
->finished
) continue;
648 qentry_print (parser
, qe
);
653 qentry_free (parser
, qe
);
655 if (fe
) fentry_free (parser
, fe
);
659 if (!count
) sentry_free_noremove (sentry
);
663 sentry_ref_rem_unneeded (LParser
*parser
, SEntry
*sentry
)
665 SList
*l
= sentry
->refs
;
669 QEntry
*qe
= (QEntry
*)l
->data
;
674 qentry_free (parser
, qe
);
688 sentry_nqlist_add (SEntry
*sentry
, time_t ltime
, const char *from
, int from_len
,
689 const char *to
, int to_len
, char dstatus
)
691 NQList
*nq
= (NQList
*)epool_alloc (&sentry
->ep
, sizeof (NQList
));
693 nq
->from
= epool_strndup0 (&sentry
->ep
, from
, from_len
);
694 nq
->to
= epool_strndup0 (&sentry
->ep
, to
, to_len
);
695 nq
->dstatus
= dstatus
;
697 nq
->next
= sentry
->nqlist
;
703 sentry_print (LParser
*parser
, SEntry
*sentry
)
707 if (parser
->msgid
|| parser
->qid
) return;
709 if (parser
->server
) {
710 if (!sentry
->connect
) return;
711 if (!strcasestr (sentry
->connect
, parser
->server
)) return;
714 if (parser
->from
|| parser
->to
||
715 parser
->exclude_greylist
|| parser
->exclude_ndrs
) {
720 if (!*(parser
->from
)) {
721 if (*(nq
->from
)) nq
->dstatus
= 0;
722 } else if (!STRMATCH(parser
->from
, nq
->from
)) {
727 if (parser
->exclude_greylist
&& nq
->dstatus
== 'G') nq
->dstatus
= 0;
729 if (parser
->exclude_ndrs
&& nq
->from
&& !*nq
->from
) nq
->dstatus
= 0;
731 if (parser
->to
&& nq
->to
&& !STRMATCH(parser
->to
, nq
->to
)) {
735 if (nq
->dstatus
!= 0) found
= 1;
742 if (parser
->strmatch
&& !sentry
->strmatch
) return;
744 if (parser
->verbose
) {
748 printf ("CTIME: %08lX\n", parser
->ctime
);
750 if (sentry
->connect
) { printf ("CONNECT: %s\n", sentry
->connect
); }
751 //printf ("EXTERNAL: %d\n", sentry->external);
757 if (nq
->from
&& nq
->to
&& nq
->dstatus
) {
758 printf ("TO:%08lX:00000000000:%c: from <%s> to <%s>\n", nq
->ltime
, nq
->dstatus
, nq
->from
, nq
->to
);
764 if (parser
->verbose
) {
766 loglist_print (&sentry
->loglist
);
774 sentry_set_connect (SEntry
*sentry
, const char *connect
, int len
)
776 if (sentry
->connect
) {
778 if (strncmp (sentry
->connect
, connect
, len
)) {
779 debug_error ("duplicate connect", NULL
);
783 sentry
->connect
= epool_strndup0 (&sentry
->ep
, connect
, len
);
788 sentry_free_noremove (SEntry
*sentry
)
794 ep_allocated
-= sentry
->ep
.allocated
;
795 printf ("MEM: %d\n", ep_allocated
);
801 if ((se
= g_hash_table_lookup (smtpd_debug_free
, sentry
))) {
802 debug_error ("SEntry already freed", NULL
);
804 g_hash_table_insert (smtpd_debug_free
, sentry
, sentry
);
810 l
= sentry
->ep
.mblocks
;
817 l
= sentry
->ep
.blocks
;
821 g_slice_free1(EPOOL_BLOCK_SIZE
, data
);
826 sentry_free (LParser
*parser
, SEntry
*sentry
)
828 g_hash_table_remove (parser
->smtpd_h
, &sentry
->pid
);
830 sentry_free_noremove (sentry
);
834 sentry_cleanup_hash (gpointer key
,
839 LParser
*parser
= (LParser
*)user_data
;
841 sentry_print (parser
, se
);
842 sentry_free_noremove (se
);
847 sentry_debug_alloc (gpointer key
,
851 LParser
*parser
= (LParser
*)user_data
;
855 if ((fe
= g_hash_table_lookup (smtpd_debug_free
, se
))) {
859 printf ("FOUND ALLOCATED SENTRY:\n");
860 sentry_print (parser
, se
);
869 qentry_new (const char *qid
)
876 qentry
= (QEntry
*)g_slice_alloc0(EPOOL_BLOCK_SIZE
);
879 qentry
->ep
.allocated
+= EPOOL_BLOCK_SIZE
;
880 ep_allocated
+= EPOOL_BLOCK_SIZE
;
881 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
887 if ((qe
= g_hash_table_lookup (qmgr_debug_alloc
, qentry
))) {
888 debug_error ("QEntry already alloced", NULL
);
890 g_hash_table_insert (qmgr_debug_alloc
, qentry
, qentry
);
895 cpos
= sizeof (QEntry
);
897 blocks
= (SList
*)((char *)qentry
+ cpos
);
899 cpos
+= sizeof (SList
);
901 blocks
->data
= qentry
;
904 qentry
->qid
= qid_cp
= (char *)qentry
+ cpos
;
905 while ((*qid_cp
++ = *qid
++)) cpos
++;
907 cpos
= (cpos
+ 4) & ~3;
909 qentry
->ep
.blocks
= blocks
;
910 qentry
->ep
.cpos
= cpos
;;
916 qentry_tolist_add (QEntry
*qentry
, time_t ltime
, char dstatus
, const char *to
, int to_len
,
917 const char *relay
, int relay_len
)
919 TOList
*tl
= (TOList
*)epool_alloc (&qentry
->ep
, sizeof (TOList
));
921 tl
->to
= epool_strndup0 (&qentry
->ep
, to
, to_len
);
922 tl
->relay
= epool_strndup0 (&qentry
->ep
, relay
, relay_len
);
923 tl
->dstatus
= dstatus
;
925 tl
->next
= qentry
->tolist
;
931 qentry_set_from (QEntry
*qentry
, const char *from
, int len
)
935 if (strncmp (qentry
->from
, from
, len
)) {
936 debug_error ("duplicate from", NULL
);
940 qentry
->from
= epool_strndup0 (&qentry
->ep
, from
, len
);
945 qentry_set_msgid (QEntry
*qentry
, const char *msgid
, int len
)
949 if (strncmp (qentry
->msgid
, msgid
, len
)) {
950 debug_error ("duplicate msgid", NULL
);
954 qentry
->msgid
= epool_strndup0 (&qentry
->ep
, msgid
, len
);
959 qentry_set_client (QEntry
*qentry
, const char *client
, int len
)
961 if (qentry
->client
) {
963 if (strncmp (qentry
->client
, client
, len
)) {
964 debug_error ("duplicate client", NULL
);
968 qentry
->client
= epool_strndup0 (&qentry
->ep
, client
, len
);
973 qentry_print (LParser
*parser
, QEntry
*qentry
)
976 SEntry
*se
= qentry
->smtpd
;
977 FEntry
*fe
= qentry
->filter
;
980 if (!qentry
->msgid
) return;
981 if (strcasecmp (parser
->msgid
, qentry
->msgid
)) return;
986 if (fe
&& !strcmp (fe
->logid
, parser
->qid
)) found
= 1;
987 if (!strcmp (qentry
->qid
, parser
->qid
)) found
= 1;
992 if (parser
->server
) {
994 if (se
&& se
->connect
&& strcasestr (se
->connect
, parser
->server
)) found
= 1;
995 if (qentry
->client
&& strcasestr (qentry
->client
, parser
->server
)) found
= 1;
1001 if (!qentry
->from
) return;
1002 if (!*(parser
->from
)) {
1003 if (*(qentry
->from
)) return;
1004 } else if (!STRMATCH(parser
->from
, qentry
->from
)) {
1008 if (parser
->exclude_ndrs
&& qentry
->from
&& !*qentry
->from
) return;
1012 tl
= qentry
->tolist
;
1015 if (parser
->to
&& !STRMATCH(parser
->to
, tl
->to
)) {
1025 if (parser
->strmatch
&&
1026 !(qentry
->strmatch
|| (se
&& se
->strmatch
) || (fe
&& fe
->strmatch
)))
1030 if (parser
->verbose
) {
1032 printf ("QENTRY: %s\n", qentry
->qid
);
1034 printf ("CTIME: %08lX\n", parser
->ctime
);
1035 printf ("SIZE: %u\n", qentry
->size
);
1037 if (qentry
->client
) { printf ("CLIENT: %s\n", qentry
->client
); }
1039 if (qentry
->msgid
) { printf ("MSGID: %s\n", qentry
->msgid
); }
1043 tl
= qentry
->tolist
;
1047 if (fe
&& tl
->dstatus
== '2') {
1050 if (fl
->to
&& !strcmp (tl
->to
, fl
->to
)) {
1062 dstatus
= fl
->dstatus
;
1066 dstatus
= tl
->dstatus
;
1070 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");
1078 if (!parser
->verbose
) { fflush (stdout
); return; }
1082 if (se
->loglist
.log
) {
1084 loglist_print (&se
->loglist
);
1089 if (fe
->loglist
.log
) {
1090 printf ("FILTER: %s\n", fe
->logid
);
1091 loglist_print (&fe
->loglist
);
1095 if (qentry
->loglist
.log
) {
1097 loglist_print (&qentry
->loglist
);
1108 qentry_get (LParser
*parser
, const char *qid
)
1112 if ((qentry
= g_hash_table_lookup (parser
->qmgr_h
, qid
))) {
1115 if ((qentry
= qentry_new (qid
))) {
1116 g_hash_table_insert (parser
->qmgr_h
, qentry
->qid
, qentry
);
1124 qentry_free_noremove (LParser
*parser
, QEntry
*qentry
)
1130 if ((se
= qentry
->smtpd
)) {
1131 if (sentry_ref_del (se
, qentry
) == 0) {
1132 if (se
->disconnect
) {
1133 sentry_free_noremove (se
);
1139 ep_allocated
-= qentry
->ep
.allocated
;
1140 printf ("MEM: %d\n", ep_allocated
);
1146 if ((qe
= g_hash_table_lookup (qmgr_debug_free
, qentry
))) {
1147 debug_error ("QEntry already freed", NULL
);
1149 g_hash_table_insert (qmgr_debug_free
, qentry
, qentry
);
1155 l
= qentry
->ep
.mblocks
;
1162 l
= qentry
->ep
.blocks
;
1166 g_slice_free1(EPOOL_BLOCK_SIZE
, data
);
1171 qentry_free (LParser
*parser
, QEntry
*qentry
)
1173 g_hash_table_remove (parser
->qmgr_h
, qentry
->qid
);
1175 qentry_free_noremove (parser
, qentry
);
1179 qentry_cleanup_hash (gpointer key
,
1184 LParser
*parser
= (LParser
*)user_data
;
1186 qentry_print (parser
, qe
);
1187 qentry_free_noremove (parser
, qe
);
1191 qentry_finalize (LParser
*parser
, QEntry
*qentry
)
1193 if (qentry
&& qentry
->removed
) {
1194 SEntry
*se
= qentry
->smtpd
;
1196 if (se
&& !se
->disconnect
) return;
1198 FEntry
*fe
= qentry
->filter
;
1200 if (fe
&& !fe
->finished
) return;
1202 qentry_print (parser
, qentry
);
1203 qentry_free (parser
, qentry
);
1205 if (fe
) fentry_free (parser
, fe
);
1212 fentry_new (const char *logid
)
1219 fentry
= (FEntry
*)g_slice_alloc0(EPOOL_BLOCK_SIZE
);
1222 fentry
->ep
.allocated
+= EPOOL_BLOCK_SIZE
;
1223 ep_allocated
+= EPOOL_BLOCK_SIZE
;
1224 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
1230 if ((fe
= g_hash_table_lookup (filter_debug_alloc
, fentry
))) {
1231 debug_error ("FEntry already alloced", NULL
);
1233 g_hash_table_insert (filter_debug_alloc
, fentry
, fentry
);
1238 cpos
= sizeof (FEntry
);
1240 blocks
= (SList
*)((char *)fentry
+ cpos
);
1242 cpos
+= sizeof (SList
);
1244 blocks
->data
= fentry
;
1245 blocks
->next
= NULL
;
1247 fentry
->logid
= logid_cp
= (char *)fentry
+ cpos
;
1248 while ((*logid_cp
++ = *logid
++)) cpos
++;
1249 cpos
= (cpos
+ 4) & ~3;
1251 fentry
->ep
.blocks
= blocks
;
1252 fentry
->ep
.cpos
= cpos
;;
1258 fentry_get (LParser
*parser
, const char *logid
)
1262 if ((fentry
= g_hash_table_lookup (parser
->filter_h
, logid
))) {
1265 if ((fentry
= fentry_new (logid
))) {
1266 g_hash_table_insert (parser
->filter_h
, fentry
->logid
, fentry
);
1274 fentry_tolist_add (FEntry
*fentry
, char dstatus
, const char *to
, int to_len
,
1275 const char *qid
, int qid_len
)
1277 TOList
*tl
= (TOList
*)epool_alloc (&fentry
->ep
, sizeof (TOList
));
1279 tl
->to
= epool_strndup0 (&fentry
->ep
, to
, to_len
);
1282 tl
->relay
= epool_strndup0 (&fentry
->ep
, qid
, qid_len
);
1286 tl
->dstatus
= dstatus
;
1287 tl
->next
= fentry
->tolist
;
1289 fentry
->tolist
= tl
;
1293 fentry_free_noremove (LParser
*parser
, FEntry
*fentry
)
1299 ep_allocated
-= fentry
->ep
.allocated
;
1300 printf ("MEM: %d\n", ep_allocated
);
1306 if ((fe
= g_hash_table_lookup (filter_debug_free
, fentry
))) {
1307 debug_error ("FEntry already freed", NULL
);
1309 g_hash_table_insert (filter_debug_free
, fentry
, fentry
);
1315 l
= fentry
->ep
.mblocks
;
1322 l
= fentry
->ep
.blocks
;
1326 g_slice_free1(EPOOL_BLOCK_SIZE
, data
);
1331 fentry_free (LParser
*parser
, FEntry
*fentry
)
1333 g_hash_table_remove (parser
->filter_h
, fentry
->logid
);
1335 fentry_free_noremove (parser
, fentry
);
1339 fentry_cleanup_hash (gpointer key
,
1344 LParser
*parser
= (LParser
*)user_data
;
1346 fentry_free_noremove (parser
, fe
);
1354 LParser
*parser
= g_malloc0 (sizeof (LParser
));
1359 epool_init (&parser
->ep
);
1361 if (!(parser
->smtpd_h
= g_hash_table_new (g_int_hash
, g_int_equal
))) {
1365 if (!(parser
->qmgr_h
= g_hash_table_new (g_str_hash
, g_str_equal
))) {
1369 if (!(parser
->filter_h
= g_hash_table_new (g_str_hash
, g_str_equal
))) {
1373 //if (!(parser->track_h = g_hash_table_new (g_str_hash, g_str_equal))) {
1377 for (i
= 0; i
< MAX_LOGFILES
; i
++) {
1378 gettimeofday(&tv
, NULL
);
1379 tv
.tv_sec
-= 3600*24*i
;
1380 ltime
= localtime (&tv
.tv_sec
);
1381 parser
->year
[i
] = ltime
->tm_year
+ 1900;
1388 parser_free (LParser
*parser
)
1392 for (i
= 0; i
< MAX_LOGFILES
; i
++) {
1393 if (parser
->stream
[i
]) gzclose (parser
->stream
[i
]);
1396 epool_free (&parser
->ep
);
1398 g_hash_table_destroy (parser
->smtpd_h
);
1399 g_hash_table_destroy (parser
->qmgr_h
);
1400 g_hash_table_destroy (parser
->filter_h
);
1401 //g_hash_table_destroy (parser->track_h);
1408 parser_track (LParser
*parser
, const char *qid
, gboolean insert
)
1412 if ((res
= g_hash_table_lookup (parser
->track_h
, qid
))) {
1415 if (insert
&& (res
= epool_strdup (&parser
->ep
, qid
))) {
1416 g_hash_table_insert (parser
->track_h
, res
, res
);
1424 static const int linebufsize
= 8192;
1425 static int cur_year
;
1426 static int cur_month
= 0;
1427 static int cal_mtod
[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
1430 mkgmtime (struct tm
*tm
)
1434 int year
= tm
->tm_year
+ 1900;
1435 int mon
= tm
->tm_mon
;
1437 res
= (year
- 1970) * 365 + cal_mtod
[mon
];
1439 // leap year corrections (gregorian calendar)
1440 if (mon
<= 1) year
-= 1;
1441 res
+= (year
- 1968) / 4;
1442 res
-= (year
- 1900) / 100;
1443 res
+= (year
- 1600) / 400;
1445 res
+= tm
->tm_mday
- 1;
1446 res
= res
*24 + tm
->tm_hour
;
1447 res
= res
*60 + tm
->tm_min
;
1448 res
= res
*60 + tm
->tm_sec
;
1454 parse_time (const char **text
, int len
)
1458 int year
= cur_year
;
1467 const char *line
= *text
;
1469 if (len
== (linebufsize
- 1)) {
1470 debug_error ("skipping long line data", line
);
1475 debug_error ("skipping short line data", line
);
1480 int csum
= (line
[0]<<16) + (line
[1]<<8) + line
[2];
1483 case 4874606: mon
= 0; break;
1484 case 4613474: mon
= 1; break;
1485 case 5071218: mon
= 2; break;
1486 case 4288626: mon
= 3; break;
1487 case 5071225: mon
= 4; break;
1488 case 4879726: mon
= 5; break;
1489 case 4879724: mon
= 6; break;
1490 case 4289895: mon
= 7; break;
1491 case 5465456: mon
= 8; break;
1492 case 5202804: mon
= 9; break;
1493 case 5140342: mon
= 10; break;
1494 case 4482403: mon
= 11; break;
1496 debug_error ("unable to parse month", line
);
1500 // year change heuristik
1501 if (cur_month
== 11 && mon
== 0) {
1504 if (mon
> cur_month
) cur_month
= mon
;
1506 ltime
= (year
- 1970)*365 + cal_mtod
[mon
];
1508 // leap year corrections (gregorian calendar)
1509 if (mon
<= 1) year
-= 1;
1510 ltime
+= (year
- 1968) / 4;
1511 ltime
-= (year
- 1900) / 100;
1512 ltime
+= (year
- 1600) / 400;
1514 const char *cpos
= line
+ 3;
1516 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
1518 debug_error ("missing spaces after month", line
);
1522 found
= 0; while (isdigit (*cpos
)) { mday
= mday
*10 + *cpos
- 48; cpos
++; found
++; }
1523 if (found
< 1 || found
> 2) {
1524 debug_error ("unable to parse day of month", line
);
1530 found
= 0; while (isspace (*cpos
)) { cpos
++; found
++; }
1532 debug_error ("missing spaces after day of month", line
);
1536 found
= 0; while (isdigit (*cpos
)) { hour
= hour
*10 + *cpos
- 48; cpos
++; found
++; }
1537 if (found
< 1 || found
> 2) {
1538 debug_error ("unable to parse hour", line
);
1546 debug_error ("missing collon after hour", line
);
1551 found
= 0; while (isdigit (*cpos
)) { min
= min
*10 + *cpos
- 48; cpos
++; found
++; }
1552 if (found
< 1 || found
> 2) {
1553 debug_error ("unable to parse minute", line
);
1561 debug_error ("missing collon after minute", line
);
1566 found
= 0; while (isdigit (*cpos
)) { sec
= sec
*10 + *cpos
- 48; cpos
++; found
++; }
1567 if (found
< 1 || found
> 2) {
1568 debug_error ("unable to parse second", line
);
1575 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
1577 debug_error ("missing spaces after time", line
);
1588 parser_count_files (LParser
*parser
)
1591 time_t start
= parser
->start
;
1592 char linebuf
[linebufsize
];
1596 for (i
= 0; i
< MAX_LOGFILES
; i
++) {
1597 cur_year
= parser
->year
[i
];
1600 if ((stream
= gzopen (logfiles
[i
], "r"))) {
1601 if ((line
= gzgets (stream
, linebuf
, linebufsize
))) {
1602 if (parse_time (&line
, strlen (line
)) < start
) {
1618 parse_qid (const char **text
, char *out
, char delim
, int maxlen
)
1625 while (isxdigit (*idx
)) { *copy
++ = *idx
++; found
++; if (found
> maxlen
) break; }
1627 if (found
> 1 && found
< maxlen
&&
1628 ((delim
&& (*idx
== delim
)) || (!delim
&& isspace (*idx
)))) {
1631 while (isspace (*idx
)) idx
++;
1639 print_usage (const char *name
)
1641 fprintf (stderr
, "usage: %s [OPTIONS] [OUTPUTFILENAME]\n", name
);
1642 fprintf (stderr
, "\t-f SENDER mails from SENDER\n");
1643 fprintf (stderr
, "\t-t RECIPIENT mails to RECIPIENT\n");
1644 fprintf (stderr
, "\t-h Server Server IP or Hostname\n");
1645 fprintf (stderr
, "\t-s START start time (YYYY-MM-DD HH:MM:SS)\n");
1646 fprintf (stderr
, "\t or seconds since epoch\n");
1647 fprintf (stderr
, "\t-e END end time (YYYY-MM-DD HH:MM:SS)\n");
1648 fprintf (stderr
, "\t or seconds since epoch\n");
1649 fprintf (stderr
, "\t-m MSGID message ID (exact match)\n");
1650 fprintf (stderr
, "\t-q QID queue ID (exact match)\n");
1651 fprintf (stderr
, "\t-x STRING search for strings\n");
1652 fprintf (stderr
, "\t-l LIMIT print max limit entries\n");
1653 fprintf (stderr
, "\t-v verbose output\n");
1657 // gzgets is ways too slow, so we do it our own way
1660 mygzgetc (gzFile stream
)
1663 static char readbuf
[16384];
1664 static char *readend
= readbuf
+ sizeof (readbuf
);
1665 static char *readpos
= readbuf
+ sizeof (readbuf
);
1667 if (readpos
< readend
) return *readpos
++;
1669 if ((br
= gzread (stream
, readbuf
, sizeof (readbuf
))) <= 0) {
1673 readend
= readbuf
+ br
;
1680 mygzgets (gzFile stream
, char *line
, int bufsize
)
1686 while (--bufsize
> 0 && (c
= mygzgetc(stream
)) != -1) {
1691 if (c
== -1 && cpos
== line
)
1698 extern char *optarg
;
1699 extern int optind
, opterr
, optopt
;
1702 main (int argc
, char * const argv
[])
1704 char linebuf
[linebufsize
];
1706 char *uniqueid
= NULL
;
1714 unsigned long lines
= 0;
1720 time_t ctime
, start
, end
;
1726 smtpd_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1727 qmgr_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1728 filter_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1729 smtpd_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1730 qmgr_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1731 filter_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1734 if (!(parser
= parser_new ())) {
1735 fprintf (stderr
, "unable to alloc parser structure\n");
1739 while ((opt
= getopt (argc
, argv
, "f:t:s:e:h:m:q:x:l:I:vgn")) != -1) {
1741 parser
->from
= epool_strdup (&parser
->ep
, optarg
);
1742 } else if (opt
== 't') {
1743 parser
->to
= epool_strdup (&parser
->ep
, optarg
);
1744 } else if (opt
== 'v') {
1745 parser
->verbose
= 1;
1746 } else if (opt
== 'g') {
1747 parser
->exclude_greylist
= 1;
1748 } else if (opt
== 'n') {
1749 parser
->exclude_ndrs
= 1;
1750 } else if (opt
== 'I') {
1752 } else if (opt
== 'h') {
1753 parser
->server
= epool_strdup (&parser
->ep
, optarg
);
1754 } else if (opt
== 'm') {
1755 parser
->msgid
= epool_strdup (&parser
->ep
, optarg
);
1756 } else if (opt
== 'q') {
1757 parser
->qid
= epool_strdup (&parser
->ep
, optarg
);
1758 } else if (opt
== 'x') {
1759 parser
->strmatch
= epool_strdup (&parser
->ep
, optarg
);
1760 } else if (opt
== 'l') {
1762 parser
->limit
= strtoul (optarg
, &l
, 0);
1763 if (!*optarg
|| *l
) {
1764 fprintf (stderr
, "unable to parse limit '%s'\n", optarg
);
1767 } else if (opt
== 's') {
1768 // use strptime to convert time
1771 if ((!(res
= strptime (optarg
, "%F %T", &tm
)) &&
1772 !(res
= strptime (optarg
, "%s", &tm
))) || *res
) {
1773 fprintf (stderr
, "unable to parse start time\n");
1776 parser
->start
= mkgmtime (&tm
);
1778 } else if (opt
== 'e') {
1781 if ((!(res
= strptime (optarg
, "%F %T", &tm
)) &&
1782 !(res
= strptime (optarg
, "%s", &tm
))) || *res
) {
1783 fprintf (stderr
, "unable to parse end time\n");
1786 parser
->end
= mkgmtime (&tm
);
1789 print_usage (argv
[0]);
1794 if (optind
< argc
) {
1796 if ((argc
- optind
) > 1) {
1797 print_usage (argv
[0]);
1801 char *tmpfn
= g_strdup_printf ("/tmp/.proxtrack-%08X.txt", getpid ());
1803 if ((stdout
= freopen (tmpfn
, "w", stdout
)) == NULL
) {
1804 perror ("unable to open output file");
1807 if (rename (tmpfn
, argv
[optind
]) != 0) {
1808 perror ("unable to open output file");
1814 // we use gmtime exerywhere to speedup things, this can cause problems
1815 // when daylight saving time changes
1817 gettimeofday(&tv
, NULL
);
1818 ltime
= localtime (&tv
.tv_sec
);
1820 if (!parser
->start
) {
1824 parser
->start
= mkgmtime (ltime
);
1827 ltime
= localtime (&tv
.tv_sec
);
1830 parser
->end
= mkgmtime (ltime
);
1833 if (parser
->end
< parser
->start
) {
1834 fprintf (stderr
, "end time before start time\n");
1839 if ((filecount
= parser_count_files (parser
)) <= 0) {
1840 fprintf (stderr
, "unable to access log files\n");
1845 printf ("# LogReader: %d %s\n", getpid(), uniqueid
);
1847 printf ("# LogReader: %d\n", getpid());
1850 printf ("# Query options\n");
1851 if (parser
->from
) printf ("# Sender: %s\n", parser
->from
);
1852 if (parser
->to
) printf ("# Recipient: %s\n", parser
->to
);
1853 if (parser
->server
) printf ("# Server: %s\n", parser
->server
);
1854 if (parser
->msgid
) printf ("# MsgID: %s\n", parser
->msgid
);
1855 if (parser
->qid
) printf ("# QID: %s\n", parser
->qid
);
1856 if (parser
->strmatch
) printf ("# Match: %s\n", parser
->strmatch
);
1858 strftime (linebuf
, 256, "%F %T", gmtime (&parser
->start
));
1859 printf ("# Start: %s (%lu)\n", linebuf
, parser
->start
);
1860 strftime (linebuf
, 256, "%F %T", gmtime (&parser
->end
));
1861 printf ("# END: %s (%lu)\n", linebuf
, parser
->end
);
1862 printf ("# End Query Options\n\n");
1866 start
= parser
->start
;
1870 for (i
= filecount
- 1; i
>= 0; i
--) {
1873 // syslog files does not conain years, so we must compute then
1874 // cur_year is the assumed start year
1877 cur_year
= parser
->year
[i
];
1880 if (!(stream
= (gpointer
) fopen (logfiles
[i
], "r"))) continue;
1882 if (!(stream
= (gpointer
) gzopen (logfiles
[i
], "r"))) continue;
1887 if (parser
->limit
&& (parser
->count
>= parser
->limit
)) {
1888 printf ("STATUS: aborted by limit (too many hits)\n");
1893 line
= fgets (linebuf
, linebufsize
, (FILE *)stream
);
1895 line
= mygzgets ((gzFile
)stream
, linebuf
, linebufsize
);
1900 int len
= strlen (line
);
1904 if (!(ctime
= parse_time (&cpos
, len
))) {
1908 if (ctime
< start
) continue;
1909 if (ctime
> end
) break;
1913 found
= 0; while (!isspace (*cpos
)) { cpos
++; found
= 1; }
1915 debug_error ("missing hostname", line
);
1919 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
1921 debug_error ("missing spaces after host", line
);
1925 if ((*cpos
== 'l') && !strncmp (cpos
, "last message repeated", 21)) {
1929 //printf ("LINE: %s\n", line);
1930 //const char *prog = cpos;
1933 found
= 0; while (*cpos
&& (*cpos
!= ':') && (*cpos
!= '[')) {
1934 csum_prog
= (csum_prog
<<8) + *cpos
;
1939 //idx1 = g_strndup (prog, found);
1940 //printf ("TEST:%s:%08X\n", idx1, csum_prog);
1945 found
= 0; while (isdigit (*cpos
)) {
1946 pid
= pid
*10 + *cpos
- 48;
1950 if (found
< 1 || found
> 15 || *cpos
!= ']') {
1951 debug_error ("unable to parse pid", line
);
1957 if (*cpos
++ != ':') {
1958 debug_error ("missing collon", line
);
1963 if (!isspace (*cpos
++)) {
1964 debug_error ("missing space after collon", line
);
1970 parser
->ctime
= ctime
;
1974 if (parser
->strmatch
&& STRMATCH(parser
->strmatch
, text
)) {
1978 if ((csum_prog
== 0x70726F78) ||// proxprox
1979 (csum_prog
== 0x6C746572)) { // pmg-smtp-filter
1981 if ((idx1
= parse_qid (&cpos
, qidbuf
, ':', 25))) {
1985 if (!(fe
= fentry_get (parser
, idx1
))) {
1989 loglist_add (&fe
->ep
, &fe
->loglist
, line
, len
, lines
);
1991 if (strmatch
) fe
->strmatch
= 1;
1993 //fixme: BCC, Notify?
1994 //fixme: parse virus info
1995 //fixme: parse spam score
1997 if ((*cpos
== 'a') && !strncmp (cpos
, "accept mail to <", 16)) {
1999 const char *to_s
, *to_e
;
2001 to_s
= cpos
= cpos
+ 16;
2003 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2005 if (*cpos
!= '>') continue;
2011 if ((*cpos
++ != ' ') || (*cpos
++ != '(')) continue;
2013 if (!(idx1
= parse_qid (&cpos
, qidbuf
, ')', 15))) continue;
2015 // parser_track (parser, idx1, 1);
2017 fentry_tolist_add (fe
, 'A', to_s
, to_e
- to_s
, idx1
, strlen (idx1
));
2019 } else if ((*cpos
== 'm') && !strncmp (cpos
, "moved mail for <", 16)) {
2020 const char *to_s
, *to_e
;
2022 to_s
= cpos
= cpos
+ 16;
2024 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2028 if (strncmp (cpos
, "> to ", 5)) continue;
2031 if (!strncmp (cpos
, "spam", 4)) {
2033 } else if (!strncmp (cpos
, "virus", 5)) {
2039 if (strncmp (cpos
, " quarantine - ", 14)) continue;
2042 if (!(idx1
= parse_qid (&cpos
, qidbuf
, 0, 25))) continue;
2044 fentry_tolist_add (fe
, 'Q', to_s
, to_e
- to_s
, idx1
, strlen (idx1
));
2046 } else if ((*cpos
== 'b') && !strncmp (cpos
, "block mail to <", 15)) {
2050 to_s
= cpos
= cpos
+ 15;
2052 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2054 if (*cpos
!= '>') continue;
2056 fentry_tolist_add (fe
, 'B', to_s
, cpos
- to_s
, NULL
, 0);
2058 } else if ((*cpos
== 'p') && !strncmp (cpos
, "processing time: ", 17)) {
2061 sscanf (cpos
, "%f", &fe
->ptime
);
2068 } else if (csum_prog
== 0x7265656E) { // postfix/postscreen
2073 debug_error ("no pid for postscreen", line
);
2078 if ((*text
== 'N') && !strncmp (text
, "NOQUEUE: reject: RCPT from ", 27)) {
2082 if (!(idx1
= strstr (cpos
, "; client ["))) continue;
2084 const char *client
= cpos
= idx1
+ 10;
2086 while (*cpos
&& (*cpos
!= ']')) { cpos
++; }
2088 const char *client_end
= cpos
;
2090 if (!(idx1
= strstr (cpos
, "; from=<"))) continue;
2092 const char *from
= cpos
= idx1
+ 8;
2094 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2097 if ((*cpos
== '>') && strncmp (cpos
, ">, to=<", 7)) continue;
2099 const char *to
= cpos
= cpos
+ 7;
2101 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2103 if (*cpos
!= '>') continue;
2105 if (!(se
= sentry_get (parser
, pid
))) {
2109 if (strmatch
) se
->strmatch
= 1;
2111 loglist_add (&se
->ep
, &se
->loglist
, line
, len
, lines
);
2113 sentry_nqlist_add (se
, ctime
, from
, idx1
- from
, to
, cpos
- to
, 'N');
2115 sentry_set_connect (se
, client
, client_end
- client
);
2117 g_hash_table_remove (parser
->smtpd_h
, &se
->pid
);
2121 sentry_print (parser
, se
);
2122 sentry_free (parser
, se
);
2125 } else if (csum_prog
== 0x716D6772) { // postfix/qmgr
2127 if ((idx2
= text
) && (idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2131 if (!(qe
= qentry_get (parser
, idx1
))) {
2135 if (strmatch
) qe
->strmatch
= 1;
2139 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
, lines
);
2141 if ((*idx2
== 'f') && !strncmp (idx2
, "from=<", 6)) {
2143 cpos
= idx2
= idx2
+ 6;
2144 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2147 debug_error ("unable to parse 'from' address", line
);
2151 qentry_set_from (qe
, idx2
, cpos
- idx2
);
2155 if ((*cpos
== ',') && !strncmp (cpos
, ", size=", 7)) {
2159 while (isdigit (*cpos
)) { size
= size
*10 + *cpos
++ - 48; }
2164 } else if ((*idx2
== 'r') && !strncmp (idx2
, "removed\n", 8)) {
2168 qentry_finalize (parser
, qe
);
2172 } else if ((csum_prog
== 0x736D7470) || //postfix/smtp
2173 (csum_prog
== 0x6C6D7470) || //postfix/lmtp
2174 (csum_prog
== 0x6F63616C) || //postfix/local
2175 (csum_prog
== 0x72726F72)) { //postfix/error
2177 int lmtp
= (csum_prog
== 0x6C6D7470);
2179 if ((cpos
= text
) && (idx1
= parse_qid (&cpos
, qidbuf
, ':', 15))) {
2183 if (!(qe
= qentry_get (parser
, idx1
))) {
2187 if (strmatch
) qe
->strmatch
= 1;
2191 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
, lines
);
2193 if (strncmp (cpos
, "to=<", 4)) continue;
2196 const char *to_s
, *to_e
;
2200 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2202 if (*cpos
!= '>') continue;
2208 if (!(cpos
= strstr (cpos
, ", relay="))) continue;
2211 const char *relay_s
, *relay_e
;
2215 while (*cpos
&& (*cpos
!= ',')) { cpos
++; }
2217 if (*cpos
!= ',') continue;
2221 if (!(idx1
= strstr (cpos
+ 1, ", dsn="))) continue;
2225 if (!isdigit (*cpos
)) continue;
2227 char dstatus
= *cpos
;
2229 qentry_tolist_add (qe
, ctime
, dstatus
, to_s
, to_e
- to_s
,
2230 relay_s
, relay_e
- relay_s
);
2232 if (!lmtp
) continue; // filter always uses lmtp
2234 if (!(idx1
= strstr (cpos
+ 1, "status=sent (250 2.")))
2237 cpos
= idx1
= idx1
+ 19;
2239 if (*cpos
== '5' && (idx1
= strstr (cpos
, "5.0 OK ("))) {
2240 cpos
= idx1
= idx1
+ 8;
2241 } else if (*cpos
== '7' && (idx1
= strstr (cpos
, "7.0 BLOCKED ("))) {
2242 cpos
= idx1
= idx1
+ 13;
2247 if (!(idx1
= parse_qid (&cpos
, qidbuf
, ')', 25)))
2254 if ((fe
= g_hash_table_lookup (parser
->filter_h
, idx1
))) {
2259 } else if (csum_prog
== 0x6D747064) { // postfix/smtpd
2263 debug_error ("no pid for smtpd", line
);
2267 if (!(se
= sentry_get (parser
, pid
))) {
2271 if (strmatch
) se
->strmatch
= 1;
2273 loglist_add (&se
->ep
, &se
->loglist
, line
, len
, lines
);
2275 if ((*text
== 'c') && !strncmp (text
, "connect from ", 13)) {
2277 cpos
= idx1
= text
+ 13;
2279 while (*idx1
&& !isspace (*idx1
)) { idx1
++; }
2281 sentry_set_connect (se
, cpos
, idx1
- cpos
);
2283 // fixme: do we need this?
2284 //if (strcmp (se->connect, "localhost[127.0.0.1]")) {
2285 // se->external = 1;
2288 } else if ((*text
== 'd') && !strncmp (text
, "disconnect from", 15)) {
2291 g_hash_table_remove (parser
->smtpd_h
, &se
->pid
);
2295 if (sentry_ref_rem_unneeded (parser
, se
) == 0) {
2296 sentry_print (parser
, se
);
2297 sentry_free (parser
, se
);
2299 sentry_ref_finalize (parser
, se
);
2302 } else if ((*text
== 'N') && !strncmp (text
, "NOQUEUE:", 8)) {
2306 // parse 'whatsup' (reject:)
2307 while (*cpos
&& (*cpos
!= ':')) { cpos
++; }
2308 if (*cpos
!= ':') continue;
2311 // parse '%s from %s:'
2312 while (*cpos
&& (*cpos
!= ':')) { cpos
++; }
2313 if (*cpos
!= ':') continue;
2317 while (*cpos
&& (*cpos
!= ';')) { cpos
++; }
2318 if (*cpos
!= ';') continue;
2322 *(char *)cpos
= 0; // dangerous hack: delimit string
2323 if (strstr (idx1
, ": Recipient address rejected: Service is unavailable (try later)")) {
2326 *(char *)cpos
= ';'; // dangerous hack: restore line
2328 if (!(idx1
= strstr (cpos
, "; from=<"))) continue;
2330 const char *from
= cpos
= idx1
+ 8;
2332 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2335 if ((*cpos
== '>') && strncmp (cpos
, "> to=<", 6)) continue;
2337 const char *to
= cpos
= cpos
+ 6;
2339 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2341 if (*cpos
!= '>') continue;
2343 sentry_nqlist_add (se
, ctime
, from
, idx1
- from
, to
, cpos
- to
, dstatus
);
2345 } else if ((idx2
= text
) && (idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2349 if ((qe
= qentry_get (parser
, idx1
))) {
2351 if (strmatch
) qe
->strmatch
= 1;
2353 sentry_ref_add (se
, qe
);
2355 if ((*idx2
== 'c') && !strncmp (idx2
, "client=", 7)) {
2356 cpos
= idx2
= idx2
+ 7;
2358 while (*cpos
&& !isspace (*cpos
)) { cpos
++; }
2360 qentry_set_client (qe
, idx2
, cpos
- idx2
);
2366 } else if (csum_prog
== 0x616E7570) { // postfix/cleanup
2371 if ((idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2372 if ((qe
= qentry_get (parser
, idx1
))) {
2374 if (strmatch
) qe
->strmatch
= 1;
2376 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
, lines
);
2378 if ((*idx2
== 'm') && !strncmp (idx2
, "message-id=", 11)) {
2380 cpos
= idx2
= idx2
+ 11;
2382 while (*cpos
&& !isspace(*cpos
)) { cpos
++; }
2384 qentry_set_msgid (qe
, idx2
, cpos
- idx2
);
2394 fclose ((FILE *)stream
);
2396 gzclose ((gzFile
)stream
);
2399 if (ctime
> end
) break;
2404 printf ("LINES: %d\n", lines
);
2405 printf ("MEM SMTPD entries: %d\n", g_hash_table_size (parser
->smtpd_h
));
2406 printf ("MEM QMGR entries: %d\n", g_hash_table_size (parser
->qmgr_h
));
2407 printf ("MEM FILTER entries: %d\n", g_hash_table_size (parser
->filter_h
));
2409 printf ("MEMDEB SMTPD entries: %d %d\n",
2410 g_hash_table_size (smtpd_debug_alloc
),
2411 g_hash_table_size (smtpd_debug_free
));
2412 printf ("MEMDEB QMGR entries: %d %d\n",
2413 g_hash_table_size (qmgr_debug_alloc
),
2414 g_hash_table_size (qmgr_debug_free
));
2415 printf ("MEMDEB FILTER entries: %d %d\n",
2416 g_hash_table_size (filter_debug_alloc
),
2417 g_hash_table_size (filter_debug_free
));
2420 g_hash_table_foreach (parser
->qmgr_h
, qentry_cleanup_hash
, parser
);
2421 g_hash_table_foreach (parser
->smtpd_h
, sentry_cleanup_hash
, parser
);
2422 g_hash_table_foreach (parser
->filter_h
, fentry_cleanup_hash
, parser
);
2425 printf ("MEMDEB SMTPD entries: %d %d\n",
2426 g_hash_table_size (smtpd_debug_alloc
),
2427 g_hash_table_size (smtpd_debug_free
));
2428 printf ("MEMDEB QMGR entries: %d %d\n",
2429 g_hash_table_size (qmgr_debug_alloc
),
2430 g_hash_table_size (qmgr_debug_free
));
2431 printf ("MEMDEB FILTER entries: %d %d\n",
2432 g_hash_table_size (filter_debug_alloc
),
2433 g_hash_table_size (filter_debug_free
));
2435 g_hash_table_foreach (smtpd_debug_alloc
, sentry_debug_alloc
, parser
);
2441 printf ("MEMMAX %d\n", ep_maxalloc
);
2444 parser_free (parser
);