Here we provide a particularly useful command-line command that shows you which scripts are responsible for outbound mail. For example, when WordPress themes get exploited, you will see a large number of messages coming out of a long directory inside of the user’s WordPress directory. Very large numbers of messages coming from home directories is generally a source of concern.
# grep cwd=/ /var/log/exim_mainlog | cut -d = -f 2 | cut -d " " -f 1 | sort | uniq -c | sort -n
Using Exiqgrep
Much like the exigrep utility we mentioned previously, exiqgrep is also a powerful tool to help you parse through your queue output and retrieve the specific information you’re looking for.
For example, to search through your queue and output only the messages with a specific sender address, you can use the following syntax:
# exiqgrep -f [user]@domain.tld
Above: An example of using exiqgrep with the -f flag followed by a sender address, to specifically identify messages sent by a particular user.
This is particularly useful to identify the source of local spam or to determine what happened after a user reports that a sent message has not arrived (you would first check to see if they are stuck in the queue, then use the logs to find out why).
By Recipient
Need to track down messages by their recipient instead? You can use the exiqgrep command to do this as well. Here, instead of using the -f flag, we would instead use the -r flag (a bit easier to remember, right?).
# exiqgrep -r [user]@domain.tld
Above: An example of an exiqgrep command with the -r flag followed by the recipient you’d like to search the queue for.
This can be useful to investigate when a user reports that an account is no longer receiving mail. Additionally, you can use this to identify when a user has been mail-bombed, and determine if exim has been set to automatically queue messages when over-quota.
By Age
Another handy feature of the exiqgrep utility is to search the queue for messages based on age criteria. Exiqgrep uses flags based on the younger and older terminology, and appropriately uses -o for older, and -y for younger, followed by a number of total seconds.
Two practical examples of this:
# exiqgrep -o 172800
Above: An example of searching the queue for messages older than one day (172,800 seconds).
# exiqgrep -y 1800
Above: An example of searching the queue for messages younger (newer) than 30 minutes (1,800 seconds) old.
Viewing Headers
It can be extremely useful to analyze a message’s header when attempting to determine what exactly happened to that message, or how it was handled by the server.
After acquiring the message’s Exim ID value, you can then use it to specifically output that message’s header using the following syntax:
# exim -Mvh <exim-id>
Above: Using the exim command-line tool with the -Mvh flags (case-sensitive), followed by a valid Exim ID value (the <> braces are simply for the placeholder; these should not exist in the actual command), will print that message’s header information to STDOUT (read: the terminal).
Viewing the Body
Sometimes the header just isn’t enough, and you need to see what the actual contents of the message’s body look like. You can use a very similar format using the exim command-line tool again, but this time with the -Mvb flag set, followed again by the message’s Exim ID.
# exim -Mvb <exim-id>
Above: Using the exim command line tool again but with the -Mvb flag this time, still followed by the Exim ID, to retrieve the contents of the message’s body and print it to STDOUT (I’ll be using this term ‘STDOUT’ more often as we progress; if you’re not familiar with it, just remember for now that it stands for standard output, and essentially means that it prints the text normally to your terminal).
Using xargs
The xargs utility can be extremely useful for creating quick one-liner command. It’s essentially a for loop packaged up into a single command. It works by taking the output from one command, and turning it into a line-by-line execution of another command.
For example, let’s say I’ve got a text file that has a list of files in it, each on their own line.
I could cat that file, then pipe the output to xargs to perform a different operation on each of the files listed. This can be quite useful for handling Exim queue operations in bulk, which we’ll explain in a moment.
Using Pipes
To use an example, one of the most common uses you might run across is for the purposes of either “grepping” (using the grep tool to search through a file) or paging through the output of a command:
# cat /var/log/exim_mainlog | less
Above: An example command used to allow you to scroll through the contents of a log file page-by-page, rather than printing the entire contents to the screen at one time.
# exim -bp | grep SPAM
Above: An example command using a pipe to search the output of exim -bp (the command used to print a summary of each message in the queue, remember?) for any mention of the word “SPAM”, case-intact (though the -i flag of course can be used with grep to remove case sensitivity).
Resending a Message
At times, you may want to attempt to re-send a message that exists in your queue. Maybe it was delayed for some time, but you’re ready to go ahead and try to resend it now, rather than waiting for the scheduled retry. Who knows? It’s your call.
However, to do this, you can use the exim command once again, but this time by simply providing it with the -M flag alone, followed again by the Exim ID.
What can be useful here, though, is the use of this command within a one-liner, by providing the kind of piped output and xargs command that we described before.
Let’s take a look at one practical example:
# exiqgrep -r user@domain.tld -i | xargs exim -M
Above: In this example command that utilizes piping and the xargs command, we’re instructing Exim to provide us with all messages in the queue (exiqgrep) with the recipient designated as user@domain.tld (-r user@domain.tld), and informing the exiqgrep tool that we ONLY want to output the Exim IDs that match (-i).
This, by itself, gives us a basic, line-by-line output of Exim IDs that match messages with user@domain.tld as the recipient address.
So you can probably guess what we’re doing with that next, right? We’re piping (|) that output as input for xargs to perform the exim -M command on each matching message. We know now that exim -M attempts a resend of messages, so we can discern that this full command will try to resend all messages that have the user@domain.tld address as its recipient. All in one fell swoop. Nice, right?
Deleting a Message
Now for the scarier stuff. Well… not-so-scary as long as you take caution to confirm what it is you’re taking action on.
At some point in time, you’ll almost certainly need to delete messages from your queue. When that time comes, it’s likely that it won’t just be a single message, either. You’ll probably need to clear out a large number of messages that you’ve determined as spam or otherwise “bad” mail.
The basic command syntax you would use to do this involves the exim tool again, but with the -Mrm flags this time, and again – as usual – followed by the Exim ID of the message:
# exim -Mrm <exim-id>
Above: The basic syntax for deletion of a message from the queue, based on its Exim ID. Again, we see the pattern (-M followed by rm; just like you’d rm a file from the file system).
So how about doing this as a bulk operation?
Deleting in bulk should of course always be performed with caution. Once you delete mail from your queue, there’s no guarantee that the sender will ever resend it.
So, if you delete a valid message that was intended for a recipient, you’re creating a chance that your user will never receive that mail or the message within it. So basically… be careful.
Let’s look at an example again using a similar circumstance as before.
We’re going to again utilize the exiqgrep command, but this time we’re going to look for all messages with a particular domain in its sender address, using the -f flag, then finally printing only the Exim ID values by specifying the -i flag, as we did before:
# exiqgrep -i -f @spammer.tld | xargs exim -Mrm
Above: We’re again using xargs to run exim -Mrm (the command to delete messages by Exim ID) on each Exim ID returned from the exiqgrep command that precedes it, which in our case should match all messages with a sender that uses the domain @spammer.tld (they probably could have been a bit more subtle about it, am I right?).
Note again the pipe (|) being used to pipe the output of exiqgrep into the input for xargs.
When in doubt – see https://bradthemad.org/tech/notes/exim_cheatsheet.php
Finding out the Delivery Path of an Address
exim -bt
Another very important exim command line flag we want to make sure and highlight is the -bt flag, which would be followed by a recipient email address.
Exim -bt works like some of the Email Deliverability tests found within the WHM interface. It effectively shows you where exim thinks a message should be going, and how it should get there.
For instance, messages to this user are destined for a local account:
# exim -bt dogs@animals.test dogs@animals.test router = virtual_user, transport = virtual_userdelivery
While messages to this user will leave the server to a remote destination:
# exim -bt noone@gmail.com noone@gmail.com router = lookuphost, transport = remote_smtp host gmail-smtp-in.l.google.com [64.233.169.26] MX=5 host alt1.gmail-smtp-in.l.google.com [173.194.219.26] MX=10 host alt2.gmail-smtp-in.l.google.com [173.194.204.26] MX=20 host alt3.gmail-smtp-in.l.google.com [74.125.141.26] MX=30 host alt4.gmail-smtp-in.l.google.com [64.233.186.26] MX=40
Notice that the user doesn’t actually need to exist; exim is only checking on the domain part for remote deliveries.