How to block signals in C? - c

I'm trying to create a program that blocks the signal SIGUSR1 and the it unblocks the signal.
In the middle I want to see that the signal is blocked using sigpending. But it always says that the signal isn't blocked, and I can use the signal when it's supposed to be blocked.
This is the code that I have.
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
static void signals(int signaln)
{
switch (signaln) {
case SIGUSR1:
printf("Signal SIGUSR1\n"); break;
}
return;
}
main()
{
sigset_t set,set2;
struct sigaction sigs;
sigs.sa_handler = signals;
sigemptyset(&sigs.sa_mask);
sigs.sa_flags=SA_ONESHOT;
sigaction(SIGUSR1, &sigs,0);
sigemptyset(&set);
sigemptyset(&set2);
sigaddset(&set,SIGUSR1);
if(sigprocmask(SIG_BLOCK, &set, NULL)==0){
printf("Blocking SISGUSR1...\n");
}
sigpending(&set2);
if (sigismember(&set2,SIGUSR1)==1)
{
printf("The signal is blocked\n"); //it should print this
}
wait(2);
kill(getpid(),SIGUSR1); //the signal shouldn't work
wait(2);
if(sigprocmask(SIG_UNBLOCK, &set, NULL)==0){
printf("Unblocking SIGUSR1\n");
}
}
Could anyone help me?

sigpending doesn't tell you whether a signal is blocked. It tells you whether a signal is waiting to be delivered. (i.e., the signal is blocked and one has been sent.)
Also, blocked doesn't meean that the signal won't be delivered; it means that the signal won't be delivered now. So you can send the signal, and it will be delivered as soon as the signal is unblocked; probably after the call to sigprocmask(SIGUNBLOCKED...) but before the call to printf, so you'll probably see the signal received message before you see the "unblocking" message.

Related

Linux - Why is a blocked and ignored signal pending?

