Обсуждение: [MASSMAIL]In MacOS, psql reacts on SIGINT in a strange fashion (Linux is fine)

Поиск
Список
Период
Сортировка

[MASSMAIL]In MacOS, psql reacts on SIGINT in a strange fashion (Linux is fine)

От
Dmitry Koterov
Дата:
Hi.

Preamble: this happens in MacOS only (in built-in Terminal and in iTerm2 at least). In Linux, everything is as expected.

I almost lost my mind today trying to figure out why sending a SIGINT precisely to a psql interactive process delivers this SIGINT not only to that psql, but also to its parents. 

Notice that it is NOT about Ctrl-C at all (which would've been delivered to the whole process group; but again, it's not about it, there is no Ctrl-C; I even used pliers to remove "Ctrl" and "C" keys from my keyboard). It is about a plain old "kill -SIGINT psql_pid" executed manually in a separate shell.

I tried to find a place in psql source code where it fans out SIGINT to its parent pids upon receiving. But I did not find any. There is also no shell involved in all these (see logs below).

Any ideas, who is doing this on MacOS? Who is sending 2 extra SIGINTs to 2 parents when a SIGINT is delivered to psql?

I also tried to use any other signal-trapping program instead of psql (like vim or an artificial Perl script which ignores signals) and did not observe the same behavior. It looks like something very specific to psql.

Steps to reproduce:

===========================

Terminal 1:

% psql --version
psql (PostgreSQL) 16.0

% cat /tmp/my1.pl
#!/usr/bin/perl -w
if (fork()) {
  $SIG{INT} = sub { print("int in my1\n") };
  while (1) { wait() and exit(1); }
} else {
  exec("/tmp/my2.pl");
}

% cat /tmp/my2.pl
#!/usr/bin/perl -w
if (fork()) {
  $SIG{INT} = sub { print("int in my2\n") };
  while (1) { wait() and exit(1); }
} else {
  exec("psql");
}

# PostgreSQL server is running in Docker, localhost remapped port 15432
% PGHOST=127.0.0.1 PGPORT=15432 PGUSER=postgres PGPASSWORD=postgres PGDATABASE=postgres /tmp/my1.pl
psql (16.0, server 13.5 (Debian 13.5-1.pgdg110+1))
Type "help" for help.
postgres=#


Terminal 2:

% pstree | grep -E "bash |yarn|psql|vim|sleep|lldb|my.pl|perl" | grep dmitry | grep -v grep
 
 |   |   \-+= 24550 dmitry /usr/bin/perl -w /tmp/my1.pl
 |   |     \-+- 24551 dmitry /usr/bin/perl -w /tmp/my2.pl
 |   |       \--- 24552 dmitry psql

% pgrep psql
24552
% kill -SIGINT 24552


And I see this in Terminal 1:

postgres=#
int in my2
int in my1
postgres=#

===========================

I.e. we can see that not only psql got the SIGINT received, but also its parents (two artificial Perl processes).

Re: In MacOS, psql reacts on SIGINT in a strange fashion (Linux is fine)

От
Tom Lane
Дата:
Dmitry Koterov <dmitry.koterov@gmail.com> writes:
> I almost lost my mind today trying to figure out why sending a SIGINT
> precisely to a psql interactive process delivers this SIGINT not only to
> that psql, but also to its parents.

Let me guess ... you're using zsh not bash?

I do not use zsh myself, but what I read in its man page suggests
that this is its designed behavior.  The kill command says its
arguments are "jobs", and elsewhere it says

       There are several ways to refer to jobs in the shell.  A job can be
       referred to by the process ID of any process of the job or by one of
       the following: ...

so I suspect zsh is treating that stack of processes as a "job" and
zapping all of it.  There is certainly nothing in psql that would
attempt to signal its parent process (let alone grandparent).

            regards, tom lane



Re: In MacOS, psql reacts on SIGINT in a strange fashion (Linux is fine)

От
Dmitry Koterov
Дата:
On Sat, Apr 13, 2024 at 7:53 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Let me guess ... you're using zsh not bash?

I wish it was zsh... I tested it with zsh, but with bash (and with low-level kill syscall), I observed the same effect unfortunately. 

So it's still a puzzle.

