Discussion:
[Help-bash] readline does not always handle SIGTERM on reboot
john smith
2018-04-06 13:11:24 UTC
Permalink
Hello,

I'm using bash 4.4.18 with busybox. When reboot is done SIGTERM is
sent to all processes by busybox init. The problem is that while
SIGTERM is always handled by sigterm_sighandler() in bash at reboot
it's not always handled by rl_getc(). I did 10 attempts and SIGTERM
has been not handled by rl_getc() in 8 cases. It's problematic
because rl_get() returns EOF on SIGTERM and it makes bash perform
logout and save history.

When I start a new session via telnet and kill a previously started
bash session opened on serial console I can see that SIGTERM is
handled in rl_getc() every time.

What can be the reson rl_getc() does not handle SIGTERM on reboot?
--
<***@gmail.com>
Chet Ramey
2018-04-06 13:47:20 UTC
Permalink
Post by john smith
Hello,
I'm using bash 4.4.18 with busybox. When reboot is done SIGTERM is
sent to all processes by busybox init. The problem is that while
SIGTERM is always handled by sigterm_sighandler() in bash at reboot
it's not always handled by rl_getc(). I did 10 attempts and SIGTERM
has been not handled by rl_getc() in 8 cases. It's problematic
because rl_get() returns EOF on SIGTERM and it makes bash perform
logout and save history.
When I start a new session via telnet and kill a previously started
bash session opened on serial console I can see that SIGTERM is
handled in rl_getc() every time.
What can be the reson rl_getc() does not handle SIGTERM on reboot?
What does `handled' mean? If readline's signal handlers are installed,
the SIGTERM signal handler sets a flag. If the SIGTERM doesn't interrupt
read, nothing else happens until the read returns. If it does interrupt the
read, and readline is reading a command, it returns EOF. If it's not (for
instance, if it's reading an incremental search string), it returns an
error to allow the command to deal with it. The public interface that the
rest of readline uses (rl_read_key) checks for signal receipt and calls
the signal handler immediately after rl_getc returns.

There's no difference in how readline and bash handle signals between
receiving them from init and from another process.
--
``The lyf so short, the craft so long to lerne.'' - Chaucer
``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRU ***@case.edu http://tiswww.cwru.edu/~chet/
john smith
2018-04-06 14:25:10 UTC
Permalink
Post by Chet Ramey
Post by john smith
Hello,
I'm using bash 4.4.18 with busybox. When reboot is done SIGTERM is
sent to all processes by busybox init. The problem is that while
SIGTERM is always handled by sigterm_sighandler() in bash at reboot
it's not always handled by rl_getc(). I did 10 attempts and SIGTERM
has been not handled by rl_getc() in 8 cases. It's problematic
because rl_get() returns EOF on SIGTERM and it makes bash perform
logout and save history.
When I start a new session via telnet and kill a previously started
bash session opened on serial console I can see that SIGTERM is
handled in rl_getc() every time.
What can be the reson rl_getc() does not handle SIGTERM on reboot?
What does `handled' mean?
Sorry for not being clear enough. By not handled I mean that this
part rl_getc() is not run because apparently _rl_caught_signal is 0:

