Create a gist now

Instantly share code, notes, and snippets.

@aspyct /signal.c
Last active Apr 14, 2018

What would you like to do?
Unix signal handling example in C, SIGINT, SIGALRM, SIGHUP...
/**
* More info?
* a.dotreppe@aspyct.org
* http://aspyct.org
* @aspyct (twitter)
*
* Hope it helps :)
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h> // sigaction(), sigsuspend(), sig*()
#include <unistd.h> // alarm()
void handle_signal(int signal);
void handle_sigalrm(int signal);
void do_sleep(int seconds);
/* Usage example
*
* First, compile and run this program:
* $ gcc signal.c
* $ ./a.out
*
* It will print out its pid. Use it from another terminal to send signals
* $ kill -HUP <pid>
* $ kill -USR1 <pid>
* $ kill -ALRM <pid>
*
* Exit the process with ^C ( = SIGINT) or SIGKILL, SIGTERM
*/
int main() {
struct sigaction sa;
// Print pid, so that we can send signals from other shells
printf("My pid is: %d\n", getpid());
// Setup the sighub handler
sa.sa_handler = &handle_signal;
// Restart the system call, if at all possible
sa.sa_flags = SA_RESTART;
// Block every signal during the handler
sigfillset(&sa.sa_mask);
// Intercept SIGHUP and SIGINT
if (sigaction(SIGHUP, &sa, NULL) == -1) {
perror("Error: cannot handle SIGHUP"); // Should not happen
}
if (sigaction(SIGUSR1, &sa, NULL) == -1) {
perror("Error: cannot handle SIGUSR1"); // Should not happen
}
// Will always fail, SIGKILL is intended to force kill your process
if (sigaction(SIGKILL, &sa, NULL) == -1) {
perror("Cannot handle SIGKILL"); // Will always happen
printf("You can never handle SIGKILL anyway...\n");
}
if (sigaction(SIGINT, &sa, NULL) == -1) {
perror("Error: cannot handle SIGINT"); // Should not happen
}
for (;;) {
printf("\nSleeping for ~3 seconds\n");
do_sleep(3); // Later to be replaced with a SIGALRM
}
}
void handle_signal(int signal) {
const char *signal_name;
sigset_t pending;
// Find out which signal we're handling
switch (signal) {
case SIGHUP:
signal_name = "SIGHUP";
break;
case SIGUSR1:
signal_name = "SIGUSR1";
break;
case SIGINT:
printf("Caught SIGINT, exiting now\n");
exit(0);
default:
fprintf(stderr, "Caught wrong signal: %d\n", signal);
return;
}
/*
* Please note that printf et al. are NOT safe to use in signal handlers.
* Look for async safe functions.
*/
printf("Caught %s, sleeping for ~3 seconds\n"
"Try sending another SIGHUP / SIGINT / SIGALRM "
"(or more) meanwhile\n", signal_name);
/*
* Indeed, all signals are blocked during this handler
* But, at least on OSX, if you send 2 other SIGHUP,
* only one will be delivered: signals are not queued
* However, if you send HUP, INT, HUP,
* you'll see that both INT and HUP are queued
* Even more, on my system, HUP has priority over INT
*/
do_sleep(3);
printf("Done sleeping for %s\n", signal_name);
// So what did you send me while I was asleep?
sigpending(&pending);
if (sigismember(&pending, SIGHUP)) {
printf("A SIGHUP is waiting\n");
}
if (sigismember(&pending, SIGUSR1)) {
printf("A SIGUSR1 is waiting\n");
}
printf("Done handling %s\n\n", signal_name);
}
void handle_sigalrm(int signal) {
if (signal != SIGALRM) {
fprintf(stderr, "Caught wrong signal: %d\n", signal);
}
printf("Got sigalrm, do_sleep() will end\n");
}
void do_sleep(int seconds) {
struct sigaction sa;
sigset_t mask;
sa.sa_handler = &handle_sigalrm; // Intercept and ignore SIGALRM
sa.sa_flags = SA_RESETHAND; // Remove the handler after first signal
sigfillset(&sa.sa_mask);
sigaction(SIGALRM, &sa, NULL);
// Get the current signal mask
sigprocmask(0, NULL, &mask);
// Unblock SIGALRM
sigdelset(&mask, SIGALRM);
// Wait with this mask
alarm(seconds);
sigsuspend(&mask);
printf("sigsuspend() returned\n");
}
@vitorluis

