Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

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 "someuser@recipientdomain.com" /var/log/maillog

That usually returns something like this:

  Jan 11 07:43:34 server-example sendmail[12732]: p937blksdh3: to=<someuser@recipientdomain.com>, 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.
share|improve this question
    
Is a one-liner a requirement, or would a small executable script suffice? –  Kyle Smith Feb 28 '12 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 '12 at 22:26
add comment

4 Answers

up vote 5 down vote accepted

You could do something similar to this.

for i in `grep -i "someuser@recipientdomain.com" /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 "someuser@recipientdomain.com" /var/log/maillog | awk '{print $5}' | sed 's/\://; do grep -i $i /var/log/maillog; done

Hope that helps.

share|improve this answer
    
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 "someuser@recipientdomain.com" /var/log/maillog | awk '{print $6}'; do grep -i $i /var/log/maillog; done | less –  Mike B Feb 28 '12 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 "someuser@recipientdomain.com" /var/log/maillog | awk '{print $5}'; do grep -i $i /var/log/maillog; done | less –  Harry Feb 29 '12 at 0:06
add comment

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.

share|improve this answer
add comment

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 '/someuser@recipientdomain.com/ {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 '/someuser@recipientdomain.com/ {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='someuser@recipientdomain.com' '$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.

share|improve this answer
add comment

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 '/someuser@recipientdomain.com/ {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 '/someuser@recipientdomain.com/ {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.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.