|
Home > Archive > Unix Programming > June 2006 > Who restores the terminal when default signal handler is active?
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
Who restores the terminal when default signal handler is active?
|
|
| richard.kreckel@framatome-anp.com 2006-06-16, 1:32 pm |
| Hi,
Here is a little C-program that sets the terminal into raw mode and
waits for 10 seconds for the user to press Ctr-C. When that happens, a
signal handler is invoked that calls exit(2).
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <termios.h>
#include <unistd.h>
void sighandler(int /*sig*/)
{
exit(2);
}
int main()
{
signal(SIGINT, sighandler); // Without this, terminal is reset!
termios termio;
tcgetattr(0, &termio);
termio.c_iflag &= ~( BRKINT | PARMRK | INPCK | IXOFF );
termio.c_iflag |= ( IGNBRK | IGNPAR );
termio.c_lflag &= ~( ICANON | ECHO | ECHOE | ECHOK | ECHONL );
termio.c_lflag |= ( ISIG );
tcsetattr(0, TCSADRAIN, &termio);
fprintf(stderr, "Now hit Ctrl-C\n"); fflush(stderr);
sleep( 10 );
}
If I do that on my SuSE Linux box, the terminal is left in a very
strange mode. Okay, I expected that, since I didn't write any code to
restore the terminal to its orginal settings. However, with the default
signal hander active, the terminal is magically reset.
My question is: who resets the terminal? Bash? Readline? The Great
SIG_DFL Handler(tm)? A conspiration thereof? Anyone else?
Thanks in advance
-richy.
--
Richard B. Kreckel
<http://www.ginac.de/~kreckel/>
| |
| Fletcher Glenn 2006-06-16, 1:32 pm |
|
<richard.kreckel@framatome-anp.com> wrote in message
news:1150475871.145035.102860@h76g2000cwa.googlegroups.com...
> Hi,
>
> Here is a little C-program that sets the terminal into raw mode and
> waits for 10 seconds for the user to press Ctr-C. When that happens, a
> signal handler is invoked that calls exit(2).
>
> #include <stdio.h>
> #include <stdlib.h>
> #include <signal.h>
> #include <termios.h>
> #include <unistd.h>
>
> void sighandler(int /*sig*/)
> {
> exit(2);
> }
>
> int main()
> {
> signal(SIGINT, sighandler); // Without this, terminal is reset!
> termios termio;
> tcgetattr(0, &termio);
> termio.c_iflag &= ~( BRKINT | PARMRK | INPCK | IXOFF );
> termio.c_iflag |= ( IGNBRK | IGNPAR );
> termio.c_lflag &= ~( ICANON | ECHO | ECHOE | ECHOK | ECHONL );
> termio.c_lflag |= ( ISIG );
> tcsetattr(0, TCSADRAIN, &termio);
> fprintf(stderr, "Now hit Ctrl-C\n"); fflush(stderr);
> sleep( 10 );
> }
>
> If I do that on my SuSE Linux box, the terminal is left in a very
> strange mode. Okay, I expected that, since I didn't write any code to
> restore the terminal to its orginal settings. However, with the default
> signal hander active, the terminal is magically reset.
>
> My question is: who resets the terminal? Bash? Readline? The Great
> SIG_DFL Handler(tm)? A conspiration thereof? Anyone else?
>
> Thanks in advance
> -richy.
> --
> Richard B. Kreckel
> <http://www.ginac.de/~kreckel/>
>
Your program is responsible for restoring the terminal state. I would
recommend copying your original termios struct (just after tcgetattr) into a
global, and using that to restore the appropriate flags within the signal
handler.
--
Fletcher Glenn
| |
| richard.kreckel@framatome-anp.com 2006-06-16, 7:20 pm |
| > Your program is responsible for restoring the terminal state. I would
> recommend copying your original termios struct (just after tcgetattr) into a
> global, and using that to restore the appropriate flags within the signal
> handler.
That's beside the point: Something *is* restoring it for me. I would be
interested knowing what that is?
| |
| Gordon Burditt 2006-06-16, 7:20 pm |
| >> Your program is responsible for restoring the terminal state. I would
>
>That's beside the point: Something *is* restoring it for me. I would be
>interested knowing what that is?
Sometimes shells do it. For example, csh on FreeBSD 6.0 seems to
do it. (maybe only interactive shells) Type "stty -echo" twice,
and the second one echos. I'm also not sure exactly WHAT it restores,
and to what value. It does turn echo back on. It also seems to turn
icanon back on.
Gordon L. Burditt
| |
| Fletcher Glenn 2006-06-17, 1:25 am |
|
"Gordon Burditt" <gordonb.4054j@burditt.org> wrote in message
news:12965ij8hdldr68@corp.supernews.com...
>
> Sometimes shells do it. For example, csh on FreeBSD 6.0 seems to
> do it. (maybe only interactive shells) Type "stty -echo" twice,
> and the second one echos. I'm also not sure exactly WHAT it restores,
> and to what value. It does turn echo back on. It also seems to turn
> icanon back on.
>
> Gordon L. Burditt
After a program crash, I've sometimes found it necessary to enter the
character sequence:
^Jreset^J
This will reset the tty to a roughly sane state.
--
Fletcher Glenn
| |
| Logan Shaw 2006-06-17, 1:25 am |
| Fletcher Glenn wrote:
> After a program crash, I've sometimes found it necessary to enter the
> character sequence:
>
> ^Jreset^J
>
> This will reset the tty to a roughly sane state.
You can also do
^Jstty sane^J
At least that's what I do when I'm in such a situation.
- Logan
| |
| Barry Margolin 2006-06-17, 1:29 pm |
| In article <12965ij8hdldr68@corp.supernews.com>,
gordonb.4054j@burditt.org (Gordon Burditt) wrote:
>
> Sometimes shells do it. For example, csh on FreeBSD 6.0 seems to
> do it. (maybe only interactive shells) Type "stty -echo" twice,
> and the second one echos. I'm also not sure exactly WHAT it restores,
> and to what value. It does turn echo back on. It also seems to turn
> icanon back on.
I think most shells that provide real-time (e.g. emacs-style) input
editing will reset the terminal state, because they require their own
special state rather than the normal default state.
--
Barry Margolin, barmar@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***
| |
| Chris F.A. Johnson 2006-06-17, 7:21 pm |
| On 2006-06-17, Barry Margolin wrote:
> In article <12965ij8hdldr68@corp.supernews.com>,
> gordonb.4054j@burditt.org (Gordon Burditt) wrote:
>
>
> I think most shells that provide real-time (e.g. emacs-style) input
> editing will reset the terminal state, because they require their own
> special state rather than the normal default state.
I don't find that with bash; I have to explicitly fix it after
running a script that changes the terminal settings. I usually have
an exit trap:
trap '[ -t 0 ] && stty sane; printf "$NA$cu_vis$mouse_off"' EXIT
--
Chris F.A. Johnson, author <http://cfaj.freeshell.org>
Shell Scripting Recipes: A Problem-Solution Approach (2005, Apress)
===== My code in this post, if any, assumes the POSIX locale
===== and is released under the GNU General Public Licence
| |
| richard.kreckel@framatome-anp.com 2006-06-17, 7:21 pm |
| Barry Margolin wrote:
[...]
> I think most shells that provide real-time (e.g. emacs-style) input
> editing will reset the terminal state, because they require their own
> special state rather than the normal default state.
But why does installing a non-default signal handler make a difference,
then?
| |
| Måns Rullgård 2006-06-17, 7:21 pm |
| richard.kreckel@framatome-anp.com writes:
> Barry Margolin wrote:
> [...]
>
> But why does installing a non-default signal handler make a difference,
> then?
Could it be that the shell does the cleanup if the exit status of the
program indicates it was killed by a signal?
--
Måns Rullgård
mru@inprovide.com
| |
| Barry Margolin 2006-06-18, 7:22 pm |
| In article <yw1x3be35t71.fsf@agrajag.inprovide.com>,
Måns Rullgård <mru@inprovide.com> wrote:
> richard.kreckel@framatome-anp.com writes:
>
>
> Could it be that the shell does the cleanup if the exit status of the
> program indicates it was killed by a signal?
That's probably it. Otherwise, how could the stty program change your
terminal settings, if the shell always restored them.
Actually, I realized after my earlier message that there's probably more
to what the shell does. It has to set the modes it needs for its
interactive use when a program terminates or is suspended. But it can
remember the modes that were in effect when the program returned to the
shell, and put them back before starting another program. So the
following sequence may occur:
login with normal terminal modes
shell sets raw mode for input editing
user enters a command
shell sets normal modes and executes command
command runs, never touches modes, and finishes
shell sets raw mode
user enters a command like emacs
shell sets normal modes and executes emacs
emacs runs, sets raw mode
emacs crashes, so doesn't restore modes itself
shell saves modes, sets modes that it needs
user enters a command like like "cat > foo"
shell puts back emacs's raw mode and executes cat
user now notices that he doesn't get his input echoed properly, CR isn't
processed as newline, control characters don't work, etc. because he's
in raw mode.
--
Barry Margolin, barmar@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***
| |
| spibou@gmail.com 2006-06-21, 7:38 am |
| richard.kreckel@framatome-anp.com wrote:
> Hi,
>
> Here is a little C-program that sets the terminal into raw mode and
> waits for 10 seconds for the user to press Ctr-C. When that happens, a
> signal handler is invoked that calls exit(2).
>
> #include <stdio.h>
> #include <stdlib.h>
> #include <signal.h>
> #include <termios.h>
> #include <unistd.h>
>
> void sighandler(int /*sig*/)
> {
> exit(2);
> }
>
> int main()
> {
> signal(SIGINT, sighandler); // Without this, terminal is reset!
> termios termio;
> tcgetattr(0, &termio);
> termio.c_iflag &= ~( BRKINT | PARMRK | INPCK | IXOFF );
> termio.c_iflag |= ( IGNBRK | IGNPAR );
> termio.c_lflag &= ~( ICANON | ECHO | ECHOE | ECHOK | ECHONL );
> termio.c_lflag |= ( ISIG );
> tcsetattr(0, TCSADRAIN, &termio);
> fprintf(stderr, "Now hit Ctrl-C\n"); fflush(stderr);
> sleep( 10 );
> }
This won't compile for me on either Solaris or Linux.
I'm surprised it compiled for you. Here's a modification
which will compile on both:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <termios.h>
#include <unistd.h>
void sighandler(int a)
{
exit(2);
}
int main()
{
struct termios termio;
signal(SIGINT, sighandler); /* Without this, terminal is reset! */
tcgetattr(0, &termio);
termio.c_iflag &= ~( BRKINT | PARMRK | INPCK | IXOFF );
termio.c_iflag |= ( IGNBRK | IGNPAR );
termio.c_lflag &= ~( ICANON | ECHO | ECHOE | ECHOK | ECHONL );
termio.c_lflag |= ( ISIG );
tcsetattr(0, TCSADRAIN, &termio);
fprintf(stderr, "Now hit Ctrl-C\n"); fflush(stderr);
sleep( 10 );
return 0;
}
I note also that fflush(stderr) is superfluous since stderr
is not buffered.
Now regarding results. You mentioned what the "shell" does
but you didn't say which shell and there is no reason to
assume that all shells or even all versions of the same shell
will behave the same. I did some experiments using TC 6.14.00
and BASH 3.00.16 on Linux. I also used strace on the shells
plus their child processes.
The first thing to note is that when the programme includes the
signal handler then the shell never learns that the programme
exited due to a signal. Here's the relevant line from the output
of strace:
For TC:
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 2}], WNOHANG|WSTOPPED,
{ru_utime={0, 0}, ru_stime={0, 4000}, ...}) = 5615
For BASH:
waitpid(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 2}],
WSTOPPED|WCONTINUED) = 6774
On the other hand , if I run the programme without the signal
handler I get:
For TC:
wait4(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGINT}],
WNOHANG|WSTOPPED, {ru_utime={0, 0}, ru_stime={0, 0}, ...}) = 6324
For BASH:
waitpid(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGINT}],
WSTOPPED|WCONTINUED) = 6826
On tcsh it makes no difference whether the programme
includes a signal handler or not ; the terminal works fine
after the programme exits in both cases. Indeed there doesn't
seem to be any relevant difference in the ioctl's the shell
uses in the two cases.
On the other hand the terminal characteristics are screwed up
if I run the programme with signal handler from BASH. If I use
the line:
diff bash-nohandler-afterchild-exits bash-withhandler-afterchild-exits
where the 2 arguments to diff contain the output of strace
after the child exits , I get:
14c16
< ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon
echo ...}) = 0
---
> ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig -icanon -echo ...}) = 0
34,35d35
< ioctl(0, TIOCGWINSZ, {ws_row=35, ws_col=103, ws_xpixel=0,
ws_ypixel=0}) = 0
plus some other irrelevant stuff. There is a difference as you
can see although I don't know what SNDCTL_TMR_TIMEBASE
or TCGETS mean.
> However, with the default
> signal hander active, the terminal is magically reset.
Is there a "default" handler for SIGINT ? Doesn't the process
just get killed ?
Spiros Bousbouras
| |
| spibou@gmail.com 2006-06-21, 7:38 am |
|
spibou@gmail.com wrote:
> richard.kreckel@framatome-anp.com wrote:
>
>
> This won't compile for me on either Solaris or Linux.
> I'm surprised it compiled for you. Here's a modification
> which will compile on both:
>
> #include <stdio.h>
> #include <stdlib.h>
> #include <signal.h>
> #include <termios.h>
> #include <unistd.h>
>
> void sighandler(int a)
> {
> exit(2);
>
> }
>
> int main()
> {
> struct termios termio;
>
> signal(SIGINT, sighandler); /* Without this, terminal is reset! */
> tcgetattr(0, &termio);
> termio.c_iflag &= ~( BRKINT | PARMRK | INPCK | IXOFF );
> termio.c_iflag |= ( IGNBRK | IGNPAR );
> termio.c_lflag &= ~( ICANON | ECHO | ECHOE | ECHOK | ECHONL );
> termio.c_lflag |= ( ISIG );
> tcsetattr(0, TCSADRAIN, &termio);
> fprintf(stderr, "Now hit Ctrl-C\n"); fflush(stderr);
> sleep( 10 );
> return 0;
> }
>
> I note also that fflush(stderr) is superfluous since stderr
> is not buffered.
>
> Now regarding results. You mentioned what the "shell" does
> but you didn't say which shell and there is no reason to
> assume that all shells or even all versions of the same shell
> will behave the same. I did some experiments using TC 6.14.00
> and BASH 3.00.16 on Linux. I also used strace on the shells
> plus their child processes.
>
> The first thing to note is that when the programme includes the
> signal handler then the shell never learns that the programme
> exited due to a signal. Here's the relevant line from the output
> of strace:
>
> For TC:
> wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 2}], WNOHANG|WSTOPPED,
> {ru_utime={0, 0}, ru_stime={0, 4000}, ...}) = 5615
>
> For BASH:
> waitpid(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 2}],
> WSTOPPED|WCONTINUED) = 6774
>
> On the other hand , if I run the programme without the signal
> handler I get:
>
> For TC:
> wait4(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGINT}],
> WNOHANG|WSTOPPED, {ru_utime={0, 0}, ru_stime={0, 0}, ...}) = 6324
>
> For BASH:
> waitpid(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGINT}],
> WSTOPPED|WCONTINUED) = 6826
>
> On tcsh it makes no difference whether the programme
> includes a signal handler or not ; the terminal works fine
> after the programme exits in both cases. Indeed there doesn't
> seem to be any relevant difference in the ioctl's the shell
> uses in the two cases.
>
> On the other hand the terminal characteristics are screwed up
> if I run the programme with signal handler from BASH. If I use
> the line:
> diff bash-nohandler-afterchild-exits bash-withhandler-afterchild-exits
> where the 2 arguments to diff contain the output of strace
> after the child exits , I get:
>
> 14c16
> < ioctl(0, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon
> echo ...}) = 0
> ---
> 34,35d35
> < ioctl(0, TIOCGWINSZ, {ws_row=35, ws_col=103, ws_xpixel=0,
> ws_ypixel=0}) = 0
>
> plus some other irrelevant stuff. There is a difference as you
> can see although I don't know what SNDCTL_TMR_TIMEBASE
> or TCGETS mean.
>
>
> Is there a "default" handler for SIGINT ? Doesn't the process
> just get killed ?
Some additions to my previos message in the thread (included in
its entirety above):
I notice now that Richard did mention that he uses BASH ; I hadn't
noticed it before. Still , the version might also be relevant.
Richard B. Kreckel wrote:
> If I do that on my SuSE Linux box, the terminal is left in a very
> strange mode. Okay, I expected that, since I didn't write any code to
> restore the terminal to its orginal settings. However, with the default
> signal hander active, the terminal is magically reset.
>
> My question is: who resets the terminal? Bash? Readline? The Great
> SIG_DFL Handler(tm)? A conspiration thereof? Anyone else?
Judging by the output of strace , it's BASH.
Spiros Bousbouras
| |
| richard.kreckel@framatome-anp.com 2006-06-23, 7:32 am |
| spibou@gmail.com wrote:
> The first thing to note is that when the programme includes the
> signal handler then the shell never learns that the programme
> exited due to a signal. Here's the relevant line from the output
> of strace:
>
> For TC:
> wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 2}], WNOHANG|WSTOPPED,
> {ru_utime={0, 0}, ru_stime={0, 4000}, ...}) = 5615
>
> For BASH:
> waitpid(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 2}],
> WSTOPPED|WCONTINUED) = 6774
>
> On the other hand , if I run the programme without the signal
> handler I get:
>
> For TC:
> wait4(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGINT}],
> WNOHANG|WSTOPPED, {ru_utime={0, 0}, ru_stime={0, 0}, ...}) = 6324
>
> For BASH:
> waitpid(-1, [{WIFSIGNALED(s) && WTERMSIG(s) == SIGINT}],
> WSTOPPED|WCONTINUED) = 6826
That's right. The signal handler I wrote above is just bad. I'm
inclined to deduce a more general rule, here:
Never call exit(2) from a signal handling routine for a terminating
signal. If the signal handler is not supposed to return, it's better to
install SIG_DFL and re-raise the signal. This way, parent processes get
a chance to find out their child has been killed.
-richy.
--
Richard B. Kreckel
<http://www.ginac.de/~kreckel/>
|
|
|
|
|