Re: [exim] Undocumented surprise in ${run ...} processing

Top Page
Delete this message
Reply to this message
Author: Todd Lyons
Date:  
To: Chris Siebenmann
CC: exim-users
Subject: Re: [exim] Undocumented surprise in ${run ...} processing
On Wed, Jul 2, 2014 at 2:09 PM, Todd Lyons <tlyons@???> wrote:
>> that Exim first split the unexpanded ${run} command and arguments on
>> whitespace, creating:
>>
>>         argv[0] = /bin/cat
>>         argv[1] = $local_part
>>         argv[2] = /etc/passwd

>>
>> and then expanded each in place. If an argument wound up empty after
>> expansion, it would be preserved (as empty). This was based on the
>> documentation wording that 'the command and its arguments are first
>> expanded *separately*' (emphasis mine).
>
> Yeah, that definitely implies that it would retain position. There
> must be a check of each argument to see what is the string length and
> just discard it if 0.


I looked at the code, and this is what happens for ${run {}}. Based
on the example I first pasted above:

12:42:35 25840 expanding: /bin/cat $local_part /etc/passwd
12:42:35 25840    result: /bin/cat  /etc/passwd
12:42:35 25840 direct command:
12:42:35 25840   argv[0] = /bin/cat
12:42:35 25840   argv[1] = /etc/passwd
12:42:35 25840 expanding: ${run {/bin/cat $local_part /etc/passwd}}


1) In the code for the ${run command, the entire command is expanded.
This explains how you get from line one to line two.
2) The code calls transport_set_up_command() with the entire
*already*expanded* command (as a simple string), passing a flag saying
not to expand it/args any more. If one of the args was quoted, the
quotes still exist in the string that gets passed to the function.
3) The transport_set_up_command() splits the command and args by
spaces, assigning values to a temporary argv variable. Since the
unquoted $local_part in my example expanded to nothing, the two spaces
between the original arg[0] and arg[2] are simply skipped.
4) If one of the arguments is quoted, the quotes are removed by a
dequoting function, and what's left of that arg is put in the
temporary argv (including the empty string).
5) The command is executed, the code waits for the exec to return, and
then processes the return code.
6) The function returns to the code for the ${run command.

I now understand how ${run expansion works, and it looks like the
documentation is wrong, and not due to a recent code change.** I
think documentation should be changed to :

'the command and its arguments are first expanded as one big string'

Then improve it even more by adding the clarification that any empty
expansions will result in a different number of arguments passed to
the command, then give a simple example to illustrate. It's

...Todd

** I say that it's wrong because the code clearly expands it as one
string and then splits that result on spaces. Code in the transport
which calls the piped command was heavily touched in Mar/Apr 2013, and
the "expand as one big string" function call was adjusted in Nov 2013,
but it was that same way back in 4.75, and 4.69, basically untouched
since committed by Philip Hazel in 2004.

--
The total budget at all receivers for solving senders' problems is $0.
If you want them to accept your mail and manage it the way you want,
send it the way the spec says to. --John Levine