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