1. Even more, when I send a kill() low-level syscall using e.g. Perl - perl -e 'kill("INT", 16107)' - it is the same.
2. If any other program but psql is used (e.g. vim or my custom Perl script which ignores SIGINT), the effect is not reproducible.

Can it be e.g. readline? Or something related to tty or session settings which psql could modify (I did not find any in the source code though).

=========================

chsh -s /bin/bash
# then kill all terminals

$ PGHOST=127.0.0.1 PGPORT=15432 PGUSER=postgres PGPASSWORD=postgres PGDATABASE=postgres /tmp/my1.pl
...
postgres=#

$ ps | grep zsh | grep -v grep
<empty>

$ watch -n0.5 'pstree | grep -E "yarn|psql|vim|sleep|lldb|my.pl|perl|debugserver|-bash| login" | grep -v grep'
 |   |-+= 13721 root login -fp dmitry
 |   | \-+= 13722 dmitry -bash
 |   |-+= 13700 root login -fp dmitry
 |   | \-+= 13701 dmitry -bash
 |   |   \-+= 16105 dmitry /usr/bin/perl -w /tmp/my1.pl
 |   |     \-+- 16106 dmitry /usr/bin/perl -w /tmp/my2.pl
 |   |       \--- 16107 dmitry psql
 |   \-+= 13796 root login -fp dmitry
 |     \--= 13797 dmitry -bash

$ perl -e 'kill("INT", 16107)'

I observe as previously:

int in my2
int in my1
postgres=# 

=========================


When I use a custom SIGINT-trapping script instead of psql, it all works as expected in MacOS, only that script receives the SIGINT and not its parents:

=========================

$ cat /tmp/ignore.pl
#!/usr/bin/perl -w
$SIG{INT} = sub { print("int ignored\n") };
while (1) { sleep(1); }

$ cat /tmp/my2.pl
#!/usr/bin/perl -w
if (fork()) {
  $SIG{INT} = sub { print("int in my2\n") };
  while (1) { wait() and exit(1); }
} else {
  exec("/tmp/ignore.pl");
}


=========================


CleanShot 2024-04-13 at 16.07.08@2x.png
Вложения

Re: In MacOS, psql reacts on SIGINT in a strange fashion (Linux is fine)

От
"David G. Johnston"
Дата:
On Saturday, April 13, 2024, Dmitry Koterov <dmitry.koterov@gmail.com> wrote:


% psql --version
psql (PostgreSQL) 16.0

How did you install this and can you install other, supported, versions?

David J. 

Re: In MacOS, psql reacts on SIGINT in a strange fashion (Linux is fine)

От
Thomas Munro
Дата:
On Sun, Apr 14, 2024 at 11:18 AM Dmitry Koterov
<dmitry.koterov@gmail.com> wrote:
> Can it be e.g. readline? Or something related to tty or session settings which psql could modify (I did not find any
inthe source code though). 

I was wondering about that.  Are you using libedit or libreadline?
What happens if you build without readline/edit support?  From a quick
glance at libedit, it does a bunch of signal interception, but I
didn't check the details.  It is interested in stuff like SIGWINCH,
the window-resized-by-user signal.



Re: In MacOS, psql reacts on SIGINT in a strange fashion (Linux is fine)

От
Tom Lane
Дата:
Dmitry Koterov <dmitry.koterov@gmail.com> writes:
> I wish it was zsh... I tested it with zsh, but with bash (and with
> low-level kill syscall), I observed the same effect unfortunately.

> So it's still a puzzle.

> 1. Even more, when I send a kill() low-level syscall using e.g. Perl - perl
> -e 'kill("INT", 16107)' - it is the same.
> 2. If any other program but psql is used (e.g. vim or my custom Perl script
> which ignores SIGINT), the effect is not reproducible.

> Can it be e.g. readline? Or something related to tty or session settings
> which psql could modify (I did not find any in the source code though).

OK, I tried dtruss'ing psql on macOS.  What I see is that with
Apple's libedit, the response to SIGINT includes this:

kill(0, 2)               = 0 0

that is, "SIGINT my whole process group".  If I build with libreadline
from MacPorts, I just see

kill(30902, 2)           = 0 0

