Mailing List Archive

pipe transport
we currently have some weird problems with our imap server. from time to time
it refused to work properly (segfaults). We are beginning to see a pattern...
anyway - that's not exim related...

...now comes the exim part: we use the pipe transport to deliver the mail:

local_delivery:
driver = pipe
command = "/usr/sbin/cyrdeliver ${local_part}"
return_output
log_output
prefix =
suffix =
user = cyrus
group = mail
home_directory = /tmp

local_lists_delivery:
driver = pipe
command = "/usr/sbin/cyrdeliver -m lists -a root ${local_part}"
return_output
log_output
prefix =
suffix =
user = cyrus
group = mail
home_directory = /tmp

now what happens: if imap server goes crazy also cyrdeliver segfaults!

Child process of local_delivery transport
(running command "/usr/sbin/cyrdeliver ${local_part}") was terminated by
signal 11 (Segmentation fault)

...now the problem is that all messages are bouncing! I'd prefer to have them
in the queue so I can bring the imap server back to life and don't loose any
mails...

I've looked into the manual and found something possible depending on the
result code - but the child *dies*. Any idea how to accomplish this?

We are using:

Exim version 3.35 #1 built 04-Mar-2002 23:05:40

Any hints?
--
Torsten
Re: pipe transport [ In reply to ]
--
On Tue, Jul 02, 2002 at 06:34:03PM +0200, Torsten Curdt wrote:

| now what happens: if imap server goes crazy also cyrdeliver segfaults!
|
| Child process of local_delivery transport
| (running command "/usr/sbin/cyrdeliver ${local_part}") was terminated by
| signal 11 (Segmentation fault)
|
| ...now the problem is that all messages are bouncing! I'd prefer to have them
| in the queue so I can bring the imap server back to life and don't loose any
| mails...
|
| I've looked into the manual and found something possible depending on the
| result code - but the child *dies*. Any idea how to accomplish this?

| Any hints?

You could create a wrapper program that returns a suitable exit code
if cyrdeliver segfaults. Then exim wouldn't know that cyrdeliver
segfaulted, only that the delivery temporarily failed.

-D

--

How great is the love the Father has lavished on us,
that we should be called children of God!
1 John 3:1

http://dman.ddts.net/~dman/

--
[ Content of type application/pgp-signature deleted ]
--
Re: Re: pipe transport [ In reply to ]
> | now what happens: if imap server goes crazy also cyrdeliver segfaults!
> |
> | Child process of local_delivery transport
> | (running command "/usr/sbin/cyrdeliver ${local_part}") was terminated by
> | signal 11 (Segmentation fault)
> |
> | ...now the problem is that all messages are bouncing! I'd prefer to have
> | them in the queue so I can bring the imap server back to life and don't
> | loose any mails...
> |
> | I've looked into the manual and found something possible depending on the
> | result code - but the child *dies*. Any idea how to accomplish this?
> |
> | Any hints?
>
> You could create a wrapper program that returns a suitable exit code
> if cyrdeliver segfaults. Then exim wouldn't know that cyrdeliver
> segfaulted, only that the delivery temporarily failed.

Do you thing a bash script could do that? Or do I need to code it by myself?

Thanks
--
Torsten
Re: Re: pipe transport [ In reply to ]
--
On Wed, Jul 03, 2002 at 10:12:12AM +0200, Torsten Curdt wrote:

| Do you thing a bash script could do that? Or do I need to code it by
| myself?

Yes.

Sample C program to crash on-demand <g>.


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
main( int argc , char** argv )
{
char* null = NULL ;

if ( argc >= 2 && strcmp( argv[1] , "-segv" ) == 0 )
{
printf( "null %d\n" , null ) ;
fflush( stdout ) ;
printf( "*null %d\n" , *null ) ;
fflush( stdout ) ;
printf( "*null %s\n" , *null ) ;
fflush( stdout ) ;
*null ;
}
return 0 ;
}


(the printf()s are there to prevent gcc from optimizing out the
pointer dereferencing and eliminating the crash)

Sample shell script to show what can be done :


$ ./a.out -segv && echo "success" || echo "failure" $?
null 0
failure 139


