I Hate Procmail
Its error handling is CRAP.
I am coming to this realization because I recently lost a BUNCH of messages because of a bad delivery path (I told procmail to pipe messages to a non-existent executable). So what did procmail do? According to its log:
/bin/sh: /tmp/dovecot11/libexec/dovecot/deliver: No such file or directory
procmail: Error while writing to "/tmp/dovecot11/libexec/dovecot/deliver"
Well, sure, that’s to be expected, right? So what happened to the email? VANISHED. Into the bloody ether.
Of course, determining that the message vanished is trickier than just saying “hey, it’s not in my mailbox.” Oh no, there’s a “feature”, called ORGMAIL
. What is this? According to the procmailrc documentation (*that* collection of wisdom):
ORGMAIL Usually the system mailbox (ORiGinal MAIL‐
box). If, for some obscure reason (like
‘filesystem full’) the mail could not be
delivered, then this mailbox will be the last
resort. If procmail fails to save the mail
in here (deep, deep trouble :-), then the
mail will bounce back to the sender.
And so where is THAT? Why, /var/mail/$LOGNAME
of course, where else? And if LOGNAME
isn’t set for some reason? Or what if ORGMAIL
is unset? Oh, well… nuts to you! Procmail will use $SENDMAIL
to BOUNCE THE EMAIL rather than just try again later. That’s what they mean by “deep, deep trouble.” Notice the smiley face? Here’s why the manual has a smiley-face in it: to mock your pain.
But here’s the real crux of it: procmail doesn’t see delivery errors as FATAL. If one delivery instruction fails, it’ll just keep going through the procmailrc, looking for anything else that might match. In other words, the logic of your procmailrc has to take into account the fact that sometimes mail delivery can fail. If you fail to do this, your mail CAN end up in RANDOM LOCATIONS, depending on how messages that were supposed to match earlier rules fare against later rules.
If you want “first failure bail” behavior (which makes the most sense, in my mind), you have to add an extra rule after EVERY delivery instruction. For example:
:0 H
* ^From: .*fred@there\.com
./from_fred
:0 e # handle failure
{
EXITCODE=75 # set a non-zero exit code
HOST # This causes procmail to stop, obviously
}
You agree that HOST
means “stop processing and exit”, right? Obviously. That’s procmail for you. Note that that second clause has gotta go after EVERY delivery instruction. I hope you enjoy copy-and-paste.
Another way to handle errors, since successful delivery does stop procmail, is to add something like that to the end of your procmailrc, like so:
:0 # catch-all default delivery
${DEFAULT}
# If we get this far, there must have been an error
EXITCODE=75
HOST
Of course, you could also send the mail to /dev/null
at that point, but unsetting the HOST
variable (which is what listing it does) does the same thing faster. Intuitive, right? Here’s my smiley-face:
>:-P