1

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
$./sigAction
sigAction>^Cctrl-C
// it is waiting,waiting . Nothing happens until I hit Enter, next prompt comes up.

sigAction>

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" );
  8   
  9 }
 10
 11 main()
 12 {
 13
 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) {
 20
 21         char block[4096];
 22
 23         printf("sigAction>");
 24         fflush(stdout);
 25
 26         fgets(block, 4096, stdin);
 27        
 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.
sigAction>^C
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!

kathy
  • 21
  • 1
  • 5
  • Why `SA_RESTART`? That goes back to the interrupted system call doesn't it? Also, see [How to avoid using `printf()` in signal handlers?](http://stackoverflow.com/questions/16891019) Also, you should be coding to at least the old (C99) and preferably the new (C11) standard, both of which require an explicit return type `int main(void)`. – Jonathan Leffler Aug 15 '16 at 17:27
  • There are a very limited number of functions that are safe to use in signal handlers. fprintf is not one of them. – Zan Lynx Aug 15 '16 at 17:39
  • Thank you so much! – kathy Aug 15 '16 at 20:54

2 Answers2

1

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;
    sigemptyset(&signalAction.sa_mask);
    signalAction.sa_flags = 0;      // SA_RESTART;
    sigaction(SIGINT, &signalAction, NULL);

    while (1)
    {
        char block[4096];

        printf("sigAction>");
        fflush(stdout);

        if (fgets(block, 4096, stdin) == 0)
        {
            if (errno == EINTR)
            {
                errno = 0;
                clearerr(stdin);
                fprintf(stderr, "Interrupted!\n");
                continue;
            }
            else
            {
                fprintf(stderr, "EOF spotted\n");
                break;
            }
        }

        if (!strcmp(block, "exit\n"))
        {
            fprintf(stderr, "Exiting\n");
            exit(1);
        }
        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)
Interrupted!
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.]
sigAction>exit
Exiting
$

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
    -extproc
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;
$
Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Thank you so much, Jonathan! I got what you are saying. But I still do not understand why when I hit ctrl-C, it comes up ^Cctrl-C. My another question is that if I have a parser function : yyparse(), how could I pass the input to the parser? I hope to it could execute normal input like "ls -al" if the input is not ctrl-c or exit. Thanks a lot! – kathy Aug 15 '16 at 20:53
  • The `^C` comes from the terminal driver (part of the kernel), at least on Mac OS X where I tested. The `Ctrl-C` part comes from the `fprintf()` call in the interrupt handler. When you type Control-C, the terminal driver interprets that as 'send an interrupt to the processes using the terminal'. (The full story is more complicated than that, but needn't concern us now — suffice to say job control, sessions, process groups, etc complicate things more than you need to deal with.) So, when you type Control-C, an interrupt is sent to the program, which arranges to call the handler function. – Jonathan Leffler Aug 15 '16 at 21:00
  • Got it! Thank you for the quick response! Do you know something about another question about parser in the comment? By the way, I am new to shell programming. Is there a good tool to debug parser and grammar stuff? – kathy Aug 15 '16 at 21:35
  • Updates: Solved it! – kathy Aug 18 '16 at 15:57
0

If you want to ignore, then use SIG_IGN as the action, if not then you are not ignoring but catching.

As you used SA_RESTART then the interrupted system call will be restard and not interrupted. fgets is then probably blocked in some internal system call and you specify to restart it. I don't remember how fgets behave while being interrupted but it probably returns you a NULL value. You may also use read and control errno for EINTR.

Jean-Baptiste Yunès
  • 34,548
  • 4
  • 48
  • 69