That exit code of 139 is very consistent on my system. You might even
be able to simply list that as a temporary error in your exim config.

-D

--

The truly righteous man attains life,
but he who pursues evil goes to his death.
Proverbs 11:19

http://dman.ddts.net/~dman/

--
[ Content of type application/pgp-signature deleted ]
--
Re: Re: Re: pipe transport [ In reply to ]
Hi, Derrick,

> | Do you thing a bash script could do that? Or do I need to code it by
> | myself?
>
> Yes.
>
> Sample C program to crash on-demand <g>.

probably the most useful program ever written ;-)
...but thanks for the code!

<snip/>

> That exit code of 139 is very consistent on my system. You might even
> be able to simply list that as a temporary error in your exim config.

do you really think so? I was assuming that the whole exim child dies and I
will have no return code at all!? so I thought you mean a wrapper to catch
the signal and return a specific code then...

Or did I get you wrong?

Thanks
--
Torsten
Re: Re: Re: pipe transport [ In reply to ]
--
On Thu, Jul 04, 2002 at 02:00:40PM +0200, Torsten Curdt wrote:
| Hi, Derrick,
|
| > | Do you thing a bash script could do that? Or do I need to code it by
| > | myself?
| >
| > Yes.
| >
| > Sample C program to crash on-demand <g>.
|
| probably the most useful program ever written ;-)

LOL!

| ...but thanks for the code!

You're welcome.

| <snip/>
|
| > That exit code of 139 is very consistent on my system. You might even
| > be able to simply list that as a temporary error in your exim config.
|
| do you really think so?

Yes.

| I was assuming that the whole exim child dies and I will have no
| return code at all!?

The child on the receiving end of the pipe dies, and apparently the
exim process gets back a return code that indicates the child died
with SEGV (which is how it can log it and handle it rather gracefully).

| so I thought you mean a wrapper to catch the signal and return a
| specific code then...

