notmuch vs. SIGPIPE

Thomas Schwinge thomas at codesourcery.com
Mon Jan 20 14:36:51 PST 2020


Hi!

On 2020-01-20T12:55:28+0100, I wrote:
> While looking a bit into the item raised in
> id:87muamgspy.fsf at euler.schwinge.homeip.net I noticed the following
> (mis?)behavior by notmuch.
>
> To set the stage:
>
>     $ yes | head -n 1
>     y
>     $ echo "${PIPESTATUS[@]}"
>     141 0
>
> As expected, the 'yes' process exits with SIGPIPE right after the 'head'
> process terminated.

See also <https://pmhahn.github.io/SIGPIPE/>, for example.

> However:
>
>     $ notmuch search \* | head -n 1 & sleep 22; jobs; ps -f
>     [1] 622009
>     thread:0000000000032bb2   the future [1/1] Jenna Moss; Steve Burbon, Washington (hurd list spam)
>     [1]+  Running                 notmuch search \* | head -n 1 &
>     UID          PID    PPID  C STIME TTY          TIME CMD
>     thomas    621851    4297  0 12:38 pts/38   00:00:00 /bin/bash
>     thomas    622008  621851 99 12:48 pts/38   00:00:22 /home/thomas/command/notmuch.real search \*
>     thomas    622013  621851  0 12:48 pts/38   00:00:00 ps -f
>
> Even after its "pipe-consumer" 'head' process has terminated, the
> 'notmuch' process still keeps running, and running, and running...  It
> has to be killed manually (unless it before exits because of concurrent
> database modification).  This doesn't seem expected behavior to me?
>
> Now, I do have a patch or two (actually dozensa; reverts, WIP etc.) on
> top of months-old notmuch sources, so I'll later try to reproduce that
> with clean sources.

    $ gdb -q --args notmuch dump
    Reading symbols from notmuch...
    (gdb) break sigaction
    Breakpoint 1 at 0xe130
    (gdb) r
    Starting program: [...]/notmuch dump
    [Thread debugging using libthread_db enabled]
    Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
    
    Breakpoint 1, __GI___sigaction (sig=13, act=0x0, oact=0x7fffffffd4e0) at ../nptl/sigaction.c:24
    24      ../nptl/sigaction.c: No such file or directory.
    (gdb) bt
    #0  0x00007ffff77e92f0 in __GI___sigaction (sig=13, act=0x0, oact=0x7fffffffd4e0) at ../nptl/sigaction.c:24
    #1  0x00007ffff75bfa8d in  () at /usr/lib/x86_64-linux-gnu/libgpgme.so.11
    #2  0x00007ffff75c64cd in gpgme_check_version () at /usr/lib/x86_64-linux-gnu/libgpgme.so.11
    #3  0x00007ffff75c6667 in gpgme_check_version_internal () at /usr/lib/x86_64-linux-gnu/libgpgme.so.11
    #4  0x00007ffff7f456b3 in g_mime_init () at /usr/lib/x86_64-linux-gnu/libgmime-3.0.so.0
    #5  0x000055555556539d in main (argc=2, argv=0x7fffffffd828) at notmuch.c:469

So, libgpgme via libgmime initialization is doing something with signals.
Here, 'sig=13' is SIGPIPE, 'act=0x0' means to just query, and store
current handling into 'oact':

    (gdb) finish
    Run till exit from #0  __GI___sigaction (sig=13, act=0x0, oact=0x7fffffffd4e0) at ../nptl/sigaction.c:24
    0x00007ffff75bfa8d in ?? () from /usr/lib/x86_64-linux-gnu/libgpgme.so.11
    Value returned is $1 = 0
    (gdb) print *(struct sigaction *) 0x7fffffffd4e0
    $2 = {__sigaction_handler = {sa_handler = 0x0, sa_sigaction = 0x0}, sa_mask = {__val = {0, 8, 93824992565632, 0, 32, 88, 0, 22, 0, 140733193388034, 93823560581120, 7, 93824992559120, 32, 5, 93824992694848}}, sa_flags = 0, sa_restorer = 0x0}

A '0x0' '__sigaction_handler' means 'SIG_DFL', which for SIGPIPE means to
terminate the process.  This is as expected.  However, then:

    (gdb) continue 
    Continuing.
    
    Breakpoint 1, __GI___sigaction (sig=13, act=0x7fffffffd4e0, oact=0x0) at ../nptl/sigaction.c:24
    24      in ../nptl/sigaction.c
    (gdb) bt
    #0  0x00007ffff77e92f0 in __GI___sigaction (sig=13, act=0x7fffffffd4e0, oact=0x0) at ../nptl/sigaction.c:24
    #1  0x00007ffff75bfadc in  () at /usr/lib/x86_64-linux-gnu/libgpgme.so.11
    #2  0x00007ffff75c64cd in gpgme_check_version () at /usr/lib/x86_64-linux-gnu/libgpgme.so.11
    #3  0x00007ffff75c6667 in gpgme_check_version_internal () at /usr/lib/x86_64-linux-gnu/libgpgme.so.11
    #4  0x00007ffff7f456b3 in g_mime_init () at /usr/lib/x86_64-linux-gnu/libgmime-3.0.so.0
    #5  0x000055555556539d in main (argc=2, argv=0x7fffffffd828) at notmuch.c:469
    (gdb) print *act
    $3 = {__sigaction_handler = {sa_handler = 0x1, sa_sigaction = 0x1}, sa_mask = {__val = {0 <repeats 16 times>}}, sa_flags = 0, sa_restorer = 0x0}

A '0x1' '__sigaction_handler' means 'SIG_IGN', ignore any such signals.
This is unexpected (to me, at least), not what I'd expect with notmuch?

According to a (very quick) check/survey, this has apparently been the
case "forever", and is documented on
<https://www.gnupg.org/documentation/manuals/gpgme/Signal-Handling.html>,
together with some rationale.  Aha, aha, OK, I see.

So, assuming we want to keep it that way (given the gpgme rationale), is
the problem then that we fail to handle appropriately in notmuch any
EPIPE that we may be getting from 'write' etc.?  This remains to be
explored another day.


Grüße
 Thomas


More information about the notmuch mailing list