#if defined (SIGHUP)
else if (_rl_caught_signal == SIGHUP || _rl_caught_signal == SIGTERM)
#else
else if (_rl_caught_signal == SIGTERM)
#endif
return (RL_ISSTATE (RL_STATE_READCMD) ? READERR : EOF);
Post by Chet Ramey
If readline's signal handlers are installed,
the SIGTERM signal handler sets a flag. If the SIGTERM doesn't interrupt
read, nothing else happens until the read returns. If it does interrupt the
read, and readline is reading a command, it returns EOF. If it's not (for
instance, if it's reading an incremental search string), it returns an
error to allow the command to deal with it. The public interface that the
rest of readline uses (rl_read_key) checks for signal receipt and calls
the signal handler immediately after rl_getc returns.
There's no difference in how readline and bash handle signals between
receiving them from init and from another process.
That's my understanding as well, doing reboot should do the same to
bash as kill <BASH_INSTANCE_PID> from another session.
--
<***@gmail.com>
Chet Ramey
2018-04-06 14:39:57 UTC
Permalink
Post by john smith
Post by Chet Ramey
Post by john smith
Hello,
I'm using bash 4.4.18 with busybox. When reboot is done SIGTERM is
sent to all processes by busybox init. The problem is that while
SIGTERM is always handled by sigterm_sighandler() in bash at reboot
it's not always handled by rl_getc(). I did 10 attempts and SIGTERM
has been not handled by rl_getc() in 8 cases. It's problematic
because rl_get() returns EOF on SIGTERM and it makes bash perform
logout and save history.
When I start a new session via telnet and kill a previously started
bash session opened on serial console I can see that SIGTERM is
handled in rl_getc() every time.
What can be the reson rl_getc() does not handle SIGTERM on reboot?
What does `handled' mean?
Sorry for not being clear enough. By not handled I mean that this
#if defined (SIGHUP)
else if (_rl_caught_signal == SIGHUP || _rl_caught_signal == SIGTERM)
#else
else if (_rl_caught_signal == SIGTERM)
#endif
return (RL_ISSTATE (RL_STATE_READCMD) ? READERR : EOF);
Then readline is not active when the SIGTERM arrives, or its signal
handlers are not installed.
--
``The lyf so short, the craft so long to lerne.'' - Chaucer
``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRU ***@case.edu http://tiswww.cwru.edu/~chet/
john smith
2018-04-06 16:08:58 UTC
Permalink
Post by Chet Ramey
Post by john smith
Post by Chet Ramey
Post by john smith
Hello,
I'm using bash 4.4.18 with busybox. When reboot is done SIGTERM is
sent to all processes by busybox init. The problem is that while
SIGTERM is always handled by sigterm_sighandler() in bash at reboot
it's not always handled by rl_getc(). I did 10 attempts and SIGTERM
has been not handled by rl_getc() in 8 cases. It's problematic
because rl_get() returns EOF on SIGTERM and it makes bash perform
logout and save history.
When I start a new session via telnet and kill a previously started
bash session opened on serial console I can see that SIGTERM is
handled in rl_getc() every time.
What can be the reson rl_getc() does not handle SIGTERM on reboot?
What does `handled' mean?
Sorry for not being clear enough. By not handled I mean that this
#if defined (SIGHUP)
else if (_rl_caught_signal == SIGHUP || _rl_caught_signal == SIGTERM)
#else
else if (_rl_caught_signal == SIGTERM)
#endif
return (RL_ISSTATE (RL_STATE_READCMD) ? READERR : EOF);
Then readline is not active when the SIGTERM arrives, or its signal
handlers are not installed.
Hmm, maybe I don't understand something. I wrote the following tiny
program that does the same as busybox init:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <sys/reboot.h>

int main(void)
{
kill(-1, SIGTERM);

return EXIT_SUCCESS;
}

If I run it in the ttyS0 serial console I also can't see `logout'
string and other debug messages I added to rl_getc() what means that
readline does not handle SIGTERM. However, if I run the same program
from telnet session from another machine on my network i can see
`logout' string and my debug messages what means that readline session
active on the serial console gets and handles sigterm. i know that
`kill $$' is not allowed in the interactive session so maybe the
behavior is actually correct?

The only thing that bothers me is why some attempts of reboot done in
the serial console succeed?
--
<***@gmail.com>
john smith
2018-04-07 00:03:13 UTC
Permalink
Post by john smith
Post by Chet Ramey
Then readline is not active when the SIGTERM arrives, or its signal
handlers are not installed.
Hmm, maybe I don't understand something. I wrote the following tiny
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <sys/reboot.h>
int main(void)
{
kill(-1, SIGTERM);
return EXIT_SUCCESS;
}
If I run it in the ttyS0 serial console I also can't see `logout'
string and other debug messages I added to rl_getc() what means that
readline does not handle SIGTERM.
First of all, bash ignores SIGTERM when it's interactive, so if you don't
happen to send SIGTERM while readline is reading a command, you're not
going to get anything. (There is a recent post on bug-bash explaining why
readline returns EOF when it gets SIGTERM.)
Thanks, you helped me understand something. I discovered that the
above program would logout the current session I ran it in if I ran it
in the background from another program like that:

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

