How to capture a Control + D signal?

I want to write the Ctrl + D signal to my program and write a signal handler for it. How can i do this? I am working on C and am using a Linux system.

+47
c linux unix signals
04 Oct '09 at 10:41
source share
7 answers

As others have said, to process Control + D, process the "end of file".

Control + D is the message fragment between the user and the pseudo file that you see as stdin. This does not mean specifically "end of file", but, in a more general sense, "clear my input." Flushing means that any read() call to stdin in your program returns with the input length entered since the last reset. If the line is not empty, the input becomes available for your program, although the user has not yet typed "return". If the line is empty, then read() returns with zero, and this is interpreted as "end of file".

Therefore, when you use Control + D to end a program, it only works at the beginning of a line, or if you do it twice (the first time to clear, the second time to read() to return zero).

Try:

 $ cat foo (type Control-D once) foofoo (read has returned "foo") (type Control-D again) $ 
+69
04 Oct '09 at 11:16
source share

Ctrl + D is not a signal, it is EOF (End-Of-File). He closes the pipe stdin. If read (STDIN) returns 0, it means that stdin is closed, which means that Ctrl + D was hit (if there is a keyboard on the other end of the pipe).

+21
04 Oct. '09 at 10:58
source share

Minimalist example:

 #include <unistd.h> #include <stdio.h> #include <termios.h> #include <signal.h> void sig_hnd(int sig){ (void)sig; printf("(VINTR)"); } int main(){ setvbuf(stdout,NULL,_IONBF,0); struct termios old_termios, new_termios; tcgetattr(0,&old_termios); signal( SIGINT, sig_hnd ); new_termios = old_termios; new_termios.c_cc[VEOF] = 3; // ^C new_termios.c_cc[VINTR] = 4; // ^D tcsetattr(0,TCSANOW,&new_termios); char line[256]; int len; do{ len=read(0,line,256); line[len]='\0'; if( len <0 ) printf("(len: %i)",len); if( len==0 ) printf("(VEOF)"); if( len >0 ){ if( line[len-1] == 10 ) printf("(line:'%.*s')\n",len-1,line); if( line[len-1] != 10 ) printf("(partial line:'%s')",line); } }while( line[0] != 'q' ); tcsetattr(0,TCSANOW,&old_termios); } 

The program will change the VEOF char (from Ctrl-D) to Ctrl-C and VINTR char (from Ctrl-C) to Ctrl-D. If you press Ctrl-D, then the terminal driver will send SIGINT to the program signal handler.

Note: pressing VINTR erases the terminal input buffer, so you cannot read the characters entered into the string until you press the VINTR key.

+12
Oct 04 '09 at 13:33
source share

As far as I know, Ctrl + D is translated by the system to the end of standard input, so your application will not receive any signal.

I think the only way to intercept Ctrl + D is to work directly with the api system (for example, with access to tty)

+3
Oct 04 '09 at 10:59
source share

No need to process signals.

You need to make sure that ISIG is not installed on the terminal flags, that’s all.

Here is a complete example using selection to avoid blocking on stdin:

 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <termios.h> #include <time.h> #include <sys/select.h> #define STDIN_FILENO 0 struct termios org_opts; /** Select to check if stdin has pending input */ int pending_input(void) { struct timeval tv; fd_set fds; tv.tv_sec = 0; tv.tv_usec = 0; FD_ZERO(&fds); FD_SET(STDIN_FILENO, &fds); //STDIN_FILENO is 0 select(STDIN_FILENO+1, &fds, NULL, NULL, &tv); return FD_ISSET(STDIN_FILENO, &fds); } /** Input terminal mode; save old, setup new */ void setup_terminal(void) { struct termios new_opts; tcgetattr(STDIN_FILENO, &org_opts); memcpy(&new_opts, &org_opts, sizeof(new_opts)); new_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOKE | ISIG | ICRNL); tcsetattr(STDIN_FILENO, TCSANOW, &new_opts); } /** Shutdown terminal mode */ void reset_terminal(void) { tcsetattr(STDIN_FILENO, TCSANOW, &org_opts); } /** Return next input or -1 if none */ int next_input(void) { if (!pending_input()) return -1; int rtn = fgetc(stdin); printf("Found: %d\n", rtn); return(rtn); } int main() { setup_terminal(); printf("Press Q to quit...\n"); for (;;) { int key = next_input(); if (key != -1) { if ((key == 113) || (key == 81)) { printf("\nNormal exit\n"); break; } } } reset_terminal(); return 0; } 

Output:

 doug-2:rust-sys-sterm doug$ cc junk.c doug-2:rust-sys-sterm doug$ ./a.out Press Q to quit... Found: 4 Found: 3 Found: 27 Found: 26 Found: 113 Normal exit 

NB. 3 - control C, and 4 - control D; 26 - control z. 113 is q. See http://en.wikipedia.org/wiki/ASCII#ASCII_control_characters for a complete table.

+2
Dec 19 '14 at 3:02
source share

You can use poll () and watch POLLHUP at fd # 1 because the TTY level translates ^ D to EOF.

0
04 Oct '09 at 22:20
source share

The signal can be caught so you can catch ^ C but not ^ D

Because it is not a signal ...

Hope this helps

-2
Jul 18 '17 at 17:57
source share



All Articles