]> git.proxmox.com Git - pmg-log-tracker.git/blame - pmg-log-tracker.c
re-add support for old log (for testing)
[pmg-log-tracker.git] / pmg-log-tracker.c
CommitLineData
39ec53c5
DM
1/*
2
5db26988 3 (C) 2007-2017 Proxmox Server Solutions GmbH, All Rights Reserved
39ec53c5 4
5db26988 5 Proxmox Mail Tracker
39ec53c5 6
5db26988
DM
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.
11
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.
16
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/>.
19
20 Author: Dietmar Maurer <dietmar@proxmox.com>
21
22 See http://www.proxmox.com for more information
39ec53c5
DM
23
24*/
25
26#define _GNU_SOURCE
27
28#include <stdlib.h>
29#include <stdio.h>
30#include <glib.h>
31#include <string.h>
32#include <ctype.h>
33#include <sys/time.h>
34#include <time.h>
35#include <unistd.h>
36#include <sys/types.h>
37#include <zlib.h>
38#include <fnmatch.h>
39
40/*
41 We assume the syslog files belong to one host, i.e. we do not
42 consider the hostname at all
43*/
44
45/*
46 REGEX: we do not use posix regular expressions (libc implementation
47 is too slow). fnmatch() is also slow.
48 Future versions may use our own implementation of a DFA. But currently we
49 just use strstr (no regex support at all)
50*/
51
52//#define STRMATCH(pattern, string) (fnmatch (pattern, string, FNM_CASEFOLD) == 0)
53#define STRMATCH(pattern, string) (strcasestr (string, pattern) != NULL)
54
55
56//#define EPOOL_BLOCK_SIZE 512
57//#define EPOOL_MAX_SIZE 128
58#define EPOOL_BLOCK_SIZE 2048
59#define EPOOL_MAX_SIZE 128
60#define MAX_LOGFILES 31
61//#define EPOOL_DEBUG
62//#define DEBUG
63
64typedef struct _SList SList;
65struct _SList {
66 gpointer data;
67 SList *next;
68};
69
70typedef struct _NQList NQList;
71struct _NQList {
72 char *from;
73 char *to;
74 char dstatus;
75 time_t ltime;
76 NQList *next;
77};
78
79typedef struct _TOList TOList;
80struct _TOList {
81 char *to;
82 char *relay;
83 char dstatus;
84 time_t ltime;
85 TOList *next;
86};
87
88#ifdef DEBUG
89 GHashTable *smtpd_debug_alloc;
90 GHashTable *qmgr_debug_alloc;
91 GHashTable *filter_debug_alloc;
92 GHashTable *smtpd_debug_free;
93 GHashTable *qmgr_debug_free;
94 GHashTable *filter_debug_free;
95#endif
96
97// EPool: Entry related memory pools
98
39ec53c5
DM
99
100#ifdef EPOOL_DEBUG
101int ep_allocated;
102int ep_maxalloc;
103#endif
104
105typedef struct _EPool EPool;
106struct _EPool {
f6146391 107 SList *blocks; // allocated usiing g_slice_alloc(EPOOL_BLOCK_SIZE)
39ec53c5
DM
108 SList *mblocks; // allocated use g_malloc
109 int cpos;
110#ifdef EPOOL_DEBUG
111 int allocated;
112#endif
113};
114
115typedef struct {
116 EPool ep;
117 GHashTable *smtpd_h;
118 GHashTable *qmgr_h;
119 GHashTable *filter_h;
120 //GHashTable *track_h;
53616b4f 121 gzFile stream[MAX_LOGFILES];
39ec53c5
DM
122 char *from;
123 char *to;
124 time_t year[MAX_LOGFILES];
125 time_t start;
126 time_t end;
127 time_t ctime;
128 char *server;
129 char *msgid;
130 char *qid;
131 char *strmatch;
132 unsigned long limit;
133 unsigned long count;
134 int verbose;
135 unsigned int exclude_greylist:1;
136 unsigned int exclude_ndrs:1;
137} LParser;
138
139typedef struct _SEntry SEntry;
140typedef struct _QEntry QEntry;
141typedef struct _FEntry FEntry;
142
143typedef struct _LogEntry LogEntry;
144typedef struct _LogList LogList;
145
146struct _LogEntry {
147 const char *text;
148 LogEntry *next;
149};
150
151struct _LogList {
152 LogEntry *log;
153 LogEntry *logs_last; // pointer to last log (speedup add log)
154};
155
156// SEntry: Store SMTPD related logs
157
158struct _SEntry {
159
160 EPool ep;
161 LogList loglist;
162
163 int pid;
164
165 SList *refs;
166
167 NQList *nqlist;
168
169 char *connect;
170
171 //unsigned int external:1; // not from local host
172 unsigned int disconnect:1;
173 unsigned int strmatch:1;
174
175};
176
177// QEntry: Store Queue (qmgr, smtp, lmtp) related logs
178
179struct _QEntry {
180
181 EPool ep;
182 LogList loglist;
183
184 char *qid;
185
186 SEntry *smtpd;
187 FEntry *filter;
188
189 TOList *tolist;
190
191 unsigned int size;
192 char *from;
193 char *client;
194 char *msgid;
195
196 unsigned int cleanup:1;
197 unsigned int removed:1;
198 unsigned int filtered:1; // set when passed via lmtp to filter
199 unsigned int strmatch:1;
200};
201
202// FEntry: Store filter (proxprox) related logs
203
204struct _FEntry {
205
206 EPool ep;
207 LogList loglist;
208
209 char *logid; // proxprox log id
210
211 TOList *tolist;
212
213 float ptime;
214
215 unsigned int finished:1;
216 unsigned int strmatch:1;
217};
218
219// Prototypes
220void debug_error (char *msg, const char *line);
39ec53c5
DM
221
222EPool *epool_init (EPool *ep);
223void epool_free (EPool *ep);
224gpointer epool_alloc (EPool *ep, int size);
225char *epool_strndup (EPool *ep, const char *s, int len);
226char *epool_strndup0 (EPool *ep, const char *s, int len);
227char *epool_strdup (EPool *ep, const char *s);
228
229void loglist_print (LogList *loglist);
230void loglist_add (EPool *ep, LogList *loglist, const char *text, int len);
231
232SEntry *sentry_new (int pid);
233SEntry *sentry_get (LParser *parser, int pid);
234void sentry_ref_add (SEntry *sentry, QEntry *qentry);
235int sentry_ref_del (SEntry *sentry, QEntry *qentry);
236void sentry_ref_finalize (LParser *parser, SEntry *sentry);
237int sentry_ref_rem_unneeded (LParser *parser, SEntry *sentry);
238void sentry_nqlist_add (SEntry *sentry, time_t ltime, const char *from, int from_len,
239 const char *to, int to_len, char dstatus);
240void sentry_print (LParser *parser, SEntry *sentry);
241void sentry_set_connect (SEntry *sentry, const char *connect, int len);
242void sentry_free_noremove (SEntry *sentry);
243void sentry_free (LParser *parser, SEntry *sentry);
244void sentry_cleanup_hash (gpointer key, gpointer value, gpointer user_data);
245
246
247QEntry *qentry_new (const char *qid);
248QEntry *qentry_get (LParser *parser, const char *qid);
249void qentry_tolist_add (QEntry *qentry, time_t ltime, char dstatus, const char *to,
250 int to_len, const char *relay, int relay_len);
251void qentry_set_from (QEntry *qentry, const char *from, int len);
252void qentry_set_msgid (QEntry *qentry, const char *msgid, int len);
253void qentry_set_client (QEntry *qentry, const char *client, int len);
254void qentry_print (LParser *parser, QEntry *qentry);
255void qentry_finalize (LParser *parser, QEntry *qentry);
256void qentry_free_noremove (LParser *parser, QEntry *qentry);
257void qentry_free (LParser *parser, QEntry *qentry);
258void qentry_cleanup_hash (gpointer key, gpointer value, gpointer user_data);
259
260
261FEntry *fentry_new (const char *logid);
262FEntry *fentry_get (LParser *parser, const char *logid);
263void fentry_tolist_add (FEntry *fentry, char dstatus, const char *to,
264 int to_len, const char *qid, int qid_len);
265void fentry_free_noremove (LParser *parser, FEntry *fentry);
266void fentry_free (LParser *parser, FEntry *fentry);
267void fentry_cleanup_hash (gpointer key, gpointer value, gpointer user_data);
268
269LParser *parser_new ();
270void parser_free (LParser *parser);
271
272//char *parser_track (LParser *parser, const char *qid, gboolean insert);
273
274// Implementations
275
276//#define LOGPATH "./log/"
277#define LOGPATH "/var/log/"
bf472949 278//#define LOGPATH "/root/testlog/"
39ec53c5
DM
279
280static const char *logfiles[] = {
281 LOGPATH "syslog",
282 LOGPATH "syslog.1",
283 LOGPATH "syslog.2.gz",
284 LOGPATH "syslog.3.gz",
285 LOGPATH "syslog.4.gz",
286 LOGPATH "syslog.5.gz",
287 LOGPATH "syslog.6.gz",
288 LOGPATH "syslog.7.gz",
289 LOGPATH "syslog.8.gz",
290 LOGPATH "syslog.9.gz",
291 LOGPATH "syslog.10.gz",
292 LOGPATH "syslog.11.gz",
293 LOGPATH "syslog.12.gz",
294 LOGPATH "syslog.13.gz",
295 LOGPATH "syslog.14.gz",
296 LOGPATH "syslog.15.gz",
297 LOGPATH "syslog.16.gz",
298 LOGPATH "syslog.17.gz",
299 LOGPATH "syslog.18.gz",
300 LOGPATH "syslog.19.gz",
301 LOGPATH "syslog.20.gz",
302 LOGPATH "syslog.21.gz",
303 LOGPATH "syslog.22.gz",
304 LOGPATH "syslog.23.gz",
305 LOGPATH "syslog.24.gz",
306 LOGPATH "syslog.25.gz",
307 LOGPATH "syslog.26.gz",
308 LOGPATH "syslog.27.gz",
309 LOGPATH "syslog.28.gz",
310 LOGPATH "syslog.29.gz",
311 LOGPATH "syslog.30.gz",
312 LOGPATH "syslog.31.gz",
313};
314
315void
316debug_error (char *msg, const char *line)
317{
318#ifdef DEBUG
319 fprintf (stderr, "ERROR: %s\n", msg);
320 if (line) fprintf (stderr, "LINE: %s\n", line);
321
322 G_BREAKPOINT();
323
324 exit (-1);
325#endif
326}
327
39ec53c5
DM
328EPool *
329epool_init (EPool *ep)
330{
331 gpointer data;
332 SList *blocks;
333
f6146391 334 data = g_slice_alloc0(EPOOL_BLOCK_SIZE);
39ec53c5
DM
335 blocks = (SList *)data;
336
337#ifdef EPOOL_DEBUG
338 ep->allocated += EPOOL_BLOCK_SIZE;
339 ep_allocated += EPOOL_BLOCK_SIZE;
340 ep_maxalloc = (ep_allocated > ep_maxalloc) ? ep_allocated : ep_maxalloc;
341#endif
342
343
344 blocks->data = data;
345 blocks->next = NULL;
346
347 ep->blocks = blocks;
348 ep->cpos = sizeof (SList);
349
350 return ep;
351}
352
353void
354epool_free (EPool *ep)
355{
356 SList *l;
357 gpointer data;
358
359#ifdef EPOOL_DEBUG
360 ep_allocated -= ep->allocated;
361 printf ("MEM: %d\n", ep_allocated);
362#endif
363
364#ifdef DEBUG
365 return;
366#endif
367
368 l = ep->mblocks;
369 while (l) {
370 data = l->data;
371 l = l->next;
372 g_free (data);
373 }
374
375 l = ep->blocks;
376 while (l) {
377 data = l->data;
378 l = l->next;
f6146391 379 g_slice_free1(EPOOL_BLOCK_SIZE, data);
39ec53c5
DM
380 }
381}
382
383gpointer
384epool_alloc (EPool *ep, int size)
385{
386 int rs = (size + 3) & ~3;
387 int space = EPOOL_BLOCK_SIZE - sizeof (SList) - ep->cpos;
388 gpointer data;
389
390 if (size > EPOOL_MAX_SIZE) {
391 SList *blocks;
392 if (space >= sizeof (SList)) {
393 blocks = ep->blocks->data + ep->cpos;
394 ep->cpos += sizeof (SList);
395 } else {
396 blocks = (SList *)epool_alloc (ep, sizeof (SList));
397 }
398
399 data = g_malloc (size);
400#ifdef EPOOL_DEBUG
401 ep->allocated += size;
402 ep_allocated += size;
403 ep_maxalloc = (ep_allocated > ep_maxalloc) ? ep_allocated : ep_maxalloc;
404#endif
405 blocks->data = data;
406 blocks->next = ep->mblocks;
407
408 ep->mblocks = blocks;
409
410 return data;
411
412 } else if (space >= rs) {
413 data = ep->blocks->data + ep->cpos;
414 ep->cpos += rs;
415
416 return data;
417
418 } else {
419 SList *blocks = ep->blocks->data + ep->cpos;
420
f6146391 421 data = g_slice_alloc0 (EPOOL_BLOCK_SIZE);
39ec53c5
DM
422 blocks->data = data;
423 blocks->next = ep->blocks;
424
425 ep->blocks = blocks;
426 ep->cpos = rs;
427
428#ifdef EPOOL_DEBUG
429 ep->allocated += EPOOL_BLOCK_SIZE;
430 ep_allocated += EPOOL_BLOCK_SIZE;
431 ep_maxalloc = (ep_allocated > ep_maxalloc) ? ep_allocated : ep_maxalloc;
432#endif
433
434 return data;
435 }
436}
437
438char *
439epool_strndup (EPool *ep, const char *s, int len)
440{
441 int l = len + 1;
442 char *res = epool_alloc (ep, l);
443 strncpy (res, s, l);
444 return res;
445}
446
447char *
448epool_strndup0 (EPool *ep, const char *s, int len)
449{
450 char *res = epool_alloc (ep, len + 1);
451 strncpy (res, s, len);
452 res[len] = 0;
453
454 return res;
455}
456
457char *
458epool_strdup (EPool *ep, const char *s)
459{
460 int l = strlen (s) + 1;
461 char *res = epool_alloc (ep, l);
462 strncpy (res, s, l);
463 return res;
464}
465
466void
467loglist_print (LogList *loglist)
468{
469 LogEntry *log = loglist->log;
470 while (log) {
471 printf ("%s", log->text);
472 log = log->next;
473 }
474}
475
476
477void
478loglist_add (EPool *ep, LogList *loglist, const char *text, int len)
479{
480 LogEntry *log;
481
482#ifdef DEBUG
483 if (len != strlen (text)) {
484 debug_error ("string with wrong len", NULL);
485 }
486#endif
487 if (text[len] != 0) {
488 debug_error ("string is not null terminated", NULL);
489 return;
490 }
491
492 log = epool_alloc (ep, sizeof (LogEntry));
493
494 log->text = epool_strndup (ep, text, len);
495 log->next = NULL;
496
497 if (loglist->logs_last) {
498 loglist->logs_last->next = log;
499 loglist->logs_last = log;
500 } else {
501 loglist->log = log;
502 loglist->logs_last = log;
503 }
504
505 return;
506}
507
508SEntry*
509sentry_new (int pid)
510{
511 SEntry *sentry;
512 SList *blocks;
513 int cpos;
514
f6146391 515 sentry = (SEntry *)g_slice_alloc0(EPOOL_BLOCK_SIZE);
39ec53c5
DM
516 sentry->pid = pid;
517
518#ifdef EPOOL_DEBUG
519 sentry->ep.allocated += EPOOL_BLOCK_SIZE;
520 ep_allocated += EPOOL_BLOCK_SIZE;
521 ep_maxalloc = (ep_allocated > ep_maxalloc) ? ep_allocated : ep_maxalloc;
522#endif
523
524#ifdef DEBUG
525 {
526 SEntry *se;
527 if ((se = g_hash_table_lookup (smtpd_debug_alloc, sentry))) {
528 debug_error ("SEntry already alloced", NULL);
529 } else {
530 g_hash_table_insert (smtpd_debug_alloc, sentry, sentry);
531 }
532 }
533#endif
534
535 cpos = sizeof (SEntry);
536
537 blocks = (SList *)((char *)sentry + cpos);
538
539 cpos += sizeof (SList);
540
541 blocks->data = sentry;
542 blocks->next = NULL;
543
544 sentry->ep.blocks = blocks;
545 sentry->ep.cpos = cpos;
546
547 return sentry;
548}
549
550SEntry *
551sentry_get (LParser *parser, int pid)
552{
553 SEntry *sentry;
554
555 if ((sentry = g_hash_table_lookup (parser->smtpd_h, &pid))) {
556 return sentry;
557 } else {
558
559 if ((sentry = sentry_new (pid))) {
560 g_hash_table_insert (parser->smtpd_h, &sentry->pid, sentry);
561 }
562
563 return sentry;
564 }
565}
566
567void
568sentry_ref_add (SEntry *sentry, QEntry *qentry)
569{
570 SList *l;
571
572 if (qentry->smtpd) {
573 if (qentry->smtpd != sentry) {
574 debug_error ("qentry ref already set", NULL);
575 }
576 return;
577 }
578
579 l = sentry->refs;
580 while (l) {
581 if (l->data == qentry) return;
582 l = l->next;
583 }
584
585 l = epool_alloc (&sentry->ep, sizeof (SList));
586
587 qentry->smtpd = sentry;
588
589 l->data = qentry;
590 l->next = sentry->refs;
591
592 sentry->refs = l;
593}
594
595int
596sentry_ref_del (SEntry *sentry, QEntry *qentry)
597{
598 SList *l = sentry->refs;
599 int count = 0;
600
601 if (!qentry->smtpd) {
602 debug_error ("qentry does not hav a qentry ref", NULL);
603 return 0;
604 }
605
606 l = sentry->refs;
607
608 while (l) {
609 QEntry *qe = (QEntry *)l->data;
610 if (qe == qentry) {
611 l->data = NULL;
612 } else {
613 if (qe && qe->cleanup) count++;
614 }
615 l = l->next;
616 }
617
618 return count;
619}
620
621void
622sentry_ref_finalize (LParser *parser, SEntry *sentry)
623{
624 SList *l = sentry->refs;
625
626 int count = 0;
627
628 while (l) {
629 SList *cl = l;
630 QEntry *qe = l->data;
631
632 l = l->next;
633
634 if (!qe) continue;
635
636 count++;
637
638 if (!qe->removed) continue;
639
640 FEntry *fe = qe->filter;
641
642 if (fe && !fe->finished) continue;
643
644 count--;
645
646 qentry_print (parser, qe);
647
648 cl->data = NULL;
649 qe->smtpd = NULL;
650
651 qentry_free (parser, qe);
652
653 if (fe) fentry_free (parser, fe);
654
655 }
656
657 if (!count) sentry_free_noremove (sentry);
658}
659
660int
661sentry_ref_rem_unneeded (LParser *parser, SEntry *sentry)
662{
663 SList *l = sentry->refs;
664 int count = 0;
665
666 while (l) {
667 QEntry *qe = (QEntry *)l->data;
668
669 if (qe) {
670 if (!qe->cleanup) {
671 qe->smtpd = NULL;
672 qentry_free (parser, qe);
673 l->data = NULL;
674 } else {
675 count++;
676 }
677 }
678
679 l = l->next;
680 }
681
682 return count;
683}
684
685void
686sentry_nqlist_add (SEntry *sentry, time_t ltime, const char *from, int from_len,
687 const char *to, int to_len, char dstatus)
688{
689 NQList *nq = (NQList *)epool_alloc (&sentry->ep, sizeof (NQList));
690
691 nq->from = epool_strndup0 (&sentry->ep, from, from_len);
692 nq->to = epool_strndup0 (&sentry->ep, to, to_len);
693 nq->dstatus = dstatus;
694
695 nq->next = sentry->nqlist;
696 nq->ltime = ltime;
697 sentry->nqlist = nq;
698}
699
700void
701sentry_print (LParser *parser, SEntry *sentry)
702{
703 NQList *nq;
704
705 if (parser->msgid || parser->qid) return;
706
707 if (parser->server) {
708 if (!sentry->connect) return;
709 if (!strcasestr (sentry->connect, parser->server)) return;
710 }
711
712 if (parser->from || parser->to ||
713 parser->exclude_greylist || parser->exclude_ndrs) {
714 nq = sentry->nqlist;
715 int found = 0;
716 while (nq) {
717 if (parser->from) {
718 if (!*(parser->from)) {
719 if (*(nq->from)) nq->dstatus = 0;
720 } else if (!STRMATCH(parser->from, nq->from)) {
721 nq->dstatus = 0;
722 }
723 }
724
725 if (parser->exclude_greylist && nq->dstatus == 'G') nq->dstatus = 0;
726
727 if (parser->exclude_ndrs && nq->from && !*nq->from) nq->dstatus = 0;
728
729 if (parser->to && nq->to && !STRMATCH(parser->to, nq->to)) {
730 nq->dstatus = 0;
731 }
732
733 if (nq->dstatus != 0) found = 1;
734
735 nq = nq->next;
736 }
737 if (!found) return;
738 }
739
740 if (parser->strmatch && !sentry->strmatch) return;
741
742 if (parser->verbose) {
743
744 printf ("SMTPD:\n");
745
746 printf ("CTIME: %08lX\n", parser->ctime);
747
748 if (sentry->connect) { printf ("CONNECT: %s\n", sentry->connect); }
749 //printf ("EXTERNAL: %d\n", sentry->external);
750
751 }
752
753 nq = sentry->nqlist;
754 while (nq) {
755 if (nq->from && nq->to && nq->dstatus) {
756 printf ("TO:%08lX:00000000000:%c: from <%s> to <%s>\n", nq->ltime, nq->dstatus, nq->from, nq->to);
757 parser->count++;
758 }
759 nq = nq->next;
760 }
761
762 if (parser->verbose) {
763 printf ("LOGS:\n");
764 loglist_print (&sentry->loglist);
765 printf ("\n");
766 }
767
768 fflush (stdout);
769}
770
771void
772sentry_set_connect (SEntry *sentry, const char *connect, int len)
773{
774 if (sentry->connect) {
775#ifdef DEBUG
776 if (strncmp (sentry->connect, connect, len)) {
777 debug_error ("duplicate connect", NULL);
778 }
779#endif
780 } else {
781 sentry->connect = epool_strndup0 (&sentry->ep, connect, len);
782 }
783}
784
785void
786sentry_free_noremove (SEntry *sentry)
787{
788 SList *l;
789 gpointer data;
790
791#ifdef EPOOL_DEBUG
792 ep_allocated -= sentry->ep.allocated;
793 printf ("MEM: %d\n", ep_allocated);
794#endif
795
796#ifdef DEBUG
797 {
798 SEntry *se;
799 if ((se = g_hash_table_lookup (smtpd_debug_free, sentry))) {
800 debug_error ("SEntry already freed", NULL);
801 } else {
802 g_hash_table_insert (smtpd_debug_free, sentry, sentry);
803 }
804 }
805 return;
806#endif
807
808 l = sentry->ep.mblocks;
809 while (l) {
810 data = l->data;
811 l = l->next;
812 g_free (data);
813 }
814
815 l = sentry->ep.blocks;
816 while (l) {
817 data = l->data;
818 l = l->next;
f6146391 819 g_slice_free1(EPOOL_BLOCK_SIZE, data);
39ec53c5
DM
820 }
821}
822
823void
824sentry_free (LParser *parser, SEntry *sentry)
825{
826 g_hash_table_remove (parser->smtpd_h, &sentry->pid);
827
828 sentry_free_noremove (sentry);
829}
830
831void
832sentry_cleanup_hash (gpointer key,
833 gpointer value,
834 gpointer user_data)
835{
836 SEntry *se = value;
837 LParser *parser = (LParser *)user_data;
838
839 sentry_print (parser, se);
840 sentry_free_noremove (se);
841}
842
843#ifdef DEBUG
844void
845sentry_debug_alloc (gpointer key,
846 gpointer value,
847 gpointer user_data)
848{
849 LParser *parser = (LParser *)user_data;
850 SEntry *se = value;
851 SEntry *fe;
852
853 if ((fe = g_hash_table_lookup (smtpd_debug_free, se))) {
854 return;
855 }
856
857 printf ("FOUND ALLOCATED SENTRY:\n");
858 sentry_print (parser, se);
859
860 exit (-1);
861}
862#endif
863
864// QEntry
865
866QEntry*
867qentry_new (const char *qid)
868{
869 QEntry *qentry;
870 SList *blocks;
871 int cpos;
872 char *qid_cp;
873
f6146391 874 qentry = (QEntry *)g_slice_alloc0(EPOOL_BLOCK_SIZE);
39ec53c5
DM
875
876#ifdef EPOOL_DEBUG
877 qentry->ep.allocated += EPOOL_BLOCK_SIZE;
878 ep_allocated += EPOOL_BLOCK_SIZE;
879 ep_maxalloc = (ep_allocated > ep_maxalloc) ? ep_allocated : ep_maxalloc;
880#endif
881
882#ifdef DEBUG
883 {
884 QEntry *qe;
885 if ((qe = g_hash_table_lookup (qmgr_debug_alloc, qentry))) {
886 debug_error ("QEntry already alloced", NULL);
887 } else {
888 g_hash_table_insert (qmgr_debug_alloc, qentry, qentry);
889 }
890 }
891#endif
892
893 cpos = sizeof (QEntry);
894
895 blocks = (SList *)((char *)qentry + cpos);
896
897 cpos += sizeof (SList);
898
899 blocks->data = qentry;
900 blocks->next = NULL;
901
902 qentry->qid = qid_cp = (char *)qentry + cpos;
903 while ((*qid_cp++ = *qid++)) cpos++;
904
905 cpos = (cpos + 4) & ~3;
906
907 qentry->ep.blocks = blocks;
908 qentry->ep.cpos = cpos;;
909
910 return qentry;
911}
912
913void
914qentry_tolist_add (QEntry *qentry, time_t ltime, char dstatus, const char *to, int to_len,
915 const char *relay, int relay_len)
916{
917 TOList *tl = (TOList *)epool_alloc (&qentry->ep, sizeof (TOList));
918
919 tl->to = epool_strndup0 (&qentry->ep, to, to_len);
920 tl->relay = epool_strndup0 (&qentry->ep, relay, relay_len);
921 tl->dstatus = dstatus;
922 tl->ltime = ltime;
923 tl->next = qentry->tolist;
924
925 qentry->tolist = tl;
926}
927
928void
929qentry_set_from (QEntry *qentry, const char *from, int len)
930{
931 if (qentry->from) {
932#ifdef DEBUG
933 if (strncmp (qentry->from, from, len)) {
934 debug_error ("duplicate from", NULL);
935 }
936#endif
937 } else {
938 qentry->from = epool_strndup0 (&qentry->ep, from, len);
939 }
940}
941
942void
943qentry_set_msgid (QEntry *qentry, const char *msgid, int len)
944{
945 if (qentry->msgid) {
946#ifdef DEBUG
947 if (strncmp (qentry->msgid, msgid, len)) {
948 debug_error ("duplicate msgid", NULL);
949 }
950#endif
951 } else {
952 qentry->msgid = epool_strndup0 (&qentry->ep, msgid, len);
953 }
954}
955
956void
957qentry_set_client (QEntry *qentry, const char *client, int len)
958{
959 if (qentry->client) {
960#ifdef DEBUG
961 if (strncmp (qentry->client, client, len)) {
962 debug_error ("duplicate client", NULL);
963 }
964#endif
965 } else {
966 qentry->client = epool_strndup0 (&qentry->ep, client, len);
967 }
968}
969
970void
971qentry_print (LParser *parser, QEntry *qentry)
972{
973 TOList *tl, *fl;
974 SEntry *se = qentry->smtpd;
975 FEntry *fe = qentry->filter;
976
977 if (parser->msgid) {
978 if (!qentry->msgid) return;
979 if (strcasecmp (parser->msgid, qentry->msgid)) return;
980 }
981
982 if (parser->qid) {
983 int found = 0;
984 if (fe && !strcmp (fe->logid, parser->qid)) found = 1;
985 if (!strcmp (qentry->qid, parser->qid)) found = 1;
986
987 if (!found) return;
988 }
989
990 if (parser->server) {
991 int found = 0;
992 if (se && se->connect && strcasestr (se->connect, parser->server)) found = 1;
993 if (qentry->client && strcasestr (qentry->client, parser->server)) found = 1;
994
995 if (!found) return;
996 }
997
998 if (parser->from) {
999 if (!qentry->from) return;
1000 if (!*(parser->from)) {
1001 if (*(qentry->from)) return;
1002 } else if (!STRMATCH(parser->from, qentry->from)) {
1003 return;
1004 }
1005 } else {
1006 if (parser->exclude_ndrs && qentry->from && !*qentry->from) return;
1007 }
1008
1009 if (parser->to) {
1010 tl = qentry->tolist;
1011 int found = 0;
1012 while (tl) {
1013 if (parser->to && !STRMATCH(parser->to, tl->to)) {
1014 tl->to = NULL;
1015 } else {
1016 found = 1;
1017 }
1018 tl = tl->next;
1019 }
1020 if (!found) return;
1021 }
1022
1023 if (parser->strmatch &&
1024 !(qentry->strmatch || (se && se->strmatch) || (fe && fe->strmatch)))
1025 return;
1026
1027
1028 if (parser->verbose) {
1029
1030 printf ("QENTRY: %s\n", qentry->qid);
1031
1032 printf ("CTIME: %08lX\n", parser->ctime);
1033 printf ("SIZE: %u\n", qentry->size);
1034
1035 if (qentry->client) { printf ("CLIENT: %s\n", qentry->client); }
1036
1037 if (qentry->msgid) { printf ("MSGID: %s\n", qentry->msgid); }
1038
1039 }
1040
1041 tl = qentry->tolist;
1042 while (tl) {
1043 if (tl->to) {
1044 fl = NULL;
1045 if (fe && tl->dstatus == '2') {
1046 fl = fe->tolist;
1047 while (fl) {
1048 if (fl->to && !strcmp (tl->to, fl->to)) {
1049 break;
1050 }
1051 fl = fl->next;
1052 }
1053 }
1054 char *to;
1055 char dstatus;
1056 char *relay;
1057
1058 if (fl) {
1059 to = fl->to;
1060 dstatus = fl->dstatus;
1061 relay = fl->relay;
1062 } else {
1063 to = tl->to;
1064 dstatus = tl->dstatus;
1065 relay = tl->relay;
1066 }
1067
1068 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");
1069
1070 parser->count++;
1071 }
1072 tl = tl->next;
1073 }
1074
1075
1076 if (!parser->verbose) { fflush (stdout); return; }
1077
1078 if (se) {
1079
1080 if (se->loglist.log) {
1081 printf ("SMTP:\n");
1082 loglist_print (&se->loglist);
1083 }
1084 }
1085
1086 if (fe) {
1087 if (fe->loglist.log) {
1088 printf ("FILTER: %s\n", fe->logid);
1089 loglist_print (&fe->loglist);
1090 }
1091 }
1092
1093 if (qentry->loglist.log) {
1094 printf ("QMGR:\n");
1095 loglist_print (&qentry->loglist);
1096 }
1097
1098 printf ("\n");
1099
1100 fflush (stdout);
1101
1102 //sleep (1);
1103}
1104
1105QEntry *
1106qentry_get (LParser *parser, const char *qid)
1107{
1108 QEntry *qentry;
1109
1110 if ((qentry = g_hash_table_lookup (parser->qmgr_h, qid))) {
1111 return qentry;
1112 } else {
1113 if ((qentry = qentry_new (qid))) {
1114 g_hash_table_insert (parser->qmgr_h, qentry->qid, qentry);
1115 }
1116
1117 return qentry;
1118 }
1119}
1120
1121void
1122qentry_free_noremove (LParser *parser, QEntry *qentry)
1123{
1124 SList *l;
1125 gpointer data;
1126 SEntry *se;
1127
1128 if ((se = qentry->smtpd)) {
1129 if (sentry_ref_del (se, qentry) == 0) {
1130 if (se->disconnect) {
1131 sentry_free_noremove (se);
1132 }
1133 }
1134 }
1135
1136#ifdef EPOOL_DEBUG
1137 ep_allocated -= qentry->ep.allocated;
1138 printf ("MEM: %d\n", ep_allocated);
1139#endif
1140
1141#ifdef DEBUG
1142 {
1143 QEntry *qe;
1144 if ((qe = g_hash_table_lookup (qmgr_debug_free, qentry))) {
1145 debug_error ("QEntry already freed", NULL);
1146 } else {
1147 g_hash_table_insert (qmgr_debug_free, qentry, qentry);
1148 }
1149 }
1150 return;
1151#endif
1152
1153 l = qentry->ep.mblocks;
1154 while (l) {
1155 data = l->data;
1156 l = l->next;
1157 g_free (data);
1158 }
1159
1160 l = qentry->ep.blocks;
1161 while (l) {
1162 data = l->data;
1163 l = l->next;
f6146391 1164 g_slice_free1(EPOOL_BLOCK_SIZE, data);
39ec53c5
DM
1165 }
1166}
1167
1168void
1169qentry_free (LParser *parser, QEntry *qentry)
1170{
1171 g_hash_table_remove (parser->qmgr_h, qentry->qid);
1172
1173 qentry_free_noremove (parser, qentry);
1174}
1175
1176void
1177qentry_cleanup_hash (gpointer key,
1178 gpointer value,
1179 gpointer user_data)
1180{
1181 QEntry *qe = value;
1182 LParser *parser = (LParser *)user_data;
1183
1184 qentry_print (parser, qe);
1185 qentry_free_noremove (parser, qe);
1186}
1187
1188void
1189qentry_finalize (LParser *parser, QEntry *qentry)
1190{
1191 if (qentry && qentry->removed) {
1192 SEntry *se = qentry->smtpd;
1193
1194 if (se && !se->disconnect) return;
1195
1196 FEntry *fe = qentry->filter;
1197
1198 if (fe && !fe->finished) return;
1199
1200 qentry_print (parser, qentry);
1201 qentry_free (parser, qentry);
1202
1203 if (fe) fentry_free (parser, fe);
1204 }
1205}
1206
1207// FEntry
1208
1209FEntry*
1210fentry_new (const char *logid)
1211{
1212 FEntry *fentry;
1213 SList *blocks;
1214 int cpos;
1215 char *logid_cp;
1216
f6146391 1217 fentry = (FEntry *)g_slice_alloc0(EPOOL_BLOCK_SIZE);
39ec53c5
DM
1218
1219#ifdef EPOOL_DEBUG
1220 fentry->ep.allocated += EPOOL_BLOCK_SIZE;
1221 ep_allocated += EPOOL_BLOCK_SIZE;
1222 ep_maxalloc = (ep_allocated > ep_maxalloc) ? ep_allocated : ep_maxalloc;
1223#endif
1224
1225#ifdef DEBUG
1226 {
1227 FEntry *fe;
1228 if ((fe = g_hash_table_lookup (filter_debug_alloc, fentry))) {
1229 debug_error ("FEntry already alloced", NULL);
1230 } else {
1231 g_hash_table_insert (filter_debug_alloc, fentry, fentry);
1232 }
1233 }
1234#endif
1235
1236 cpos = sizeof (FEntry);
1237
1238 blocks = (SList *)((char *)fentry + cpos);
1239
1240 cpos += sizeof (SList);
1241
1242 blocks->data = fentry;
1243 blocks->next = NULL;
1244
1245 fentry->logid = logid_cp = (char *)fentry + cpos;
1246 while ((*logid_cp++ = *logid++)) cpos++;
1247 cpos = (cpos + 4) & ~3;
1248
1249 fentry->ep.blocks = blocks;
1250 fentry->ep.cpos = cpos;;
1251
1252 return fentry;
1253}
1254
1255FEntry *
1256fentry_get (LParser *parser, const char *logid)
1257{
1258 FEntry *fentry;
1259
1260 if ((fentry = g_hash_table_lookup (parser->filter_h, logid))) {
1261 return fentry;
1262 } else {
1263 if ((fentry = fentry_new (logid))) {
1264 g_hash_table_insert (parser->filter_h, fentry->logid, fentry);
1265 }
1266
1267 return fentry;
1268 }
1269}
1270
1271void
1272fentry_tolist_add (FEntry *fentry, char dstatus, const char *to, int to_len,
1273 const char *qid, int qid_len)
1274{
1275 TOList *tl = (TOList *)epool_alloc (&fentry->ep, sizeof (TOList));
1276
1277 tl->to = epool_strndup0 (&fentry->ep, to, to_len);
1278
1279 if (qid) {
1280 tl->relay = epool_strndup0 (&fentry->ep, qid, qid_len);
1281 } else {
1282 tl->relay = NULL;
1283 }
1284 tl->dstatus = dstatus;
1285 tl->next = fentry->tolist;
1286
1287 fentry->tolist = tl;
1288}
1289
1290void
1291fentry_free_noremove (LParser *parser, FEntry *fentry)
1292{
1293 SList *l;
1294 gpointer data;
1295
1296#ifdef EPOOL_DEBUG
1297 ep_allocated -= fentry->ep.allocated;
1298 printf ("MEM: %d\n", ep_allocated);
1299#endif
1300
1301#ifdef DEBUG
1302 {
1303 FEntry *fe;
1304 if ((fe = g_hash_table_lookup (filter_debug_free, fentry))) {
1305 debug_error ("FEntry already freed", NULL);
1306 } else {
1307 g_hash_table_insert (filter_debug_free, fentry, fentry);
1308 }
1309 }
1310 return;
1311#endif
1312
1313 l = fentry->ep.mblocks;
1314 while (l) {
1315 data = l->data;
1316 l = l->next;
1317 g_free (data);
1318 }
1319
1320 l = fentry->ep.blocks;
1321 while (l) {
1322 data = l->data;
1323 l = l->next;
f6146391 1324 g_slice_free1(EPOOL_BLOCK_SIZE, data);
39ec53c5
DM
1325 }
1326}
1327
1328void
1329fentry_free (LParser *parser, FEntry *fentry)
1330{
1331 g_hash_table_remove (parser->filter_h, fentry->logid);
1332
1333 fentry_free_noremove (parser, fentry);
1334}
1335
1336void
1337fentry_cleanup_hash (gpointer key,
1338 gpointer value,
1339 gpointer user_data)
1340{
1341 FEntry *fe = value;
1342 LParser *parser = (LParser *)user_data;
1343
1344 fentry_free_noremove (parser, fe);
1345}
1346
1347// Parser
1348
1349LParser*
1350parser_new ()
1351{
1352 LParser *parser = g_malloc0 (sizeof (LParser));
1353 struct timeval tv;
1354 struct tm *ltime;
1355 int i;
1356
1357 epool_init (&parser->ep);
1358
1359 if (!(parser->smtpd_h = g_hash_table_new (g_int_hash, g_int_equal))) {
1360 return NULL;
1361 }
1362
1363 if (!(parser->qmgr_h = g_hash_table_new (g_str_hash, g_str_equal))) {
1364 return NULL;
1365 }
1366
1367 if (!(parser->filter_h = g_hash_table_new (g_str_hash, g_str_equal))) {
1368 return NULL;
1369 }
1370
1371 //if (!(parser->track_h = g_hash_table_new (g_str_hash, g_str_equal))) {
1372 //return NULL;
1373 //}
1374
952866a3 1375 for (i = 0; i < MAX_LOGFILES; i++) {
39ec53c5
DM
1376 gettimeofday(&tv, NULL);
1377 tv.tv_sec -= 3600*24*i;
1378 ltime = localtime (&tv.tv_sec);
1379 parser->year[i] = ltime->tm_year + 1900;
1380 }
1381
1382 return parser;
1383}
1384
1385void
1386parser_free (LParser *parser)
1387{
1388 int i;
1389
1390 for (i = 0; i < MAX_LOGFILES; i++) {
1391 if (parser->stream[i]) gzclose (parser->stream[i]);
1392 }
1393
1394 epool_free (&parser->ep);
1395
1396 g_hash_table_destroy (parser->smtpd_h);
1397 g_hash_table_destroy (parser->qmgr_h);
1398 g_hash_table_destroy (parser->filter_h);
1399 //g_hash_table_destroy (parser->track_h);
1400
1401 g_free (parser);
1402}
1403
1404#if 0
1405char *
1406parser_track (LParser *parser, const char *qid, gboolean insert)
1407{
1408 char *res;
1409
1410 if ((res = g_hash_table_lookup (parser->track_h, qid))) {
1411 return res;
1412 } else {
1413 if (insert && (res = epool_strdup (&parser->ep, qid))) {
1414 g_hash_table_insert (parser->track_h, res, res);
1415 return res;
1416 }
1417 }
1418 return NULL;
1419}
1420#endif
1421
1422static const int linebufsize = 8192;
1423static int cur_year;
1424static int cur_month = 0;
1425static int cal_mtod[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
1426
1427static time_t
1428mkgmtime (struct tm *tm)
1429{
1430 time_t res;
1431
1432 int year = tm->tm_year + 1900;
1433 int mon = tm->tm_mon;
1434
1435 res = (year - 1970) * 365 + cal_mtod[mon];
1436
1437 // leap year corrections (gregorian calendar)
1438 if (mon <= 1) year -= 1;
1439 res += (year - 1968) / 4;
1440 res -= (year - 1900) / 100;
1441 res += (year - 1600) / 400;
1442
1443 res += tm->tm_mday - 1;
1444 res = res*24 + tm->tm_hour;
1445 res = res*60 + tm->tm_min;
1446 res = res*60 + tm->tm_sec;
1447
1448 return res;
1449}
1450
1451time_t
1452parse_time (const char **text, int len)
1453{
1454 time_t ltime = 0;
1455
1456 int year = cur_year;
1457
1458 int mon = 0;
1459 int mday = 0;
1460 int hour = 0;
1461 int min = 0;
1462 int sec = 0;
1463 int found;
1464
1465 const char *line = *text;
1466
1467 if (len == (linebufsize - 1)) {
1468 debug_error ("skipping long line data", line);
1469 return 0;
1470 }
1471
1472 if (len < 15) {
1473 debug_error ("skipping short line data", line);
1474 return 0;
1475 }
1476
1477 // parse month
1478 int csum = (line[0]<<16) + (line[1]<<8) + line[2];
1479
1480 switch (csum) {
1481 case 4874606: mon = 0; break;
1482 case 4613474: mon = 1; break;
1483 case 5071218: mon = 2; break;
1484 case 4288626: mon = 3; break;
1485 case 5071225: mon = 4; break;
1486 case 4879726: mon = 5; break;
1487 case 4879724: mon = 6; break;
1488 case 4289895: mon = 7; break;
1489 case 5465456: mon = 8; break;
1490 case 5202804: mon = 9; break;
1491 case 5140342: mon = 10; break;
1492 case 4482403: mon = 11; break;
1493 default:
1494 debug_error ("unable to parse month", line);
1495 return 0;
1496 }
1497
1498 // year change heuristik
1499 if (cur_month == 11 && mon == 0) {
1500 year++;
1501 }
1502 if (mon > cur_month) cur_month = mon;
1503
1504 ltime = (year - 1970)*365 + cal_mtod[mon];
1505
1506 // leap year corrections (gregorian calendar)
1507 if (mon <= 1) year -= 1;
1508 ltime += (year - 1968) / 4;
1509 ltime -= (year - 1900) / 100;
1510 ltime += (year - 1600) / 400;
1511
1512 const char *cpos = line + 3;
1513
1514 found = 0; while (isspace (*cpos)) { cpos++; found = 1; }
1515 if (!found) {
1516 debug_error ("missing spaces after month", line);
1517 return 0;
1518 }
1519
1520 found = 0; while (isdigit (*cpos)) { mday = mday*10 + *cpos - 48; cpos++; found++; }
1521 if (found < 1 || found > 2) {
1522 debug_error ("unable to parse day of month", line);
1523 return 0;
1524 }
1525
1526 ltime += mday - 1;
1527
1528 found = 0; while (isspace (*cpos)) { cpos++; found++; }
1529 if (!found) {
1530 debug_error ("missing spaces after day of month", line);
1531 return 0;
1532 }
1533
1534 found = 0; while (isdigit (*cpos)) { hour = hour*10 + *cpos - 48; cpos++; found++; }
1535 if (found < 1 || found > 2) {
1536 debug_error ("unable to parse hour", line);
1537 return 0;
1538 }
1539
1540 ltime *= 24;
1541 ltime += hour;
1542
1543 if (*cpos != ':') {
1544 debug_error ("missing collon after hour", line);
1545 return 0;
1546 }
1547 cpos++;
1548
1549 found = 0; while (isdigit (*cpos)) { min = min*10 + *cpos - 48; cpos++; found++; }
1550 if (found < 1 || found > 2) {
1551 debug_error ("unable to parse minute", line);
1552 return 0;
1553 }
1554
1555 ltime *= 60;
1556 ltime += min;
1557
1558 if (*cpos != ':') {
1559 debug_error ("missing collon after minute", line);
1560 return 0;
1561 }
1562 cpos++;
1563
1564 found = 0; while (isdigit (*cpos)) { sec = sec*10 + *cpos - 48; cpos++; found++; }
1565 if (found < 1 || found > 2) {
1566 debug_error ("unable to parse second", line);
1567 return 0;
1568 }
1569
1570 ltime *= 60;
1571 ltime += sec;
1572
1573 found = 0; while (isspace (*cpos)) { cpos++; found = 1; }
1574 if (!found) {
1575 debug_error ("missing spaces after time", line);
1576 return 0;
1577 }
1578
1579 *text = cpos;
1580
1581 return ltime;
1582}
1583
1584
1585int
1586parser_count_files (LParser *parser)
1587{
1588 int i;
1589 time_t start = parser->start;
1590 char linebuf[linebufsize];
1591 const char *line;
53616b4f 1592 gzFile stream;
39ec53c5
DM
1593
1594 for (i = 0; i < MAX_LOGFILES; i++) {
1595 cur_year = parser->year[i];
1596 cur_month = 0;
1597
1598 if ((stream = gzopen (logfiles[i], "r"))) {
1599 if ((line = gzgets (stream, linebuf, linebufsize))) {
1600 if (parse_time (&line, strlen (line)) < start) {
1601 break;
1602 }
1603 } else {
1604 return i;
1605 }
1606 gzclose (stream);
1607 } else {
1608 return i;
1609 }
1610 }
1611
1612 return i + 1;
1613}
1614
1615static char *
1616parse_qid (const char **text, char *out, char delim, int maxlen)
1617{
1618 const char *idx;
1619 char *copy = out;
1620
1621 int found = 0;
1622 idx = *text;
1623 while (isxdigit (*idx)) { *copy++ = *idx++; found++; if (found > maxlen) break; }
1624
1625 if (found > 1 && found < maxlen &&
1626 ((delim && (*idx == delim)) || (!delim && isspace (*idx)))) {
1627 *copy = 0;
1628 idx++;
1629 while (isspace (*idx)) idx++;
1630 *text = idx;
1631 return out;
1632 }
1633 return NULL;
1634}
1635
1636void
1637print_usage (const char *name)
1638{
1639 fprintf (stderr, "usage: %s [OPTIONS] [OUTPUTFILENAME]\n", name);
1640 fprintf (stderr, "\t-f SENDER mails from SENDER\n");
1641 fprintf (stderr, "\t-t RECIPIENT mails to RECIPIENT\n");
1642 fprintf (stderr, "\t-h Server Server IP or Hostname\n");
1643 fprintf (stderr, "\t-s START start time (YYYY-MM-DD HH:MM:SS)\n");
1644 fprintf (stderr, "\t or seconds since epoch\n");
1645 fprintf (stderr, "\t-e END end time (YYYY-MM-DD HH:MM:SS)\n");
1646 fprintf (stderr, "\t or seconds since epoch\n");
1647 fprintf (stderr, "\t-m MSGID message ID (exact match)\n");
1648 fprintf (stderr, "\t-q QID queue ID (exact match)\n");
1649 fprintf (stderr, "\t-x STRING search for strings\n");
1650 fprintf (stderr, "\t-l LIMIT print max limit entries\n");
1651 fprintf (stderr, "\t-v verbose output\n");
1652}
1653
1654
1655// gzgets is ways too slow, so we do it our own way
1656
1657static int
1658mygzgetc (gzFile stream)
1659{
1660 int br;
1661 static char readbuf[16384];
1662 static char *readend = readbuf + sizeof (readbuf);
1663 static char *readpos = readbuf + sizeof (readbuf);
1664
1665 if (readpos < readend) return *readpos++;
1666
1667 if ((br = gzread (stream, readbuf, sizeof (readbuf))) <= 0) {
1668 return -1;
1669 } else {
1670 readpos = readbuf;
1671 readend = readbuf + br;
1672
1673 return *readpos++;
1674 }
1675}
1676
1677static char *
1678mygzgets (gzFile stream, char *line, int bufsize)
1679{
1680 int c=0;
1681 char *cpos;
1682
1683 cpos = line;
1684 while (--bufsize > 0 && (c = mygzgetc(stream)) != -1) {
1685 *cpos++ = c;
1686 if (c == '\n')
1687 break;
1688 }
1689 if (c == -1 && cpos == line)
1690 return NULL;
1691 *cpos++ = '\0';
1692 return line;
1693}
1694
1695
1696extern char *optarg;
1697extern int optind, opterr, optopt;
1698
1699int
1700main (int argc, char * const argv[])
1701{
1702 char linebuf[linebufsize];
1703 char *line;
1704 char *uniqueid = NULL;
1705
1706 const char *text;
39ec53c5
DM
1707 const char *idx1;
1708 const char *idx2;
1709 const char *cpos;
1710 int found = 0;
1711 int csum_prog;
1712 int lines = 0;
1713 char qidbuf[30];
1714 int i;
1715
1716 struct tm *ltime;
1717 struct timeval tv;
1718 time_t ctime, start, end;
1719
1720 LParser *parser;
1721 int opt;
1722
1723#ifdef DEBUG
1724 smtpd_debug_alloc = g_hash_table_new (g_direct_hash, g_direct_equal);
1725 qmgr_debug_alloc = g_hash_table_new (g_direct_hash, g_direct_equal);
1726 filter_debug_alloc = g_hash_table_new (g_direct_hash, g_direct_equal);
1727 smtpd_debug_free = g_hash_table_new (g_direct_hash, g_direct_equal);
1728 qmgr_debug_free = g_hash_table_new (g_direct_hash, g_direct_equal);
1729 filter_debug_free = g_hash_table_new (g_direct_hash, g_direct_equal);
1730#endif
1731
39ec53c5
DM
1732 if (!(parser = parser_new ())) {
1733 fprintf (stderr, "unable to alloc parser structure\n");
1734 exit (-1);
1735 }
1736
1737 while ((opt = getopt (argc, argv, "f:t:s:e:h:m:q:x:l:I:vgn")) != -1) {
1738 if (opt == 'f') {
1739 parser->from = epool_strdup (&parser->ep, optarg);
1740 } else if (opt == 't') {
1741 parser->to = epool_strdup (&parser->ep, optarg);
1742 } else if (opt == 'v') {
1743 parser->verbose = 1;
1744 } else if (opt == 'g') {
1745 parser->exclude_greylist = 1;
1746 } else if (opt == 'n') {
1747 parser->exclude_ndrs = 1;
1748 } else if (opt == 'I') {
1749 uniqueid = optarg;
1750 } else if (opt == 'h') {
1751 parser->server = epool_strdup (&parser->ep, optarg);
1752 } else if (opt == 'm') {
1753 parser->msgid = epool_strdup (&parser->ep, optarg);
1754 } else if (opt == 'q') {
1755 parser->qid = epool_strdup (&parser->ep, optarg);
1756 } else if (opt == 'x') {
1757 parser->strmatch = epool_strdup (&parser->ep, optarg);
1758 } else if (opt == 'l') {
1759 char *l;
1760 parser->limit = strtoul (optarg, &l, 0);
1761 if (!*optarg || *l) {
1762 fprintf (stderr, "unable to parse limit '%s'\n", optarg);
1763 exit (-1);
1764 }
1765 } else if (opt == 's') {
1766 // use strptime to convert time
1767 struct tm tm;
1768 char *res;
1769 if ((!(res = strptime (optarg, "%F %T", &tm)) &&
1770 !(res = strptime (optarg, "%s", &tm))) || *res) {
1771 fprintf (stderr, "unable to parse start time\n");
1772 exit (-1);
1773 } else {
1774 parser->start = mkgmtime (&tm);
1775 }
1776 } else if (opt == 'e') {
1777 struct tm tm;
1778 char *res;
1779 if ((!(res = strptime (optarg, "%F %T", &tm)) &&
1780 !(res = strptime (optarg, "%s", &tm))) || *res) {
1781 fprintf (stderr, "unable to parse end time\n");
1782 exit (-1);
1783 } else {
1784 parser->end = mkgmtime (&tm);
1785 }
1786 } else {
1787 print_usage (argv[0]);
1788 exit (-1);
1789 }
1790 }
1791
1792 if (optind < argc) {
1793
1794 if ((argc - optind) > 1) {
1795 print_usage (argv[0]);
1796 exit (-1);
1797 }
1798
1799 char *tmpfn = g_strdup_printf ("/tmp/.proxtrack-%08X.txt", getpid ());
1800
1801 if ((stdout = freopen (tmpfn, "w", stdout)) == NULL) {
1802 perror ("unable to open output file");
1803 exit (-1);
1804 }
1805 if (rename (tmpfn, argv[optind]) != 0) {
1806 perror ("unable to open output file");
1807 unlink (tmpfn);
1808 exit (-1);
1809 }
1810 }
1811
1812 // we use gmtime exerywhere to speedup things, this can cause problems
1813 // when daylight saving time changes
1814
1815 gettimeofday(&tv, NULL);
1816 ltime = localtime (&tv.tv_sec);
1817
1818 if (!parser->start) {
1819 ltime->tm_sec = 0;
1820 ltime->tm_min = 0;
1821 ltime->tm_hour = 0;
1822 parser->start = mkgmtime (ltime);
1823 }
1824
1825 ltime = localtime (&tv.tv_sec);
1826
1827 if (!parser->end) {
1828 parser->end = mkgmtime (ltime);
1829 }
1830
1831 if (parser->end < parser->start) {
1832 fprintf (stderr, "end time before start time\n");
1833 exit (-1);
1834 }
1835
1836 int filecount;
1837 if ((filecount = parser_count_files (parser)) <= 0) {
1838 fprintf (stderr, "unable to access log files\n");
1839 exit (-1);
1840 }
1841
1842 if (uniqueid) {
1843 printf ("# LogReader: %d %s\n", getpid(), uniqueid);
1844 } else {
1845 printf ("# LogReader: %d\n", getpid());
1846 }
1847
1848 printf ("# Query options\n");
1849 if (parser->from) printf ("# Sender: %s\n", parser->from);
1850 if (parser->to) printf ("# Recipient: %s\n", parser->to);
1851 if (parser->server) printf ("# Server: %s\n", parser->server);
1852 if (parser->msgid) printf ("# MsgID: %s\n", parser->msgid);
1853 if (parser->qid) printf ("# QID: %s\n", parser->qid);
1854 if (parser->strmatch) printf ("# Match: %s\n", parser->strmatch);
1855
1856 strftime (linebuf, 256, "%F %T", gmtime (&parser->start));
1857 printf ("# Start: %s (%lu)\n", linebuf, parser->start);
1858 strftime (linebuf, 256, "%F %T", gmtime (&parser->end));
1859 printf ("# END: %s (%lu)\n", linebuf, parser->end);
1860 printf ("# End Query Options\n\n");
1861
1862 fflush (stdout);
1863
1864 start = parser->start;
1865 end = parser->end;
1866 ctime = 0;
1867
1868 for (i = filecount - 1; i >= 0; i--) {
1869 gpointer stream;
1870
1871 // syslog files does not conain years, so we must compute then
1872 // cur_year is the assumed start year
1873
1874 cur_month = 0;
1875 cur_year = parser->year[i];
1876
1877 if (i <= 1) {
1878 if (!(stream = (gpointer) fopen (logfiles[i], "r"))) continue;
1879 } else {
1880 if (!(stream = (gpointer) gzopen (logfiles[i], "r"))) continue;
1881 }
1882
1883 while (1) {
1884
1885 if (parser->limit && (parser->count >= parser->limit)) {
1886 printf ("STATUS: aborted by limit (too many hits)\n");
1887 exit (0);
1888 }
1889
1890 if (i <= 1) {
1891 line = fgets (linebuf, linebufsize, (FILE *)stream);
1892 } else {
1893 line = mygzgets ((gzFile)stream, linebuf, linebufsize);
1894 }
1895
1896 if (!line) break;
1897
1898 int len = strlen (line);
1899 int pid = 0;
1900
1901 cpos = line;
1902 if (!(ctime = parse_time (&cpos, len))) {
1903 continue;
1904 }
1905
1906 if (ctime < start) continue;
1907 if (ctime > end) break;
1908
1909 lines++;
1910
1911 found = 0; while (!isspace (*cpos)) { cpos++; found = 1; }
1912 if (!found) {
1913 debug_error ("missing hostname", line);
1914 continue;
1915 }
1916
1917 found = 0; while (isspace (*cpos)) { cpos++; found = 1; }
1918 if (!found) {
1919 debug_error ("missing spaces after host", line);
1920 continue;
1921 }
1922
1923 if ((*cpos == 'l') && !strncmp (cpos, "last message repeated", 21)) {
1924 continue;
1925 }
1926
1927 //printf ("LINE: %s\n", line);
2e62093c 1928 //const char *prog = cpos;
39ec53c5
DM
1929
1930 csum_prog = 0;
1931 found = 0; while (*cpos && (*cpos != ':') && (*cpos != '[')) {
1932 csum_prog = (csum_prog <<8) + *cpos;
1933 cpos++;
1934 found++;
1935 }
1936
1937 //idx1 = g_strndup (prog, found);
1938 //printf ("TEST:%s:%08X\n", idx1, csum_prog);
2e62093c 1939 //free (idx1);
39ec53c5
DM
1940
1941 if (*cpos == '[') {
1942 cpos++;
1943 found = 0; while (isdigit (*cpos)) {
1944 pid = pid*10 + *cpos - 48;
1945 cpos++;
1946 found++;
1947 }
1948 if (found < 1 || found > 15 || *cpos != ']') {
1949 debug_error ("unable to parse pid", line);
1950 continue;
1951 }
1952 cpos++;
1953 }
1954
1955 if (*cpos++ != ':') {
1956 debug_error ("missing collon", line);
1957 continue;
1958 }
1959
1960
1961 if (!isspace (*cpos++)) {
1962 debug_error ("missing space after collon", line);
1963 continue;
1964 }
1965
1966 text = cpos;
1967
1968 parser->ctime = ctime;
1969
1970 int strmatch = 0;
1971
1972 if (parser->strmatch && STRMATCH(parser->strmatch, text)) {
1973 strmatch = 1;
1974 }
1975
bf472949
DM
1976 if ((csum_prog == 0x70726F78) ||// proxprox
1977 (csum_prog == 0x6C746572)) { // pmg-smtp-filter
39ec53c5
DM
1978
1979 if ((idx1 = parse_qid (&cpos, qidbuf, ':', 25))) {
1980
1981 FEntry *fe;
1982
1983 if (!(fe = fentry_get (parser, idx1))) {
1984 continue;
1985 }
1986
1987 loglist_add (&fe->ep, &fe->loglist, line, len);
1988
1989 if (strmatch) fe->strmatch = 1;
1990
1991 //fixme: BCC, Notify?
1992 //fixme: parse virus info
1993 //fixme: parse spam score
1994
1995 if ((*cpos == 'a') && !strncmp (cpos, "accept mail to <", 16)) {
1996
1997 const char *to_s, *to_e;
1998
1999 to_s = cpos = cpos + 16;
2000
2001 while (*cpos && (*cpos != '>')) { cpos++; }
2002
2003 if (*cpos != '>') continue;
2004
2005 to_e = cpos;
2006
2007 cpos++;
2008
2009 if ((*cpos++ != ' ') || (*cpos++ != '(')) continue;
2010
2011 if (!(idx1 = parse_qid (&cpos, qidbuf, ')', 15))) continue;
2012
2013 // parser_track (parser, idx1, 1);
2014
2015 fentry_tolist_add (fe, 'A', to_s, to_e - to_s, idx1, strlen (idx1));
2016
2017 } else if ((*cpos == 'm') && !strncmp (cpos, "moved mail for <", 16)) {
2018 const char *to_s, *to_e;
2019
2020 to_s = cpos = cpos + 16;
2021
2022 while (*cpos && (*cpos != '>')) { cpos++; }
2023
2024 to_e = cpos;
2025
2026 if (strncmp (cpos, "> to ", 5)) continue;
2027 cpos += 5;
2028
2029 if (!strncmp (cpos, "spam", 4)) {
2030 cpos += 4;
2031 } else if (!strncmp (cpos, "virus", 5)) {
2032 cpos += 5;
2033 } else {
2034 continue;
2035 }
2036
2037 if (strncmp (cpos, " quarantine - ", 14)) continue;
2038 cpos += 14;
2039
2040 if (!(idx1 = parse_qid (&cpos, qidbuf, 0, 25))) continue;
2041
2042 fentry_tolist_add (fe, 'Q', to_s, to_e - to_s, idx1, strlen (idx1));
2043
2044 } else if ((*cpos == 'b') && !strncmp (cpos, "block mail to <", 15)) {
2045
2046 const char *to_s;
2047
2048 to_s = cpos = cpos + 15;
2049
2050 while (*cpos && (*cpos != '>')) { cpos++; }
2051
2052 if (*cpos != '>') continue;
2053
2054 fentry_tolist_add (fe, 'B', to_s, cpos - to_s, NULL, 0);
2055
2056 } else if ((*cpos == 'p') && !strncmp (cpos, "processing time: ", 17)) {
2057 cpos += 17;
2058
2059 sscanf (cpos, "%f", &fe->ptime);
2060
2061 fe->finished = 1;
2062 }
2063
2064 }
2065
2066 } else if (csum_prog == 0x7265656E) { // postfix/postscreen
2067
2068 SEntry *se;
2069
2070 if (!pid) {
2071 debug_error ("no pid for postscreen", line);
2072 continue;
2073 }
2074
2075
2076 if ((*text == 'N') && !strncmp (text, "NOQUEUE: reject: RCPT from ", 27)) {
2077
2078 cpos = text + 27;
2079
2080 if (!(idx1 = strstr (cpos, "; client ["))) continue;
2081
2082 const char *client = cpos = idx1 + 10;
2083
2084 while (*cpos && (*cpos != ']')) { cpos++; }
2085
2086 const char *client_end = cpos;
2087
2088 if (!(idx1 = strstr (cpos, "; from=<"))) continue;
2089
2090 const char *from = cpos = idx1 + 8;
2091
2092 while (*cpos && (*cpos != '>')) { cpos++; }
2093 idx1 = cpos;
2094
2095 if ((*cpos == '>') && strncmp (cpos, ">, to=<", 7)) continue;
2096
2097 const char *to = cpos = cpos + 7;
2098
2099 while (*cpos && (*cpos != '>')) { cpos++; }
2100
2101 if (*cpos != '>') continue;
2102
2103 if (!(se = sentry_get (parser, pid))) {
2104 continue;
2105 }
2106
2107 if (strmatch) se->strmatch = 1;
2108
2109 loglist_add (&se->ep, &se->loglist, line, len);
2110
2111 sentry_nqlist_add (se, ctime, from, idx1 - from, to, cpos - to, 'N');
2112
2113 sentry_set_connect (se, client, client_end - client);
2114
2115 g_hash_table_remove (parser->smtpd_h, &se->pid);
2116
2117 se->disconnect = 1;
2118
2119 sentry_print (parser, se);
2120 sentry_free (parser, se);
2121 }
2122
2123 } else if (csum_prog == 0x716D6772) { // postfix/qmgr
2124
2125 if ((idx2 = text) && (idx1 = parse_qid (&idx2, qidbuf, ':', 15))) {
2126
2127 QEntry *qe;
2128
2129 if (!(qe = qentry_get (parser, idx1))) {
2130 continue;
2131 }
2132
2133 if (strmatch) qe->strmatch = 1;
2134
2135 qe->cleanup = 1;
2136
2137 loglist_add (&qe->ep, &qe->loglist, line, len);
2138
2139 if ((*idx2 == 'f') && !strncmp (idx2, "from=<", 6)) {
2140
2141 cpos = idx2 = idx2 + 6;
2142 while (*cpos && (*cpos != '>')) { cpos++; }
2143
2144 if (*cpos != '>') {
2145 debug_error ("unable to parse 'from' address", line);
2146 continue;
2147 }
2148
2149 qentry_set_from (qe, idx2, cpos - idx2);
2150
2151 cpos++;
2152
2153 if ((*cpos == ',') && !strncmp (cpos, ", size=", 7)) {
2154 int size = 0;
2155 cpos += 7;
2156
2157 while (isdigit (*cpos)) { size = size*10 + *cpos++ - 48; }
2158
2159 qe->size = size;
2160 }
2161
2162 } else if ((*idx2 == 'r') && !strncmp (idx2, "removed\n", 8)) {
2163
2164 qe->removed = 1;
2165
2166 qentry_finalize (parser, qe);
2167 }
2168 }
2169
2170 } else if ((csum_prog == 0x736D7470) || //postfix/smtp
2e62093c 2171 (csum_prog == 0x6C6D7470) || //postfix/lmtp
34b47143 2172 (csum_prog == 0x6F63616C) || //postfix/local
2e62093c 2173 (csum_prog == 0x72726F72)) { //postfix/error
39ec53c5
DM
2174
2175 int lmtp = (csum_prog == 0x6C6D7470);
2176
2177 if ((cpos = text) && (idx1 = parse_qid (&cpos, qidbuf, ':', 15))) {
2178
2179 QEntry *qe;
2180
2181 if (!(qe = qentry_get (parser, idx1))) {
2182 continue;
2183 }
2184
2185 if (strmatch) qe->strmatch = 1;
2186
2187 qe->cleanup = 1;
2188
2189 loglist_add (&qe->ep, &qe->loglist, line, len);
2190
2191 if (strncmp (cpos, "to=<", 4)) continue;
2192 cpos += 4;
2193
2194 const char *to_s, *to_e;
2195
2196 to_s = cpos;
2197
2198 while (*cpos && (*cpos != '>')) { cpos++; }
2199
2200 if (*cpos != '>') continue;
2201
2202 to_e = cpos;
2203
2204 cpos ++;
2205
2206 if (!(cpos = strstr (cpos, ", relay="))) continue;
2207 cpos += 8;
2208
2209 const char *relay_s, *relay_e;
2210
2211 relay_s = cpos;
2212
2213 while (*cpos && (*cpos != ',')) { cpos++; }
2214
2215 if (*cpos != ',') continue;
2216
2217 relay_e = cpos;
2218
2219 if (!(idx1 = strstr (cpos + 1, ", dsn="))) continue;
2220
2221 cpos = idx1 + 6;
2222
2223 if (!isdigit (*cpos)) continue;
2224
2225 char dstatus = *cpos;
2226
2227 qentry_tolist_add (qe, ctime, dstatus, to_s, to_e - to_s,
2228 relay_s, relay_e - relay_s);
2229
2230 if (!lmtp) continue; // filter always uses lmtp
2231
2232 if (!(idx1 = strstr (cpos + 1, "status=sent (250 2.")))
2233 continue;
2234
2235 cpos = idx1 = idx1 + 19;
2236
2237 if (*cpos == '5' && (idx1 = strstr (cpos, "5.0 OK ("))) {
2238 cpos = idx1 = idx1 + 8;
2239 } else if (*cpos == '7' && (idx1 = strstr (cpos, "7.0 BLOCKED ("))) {
2240 cpos = idx1 = idx1 + 13;
2241 } else {
2242 continue;
2243 }
2244
2245 if (!(idx1 = parse_qid (&cpos, qidbuf, ')', 25)))
2246 continue;
2247
2248 FEntry *fe;
2249
2250 qe->filtered = 1;
2251
2252 if ((fe = g_hash_table_lookup (parser->filter_h, idx1))) {
2253 qe->filter = fe;
2254 }
2255 }
2256
2257 } else if (csum_prog == 0x6D747064) { // postfix/smtpd
2258 SEntry *se;
2259
2260 if (!pid) {
2261 debug_error ("no pid for smtpd", line);
2262 continue;
2263 }
2264
2265 if (!(se = sentry_get (parser, pid))) {
2266 continue;
2267 }
2268
2269 if (strmatch) se->strmatch = 1;
2270
2271 loglist_add (&se->ep, &se->loglist, line, len);
2272
2273 if ((*text == 'c') && !strncmp (text, "connect from ", 13)) {
2274
2275 cpos = idx1 = text + 13;
2276
2277 while (*idx1 && !isspace (*idx1)) { idx1++; }
2278
2279 sentry_set_connect (se, cpos, idx1 - cpos);
2280
2281 // fixme: do we need this?
2282 //if (strcmp (se->connect, "localhost[127.0.0.1]")) {
2283 // se->external = 1;
2284 //}
2285
2286 } else if ((*text == 'd') && !strncmp (text, "disconnect from", 15)) {
2287
2288 // unlink
2289 g_hash_table_remove (parser->smtpd_h, &se->pid);
2290
2291 se->disconnect = 1;
2292
2293 if (sentry_ref_rem_unneeded (parser, se) == 0) {
2294 sentry_print (parser, se);
2295 sentry_free (parser, se);
2296 } else {
2297 sentry_ref_finalize (parser, se);
2298 }
2299
2300 } else if ((*text == 'N') && !strncmp (text, "NOQUEUE:", 8)) {
2301
2302 cpos = text + 8;
2303
2304 // parse 'whatsup' (reject:)
2305 while (*cpos && (*cpos != ':')) { cpos++; }
2306 if (*cpos != ':') continue;
2307 cpos++;
2308
2309 // parse '%s from %s:'
2310 while (*cpos && (*cpos != ':')) { cpos++; }
2311 if (*cpos != ':') continue;
2312 idx1 = cpos++;
2313
2314 // parse '%s;'
2315 while (*cpos && (*cpos != ';')) { cpos++; }
2316 if (*cpos != ';') continue;
2317
2318 // greylisting test
2319 char dstatus = 'N';
2320 *(char *)cpos = 0; // dangerous hack: delimit string
2321 if (strstr (idx1, ": Recipient address rejected: Service is unavailable (try later)")) {
2322 dstatus = 'G';
2323 }
2324 *(char *)cpos = ';'; // dangerous hack: restore line
2325
2326 if (!(idx1 = strstr (cpos, "; from=<"))) continue;
2327
2328 const char *from = cpos = idx1 + 8;
2329
2330 while (*cpos && (*cpos != '>')) { cpos++; }
2331 idx1 = cpos;
2332
2333 if ((*cpos == '>') && strncmp (cpos, "> to=<", 6)) continue;
2334
2335 const char *to = cpos = cpos + 6;
2336
2337 while (*cpos && (*cpos != '>')) { cpos++; }
2338
2339 if (*cpos != '>') continue;
2340
2341 sentry_nqlist_add (se, ctime, from, idx1 - from, to, cpos - to, dstatus);
2342
2343 } else if ((idx2 = text) && (idx1 = parse_qid (&idx2, qidbuf, ':', 15))) {
2344
2345 QEntry *qe;
2346
2347 if ((qe = qentry_get (parser, idx1))) {
2348
2349 if (strmatch) qe->strmatch = 1;
2350
2351 sentry_ref_add (se, qe);
2352
2353 if ((*idx2 == 'c') && !strncmp (idx2, "client=", 7)) {
2354 cpos = idx2 = idx2 + 7;
2355
2356 while (*cpos && !isspace (*cpos)) { cpos++; }
2357
2358 qentry_set_client (qe, idx2, cpos - idx2);
2359 }
2360 }
2361
2362 }
2363
2364 } else if (csum_prog == 0x616E7570) { // postfix/cleanup
2365
2366 QEntry *qe;
2367
2368 idx2 = text;
2369 if ((idx1 = parse_qid (&idx2, qidbuf, ':', 15))) {
2370 if ((qe = qentry_get (parser, idx1))) {
2371
2372 if (strmatch) qe->strmatch = 1;
2373
2374 loglist_add (&qe->ep, &qe->loglist, line, len);
2375
2376 if ((*idx2 == 'm') && !strncmp (idx2, "message-id=", 11)) {
2377
2378 cpos = idx2 = idx2 + 11;
2379
2380 while (*cpos && !isspace(*cpos)) { cpos++; }
2381
2382 qentry_set_msgid (qe, idx2, cpos - idx2);
2383
2384 qe->cleanup = 1;
2385 }
2386 }
2387 }
2388 }
2389 }
2390
2391 if (i <= 1) {
2392 fclose ((FILE *)stream);
2393 } else {
2394 gzclose ((gzFile)stream);
2395 }
2396
2397 if (ctime > end) break;
2398 }
2399
2400
2401#ifdef DEBUG
2402 printf ("LINES: %d\n", lines);
2403 printf ("MEM SMTPD entries: %d\n", g_hash_table_size (parser->smtpd_h));
2404 printf ("MEM QMGR entries: %d\n", g_hash_table_size (parser->qmgr_h));
2405 printf ("MEM FILTER entries: %d\n", g_hash_table_size (parser->filter_h));
2406
2407 printf ("MEMDEB SMTPD entries: %d %d\n",
2408 g_hash_table_size (smtpd_debug_alloc),
2409 g_hash_table_size (smtpd_debug_free));
2410 printf ("MEMDEB QMGR entries: %d %d\n",
2411 g_hash_table_size (qmgr_debug_alloc),
2412 g_hash_table_size (qmgr_debug_free));
2413 printf ("MEMDEB FILTER entries: %d %d\n",
2414 g_hash_table_size (filter_debug_alloc),
2415 g_hash_table_size (filter_debug_free));
2416#endif
2417
2418 g_hash_table_foreach (parser->qmgr_h, qentry_cleanup_hash, parser);
2419 g_hash_table_foreach (parser->smtpd_h, sentry_cleanup_hash, parser);
2420 g_hash_table_foreach (parser->filter_h, fentry_cleanup_hash, parser);
2421
2422#ifdef DEBUG
2423 printf ("MEMDEB SMTPD entries: %d %d\n",
2424 g_hash_table_size (smtpd_debug_alloc),
2425 g_hash_table_size (smtpd_debug_free));
2426 printf ("MEMDEB QMGR entries: %d %d\n",
2427 g_hash_table_size (qmgr_debug_alloc),
2428 g_hash_table_size (qmgr_debug_free));
2429 printf ("MEMDEB FILTER entries: %d %d\n",
2430 g_hash_table_size (filter_debug_alloc),
2431 g_hash_table_size (filter_debug_free));
2432
2433 g_hash_table_foreach (smtpd_debug_alloc, sentry_debug_alloc, parser);
2434
2435#endif
2436
2437
2438#ifdef EPOOL_DEBUG
2439 printf ("MEMMAX %d\n", ep_maxalloc);
2440#endif
2441
2442 parser_free (parser);
2443
2444 fclose (stdout);
2445
2446 exit (0);
2447}