That's what I meant, but as I was creating that wrapper (the one-liner
I posted, just replace the echo's with exit) I noticed that the shell
did have an exit code from the bad C program. Knowing that, the
solution can be simplified to simply telling exim to treat that code
as a temporary failure. Try it and find out! (that's what the C
program is for -- it allows you to control when it crashes and when it
doesn't for testing)

-D

--

In his heart a man plans his course,
but the Lord determines his steps.
Proverbs 16:9

http://dman.ddts.net/~dman/

--
[ Content of type application/pgp-signature deleted ]
--
Re: Re: Re: Re: pipe transport [ In reply to ]
>| I was assuming that the whole exim child dies and I will have no
>| return code at all!?
>
>The child on the receiving end of the pipe dies, and apparently the
>exim process gets back a return code that indicates the child died
>with SEGV (which is how it can log it and handle it rather gracefully).
>
>| so I thought you mean a wrapper to catch the signal and return a
>| specific code then...
>
>That's what I meant, but as I was creating that wrapper (the one-liner
>I posted, just replace the echo's with exit) I noticed that the shell
>did have an exit code from the bad C program. Knowing that, the
>solution can be simplified to simply telling exim to treat that code
>as a temporary failure. Try it and find out! (that's what the C
>program is for -- it allows you to control when it crashes and when it
>doesn't for testing)

Hi, Derrick this is what I tried:

# crash ; echo $? -> 0
# crash -segv ; echo $? -> 139

So I've setup two directors and transports:

test_ok_delivery:
driver = pipe
command = "/tmp/crash"
temp_errors = 139
ignore_status = false
return_output
log_output
prefix =
suffix =
user = cyrus
group = mail
home_directory = /tmp

test_crash_delivery:
driver = pipe
command = "/tmp/crash -segv"
temp_errors = 139
ignore_status = false
return_output
log_output
prefix =
suffix =
user = cyrus
group = mail
home_directory = /tmp

...


test_ok_director:
local_parts = "test-ok"
driver = smartuser
transport = test_ok_delivery

test_crash_director:
local_parts = "test-crash"
driver = smartuser
transport = test_crash_delivery


Then I tried them out:

# ( echo "To: test-ok" ; echo ; echo "body" ) | exim -t -d9
# ( echo "To: test-crash" ; echo ; echo "body" ) | exim -t -d9

The first works just fine (as exspected) but the second one gives a "mail
delivery failed":

A message that you sent could not be delivered to one or more of its
recipients. This is a permanent error. The following address(es) failed:

test-crash@dff.st.spam.no
Child process of test_crash_delivery transport
(running command"/tmp/crash -segv") was terminated by
signal 11 (Segmentation fault)

The following text was generated during the delivery attempt:

------ test-crash@dff.st.spam.no ------

null 0

^--- watch this!

At least me for it looks like it's not that easy because the child that dies
with a segfault seems to return 0 instead of 139:((
Or did I get something wrong here?
--
Torsten
Re: Re: Re: Re: pipe transport [ In reply to ]
--
On Fri, Jul 05, 2002 at 02:37:14PM +0200, Torsten Curdt wrote:

| # ( echo "To: test-crash" ; echo ; echo "body" ) | exim -t -d9
|
| The first works just fine (as exspected) but the second one gives a "mail
| delivery failed":
|
| A message that you sent could not be delivered to one or more of its
| recipients. This is a permanent error. The following address(es) failed:
|
| test-crash@dff.st.spam.no
| Child process of test_crash_delivery transport
| (running command"/tmp/crash -segv") was terminated by
| signal 11 (Segmentation fault)
|
| The following text was generated during the delivery attempt:
|
| ------ test-crash@dff.st.spam.no ------
|
| null 0
|
| ^--- watch this!
|
| At least me for it looks like it's not that easy because the child that dies
| with a segfault seems to return 0 instead of 139:((
| Or did I get something wrong here?

That text has nothing to do with the exit code (the "null 0"). It
comes from a printf() statement in the code that prints out the value
of the null pointer as an integer (which will always be 0).

Hmm, I just realized that any output probably makes exim consider the
delivery to have failed, but since you still get the seg fault message
I don't think that is relevant.

Try using a shell script like this instead then (untested) :


#!/bin/sh

# do this as if we really were sending the input (message) to cyrdeliver
cat | crash -segv
ECODE=$?

# determine what exit code we really want to give to exim
if [ $ECODE == 139 ] ; then
# temporary error
exit 75 ;
fi
exit $ECODE


-D

--

Religion that God our Father accepts as pure and faultless is this: to
look after orphans and widows in their distress and to keep oneself from
being polluted by the world.
James 1:27

http://dman.ddts.net/~dman/

--
[ Content of type application/pgp-signature deleted ]
--
Re: Re: Re: Re: pipe transport [ In reply to ]
> That text has nothing to do with the exit code (the "null 0"). It
> comes from a printf() statement in the code that prints out the value
> of the null pointer as an integer (which will always be 0).
>
> Hmm, I just realized that any output probably makes exim consider the
> delivery to have failed, but since you still get the seg fault message
> I don't think that is relevant.
>
> Try using a shell script like this instead then (untested) :

It seems not to depend on the exit code only. This one works fine:

#!/bin/sh
if [ "$1" == "" ]; then
logger "should be delivered"
exit 0
else
logger "should stay in q"
exit 139
fi

while this one does not work correctly:

#!/bin/sh
/bin/cat - | /tmp/crash.bin $1
ECODE=$?
logger "crash exit code is $ECODE"
if [ $ECODE == 139 ] ; then
logger "should stay in q"
exit 139
else
logger "should be delivered"
exit 0
fi

Both should actually give the same result. As you stated this might be of the
output of the signal handler. So I tried:

#!/bin/sh
(
/bin/cat - | /tmp/crash.bin $1
) 1>/dev/null 2>/dev/null
ECODE=$?
logger "crash exit code is $ECODE"
if [ $ECODE == 139 ] ; then
logger "should stay in q"
exit 139
else
logger "should be delivered"
exit 0
fi

Which seem to give the exspected result. I looked again in the FM and found I
should set preferably:

temp_errors = 139
return_fail_output = true
return_output = false

unfortunately this seems only to include stdout *NOT* stderr so I created a
wrapper script that does the trick:

#!/bin/sh
(
/bin/cat - | /tmp/crash.bin $*
) 2>/dev/null

Shouldn't return_fail_output include stderr?
--
Torsten