When sending a signal to a process that both blocks and ignores it, the kernel still keeps this signal in the pending list (my terminology here). In this case the kernel behaves like the signal is only blocked, although it should also be ignored. I can't understand this behavior. Here is a C code for example with SIGUSR1 (which has the index 10):
#define _GNU_SOURCE
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
void handler(int sig)
{
printf("Received signal %d\n", sig);
}
int main(int argc, char *argv[])
{
printf("PID is %ld\n", (long) getpid());
int sig = SIGUSR1;
//creating the sigaction struct and setting the handler
struct sigaction act;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
if(sigaction(sig, &act, NULL) == -1)
{
printf("Error: sigaction\n");
exit(1);
}
//Blocking the signal
sigset_t blockedSignals;
sigemptyset(&blockedSignals);
sigaddset(&blockedSignals, sig);
printf("Blocking signal %d\n", sig);
if(sigprocmask(SIG_SETMASK, &blockedSignals, NULL) == -1)
{
printf("Error: sigprocmask\n");
exit(1);
}
//Ignoring the signal
act.sa_handler = SIG_IGN;
printf("Ignoring signal %d\n", sig);
if(sigaction(sig, &act, NULL) == -1)
{
printf("Error: sigaction\n");
exit(1);
}
//Sleeping for a while in order to give the user a chance to send the signal to this process
printf("Sleeping for 20 sec. Please send the signal.\n");
sleep(20);
//Unblocking the signal
/*sigemptyset(&blockedSignals);
printf("Unblocking signal %d\n", sig);
if(sigprocmask(SIG_SETMASK, &blockedSignals, NULL) == -1)
{
printf("Error: sigprocmask\n");
exit(1);
}*/
//Let's check the pending list
sigset_t pendingSignals;
sigemptyset(&pendingSignals);
if(sigpending(&pendingSignals) == -1)
{
printf("Error: sigpending\n");
exit(1);
}
if(sigismember(&pendingSignals, sig) == 1)
{
printf("Signal %d is pending.\n", sig);
}
else
{
printf("Signal %d isn't pending.\n", sig);
}
exit(0);
}
SIGUSR1 is both blocked and ignored. While this process sleeps, if I send a SIGUSR1 to it (from shell: kill -s SIGUSR1 PID), and then checks the pending list, I get this printing:
Signal 10 is pending.
If I uncomment the commented block of code, which unblocks the signal:
sigemptyset(&blockedSignals);
printf("Unblocking signal %d\n", sig);
if(sigprocmask(SIG_SETMASK, &blockedSignals, NULL) == -1)
{
printf("Error: sigprocmask\n");
exit(1);
}
and repeat the experiment, I see the following printing:
Signal 10 isn't pending.
It's like the kernel gives priority to the 'blocking' over the 'ignoring'.
Is it really the case?
Update: As far as I understand, when the process ignores a signal, it means that the kernel won't send it to the process. This also means that it won't keep it in the pending list. For example, if a signal is only blocked by the process, and exists in the pending list, and then we call 'sigaction' in order to ignore it, the kernel will remove this signal from the pending list. So the question is, why if we block+ignore in ahead, the kernel inserts the signal to its pending list?
Blocking a signal and ignoring it are two separate and independent things.
Ignoring a signal by setting its disposition to SIG_IGN instructs that when the signal is delivered the resulting action should be to do nothing.
Blocking a signal (by setting a signal mask that includes that signal) has the effect of preventing that signal from being delivered at all. If it is received, then it will remain pending until unblocked or the process terminates. Signal disposition does not matter until the signal is actually delivered. So,
It's like the kernel gives priority to the 'blocking' over the 'ignoring'. Is it really the case?
Yes. The effect of ignoring a signal cannot be realized while that signal is blocked.
With regard to the update to the question:
As far as I understand, when the process ignores a signal, it means
that the kernel won't send it to the process.
No, that's incorrect. SIG_IGN is a signal disposition. That's what the process does in response to a signal. It can't respond if the kernel doesn't send the signal in the first place.
Note that another option for signal disposition is for the process to run a custom signal handler function. It should be clearer that this is something that the process must do, not something that the kernel does for it.
This also means that it
won't keep it in the pending list.
It would mean that ignored signals never became pending, but your understanding of the semantics is incorrect.
So the question is, why if we
block+ignore from ahead, the kernel inserts the signal to its pending
list?
Because that's what the kernel does with signals. You can characterize it as what the kernel does with all signals, but those that aren't blocked don't stay pending very long.

Is SIGABRT a blockable signal

Consider the simple example below which registers for a signal handler for SIGABRT, then calls abort(). When I run it, the program terminates before printing Done but after async-signal-safe printing in the trapped signal.
This implies that SIGABRT is not a blockable signal. This seems to be supported by this StackOverflow answer. However, I cannot find any corroborating evidence of that behavior in the signal man page, which clearly states that The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored but makes no similar mention for SIGABRT.
Can someone please enlighten me on this behavior?
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
static struct sigaction old_sa;
static void my_handler(int signo)
{
const char x[] = "In Handler\n";
write(STDOUT_FILENO, x, strlen(x));
}
int main()
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = my_handler;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGABRT);
if (0 != sigaction(SIGABRT, &sa, &old_sa))
{
perror("sigaction");
exit(EXIT_FAILURE);
}
printf("Ready\n");
abort();
printf("Done\n");
exit(0);
}
Compiled with gcc ./try.c && ./a.out generates the following output
Ready
In Handler
Aborted
SIGABRT can be blocked. But the abort() function unblocks the signal before sending the signal.
This is specified in POSIX:
The abort() function shall override blocking or ignoring the SIGABRT signal.
You'll get the expected result if you use
kill(getpid(), SIGABRT);
instead of calling abort()
Your handler caught the signal. Then it returned. C 2018 7.22.4.1 2 says “The abort function causes abnormal program termination to occur, unless the signal SIGABRT is being caught and the signal handler does not return.” So, once your handler returns, the abort routine continues doing its thing, which is to terminate your program.
If instead of abort(); you use raise(SIGABRT); to raise the signal without calling the abort routine, then the signal handler will be called, will print, and will return, after which printf("Done\n"); will be executed.

Signal receiver threads in C and pthread_join stalling program