int main(void)
{
if(fork() == 0)
{
execv("/bin/kill-mine", NULL);
}
}

Or put daemon() at the top:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

int main(void)
{
daemon(0, 0);
kill(-1, SIGTERM);

return EXIT_SUCCESS;
}

Or simply run it with &:

$ kill-mine &

So the general conclusion is that it has be detached from the terminal
before sending SIGTERM to bash. I still don't know answers for 2
questions:

1. Where is the above checked in bash?

2. Why does busybox reboot make bash logout occasionally even if it
theoretically shouldn't because it does not detach from terminal? The
interesting thing is that reboot on my Slackware system behaves
differently - it always makes bash logout. I think that is because
`reboot' command informs init daemon that reboot should be done via
/dev/initctl named pipe and this is init process with PID 1 that
actually delivers SIGTERM to bash. As it's detached from terminal
readline returns EOF and bash correctly logs out.

(BTW, I've inadvertently sent a previous e-mail only to you and not a
mailing list, sorry).
--
<***@gmail.com>
john smith
2018-04-07 22:03:48 UTC
Permalink
I discovered that I can kill current session of bash with the following:

(sleep 1; kill $$) &

Only in such situation is rl_signal_handler() called, errno is set to
EINTR in rl_getc() and EOF is returned. I just don't understand why
rl_signal_handler() is not called when this is done:

kill $$

Instead, only sigterm_sighandler() is called. I checked that
rl_set_signals() is called each time after pressing Enter key so bash
should leave after doing the above too?
--
<***@gmail.com>
Chet Ramey
2018-04-09 13:41:11 UTC
Permalink
Post by john smith
(sleep 1; kill $$) &
Only in such situation is rl_signal_handler() called, errno is set to
EINTR in rl_getc() and EOF is returned. I just don't understand why
kill $$
Because readline's signal handlers are not installed when the calling
application is not making a call to readline().

Think about what happens when you run this. You hit return, readline
uninstalls its signal handlers and returns "kill $$" to bash, the calling
application. Bash takes that command and runs it, waiting for the command
to complete and collecting its return status. The command causes a signal
to be sent, which is handled by the bash SIGTERM signal handler.
--
``The lyf so short, the craft so long to lerne.'' - Chaucer
``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRU ***@case.edu http://tiswww.cwru.edu/~chet/
Chet Ramey
2018-04-09 13:38:18 UTC
Permalink
Post by john smith
First of all, bash ignores SIGTERM when it's interactive, so if you don't
happen to send SIGTERM while readline is reading a command, you're not
going to get anything. (There is a recent post on bug-bash explaining why
readline returns EOF when it gets SIGTERM.)
Thanks, you helped me understand something. I discovered that the
above program would logout the current session I ran it in if I ran it
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
if(fork() == 0)
{
execv("/bin/kill-mine", NULL);
}
}
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
int main(void)
{
daemon(0, 0);
kill(-1, SIGTERM);
return EXIT_SUCCESS;
}
$ kill-mine &
So the general conclusion is that it has be detached from the terminal
before sending SIGTERM to bash. I still don't know answers for 2
1. Where is the above checked in bash?
Sorry, the above what?
Post by john smith
2. Why does busybox reboot make bash logout occasionally even if it
theoretically shouldn't because it does not detach from terminal?
I don't run busybox, so I won't speculate. My first guess would be that
the signal comes in while bash is ignoring SIGTERM (by catching the signal
and discarding it).
--
``The lyf so short, the craft so long to lerne.'' - Chaucer
``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRU ***@case.edu http://tiswww.cwru.edu/~chet/
john smith
2018-04-09 16:55:15 UTC
Permalink
Post by Chet Ramey
Post by john smith
2. Why does busybox reboot make bash logout occasionally even if it
theoretically shouldn't because it does not detach from terminal?
I don't run busybox, so I won't speculate. My first guess would be that
the signal comes in while bash is ignoring SIGTERM (by catching the signal
and discarding it).
I've analyzed bash and readline during the weekend and I learned that
readline() overwrites bash SIGTERM handler and restores it after it
finishes, this is why `kill $$' does not kill the current session but
`(sleep 1; kill $$) &' does. I've also learned that busybox reboot
behave similarly to Slackware reboot, that is it sends SIGTERM to init
which in turns sends SIGTERM to all processes but reboot itself
returns too late and bash gets SIGTERM when it's still before next
readline(), just as you said.
--
<***@gmail.com>
Chet Ramey
2018-04-09 17:51:24 UTC
Permalink
Post by john smith
Post by Chet Ramey
I don't run busybox, so I won't speculate. My first guess would be that
the signal comes in while bash is ignoring SIGTERM (by catching the signal
and discarding it).
I've analyzed bash and readline during the weekend and I learned that
readline() overwrites bash SIGTERM handler and restores it after it
finishes, this is why `kill $$' does not kill the current session but
`(sleep 1; kill $$) &' does.
You left out the part where readline resends the signal to the calling
application, assuming the calling application hasn't set the signal
handler to SIG_IGN (in which case readline would not install a handler
for it at all).
--
``The lyf so short, the craft so long to lerne.'' - Chaucer
``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRU ***@case.edu http://tiswww.cwru.edu/~chet/
john smith
2018-04-09 18:56:04 UTC
Permalink
Post by Chet Ramey
Post by john smith
Post by Chet Ramey
I don't run busybox, so I won't speculate. My first guess would be that
the signal comes in while bash is ignoring SIGTERM (by catching the signal
and discarding it).
I've analyzed bash and readline during the weekend and I learned that
readline() overwrites bash SIGTERM handler and restores it after it
finishes, this is why `kill $$' does not kill the current session but
`(sleep 1; kill $$) &' does.
You left out the part where readline resends the signal to the calling
application, assuming the calling application hasn't set the signal
handler to SIG_IGN (in which case readline would not install a handler
for it at all).
Yes, I think I also got this. I wondered why history is saved when I
simply close my xterm window, now I know that readline() resends
SIGHUP back to bash in _rl_handle_signal().
--
<***@gmail.com>
Chet Ramey
2018-04-09 19:01:59 UTC
Permalink
Post by john smith
Post by Chet Ramey
Post by john smith
Post by Chet Ramey
I don't run busybox, so I won't speculate. My first guess would be that
the signal comes in while bash is ignoring SIGTERM (by catching the signal
and discarding it).
I've analyzed bash and readline during the weekend and I learned that
readline() overwrites bash SIGTERM handler and restores it after it
finishes, this is why `kill $$' does not kill the current session but
`(sleep 1; kill $$) &' does.
You left out the part where readline resends the signal to the calling
application, assuming the calling application hasn't set the signal
handler to SIG_IGN (in which case readline would not install a handler
for it at all).
Yes, I think I also got this. I wondered why history is saved when I
simply close my xterm window, now I know that readline() resends
SIGHUP back to bash in _rl_handle_signal().
That's the same reason that bash catches, but discards, SIGTERM and allows
readline to return EOF.
--
``The lyf so short, the craft so long to lerne.'' - Chaucer
``Ars longa, vita brevis'' - Hippocrates
Chet Ramey, UTech, CWRU ***@case.edu http://tiswww.cwru.edu/~chet/
Chet Ramey
2018-04-09 13:45:13 UTC
Permalink
Didn't send this one to the list.
Loading...