summaryrefslogtreecommitdiffstats
path: root/auxiliary/collate/collate.pl
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xauxiliary/collate/collate.pl146
1 files changed, 146 insertions, 0 deletions
diff --git a/auxiliary/collate/collate.pl b/auxiliary/collate/collate.pl
new file mode 100755
index 0000000..62d08b9
--- /dev/null
+++ b/auxiliary/collate/collate.pl
@@ -0,0 +1,146 @@
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+
+# Postfix delivery agents
+my @agents = qw(discard error lmtp local pipe smtp virtual);
+
+my $instre = qr{(?x)
+ \A # Absolute line start
+ (?:\S+ \s+){3} # Timestamp, adjust for other time formats
+ \S+ \s+ # Hostname
+ (postfix(?:-[^/\s]+)?) # Capture instance name stopping before first '/'
+ (?:/\S+)* # Optional non-captured '/'-delimited qualifiers
+ / # Final '/' before the daemon program name
+ };
+
+my $cmdpidre = qr{(?x)
+ \G # Continue from previous match
+ (\S+)\[(\d+)\]:\s+ # command[pid]:
+};
+
+my %smtpd;
+my %smtp;
+my %transaction;
+my $i = 0;
+my %seqno;
+my %deleted;
+
+my %isagent = map { ($_, 1) } @agents;
+
+while (<>) {
+ next unless m{$instre}ogc; my $inst = $1;
+ next unless m{$cmdpidre}ogc; my $command = $1; my $pid = $2;
+
+ if ($command eq "smtpd") {
+ if (m{\Gconnect from }gc) {
+ # Start new log
+ $smtpd{$pid}->{"log"} = $_; next;
+ undef $smtpd{$pid}->{"qid"};
+ }
+
+ $smtpd{$pid}->{"log"} .= $_;
+
+ if (m{\G(\w+): client=}gc) {
+ # Fresh transaction
+ my $qid = "$inst/$1";
+ $smtpd{$pid}->{"qid"} = $qid;
+ $transaction{$qid} = $smtpd{$pid}->{"log"};
+ $seqno{$qid} = ++$i;
+ next;
+ }
+
+ my $qid = $smtpd{$pid}->{"qid"};
+ $transaction{$qid} .= $_
+ if (defined($qid) && exists $transaction{$qid});
+ if (m{\Gdisconnect from}gc) {
+ if (!defined($qid)) {
+ print $smtpd{$pid}->{"log"}, "\n";
+ } elsif (delete $deleted{$qid}) {
+ print delete $transaction{$qid}, "\n";
+ }
+ delete $smtpd{$pid};
+ }
+ next;
+ }
+
+ if ($command eq "pickup") {
+ if (m{\G(\w+): uid=}gc) {
+ my $qid = "$inst/$1";
+ $transaction{$qid} = $_;
+ $seqno{$qid} = ++$i;
+ }
+ next;
+ }
+
+ # bounce(8) logs transaction start after cleanup(8) already logged
+ # the message-id, so the cleanup log entry may be first
+ #
+ if ($command eq "cleanup") {
+ next unless (m{\G(\w+): }gc);
+ my $qid = "$inst/$1";
+ $transaction{$qid} .= $_;
+ $seqno{$qid} = ++$i if (! exists $seqno{$qid});
+ if (m{\G(?:milter(?:-(?:header|body))?-)?(?:reject|discard|hold): }) {
+ $deleted{$qid} = 1;
+ }
+ next;
+ }
+
+ if ($command eq "qmgr") {
+ next unless (m{\G(\w+): }gc);
+ my $qid = "$inst/$1";
+ if (defined($transaction{$qid})) {
+ $transaction{$qid} .= $_;
+ if (m{\Gremoved$}gc) {
+ print delete $transaction{$qid}, "\n";
+ }
+ }
+ next;
+ }
+
+ # Save pre-delivery messages for smtp(8) and lmtp(8)
+ #
+ if ($command eq "smtp" || $command eq "lmtp") {
+ $smtp{$pid} .= $_;
+
+ if (m{\G(\w+): to=}gc) {
+ my $qid = "$inst/$1";
+ if (defined($transaction{$qid})) {
+ $transaction{$qid} .= $smtp{$pid};
+ }
+ delete $smtp{$pid};
+ }
+ next;
+ }
+
+ if ($command eq "bounce") {
+ if (m{\G(\w+): .*? notification: (\w+)$}gc) {
+ my $qid = "$inst/$1";
+ my $newid = "$inst/$2";
+ if (defined($transaction{$qid})) {
+ $transaction{$qid} .= $_;
+ }
+ $transaction{$newid} =
+ $_ . $transaction{$newid};
+ $seqno{$newid} = ++$i if (! exists $seqno{$newid});
+ }
+ next;
+ }
+
+ if ($isagent{$command}) {
+ if (m{\G(\w+): to=}gc) {
+ my $qid = "$inst/$1";
+ if (defined($transaction{$qid})) {
+ $transaction{$qid} .= $_;
+ }
+ }
+ next;
+ }
+}
+
+# Dump logs of incomplete transactions.
+foreach my $qid (sort {$seqno{$a} <=> $seqno{$b}} keys %transaction) {
+ print $transaction{$qid}, "\n";
+}