kathy kathy - 1 year ago 81
Bash Question

implement ctr-c using sigaction

I am trying to use sigaction to ignore ctrl-c. The code is in the end to help understanding.The problem is when I press ctrl-C, ^Cctrl-c comes up instead of ^C, and it does not print next prompt instantly. Only until I hit Enter, it starts printing the prompt. Like this:

> // run the program sigAction
// it is waiting,waiting . Nothing happens until I hit Enter, next prompt comes up.


The result I am looking for is when I hit ctrl-C without hitting Enter, next prompt should come up instantly. The problem is below:

1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <signal.h>
4 #include <string.h>
5 // implement ignoring ctrl-C
6 void sigIntHandler(int signum) {
7 fprintf(stderr, "ctrl-C\n" );
9 }
11 main()
12 {
14 struct sigaction signalAction;
15 signalAction.sa_handler = sigIntHandler;
16 sigemptyset(&signalAction.sa_mask);
17 signalAction.sa_flags = SA_RESTART;
18 sigaction(SIGINT, &signalAction, NULL);
19 while(1) {
21 char block[4096];
23 printf("sigAction>");
24 fflush(stdout);
26 fgets(block, 4096, stdin);
28 if (!strcmp(block, "exit\n")) {
29 exit(1);
30 }
31 }
32 }

When I use gdb to look inside, it gives me information below:

(gdb) n
Single stepping until exit from function main,
which has no line number information.
Program received signal SIGINT, Interrupt.
0x00007ffff7b15d10 in __read_nocancel () from /lib64/libc.so.6
(gdb) n
Single stepping until exit from function __read_nocancel,
which has no line number information

Any idea helps! Thanks in advance!

Answer Source

As I noted in a comment, you don't really want to use SA_RESTART. It means that when the read() gets an interrupt, the read resumes after the interrupt, which precludes the opportunity for your main program to print a new prompt.

This code seems likely to be close to what you want:

#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void sigIntHandler(int signum)
    fprintf(stderr, "Ctrl-C (%d)\n", signum);

int main(void)
    struct sigaction signalAction;
    signalAction.sa_handler = sigIntHandler;
    signalAction.sa_flags = 0;      // SA_RESTART;
    sigaction(SIGINT, &signalAction, NULL);

    while (1)
        char block[4096];


        if (fgets(block, 4096, stdin) == 0)
            if (errno == EINTR)
                errno = 0;
                fprintf(stderr, "Interrupted!\n");
                fprintf(stderr, "EOF spotted\n");

        if (!strcmp(block, "exit\n"))
            fprintf(stderr, "Exiting\n");
        printf("Read: [%.*s]\n", (int)(strlen(block) - 1), block);

Note that officially, you're not supposed to use the printf() family of functions in signal handlers — see How to avoid using printf() in a signal handler?

Example runs (program called sigact):

$ ./sigact
sigAction>Ordinary input
Read: [Ordinary input]
sigAction>Some typing, then an interrupt! ^CCtrl-C (2)
sigAction>More input?
Read: [More input?]
sigAction>Yes, more input.
Read: [Yes, more input.]
sigAction>And why you type control-D?
Read: [And why you type control-D?]
sigAction>EOF spotted
$ ./sigact
sigAction>Also typing exit works, of course.
Read: [Also typing exit works, of course.]

The ^C comes from the terminal driver (on Mac OS X 10.11.6). The echoctl at the end of the first line of lflags does that:

$ stty -a
speed 9600 baud; 50 rows; 141 columns;
lflags: icanon isig iexten echo echoe -echok echoke -echonl echoctl
    -echoprt -altwerase -noflsh -tostop -flusho pendin -nokerninfo
iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel iutf8
    -ignbrk brkint -inpck -ignpar -parmrk
oflags: opost onlcr -oxtabs -onocr -onlret
cflags: cread cs8 -parenb -parodd hupcl -clocal -cstopb -crtscts -dsrflow
    -dtrflow -mdmbuf
cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = <undef>;
    eol2 = <undef>; erase = ^?; intr = ^C; kill = ^X; lnext = ^V;
    min = 1; quit = ^\; reprint = ^R; start = ^Q; status = ^T;
    stop = ^S; susp = ^Z; time = 0; werase = ^W;