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