Which Signal Does Ctrl-X Send When Used in a Terminal

Which signal does ctrl-x send when used in a terminal?

To get all the terminal control character assignments:

stty -a

What is the behavior of each ctrl + key and its signals?

The description of Linux terminal input handling is found in man tcsetattr, which is the function you would use to configure the mapping for cooked input. (Look for the c_cc array for the list of special characters.) The Linux terminal is based on the Posix standard but it has a number of extensions. (The list of special characters is in §11.1.9 of Posix, but you should also read the preceding sections for a complete understanding of the terminal programming model.)

There are three signals which the terminal driver will send in response to control characters:

  • SIGINT (VINTR, default Ctrl-C)

  • SIGQUIT (VQUIT, default Ctrl-\)

  • SIGTSTP (VSUSP, default Ctrl-Z)

If you set the terminal to raw mode (with the same function) then you will have to issue the signals yourself (if you want to) and you can use whatever mechanism you choose to decide what triggers each signal.

The readline library (used by many shells) puts the terminal into raw mode while it is reading, then does its own character mapping [Note 2]. However, when bash hands the terminal over to a command application, the terminal is restored to default mode [Note 1], so the mapping is controlled by the terminal driver as explained in the tcsetattr documention.

If you want to use readline, you will need to consult its documentation.

Most applications using ncurses also start by putting the terminal into raw mode.


Notes

  1. This is actually a bit imprecise, since commands can modify the terminal mode. (See man stty, for example.) If some command changes a control character, that becomes part of the mode which will be passed to the next command.

  2. readline doesn't turn off VINTR handling, so SIGINT can be sent even if readline is being used; this will obey the current terminal setting. But it does turn off the other control characters.

In detail, what happens when you press Ctrl-C in a terminal?

tl;dr the kernel does it.

Each pty (pseudo tty) has two ends, a master and a slave. In the xterm example, xterm would be holding onto the master file descriptor. Any key presses are written directly into the master fd. The slave fd (pts, or pty slave) is owned by a session and passed to whatever the foreground process group is.

Whenever an ASCII ETX character (^C) is written to the master, the kernel translates that into sending SIGINT to the foreground process group with the corresponding controlling terminal. This is actually a pty setting. You can run stty -a and see that the default is intr = ^C;, meaning ^C or ETX is the "SIGINT" character. This can be changed to a different character or disabled entirely.

A more complex example would be how Ctrl-C works through an interactive SSH session. Interactive SSH sessions allocate a pty on the server side. The client side pty is set to raw mode, meaning that the client side kernel will not translate ETX into SIGINT. Instead, the client side kernel passes the ETX along to the slave. In this case, the ssh client process takes that ETX and passes it along to the server sshd process. If the server sshd pty is not in raw mode, then the server's kernel will translate that ETX into a SIGINT to its foreground process group. This is how Ctrl-C sends SIGINT to the process running on the server instead of killing your client side SSH and leaving you hanging.

Forcing a terminal not to print Ctrl hotkeys when signals are caught

When you type a key on a terminal, two things happen

  • the character is echoed (displayed) on this terminal
  • the character is sent (over the line) to the attached program

Both these actions can be controlled via termios/tcsetattr(): a different character(s) can be sent or echoed, some can be suppressed, etc. (some/most of these actions take place in the terminal-driver , but this is not relevant here)

Demonstration: using tcsetattr() to control the echoing of the terminal:



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

#define _SVID_SOURCE 1
#include <termios.h>
#include <unistd.h>
#include <signal.h>

struct termios termios_save;

void reset_the_terminal(void)
{
tcsetattr(0, 0, &termios_save );
}

sig_atomic_t the_flag = 0;
void handle_the_stuff(int num)
{
char buff[4];
buff[0] = '[';
buff[2] = '0' + num%10;
num /= 10;
buff[1] = '0' + num%10;
buff[3] = ']';
write(0, buff, sizeof buff);
the_flag = 1;
}

int main (void)
{
int rc;
int ch;
struct termios termios_new;

rc = tcgetattr(0, &termios_save );
if (rc) {perror("tcgetattr"); exit(1); }

rc = atexit(reset_the_terminal);
if (rc) {perror("atexit"); exit(1); }

termios_new = termios_save;
termios_new.c_lflag &= ~ECHOCTL;
rc = tcsetattr(0, 0, &termios_new );
if (rc) {perror("tcsetattr"); exit(1); }

signal(SIGINT, handle_the_stuff);

printf("(pseudoshell)Start typing:\n" );
while(1) {
ch = getc(stdin);
if (the_flag) {
printf("Saw the signal, last character was %02x\n", (unsigned) ch);
break;
}
}

exit (0);
}

What keyboard signal apart from Ctrl-C can I catch?

You can use ctrl+Z,

SIGTSTP 

Value = 20

For more details refer this link.

What happens in BASH when you do Ctrl-C (hint, it's not simply sending a SIGINT)

Okay mystery solved! Thanks to the helpful folks over at the Indian Linux Users Group.

The answer here is two-fold -

Firstly, apt-get invokes another program called http for downloading data.

[~] ➔ file /usr/lib/apt/methods/http

/usr/lib/apt/methods/http: ELF 32-bit LSB executable, Intel 80386, version 1
(SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15,
stripped

Note that that's an executable, not even a script, probably to support downloading files during system installation when none of perl/python/ruby etc. are yet available.

Secondly, when you press Ctrl-C after running apt-get, the SIGINT gets sent to http, and not to apt-get. When http receives the SIGINT, it saves the download state before shutting down.

Here's the updated script that works perfectly -

#!/bin/sh
for i in `seq 1 100` ; do
sudo apt-get install foo -y &
sleep 10
sudo kill -2 `ps -ae | grep " http" | awk '{print $1}'`
done


Related Topics



Leave a reply



Submit