How to defer things in Exim

November 14, 2009

Normally, Exim routers will only accept or fail addresses (or be uninterested in them). This is good enough for normal handling of addresses, but if you are using routers to their full power, there are times when you want to force routers to defer addresses instead. There are two general ways to do this.

(Unsuccessful DNS lookups can cause addresses to defer, but this is not normally under your control.)

The straightforward way is to use a separate router to explicitly defer the address using the :defer: action of the redirect driver, like so:

defer_addr:
  driver = redirect
  allow_defer
  data = :defer:stalling

  [... whatever condition needed ...]

Using a separate router is straightforward and makes for clear log messages about what is going on. However, it's not always possible (or desirable) to use a separate router. In that case you can abuse string expansion to cause an expansion failure while expanding some option where this will force the router to defer.

This is moderately tricky for two reasons. First, you cannot just force string expansion to fail explicitly (via an ${if} or the like), because explicit failure doesn't wind up causing options to defer this way; instead, the router generally fails or passes on the address. Only 'natural' expansion failure, for reasons that Exim thinks are outside of your control, cause this failure. The one case that I know of is if you use ${readfile} on a nonexistent file.

Second, you need to pick a router option where expansion failure causes a deferral and, ideally, that you are not already using. The Exim documentation is the final authority on what router options will do for this (see generic options for routers and check what each option does on non-forced expansion failure); the one that I have found useful in our mailer configuration is address_data. Thus, part of our deliver-to-/var/mail router looks like:

postbox:
  driver = accept
  transport = local_delivery
  # make sure it's mounted
  address_data = ${readfile{/var/mail/.MOUNTED}}

  [....]

(Our /var/mail is NFS mounted on the mail server, and obviously we only want to do deliveries there if it is the real, NFS-mounted filesystem, not the empty directory that's visible if the mount has failed for some reason. .MOUNTED is just an empty file.)

The drawback of this approach is that Exim will log alarmed looking and rather cryptic error messages if the condition every fails and forces messages to be deferred, so it is best reserved for conditions that you don't expect to happen very often.

Written on 14 November 2009.
« (Ab)using Exim routers for their full power
A limitation of Python types from C extension modules »

Page tools: View Source.
Search:
Login: Password:

Last modified: Sat Nov 14 02:39:55 2009
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.