/*
- (C) 2007-2017 Proxmox Server Solutions GmbH, All Rights Reserved
+ (C) 2007-2017 Proxmox Server Solutions GmbH, All Rights Reserved
- Proxmox Mail Tracker
+ Proxmox Mail Tracker
- See http://www.proxmox.com for more information
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+ Author: Dietmar Maurer <dietmar@proxmox.com>
+
+ See http://www.proxmox.com for more information
*/
//#define EPOOL_MAX_SIZE 128
#define EPOOL_BLOCK_SIZE 2048
#define EPOOL_MAX_SIZE 128
-#define MAX_LOGFILES 31
+#define MAX_LOGFILES 32
//#define EPOOL_DEBUG
//#define DEBUG
TOList *next;
};
+#define MatchTypeQID 1
+#define MatchTypeRelLineNr 2
+
+typedef struct _MatchList MatchList;
+struct _MatchList {
+ unsigned int mtype;
+ char *id;
+ time_t ltime;
+ unsigned long rel_line_nr;
+ MatchList *next;
+};
+
#ifdef DEBUG
GHashTable *smtpd_debug_alloc;
GHashTable *qmgr_debug_alloc;
time_t start;
time_t end;
time_t ctime;
+ MatchList *match_list;
char *server;
char *msgid;
- char *qid;
char *strmatch;
unsigned long limit;
unsigned long count;
struct _LogEntry {
const char *text;
+ unsigned long linenr;
LogEntry *next;
};
char *connect;
+ // time,rel_line_nr is used as cursor/ID
+ time_t ltime;
+ unsigned long rel_line_nr;
+
//unsigned int external:1; // not from local host
unsigned int disconnect:1;
unsigned int strmatch:1;
// Prototypes
void debug_error (char *msg, const char *line);
-SList *slist_remove (SList *list, gpointer data);
EPool *epool_init (EPool *ep);
void epool_free (EPool *ep);
char *epool_strdup (EPool *ep, const char *s);
void loglist_print (LogList *loglist);
-void loglist_add (EPool *ep, LogList *loglist, const char *text, int len);
+void loglist_add (EPool *ep, LogList *loglist, const char *text, int len, unsigned long linenr);
-SEntry *sentry_new (int pid);
-SEntry *sentry_get (LParser *parser, int pid);
+SEntry *sentry_new (int pid, time_t ltime, unsigned long rel_line_nr);
+SEntry *sentry_get (LParser *parser, int pid, time_t ltime, unsigned long rel_line_nr);
void sentry_ref_add (SEntry *sentry, QEntry *qentry);
int sentry_ref_del (SEntry *sentry, QEntry *qentry);
void sentry_ref_finalize (LParser *parser, SEntry *sentry);
//#define LOGPATH "./log/"
#define LOGPATH "/var/log/"
-//#define LOGPATH "/var/log5/"
+//#define LOGPATH "/root/testlog/"
static const char *logfiles[] = {
LOGPATH "syslog",
#endif
}
-SList *
-slist_remove (SList *list, gpointer data)
-{
- SList *p = NULL;
- SList *l = list;
-
- while (l) {
- if (l->data == data) {
- if (p)
- p->next = l->next;
- if (list == l)
- list = list->next;
-
- l->next = NULL;
-
- break;
- }
-
- p = l;
- l = l->next;
- }
-
- return list;
-}
-
EPool *
epool_init (EPool *ep)
{
{
LogEntry *log = loglist->log;
while (log) {
- printf ("%s", log->text);
+ printf ("L%08X %s", log->linenr, log->text);
log = log->next;
}
}
void
-loglist_add (EPool *ep, LogList *loglist, const char *text, int len)
+loglist_add (EPool *ep, LogList *loglist, const char *text, int len, unsigned long linenr)
{
LogEntry *log;
log = epool_alloc (ep, sizeof (LogEntry));
log->text = epool_strndup (ep, text, len);
+ log->linenr = linenr;
log->next = NULL;
if (loglist->logs_last) {
}
SEntry*
-sentry_new (int pid)
+sentry_new (int pid, time_t ltime, unsigned long rel_line_nr)
{
SEntry *sentry;
SList *blocks;
sentry = (SEntry *)g_slice_alloc0(EPOOL_BLOCK_SIZE);
sentry->pid = pid;
+ sentry->ltime = ltime;
+ sentry->rel_line_nr = rel_line_nr;
#ifdef EPOOL_DEBUG
sentry->ep.allocated += EPOOL_BLOCK_SIZE;
}
SEntry *
-sentry_get (LParser *parser, int pid)
+sentry_get (LParser *parser, int pid, time_t ltime, unsigned long rel_line_nr)
{
SEntry *sentry;
return sentry;
} else {
- if ((sentry = sentry_new (pid))) {
+ if ((sentry = sentry_new (pid, ltime, rel_line_nr))) {
g_hash_table_insert (parser->smtpd_h, &sentry->pid, sentry);
}
{
NQList *nq;
- if (parser->msgid || parser->qid) return;
+ if (parser->msgid) return;
if (parser->server) {
if (!sentry->connect) return;
if (!strcasestr (sentry->connect, parser->server)) return;
}
+ MatchList *match = parser->match_list;
+ if (match) {
+ int found = 0;
+ while(match) {
+ if (match->mtype == MatchTypeQID) {
+ return;
+ } else if (match->mtype == MatchTypeRelLineNr) {
+ if (match->ltime == sentry->ltime && match->rel_line_nr == sentry->rel_line_nr) {
+ found = 1;
+ break;
+ }
+ } else {
+ g_error("implement me");
+ }
+ match = match->next;
+ }
+ if (!found) return;
+ }
+
if (parser->from || parser->to ||
parser->exclude_greylist || parser->exclude_ndrs) {
nq = sentry->nqlist;
if (parser->verbose) {
- printf ("SMTPD:\n");
+ printf ("SMTPD: T%08lXL%08lX\n", sentry->ltime, sentry->rel_line_nr);
printf ("CTIME: %08lX\n", parser->ctime);
- if (sentry->connect) { printf ("CONNECT: %s\n", sentry->connect); }
+ if (sentry->connect) { printf ("CLIENT: %s\n", sentry->connect); }
//printf ("EXTERNAL: %d\n", sentry->external);
}
nq = sentry->nqlist;
while (nq) {
if (nq->from && nq->to && nq->dstatus) {
- printf ("TO:%08lX:00000000000:%c: from <%s> to <%s>\n", nq->ltime, nq->dstatus, nq->from, nq->to);
+ printf ("TO:%08lX:T%08lXL%08lX:%c: from <%s> to <%s>\n", nq->ltime,
+ sentry->ltime, sentry->rel_line_nr, nq->dstatus,
+ nq->from, nq->to);
parser->count++;
}
nq = nq->next;
}
- if (parser->verbose) {
+ if (!parser->verbose) { fflush (stdout); return; }
+
+ if (parser->verbose > 1) {
printf ("LOGS:\n");
loglist_print (&sentry->loglist);
- printf ("\n");
}
+ printf ("\n");
+
fflush (stdout);
}
if (strcasecmp (parser->msgid, qentry->msgid)) return;
}
- if (parser->qid) {
+ MatchList *match = parser->match_list;
+ if (match) {
int found = 0;
- if (fe && !strcmp (fe->logid, parser->qid)) found = 1;
- if (!strcmp (qentry->qid, parser->qid)) found = 1;
-
- if (!found) return;
+ while(match) {
+ if (match->mtype == MatchTypeQID) {
+ if ((fe && !strcmp (fe->logid, match->id)) ||
+ (!strcmp (qentry->qid, match->id))) {
+ found = 1;
+ break;
+ }
+ } else if (match->mtype == MatchTypeRelLineNr) {
+ if (se && match->ltime == se->ltime && match->rel_line_nr == se->rel_line_nr) {
+ found = 1;
+ break;
+ }
+ } else {
+ g_error("implement me");
+ }
+ match = match->next;
+ }
+ if (!found) return;
}
if (parser->server) {
printf ("CTIME: %08lX\n", parser->ctime);
printf ("SIZE: %u\n", qentry->size);
- if (qentry->client) { printf ("CLIENT: %s\n", qentry->client); }
+ if (qentry->client) {
+ printf ("CLIENT: %s\n", qentry->client);
+ } else if (se && se->connect) {
+ printf ("CLIENT: %s\n", se->connect);
+ }
if (qentry->msgid) { printf ("MSGID: %s\n", qentry->msgid); }
if (!parser->verbose) { fflush (stdout); return; }
- if (se) {
+ if (parser->verbose > 1) {
- if (se->loglist.log) {
+ if (se && se->loglist.log) {
printf ("SMTP:\n");
loglist_print (&se->loglist);
}
- }
- if (fe) {
- if (fe->loglist.log) {
- printf ("FILTER: %s\n", fe->logid);
- loglist_print (&fe->loglist);
- }
- }
+ if (fe && fe->loglist.log) {
+ printf ("FILTER: %s\n", fe->logid);
+ loglist_print (&fe->loglist);
+ }
- if (qentry->loglist.log) {
- printf ("QMGR:\n");
- loglist_print (&qentry->loglist);
+ if (qentry->loglist.log) {
+ printf ("QMGR:\n");
+ loglist_print (&qentry->loglist);
+ }
}
printf ("\n");
// parse month
int csum = (line[0]<<16) + (line[1]<<8) + line[2];
-
+
switch (csum) {
case 4874606: mon = 0; break;
case 4613474: mon = 1; break;
const char *line;
gzFile stream;
- for (i = 0; i < MAX_LOGFILES; i++) {
+ for (i = 0; i < (MAX_LOGFILES - 1); i++) {
cur_year = parser->year[i];
cur_month = 0;
fprintf (stderr, "\t-e END end time (YYYY-MM-DD HH:MM:SS)\n");
fprintf (stderr, "\t or seconds since epoch\n");
fprintf (stderr, "\t-m MSGID message ID (exact match)\n");
- fprintf (stderr, "\t-q QID queue ID (exact match)\n");
+ fprintf (stderr, "\t-q QID queue ID (exact match), can be\n");
+ fprintf (stderr, "\t specified multiple times.\n");
fprintf (stderr, "\t-x STRING search for strings\n");
fprintf (stderr, "\t-l LIMIT print max limit entries\n");
- fprintf (stderr, "\t-v verbose output\n");
+ fprintf (stderr, "\t-g exclude greylist entries\n");
+ fprintf (stderr, "\t-n exclude NDR entries\n");
+ fprintf (stderr, "\t-v verbose output (no logs)\n");
+ fprintf (stderr, "\t-vv verbose output with logs\n");
}
const char *cpos;
int found = 0;
int csum_prog;
- int lines = 0;
+ unsigned long lines = 0;
+ unsigned long rel_line_nr = 0;
char qidbuf[30];
int i;
struct tm *ltime;
struct timeval tv;
- time_t ctime, start, end;
+ time_t ctime, next_ctime, start, end;
LParser *parser;
int opt;
} else if (opt == 't') {
parser->to = epool_strdup (&parser->ep, optarg);
} else if (opt == 'v') {
- parser->verbose = 1;
+ parser->verbose += 1;
} else if (opt == 'g') {
parser->exclude_greylist = 1;
} else if (opt == 'n') {
} else if (opt == 'm') {
parser->msgid = epool_strdup (&parser->ep, optarg);
} else if (opt == 'q') {
- parser->qid = epool_strdup (&parser->ep, optarg);
+ time_t ltime;
+ unsigned long rel_line_nr;
+ MatchList *match = (MatchList *)epool_alloc(&parser->ep, sizeof(MatchList));
+ if (sscanf(optarg, "T%08lXL%08lX", <ime, &rel_line_nr) == 2) {
+ match->mtype = MatchTypeRelLineNr;
+ match->ltime = ltime;
+ match->rel_line_nr = rel_line_nr;
+ match->next = parser->match_list;
+ parser->match_list = match;
+ } else {
+ match->mtype = MatchTypeQID;
+ match->id = epool_strdup(&parser->ep, optarg);
+ match->next = parser->match_list;
+ parser->match_list = match;
+ }
} else if (opt == 'x') {
parser->strmatch = epool_strdup (&parser->ep, optarg);
} else if (opt == 'l') {
if (parser->to) printf ("# Recipient: %s\n", parser->to);
if (parser->server) printf ("# Server: %s\n", parser->server);
if (parser->msgid) printf ("# MsgID: %s\n", parser->msgid);
- if (parser->qid) printf ("# QID: %s\n", parser->qid);
+
+ MatchList *match = parser->match_list;
+ while (match) {
+ if (match->mtype == MatchTypeQID) {
+ printf ("# QID: %s\n", match->id);
+ } else if (match->mtype == MatchTypeRelLineNr) {
+ printf ("# QID: T%08lXL%08lX\n", match->ltime, match->rel_line_nr);
+ } else {
+ g_error("internal error - unknown match type %d\n", match->mtype);
+ }
+ match = match->next;
+ }
+
if (parser->strmatch) printf ("# Match: %s\n", parser->strmatch);
strftime (linebuf, 256, "%F %T", gmtime (&parser->start));
int pid = 0;
cpos = line;
- if (!(ctime = parse_time (&cpos, len))) {
+
+ next_ctime = parse_time (&cpos, len);
+
+ if (!next_ctime) {
continue;
- }
+ }
+
+ if (next_ctime != ctime) {
+ rel_line_nr = 0;
+ } else {
+ rel_line_nr++;
+ }
+
+ ctime = next_ctime;
if (ctime < start) continue;
if (ctime > end) break;
strmatch = 1;
}
- if (csum_prog == 0x70726F78) { // proxprox
+ if ((csum_prog == 0x70726F78) ||// proxprox
+ (csum_prog == 0x6C746572)) { // pmg-smtp-filter
if ((idx1 = parse_qid (&cpos, qidbuf, ':', 25))) {
continue;
}
- loglist_add (&fe->ep, &fe->loglist, line, len);
+ loglist_add (&fe->ep, &fe->loglist, line, len, lines);
if (strmatch) fe->strmatch = 1;
if (*cpos != '>') continue;
- if (!(se = sentry_get (parser, pid))) {
+ if (!(se = sentry_get (parser, pid, ctime, rel_line_nr))) {
continue;
}
if (strmatch) se->strmatch = 1;
- loglist_add (&se->ep, &se->loglist, line, len);
+ loglist_add (&se->ep, &se->loglist, line, len, lines);
sentry_nqlist_add (se, ctime, from, idx1 - from, to, cpos - to, 'N');
qe->cleanup = 1;
- loglist_add (&qe->ep, &qe->loglist, line, len);
+ loglist_add (&qe->ep, &qe->loglist, line, len, lines);
if ((*idx2 == 'f') && !strncmp (idx2, "from=<", 6)) {
} else if ((csum_prog == 0x736D7470) || //postfix/smtp
(csum_prog == 0x6C6D7470) || //postfix/lmtp
+ (csum_prog == 0x6F63616C) || //postfix/local
(csum_prog == 0x72726F72)) { //postfix/error
int lmtp = (csum_prog == 0x6C6D7470);
qe->cleanup = 1;
- loglist_add (&qe->ep, &qe->loglist, line, len);
+ loglist_add (&qe->ep, &qe->loglist, line, len, lines);
if (strncmp (cpos, "to=<", 4)) continue;
cpos += 4;
continue;
}
- if (!(se = sentry_get (parser, pid))) {
+ if (!(se = sentry_get (parser, pid, ctime, rel_line_nr))) {
continue;
}
if (strmatch) se->strmatch = 1;
- loglist_add (&se->ep, &se->loglist, line, len);
+ loglist_add (&se->ep, &se->loglist, line, len, lines);
if ((*text == 'c') && !strncmp (text, "connect from ", 13)) {
if (strmatch) qe->strmatch = 1;
- loglist_add (&qe->ep, &qe->loglist, line, len);
+ loglist_add (&qe->ep, &qe->loglist, line, len, lines);
if ((*idx2 == 'm') && !strncmp (idx2, "message-id=", 11)) {