5

CENTOS 5.x | Sendmail

Occasionally I need to search through sendmail delivery logs to find out what happened to a missing message. This usually involves two (or more) steps:

STEP 1: Search /var/log/maillog for the user's email address. For example grep -i "[email protected]" /var/log/maillog

That usually returns something like this:

  Jan 11 07:43:34 server-example sendmail[12732]: p937blksdh3: to=<[email protected]>, delay=00:00:00, xdelay=00:00:00, mailer=esmtp, pri=102537, relay=mta.recipientdomain.com. [12.34.56.78], dsn=5.7.1, stat=Service unavailable

STEP 2: I'll then grab the unique message name (in this case p937blksdh3) and search for that. For example: grep -i p937blksdh3 /var/log/maillog

I want to combine steps 1 and 2 into a one-liner and have it automatically perform the same search for other ids. So in a single command, I'd like to do the following:

  1. Search sendmail maillog for specific string.
  2. Identify the message-id (in the example above, this was p937blksdh3) for the email. (I'm guessing awk '{print $}' would be used?)
  3. Search the same log but search for the message id instead (basically grep -i p937blksdh3 /var/log/maillog in the example above)
  4. Output the results of step 3. Repeat this for other message ids.
2
  • Is a one-liner a requirement, or would a small executable script suffice?
    – Kyle Smith
    Feb 28, 2012 at 22:23
  • @KyleSmith I was hoping for a script since I would need to run this on various systems and having something portable would be more convenient
    – Mike B
    Feb 28, 2012 at 22:26

4 Answers 4

7

You could do something similar to this.

for i in `grep -i "[email protected]" /var/log/maillog | awk '{print $5}'`; do grep -i $i /var/log/maillog; done

This will grep out the line for the user you are looking for, then select the 5th item on the line (seperated by spaces iirc). Then for each message ID in that list, will then grep for the lines containing the message ID's.

If you want to remove the : from the end of the message ID, you can do something like for i in grep -i "[email protected]" /var/log/maillog | awk '{print $5}' | sed 's/\://; do grep -i $i /var/log/maillog; done

Hope that helps.

2
  • AWESOME! A couple quick questions... wouldn't it technically be awk '{print $6} based on my example? Also... is there a way to paginate? Maybe something like: for i in grep -i "[email protected]" /var/log/maillog | awk '{print $6}'; do grep -i $i /var/log/maillog; done | less
    – Mike B
    Feb 28, 2012 at 23:56
  • 1
    You are correct. I can do math! You would do awk '{print $6}' I just missed a space when glancing at it. If you wanted to page the output, then a less at the end would be correct. for i in grep -i "[email protected]" /var/log/maillog | awk '{print $5}'; do grep -i $i /var/log/maillog; done | less
    – Harry
    Feb 29, 2012 at 0:06
2

This is not a direct answer to your specific problem (Harry answered that already) but just a thought to plan ahead if you need such information often. Install MIMEDefang and then have the filter_recipient routine log all the information that you need in the format that suits you best.

2

This is a refinement of Harry's answer.

First, awk can do pattern matches like grep, so you can skip the first grep. Also, you might get duplicates, so we can remove those with sort -u:

for i in `awk '/[email protected]/ {print $6}' /var/log/maillog | sort -u `; do grep -i $i /var/log/maillog; done

This version uses two passes through the mail log:

awk '/[email protected]/ {print $6}' /var/log/maillog | sort -u > /tmp/message-ids.list
grep -f /tmp/message-ids.list /var/log/maillog

In the first version, the for loop will grep through mail.log for as many message id's found in the log. In the second version, grep will use the list of message id's as a filter and only process the log file once, which will be more efficient for a large log file.

1

There's no reason to pipe grep into awk since AWK can do grep's job. There's also no reason to use a for loop since grep can accept patterns on its standard input.

awk '/[email protected]/ {print $6}' /var/log/maillog | sort -u  | grep -i -f - /var/log/maillog

You can modify that further, removing the need for sort -u (really, sort is unnecessary - you could have used uniq)

awk '/[email protected]/ {addr[$6]} END {for (a in addr) {print addr[a]}}' /var/log/maillog | grep -i -f - /var/log/maillog

Both of those (and the other answers) do the search in two or more passes over the log file. It would be interesting to see if the following one-pass technique would be faster (and faster yet using Perl or Python).

awk -v addr='[email protected]' '$0 ~ addr {
         ids[$6]
     }
     {
         for (id in ids) {
             if ($0 ~ "^" ids[id] "$") {
                 print
             }
         }
     }
     $NF == "Completed" {
         delete ids[$1]
     }' /var/log/maillog

I haven't tested that code. You may need to adjust the field numbers, particularly in the section that deletes the id when the "Completed" log entry is found. It can be easily made into a one-liner.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .