Re: [exim] pipes: what am I missing?

Top Page

Reply to this message
Author: Phil Pennock
Date:  
To: Yan Seiner
CC: exim-users
Subject: Re: [exim] pipes: what am I missing?
On 2011-09-09 at 20:54 -0700, Yan Seiner wrote:
> I am trying to set up a pipe to a shell script.
>
> rt_router:
> driver = accept
> local_part_prefix_optional
> local_part_prefix = rt-
> transport = rt_transport
>
> rt_transport:
> driver = pipe
> command = /usr/local/bin/rtcat
>
> The goal is to have exim dump the email to a shell script if it's
> prefaced with a 'rt-'.


In that case, you should remove the "local_part_prefix_optional".

> The router and transport above work - as long as I don't use redirection
> in the above scrips. As soon as I use redirection, the transport fails.
>
> So for rtcat:
>
> #!/bin/sh
>
> echo 1234567 > /tmp/abc
>
> results in
>
> 2011-09-09 20:47:34 1R2EXW-00017z-RW ** 1234@???
> <rt-1234@???> R=rt_router T=rt_transport: Child process of
> rt_transport transport returned 1 from command: /bin/sh


This means that the echo command, or its preparation, failed. I
strongly suspect that /tmp/abc already exists, owned by you when testing
this script, with permissions that keep others from modifying it. Then
when Exim runs this, as the Exim run-time user (because you didn't
specify a different user in the Transport), the shell process is unable
to open /tmp/abc for writing and so the setup fails and echo is never
run.

Working with predictable filenames in /tmp normally has unpleasant
security consequences. Use the mktemp(1) command, like so:

----------------------------8< cut here >8------------------------------
#!/bin/sh

die() { echo >&2 "$0: $*"; exit 1; }

WorkDir="$(mktemp -d -- "${TMPDIR:-/tmp}/rtcat.XXXXXXXX")" || die "mktemp failed"
trap 'cd /; rm -rf -- "${WorkDir:?}"' EXIT INT TERM

cd "$WorkDir" || die "chdir($WorkDir) failed"
echo 1234567 > abc
----------------------------8< cut here >8------------------------------

Here:
* we assume that the script's own name doesn't start with '-'
* we define die() to emit a single string to stderr (one of the few
  scenarios where $* is correct, instead of $@)
* We define $WorkDir to be a directory (-d) made securely in the current
  temporary directory (or /tmp if that is not defined)
* If mktemp fails, we die
  * do _not_ use {{ local WorkDir=$(whatever) || die }} because the die
    will never run; shell is Weird, "local" is a command, not an
    attribute, and "local" always succeeds, losing overwriting the
    returncode from mktemp.
* We set up an action to happen automatically on a signal; this includes
  the pseudo-signal, "EXIT".  We also catch INT (typically from
  Control-C) and TERM (default signal from the kill(1) command).
* We're paranoid and cause the shell script to abort if WorkDir is empty
  or unset
* We change directory to our new working directory, handling failure
* We finally do some work
* All of the quoting will protect us against directories with embedded
  whitespace, even when not using zsh (the only shell with sane
  whitespace semantics).
* If you're extra-careful, you'll add "-eu" (without quotes) to the #!
  line at the end; that will abort the script if any command exits
  non-zero without being handle by an "||" or an if [-e], and abort the
  script if you reference an undefined variable [-u].


See how much commentary there is even on a simple task? POSIX shell is
deceptive; it appears simple and safe, but often isn't. Learn to use
shell, keep it close, but it's a case of "keep your friends close, but
your enemies closer". Be wary.

Regards,
-Phil