216

How does one catch Ctrl+C in C?

2
  • 7
    There's no such thing as catching signals in C.... or at least so I thought until I read the C99 standard. It turns out that there is signal handling defined in C but Ctrl-C isn't mandated to produce any specific signal or a signal at all. Depending on your platform, this might be impossible. Commented Nov 18, 2010 at 16:40
  • 2
    Signal handling is mostly implementation dependent. On *nix platforms, use <signal.h>, and if you're on OSX you can take advantage of GCD to make things even easier~. Commented Nov 18, 2010 at 19:09

9 Answers 9

295

With a signal handler.

Here is a simple example flipping a bool used in main():

#include <signal.h> static volatile int keepRunning = 1; void intHandler(int dummy) { keepRunning = 0; } // ... int main(void) { signal(SIGINT, intHandler); while (keepRunning) { // ... 
Sign up to request clarification or add additional context in comments.

16 Comments

Let's mention that we need to #include <signal.h> for this to work!
How about #include <stdbool.h>? :)
static It should be bool volatile keepRunning = true; to be 100% safe. The compiler is free to cache keepRunning in a register and the volatile will prevent this. In practice it may most likely also work without the volatile keyword when the while loop calls at least one non-inline function.
@DirkEddelbuettel I stand corrected, I thought my improvements will reflect your original intentions more, sorry if that did not happen. Anyway, the greater issue is that, since your answer tries to be generic enough, and because IMO it should provide a snippet that is also working under asynchronous interrupts: I would either use sig_atomic_t or atomic_bool types there. I just missed that one. Now, since we are talking: would you like me to rollback my latest edit? No hard feelings there it would be perfectly understandable from your point of view :)
That is much better!
|
55

Check here:

Note: Obviously, this is a simple example explaining just how to set up a Ctrl+C handler, but as always there are rules that need to be obeyed in order not to break something else. Please read the comments below.

The sample code from above:

#include <stdio.h> #include <signal.h> #include <stdlib.h> void INThandler(int); int main(void) { signal(SIGINT, INThandler); while (1) pause(); return 0; } void INThandler(int sig) { char c; signal(sig, SIG_IGN); printf("OUCH, did you hit Ctrl-C?\n" "Do you really want to quit? [y/n] "); c = getchar(); if (c == 'y' || c == 'Y') exit(0); else signal(SIGINT, INThandler); getchar(); // Get new line character } 

8 Comments

@Derrick Agree, int main is a proper thing, but gcc and other compilers compile this to a correctly running programs since 1990's. Explained pretty well here: eskimo.com/~scs/readings/voidmain.960823.html - it's basically a "feature", I take it like that.
@icyrock.com: All very true (regarding void main() in C), but when posting publicly it is probably as well to avoid the debate altogether by using int main() lest it distract from teh main point.
There is a huge flaw in this. You cannot safely use printf within the content of a signal handler. It's a violation of asynchronous signal safety. This is because printf is not reentrant. What happens if the program was in the middle of using printf when Ctrl-C was pressed, and your signal handler starts using it at the same time? Hint: It will likely break. write and fwrite are same to use in this context.
@icyrock.com: Doing anything complicated in a signal handler is going to cause headaches. Especially using the io system.
@stacker Thanks - I think it's worth it at the end. If somebody stumbles across this code in the future, better to have it right as much as possible, regardless of the topic of the question.
|
38

Addendum regarding Unix-like platforms.

According to the signal(2) man page on GNU/Linux, the behavior of signal is not as portable as behavior of sigaction:

The behavior of signal() varies across UNIX versions, and has also varied historically across different versions of Linux. Avoid its use: use sigaction(2) instead.

On System V, the system did not block the delivery of further instances of the signal and delivery of a signal would reset the handler to the default one. In BSD, the semantics changed.

The following variation of the answer by Dirk Eddelbuettel uses sigaction instead of signal:

#include <signal.h> #include <stdlib.h> static bool keepRunning = true; void intHandler(int) { keepRunning = false; } int main(int argc, char *argv[]) { struct sigaction act; act.sa_handler = intHandler; sigaction(SIGINT, &act, NULL); while (keepRunning) { // Main loop } } 

1 Comment

31

Although the above snippet is a correct example, one should use the more modern types and guarantees provided by the later standards if possible. Therefore, here is a safer and modern alternative for those who are seeking for the and conforming implementation:

#include <signal.h> #include <stdlib.h> #include <stdio.h> static volatile sig_atomic_t keep_running = 1; static void sig_handler(int _) { (void)_; keep_running = 0; } int main(void) { signal(SIGINT, sig_handler); while (keep_running) puts("Still running..."); puts("Stopped by signal `SIGINT'"); return EXIT_SUCCESS; } 

C11 Standard: 7.14§2 The header <signal.h> declare a type ... sig_atomic_t which is the (possibly volatile-qualified) integer type of an object that can be accessed as an atomic entity, even in the presence of asynchronous interrupts.

Furthermore:

C11 Standard: 7.14.1.1§5 If the signal occurs other than as the result of calling the abort or raise function, the behavior is undefined if the signal handler refers to any object with static or thread storage duration that is not a lock-free atomic object other than by assigning a value to an object declared as volatile sig_atomic_t...

@Peter Varo updated Dirk's answer, but Dirk rejected the change. This is the new answer by Peter.

3 Comments

I don't unserstand the (void)_;... What's its purpose? Is it so the compiler doesn't warn about an unused variable?
I had just found that answer and was about to paste that link here! Thanks :)
17

Or you can put the terminal in raw mode, like this:

struct termios term; term.c_iflag |= IGNBRK; term.c_iflag &= ~(INLCR | ICRNL | IXON | IXOFF); term.c_lflag &= ~(ICANON | ECHO | ECHOK | ECHOE | ECHONL | ISIG | IEXTEN); term.c_cc[VMIN] = 1; term.c_cc[VTIME] = 0; tcsetattr(fileno(stdin), TCSANOW, &term); 

Now it should be possible to read Ctrl+C keystrokes using fgetc(stdin). Beware using this though because you can't Ctrl+Z, Ctrl+Q, Ctrl+S, etc. like normally any more either.

Comments

11

Set up a trap (you can trap several signals with one handler):

 signal (SIGQUIT, my_handler); signal (SIGINT, my_handler); 

Handle the signal however you want, but be aware of limitations and gotchas:

 void my_handler (int sig) { /* Your code here. */ } 

Comments

5

Regarding existing answers, note that signal handling is platform dependent. Win32 for example handles far fewer signals than POSIX operating systems; see here. While SIGINT is declared in signals.h on Win32, see the note in the documentation that explains that it will not do what you might expect.

Comments

3
#include<stdio.h> #include<signal.h> #include<unistd.h> void sig_handler(int signo) { if (signo == SIGINT) printf("received SIGINT\n"); } int main(void) { if (signal(SIGINT, sig_handler) == SIG_ERR) printf("\ncan't catch SIGINT\n"); // A long long wait so that we can easily issue a signal to this process while(1) sleep(1); return 0; } 

The function sig_handler checks if the value of the argument passed is equal to the SIGINT, then the printf is executed.

1 Comment

Never call I/O routines in a signal handler.
2

This just prints before exiting.

#include <stdio.h> #include <stdlib.h> #include <unistd.h> void sigint_handler(int); int main(void) { signal(SIGINT, sigint_handler); while (1) { pause(); } return 0; } void sigint_handler(int sig) { /* Do something */ printf("killing process %d\n", getpid()); exit(0); } 

1 Comment

Never call I/O in a signal handler.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.