Related
I'm learning to write a signal handler in C for a Linux system. Here's my code:
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
void sig_handler(int signum){
//Return type of the handler function should be void
printf("\nInside handler function\n");
}
int main(){
signal(SIGINT,sig_handler); // Register signal handler
for(int i=1;;i++){ //Infinite loop
printf("%d : Inside main function\n",i);
sleep(1); // Delay for 1 second
}
return 0;
}
My question is, why when I hit Ctrl+C twice, the program stops? Shouldn't it be that everytime I hit Ctrl+C the signal handler runs, so effectively the program should run forever?
In reality, this is my output, the signal handler is only called in the first Ctrl+C, not the second time:
1 : Inside main function
2 : Inside main function
3 : Inside main function
4 : Inside main function
^C
Inside handler function
5 : Inside main function
6 : Inside main function
7 : Inside main function
8 : Inside main function
9 : Inside main function
10 : Inside main function
^C
On Linux, a number of factors contribute to the behaviour of signal. Depending on the version of glibc, the defined feature_test_macros(7), and whether or not the kernel version of the function is used, the results will differ between two polarizing behaviours, described as System V semantics and BSD semantics:
With System V semantics, when a signal handler is invoked, the disposition of the signal is reset to its default behaviour (SIG_DFL). Additionally, further delivery of the signal is not blocked during the execution of the signal handler.
With BSD semantics, when a signal handler is invoked, the disposition of the signal is not reset, and further delivery of the signal is blocked during the execution of the signal handler. Additionally, certain system calls will be automatically be restarted if interrupted by the signal handler.
Your version of signal appears to be supplying System V semantics. The signal disposition is reset after the first signal handler, and the following SIGINT terminates the program.
See signal(2) and its notes on portability for more details.
See signal-safety(7) for a list of functions that are safe to call from within a signal handler. printf is not an async-signal-safe function, and should not be called from within a signal handler.
Use write(2) instead.
The quickest fix is to call signal within the signal handler to once again set the signal disposition to the signal handler.
void sig_handler(int signum)
{
(void) signum;
char msg[] = "Signal handler called!\n";
write(STDOUT_FILENO, msg, strlen(msg));
signal(SIGINT, sig_handler);
}
The more robust solution is to use sigaction(2) to establish signal handlers, as its behaviour is more consistent and provides better portability.
A basic example:
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
volatile sig_atomic_t sig_count = 0;
void sig_handler(int signum)
{
(void) signum;
sig_count++;
char msg[] = "Signal handler called!\n";
write(STDOUT_FILENO, msg, strlen(msg));
}
int main(void)
{
struct sigaction sa = { .sa_handler = sig_handler };
sigaction(SIGINT, &sa, NULL);
while (sig_count < 5);
}
The default behaviour of sigaction is similar to that described by BSD semantics, with the exception that certain system calls will not restart when interrupted by the signal handler.
To enable this behaviour, the .sa_flags member of the struct sigaction should contain the SA_RESTART flag.
To mimic System V semantics, the .sa_flags member of the struct sigaction should contain the SA_RESETHAND and SA_NODEFER flags.
TL;DR: use sigaction(), not signal(), to install signal handlers.
As comments and other answers have observed, your signal handler has undefined behavior as a result of its call to printf(). In the event that the signal handler is ever triggered, that gives the whole program UB, which makes it very difficult to reason about its observed behavior.
But consider this variation instead:
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
void sig_handler(int signum){
static const char msg[] = "\nInside handler function\n";
write(1, msg, sizeof(msg) - 1);
}
int main(){
signal(SIGINT,sig_handler); // Register signal handler
for(int i=1;;i++){ //Infinite loop
printf("%d : Inside main function\n",i);
sleep(1); // Delay for 1 second
}
return 0;
}
I have switched from printf to write inside the signal handler, thereby removing the UB. And if I compile it with
gcc -std=c11 htest.c -o htest
then the resulting executable still exhibits the behavior you describe: the first Ctrl-C is handled, but the second is not.
HOWEVER, if I instead compile with
gcc htest.c -o htest
then the resulting program intercepts every Ctrl-C I type, as I guess you were expecting. So what's going on?
The problem revolves around the fact that the details of the behavior of the signal() function have varied historically. For more detail, see the portability notes in the signal(2) manual page, but here's a brief rundown:
In the original System V UNIX, the custom signal handlers installed via signal() were one-shots: when such a handler was triggered, the disposition for the signal was reset to its default, and the signal was not blocked during execution of the handler.
The System V behavior has some issues, so BSD implemented signal() differently in these respects: signal disposition is not automatically reset, and the signal is blocked during execution of the handler. And additionally, in BSD, certain blocking system calls are automatically restarted if interrupted by a signal that does not result in the program terminating.
These differences mean that the only portable uses for the signal() function are for setting signal disposition to SIG_DFL or SIG_IGN.
Glibc supports both alternatives, and which one you get is controlled by feature test macros, which can be influenced by compiler command-line options. It defaults to BSD semantics as long as the _DEFAULT_SOURCE macro is defined (_BSD_SOURCE prior to glibc 2.19). Gcc defines that macro by default, but some command line options, notably the strict-conformance -std options, cause Gcc not to define it. And that's why I could get different behavior depending on how I compiled the program.
On POSIX systems, the solution is to use the sigaction() function instead of signal() to register signal handlers. This function has well-defined semantics for all the areas of behavioral difference described above, including a well-defined means to choose among them. However, to get its declaration included in all cases, you will need to ensure that a different feature-test macro is defined. For example:
// Manipulation of feature-test macros should precede all header inclusions
#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 1
#endif
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void sig_handler(int signum) {
static const char msg[] = "\nInside handler function\n";
write(1, msg, sizeof(msg) - 1);
}
int main(void) {
// Register signal handler
struct sigaction sa = { .sa_handler = sig_handler /* default sa_mask and sa_flags */ };
sigaction(SIGINT, &sa, NULL);
while (int i = 1; ; i++) {
printf("%d : Inside main function\n",i);
sleep(1);
}
return 0;
}
That will reliably get you a handler that is not reset when triggered, does have SIGINT blocked while it is being handled, and does not automatically restart system calls interrupted by the signal. That will prevent the program from being killed via a SIGINT, though there are other ways it can be killed, such as via a SIGKILL (which cannot be blocked or handled).
My question is, why when I hit Ctrl+C twice, the program stops?
Undefined behaviour:
Your code invokes undefined behaviour because it calls a function (printf) that isn't async-signal-safe.¹
From C11:
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, or the signal handler calls any
function in the standard library other than the abort function, the
_Exit function, the quick_exit function, or the signal function with the first argument equal to the signal number corresponding to the
signal that caused the invocation of the handler.
As per the C Standard, you can only call:
abort()
_Exit()
quick_exit()
signal() /* With the first argument equal to the signal number the handler caught */
safely inside a signal handler.
The POSIX standard, however, specifies many more functions. So you can have write(), but not printf().
[1] — An async-signal-safe function is one that can be safely called from within a signal handler. (Linux man page)
Don't use signal(). Instead use sigaction().
Per the spec, [w]hen a signal occurs, and [the second argument to signal] points to a function, it is implementation-defined whether the equivalent of a
signal(sig, SIG_DFL); is executed...
Historically, some UNIX flavors reset the signal disposition to the default behavior upon entry into the user-defined signal handler. That meant that your first SIGFOO would invoke your handler, but your next one would trigger SIG_DFL behavior. Importantly, this is consistent with what you observe.
Yes, printf() is unsafe (undefined behavior) to call in a signal handler. In the code you provide, it might conceivably interrupt itself, which can lead to all sorts of nastiness. In practice, people call printf() all the time in trivial programs to no great harm. The fact that your behavior is repeatable suggests that you are getting deterministic behavior, and not dreadful eldritch magic undefined behavior.
sigaction() forces you to specify what kind of behavior you want when the user-supplied handler is invoked, and is thus superior to signal().
I have this assignment to do:
Write a function void myfunct(void(*f)(int sig)) which sets f as handler to all the possible signals.
I have two problems:
How can a get all the possible signals? Is there a function for this? Can I iterate through them in some way?
Will it really work to set the function f as handler given that it takes a parameter? Shouldn't it not have any parameters?
Thank you.
I would personally instead iterate over a static list of signal numbers, and use preprocessor directives to detect which ones are supported (at compile time). For example:
#include <signal.h>
static const all_signals[] = {
#ifdef SIGHUP
SIGHUP, /* POSIX.1 */
#endif
#ifdef SIGQUIT
SIGQUIT, /* POSIX.1 */
#endif
#ifdef SIGTRAP
SIGTRAP, /* POSIX.1 */
#endif
#ifdef SIGIO
SIGIO, /* BSD/Linux */
#endif
/*
* Other signal names omitted for brevity
*/
/* C89/C99/C11 standard signals: */
SIGABRT,
SIGFPE,
SIGILL,
SIGINT,
SIGSEGV,
/* SIGTERM (C89/C99/C11) is also the terminating signal number */
SIGTERM
};
with SIGTERM being the last entry in the array for which a signal handler is installed:
struct sigaction act;
int i = 0;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = your_signal_handler;
act.sa_flags = 0;
do {
if (sigaction(all_signals[i], &act, NULL)) {
fprintf(stderr, "Cannot install signal %d handler: %s.\n", all_signals[i], strerror(errno));
exit(EXIT_FAILURE);
}
} while (all_signals[i++] != SIGTERM);
This way your code does not require POSIX etc. support to work, but does support POSIX signals if available at compile time.
You can check the Wikipedia Unix signal article and man 7 signal for known signal names.
You can also install the signal handler for POSIX realtime signals using
#if SIGRTMAX-0 > SIGRTMIN-0
for (i = SIGRTMIN; i <= SIGRTMAX; i++)
if (sigaction(i, &act, NULL)) {
fprintf(stderr, "Cannot install realtime signal %d handler: %s.\n", i, strerror(errno));
exit(EXIT_FAILURE);
}
#endif
How can a get all the possible signals? Is there a function for this? Can I iterate through them in some way?
Most implementation provide a constant such as NSIG (Glibc provides NSIG) or _NSIG (Linux provides _NSIG). So, you can loop through that constant and set the same signal handling function for all of them.
There's no POSIX defined value for "highest signal number". There's been a proposal in POSIX to add a macro NSIG_MAX.
{NSIG_MAX}
Maximum possible return value of sysconf(_SC_NSIG). See [cross-ref to XSH sysconf()]. The value of {NSIG_MAX} shall be no greater than the number of signals that the sigset_t type (see [cross-ref to ]) is capable of representing, ignoring any restrictions imposed by sigfillset() or sigaddset().
But it hasn't made it to POSIX yet (most probably it'll a part of the POSIX version - issue 8).
Will it really work to set the function f as handler given that it takes a parameter? Shouldn't it not have any parameters?
The parameter that the signal handling function takes doesn't matter when you are setting a signal disposition. It takes the signal number but that doesn't prevent you from using it as a handler for multiple signals.
But there are special cases you need to handle. Certain signals that can't caught or ignored (SIGKILL and SIGSTOP). There are other signals (SIGFPE, SIGILL and SIGSEGV) for which, while allowed to caught, the signal handler can't return to its caller (i.e. you need exit from the signal handler).
1. How can a get all the possible signals? Is there a function for this? Can I iterate through them in some way?
Seems like there isn't a standardized list you can iterate, but we actually don't need such a list for your task. Signal numbers are always positive and at most SIGRTMAX. In between 1 and SIGRTMAX there might be some numbers that do not correspond to any signal, but you can still try to set a handler for these. From the posix specification of sigaction(sig, ...) (emphasis mine):
RETURN VALUE
Upon successful completion, sigaction() shall return 0; otherwise, -1 shall be returned, errno shall be set to indicate the error, and no new signal-catching function shall be installed.
ERRORS
The sigaction() function shall fail if:
[EINVAL]
The sig argument is not a valid signal number or an attempt is made to catch a signal that cannot be caught or ignore a signal that cannot be ignored.
So it is perfectly fine to call sigaction(123, ...) where 123 is not a signal.
2. Will it really work to set the function f as handler given that it takes a parameter? Shouldn't it not have any parameters?
Why do you think it should have no parameters?
Again, from the posix specification of sigaction():
Member Type
Member Name
Description
void(*) (int)
sa_handler
Pointer to a signal-catching function
The signal handler is supposed to have an int parameter, just like your function f has, so there is no problem.
Putting everything together
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
struct sigaction siga;
void f(int sig) {
printf("Caught signal %d\n", sig);
}
// sets f as handler to all the possible signals.
void myfunct(void(*f)(int sig)) {
siga.sa_handler = f;
for (int sig = 1; sig <= SIGRTMAX; ++sig) {
// this might return -1 and set errno, but we don't care
sigaction(sig, &siga, NULL);
}
}
int main() {
myfunct(f);
pause(); // wait for signal
return 0;
}
When I capture any signal, I want to send message 'end' to child process and if they still live use kill pid to kill it. NO global variable
I think I have to use sa_sigaction, but I confuse how to send FILE* of pipe and pid of child to it. Can someone can give em an example for this??
I'd like to pass pip and pid to hdl how to change my code??
I'd like to capture all signal that can be captured, what is the first parameter
of sigaction(SIGINT, &act, pip) ?? instead of just SIGINT
Thanks in advance
static void hdl (int sig, siginfo_t *siginfo, void *pip)
{
xxxxxxx
}
int main() {
FILE** pip;
int* pid;
struct sigaction act;
memset (&act, '\0', sizeof(act));
act.sa_sigaction = &hdl;
act.sa_flags = SA_SIGINFO;
sigaction(SIGINT, &act, pip);
sleep (10);
return 0;
}
This is simply not possible (to pass more arguments to a signal handler). You need to use some global or static variable.
You cannot add any parameter to your signal handler, even as simple as a pid_t or FILE* or some void*
Signals are delivered by the kernel, and it is the kernel (with some help from some low-level, machine and ABI specific, trampoline-like code in the libc) which pushes a call frame for the signal handler (and also one for sigreturn(2)). So the signature of handlers is fixed, as documented in signal(7)
Besides, you have a small number of signals. So you could consider having a global variable with an array of data related to the signal.
With sigaction(2) (using SA_SIGINFO) you get an int signal number, a siginfo_t and a ucontext_t pointers to your handler. You can use them appropriately. For instance, for a SIGIO you can use the si_fd to get the file descriptor causing that signal.
Beware that signal handlers are only allowed to call (even indirectly) a very small set of functions (the so-called async-signal-safe functions, which are often a subset of syscalls). In particular calling fputs or any <stdio.h> function; or malloc is forbidden inside signal handlers. Hence, even thru some global variables, you should not use any FILE* inside your signal handler (that would be undefined behavior, even if it might apparently work as you want sometimes).
A common habit (see POSIX documentation about signal.h) is to only set some global volatile sig_atomic_t flags in the signal handler, and to test (and reset) that flag outside the handler. You'll then be able typically to call poll(2) -probably using fileno(3)- or waitpid(2) (outside of yous signal handler, e.g. in some loop inside your main code).
You need to reads books on Advanced Linux Programming and or Advanced Posix Programming
This question already has answers here:
Catch Ctrl-C in C
(9 answers)
Closed 2 years ago.
I use the following code to catch Ctrl+C in my C program
Code
void sig_handler(int signo)
{
if (signo == SIGINT)
exit(EXIT_SUCCESS);
}
void main ()
{
......
if(signal(SIGINT, sig_handler)== SIG_ERR)
{
printf(">>>>>>>>>>>>>>>>>>>>> SIG INT EROOR !!!! sigint=%d ID=%d \n",SIGINT, getpid());
}
else
printf(">>>>>>>>>>AFTER>>>>>>>>>>> SIG INT sigint=%d PID=%d \n",SIGINT, getpid());
char *buf = NULL;
asprintf(&buf, "%d", getpid());
write(fd, buf, strlen(buf));
free(buf);
uloop_run(); //entering main loop
ubus_exit();
uloop_done();
xml_exit();
config_exit();
free(tmp);
closelog();
log_message(NAME, L_NOTICE, "exiting\n");
return 0;
}
My purpose is to catch Ctrl + C but it seem the signal handler function i.e sig_handler() doesn't run.
I want to know how to fix it?
As iharob answered, you should add the handler for the signal.
However, you should carefully read signal(7) and notice that it is not legal to call printf from inside a signal handler (since printf is not an async-signal-safe function). You should use write(2) instead of printf(3).
This restriction is significant and important. Don't forget that e.g. both printf and malloc could be interrupted at arbitrary moments, but they are not designed for that.
At the very least, call fflush(3) and/or end your printf format string with a \n; but that would still be undefined behavior (but you might be "unlucky" to have it do what you want most of the time).
BTW, it is recommended today to use sigaction(2) instead of the "obsolete" signal(2)
In practice, the recommended practice inside a signal handler would be most of the time to set some volatile sigatomic_t flag (to be tested outside the handler), or to call siglongjmp(3). If you insist on doing something else, be sure that you use (even indirectly) only async-signal-safe functions (and there are few of them, mostly the syscalls(2) ....). In particular, stdio(3) & malloc(3) should never be used from a signal handler (and that rules out most of the standard C functions, or most of library functions).
You may want to have some event loop around poll(2) (then you might be interested by the Linux specific signalfd(2)....); you should compile with all warnings and debug info (gcc -Wall -Wextra -g). Then use the gdb debugger (and also strace(1)) to debug your program.
Are you sure that the functions you are using (e.g. uloop_run, etc...) are not blocking or ignoring signals?. You should strace your program to find out!
You should add the handler to the signal with this function
sighandler_t signal(int signum, sighandler_t handler);
in your case
signal(SIGNINT, sig_handler);
One more thing, your main function must return int, so void main() is wrong, it should be int main().
The uloop_run function, from OpenWrt installs a signal handler for SIGINT so it's not possible to interrup it, and it overrides your signal handler.
That is the actual reason why your signal handler is never called.
The program wont handle the signal, until the uloop_run function exits, this is the uloop_run source with the relevant part
static void uloop_setup_signals(bool add)
{
struct sigaction s;
struct sigaction *act, *oldact;
memset(&s, 0, sizeof(struct sigaction));
if (add) {
s.sa_handler = uloop_handle_sigint;
s.sa_flags = 0;
act = &s;
oldact = &org_sighandler;
} else {
act = &org_sighandler;
oldact = NULL;
}
sigaction(SIGINT, act, oldact);
if (uloop_handle_sigchld) {
if (add) {
//act already points to s, so no need to update pointer
s.sa_handler = uloop_sigchld;
oldact = &org_sighandler_child;
} else {
act = &org_sighandler_child;
oldact = NULL;
}
sigaction(SIGCHLD, act, oldact);
}
}
void uloop_run(void)
{
struct timeval tv;
/*
* Handlers are only updated for the first call to uloop_run() (and restored
* when this call is done).
*/
if (!uloop_recursive_count++)
uloop_setup_signals(true);
while(!uloop_cancelled)
{
uloop_gettime(&tv);
uloop_gettime(&tv);
uloop_run_events(uloop_get_next_timeout(&tv));
}
if (!--uloop_recursive_count)
uloop_setup_signals(false);
}
as you can see, uloop_setup_signals(true); installs a new signal handler for SIGNINT and when the loop is finished uloop_setup_signals(false); is called restoring the previous signal handler.
So, this is the reason.
I've got a small program that opens a file and does some operation on it. I subscribed the file closure to the program termination as follows:
static
void exit_handler (int ev, void *arg)
{
fprintf(stderr, "bye %d\n", WEXITSTATUS(ev));
fclose((FILE *)arg);
}
int main (int argc, char *argv[])
{
FILE *out;
...
out = fopen(argv[1], "wt");
if (out == NULL) {
perror("Opening output file");
exit(EXIT_FAILURE);
}
on_exit(exit_handler, out);
...
}
Trying to execute this I notice that it works properly only if the program terminates normally. In case of CTRL+C (SIGINT) the exit_handler callback is not executed.
Isn't that weird? Should I associate a exit(EXIT_FAILURE) call to the signal handler for SIGTERM? What is the best practice in this case?
on_exit will not be invoked for SIGTERM signals. You need to add a handler for it with signal. For example:
void signalHandler(void)
{
...
}
int main(void)
{
signal(SIGTERM, signalHandler);
}
Also note that SIGKILL can not be caught by design.
First of all, on_exit isn't specified by POSIX (atexit with the same semantics is). Second , the linux manual says:
The on_exit() function registers the given function to be called
at normal process termination, whether via exit(3) or via return from
the program's main().
Getting killed by a signal is not a normal exit for a process so callbacks installed with on_exit and atexit aren't implicitly called.
No, and in fact what you want is impossible. The signal generated by Ctrl+C is asynchronous, meaning it could occur between any two machine instructions in your program depending on when Ctrl+C is hit. As such, unless your program is thoroughly avoiding calling async-signal-unsafe functions anywhere in the main program flow, it's illegal to call async-signal-unsafe functions from the signal handler. exit is async-signal-unsafe, as is most of the default cleanup activity it does (like flushing/closing open files). I would expect the atexit function you want to register (atexit, not on_exit, is the correct name for this function) is also going to want to do async-signal-unsafe things.
If you need to perform cleanup when exiting based on a signal, you need to install a signal handler that does not exit itself, but instead sets a global volatile flag that your main program flow will later inspect (and exit if it's true).
From man page of on_exit,
The on_exit() function registers the given function to be called at
normal process termination, whether via exit(3) or via return from the
program's main().
So you need to explicity hook up a handler for SIGTERM using specific functions from signal.h
Something on the lines of
struct sigaction action;
memset (&action, 0, sizeof(action));
action.sa_handler = sigterm_handler;
if (sigaction(SIGTERM, &action, 0))
{
perror ("sigaction");
return 1;
}
/* SIGTERM handler. */
static void sigterm_handler (int sig)
{
...
}