Chapter 48 - Using Exim as a non-queueing client
On a personal computer, it is a common requirement for all email to be sent to a “smart host”. There are plenty of MUAs that can be configured to operate that way, for all the popular operating systems. However, there are some MUAs for Unix-like systems that cannot be so configured: they submit messages using the command line interface of /usr/sbin/sendmail. Furthermore, utility programs such as cron submit messages this way.
If the personal computer runs continuously, there is no problem, because it can run a conventional MTA that handles delivery to the smart host, and deal with any delays via its queueing mechanism. However, if the computer does not run continuously or runs different operating systems at different times, queueing email is not desirable.
There is therefore a requirement for something that can provide the /usr/sbin/sendmail interface but deliver messages to a smart host without any queueing or retrying facilities. Furthermore, the delivery to the smart host should be synchronous, so that if it fails, the sending MUA is immediately informed. In other words, we want something that extends an MUA that submits to a local MTA via the command line so that it behaves like one that submits to a remote smart host using TCP/SMTP.
There are a number of applications (for example, there is one called ssmtp) that do this job. However, people have found them to be lacking in various ways. For instance, you might want to allow aliasing and forwarding to be done before sending a message to the smart host.
Exim already had the necessary infrastructure for doing this job. Just a few tweaks were needed to make it behave as required, though it is somewhat of an overkill to use a fully-featured MTA for this purpose.
There is a Boolean global option called mua_wrapper, defaulting false. Setting mua_wrapper true causes Exim to run in a special mode where it assumes that it is being used to “wrap” a command-line MUA in the manner just described. As well as setting mua_wrapper, you also need to provide a compatible router and transport configuration. Typically there will be just one router and one transport, sending everything to a smart host.
When run in MUA wrapping mode, the behaviour of Exim changes in the following ways:
A daemon cannot be run, nor will Exim accept incoming messages from inetd. In other words, the only way to submit messages is via the command line.
Each message is synchronously delivered as soon as it is received (-odi is assumed). All queueing options (queue_only, queue_smtp_domains, control in an ACL, etc.) are quietly ignored. The Exim reception process does not finish until the delivery attempt is complete. If the delivery is successful, a zero return code is given.
Address redirection is permitted, but the final routing for all addresses must be to the same remote transport, and to the same list of hosts. Furthermore, the return address (envelope sender) must be the same for all recipients, as must any added or deleted header lines. In other words, it must be possible to deliver the message in a single SMTP transaction, however many recipients there are.
If these conditions are not met, or if routing any address results in a failure or defer status, or if Exim is unable to deliver all the recipients successfully to one of the smart hosts, delivery of the entire message fails.
Because no queueing is allowed, all failures are treated as permanent; there is no distinction between 4xx and 5xx SMTP response codes from the smart host. Furthermore, because only a single yes/no response can be given to the caller, it is not possible to deliver to some recipients and not others. If there is an error (temporary or permanent) for any recipient, all are failed.
If more than one smart host is listed, Exim will try another host after a connection failure or a timeout, in the normal way. However, if this kind of failure happens for all the hosts, the delivery fails.
When delivery fails, an error message is written to the standard error stream (as well as to Exim’s log), and Exim exits to the caller with a return code value 1. The message is expunged from Exim’s spool files. No bounce messages are ever generated.
No retry data is maintained, and any retry rules are ignored.
A number of Exim options are overridden: deliver_drop_privilege is forced true, max_rcpt in the smtp transport is forced to “unlimited”, remote_max_parallel is forced to one, and fallback hosts are ignored.
The overall effect is that Exim makes a single synchronous attempt to deliver the message, failing if there is any kind of problem. Because no local deliveries are done and no daemon can be run, Exim does not need root privilege. It should be possible to run it setuid to exim instead of setuid to root. See section 52.3 for a general discussion about the advantages and disadvantages of running without root privilege.