I'm trying to build a basic program which has 2/3 threads. The main thread, signaler thread, and receiver thread. I'm trying to make it so the main thread starts both a signaler and receiver thread. The signaler thread is then supposed to send 10 SIGUSR1 signals to the receiver thread and increase a global counter. The receiver thread is supposed to receive the 10 signals while increasing its own counter for each signal received.
The trouble I'm having is with joining the threads back together at the end, specifically joining the receiver thread. The majority of the time the program stalls if I try to join them back together, I assume because the receiver thread hasn't finished its job (maybe it has missed signals?) and so it never finishes. I thought maybe this was the case, so I introduced a wait command in the signaler thread to slow it down, but that didn't change anything.
If I comment out the pthread_join(receiver, NULL) then the program runs, but it only catches one signal most of the time. I assume this is because the receiver thread isn't getting much time to run. (although sometimes it catches various amounts, depending on when it was preempted?)
Leaving the pthread_join(receiver, NULL) in the program makes it stall 19/20 times, but that 1/20 time the program returns 10 signals sent and 10 received. This leads me to believe it has something to do with preemption, but I don't understand why it would ever stall in the first place.
Also right now I just have the receiver thread receiving threads while the received counter is < 10. Ideally I would just want to leave it in while(1) loop, but then again I don't know how to join that back into the main thread without freezing everything.
If someone could help me figure out why signals are being missed / why the program is freezing I would be most grateful. I have a suspicion that I'm not setting up the signal mask correctly for the receiver, but I don't know how else I am supposed to do it. Here is the code:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <signal.h>
void *receivetest(void *args);
void *signaltest(void *args);
int received = 0;
int sent = 0;
pthread_t signaler;
pthread_t receiver;
sigset_t mask;
int main(){
//setting up the signal mask
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
//creation of both threads
pthread_create(&receiver, NULL, receivetest, NULL);
pthread_create(&signaler, NULL, signaltest, NULL);
pthread_join(signaler, NULL);
pthread_join(receiver, NULL);
//printing results after joining them back
printf("I'm the main function\n");
printf("Receieved: %d, sent: %d\n", received, sent);
}
void *signaltest(void *args){
int i = 0;
for(i=0;i<10;i++){ //sends 10 signals to receiver thread
pthread_kill(receiver, SIGUSR1);
sent++;
}
}
void *receivetest(void *args){
int c; //sigwait needs int
pthread_sigmask(SIG_BLOCK, &mask, NULL); //sets up the signal mask for this thread
while(received < 10){ //waits for 10 signals and increments receieved
sigwait(&mask, &c);
received++;
}
}
Signals just don't work that way. If a thread receives a signal while that same signal is already pending, nothing happens. Please use an appropriate inter-thread communication method, not signals.
You almost got it right. It is recommended to block the signals from the main thread to protect all new threads from mishandling the signal.
Any thread created after pthread_sigmask(), inherits the sigmask. The first parameter tells pthread_sigmask() what to do with the signals listed in the second parameter.
This line pthread_sigmask(SIG_BLOCK, &mask, NULL) should be placed in the main thread, not on the signal handler. Your original code will work too, but if the signal is sent to the wrong thread, it will kill the whole process.
In addition, the sender is sending signal faster than the receiver can handle. By sleeping for 1 second between each iteration, the receiver will be able to catch up.
I took the liberty and modified your code. I added some prints within the sender and receiver so you'll know what it is doing.
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void receivetest(void); //function does not return anything
void signaltest(void); // does not accept parameter either.
int received = 0;
int sent = 0;
pthread_t signaler;
pthread_t receiver;
sigset_t mask;
int main(){
//setting up the signal mask
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
pthread_sigmask(SIG_BLOCK, &mask, NULL); //sets up the signal mask for this thread
//creation of both threads
pthread_create(&receiver, NULL, (void *)receivetest, NULL);
pthread_create(&signaler, NULL, (void *)signaltest, NULL);
pthread_join(signaler, NULL);
pthread_join(receiver, NULL);
//printing results after joining them back
printf("I'm the main function\n");
printf("Receieved: %d, sent: %d\n", received, sent);
return 0;
}
void signaltest(void){
int i = 0;
for(i=0;i<10;i++){ //sends 10 signals to receiver thread
printf("Sending signal\n");
pthread_kill(receiver, SIGUSR1);
sleep(1); // Don't send signals too fast
sent++;
}
}
void receivetest(void){
int c; //sigwait needs int
while(received < 10){ //waits for 10 signals and increments receieved
sigwait(&mask, &c);
printf("Signal received\n");
received++;
}
}
Output:
Sending signal
Signal received
Sending signal
Signal received
Sending signal
Signal received
Sending signal
Signal received
Sending signal
Signal received
Sending signal
Signal received
Sending signal
Signal received
Sending signal
Signal received
Sending signal
Signal received
Sending signal
Signal received
I'm the main function
Receieved: 10, sent: 10