(30902 being the process's own PID).  I'm not real sure why either
library finds it necessary to re-signal the process --- maybe they
trap SIGINT and then try to hide that by reinstalling the app's
normal SIGINT handler and re-delivering the signal.  But anyway,
libedit seems to be vastly exceeding its authority here.  If
signaling the whole process group were wanted, it would have been
the responsibility of the original signaller to do that.

            regards, tom lane



Re: In MacOS, psql reacts on SIGINT in a strange fashion (Linux is fine)

От
Thomas Munro
Дата:
On Sun, Apr 14, 2024 at 11:49 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
> Dmitry Koterov <dmitry.koterov@gmail.com> writes:
> > I wish it was zsh... I tested it with zsh, but with bash (and with
> > low-level kill syscall), I observed the same effect unfortunately.
>
> > So it's still a puzzle.
>
> > 1. Even more, when I send a kill() low-level syscall using e.g. Perl - perl
> > -e 'kill("INT", 16107)' - it is the same.
> > 2. If any other program but psql is used (e.g. vim or my custom Perl script
> > which ignores SIGINT), the effect is not reproducible.
>
> > Can it be e.g. readline? Or something related to tty or session settings
> > which psql could modify (I did not find any in the source code though).
>
> OK, I tried dtruss'ing psql on macOS.  What I see is that with
> Apple's libedit, the response to SIGINT includes this:
>
> kill(0, 2)               = 0 0
>
> that is, "SIGINT my whole process group".  If I build with libreadline
> from MacPorts, I just see
>
> kill(30902, 2)           = 0 0
>
> (30902 being the process's own PID).  I'm not real sure why either
> library finds it necessary to re-signal the process --- maybe they
> trap SIGINT and then try to hide that by reinstalling the app's
> normal SIGINT handler and re-delivering the signal.  But anyway,
> libedit seems to be vastly exceeding its authority here.  If
> signaling the whole process group were wanted, it would have been
> the responsibility of the original signaller to do that.

Probably to intercept SIGWINCH -- I assume it's trying to propagate
the signal to the "real" signal handler, but it's propagating it a
little too far.

https://github.com/NetBSD/src/blob/1de18f216411bce77e26740327b0782976a89965/lib/libedit/sig.c#L110



Re: In MacOS, psql reacts on SIGINT in a strange fashion (Linux is fine)

От
Dmitry Koterov
Дата:
> OK, I tried dtruss'ing psql on macOS.  What I see is that with
> Apple's libedit, the response to SIGINT includes this:
> kill(0, 2)               = 0 0

OK, so it's libedit who does this. I should've tried drtuss instead of not quite working lldb. I'll try to dig further. 

Thank you, Tom!



> How did you install this and can you install other, supported, versions?
...
I was wondering about that.  Are you using libedit or libreadline?

I did not build psql, I use the version delivered by brew on MacOS. 

I think Tom already found out that it's libedit who is guilty. But for the sake of history, posting the following below.

I just wrote a small C wrapper which prints, WHO is sending that SIGINT to the parent processes, and it was indeed the psql process:

================

bash-3.2$ ./0-build.sh && ./1-run.sh
+ gcc my0.c -o my0
+ ./my0
my0 pid is 45710
psql (16.0, server 13.5 (Debian 13.5-1.pgdg110+1))
Type "help" for help.
postgres=#
int in my2
int in my1
my0 got signal 2 from process 45723 running as user 501
postgres=#

bash-3.2$ ./2-watch.sh
Every 0.5s: pstree | grep -E "bash |yarn|psql|vim|sleep|lldb|my.pl|perl|debugserver|my0|my1|my2" | grep dmitry | grep -v grep
 |   |     \-+= 36468 dmitry bash -rcfile .bashrc
 |   |       \-+= 45709 dmitry /bin/bash ./1-run.sh
 |   |         \-+- 45710 dmitry ./my0
 |   |           \-+- 45711 dmitry /usr/bin/perl -w ./my1.pl
 |   |             \-+- 45712 dmitry /usr/bin/perl -w /tmp/my2.pl
 |   |               \--- 45723 dmitry psql
 |   |   \-+= 44170 dmitry /bin/bash ./2-watch.sh

bash-3.2$ ./3-kill.sh
+ perl -e 'kill("INT", `pgrep psql`)'


bash-3.2$ cat ./my0.c
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>

void handler(int signal, siginfo_t *info, void *data) {
  printf("my0 got signal %d from process %d running as user %d\n",
         signal, info->si_pid, info->si_uid);
}

int main(void) {
  printf("my0 pid is %d\n", getpid());
  if (fork()) {
    struct sigaction sa;
    sigset_t mask;
    sigemptyset(&mask);
    sa.sa_sigaction = &handler;
    sa.sa_mask = mask;
    sa.sa_flags = SA_SIGINFO;
    sigaction(SIGINT, &sa, NULL);
    while (wait(NULL) == -1);
  } else {
    if (execl("./my1.pl", "my1", NULL) == -1) {
      perror("execl");
    }
  }
  return 0;
}


================



On Sat, Apr 13, 2024 at 4:49 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Dmitry Koterov <dmitry.koterov@gmail.com> writes:
> I wish it was zsh... I tested it with zsh, but with bash (and with
> low-level kill syscall), I observed the same effect unfortunately.

> So it's still a puzzle.

> 1. Even more, when I send a kill() low-level syscall using e.g. Perl - perl
> -e 'kill("INT", 16107)' - it is the same.
> 2. If any other program but psql is used (e.g. vim or my custom Perl script
> which ignores SIGINT), the effect is not reproducible.

> Can it be e.g. readline? Or something related to tty or session settings
> which psql could modify (I did not find any in the source code though).

OK, I tried dtruss'ing psql on macOS.  What I see is that with
Apple's libedit, the response to SIGINT includes this:

kill(0, 2)               = 0 0

that is, "SIGINT my whole process group".  If I build with libreadline
from MacPorts, I just see

kill(30902, 2)           = 0 0

(30902 being the process's own PID).  I'm not real sure why either
library finds it necessary to re-signal the process --- maybe they
trap SIGINT and then try to hide that by reinstalling the app's
normal SIGINT handler and re-delivering the signal.  But anyway,
libedit seems to be vastly exceeding its authority here.  If
signaling the whole process group were wanted, it would have been
the responsibility of the original signaller to do that.

                        regards, tom lane

Re: In MacOS, psql reacts on SIGINT in a strange fashion (Linux is fine)

От
Tom Lane
Дата:
Thomas Munro <thomas.munro@gmail.com> writes:
> On Sun, Apr 14, 2024 at 11:49 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
>> OK, I tried dtruss'ing psql on macOS.  What I see is that with
>> Apple's libedit, the response to SIGINT includes this:
>> kill(0, 2)               = 0 0

> https://github.com/NetBSD/src/blob/1de18f216411bce77e26740327b0782976a89965/lib/libedit/sig.c#L110

Ah, I was wondering if that was from upstream libedit or was an
Apple-ism.  Somebody should file a bug.

            regards, tom lane



Re: In MacOS, psql reacts on SIGINT in a strange fashion (Linux is fine)

От
Dmitry Koterov
Дата:
Thanks to everyone!

I will file a bug.

Anyways, I just built a tool to work-around it all. It allows to run psql from processes which don't handle SIGINT in a proper shell-like manner (like yarn for instance): https://github.com/dimikot/run-in-separate-pgrp

Basically, without this, an attempt to run `yarn psql` and then pressing ^C kills yarn (it kills it in Linux too, since ^C propagates SIGINT to all processes in the foreground group), because yarn doesn't behave nicely with exit codes (and exit signals) of its child processes. With run-in-separate-pgrp wrapping, ^C is only delivered to psql.


On Sat, Apr 13, 2024 at 5:09 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Thomas Munro <thomas.munro@gmail.com> writes:
> On Sun, Apr 14, 2024 at 11:49 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
>> OK, I tried dtruss'ing psql on macOS.  What I see is that with
>> Apple's libedit, the response to SIGINT includes this:
>> kill(0, 2)               = 0 0

> https://github.com/NetBSD/src/blob/1de18f216411bce77e26740327b0782976a89965/lib/libedit/sig.c#L110

Ah, I was wondering if that was from upstream libedit or was an
Apple-ism.  Somebody should file a bug.

                        regards, tom lane