]>
git.proxmox.com Git - pmg-log-tracker.git/blob - pmg-log-tracker.c
37f59c6eb44669615eec65b78fd2cd1ca72895ee
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
;
1525 #define JAN (('J'<<16)|('a'<<8)|'n')
1526 #define FEB (('F'<<16)|('e'<<8)|'b')
1527 #define MAR (('M'<<16)|('a'<<8)|'r')
1528 #define APR (('A'<<16)|('p'<<8)|'r')
1529 #define MAY (('M'<<16)|('a'<<8)|'y')
1530 #define JUN (('J'<<16)|('u'<<8)|'n')
1531 #define JUL (('J'<<16)|('u'<<8)|'l')
1532 #define AUG (('A'<<16)|('u'<<8)|'g')
1533 #define SEP (('S'<<16)|('e'<<8)|'p')
1534 #define OCT (('O'<<16)|('c'<<8)|'t')
1535 #define NOV (('N'<<16)|('o'<<8)|'v')
1536 #define DEC (('D'<<16)|('e'<<8)|'c')
1539 parse_time (const char **text
, int len
)
1543 int year
= cur_year
;
1552 const char *line
= *text
;
1554 if (len
== (linebufsize
- 1)) {
1555 debug_error ("skipping long line data", line
);
1560 debug_error ("skipping short line data", line
);
1565 int csum
= (line
[0]<<16) + (line
[1]<<8) + line
[2];
1568 case JAN
: mon
= 0; break;
1569 case FEB
: mon
= 1; break;
1570 case MAR
: mon
= 2; break;
1571 case APR
: mon
= 3; break;
1572 case MAY
: mon
= 4; break;
1573 case JUN
: mon
= 5; break;
1574 case JUL
: mon
= 6; break;
1575 case AUG
: mon
= 7; break;
1576 case SEP
: mon
= 8; break;
1577 case OCT
: mon
= 9; break;
1578 case NOV
: mon
= 10; break;
1579 case DEC
: mon
= 11; break;
1581 debug_error ("unable to parse month", line
);
1585 // year change heuristik
1586 if (cur_month
== 11 && mon
== 0) {
1589 if (mon
> cur_month
) cur_month
= mon
;
1591 ltime
= (year
- 1970)*365 + cal_mtod
[mon
];
1593 // leap year corrections (gregorian calendar)
1594 if (mon
<= 1) year
-= 1;
1595 ltime
+= (year
- 1968) / 4;
1596 ltime
-= (year
- 1900) / 100;
1597 ltime
+= (year
- 1600) / 400;
1599 const char *cpos
= line
+ 3;
1601 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
1603 debug_error ("missing spaces after month", line
);
1607 found
= 0; while (isdigit (*cpos
)) { mday
= mday
*10 + *cpos
- '0'; cpos
++; found
++; }
1608 if (found
< 1 || found
> 2) {
1609 debug_error ("unable to parse day of month", line
);
1615 found
= 0; while (isspace (*cpos
)) { cpos
++; found
++; }
1617 debug_error ("missing spaces after day of month", line
);
1621 found
= 0; while (isdigit (*cpos
)) { hour
= hour
*10 + *cpos
- '0'; cpos
++; found
++; }
1622 if (found
< 1 || found
> 2) {
1623 debug_error ("unable to parse hour", line
);
1631 debug_error ("missing collon after hour", line
);
1636 found
= 0; while (isdigit (*cpos
)) { min
= min
*10 + *cpos
- '0'; cpos
++; found
++; }
1637 if (found
< 1 || found
> 2) {
1638 debug_error ("unable to parse minute", line
);
1646 debug_error ("missing collon after minute", line
);
1651 found
= 0; while (isdigit (*cpos
)) { sec
= sec
*10 + *cpos
- '0'; cpos
++; found
++; }
1652 if (found
< 1 || found
> 2) {
1653 debug_error ("unable to parse second", line
);
1660 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
1662 debug_error ("missing spaces after time", line
);
1673 parser_count_files (LParser
*parser
)
1676 time_t start
= parser
->start
;
1677 char linebuf
[linebufsize
];
1681 for (i
= 0; i
< (MAX_LOGFILES
- 1); i
++) {
1682 cur_year
= parser
->year
[i
];
1685 if ((stream
= gzopen (logfiles
[i
], "r"))) {
1686 if ((line
= gzgets (stream
, linebuf
, linebufsize
))) {
1687 if (parse_time (&line
, strlen (line
)) < start
) {
1703 parse_qid (const char **text
, char *out
, char delim
, int maxlen
)
1710 while (isxdigit (*idx
)) { *copy
++ = *idx
++; found
++; if (found
> maxlen
) break; }
1712 if (found
> 1 && found
< maxlen
&&
1713 ((delim
&& (*idx
== delim
)) || (!delim
&& isspace (*idx
)))) {
1716 while (isspace (*idx
)) idx
++;
1724 print_usage (const char *name
)
1726 fprintf (stderr
, "usage: %s [OPTIONS] [OUTPUTFILENAME]\n", name
);
1727 fprintf (stderr
, "\t-f SENDER mails from SENDER\n");
1728 fprintf (stderr
, "\t-t RECIPIENT mails to RECIPIENT\n");
1729 fprintf (stderr
, "\t-h Server Server IP or Hostname\n");
1730 fprintf (stderr
, "\t-s START start time (YYYY-MM-DD HH:MM:SS)\n");
1731 fprintf (stderr
, "\t or seconds since epoch\n");
1732 fprintf (stderr
, "\t-e END end time (YYYY-MM-DD HH:MM:SS)\n");
1733 fprintf (stderr
, "\t or seconds since epoch\n");
1734 fprintf (stderr
, "\t-m MSGID message ID (exact match)\n");
1735 fprintf (stderr
, "\t-q QID queue ID (exact match), can be\n");
1736 fprintf (stderr
, "\t specified multiple times.\n");
1737 fprintf (stderr
, "\t-x STRING search for strings\n");
1738 fprintf (stderr
, "\t-l LIMIT print max limit entries\n");
1739 fprintf (stderr
, "\t-g exclude greylist entries\n");
1740 fprintf (stderr
, "\t-n exclude NDR entries\n");
1741 fprintf (stderr
, "\t-v verbose output (no logs)\n");
1742 fprintf (stderr
, "\t-vv verbose output with logs\n");
1746 // gzgets is ways too slow, so we do it our own way
1749 mygzgetc (gzFile stream
)
1752 static char readbuf
[16384];
1753 static char *readend
= readbuf
+ sizeof (readbuf
);
1754 static char *readpos
= readbuf
+ sizeof (readbuf
);
1756 if (readpos
< readend
) return *readpos
++;
1758 if ((br
= gzread (stream
, readbuf
, sizeof (readbuf
))) <= 0) {
1762 readend
= readbuf
+ br
;
1769 mygzgets (gzFile stream
, char *line
, int bufsize
)
1775 while (--bufsize
> 0 && (c
= mygzgetc(stream
)) != -1) {
1780 if (c
== -1 && cpos
== line
)
1787 extern char *optarg
;
1788 extern int optind
, opterr
, optopt
;
1791 main (int argc
, char * const argv
[])
1793 char linebuf
[linebufsize
];
1795 char *inputfile
= NULL
;
1803 unsigned long lines
= 0;
1804 unsigned long rel_line_nr
= 0;
1810 time_t ctime
, next_ctime
, start
, end
;
1816 smtpd_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1817 qmgr_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1818 filter_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1819 smtpd_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1820 qmgr_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1821 filter_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1824 if (!(parser
= parser_new ())) {
1825 fprintf (stderr
, "unable to alloc parser structure\n");
1829 while ((opt
= getopt (argc
, argv
, "f:t:s:e:h:m:q:x:i:l:vgn")) != -1) {
1831 parser
->from
= epool_strdup (&parser
->ep
, optarg
);
1832 } else if (opt
== 't') {
1833 parser
->to
= epool_strdup (&parser
->ep
, optarg
);
1834 } else if (opt
== 'v') {
1835 parser
->verbose
+= 1;
1836 } else if (opt
== 'g') {
1837 parser
->exclude_greylist
= 1;
1838 } else if (opt
== 'n') {
1839 parser
->exclude_ndrs
= 1;
1840 } else if (opt
== 'h') {
1841 parser
->server
= epool_strdup (&parser
->ep
, optarg
);
1842 } else if (opt
== 'm') {
1843 parser
->msgid
= epool_strdup (&parser
->ep
, optarg
);
1844 } else if (opt
== 'q') {
1846 unsigned long rel_line_nr
;
1847 MatchList
*match
= (MatchList
*)epool_alloc(&parser
->ep
, sizeof(MatchList
));
1848 if (sscanf(optarg
, "T%08lXL%08lX", <ime
, &rel_line_nr
) == 2) {
1849 match
->mtype
= MatchTypeRelLineNr
;
1850 match
->ltime
= ltime
;
1851 match
->rel_line_nr
= rel_line_nr
;
1852 match
->next
= parser
->match_list
;
1853 parser
->match_list
= match
;
1855 match
->mtype
= MatchTypeQID
;
1856 match
->id
= epool_strdup(&parser
->ep
, optarg
);
1857 match
->next
= parser
->match_list
;
1858 parser
->match_list
= match
;
1860 } else if (opt
== 'x') {
1861 parser
->strmatch
= epool_strdup (&parser
->ep
, optarg
);
1862 } else if (opt
== 'i') {
1864 } else if (opt
== 'l') {
1866 parser
->limit
= strtoul (optarg
, &l
, 0);
1867 if (!*optarg
|| *l
) {
1868 fprintf (stderr
, "unable to parse limit '%s'\n", optarg
);
1871 } else if (opt
== 's') {
1872 // use strptime to convert time
1875 if ((!(res
= strptime (optarg
, "%F %T", &tm
)) &&
1876 !(res
= strptime (optarg
, "%s", &tm
))) || *res
) {
1877 fprintf (stderr
, "unable to parse start time\n");
1880 parser
->start
= mkgmtime (&tm
);
1882 } else if (opt
== 'e') {
1885 if ((!(res
= strptime (optarg
, "%F %T", &tm
)) &&
1886 !(res
= strptime (optarg
, "%s", &tm
))) || *res
) {
1887 fprintf (stderr
, "unable to parse end time\n");
1890 parser
->end
= mkgmtime (&tm
);
1893 print_usage (argv
[0]);
1898 if (optind
< argc
) {
1900 if ((argc
- optind
) > 1) {
1901 print_usage (argv
[0]);
1905 char *tmpfn
= g_strdup_printf ("/tmp/.proxtrack-%08X.txt", getpid ());
1907 if ((stdout
= freopen (tmpfn
, "w", stdout
)) == NULL
) {
1908 perror ("unable to open output file");
1911 if (rename (tmpfn
, argv
[optind
]) != 0) {
1912 perror ("unable to open output file");
1918 // we use gmtime exerywhere to speedup things, this can cause problems
1919 // when daylight saving time changes
1921 gettimeofday(&tv
, NULL
);
1922 ltime
= localtime (&tv
.tv_sec
);
1924 if (!parser
->start
) {
1928 parser
->start
= mkgmtime (ltime
);
1931 ltime
= localtime (&tv
.tv_sec
);
1934 parser
->end
= mkgmtime (ltime
);
1937 if (parser
->end
< parser
->start
) {
1938 fprintf (stderr
, "end time before start time\n");
1945 } else if ((filecount
= parser_count_files (parser
)) <= 0) {
1946 fprintf (stderr
, "unable to access log files\n");
1950 printf ("# LogReader: %d\n", getpid());
1952 printf ("# Query options\n");
1953 if (parser
->from
) printf ("# Sender: %s\n", parser
->from
);
1954 if (parser
->to
) printf ("# Recipient: %s\n", parser
->to
);
1955 if (parser
->server
) printf ("# Server: %s\n", parser
->server
);
1956 if (parser
->msgid
) printf ("# MsgID: %s\n", parser
->msgid
);
1958 MatchList
*match
= parser
->match_list
;
1960 if (match
->mtype
== MatchTypeQID
) {
1961 printf ("# QID: %s\n", match
->id
);
1962 } else if (match
->mtype
== MatchTypeRelLineNr
) {
1963 printf ("# QID: T%08lXL%08lX\n", match
->ltime
, match
->rel_line_nr
);
1965 g_error("internal error - unknown match type %d\n", match
->mtype
);
1967 match
= match
->next
;
1970 if (parser
->strmatch
) printf ("# Match: %s\n", parser
->strmatch
);
1972 strftime (linebuf
, 256, "%F %T", gmtime (&parser
->start
));
1973 printf ("# Start: %s (%lu)\n", linebuf
, parser
->start
);
1974 strftime (linebuf
, 256, "%F %T", gmtime (&parser
->end
));
1975 printf ("# END: %s (%lu)\n", linebuf
, parser
->end
);
1976 printf ("# End Query Options\n\n");
1980 start
= parser
->start
;
1984 for (i
= filecount
- 1; i
>= 0; i
--) {
1987 // syslog files does not conain years, so we must compute then
1988 // cur_year is the assumed start year
1991 cur_year
= parser
->year
[i
];
1994 if (inputfile
&& strlen(inputfile
) == 1 && *inputfile
== '-') {
1995 stream
= (gpointer
) stdin
;
1996 } else if (inputfile
) {
1997 if (!(stream
= (gpointer
) fopen (inputfile
, "r"))) {
1998 fprintf(stderr
, "unable to open log file\n");
2001 } else if (!(stream
= (gpointer
) fopen (logfiles
[i
], "r"))) continue;
2003 if (!(stream
= (gpointer
) gzopen (logfiles
[i
], "r"))) continue;
2008 if (parser
->limit
&& (parser
->count
>= parser
->limit
)) {
2009 printf ("STATUS: aborted by limit (too many hits)\n");
2014 line
= fgets (linebuf
, linebufsize
, (FILE *)stream
);
2016 line
= mygzgets ((gzFile
)stream
, linebuf
, linebufsize
);
2021 int len
= strlen (line
);
2026 next_ctime
= parse_time (&cpos
, len
);
2032 if (next_ctime
!= ctime
) {
2040 if (ctime
< start
) continue;
2041 if (ctime
> end
) break;
2045 found
= 0; while (!isspace (*cpos
)) { cpos
++; found
= 1; }
2047 debug_error ("missing hostname", line
);
2051 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
2053 debug_error ("missing spaces after host", line
);
2057 if ((*cpos
== 'l') && !strncmp (cpos
, "last message repeated", 21)) {
2061 //printf ("LINE: %s\n", line);
2062 //const char *prog = cpos;
2065 found
= 0; while (*cpos
&& (*cpos
!= ':') && (*cpos
!= '[')) {
2066 csum_prog
= ((csum_prog
<< 8)|(csum_prog
>> 24)) + *cpos
;
2071 //idx1 = g_strndup (prog, found);
2072 //printf ("TEST:%s:%08X\n", idx1, csum_prog);
2077 found
= 0; while (isdigit (*cpos
)) {
2078 pid
= pid
*10 + *cpos
- '0';
2082 if (found
< 1 || found
> 15 || *cpos
!= ']') {
2083 debug_error ("unable to parse pid", line
);
2089 if (*cpos
++ != ':') {
2090 debug_error ("missing collon", line
);
2095 if (!isspace (*cpos
++)) {
2096 debug_error ("missing space after collon", line
);
2102 parser
->ctime
= ctime
;
2106 if (parser
->strmatch
&& STRMATCH(parser
->strmatch
, text
)) {
2110 if ((csum_prog
== PROXPROX
) ||
2111 (csum_prog
== PMG_SMTP_FILTER
)) {
2113 if ((idx1
= parse_qid (&cpos
, qidbuf
, ':', 25))) {
2117 if (!(fe
= fentry_get (parser
, idx1
))) {
2121 loglist_add (&fe
->ep
, &fe
->loglist
, line
, len
, lines
);
2123 if (strmatch
) fe
->strmatch
= 1;
2125 //fixme: BCC, Notify?
2126 //fixme: parse virus info
2127 //fixme: parse spam score
2129 if ((*cpos
== 'a') && !strncmp (cpos
, "accept mail to <", 16)) {
2131 const char *to_s
, *to_e
;
2133 to_s
= cpos
= cpos
+ 16;
2135 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2137 if (*cpos
!= '>') continue;
2143 if ((*cpos
++ != ' ') || (*cpos
++ != '(')) continue;
2145 if (!(idx1
= parse_qid (&cpos
, qidbuf
, ')', 15))) continue;
2147 // parser_track (parser, idx1, 1);
2149 fentry_tolist_add (fe
, 'A', to_s
, to_e
- to_s
, idx1
, strlen (idx1
));
2151 } else if ((*cpos
== 'm') && !strncmp (cpos
, "moved mail for <", 16)) {
2152 const char *to_s
, *to_e
;
2154 to_s
= cpos
= cpos
+ 16;
2156 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2160 if (strncmp (cpos
, "> to ", 5)) continue;
2163 if (!strncmp (cpos
, "spam", 4)) {
2165 } else if (!strncmp (cpos
, "virus", 5)) {
2171 if (strncmp (cpos
, " quarantine - ", 14)) continue;
2174 if (!(idx1
= parse_qid (&cpos
, qidbuf
, 0, 25))) continue;
2176 fentry_tolist_add (fe
, 'Q', to_s
, to_e
- to_s
, idx1
, strlen (idx1
));
2178 } else if ((*cpos
== 'b') && !strncmp (cpos
, "block mail to <", 15)) {
2182 to_s
= cpos
= cpos
+ 15;
2184 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2186 if (*cpos
!= '>') continue;
2188 fentry_tolist_add (fe
, 'B', to_s
, cpos
- to_s
, NULL
, 0);
2190 } else if ((*cpos
== 'p') && !strncmp (cpos
, "processing time: ", 17)) {
2193 sscanf (cpos
, "%f", &fe
->ptime
);
2200 } else if (csum_prog
== POSTFIX_POSTSCREEN
) {
2205 debug_error ("no pid for postscreen", line
);
2210 if ((*text
== 'N') && !strncmp (text
, "NOQUEUE: reject: RCPT from ", 27)) {
2214 if (!(idx1
= strstr (cpos
, "; client ["))) continue;
2216 const char *client
= cpos
= idx1
+ 10;
2218 while (*cpos
&& (*cpos
!= ']')) { cpos
++; }
2220 const char *client_end
= cpos
;
2222 if (!(idx1
= strstr (cpos
, "; from=<"))) continue;
2224 const char *from
= cpos
= idx1
+ 8;
2226 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2229 if ((*cpos
== '>') && strncmp (cpos
, ">, to=<", 7)) continue;
2231 const char *to
= cpos
= cpos
+ 7;
2233 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2235 if (*cpos
!= '>') continue;
2237 if (!(se
= sentry_get (parser
, pid
, ctime
, rel_line_nr
))) {
2241 if (strmatch
) se
->strmatch
= 1;
2243 loglist_add (&se
->ep
, &se
->loglist
, line
, len
, lines
);
2245 sentry_nqlist_add (se
, ctime
, from
, idx1
- from
, to
, cpos
- to
, 'N');
2247 sentry_set_connect (se
, client
, client_end
- client
);
2249 g_hash_table_remove (parser
->smtpd_h
, &se
->pid
);
2253 sentry_print (parser
, se
);
2254 sentry_free (parser
, se
);
2257 } else if (csum_prog
== POSTFIX_QMGR
) {
2259 if ((idx2
= text
) && (idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2263 if (!(qe
= qentry_get (parser
, idx1
))) {
2267 if (strmatch
) qe
->strmatch
= 1;
2271 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
, lines
);
2273 if ((*idx2
== 'f') && !strncmp (idx2
, "from=<", 6)) {
2275 cpos
= idx2
= idx2
+ 6;
2276 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2279 debug_error ("unable to parse 'from' address", line
);
2283 qentry_set_from (qe
, idx2
, cpos
- idx2
);
2287 if ((*cpos
== ',') && !strncmp (cpos
, ", size=", 7)) {
2291 while (isdigit (*cpos
)) { size
= size
*10 + *cpos
++ - '0'; }
2296 } else if ((*idx2
== 'r') && !strncmp (idx2
, "removed\n", 8)) {
2300 qentry_finalize (parser
, qe
);
2304 } else if ((csum_prog
== POSTFIX_SMTP
) ||
2305 (csum_prog
== POSTFIX_LMTP
) ||
2306 (csum_prog
== POSTFIX_LOCAL
) ||
2307 (csum_prog
== POSTFIX_ERROR
)) {
2309 int lmtp
= (csum_prog
== POSTFIX_LMTP
);
2311 if ((cpos
= text
) && (idx1
= parse_qid (&cpos
, qidbuf
, ':', 15))) {
2315 if (!(qe
= qentry_get (parser
, idx1
))) {
2319 if (strmatch
) qe
->strmatch
= 1;
2323 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
, lines
);
2325 if (strncmp (cpos
, "to=<", 4)) continue;
2328 const char *to_s
, *to_e
;
2332 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2334 if (*cpos
!= '>') continue;
2340 if (!(cpos
= strstr (cpos
, ", relay="))) continue;
2343 const char *relay_s
, *relay_e
;
2347 while (*cpos
&& (*cpos
!= ',')) { cpos
++; }
2349 if (*cpos
!= ',') continue;
2353 if (!(idx1
= strstr (cpos
+ 1, ", dsn="))) continue;
2357 if (!isdigit (*cpos
)) continue;
2359 char dstatus
= *cpos
;
2361 qentry_tolist_add (qe
, ctime
, dstatus
, to_s
, to_e
- to_s
,
2362 relay_s
, relay_e
- relay_s
);
2364 if (!lmtp
) continue; // filter always uses lmtp
2366 if (!(idx1
= strstr (cpos
+ 1, "status=sent (250 2.")))
2369 cpos
= idx1
= idx1
+ 19;
2371 if (*cpos
== '5' && (idx1
= strstr (cpos
, "5.0 OK ("))) {
2372 cpos
= idx1
= idx1
+ 8;
2373 } else if (*cpos
== '7' && (idx1
= strstr (cpos
, "7.0 BLOCKED ("))) {
2374 cpos
= idx1
= idx1
+ 13;
2379 if (!(idx1
= parse_qid (&cpos
, qidbuf
, ')', 25)))
2386 if ((fe
= g_hash_table_lookup (parser
->filter_h
, idx1
))) {
2391 } else if (csum_prog
== POSTFIX_SMTPD
) {
2395 debug_error ("no pid for smtpd", line
);
2399 if (!(se
= sentry_get (parser
, pid
, ctime
, rel_line_nr
))) {
2403 if (strmatch
) se
->strmatch
= 1;
2405 loglist_add (&se
->ep
, &se
->loglist
, line
, len
, lines
);
2407 if ((*text
== 'c') && !strncmp (text
, "connect from ", 13)) {
2409 cpos
= idx1
= text
+ 13;
2411 while (*idx1
&& !isspace (*idx1
)) { idx1
++; }
2413 sentry_set_connect (se
, cpos
, idx1
- cpos
);
2415 // fixme: do we need this?
2416 //if (strcmp (se->connect, "localhost[127.0.0.1]")) {
2417 // se->external = 1;
2420 } else if ((*text
== 'd') && !strncmp (text
, "disconnect from", 15)) {
2423 g_hash_table_remove (parser
->smtpd_h
, &se
->pid
);
2427 if (sentry_ref_rem_unneeded (parser
, se
) == 0) {
2428 sentry_print (parser
, se
);
2429 sentry_free (parser
, se
);
2431 sentry_ref_finalize (parser
, se
);
2434 } else if ((*text
== 'N') && !strncmp (text
, "NOQUEUE:", 8)) {
2438 // parse 'whatsup' (reject:)
2439 while (*cpos
&& (*cpos
!= ':')) { cpos
++; }
2440 if (*cpos
!= ':') continue;
2443 // parse '%s from %s:'
2444 while (*cpos
&& (*cpos
!= ':')) { cpos
++; }
2445 if (*cpos
!= ':') continue;
2449 while (*cpos
&& (*cpos
!= ';')) { cpos
++; }
2450 if (*cpos
!= ';') continue;
2454 *(char *)cpos
= 0; // dangerous hack: delimit string
2455 if (strstr (idx1
, ": Recipient address rejected: Service is unavailable (try later)")) {
2458 *(char *)cpos
= ';'; // dangerous hack: restore line
2460 if (!(idx1
= strstr (cpos
, "; from=<"))) continue;
2462 const char *from
= cpos
= idx1
+ 8;
2464 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2467 if ((*cpos
== '>') && strncmp (cpos
, "> to=<", 6)) continue;
2469 const char *to
= cpos
= cpos
+ 6;
2471 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2473 if (*cpos
!= '>') continue;
2475 sentry_nqlist_add (se
, ctime
, from
, idx1
- from
, to
, cpos
- to
, dstatus
);
2477 } else if ((idx2
= text
) && (idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2481 if ((qe
= qentry_get (parser
, idx1
))) {
2483 if (strmatch
) qe
->strmatch
= 1;
2485 sentry_ref_add (se
, qe
);
2487 if ((*idx2
== 'c') && !strncmp (idx2
, "client=", 7)) {
2488 cpos
= idx2
= idx2
+ 7;
2490 while (*cpos
&& !isspace (*cpos
)) { cpos
++; }
2492 qentry_set_client (qe
, idx2
, cpos
- idx2
);
2498 } else if (csum_prog
== POSTFIX_CLEANUP
) {
2503 if ((idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2504 if ((qe
= qentry_get (parser
, idx1
))) {
2506 if (strmatch
) qe
->strmatch
= 1;
2508 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
, lines
);
2510 if ((*idx2
== 'm') && !strncmp (idx2
, "message-id=", 11)) {
2512 cpos
= idx2
= idx2
+ 11;
2514 while (*cpos
&& !isspace(*cpos
)) { cpos
++; }
2516 qentry_set_msgid (qe
, idx2
, cpos
- idx2
);
2526 fclose ((FILE *)stream
);
2528 gzclose ((gzFile
)stream
);
2531 if (ctime
> end
) break;
2536 printf ("LINES: %d\n", lines
);
2537 printf ("MEM SMTPD entries: %d\n", g_hash_table_size (parser
->smtpd_h
));
2538 printf ("MEM QMGR entries: %d\n", g_hash_table_size (parser
->qmgr_h
));
2539 printf ("MEM FILTER entries: %d\n", g_hash_table_size (parser
->filter_h
));
2541 printf ("MEMDEB SMTPD entries: %d %d\n",
2542 g_hash_table_size (smtpd_debug_alloc
),
2543 g_hash_table_size (smtpd_debug_free
));
2544 printf ("MEMDEB QMGR entries: %d %d\n",
2545 g_hash_table_size (qmgr_debug_alloc
),
2546 g_hash_table_size (qmgr_debug_free
));
2547 printf ("MEMDEB FILTER entries: %d %d\n",
2548 g_hash_table_size (filter_debug_alloc
),
2549 g_hash_table_size (filter_debug_free
));
2552 g_hash_table_foreach (parser
->qmgr_h
, qentry_cleanup_hash
, parser
);
2553 g_hash_table_foreach (parser
->smtpd_h
, sentry_cleanup_hash
, parser
);
2554 g_hash_table_foreach (parser
->filter_h
, fentry_cleanup_hash
, parser
);
2557 printf ("MEMDEB SMTPD entries: %d %d\n",
2558 g_hash_table_size (smtpd_debug_alloc
),
2559 g_hash_table_size (smtpd_debug_free
));
2560 printf ("MEMDEB QMGR entries: %d %d\n",
2561 g_hash_table_size (qmgr_debug_alloc
),
2562 g_hash_table_size (qmgr_debug_free
));
2563 printf ("MEMDEB FILTER entries: %d %d\n",
2564 g_hash_table_size (filter_debug_alloc
),
2565 g_hash_table_size (filter_debug_free
));
2567 g_hash_table_foreach (smtpd_debug_alloc
, sentry_debug_alloc
, parser
);
2573 printf ("MEMMAX %d\n", ep_maxalloc
);
2576 parser_free (parser
);