Using sigaction(), c

I was doing a little reading about sigaction() (sources are from my course notes) and I'm not sure I understand this text:
The signal mask is calculated and installed only for the duration of
the signal handler.
By default, the signal “sig” is also blocked when the signal occurs.
Once an action is installed for a specific signal using sigaction,
it remains installed until another action is explicitly requested.
Does this mean that the default signal mask is restored after returning form the signal handler?
Also, do I have to re-install the handler after using it, as if I was using signal()?
Also, there's this piece of code:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void termination_handler(int signum) {
exit(7);
}
int main (void) {
struct sigaction new_action,old_action;
new_action.sa_handler = termination_handler;
sigemptyset(&new_action.sa_mask);
sigaddset(&new_action.sa_mask, SIGTERM);
new_action.sa_flags = 0;
sigaction(SIGINT, NULL, &old_action);
if (old_action.sa_handler != SIG_IGN) {
sigaction(SIGINT,&new_action,NULL);
}
sleep(10);
return 0;
}
So - how exactly will SIGTERM be handled? I can see that the installed handler is termination handler(), but then SIGTERM was added to the signal mask with no use of sigprocmask(). What does this mean? Thanks!
P.s. one last question: why the if statement in main()?
Let's try to understand what's happening with a modified version of your code :
#include <signal.h>
#include <stdio.h>
void termination_handler(int signum)
{
printf("Hello from handler\n");
sleep(1);
}
int main (void)
{
//Structs that will describe the old action and the new action
//associated to the SIGINT signal (Ctrl+c from keyboard).
struct sigaction new_action, old_action;
//Set the handler in the new_action struct
new_action.sa_handler = termination_handler;
//Set to empty the sa_mask. It means that no signal is blocked
// while the handler run.
sigemptyset(&new_action.sa_mask);
//Block the SEGTERM signal.
// It means that while the handler run, the SIGTERM signal is ignored
sigaddset(&new_action.sa_mask, SIGTERM);
//Remove any flag from sa_flag. See documentation for flags allowed
new_action.sa_flags = 0;
//Read the old signal associated to SIGINT (keyboard, see signal(7))
sigaction(SIGINT, NULL, &old_action);
//If the old handler wasn't SIG_IGN (it's a handler that just
// "ignore" the signal)
if (old_action.sa_handler != SIG_IGN)
{
//Replace the signal handler of SIGINT with the one described by new_action
sigaction(SIGINT,&new_action,NULL);
}
while(1)
{
printf("In the loop\n");
sleep(100);
}
return 0;
}
So, if you compile it and launch it, and press Ctrl+C, then you'll have the handler message executed, and then you get back immediately out of the main's sleep. You can do it as many time as you want, and the handler message and the inloop message are still displayed.
So, you give a function, and sigaction does everything needed to hook the signal with your handler.
Now, what about sigterm? If you increase the sleep time in termination_handler, you can type something like "pkill --signal SIGTERM ./a.out" after pressing Ctrl+C. Then, what happens? Nothing! The SIGTERM signal is blocked while termination_handler is running. But once you are back in the main, now the SIGTERM will kill the application.
(Remember, while you are testing this code, you can still kill applications by sending a SIGKILL signal.)
If you want to know more, and have more fun with signals, you have the signal manual and the sigaction manual which tell a lot more. Notice that you also have the detailed description of the sigaction structure.

