]>
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>
42 We assume the syslog files belong to one host, i.e. we do not
43 consider the hostname at all
47 REGEX: we do not use posix regular expressions (libc implementation
48 is too slow). fnmatch() is also slow.
49 Future versions may use our own implementation of a DFA. But currently we
50 just use strstr (no regex support at all)
53 //#define STRMATCH(pattern, string) (fnmatch (pattern, string, FNM_CASEFOLD) == 0)
54 #define STRMATCH(pattern, string) (strcasestr (string, pattern) != NULL)
57 //#define EPOOL_BLOCK_SIZE 512
58 //#define EPOOL_MAX_SIZE 128
59 #define EPOOL_BLOCK_SIZE 2048
60 #define EPOOL_MAX_SIZE 128
61 #define MAX_LOGFILES 32
65 typedef struct _SList SList
;
71 typedef struct _NQList NQList
;
80 typedef struct _TOList TOList
;
89 #define MatchTypeQID 1
90 #define MatchTypeRelLineNr 2
92 typedef struct _MatchList MatchList
;
97 unsigned long rel_line_nr
;
102 GHashTable
*smtpd_debug_alloc
;
103 GHashTable
*qmgr_debug_alloc
;
104 GHashTable
*filter_debug_alloc
;
105 GHashTable
*smtpd_debug_free
;
106 GHashTable
*qmgr_debug_free
;
107 GHashTable
*filter_debug_free
;
110 // EPool: Entry related memory pools
118 typedef struct _EPool EPool
;
120 SList
*blocks
; // allocated usiing g_slice_alloc(EPOOL_BLOCK_SIZE)
121 SList
*mblocks
; // allocated use g_malloc
132 GHashTable
*filter_h
;
133 //GHashTable *track_h;
134 gzFile stream
[MAX_LOGFILES
];
137 time_t year
[MAX_LOGFILES
];
141 MatchList
*match_list
;
148 unsigned int exclude_greylist
:1;
149 unsigned int exclude_ndrs
:1;
152 typedef struct _SEntry SEntry
;
153 typedef struct _QEntry QEntry
;
154 typedef struct _FEntry FEntry
;
156 typedef struct _LogEntry LogEntry
;
157 typedef struct _LogList LogList
;
161 unsigned long linenr
;
167 LogEntry
*logs_last
; // pointer to last log (speedup add log)
170 // SEntry: Store SMTPD related logs
185 // time,rel_line_nr is used as cursor/ID
187 unsigned long rel_line_nr
;
189 //unsigned int external:1; // not from local host
190 unsigned int disconnect
:1;
191 unsigned int strmatch
:1;
195 // QEntry: Store Queue (qmgr, smtp, lmtp) related logs
214 unsigned int cleanup
:1;
215 unsigned int removed
:1;
216 unsigned int filtered
:1; // set when passed via lmtp to filter
217 unsigned int strmatch
:1;
220 // FEntry: Store filter (proxprox) related logs
227 char *logid
; // proxprox log id
233 unsigned int finished
:1;
234 unsigned int strmatch
:1;
238 void debug_error (char *msg
, const char *line
);
240 EPool
*epool_init (EPool
*ep
);
241 void epool_free (EPool
*ep
);
242 gpointer
epool_alloc (EPool
*ep
, int size
);
243 char *epool_strndup (EPool
*ep
, const char *s
, int len
);
244 char *epool_strndup0 (EPool
*ep
, const char *s
, int len
);
245 char *epool_strdup (EPool
*ep
, const char *s
);
247 void loglist_print (LogList
*loglist
);
248 void loglist_add (EPool
*ep
, LogList
*loglist
, const char *text
, int len
, unsigned long linenr
);
250 SEntry
*sentry_new (int pid
, time_t ltime
, unsigned long rel_line_nr
);
251 SEntry
*sentry_get (LParser
*parser
, int pid
, time_t ltime
, unsigned long rel_line_nr
);
252 void sentry_ref_add (SEntry
*sentry
, QEntry
*qentry
);
253 int sentry_ref_del (SEntry
*sentry
, QEntry
*qentry
);
254 void sentry_ref_finalize (LParser
*parser
, SEntry
*sentry
);
255 int sentry_ref_rem_unneeded (LParser
*parser
, SEntry
*sentry
);
256 void sentry_nqlist_add (SEntry
*sentry
, time_t ltime
, const char *from
, int from_len
,
257 const char *to
, int to_len
, char dstatus
);
258 void sentry_print (LParser
*parser
, SEntry
*sentry
);
259 void sentry_set_connect (SEntry
*sentry
, const char *connect
, int len
);
260 void sentry_free_noremove (SEntry
*sentry
);
261 void sentry_free (LParser
*parser
, SEntry
*sentry
);
262 void sentry_cleanup_hash (gpointer key
, gpointer value
, gpointer user_data
);
265 QEntry
*qentry_new (const char *qid
);
266 QEntry
*qentry_get (LParser
*parser
, const char *qid
);
267 void qentry_tolist_add (QEntry
*qentry
, time_t ltime
, char dstatus
, const char *to
,
268 int to_len
, const char *relay
, int relay_len
);
269 void qentry_set_from (QEntry
*qentry
, const char *from
, int len
);
270 void qentry_set_msgid (QEntry
*qentry
, const char *msgid
, int len
);
271 void qentry_set_client (QEntry
*qentry
, const char *client
, int len
);
272 void qentry_print (LParser
*parser
, QEntry
*qentry
);
273 void qentry_finalize (LParser
*parser
, QEntry
*qentry
);
274 void qentry_free_noremove (LParser
*parser
, QEntry
*qentry
);
275 void qentry_free (LParser
*parser
, QEntry
*qentry
);
276 void qentry_cleanup_hash (gpointer key
, gpointer value
, gpointer user_data
);
279 FEntry
*fentry_new (const char *logid
);
280 FEntry
*fentry_get (LParser
*parser
, const char *logid
);
281 void fentry_tolist_add (FEntry
*fentry
, char dstatus
, const char *to
,
282 int to_len
, const char *qid
, int qid_len
);
283 void fentry_free_noremove (LParser
*parser
, FEntry
*fentry
);
284 void fentry_free (LParser
*parser
, FEntry
*fentry
);
285 void fentry_cleanup_hash (gpointer key
, gpointer value
, gpointer user_data
);
287 LParser
*parser_new ();
288 void parser_free (LParser
*parser
);
290 //char *parser_track (LParser *parser, const char *qid, gboolean insert);
295 #define PROXPROX 0xE0E4DEF0
296 #define PMG_SMTP_FILTER 0x0A85A6B7
297 #define POSTFIX_POSTSCREEN 0xD17E2019
298 #define POSTFIX_QMGR 0x48465316
299 #define POSTFIX_SMTP 0x4A466014
300 #define POSTFIX_LMTP 0x43466014
301 #define POSTFIX_LOCAL 0x484F05AF
302 #define POSTFIX_ERROR 0x4B5E13AE
303 #define POSTFIX_SMTPD 0x466014AE
304 #define POSTFIX_CLEANUP 0x05A8BAC1
306 //#define LOGPATH "./log/"
307 #define LOGPATH "/var/log/"
308 //#define LOGPATH "/root/testlog/"
310 static const char *logfiles
[] = {
313 LOGPATH
"syslog.2.gz",
314 LOGPATH
"syslog.3.gz",
315 LOGPATH
"syslog.4.gz",
316 LOGPATH
"syslog.5.gz",
317 LOGPATH
"syslog.6.gz",
318 LOGPATH
"syslog.7.gz",
319 LOGPATH
"syslog.8.gz",
320 LOGPATH
"syslog.9.gz",
321 LOGPATH
"syslog.10.gz",
322 LOGPATH
"syslog.11.gz",
323 LOGPATH
"syslog.12.gz",
324 LOGPATH
"syslog.13.gz",
325 LOGPATH
"syslog.14.gz",
326 LOGPATH
"syslog.15.gz",
327 LOGPATH
"syslog.16.gz",
328 LOGPATH
"syslog.17.gz",
329 LOGPATH
"syslog.18.gz",
330 LOGPATH
"syslog.19.gz",
331 LOGPATH
"syslog.20.gz",
332 LOGPATH
"syslog.21.gz",
333 LOGPATH
"syslog.22.gz",
334 LOGPATH
"syslog.23.gz",
335 LOGPATH
"syslog.24.gz",
336 LOGPATH
"syslog.25.gz",
337 LOGPATH
"syslog.26.gz",
338 LOGPATH
"syslog.27.gz",
339 LOGPATH
"syslog.28.gz",
340 LOGPATH
"syslog.29.gz",
341 LOGPATH
"syslog.30.gz",
342 LOGPATH
"syslog.31.gz",
346 debug_error (char *msg
, const char *line
)
349 fprintf (stderr
, "ERROR: %s\n", msg
);
350 if (line
) fprintf (stderr
, "LINE: %s\n", line
);
359 epool_init (EPool
*ep
)
364 data
= g_slice_alloc0(EPOOL_BLOCK_SIZE
);
365 blocks
= (SList
*)data
;
368 ep
->allocated
+= EPOOL_BLOCK_SIZE
;
369 ep_allocated
+= EPOOL_BLOCK_SIZE
;
370 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
378 ep
->cpos
= sizeof (SList
);
384 epool_free (EPool
*ep
)
390 ep_allocated
-= ep
->allocated
;
391 printf ("MEM: %d\n", ep_allocated
);
409 g_slice_free1(EPOOL_BLOCK_SIZE
, data
);
414 epool_alloc (EPool
*ep
, int size
)
416 int rs
= (size
+ 3) & ~3;
417 int space
= EPOOL_BLOCK_SIZE
- sizeof (SList
) - ep
->cpos
;
420 if (size
> EPOOL_MAX_SIZE
) {
422 if (space
>= sizeof (SList
)) {
423 blocks
= (SList
*)((char *)ep
->blocks
->data
+ ep
->cpos
);
424 ep
->cpos
+= sizeof (SList
);
426 blocks
= (SList
*)epool_alloc (ep
, sizeof (SList
));
429 data
= g_malloc (size
);
431 ep
->allocated
+= size
;
432 ep_allocated
+= size
;
433 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
436 blocks
->next
= ep
->mblocks
;
438 ep
->mblocks
= blocks
;
442 } else if (space
>= rs
) {
443 data
= (char *)ep
->blocks
->data
+ ep
->cpos
;
449 SList
*blocks
= (SList
*)((char *)ep
->blocks
->data
+ ep
->cpos
);
451 data
= g_slice_alloc0 (EPOOL_BLOCK_SIZE
);
453 blocks
->next
= ep
->blocks
;
459 ep
->allocated
+= EPOOL_BLOCK_SIZE
;
460 ep_allocated
+= EPOOL_BLOCK_SIZE
;
461 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
469 epool_strndup (EPool
*ep
, const char *s
, int len
)
472 char *res
= epool_alloc (ep
, l
);
478 epool_strndup0 (EPool
*ep
, const char *s
, int len
)
480 char *res
= epool_alloc (ep
, len
+ 1);
481 strncpy (res
, s
, len
);
488 epool_strdup (EPool
*ep
, const char *s
)
490 int l
= strlen (s
) + 1;
491 char *res
= epool_alloc (ep
, l
);
497 loglist_print (LogList
*loglist
)
499 LogEntry
*log
= loglist
->log
;
501 printf ("L%08X %s", log
->linenr
, log
->text
);
508 loglist_add (EPool
*ep
, LogList
*loglist
, const char *text
, int len
, unsigned long linenr
)
513 if (len
!= strlen (text
)) {
514 debug_error ("string with wrong len", NULL
);
517 if (text
[len
] != 0) {
518 debug_error ("string is not null terminated", NULL
);
522 log
= epool_alloc (ep
, sizeof (LogEntry
));
524 log
->text
= epool_strndup (ep
, text
, len
);
525 log
->linenr
= linenr
;
528 if (loglist
->logs_last
) {
529 loglist
->logs_last
->next
= log
;
530 loglist
->logs_last
= log
;
533 loglist
->logs_last
= log
;
540 sentry_new (int pid
, time_t ltime
, unsigned long rel_line_nr
)
546 sentry
= (SEntry
*)g_slice_alloc0(EPOOL_BLOCK_SIZE
);
548 sentry
->ltime
= ltime
;
549 sentry
->rel_line_nr
= rel_line_nr
;
552 sentry
->ep
.allocated
+= EPOOL_BLOCK_SIZE
;
553 ep_allocated
+= EPOOL_BLOCK_SIZE
;
554 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
560 if ((se
= g_hash_table_lookup (smtpd_debug_alloc
, sentry
))) {
561 debug_error ("SEntry already alloced", NULL
);
563 g_hash_table_insert (smtpd_debug_alloc
, sentry
, sentry
);
568 cpos
= sizeof (SEntry
);
570 blocks
= (SList
*)((char *)sentry
+ cpos
);
572 cpos
+= sizeof (SList
);
574 blocks
->data
= sentry
;
577 sentry
->ep
.blocks
= blocks
;
578 sentry
->ep
.cpos
= cpos
;
584 sentry_get (LParser
*parser
, int pid
, time_t ltime
, unsigned long rel_line_nr
)
588 if ((sentry
= g_hash_table_lookup (parser
->smtpd_h
, &pid
))) {
592 if ((sentry
= sentry_new (pid
, ltime
, rel_line_nr
))) {
593 g_hash_table_insert (parser
->smtpd_h
, &sentry
->pid
, sentry
);
601 sentry_ref_add (SEntry
*sentry
, QEntry
*qentry
)
606 if (qentry
->smtpd
!= sentry
) {
607 debug_error ("qentry ref already set", NULL
);
614 if (l
->data
== qentry
) return;
618 l
= epool_alloc (&sentry
->ep
, sizeof (SList
));
620 qentry
->smtpd
= sentry
;
623 l
->next
= sentry
->refs
;
629 sentry_ref_del (SEntry
*sentry
, QEntry
*qentry
)
631 SList
*l
= sentry
->refs
;
634 if (!qentry
->smtpd
) {
635 debug_error ("qentry does not hav a qentry ref", NULL
);
642 QEntry
*qe
= (QEntry
*)l
->data
;
646 if (qe
&& qe
->cleanup
) count
++;
655 sentry_ref_finalize (LParser
*parser
, SEntry
*sentry
)
657 SList
*l
= sentry
->refs
;
663 QEntry
*qe
= l
->data
;
671 if (!qe
->removed
) continue;
673 FEntry
*fe
= qe
->filter
;
675 if (fe
&& !fe
->finished
) continue;
679 qentry_print (parser
, qe
);
684 qentry_free (parser
, qe
);
686 if (fe
) fentry_free (parser
, fe
);
690 if (!count
) sentry_free_noremove (sentry
);
694 sentry_ref_rem_unneeded (LParser
*parser
, SEntry
*sentry
)
696 SList
*l
= sentry
->refs
;
700 QEntry
*qe
= (QEntry
*)l
->data
;
705 qentry_free (parser
, qe
);
719 sentry_nqlist_add (SEntry
*sentry
, time_t ltime
, const char *from
, int from_len
,
720 const char *to
, int to_len
, char dstatus
)
722 NQList
*nq
= (NQList
*)epool_alloc (&sentry
->ep
, sizeof (NQList
));
724 nq
->from
= epool_strndup0 (&sentry
->ep
, from
, from_len
);
725 nq
->to
= epool_strndup0 (&sentry
->ep
, to
, to_len
);
726 nq
->dstatus
= dstatus
;
728 nq
->next
= sentry
->nqlist
;
734 sentry_print (LParser
*parser
, SEntry
*sentry
)
738 if (parser
->msgid
) return;
740 if (parser
->server
) {
741 if (!sentry
->connect
) return;
742 if (!strcasestr (sentry
->connect
, parser
->server
)) return;
745 MatchList
*match
= parser
->match_list
;
749 if (match
->mtype
== MatchTypeQID
) {
751 } else if (match
->mtype
== MatchTypeRelLineNr
) {
752 if (match
->ltime
== sentry
->ltime
&& match
->rel_line_nr
== sentry
->rel_line_nr
) {
757 g_error("implement me");
764 if (parser
->from
|| parser
->to
||
765 parser
->exclude_greylist
|| parser
->exclude_ndrs
) {
770 if (!*(parser
->from
)) {
771 if (*(nq
->from
)) nq
->dstatus
= 0;
772 } else if (!STRMATCH(parser
->from
, nq
->from
)) {
777 if (parser
->exclude_greylist
&& nq
->dstatus
== 'G') nq
->dstatus
= 0;
779 if (parser
->exclude_ndrs
&& nq
->from
&& !*nq
->from
) nq
->dstatus
= 0;
781 if (parser
->to
&& nq
->to
&& !STRMATCH(parser
->to
, nq
->to
)) {
785 if (nq
->dstatus
!= 0) found
= 1;
792 if (parser
->strmatch
&& !sentry
->strmatch
) return;
794 if (parser
->verbose
) {
796 printf ("SMTPD: T%08lXL%08lX\n", sentry
->ltime
, sentry
->rel_line_nr
);
798 printf ("CTIME: %08lX\n", parser
->ctime
);
800 if (sentry
->connect
) { printf ("CLIENT: %s\n", sentry
->connect
); }
801 //printf ("EXTERNAL: %d\n", sentry->external);
807 if (nq
->from
&& nq
->to
&& nq
->dstatus
) {
808 printf ("TO:%08lX:T%08lXL%08lX:%c: from <%s> to <%s>\n", nq
->ltime
,
809 sentry
->ltime
, sentry
->rel_line_nr
, nq
->dstatus
,
816 if (!parser
->verbose
) { fflush (stdout
); return; }
818 if (parser
->verbose
> 1) {
820 loglist_print (&sentry
->loglist
);
829 sentry_set_connect (SEntry
*sentry
, const char *connect
, int len
)
831 if (sentry
->connect
) {
833 if (strncmp (sentry
->connect
, connect
, len
)) {
834 debug_error ("duplicate connect", NULL
);
838 sentry
->connect
= epool_strndup0 (&sentry
->ep
, connect
, len
);
843 sentry_free_noremove (SEntry
*sentry
)
849 ep_allocated
-= sentry
->ep
.allocated
;
850 printf ("MEM: %d\n", ep_allocated
);
856 if ((se
= g_hash_table_lookup (smtpd_debug_free
, sentry
))) {
857 debug_error ("SEntry already freed", NULL
);
859 g_hash_table_insert (smtpd_debug_free
, sentry
, sentry
);
865 l
= sentry
->ep
.mblocks
;
872 l
= sentry
->ep
.blocks
;
876 g_slice_free1(EPOOL_BLOCK_SIZE
, data
);
881 sentry_free (LParser
*parser
, SEntry
*sentry
)
883 g_hash_table_remove (parser
->smtpd_h
, &sentry
->pid
);
885 sentry_free_noremove (sentry
);
889 sentry_cleanup_hash (gpointer key
,
894 LParser
*parser
= (LParser
*)user_data
;
896 sentry_print (parser
, se
);
897 sentry_free_noremove (se
);
902 sentry_debug_alloc (gpointer key
,
906 LParser
*parser
= (LParser
*)user_data
;
910 if ((fe
= g_hash_table_lookup (smtpd_debug_free
, se
))) {
914 printf ("FOUND ALLOCATED SENTRY:\n");
915 sentry_print (parser
, se
);
924 qentry_new (const char *qid
)
931 qentry
= (QEntry
*)g_slice_alloc0(EPOOL_BLOCK_SIZE
);
934 qentry
->ep
.allocated
+= EPOOL_BLOCK_SIZE
;
935 ep_allocated
+= EPOOL_BLOCK_SIZE
;
936 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
942 if ((qe
= g_hash_table_lookup (qmgr_debug_alloc
, qentry
))) {
943 debug_error ("QEntry already alloced", NULL
);
945 g_hash_table_insert (qmgr_debug_alloc
, qentry
, qentry
);
950 cpos
= sizeof (QEntry
);
952 blocks
= (SList
*)((char *)qentry
+ cpos
);
954 cpos
+= sizeof (SList
);
956 blocks
->data
= qentry
;
959 qentry
->qid
= qid_cp
= (char *)qentry
+ cpos
;
960 while ((*qid_cp
++ = *qid
++)) cpos
++;
962 cpos
= (cpos
+ 4) & ~3;
964 qentry
->ep
.blocks
= blocks
;
965 qentry
->ep
.cpos
= cpos
;;
971 qentry_tolist_add (QEntry
*qentry
, time_t ltime
, char dstatus
, const char *to
, int to_len
,
972 const char *relay
, int relay_len
)
974 TOList
*tl
= (TOList
*)epool_alloc (&qentry
->ep
, sizeof (TOList
));
976 tl
->to
= epool_strndup0 (&qentry
->ep
, to
, to_len
);
977 tl
->relay
= epool_strndup0 (&qentry
->ep
, relay
, relay_len
);
978 tl
->dstatus
= dstatus
;
980 tl
->next
= qentry
->tolist
;
986 qentry_set_from (QEntry
*qentry
, const char *from
, int len
)
990 if (strncmp (qentry
->from
, from
, len
)) {
991 debug_error ("duplicate from", NULL
);
995 qentry
->from
= epool_strndup0 (&qentry
->ep
, from
, len
);
1000 qentry_set_msgid (QEntry
*qentry
, const char *msgid
, int len
)
1002 if (qentry
->msgid
) {
1004 if (strncmp (qentry
->msgid
, msgid
, len
)) {
1005 debug_error ("duplicate msgid", NULL
);
1009 qentry
->msgid
= epool_strndup0 (&qentry
->ep
, msgid
, len
);
1014 qentry_set_client (QEntry
*qentry
, const char *client
, int len
)
1016 if (qentry
->client
) {
1018 if (strncmp (qentry
->client
, client
, len
)) {
1019 debug_error ("duplicate client", NULL
);
1023 qentry
->client
= epool_strndup0 (&qentry
->ep
, client
, len
);
1028 qentry_print (LParser
*parser
, QEntry
*qentry
)
1031 SEntry
*se
= qentry
->smtpd
;
1032 FEntry
*fe
= qentry
->filter
;
1034 if (parser
->msgid
) {
1035 if (!qentry
->msgid
) return;
1036 if (strcasecmp (parser
->msgid
, qentry
->msgid
)) return;
1039 MatchList
*match
= parser
->match_list
;
1043 if (match
->mtype
== MatchTypeQID
) {
1044 if ((fe
&& !strcmp (fe
->logid
, match
->id
)) ||
1045 (!strcmp (qentry
->qid
, match
->id
))) {
1049 } else if (match
->mtype
== MatchTypeRelLineNr
) {
1050 if (se
&& match
->ltime
== se
->ltime
&& match
->rel_line_nr
== se
->rel_line_nr
) {
1055 g_error("implement me");
1057 match
= match
->next
;
1062 if (parser
->server
) {
1064 if (se
&& se
->connect
&& strcasestr (se
->connect
, parser
->server
)) found
= 1;
1065 if (qentry
->client
&& strcasestr (qentry
->client
, parser
->server
)) found
= 1;
1071 if (!qentry
->from
) return;
1072 if (!*(parser
->from
)) {
1073 if (*(qentry
->from
)) return;
1074 } else if (!STRMATCH(parser
->from
, qentry
->from
)) {
1078 if (parser
->exclude_ndrs
&& qentry
->from
&& !*qentry
->from
) return;
1082 tl
= qentry
->tolist
;
1085 if (parser
->to
&& !STRMATCH(parser
->to
, tl
->to
)) {
1095 if (parser
->strmatch
&&
1096 !(qentry
->strmatch
|| (se
&& se
->strmatch
) || (fe
&& fe
->strmatch
)))
1100 if (parser
->verbose
) {
1102 printf ("QENTRY: %s\n", qentry
->qid
);
1104 printf ("CTIME: %08lX\n", parser
->ctime
);
1105 printf ("SIZE: %u\n", qentry
->size
);
1107 if (qentry
->client
) {
1108 printf ("CLIENT: %s\n", qentry
->client
);
1109 } else if (se
&& se
->connect
) {
1110 printf ("CLIENT: %s\n", se
->connect
);
1113 if (qentry
->msgid
) { printf ("MSGID: %s\n", qentry
->msgid
); }
1117 tl
= qentry
->tolist
;
1121 if (fe
&& tl
->dstatus
== '2') {
1124 if (fl
->to
&& !strcmp (tl
->to
, fl
->to
)) {
1136 dstatus
= fl
->dstatus
;
1140 dstatus
= tl
->dstatus
;
1144 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");
1152 if (!parser
->verbose
) { fflush (stdout
); return; }
1154 if (parser
->verbose
> 1) {
1156 if (se
&& se
->loglist
.log
) {
1158 loglist_print (&se
->loglist
);
1161 if (fe
&& fe
->loglist
.log
) {
1162 printf ("FILTER: %s\n", fe
->logid
);
1163 loglist_print (&fe
->loglist
);
1166 if (qentry
->loglist
.log
) {
1168 loglist_print (&qentry
->loglist
);
1180 qentry_get (LParser
*parser
, const char *qid
)
1184 if ((qentry
= g_hash_table_lookup (parser
->qmgr_h
, qid
))) {
1187 if ((qentry
= qentry_new (qid
))) {
1188 g_hash_table_insert (parser
->qmgr_h
, qentry
->qid
, qentry
);
1196 qentry_free_noremove (LParser
*parser
, QEntry
*qentry
)
1202 if ((se
= qentry
->smtpd
)) {
1203 if (sentry_ref_del (se
, qentry
) == 0) {
1204 if (se
->disconnect
) {
1205 sentry_free_noremove (se
);
1211 ep_allocated
-= qentry
->ep
.allocated
;
1212 printf ("MEM: %d\n", ep_allocated
);
1218 if ((qe
= g_hash_table_lookup (qmgr_debug_free
, qentry
))) {
1219 debug_error ("QEntry already freed", NULL
);
1221 g_hash_table_insert (qmgr_debug_free
, qentry
, qentry
);
1227 l
= qentry
->ep
.mblocks
;
1234 l
= qentry
->ep
.blocks
;
1238 g_slice_free1(EPOOL_BLOCK_SIZE
, data
);
1243 qentry_free (LParser
*parser
, QEntry
*qentry
)
1245 g_hash_table_remove (parser
->qmgr_h
, qentry
->qid
);
1247 qentry_free_noremove (parser
, qentry
);
1251 qentry_cleanup_hash (gpointer key
,
1256 LParser
*parser
= (LParser
*)user_data
;
1258 qentry_print (parser
, qe
);
1259 qentry_free_noremove (parser
, qe
);
1263 qentry_finalize (LParser
*parser
, QEntry
*qentry
)
1265 if (qentry
&& qentry
->removed
) {
1266 SEntry
*se
= qentry
->smtpd
;
1268 if (se
&& !se
->disconnect
) return;
1270 FEntry
*fe
= qentry
->filter
;
1272 if (fe
&& !fe
->finished
) return;
1274 qentry_print (parser
, qentry
);
1275 qentry_free (parser
, qentry
);
1277 if (fe
) fentry_free (parser
, fe
);
1284 fentry_new (const char *logid
)
1291 fentry
= (FEntry
*)g_slice_alloc0(EPOOL_BLOCK_SIZE
);
1294 fentry
->ep
.allocated
+= EPOOL_BLOCK_SIZE
;
1295 ep_allocated
+= EPOOL_BLOCK_SIZE
;
1296 ep_maxalloc
= (ep_allocated
> ep_maxalloc
) ? ep_allocated
: ep_maxalloc
;
1302 if ((fe
= g_hash_table_lookup (filter_debug_alloc
, fentry
))) {
1303 debug_error ("FEntry already alloced", NULL
);
1305 g_hash_table_insert (filter_debug_alloc
, fentry
, fentry
);
1310 cpos
= sizeof (FEntry
);
1312 blocks
= (SList
*)((char *)fentry
+ cpos
);
1314 cpos
+= sizeof (SList
);
1316 blocks
->data
= fentry
;
1317 blocks
->next
= NULL
;
1319 fentry
->logid
= logid_cp
= (char *)fentry
+ cpos
;
1320 while ((*logid_cp
++ = *logid
++)) cpos
++;
1321 cpos
= (cpos
+ 4) & ~3;
1323 fentry
->ep
.blocks
= blocks
;
1324 fentry
->ep
.cpos
= cpos
;;
1330 fentry_get (LParser
*parser
, const char *logid
)
1334 if ((fentry
= g_hash_table_lookup (parser
->filter_h
, logid
))) {
1337 if ((fentry
= fentry_new (logid
))) {
1338 g_hash_table_insert (parser
->filter_h
, fentry
->logid
, fentry
);
1346 fentry_tolist_add (FEntry
*fentry
, char dstatus
, const char *to
, int to_len
,
1347 const char *qid
, int qid_len
)
1349 TOList
*tl
= (TOList
*)epool_alloc (&fentry
->ep
, sizeof (TOList
));
1351 tl
->to
= epool_strndup0 (&fentry
->ep
, to
, to_len
);
1354 tl
->relay
= epool_strndup0 (&fentry
->ep
, qid
, qid_len
);
1358 tl
->dstatus
= dstatus
;
1359 tl
->next
= fentry
->tolist
;
1361 fentry
->tolist
= tl
;
1365 fentry_free_noremove (LParser
*parser
, FEntry
*fentry
)
1371 ep_allocated
-= fentry
->ep
.allocated
;
1372 printf ("MEM: %d\n", ep_allocated
);
1378 if ((fe
= g_hash_table_lookup (filter_debug_free
, fentry
))) {
1379 debug_error ("FEntry already freed", NULL
);
1381 g_hash_table_insert (filter_debug_free
, fentry
, fentry
);
1387 l
= fentry
->ep
.mblocks
;
1394 l
= fentry
->ep
.blocks
;
1398 g_slice_free1(EPOOL_BLOCK_SIZE
, data
);
1403 fentry_free (LParser
*parser
, FEntry
*fentry
)
1405 g_hash_table_remove (parser
->filter_h
, fentry
->logid
);
1407 fentry_free_noremove (parser
, fentry
);
1411 fentry_cleanup_hash (gpointer key
,
1416 LParser
*parser
= (LParser
*)user_data
;
1418 fentry_free_noremove (parser
, fe
);
1426 LParser
*parser
= g_malloc0 (sizeof (LParser
));
1431 epool_init (&parser
->ep
);
1433 if (!(parser
->smtpd_h
= g_hash_table_new (g_int_hash
, g_int_equal
))) {
1437 if (!(parser
->qmgr_h
= g_hash_table_new (g_str_hash
, g_str_equal
))) {
1441 if (!(parser
->filter_h
= g_hash_table_new (g_str_hash
, g_str_equal
))) {
1445 //if (!(parser->track_h = g_hash_table_new (g_str_hash, g_str_equal))) {
1449 for (i
= 0; i
< MAX_LOGFILES
; i
++) {
1450 gettimeofday(&tv
, NULL
);
1451 tv
.tv_sec
-= 3600*24*i
;
1452 ltime
= localtime (&tv
.tv_sec
);
1453 parser
->year
[i
] = ltime
->tm_year
+ 1900;
1460 parser_free (LParser
*parser
)
1464 for (i
= 0; i
< MAX_LOGFILES
; i
++) {
1465 if (parser
->stream
[i
]) gzclose (parser
->stream
[i
]);
1468 epool_free (&parser
->ep
);
1470 g_hash_table_destroy (parser
->smtpd_h
);
1471 g_hash_table_destroy (parser
->qmgr_h
);
1472 g_hash_table_destroy (parser
->filter_h
);
1473 //g_hash_table_destroy (parser->track_h);
1480 parser_track (LParser
*parser
, const char *qid
, gboolean insert
)
1484 if ((res
= g_hash_table_lookup (parser
->track_h
, qid
))) {
1487 if (insert
&& (res
= epool_strdup (&parser
->ep
, qid
))) {
1488 g_hash_table_insert (parser
->track_h
, res
, res
);
1496 static const int linebufsize
= 8192;
1497 static int cur_year
;
1498 static int cur_month
= 0;
1499 static int cal_mtod
[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
1502 mkgmtime (struct tm
*tm
)
1506 int year
= tm
->tm_year
+ 1900;
1507 int mon
= tm
->tm_mon
;
1509 res
= (year
- 1970) * 365 + cal_mtod
[mon
];
1511 // leap year corrections (gregorian calendar)
1512 if (mon
<= 1) year
-= 1;
1513 res
+= (year
- 1968) / 4;
1514 res
-= (year
- 1900) / 100;
1515 res
+= (year
- 1600) / 400;
1517 res
+= tm
->tm_mday
- 1;
1518 res
= res
*24 + tm
->tm_hour
;
1519 res
= res
*60 + tm
->tm_min
;
1520 res
= res
*60 + tm
->tm_sec
;
1526 parse_time (const char **text
, int len
)
1530 int year
= cur_year
;
1539 const char *line
= *text
;
1541 if (len
== (linebufsize
- 1)) {
1542 debug_error ("skipping long line data", line
);
1547 debug_error ("skipping short line data", line
);
1552 int csum
= (line
[0]<<16) + (line
[1]<<8) + line
[2];
1555 case 4874606: mon
= 0; break;
1556 case 4613474: mon
= 1; break;
1557 case 5071218: mon
= 2; break;
1558 case 4288626: mon
= 3; break;
1559 case 5071225: mon
= 4; break;
1560 case 4879726: mon
= 5; break;
1561 case 4879724: mon
= 6; break;
1562 case 4289895: mon
= 7; break;
1563 case 5465456: mon
= 8; break;
1564 case 5202804: mon
= 9; break;
1565 case 5140342: mon
= 10; break;
1566 case 4482403: mon
= 11; break;
1568 debug_error ("unable to parse month", line
);
1572 // year change heuristik
1573 if (cur_month
== 11 && mon
== 0) {
1576 if (mon
> cur_month
) cur_month
= mon
;
1578 ltime
= (year
- 1970)*365 + cal_mtod
[mon
];
1580 // leap year corrections (gregorian calendar)
1581 if (mon
<= 1) year
-= 1;
1582 ltime
+= (year
- 1968) / 4;
1583 ltime
-= (year
- 1900) / 100;
1584 ltime
+= (year
- 1600) / 400;
1586 const char *cpos
= line
+ 3;
1588 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
1590 debug_error ("missing spaces after month", line
);
1594 found
= 0; while (isdigit (*cpos
)) { mday
= mday
*10 + *cpos
- 48; cpos
++; found
++; }
1595 if (found
< 1 || found
> 2) {
1596 debug_error ("unable to parse day of month", line
);
1602 found
= 0; while (isspace (*cpos
)) { cpos
++; found
++; }
1604 debug_error ("missing spaces after day of month", line
);
1608 found
= 0; while (isdigit (*cpos
)) { hour
= hour
*10 + *cpos
- 48; cpos
++; found
++; }
1609 if (found
< 1 || found
> 2) {
1610 debug_error ("unable to parse hour", line
);
1618 debug_error ("missing collon after hour", line
);
1623 found
= 0; while (isdigit (*cpos
)) { min
= min
*10 + *cpos
- 48; cpos
++; found
++; }
1624 if (found
< 1 || found
> 2) {
1625 debug_error ("unable to parse minute", line
);
1633 debug_error ("missing collon after minute", line
);
1638 found
= 0; while (isdigit (*cpos
)) { sec
= sec
*10 + *cpos
- 48; cpos
++; found
++; }
1639 if (found
< 1 || found
> 2) {
1640 debug_error ("unable to parse second", line
);
1647 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
1649 debug_error ("missing spaces after time", line
);
1660 parser_count_files (LParser
*parser
)
1663 time_t start
= parser
->start
;
1664 char linebuf
[linebufsize
];
1668 for (i
= 0; i
< (MAX_LOGFILES
- 1); i
++) {
1669 cur_year
= parser
->year
[i
];
1672 if ((stream
= gzopen (logfiles
[i
], "r"))) {
1673 if ((line
= gzgets (stream
, linebuf
, linebufsize
))) {
1674 if (parse_time (&line
, strlen (line
)) < start
) {
1690 parse_qid (const char **text
, char *out
, char delim
, int maxlen
)
1697 while (isxdigit (*idx
)) { *copy
++ = *idx
++; found
++; if (found
> maxlen
) break; }
1699 if (found
> 1 && found
< maxlen
&&
1700 ((delim
&& (*idx
== delim
)) || (!delim
&& isspace (*idx
)))) {
1703 while (isspace (*idx
)) idx
++;
1711 print_usage (const char *name
)
1713 fprintf (stderr
, "usage: %s [OPTIONS] [OUTPUTFILENAME]\n", name
);
1714 fprintf (stderr
, "\t-f SENDER mails from SENDER\n");
1715 fprintf (stderr
, "\t-t RECIPIENT mails to RECIPIENT\n");
1716 fprintf (stderr
, "\t-h Server Server IP or Hostname\n");
1717 fprintf (stderr
, "\t-s START start time (YYYY-MM-DD HH:MM:SS)\n");
1718 fprintf (stderr
, "\t or seconds since epoch\n");
1719 fprintf (stderr
, "\t-e END end time (YYYY-MM-DD HH:MM:SS)\n");
1720 fprintf (stderr
, "\t or seconds since epoch\n");
1721 fprintf (stderr
, "\t-m MSGID message ID (exact match)\n");
1722 fprintf (stderr
, "\t-q QID queue ID (exact match), can be\n");
1723 fprintf (stderr
, "\t specified multiple times.\n");
1724 fprintf (stderr
, "\t-x STRING search for strings\n");
1725 fprintf (stderr
, "\t-l LIMIT print max limit entries\n");
1726 fprintf (stderr
, "\t-g exclude greylist entries\n");
1727 fprintf (stderr
, "\t-n exclude NDR entries\n");
1728 fprintf (stderr
, "\t-v verbose output (no logs)\n");
1729 fprintf (stderr
, "\t-vv verbose output with logs\n");
1733 // gzgets is ways too slow, so we do it our own way
1736 mygzgetc (gzFile stream
)
1739 static char readbuf
[16384];
1740 static char *readend
= readbuf
+ sizeof (readbuf
);
1741 static char *readpos
= readbuf
+ sizeof (readbuf
);
1743 if (readpos
< readend
) return *readpos
++;
1745 if ((br
= gzread (stream
, readbuf
, sizeof (readbuf
))) <= 0) {
1749 readend
= readbuf
+ br
;
1756 mygzgets (gzFile stream
, char *line
, int bufsize
)
1762 while (--bufsize
> 0 && (c
= mygzgetc(stream
)) != -1) {
1767 if (c
== -1 && cpos
== line
)
1774 extern char *optarg
;
1775 extern int optind
, opterr
, optopt
;
1778 main (int argc
, char * const argv
[])
1780 char linebuf
[linebufsize
];
1782 char *inputfile
= NULL
;
1790 unsigned long lines
= 0;
1791 unsigned long rel_line_nr
= 0;
1797 time_t ctime
, next_ctime
, start
, end
;
1803 smtpd_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1804 qmgr_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1805 filter_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1806 smtpd_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1807 qmgr_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1808 filter_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1811 if (!(parser
= parser_new ())) {
1812 fprintf (stderr
, "unable to alloc parser structure\n");
1816 while ((opt
= getopt (argc
, argv
, "f:t:s:e:h:m:q:x:i:l:vgn")) != -1) {
1818 parser
->from
= epool_strdup (&parser
->ep
, optarg
);
1819 } else if (opt
== 't') {
1820 parser
->to
= epool_strdup (&parser
->ep
, optarg
);
1821 } else if (opt
== 'v') {
1822 parser
->verbose
+= 1;
1823 } else if (opt
== 'g') {
1824 parser
->exclude_greylist
= 1;
1825 } else if (opt
== 'n') {
1826 parser
->exclude_ndrs
= 1;
1827 } else if (opt
== 'h') {
1828 parser
->server
= epool_strdup (&parser
->ep
, optarg
);
1829 } else if (opt
== 'm') {
1830 parser
->msgid
= epool_strdup (&parser
->ep
, optarg
);
1831 } else if (opt
== 'q') {
1833 unsigned long rel_line_nr
;
1834 MatchList
*match
= (MatchList
*)epool_alloc(&parser
->ep
, sizeof(MatchList
));
1835 if (sscanf(optarg
, "T%08lXL%08lX", <ime
, &rel_line_nr
) == 2) {
1836 match
->mtype
= MatchTypeRelLineNr
;
1837 match
->ltime
= ltime
;
1838 match
->rel_line_nr
= rel_line_nr
;
1839 match
->next
= parser
->match_list
;
1840 parser
->match_list
= match
;
1842 match
->mtype
= MatchTypeQID
;
1843 match
->id
= epool_strdup(&parser
->ep
, optarg
);
1844 match
->next
= parser
->match_list
;
1845 parser
->match_list
= match
;
1847 } else if (opt
== 'x') {
1848 parser
->strmatch
= epool_strdup (&parser
->ep
, optarg
);
1849 } else if (opt
== 'i') {
1851 } else if (opt
== 'l') {
1853 parser
->limit
= strtoul (optarg
, &l
, 0);
1854 if (!*optarg
|| *l
) {
1855 fprintf (stderr
, "unable to parse limit '%s'\n", optarg
);
1858 } else if (opt
== 's') {
1859 // use strptime to convert time
1862 if ((!(res
= strptime (optarg
, "%F %T", &tm
)) &&
1863 !(res
= strptime (optarg
, "%s", &tm
))) || *res
) {
1864 fprintf (stderr
, "unable to parse start time\n");
1867 parser
->start
= mkgmtime (&tm
);
1869 } else if (opt
== 'e') {
1872 if ((!(res
= strptime (optarg
, "%F %T", &tm
)) &&
1873 !(res
= strptime (optarg
, "%s", &tm
))) || *res
) {
1874 fprintf (stderr
, "unable to parse end time\n");
1877 parser
->end
= mkgmtime (&tm
);
1880 print_usage (argv
[0]);
1885 if (optind
< argc
) {
1887 if ((argc
- optind
) > 1) {
1888 print_usage (argv
[0]);
1892 char *tmpfn
= g_strdup_printf ("/tmp/.proxtrack-%08X.txt", getpid ());
1894 if ((stdout
= freopen (tmpfn
, "w", stdout
)) == NULL
) {
1895 perror ("unable to open output file");
1898 if (rename (tmpfn
, argv
[optind
]) != 0) {
1899 perror ("unable to open output file");
1905 // we use gmtime exerywhere to speedup things, this can cause problems
1906 // when daylight saving time changes
1908 gettimeofday(&tv
, NULL
);
1909 ltime
= localtime (&tv
.tv_sec
);
1911 if (!parser
->start
) {
1915 parser
->start
= mkgmtime (ltime
);
1918 ltime
= localtime (&tv
.tv_sec
);
1921 parser
->end
= mkgmtime (ltime
);
1924 if (parser
->end
< parser
->start
) {
1925 fprintf (stderr
, "end time before start time\n");
1932 } else if ((filecount
= parser_count_files (parser
)) <= 0) {
1933 fprintf (stderr
, "unable to access log files\n");
1937 printf ("# LogReader: %d\n", getpid());
1939 printf ("# Query options\n");
1940 if (parser
->from
) printf ("# Sender: %s\n", parser
->from
);
1941 if (parser
->to
) printf ("# Recipient: %s\n", parser
->to
);
1942 if (parser
->server
) printf ("# Server: %s\n", parser
->server
);
1943 if (parser
->msgid
) printf ("# MsgID: %s\n", parser
->msgid
);
1945 MatchList
*match
= parser
->match_list
;
1947 if (match
->mtype
== MatchTypeQID
) {
1948 printf ("# QID: %s\n", match
->id
);
1949 } else if (match
->mtype
== MatchTypeRelLineNr
) {
1950 printf ("# QID: T%08lXL%08lX\n", match
->ltime
, match
->rel_line_nr
);
1952 g_error("internal error - unknown match type %d\n", match
->mtype
);
1954 match
= match
->next
;
1957 if (parser
->strmatch
) printf ("# Match: %s\n", parser
->strmatch
);
1959 strftime (linebuf
, 256, "%F %T", gmtime (&parser
->start
));
1960 printf ("# Start: %s (%lu)\n", linebuf
, parser
->start
);
1961 strftime (linebuf
, 256, "%F %T", gmtime (&parser
->end
));
1962 printf ("# END: %s (%lu)\n", linebuf
, parser
->end
);
1963 printf ("# End Query Options\n\n");
1967 start
= parser
->start
;
1971 for (i
= filecount
- 1; i
>= 0; i
--) {
1974 // syslog files does not conain years, so we must compute then
1975 // cur_year is the assumed start year
1978 cur_year
= parser
->year
[i
];
1981 if (inputfile
&& strlen(inputfile
) == 1 && *inputfile
== '-') {
1982 stream
= (gpointer
) stdin
;
1983 } else if (inputfile
) {
1984 if (!(stream
= (gpointer
) fopen (inputfile
, "r"))) {
1985 fprintf(stderr
, "unable to open log file\n");
1988 } else if (!(stream
= (gpointer
) fopen (logfiles
[i
], "r"))) continue;
1990 if (!(stream
= (gpointer
) gzopen (logfiles
[i
], "r"))) continue;
1995 if (parser
->limit
&& (parser
->count
>= parser
->limit
)) {
1996 printf ("STATUS: aborted by limit (too many hits)\n");
2001 line
= fgets (linebuf
, linebufsize
, (FILE *)stream
);
2003 line
= mygzgets ((gzFile
)stream
, linebuf
, linebufsize
);
2008 int len
= strlen (line
);
2013 next_ctime
= parse_time (&cpos
, len
);
2019 if (next_ctime
!= ctime
) {
2027 if (ctime
< start
) continue;
2028 if (ctime
> end
) break;
2032 found
= 0; while (!isspace (*cpos
)) { cpos
++; found
= 1; }
2034 debug_error ("missing hostname", line
);
2038 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
2040 debug_error ("missing spaces after host", line
);
2044 if ((*cpos
== 'l') && !strncmp (cpos
, "last message repeated", 21)) {
2048 //printf ("LINE: %s\n", line);
2049 //const char *prog = cpos;
2052 found
= 0; while (*cpos
&& (*cpos
!= ':') && (*cpos
!= '[')) {
2053 csum_prog
= ((csum_prog
<< 8)|(csum_prog
>> 24)) + *cpos
;
2058 //idx1 = g_strndup (prog, found);
2059 //printf ("TEST:%s:%08X\n", idx1, csum_prog);
2064 found
= 0; while (isdigit (*cpos
)) {
2065 pid
= pid
*10 + *cpos
- 48;
2069 if (found
< 1 || found
> 15 || *cpos
!= ']') {
2070 debug_error ("unable to parse pid", line
);
2076 if (*cpos
++ != ':') {
2077 debug_error ("missing collon", line
);
2082 if (!isspace (*cpos
++)) {
2083 debug_error ("missing space after collon", line
);
2089 parser
->ctime
= ctime
;
2093 if (parser
->strmatch
&& STRMATCH(parser
->strmatch
, text
)) {
2097 if ((csum_prog
== PROXPROX
) ||
2098 (csum_prog
== PMG_SMTP_FILTER
)) {
2100 if ((idx1
= parse_qid (&cpos
, qidbuf
, ':', 25))) {
2104 if (!(fe
= fentry_get (parser
, idx1
))) {
2108 loglist_add (&fe
->ep
, &fe
->loglist
, line
, len
, lines
);
2110 if (strmatch
) fe
->strmatch
= 1;
2112 //fixme: BCC, Notify?
2113 //fixme: parse virus info
2114 //fixme: parse spam score
2116 if ((*cpos
== 'a') && !strncmp (cpos
, "accept mail to <", 16)) {
2118 const char *to_s
, *to_e
;
2120 to_s
= cpos
= cpos
+ 16;
2122 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2124 if (*cpos
!= '>') continue;
2130 if ((*cpos
++ != ' ') || (*cpos
++ != '(')) continue;
2132 if (!(idx1
= parse_qid (&cpos
, qidbuf
, ')', 15))) continue;
2134 // parser_track (parser, idx1, 1);
2136 fentry_tolist_add (fe
, 'A', to_s
, to_e
- to_s
, idx1
, strlen (idx1
));
2138 } else if ((*cpos
== 'm') && !strncmp (cpos
, "moved mail for <", 16)) {
2139 const char *to_s
, *to_e
;
2141 to_s
= cpos
= cpos
+ 16;
2143 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2147 if (strncmp (cpos
, "> to ", 5)) continue;
2150 if (!strncmp (cpos
, "spam", 4)) {
2152 } else if (!strncmp (cpos
, "virus", 5)) {
2158 if (strncmp (cpos
, " quarantine - ", 14)) continue;
2161 if (!(idx1
= parse_qid (&cpos
, qidbuf
, 0, 25))) continue;
2163 fentry_tolist_add (fe
, 'Q', to_s
, to_e
- to_s
, idx1
, strlen (idx1
));
2165 } else if ((*cpos
== 'b') && !strncmp (cpos
, "block mail to <", 15)) {
2169 to_s
= cpos
= cpos
+ 15;
2171 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2173 if (*cpos
!= '>') continue;
2175 fentry_tolist_add (fe
, 'B', to_s
, cpos
- to_s
, NULL
, 0);
2177 } else if ((*cpos
== 'p') && !strncmp (cpos
, "processing time: ", 17)) {
2180 sscanf (cpos
, "%f", &fe
->ptime
);
2187 } else if (csum_prog
== POSTFIX_POSTSCREEN
) {
2192 debug_error ("no pid for postscreen", line
);
2197 if ((*text
== 'N') && !strncmp (text
, "NOQUEUE: reject: RCPT from ", 27)) {
2201 if (!(idx1
= strstr (cpos
, "; client ["))) continue;
2203 const char *client
= cpos
= idx1
+ 10;
2205 while (*cpos
&& (*cpos
!= ']')) { cpos
++; }
2207 const char *client_end
= cpos
;
2209 if (!(idx1
= strstr (cpos
, "; from=<"))) continue;
2211 const char *from
= cpos
= idx1
+ 8;
2213 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2216 if ((*cpos
== '>') && strncmp (cpos
, ">, to=<", 7)) continue;
2218 const char *to
= cpos
= cpos
+ 7;
2220 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2222 if (*cpos
!= '>') continue;
2224 if (!(se
= sentry_get (parser
, pid
, ctime
, rel_line_nr
))) {
2228 if (strmatch
) se
->strmatch
= 1;
2230 loglist_add (&se
->ep
, &se
->loglist
, line
, len
, lines
);
2232 sentry_nqlist_add (se
, ctime
, from
, idx1
- from
, to
, cpos
- to
, 'N');
2234 sentry_set_connect (se
, client
, client_end
- client
);
2236 g_hash_table_remove (parser
->smtpd_h
, &se
->pid
);
2240 sentry_print (parser
, se
);
2241 sentry_free (parser
, se
);
2244 } else if (csum_prog
== POSTFIX_QMGR
) {
2246 if ((idx2
= text
) && (idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2250 if (!(qe
= qentry_get (parser
, idx1
))) {
2254 if (strmatch
) qe
->strmatch
= 1;
2258 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
, lines
);
2260 if ((*idx2
== 'f') && !strncmp (idx2
, "from=<", 6)) {
2262 cpos
= idx2
= idx2
+ 6;
2263 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2266 debug_error ("unable to parse 'from' address", line
);
2270 qentry_set_from (qe
, idx2
, cpos
- idx2
);
2274 if ((*cpos
== ',') && !strncmp (cpos
, ", size=", 7)) {
2278 while (isdigit (*cpos
)) { size
= size
*10 + *cpos
++ - 48; }
2283 } else if ((*idx2
== 'r') && !strncmp (idx2
, "removed\n", 8)) {
2287 qentry_finalize (parser
, qe
);
2291 } else if ((csum_prog
== POSTFIX_SMTP
) ||
2292 (csum_prog
== POSTFIX_LMTP
) ||
2293 (csum_prog
== POSTFIX_LOCAL
) ||
2294 (csum_prog
== POSTFIX_ERROR
)) {
2296 int lmtp
= (csum_prog
== POSTFIX_LMTP
);
2298 if ((cpos
= text
) && (idx1
= parse_qid (&cpos
, qidbuf
, ':', 15))) {
2302 if (!(qe
= qentry_get (parser
, idx1
))) {
2306 if (strmatch
) qe
->strmatch
= 1;
2310 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
, lines
);
2312 if (strncmp (cpos
, "to=<", 4)) continue;
2315 const char *to_s
, *to_e
;
2319 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2321 if (*cpos
!= '>') continue;
2327 if (!(cpos
= strstr (cpos
, ", relay="))) continue;
2330 const char *relay_s
, *relay_e
;
2334 while (*cpos
&& (*cpos
!= ',')) { cpos
++; }
2336 if (*cpos
!= ',') continue;
2340 if (!(idx1
= strstr (cpos
+ 1, ", dsn="))) continue;
2344 if (!isdigit (*cpos
)) continue;
2346 char dstatus
= *cpos
;
2348 qentry_tolist_add (qe
, ctime
, dstatus
, to_s
, to_e
- to_s
,
2349 relay_s
, relay_e
- relay_s
);
2351 if (!lmtp
) continue; // filter always uses lmtp
2353 if (!(idx1
= strstr (cpos
+ 1, "status=sent (250 2.")))
2356 cpos
= idx1
= idx1
+ 19;
2358 if (*cpos
== '5' && (idx1
= strstr (cpos
, "5.0 OK ("))) {
2359 cpos
= idx1
= idx1
+ 8;
2360 } else if (*cpos
== '7' && (idx1
= strstr (cpos
, "7.0 BLOCKED ("))) {
2361 cpos
= idx1
= idx1
+ 13;
2366 if (!(idx1
= parse_qid (&cpos
, qidbuf
, ')', 25)))
2373 if ((fe
= g_hash_table_lookup (parser
->filter_h
, idx1
))) {
2378 } else if (csum_prog
== POSTFIX_SMTPD
) {
2382 debug_error ("no pid for smtpd", line
);
2386 if (!(se
= sentry_get (parser
, pid
, ctime
, rel_line_nr
))) {
2390 if (strmatch
) se
->strmatch
= 1;
2392 loglist_add (&se
->ep
, &se
->loglist
, line
, len
, lines
);
2394 if ((*text
== 'c') && !strncmp (text
, "connect from ", 13)) {
2396 cpos
= idx1
= text
+ 13;
2398 while (*idx1
&& !isspace (*idx1
)) { idx1
++; }
2400 sentry_set_connect (se
, cpos
, idx1
- cpos
);
2402 // fixme: do we need this?
2403 //if (strcmp (se->connect, "localhost[127.0.0.1]")) {
2404 // se->external = 1;
2407 } else if ((*text
== 'd') && !strncmp (text
, "disconnect from", 15)) {
2410 g_hash_table_remove (parser
->smtpd_h
, &se
->pid
);
2414 if (sentry_ref_rem_unneeded (parser
, se
) == 0) {
2415 sentry_print (parser
, se
);
2416 sentry_free (parser
, se
);
2418 sentry_ref_finalize (parser
, se
);
2421 } else if ((*text
== 'N') && !strncmp (text
, "NOQUEUE:", 8)) {
2425 // parse 'whatsup' (reject:)
2426 while (*cpos
&& (*cpos
!= ':')) { cpos
++; }
2427 if (*cpos
!= ':') continue;
2430 // parse '%s from %s:'
2431 while (*cpos
&& (*cpos
!= ':')) { cpos
++; }
2432 if (*cpos
!= ':') continue;
2436 while (*cpos
&& (*cpos
!= ';')) { cpos
++; }
2437 if (*cpos
!= ';') continue;
2441 *(char *)cpos
= 0; // dangerous hack: delimit string
2442 if (strstr (idx1
, ": Recipient address rejected: Service is unavailable (try later)")) {
2445 *(char *)cpos
= ';'; // dangerous hack: restore line
2447 if (!(idx1
= strstr (cpos
, "; from=<"))) continue;
2449 const char *from
= cpos
= idx1
+ 8;
2451 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2454 if ((*cpos
== '>') && strncmp (cpos
, "> to=<", 6)) continue;
2456 const char *to
= cpos
= cpos
+ 6;
2458 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2460 if (*cpos
!= '>') continue;
2462 sentry_nqlist_add (se
, ctime
, from
, idx1
- from
, to
, cpos
- to
, dstatus
);
2464 } else if ((idx2
= text
) && (idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2468 if ((qe
= qentry_get (parser
, idx1
))) {
2470 if (strmatch
) qe
->strmatch
= 1;
2472 sentry_ref_add (se
, qe
);
2474 if ((*idx2
== 'c') && !strncmp (idx2
, "client=", 7)) {
2475 cpos
= idx2
= idx2
+ 7;
2477 while (*cpos
&& !isspace (*cpos
)) { cpos
++; }
2479 qentry_set_client (qe
, idx2
, cpos
- idx2
);
2485 } else if (csum_prog
== POSTFIX_CLEANUP
) {
2490 if ((idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2491 if ((qe
= qentry_get (parser
, idx1
))) {
2493 if (strmatch
) qe
->strmatch
= 1;
2495 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
, lines
);
2497 if ((*idx2
== 'm') && !strncmp (idx2
, "message-id=", 11)) {
2499 cpos
= idx2
= idx2
+ 11;
2501 while (*cpos
&& !isspace(*cpos
)) { cpos
++; }
2503 qentry_set_msgid (qe
, idx2
, cpos
- idx2
);
2513 fclose ((FILE *)stream
);
2515 gzclose ((gzFile
)stream
);
2518 if (ctime
> end
) break;
2523 printf ("LINES: %d\n", lines
);
2524 printf ("MEM SMTPD entries: %d\n", g_hash_table_size (parser
->smtpd_h
));
2525 printf ("MEM QMGR entries: %d\n", g_hash_table_size (parser
->qmgr_h
));
2526 printf ("MEM FILTER entries: %d\n", g_hash_table_size (parser
->filter_h
));
2528 printf ("MEMDEB SMTPD entries: %d %d\n",
2529 g_hash_table_size (smtpd_debug_alloc
),
2530 g_hash_table_size (smtpd_debug_free
));
2531 printf ("MEMDEB QMGR entries: %d %d\n",
2532 g_hash_table_size (qmgr_debug_alloc
),
2533 g_hash_table_size (qmgr_debug_free
));
2534 printf ("MEMDEB FILTER entries: %d %d\n",
2535 g_hash_table_size (filter_debug_alloc
),
2536 g_hash_table_size (filter_debug_free
));
2539 g_hash_table_foreach (parser
->qmgr_h
, qentry_cleanup_hash
, parser
);
2540 g_hash_table_foreach (parser
->smtpd_h
, sentry_cleanup_hash
, parser
);
2541 g_hash_table_foreach (parser
->filter_h
, fentry_cleanup_hash
, parser
);
2544 printf ("MEMDEB SMTPD entries: %d %d\n",
2545 g_hash_table_size (smtpd_debug_alloc
),
2546 g_hash_table_size (smtpd_debug_free
));
2547 printf ("MEMDEB QMGR entries: %d %d\n",
2548 g_hash_table_size (qmgr_debug_alloc
),
2549 g_hash_table_size (qmgr_debug_free
));
2550 printf ("MEMDEB FILTER entries: %d %d\n",
2551 g_hash_table_size (filter_debug_alloc
),
2552 g_hash_table_size (filter_debug_free
));
2554 g_hash_table_foreach (smtpd_debug_alloc
, sentry_debug_alloc
, parser
);
2560 printf ("MEMMAX %d\n", ep_maxalloc
);
2563 parser_free (parser
);