]>
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 (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 (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%08lX %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 (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 (qentry
);
1251 qentry_cleanup_hash (gpointer key
,
1256 LParser
*parser
= (LParser
*)user_data
;
1258 qentry_print (parser
, qe
);
1259 qentry_free_noremove (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 (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 (fentry
);
1411 fentry_cleanup_hash (gpointer key
,
1417 fentry_free_noremove (fe
);
1425 LParser
*parser
= g_malloc0 (sizeof (LParser
));
1430 epool_init (&parser
->ep
);
1432 if (!(parser
->smtpd_h
= g_hash_table_new (g_int_hash
, g_int_equal
))) {
1436 if (!(parser
->qmgr_h
= g_hash_table_new (g_str_hash
, g_str_equal
))) {
1440 if (!(parser
->filter_h
= g_hash_table_new (g_str_hash
, g_str_equal
))) {
1444 //if (!(parser->track_h = g_hash_table_new (g_str_hash, g_str_equal))) {
1448 for (i
= 0; i
< MAX_LOGFILES
; i
++) {
1449 gettimeofday(&tv
, NULL
);
1450 tv
.tv_sec
-= 3600*24*i
;
1451 ltime
= localtime (&tv
.tv_sec
);
1452 parser
->year
[i
] = ltime
->tm_year
+ 1900;
1459 parser_free (LParser
*parser
)
1463 for (i
= 0; i
< MAX_LOGFILES
; i
++) {
1464 if (parser
->stream
[i
]) gzclose (parser
->stream
[i
]);
1467 epool_free (&parser
->ep
);
1469 g_hash_table_destroy (parser
->smtpd_h
);
1470 g_hash_table_destroy (parser
->qmgr_h
);
1471 g_hash_table_destroy (parser
->filter_h
);
1472 //g_hash_table_destroy (parser->track_h);
1479 parser_track (LParser
*parser
, const char *qid
, gboolean insert
)
1483 if ((res
= g_hash_table_lookup (parser
->track_h
, qid
))) {
1486 if (insert
&& (res
= epool_strdup (&parser
->ep
, qid
))) {
1487 g_hash_table_insert (parser
->track_h
, res
, res
);
1495 static const int linebufsize
= 8192;
1496 static int cur_year
;
1497 static int cur_month
= 0;
1498 static int cal_mtod
[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
1501 mkgmtime (struct tm
*tm
)
1505 int year
= tm
->tm_year
+ 1900;
1506 int mon
= tm
->tm_mon
;
1508 res
= (year
- 1970) * 365 + cal_mtod
[mon
];
1510 // leap year corrections (gregorian calendar)
1511 if (mon
<= 1) year
-= 1;
1512 res
+= (year
- 1968) / 4;
1513 res
-= (year
- 1900) / 100;
1514 res
+= (year
- 1600) / 400;
1516 res
+= tm
->tm_mday
- 1;
1517 res
= res
*24 + tm
->tm_hour
;
1518 res
= res
*60 + tm
->tm_min
;
1519 res
= res
*60 + tm
->tm_sec
;
1524 #define JAN (('J'<<16)|('a'<<8)|'n')
1525 #define FEB (('F'<<16)|('e'<<8)|'b')
1526 #define MAR (('M'<<16)|('a'<<8)|'r')
1527 #define APR (('A'<<16)|('p'<<8)|'r')
1528 #define MAY (('M'<<16)|('a'<<8)|'y')
1529 #define JUN (('J'<<16)|('u'<<8)|'n')
1530 #define JUL (('J'<<16)|('u'<<8)|'l')
1531 #define AUG (('A'<<16)|('u'<<8)|'g')
1532 #define SEP (('S'<<16)|('e'<<8)|'p')
1533 #define OCT (('O'<<16)|('c'<<8)|'t')
1534 #define NOV (('N'<<16)|('o'<<8)|'v')
1535 #define DEC (('D'<<16)|('e'<<8)|'c')
1538 parse_time (const char **text
, int len
)
1542 int year
= cur_year
;
1551 const char *line
= *text
;
1553 if (len
== (linebufsize
- 1)) {
1554 debug_error ("skipping long line data", line
);
1559 debug_error ("skipping short line data", line
);
1564 int csum
= (line
[0]<<16) + (line
[1]<<8) + line
[2];
1567 case JAN
: mon
= 0; break;
1568 case FEB
: mon
= 1; break;
1569 case MAR
: mon
= 2; break;
1570 case APR
: mon
= 3; break;
1571 case MAY
: mon
= 4; break;
1572 case JUN
: mon
= 5; break;
1573 case JUL
: mon
= 6; break;
1574 case AUG
: mon
= 7; break;
1575 case SEP
: mon
= 8; break;
1576 case OCT
: mon
= 9; break;
1577 case NOV
: mon
= 10; break;
1578 case DEC
: mon
= 11; break;
1580 debug_error ("unable to parse month", line
);
1584 // year change heuristik
1585 if (cur_month
== 11 && mon
== 0) {
1588 if (mon
> cur_month
) cur_month
= mon
;
1590 ltime
= (year
- 1970)*365 + cal_mtod
[mon
];
1592 // leap year corrections (gregorian calendar)
1593 if (mon
<= 1) year
-= 1;
1594 ltime
+= (year
- 1968) / 4;
1595 ltime
-= (year
- 1900) / 100;
1596 ltime
+= (year
- 1600) / 400;
1598 const char *cpos
= line
+ 3;
1600 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
1602 debug_error ("missing spaces after month", line
);
1606 found
= 0; while (isdigit (*cpos
)) { mday
= mday
*10 + *cpos
- '0'; cpos
++; found
++; }
1607 if (found
< 1 || found
> 2) {
1608 debug_error ("unable to parse day of month", line
);
1614 found
= 0; while (isspace (*cpos
)) { cpos
++; found
++; }
1616 debug_error ("missing spaces after day of month", line
);
1620 found
= 0; while (isdigit (*cpos
)) { hour
= hour
*10 + *cpos
- '0'; cpos
++; found
++; }
1621 if (found
< 1 || found
> 2) {
1622 debug_error ("unable to parse hour", line
);
1630 debug_error ("missing collon after hour", line
);
1635 found
= 0; while (isdigit (*cpos
)) { min
= min
*10 + *cpos
- '0'; cpos
++; found
++; }
1636 if (found
< 1 || found
> 2) {
1637 debug_error ("unable to parse minute", line
);
1645 debug_error ("missing collon after minute", line
);
1650 found
= 0; while (isdigit (*cpos
)) { sec
= sec
*10 + *cpos
- '0'; cpos
++; found
++; }
1651 if (found
< 1 || found
> 2) {
1652 debug_error ("unable to parse second", line
);
1659 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
1661 debug_error ("missing spaces after time", line
);
1672 parser_count_files (LParser
*parser
)
1675 time_t start
= parser
->start
;
1676 char linebuf
[linebufsize
];
1680 for (i
= 0; i
< (MAX_LOGFILES
- 1); i
++) {
1681 cur_year
= parser
->year
[i
];
1684 if ((stream
= gzopen (logfiles
[i
], "r"))) {
1685 if ((line
= gzgets (stream
, linebuf
, linebufsize
))) {
1686 if (parse_time (&line
, strlen (line
)) < start
) {
1702 parse_qid (const char **text
, char *out
, char delim
, int maxlen
)
1709 while (isxdigit (*idx
)) { *copy
++ = *idx
++; found
++; if (found
> maxlen
) break; }
1711 if (found
> 1 && found
< maxlen
&&
1712 ((delim
&& (*idx
== delim
)) || (!delim
&& isspace (*idx
)))) {
1715 while (isspace (*idx
)) idx
++;
1723 print_usage (const char *name
)
1725 fprintf (stderr
, "usage: %s [OPTIONS] [OUTPUTFILENAME]\n", name
);
1726 fprintf (stderr
, "\t-f SENDER mails from SENDER\n");
1727 fprintf (stderr
, "\t-t RECIPIENT mails to RECIPIENT\n");
1728 fprintf (stderr
, "\t-h Server Server IP or Hostname\n");
1729 fprintf (stderr
, "\t-s START start time (YYYY-MM-DD HH:MM:SS)\n");
1730 fprintf (stderr
, "\t or seconds since epoch\n");
1731 fprintf (stderr
, "\t-e END end time (YYYY-MM-DD HH:MM:SS)\n");
1732 fprintf (stderr
, "\t or seconds since epoch\n");
1733 fprintf (stderr
, "\t-m MSGID message ID (exact match)\n");
1734 fprintf (stderr
, "\t-q QID queue ID (exact match), can be\n");
1735 fprintf (stderr
, "\t specified multiple times.\n");
1736 fprintf (stderr
, "\t-x STRING search for strings\n");
1737 fprintf (stderr
, "\t-l LIMIT print max limit entries\n");
1738 fprintf (stderr
, "\t-g exclude greylist entries\n");
1739 fprintf (stderr
, "\t-n exclude NDR entries\n");
1740 fprintf (stderr
, "\t-v verbose output (no logs)\n");
1741 fprintf (stderr
, "\t-vv verbose output with logs\n");
1745 // gzgets is ways too slow, so we do it our own way
1748 mygzgetc (gzFile stream
)
1751 static char readbuf
[16384];
1752 static char *readend
= readbuf
+ sizeof (readbuf
);
1753 static char *readpos
= readbuf
+ sizeof (readbuf
);
1755 if (readpos
< readend
) return *readpos
++;
1757 if ((br
= gzread (stream
, readbuf
, sizeof (readbuf
))) <= 0) {
1761 readend
= readbuf
+ br
;
1768 mygzgets (gzFile stream
, char *line
, int bufsize
)
1774 while (--bufsize
> 0 && (c
= mygzgetc(stream
)) != -1) {
1779 if (c
== -1 && cpos
== line
)
1786 extern char *optarg
;
1787 extern int optind
, opterr
, optopt
;
1790 main (int argc
, char * const argv
[])
1792 char linebuf
[linebufsize
];
1794 char *inputfile
= NULL
;
1802 unsigned long lines
= 0;
1803 unsigned long rel_line_nr
= 0;
1809 time_t ctime
, next_ctime
, start
, end
;
1815 smtpd_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1816 qmgr_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1817 filter_debug_alloc
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1818 smtpd_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1819 qmgr_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1820 filter_debug_free
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1823 if (!(parser
= parser_new ())) {
1824 fprintf (stderr
, "unable to alloc parser structure\n");
1828 while ((opt
= getopt (argc
, argv
, "f:t:s:e:h:m:q:x:i:l:vgn")) != -1) {
1830 parser
->from
= epool_strdup (&parser
->ep
, optarg
);
1831 } else if (opt
== 't') {
1832 parser
->to
= epool_strdup (&parser
->ep
, optarg
);
1833 } else if (opt
== 'v') {
1834 parser
->verbose
+= 1;
1835 } else if (opt
== 'g') {
1836 parser
->exclude_greylist
= 1;
1837 } else if (opt
== 'n') {
1838 parser
->exclude_ndrs
= 1;
1839 } else if (opt
== 'h') {
1840 parser
->server
= epool_strdup (&parser
->ep
, optarg
);
1841 } else if (opt
== 'm') {
1842 parser
->msgid
= epool_strdup (&parser
->ep
, optarg
);
1843 } else if (opt
== 'q') {
1845 unsigned long rel_line_nr
;
1846 MatchList
*match
= (MatchList
*)epool_alloc(&parser
->ep
, sizeof(MatchList
));
1847 if (sscanf(optarg
, "T%08lXL%08lX", <ime
, &rel_line_nr
) == 2) {
1848 match
->mtype
= MatchTypeRelLineNr
;
1849 match
->ltime
= ltime
;
1850 match
->rel_line_nr
= rel_line_nr
;
1851 match
->next
= parser
->match_list
;
1852 parser
->match_list
= match
;
1854 match
->mtype
= MatchTypeQID
;
1855 match
->id
= epool_strdup(&parser
->ep
, optarg
);
1856 match
->next
= parser
->match_list
;
1857 parser
->match_list
= match
;
1859 } else if (opt
== 'x') {
1860 parser
->strmatch
= epool_strdup (&parser
->ep
, optarg
);
1861 } else if (opt
== 'i') {
1863 } else if (opt
== 'l') {
1865 parser
->limit
= strtoul (optarg
, &l
, 0);
1866 if (!*optarg
|| *l
) {
1867 fprintf (stderr
, "unable to parse limit '%s'\n", optarg
);
1870 } else if (opt
== 's') {
1871 // use strptime to convert time
1874 if ((!(res
= strptime (optarg
, "%F %T", &tm
)) &&
1875 !(res
= strptime (optarg
, "%s", &tm
))) || *res
) {
1876 fprintf (stderr
, "unable to parse start time\n");
1879 parser
->start
= mkgmtime (&tm
);
1881 } else if (opt
== 'e') {
1884 if ((!(res
= strptime (optarg
, "%F %T", &tm
)) &&
1885 !(res
= strptime (optarg
, "%s", &tm
))) || *res
) {
1886 fprintf (stderr
, "unable to parse end time\n");
1889 parser
->end
= mkgmtime (&tm
);
1892 print_usage (argv
[0]);
1897 if (optind
< argc
) {
1899 if ((argc
- optind
) > 1) {
1900 print_usage (argv
[0]);
1904 char *tmpfn
= g_strdup_printf ("/tmp/.proxtrack-%08X.txt", getpid ());
1906 if ((stdout
= freopen (tmpfn
, "w", stdout
)) == NULL
) {
1907 perror ("unable to open output file");
1910 if (rename (tmpfn
, argv
[optind
]) != 0) {
1911 perror ("unable to open output file");
1917 // we use gmtime exerywhere to speedup things, this can cause problems
1918 // when daylight saving time changes
1920 gettimeofday(&tv
, NULL
);
1921 ltime
= localtime (&tv
.tv_sec
);
1923 if (!parser
->start
) {
1927 parser
->start
= mkgmtime (ltime
);
1930 ltime
= localtime (&tv
.tv_sec
);
1933 parser
->end
= mkgmtime (ltime
);
1936 if (parser
->end
< parser
->start
) {
1937 fprintf (stderr
, "end time before start time\n");
1944 } else if ((filecount
= parser_count_files (parser
)) <= 0) {
1945 fprintf (stderr
, "unable to access log files\n");
1949 printf ("# LogReader: %d\n", getpid());
1951 printf ("# Query options\n");
1952 if (parser
->from
) printf ("# Sender: %s\n", parser
->from
);
1953 if (parser
->to
) printf ("# Recipient: %s\n", parser
->to
);
1954 if (parser
->server
) printf ("# Server: %s\n", parser
->server
);
1955 if (parser
->msgid
) printf ("# MsgID: %s\n", parser
->msgid
);
1957 MatchList
*match
= parser
->match_list
;
1959 if (match
->mtype
== MatchTypeQID
) {
1960 printf ("# QID: %s\n", match
->id
);
1961 } else if (match
->mtype
== MatchTypeRelLineNr
) {
1962 printf ("# QID: T%08lXL%08lX\n", match
->ltime
, match
->rel_line_nr
);
1964 g_error("internal error - unknown match type %d\n", match
->mtype
);
1966 match
= match
->next
;
1969 if (parser
->strmatch
) printf ("# Match: %s\n", parser
->strmatch
);
1971 strftime (linebuf
, 256, "%F %T", gmtime (&parser
->start
));
1972 printf ("# Start: %s (%lu)\n", linebuf
, parser
->start
);
1973 strftime (linebuf
, 256, "%F %T", gmtime (&parser
->end
));
1974 printf ("# END: %s (%lu)\n", linebuf
, parser
->end
);
1975 printf ("# End Query Options\n\n");
1979 start
= parser
->start
;
1983 for (i
= filecount
- 1; i
>= 0; i
--) {
1986 // syslog files does not conain years, so we must compute then
1987 // cur_year is the assumed start year
1990 cur_year
= parser
->year
[i
];
1993 if (inputfile
&& strlen(inputfile
) == 1 && *inputfile
== '-') {
1994 stream
= (gpointer
) stdin
;
1995 } else if (inputfile
) {
1996 if (!(stream
= (gpointer
) fopen (inputfile
, "r"))) {
1997 fprintf(stderr
, "unable to open log file\n");
2000 } else if (!(stream
= (gpointer
) fopen (logfiles
[i
], "r"))) continue;
2002 if (!(stream
= (gpointer
) gzopen (logfiles
[i
], "r"))) continue;
2007 if (parser
->limit
&& (parser
->count
>= parser
->limit
)) {
2008 printf ("STATUS: aborted by limit (too many hits)\n");
2013 line
= fgets (linebuf
, linebufsize
, (FILE *)stream
);
2015 line
= mygzgets ((gzFile
)stream
, linebuf
, linebufsize
);
2020 int len
= strlen (line
);
2025 next_ctime
= parse_time (&cpos
, len
);
2031 if (next_ctime
!= ctime
) {
2039 if (ctime
< start
) continue;
2040 if (ctime
> end
) break;
2044 found
= 0; while (!isspace (*cpos
)) { cpos
++; found
= 1; }
2046 debug_error ("missing hostname", line
);
2050 found
= 0; while (isspace (*cpos
)) { cpos
++; found
= 1; }
2052 debug_error ("missing spaces after host", line
);
2056 if ((*cpos
== 'l') && !strncmp (cpos
, "last message repeated", 21)) {
2060 //printf ("LINE: %s\n", line);
2061 //const char *prog = cpos;
2064 found
= 0; while (*cpos
&& (*cpos
!= ':') && (*cpos
!= '[')) {
2065 csum_prog
= ((csum_prog
<< 8)|(csum_prog
>> 24)) + *cpos
;
2070 //idx1 = g_strndup (prog, found);
2071 //printf ("TEST:%s:%08X\n", idx1, csum_prog);
2076 found
= 0; while (isdigit (*cpos
)) {
2077 pid
= pid
*10 + *cpos
- '0';
2081 if (found
< 1 || found
> 15 || *cpos
!= ']') {
2082 debug_error ("unable to parse pid", line
);
2088 if (*cpos
++ != ':') {
2089 debug_error ("missing collon", line
);
2094 if (!isspace (*cpos
++)) {
2095 debug_error ("missing space after collon", line
);
2101 parser
->ctime
= ctime
;
2105 if (parser
->strmatch
&& STRMATCH(parser
->strmatch
, text
)) {
2109 if ((csum_prog
== PROXPROX
) ||
2110 (csum_prog
== PMG_SMTP_FILTER
)) {
2112 if ((idx1
= parse_qid (&cpos
, qidbuf
, ':', 25))) {
2116 if (!(fe
= fentry_get (parser
, idx1
))) {
2120 loglist_add (&fe
->ep
, &fe
->loglist
, line
, len
, lines
);
2122 if (strmatch
) fe
->strmatch
= 1;
2124 //fixme: BCC, Notify?
2125 //fixme: parse virus info
2126 //fixme: parse spam score
2128 if ((*cpos
== 'a') && !strncmp (cpos
, "accept mail to <", 16)) {
2130 const char *to_s
, *to_e
;
2132 to_s
= cpos
= cpos
+ 16;
2134 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2136 if (*cpos
!= '>') continue;
2142 if ((*cpos
++ != ' ') || (*cpos
++ != '(')) continue;
2144 if (!(idx1
= parse_qid (&cpos
, qidbuf
, ')', 15))) continue;
2146 // parser_track (parser, idx1, 1);
2148 fentry_tolist_add (fe
, 'A', to_s
, to_e
- to_s
, idx1
, strlen (idx1
));
2150 } else if ((*cpos
== 'm') && !strncmp (cpos
, "moved mail for <", 16)) {
2151 const char *to_s
, *to_e
;
2153 to_s
= cpos
= cpos
+ 16;
2155 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2159 if (strncmp (cpos
, "> to ", 5)) continue;
2162 if (!strncmp (cpos
, "spam", 4)) {
2164 } else if (!strncmp (cpos
, "virus", 5)) {
2170 if (strncmp (cpos
, " quarantine - ", 14)) continue;
2173 if (!(idx1
= parse_qid (&cpos
, qidbuf
, 0, 25))) continue;
2175 fentry_tolist_add (fe
, 'Q', to_s
, to_e
- to_s
, idx1
, strlen (idx1
));
2177 } else if ((*cpos
== 'b') && !strncmp (cpos
, "block mail to <", 15)) {
2181 to_s
= cpos
= cpos
+ 15;
2183 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2185 if (*cpos
!= '>') continue;
2187 fentry_tolist_add (fe
, 'B', to_s
, cpos
- to_s
, NULL
, 0);
2189 } else if ((*cpos
== 'p') && !strncmp (cpos
, "processing time: ", 17)) {
2192 sscanf (cpos
, "%f", &fe
->ptime
);
2199 } else if (csum_prog
== POSTFIX_POSTSCREEN
) {
2204 debug_error ("no pid for postscreen", line
);
2209 if ((*text
== 'N') && !strncmp (text
, "NOQUEUE: reject: RCPT from ", 27)) {
2213 if (!(idx1
= strstr (cpos
, "; client ["))) continue;
2215 const char *client
= cpos
= idx1
+ 10;
2217 while (*cpos
&& (*cpos
!= ']')) { cpos
++; }
2219 const char *client_end
= cpos
;
2221 if (!(idx1
= strstr (cpos
, "; from=<"))) continue;
2223 const char *from
= cpos
= idx1
+ 8;
2225 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2228 if ((*cpos
== '>') && strncmp (cpos
, ">, to=<", 7)) continue;
2230 const char *to
= cpos
= cpos
+ 7;
2232 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2234 if (*cpos
!= '>') continue;
2236 if (!(se
= sentry_get (parser
, pid
, ctime
, rel_line_nr
))) {
2240 if (strmatch
) se
->strmatch
= 1;
2242 loglist_add (&se
->ep
, &se
->loglist
, line
, len
, lines
);
2244 sentry_nqlist_add (se
, ctime
, from
, idx1
- from
, to
, cpos
- to
, 'N');
2246 sentry_set_connect (se
, client
, client_end
- client
);
2248 g_hash_table_remove (parser
->smtpd_h
, &se
->pid
);
2252 sentry_print (parser
, se
);
2253 sentry_free (parser
, se
);
2256 } else if (csum_prog
== POSTFIX_QMGR
) {
2258 if ((idx2
= text
) && (idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2262 if (!(qe
= qentry_get (parser
, idx1
))) {
2266 if (strmatch
) qe
->strmatch
= 1;
2270 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
, lines
);
2272 if ((*idx2
== 'f') && !strncmp (idx2
, "from=<", 6)) {
2274 cpos
= idx2
= idx2
+ 6;
2275 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2278 debug_error ("unable to parse 'from' address", line
);
2282 qentry_set_from (qe
, idx2
, cpos
- idx2
);
2286 if ((*cpos
== ',') && !strncmp (cpos
, ", size=", 7)) {
2290 while (isdigit (*cpos
)) { size
= size
*10 + *cpos
++ - '0'; }
2295 } else if ((*idx2
== 'r') && !strncmp (idx2
, "removed\n", 8)) {
2299 qentry_finalize (parser
, qe
);
2303 } else if ((csum_prog
== POSTFIX_SMTP
) ||
2304 (csum_prog
== POSTFIX_LMTP
) ||
2305 (csum_prog
== POSTFIX_LOCAL
) ||
2306 (csum_prog
== POSTFIX_ERROR
)) {
2308 int lmtp
= (csum_prog
== POSTFIX_LMTP
);
2310 if ((cpos
= text
) && (idx1
= parse_qid (&cpos
, qidbuf
, ':', 15))) {
2314 if (!(qe
= qentry_get (parser
, idx1
))) {
2318 if (strmatch
) qe
->strmatch
= 1;
2322 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
, lines
);
2324 if (strncmp (cpos
, "to=<", 4)) continue;
2327 const char *to_s
, *to_e
;
2331 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2333 if (*cpos
!= '>') continue;
2339 if (!(cpos
= strstr (cpos
, ", relay="))) continue;
2342 const char *relay_s
, *relay_e
;
2346 while (*cpos
&& (*cpos
!= ',')) { cpos
++; }
2348 if (*cpos
!= ',') continue;
2352 if (!(idx1
= strstr (cpos
+ 1, ", dsn="))) continue;
2356 if (!isdigit (*cpos
)) continue;
2358 char dstatus
= *cpos
;
2360 qentry_tolist_add (qe
, ctime
, dstatus
, to_s
, to_e
- to_s
,
2361 relay_s
, relay_e
- relay_s
);
2363 if (!lmtp
) continue; // filter always uses lmtp
2365 if (!(idx1
= strstr (cpos
+ 1, "status=sent (250 2.")))
2368 cpos
= idx1
= idx1
+ 19;
2370 if (*cpos
== '5' && (idx1
= strstr (cpos
, "5.0 OK ("))) {
2371 cpos
= idx1
= idx1
+ 8;
2372 } else if (*cpos
== '7' && (idx1
= strstr (cpos
, "7.0 BLOCKED ("))) {
2373 cpos
= idx1
= idx1
+ 13;
2378 if (!(idx1
= parse_qid (&cpos
, qidbuf
, ')', 25)))
2385 if ((fe
= g_hash_table_lookup (parser
->filter_h
, idx1
))) {
2390 } else if (csum_prog
== POSTFIX_SMTPD
) {
2394 debug_error ("no pid for smtpd", line
);
2398 if (!(se
= sentry_get (parser
, pid
, ctime
, rel_line_nr
))) {
2402 if (strmatch
) se
->strmatch
= 1;
2404 loglist_add (&se
->ep
, &se
->loglist
, line
, len
, lines
);
2406 if ((*text
== 'c') && !strncmp (text
, "connect from ", 13)) {
2408 cpos
= idx1
= text
+ 13;
2410 while (*idx1
&& !isspace (*idx1
)) { idx1
++; }
2412 sentry_set_connect (se
, cpos
, idx1
- cpos
);
2414 // fixme: do we need this?
2415 //if (strcmp (se->connect, "localhost[127.0.0.1]")) {
2416 // se->external = 1;
2419 } else if ((*text
== 'd') && !strncmp (text
, "disconnect from", 15)) {
2422 g_hash_table_remove (parser
->smtpd_h
, &se
->pid
);
2426 if (sentry_ref_rem_unneeded (parser
, se
) == 0) {
2427 sentry_print (parser
, se
);
2428 sentry_free (parser
, se
);
2430 sentry_ref_finalize (parser
, se
);
2433 } else if ((*text
== 'N') && !strncmp (text
, "NOQUEUE:", 8)) {
2437 // parse 'whatsup' (reject:)
2438 while (*cpos
&& (*cpos
!= ':')) { cpos
++; }
2439 if (*cpos
!= ':') continue;
2442 // parse '%s from %s:'
2443 while (*cpos
&& (*cpos
!= ':')) { cpos
++; }
2444 if (*cpos
!= ':') continue;
2448 while (*cpos
&& (*cpos
!= ';')) { cpos
++; }
2449 if (*cpos
!= ';') continue;
2453 *(char *)cpos
= 0; // dangerous hack: delimit string
2454 if (strstr (idx1
, ": Recipient address rejected: Service is unavailable (try later)")) {
2457 *(char *)cpos
= ';'; // dangerous hack: restore line
2459 if (!(idx1
= strstr (cpos
, "; from=<"))) continue;
2461 const char *from
= cpos
= idx1
+ 8;
2463 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2466 if ((*cpos
== '>') && strncmp (cpos
, "> to=<", 6)) continue;
2468 const char *to
= cpos
= cpos
+ 6;
2470 while (*cpos
&& (*cpos
!= '>')) { cpos
++; }
2472 if (*cpos
!= '>') continue;
2474 sentry_nqlist_add (se
, ctime
, from
, idx1
- from
, to
, cpos
- to
, dstatus
);
2476 } else if ((idx2
= text
) && (idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2480 if ((qe
= qentry_get (parser
, idx1
))) {
2482 if (strmatch
) qe
->strmatch
= 1;
2484 sentry_ref_add (se
, qe
);
2486 if ((*idx2
== 'c') && !strncmp (idx2
, "client=", 7)) {
2487 cpos
= idx2
= idx2
+ 7;
2489 while (*cpos
&& !isspace (*cpos
)) { cpos
++; }
2491 qentry_set_client (qe
, idx2
, cpos
- idx2
);
2497 } else if (csum_prog
== POSTFIX_CLEANUP
) {
2502 if ((idx1
= parse_qid (&idx2
, qidbuf
, ':', 15))) {
2503 if ((qe
= qentry_get (parser
, idx1
))) {
2505 if (strmatch
) qe
->strmatch
= 1;
2507 loglist_add (&qe
->ep
, &qe
->loglist
, line
, len
, lines
);
2509 if ((*idx2
== 'm') && !strncmp (idx2
, "message-id=", 11)) {
2511 cpos
= idx2
= idx2
+ 11;
2513 while (*cpos
&& !isspace(*cpos
)) { cpos
++; }
2515 qentry_set_msgid (qe
, idx2
, cpos
- idx2
);
2525 fclose ((FILE *)stream
);
2527 gzclose ((gzFile
)stream
);
2530 if (ctime
> end
) break;
2535 printf ("LINES: %d\n", lines
);
2536 printf ("MEM SMTPD entries: %d\n", g_hash_table_size (parser
->smtpd_h
));
2537 printf ("MEM QMGR entries: %d\n", g_hash_table_size (parser
->qmgr_h
));
2538 printf ("MEM FILTER entries: %d\n", g_hash_table_size (parser
->filter_h
));
2540 printf ("MEMDEB SMTPD entries: %d %d\n",
2541 g_hash_table_size (smtpd_debug_alloc
),
2542 g_hash_table_size (smtpd_debug_free
));
2543 printf ("MEMDEB QMGR entries: %d %d\n",
2544 g_hash_table_size (qmgr_debug_alloc
),
2545 g_hash_table_size (qmgr_debug_free
));
2546 printf ("MEMDEB FILTER entries: %d %d\n",
2547 g_hash_table_size (filter_debug_alloc
),
2548 g_hash_table_size (filter_debug_free
));
2551 g_hash_table_foreach (parser
->qmgr_h
, qentry_cleanup_hash
, parser
);
2552 g_hash_table_foreach (parser
->smtpd_h
, sentry_cleanup_hash
, parser
);
2553 g_hash_table_foreach (parser
->filter_h
, fentry_cleanup_hash
, parser
);
2556 printf ("MEMDEB SMTPD entries: %d %d\n",
2557 g_hash_table_size (smtpd_debug_alloc
),
2558 g_hash_table_size (smtpd_debug_free
));
2559 printf ("MEMDEB QMGR entries: %d %d\n",
2560 g_hash_table_size (qmgr_debug_alloc
),
2561 g_hash_table_size (qmgr_debug_free
));
2562 printf ("MEMDEB FILTER entries: %d %d\n",
2563 g_hash_table_size (filter_debug_alloc
),
2564 g_hash_table_size (filter_debug_free
));
2566 g_hash_table_foreach (smtpd_debug_alloc
, sentry_debug_alloc
, parser
);
2572 printf ("MEMMAX %d\n", ep_maxalloc
);
2575 parser_free (parser
);