Non-blocking check for signals in a loop

I have a thread in an application that has a loop like this:
...
while (1)
{
checkDatabase();
checkChildren();
sleep(3);
}
...
checkDatabase() is self-explanatory; checkChildren() simply calls waitpid(-1, &status, WNOHANG) to deal with child processes that have either exited or received a signal.
The application works fairly well, but it has default signal handling. The problem is that this parent process has a number of threads (don't worry about child processes for now) and I don't have any experience with synchronous signals, let alone in a POSIX threads application. I have used signal() before but apparently it's non-portable and it doesn't do what I need anyway. I have no experience at all with sigaction methods, and I can't find good documentation on how to fill in the structs and so on.
What I need to do is to synchronously catch terminating signals like SIGINT, SIGTERM and SIGQUIT in the above loop (and I need to ignore SIGPIPE altogether so that I can catch the EPIPE error from IO methods), so it would look like this:
...
while (1)
{
checkDatabase();
checkChildren();
checkForSignals();
sleep(3);
}
...
All other threads should not have anything to do with the signal; only the thread that executes this loop should be aware of it. And, obviously, it needs to be a non-blocking check so the loop doesn't block during its first iteration. The method called if a signal is found will sort out the other threads and destroy mutexes, and all that.
Could anyone please give me a heads-up? Many thanks.
(Following the question's comments, and for completeness, this solution tries to avoid signal handlers.)
It is possible to block signals from being raised through sigprocmask() (or, rather, pthread_sigmask() since you're using threads). From there on, the signals that were raised but blocked are available through sigpending().
Therefore, you could do something like (error checking omitted for brevity):
sigset_t blocked;
sigemptyset(&blocked);
sigaddset(&blocked, SIGINT);
sigaddset(&blocked, SIGTERM);
sigaddset(&blocked, SIGQUIT);
pthread_sigmask(SIG_BLOCK, &blocked, NULL); // Block SIGINT, SIGTERM and SIGQUIT.
signal(SIGPIPE, SIG_IGN); // Ignore SIGPIPE.
Then, later:
void checkForSignals(void)
{
sigset_t pending;
sigpending(&pending);
if (sigismember(&pending, SIGINT)) {
// Handle SIGINT...
}
if (sigismember(&pending, SIGTERM)) {
// Handle SIGTERM...
}
if (sigismember(&pending, SIGQUIT)) {
// Handle SIGQUIT...
}
}
Since sigpending() does not block, this seems to match your requirements.
Create a signal handler for SIGINT, SIGTERM and SIGQUIT, using the same function. In that signal function just set a flag that can be polled in your loop.
Something like this:
/* Global variable, will be set to non-zero if SIGINT, SIGTERM or SIGQUIT is caught */
int term_signal_set = 0;
void my_signal_handler(int)
{
term_signal_set = 1;
}
/* ... */
signal(SIGINT, my_signal_handler);
signal(SIGTERM, my_signal_handler);
signal(SIGQUIT, my_signal_handler);
signal(SIGPIPE, SIG_IGN); /* So functions return EPIPE */
while (1)
{
/* ... */
if (term_signal_set > 0)
break; /* Or do something else */
sleep(3);
}
In a multithreaded application receiving a signal, there is no predetermination, which thread receives the signal. Typical workaraounds include setting a global variable in the signal handler and checking it from a dedicated thread.
So in your case the signal handler (called from whatever thread) would just set something like a global variable for the signal received, and in CheckForSignals() you would test it.
sigaction is the way to go. man sigaction should help you. Here is an example from the web
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
struct sigaction act;
void sighandler(int signum, siginfo_t *info, void *ptr)
{
printf("Received signal %d\n", signum);
printf("Signal originates from process %lu\n",
(unsigned long)info->si_pid);
}
int main()
{
printf("I am %lu\n", (unsigned long)getpid());
memset(&act, 0, sizeof(act));
act.sa_sigaction = sighandler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGTERM, &act, NULL);
// Waiting for CTRL+C...
sleep(100);
return 0;
}

Resources