This comment has been minimized.

Show comment Hide comment
@vitorluis

vitorluis Mar 5, 2014

Good example man, many thanks.

Good example man, many thanks.

@idning

This comment has been minimized.

Show comment Hide comment
@idning

idning Jun 17, 2014

you should not use printf in signal handler. see: http://man7.org/linux/man-pages/man7/signal.7.html

idning commented Jun 17, 2014

you should not use printf in signal handler. see: http://man7.org/linux/man-pages/man7/signal.7.html

@JulianTonti

This comment has been minimized.

Show comment Hide comment
@JulianTonti

JulianTonti May 13, 2015

I'm reading man pages and getting buried in technical details. Not fun.

It really helps to read some neatly written code like this. I learned a lot. Thanks for this!

I'm reading man pages and getting buried in technical details. Not fun.

It really helps to read some neatly written code like this. I learned a lot. Thanks for this!

@aspyct

This comment has been minimized.

Show comment Hide comment
@aspyct

aspyct Jun 8, 2015

@idning Thanks for the remark! I added a comment to reflect that :)

Owner

aspyct commented Jun 8, 2015

@idning Thanks for the remark! I added a comment to reflect that :)

@andrewandrepowell

This comment has been minimized.

Show comment Hide comment
@andrewandrepowell

andrewandrepowell Aug 17, 2016

Sure did help!

Sure did help!

@JoschK

This comment has been minimized.

Show comment Hide comment
@JoschK

JoschK Dec 5, 2016

thanks a lot bro very helpful! replace printf with this line:
const char buf[ ] = "your message\n";
write(STDOUT_FILENO, buf, strlen(buf));

JoschK commented Dec 5, 2016

thanks a lot bro very helpful! replace printf with this line:
const char buf[ ] = "your message\n";
write(STDOUT_FILENO, buf, strlen(buf));

@sachind

This comment has been minimized.

Show comment Hide comment
@sachind

sachind Mar 10, 2017

Thanks.

sachind commented Mar 10, 2017

Thanks.

@golh30

This comment has been minimized.

Show comment Hide comment
@golh30

golh30 Apr 11, 2017

Thanks a lot.

golh30 commented Apr 11, 2017

Thanks a lot.

@JimDennis

This comment has been minimized.

Show comment Hide comment
@JimDennis

JimDennis May 8, 2017

Instead of calling printf() from within your signal handler (bad idea) you could implement a FIFO of messages to be written and check that (calling printf() on non-empty) from your main event loop. (Basically any case where you're going to handle signals in a non-terminating fashion entails implementing one or more event loops to handle the interruption by signals and resume whatever was being done by main()).

Instead of calling printf() from within your signal handler (bad idea) you could implement a FIFO of messages to be written and check that (calling printf() on non-empty) from your main event loop. (Basically any case where you're going to handle signals in a non-terminating fashion entails implementing one or more event loops to handle the interruption by signals and resume whatever was being done by main()).

@chailoek

This comment has been minimized.

Show comment Hide comment
@chailoek

chailoek Sep 10, 2017

Thanks. 👍

Thanks. 👍

@BH1SCW

This comment has been minimized.

Show comment Hide comment
@BH1SCW

BH1SCW Sep 12, 2017

Thanks, man, this is useful for me

BH1SCW

BH1SCW commented Sep 12, 2017

Thanks, man, this is useful for me

BH1SCW

@JinhaiZ

This comment has been minimized.

Show comment Hide comment
@JinhaiZ

JinhaiZ Oct 9, 2017

Thanks 👍

JinhaiZ commented Oct 9, 2017

Thanks 👍

@kohbo

This comment has been minimized.

Show comment Hide comment
@kohbo

kohbo Feb 28, 2018

I must be missing an important concept here. After reading these comments and the signal man pages it seems multiple people say that using printf in a signal handler is a bad idea, but not why. Could someone elaborate?

kohbo commented Feb 28, 2018

I must be missing an important concept here. After reading these comments and the signal man pages it seems multiple people say that using printf in a signal handler is a bad idea, but not why. Could someone elaborate?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment