What is the best way in C on Linux for setting up a program that can handle multiple POSIX-signals with the same function?
For example in my code I have a handler function that I want to generically call when ever a signal is caught to perform some actions:
/* Exit handler function called by sigaction */
void exitHandler( int sig, siginfo_t *siginfo, void *ignore )
{
printf("*** Got %d signal from %d\n", siginfo->si_signo, siginfo->si_pid);
loopCounter=0;
return;
}
I have set up two signals to catch by having individual sigaction calls for each signal:
/* Set exit handler function for SIGUSR1 , SIGINT (ctrl+c) */
struct sigaction act;
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = exitHandler;
sigaction( SIGUSR1, &act, 0 );
sigaction( SIGINT, &act, 0 );
Is this the correct way to set up this type of handling? Is there any other way where I don't have to enumerate all the possible signal numbers?
I can't see how you can straightforwardly set a single handler for all signals. However, you can get fairly close by using sigfillset() to generate a set containing all valid signal numbers, and then iterate over possible signal numbers using sigismember() to determine whether that number is in the set, and set a handler if so. OK, I can't see a method of determining what the maximum possible signal number is, so you might have to guess a suitable maximum value.
"signum" parameter of "sigaction" system call is an integer value, which does not work as a flag.
As far as I know, there's no way to assign one handler function for several signals in one call.
Is this the correct way to set up this type of handling?
Not quite - it's not safe to use printf() inside a signal handler, but you can still use write() to stdout or stderr.
Related
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
I am trying to understand how signals work in Linux from the sample program that I found online, but it has some parts which I don't really understand.
This is my sample program:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
void catcher(int sig) {
printf("catcher() has gained control\n");
}
int main(int argc, char *argv[]) {
struct sigaction sigact;
sigset_t sigset;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
sigact.sa_handler = catcher;
sigaction(SIGUSR1, &sigact, NULL);
printf("before first kill()\n");
kill(getpid(), SIGUSR1);
sigemptyset(&sigset);
sigaddset(&sigset, SIGUSR1);
sigprocmask(SIG_SETMASK, &sigset, NULL);
printf("before second kill()\n");
kill(getpid(), SIGUSR1);
printf("after second kill()\n");
return 0;
}
Here is the sample output from my program:
before first kill()
catcher() has gained control
before second kill()
after second kill()
Can I know why the first line in the output is before first kill()? Why doesn't catcher() has gained control appear first?
From what I know, sa_handler consists of two types of signal, signal default and signal ignore.
How do we know which signal it will generate? Why would it trigger the function to print the catcher() has gained control if the signal ignore being generate?
Besides, what is the sa_mask function in this program? In my understanding, sa_mask will block the specified signal.
Can I know why the first line in the output is before first kill()?
Why doesn't catcher() has gained control appear first?
You installed a signal handler that catches SIGUSR1. Until SIGUSR1 is delivered to the process, normal program execution flow keeps happening. So, here:
printf("before first kill()\n");
kill(getpid(), SIGUSR1);
You only generate the signal after printing before first kill(). Why don't you expect this to appear before catcher() has gained control? In other words, when you call printf("before first kill()\n");, no signals have been raised yet, so you can only expect program execution to remain normal.
This line:
kill(getpid(), SIGUSR1);
Generates SIGUSR1. The operating system delivers the signal to the process at a convenient time. Because you installed a handler for SIGUSR1, your signal handler (catcher()) is invoked. You raise the signal after printing the first line, so it is expectable that the next line of output will come from the signal handler.
Note that printf(3) is not async-signal safe, so technically you can't call it from inside a signal handler, but it is usually ok for these toy examples.
From what I know, sa_handler consists of two types of signal, signal
default and signal ignore.
There's more to it than that. The sa_handler field of struct sigaction can have the values SIG_DFL, which corresponds to the default signal action (the default action is listed in man signal), and SIG_IGN, which means the signal is ignored (nothing happens when it is raised). But sa_handler can also be a pointer to a function that you want to be invoked every time the signal is delivered. This is what the code you showed is doing - it is saying: Hey, when SIGUSR1 is delivered, please call catcher().
How do we know which signal it will generate? Why would it trigger the
function to print the catcher() has gained control if the signal
ignore being generate?
You indicated a signal (SIGUSR1) when you called sigaction(2) to setup the handler. So, catcher() will be called when SIGUSR1 is delivered.
Besides, what is the sa_mask function in this program? In my
understanding, sa_mask will block the specified signal.
It's a signal mask that is atomically installed when the signal handler is entered, and uninstalled when the signal handler returns. By default, even if you pass it an empty mask, the signal being caught is always blocked upon entering the handler (unless the SA_NODEFER flag is set in the sa_flags field of struct sigaction). However, you might want to block other signals while the handler is executing - the way you do that is by indicating these signals in sa_mask.
i have the following case
void foo() {
printf("hi\n");
while(1);
}
int main(void)
{
struct sigaction temp;
temp.sa_handler = &foo;
sigfillset(&temp.sa_mask);
sigdelset(&temp.sa_mask, SIGVTALRM);
sigdelset(&temp.sa_mask, SIGINT );
sigaction(SIGVTALRM, &temp, NULL);
struct itimerval tv;
tv.it_value.tv_sec = 2; /* first time interval, seconds part */
tv.it_value.tv_usec = 0; /* first time interval, microseconds part */
tv.it_interval.tv_sec = 2; /* following time intervals, seconds part */
tv.it_interval.tv_usec = 0; /* following time intervals, microseconds part */
if (setitimer(ITIMER_VIRTUAL, &tv, NULL)){
perror(NULL);
}
while(1);
return 0;
}
all I want is that every 2 seconds foo will be called (foo actually does some other stuff other than while(1), just assume foo run takes more than 2 seconds), after 2 seconds foo is indeed called but then no other call is made untill foo returns. I tried playing with the signal masks (hence the sigfillset) but also when simply calling signal(SIGVTALRM, foo) no changes are made in the result. I also tried having the itimerval and the sigactions variables declared outside main and it didn't quite affect anything.
is the thing I'm trying to do even possible?
thanks!
reference: <http://www.gnu.org/software/libc/manual/html_node/Signals-in-Handler.html>
24.4.4 Signals Arriving While a Handler Runs
What happens if another signal arrives while your signal handler function is running?
When the handler for a particular signal is invoked, that signal is automatically blocked until the handler returns. That means that if two signals of the same kind arrive close together, the second one will be held until the first has been handled. (The handler can explicitly unblock the signal using sigprocmask, if you want to allow more signals of this type to arrive; see Process Signal Mask.)
However, your handler can still be interrupted by delivery of another kind of signal. To avoid this, you can use the sa_mask member of the action structure passed to sigaction to explicitly specify which signals should be blocked while the signal handler runs. These signals are in addition to the signal for which the handler was invoked, and any other signals that are normally blocked by the process. See Blocking for Handler.
When the handler returns, the set of blocked signals is restored to the value it had before the handler ran. So using sigprocmask inside the handler only affects what signals can arrive during the execution of the handler itself, not what signals can arrive once the handler returns.
Portability Note: Always use sigaction to establish a handler for a signal that you expect to receive asynchronously, if you want your program to work properly on System V Unix. On this system, the handling of a signal whose handler was established with signal automatically sets the signal’s action back to SIG_DFL, and the handler must re-establish itself each time it runs. This practice, while inconvenient, does work when signals cannot arrive in succession. However, if another signal can arrive right away, it may arrive before the handler can re-establish itself. Then the second signal would receive the default handling, which could terminate the process.
reference:<http://www.gnu.org/software/libc/manual/html_node/Process-Signal-Mask.html#Process-Signal-Mask>
24.7.3 Process Signal Mask
The collection of signals that are currently blocked is called the signal mask. Each process has its own signal mask. When you create a new process (see Creating a Process), it inherits its parent’s mask. You can block or unblock signals with total flexibility by modifying the signal mask.
The prototype for the sigprocmask function is in signal.h.
Note that you must not use sigprocmask in multi-threaded processes, because each thread has its own signal mask and there is no single process signal mask. According to POSIX, the behavior of sigprocmask in a multi-threaded process is “unspecified”. Instead, use pthread_sigmask.
Function: int sigprocmask (int how, const sigset_t *restrict set, sigset_t *restrict oldset)
Preliminary: | MT-Unsafe race:sigprocmask/bsd(SIG_UNBLOCK) | AS-Unsafe lock/hurd | AC-Unsafe lock/hurd | See POSIX Safety Concepts.
The sigprocmask function is used to examine or change the calling process’s signal mask. The how argument determines how the signal mask is changed, and must be one of the following values:
SIG_BLOCK
Block the signals in set—add them to the existing mask. In other words, the new mask is the union of the existing mask and set.
SIG_UNBLOCK
Unblock the signals in set—remove them from the existing mask.
SIG_SETMASK
Use set for the mask; ignore the previous value of the mask.
The last argument, oldset, is used to return information about the old process signal mask. If you just want to change the mask without looking at it, pass a null pointer as the oldset argument. Similarly, if you want to know what’s in the mask without changing it, pass a null pointer for set (in this case the how argument is not significant). The oldset argument is often used to remember the previous signal mask in order to restore it later. (Since the signal mask is inherited over fork and exec calls, you can’t predict what its contents are when your program starts running.)
If invoking sigprocmask causes any pending signals to be unblocked, at least one of those signals is delivered to the process before sigprocmask returns. The order in which pending signals are delivered is not specified, but you can control the order explicitly by making multiple sigprocmask calls to unblock various signals one at a time.
The sigprocmask function returns 0 if successful, and -1 to indicate an error. The following errno error conditions are defined for this function:
EINVAL
The how argument is invalid.
You can’t block the SIGKILL and SIGSTOP signals, but if the signal set includes these, sigprocmask just ignores them instead of returning an error status.
Remember, too, that blocking program error signals such as SIGFPE leads to undesirable results for signals generated by an actual program error (as opposed to signals sent with raise or kill). This is because your program may be too broken to be able to continue executing to a point where the signal is unblocked again. See Program Error Signals.
I know that this has been answered and accepted already but I made tiny changes to the OP's question as follows in accordance with my comments and had a successful result (foo being called every 2 seconds, ad infinitum)
Note that addition of the memset of the temp variable and the changing from SIGVTALRM to SIGALRM.
#include <stdio.h>
#include <sys/time.h>
void foo() {
printf("hi\n");
}
int main(int argc, char **argv)
{
struct sigaction temp;
memset(&temp, 0, sizeof(temp));
temp.sa_handler = &foo;
sigfillset(&temp.sa_mask);
sigdelset(&temp.sa_mask, SIGALRM);
sigdelset(&temp.sa_mask, SIGINT );
sigaction(SIGALRM, &temp, NULL);
struct itimerval tv;
tv.it_value.tv_sec = 2; /* first time interval, seconds part */
tv.it_value.tv_usec = 0; /* first time interval, microseconds part */
tv.it_interval.tv_sec = 2; /* following time intervals, seconds part */
tv.it_interval.tv_usec = 0; /* following time intervals, microseconds part */
if (setitimer(ITIMER_REAL, &tv, NULL)){
fprintf (stderr, "cannot start timer\n");
perror(NULL);
}
while(1) {
fprintf (stdout, "sleep 1\n");
sleep (1);
}
return 0;
}
What is the best way in C on Linux for setting up a program that can handle multiple POSIX-signals with the same function?
For example in my code I have a handler function that I want to generically call when ever a signal is caught to perform some actions:
/* Exit handler function called by sigaction */
void exitHandler( int sig, siginfo_t *siginfo, void *ignore )
{
printf("*** Got %d signal from %d\n", siginfo->si_signo, siginfo->si_pid);
loopCounter=0;
return;
}
I have set up two signals to catch by having individual sigaction calls for each signal:
/* Set exit handler function for SIGUSR1 , SIGINT (ctrl+c) */
struct sigaction act;
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = exitHandler;
sigaction( SIGUSR1, &act, 0 );
sigaction( SIGINT, &act, 0 );
Is this the correct way to set up this type of handling? Is there any other way where I don't have to enumerate all the possible signal numbers?
I can't see how you can straightforwardly set a single handler for all signals. However, you can get fairly close by using sigfillset() to generate a set containing all valid signal numbers, and then iterate over possible signal numbers using sigismember() to determine whether that number is in the set, and set a handler if so. OK, I can't see a method of determining what the maximum possible signal number is, so you might have to guess a suitable maximum value.
"signum" parameter of "sigaction" system call is an integer value, which does not work as a flag.
As far as I know, there's no way to assign one handler function for several signals in one call.
Is this the correct way to set up this type of handling?
Not quite - it's not safe to use printf() inside a signal handler, but you can still use write() to stdout or stderr.