How to cancel a pending signal? - linux

How can I cancel a sent signal to a process which is still not delivered?
Consider the scenario where I have sent a signal to a process, but the process was in un-interruptible state.
I am doing a conditional wait for the signal to be handled. But since it did not I want to continue executing further. In that case, is there a way that I can cancel the sent signal(it is not yet delivered)

A pending signal is canceled if that signal is ignored before the signal is delivered. You just have to ignore the signal. You can do this with sigaction() by setting the sa_handler field in struct sigaction to SIG_IGN.
Here's some example code that illustrates this and shows that it works. The code does the following:
Blocks SIGINT so that we have a window of time to send it SIGINT when it is blocked - this will generate a pending SIGINT that will be delivered when the process signal mask is changed to a mask that does not include SIGINT (and will be canceled if the signal is ignored).
Waits for you to send SIGINT
Ignores SIGINT after knowing that a SIGINT is pending. This has the effect of canceling the pending signal.
Restores the original default action for SIGINT, which is to terminate the process
Restores the original process signal mask, that does not block SIGINT.
Waits for you to enter any character to terminate.
You can see that the process does not terminate after step 5 and waits for your input, which means that the pending signal was canceled.
Here's the code that illustrates this:
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void) {
sigset_t block_sigint, prev_mask;
sigemptyset(&block_sigint);
sigaddset(&block_sigint, SIGINT);
if (sigprocmask(SIG_SETMASK, &block_sigint, &prev_mask) < 0) {
perror("Couldn't block SIGINT");
return 0;
}
printf("SIGINT blocked: kill -SIGINT %ld to generate a pending SIGINT. Press return when done.\n", (long) getpid());
/* Now, open a new terminal and send SIGINT to this process.
*
* After doing that, the signal will be pending delivery because it is currently blocked.
*
* Now, if we ignore SIGINT, the pending signal will be cancelled
*/
getchar();
struct sigaction ign_sigint, prev;
ign_sigint.sa_handler = SIG_IGN;
ign_sigint.sa_flags = 0;
sigemptyset(&ign_sigint.sa_mask);
if (sigaction(SIGINT, &ign_sigint, &prev) < 0) {
perror("Couldn't ignore SIGINT");
return 0;
}
printf("SIGINT ignored - pending SIGINT was canceled.\n");
/* Now, restore the default action for SIGINT */
if (sigaction(SIGINT, &prev, NULL) < 0) {
perror("Couldn't restore default SIGINT behavior");
return 0;
}
/* And also restore the process's original sigmask, which does not block SIGINT */
if (sigprocmask(SIG_SETMASK, &prev_mask, NULL) < 0) {
perror("Couldn't restore original process sigmask");
return 0;
}
printf("Process sigmask and action for SIGINT are now restored\n");
/* We will not receive SIGINT at this point because it was canceled
* So the process will block on getchar() here instead of terminating
*/
getchar();
return 0;
}

Related

Catching SIGUSR1 with sigtimedwait()

I'm not new to programming, but pretty new to Linux. I'm trying to use signals to asynchronously catch a push on a button, like this:
Run a worker thread which raises SIGUSR1 when the button is pushed.
Run a loop (main thread) around sigtimedwait() that will rotate info every two seconds (as long as the button is not pushed) or break (when the button is pushed).
According to the notes on sigtimedwait(), one should block the signals you want to wait for, then call sigtimedwait(). But I never see sigtimedwait() catching the blocked signals. I have run the code below in a few ways to see what happens with different scenarios:
Call to pthread_sigmask() disabled, call to signal() disabled,
result: programs exits with message "User defined signal 1".
Call to pthread_sigmask() disabled, call to signal() enabled, result:
message "Button 1 pressed sync1 hit" appears, sigtimedwait() always
returns EAGAIN.
Call to pthread_sigmask() enabled, call to signal() disabled, result:
message "Button 1 pressed" appears, sigtimedwait() always returns
EAGAIN.
Call to pthread_sigmask() enabled, call to signal() enabled, result
of course same as previous because the handler will not be called.
All as expected, except for the fact that sigtimedwait() doesn't seem to catch the signal when it's pending.
I've looked into similar code, e.g. this. But I don't understand how that particular code could work: SIGUSR1 isn't blocked, so raising that should immediately terminate the program (the default action for SIGUSR1).
It looks like I'm missing something here. What am I doing wrong? Or is the whole idea of using raise() in a worker thread wrong? I'm running this on a Raspberry Pi 3 with Raspbian Stretch (Debian 9.1), could there be a problem in that?
[I know printf() shouldn't be used in a signal handler, but for this purpose it works]
Any help appreciated, thx!
#include <stdio.h>
#include <stdlib.h>
#include <bcm2835.h>
#include <signal.h>
#include <pthread.h>
#include <errno.h>
#define PIN_BUTTON1 RPI_V2_GPIO_P1_22 // GPIO #24
// Thread function
void* check_button1(void* param)
{
while (true)
{
if (bcm2835_gpio_lev(PIN_BUTTON1) == HIGH)
{
printf("Button 1 pressed ");
raise(SIGUSR1);
}
delay(250);
}
}
// Signal handler, if applied
volatile sig_atomic_t usr_interrupt = 0;
void sync1(int sig)
{
printf("sync1 hit ... ");
usr_interrupt = 1;
}
int main(int argc, char** argv)
{
if (!bcm2835_init())
{
printf("Failed to initialize BCM2835 GPIO library.");
return 1;
}
bcm2835_gpio_fsel(PIN_BUTTON1, BCM2835_GPIO_FSEL_INPT);
sigset_t sigusr;
sigemptyset(&sigusr);
sigaddset(&sigusr, SIGUSR1);
pthread_sigmask(SIG_BLOCK, &sigusr, NULL);
signal(SIGUSR1, sync1);
// Start the threads to read the button pin state
pthread_t th1;
pthread_create(&th1, NULL, check_button1, NULL);
// Create a two second loop
struct timespec timeout = { 0 };
timeout.tv_sec = 2;
usr_interrupt = 0;
int nLoopCount = 0;
while (true)
{
printf("Loop %d, waiting %d seconds ... ", ++nLoopCount, timeout.tv_sec);
int nResult = sigtimedwait(&sigusr, NULL, &timeout);
if (nResult < 0)
{
switch (errno)
{
case EAGAIN: printf("EAGAIN "); break; // Time out, no signal raised, next loop
case EINTR: printf("EINTR "); break; // Interrupted by a signal other than SIGCHLD.
case EINVAL: printf("EINVAL "); exit(1); // Invalid timeout
default: printf("Result=%d Error=%d ", nResult, errno); break;
}
printf("\n");
continue;
}
printf("Signal %d caught\n", nResult);
}
return 0;
}
ADDENDUM: In the meantime, I got this working by replacing raise(SIGUSR1) by kill(getpid(), SIGUSR1). Strange, because according to the manual raise(x) is equivalent to kill(getpid, x) in single-threaded programs and to pthread_kill(pthread_self(), x) in multi-threaded ones. Replacing raise(SIGUSR1) by pthread_kill(pthread_self, SIGUSR1) has no effect. If anyone could explain this to me ...

Sometimes signal not received to signal handler

I'm sending say 100 signal (SIGINT) from terminal in while loop continuously, as i have register signal handler for this signal, so any signal comes either in auxiliary thread or main thread (as signal disposition is shared by all threads) should print "caught signal: ..." message as output every time i send the signal. but i observed out of 100, some random less no of times say 95, 84 times being this message printed. can some one please explain why all 100 signal is not being printed and how to print all 100 signal with below code.
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
// Signal Handler for entire Process (shared by all threads)
void signalHandler(int param)
{
printf("Caught signal: %d ", param);
}
void *childFun(void *arg)
{
// Register signal Handler here
struct sigaction childpsa;
childpsa.sa_handler = signalHandler;
sigaction(SIGTERM, &childpsa, NULL);
sigaction(SIGHUP, &childpsa, NULL);
sigaction(SIGINT, &childpsa, NULL);
sigaction(SIGCONT, &childpsa, NULL);
sigaction(SIGTSTP, &childpsa, NULL);
while (1) {
// doSomething in while loop
}
}
int main(void)
{
// create a auxiliary thread here
pthread_t child_threadId;
int err = pthread_create(&child_threadId, NULL, &childFun, NULL);
while (1) {
// main program do something
}
return 1;
}
Most of Linux implementation doesn't support queueing of signal, so if a process receives same signal 'x' number of times, it will be called only once.
Also you shouldn't use function like printf inside signal handler(instead use some counters(an integer) that increments for every invocation )
Except
POSIX.1 allows the system to deliver the signal either once or more than once in case a blocked signal is generated more than once before the process unblocks the signal. If the system delivers the signal more than once, we say that the signals are queued. Most UNIX systems, however, do not queue signals unless they support the real-time extensions to POSIX.1. Instead, the UNIX kernel simply delivers the signal once. [p336]
https://notes.shichao.io/apue/ch10/#reliable-signal-terminology-and-semantics

Signals in the "set" parameter of sigtimedwait() not getting delivered

I have been working on signal handling on Linux lately, and have read all the concepts related to signal handling. One question that's tinkering my head is that why the signal in the set of sigtimedwait() doesn't get delivered while the process is unblocked. My code is as follows :-
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
void sighandler1(int sig)
{
printf("SIGINT caught\n");
}
void sighandler2(int sig)
{
printf("SIGTSTP caught\n");
}
void sighandler3(int sig)
{
printf("SIGALRM caught\n");
}
int main()
{
sigset_t s1,s2;
struct sigaction act1,act2,act3;
int ret;
sigemptyset(&s1);// The bit-mask s1 is cleared
sigaddset(&s1,SIGINT);//Add SIGINT to the bit-mask s1
sigemptyset(&s2);// The bit-mask s2 is cleared
sigaddset(&s2,SIGALRM);//Add SIGALRM to the bit-mask s2
sigprocmask(SIG_BLOCK,&s2,NULL);//Signal(s) in s2 blocked
act1.sa_handler = sighandler1; //function pointer pointing to the signal handler
act1.sa_flags = 0;
sigaction(SIGINT,&act1,NULL); // installing the action
// for SIGINT
act2.sa_handler = sighandler2; //function pointer pointing to another signal handler
act2.sa_flags = 0; // no flags
sigaction(SIGTSTP,&act2,NULL); // installing the action
// for SIGTSTP
act3.sa_handler = sighandler3; //function pointer pointing to another signal handler
act3.sa_flags = 0; // no flags
sigaction(SIGALRM,&act3,NULL); // installing the action for SIGALRM
sigprocmask(SIG_SETMASK,&s1,NULL); //Signals in s1 blocked and other signals unblocked
printf("sigprocmask() called with SIG_SETMASK on s1,which contains SIGINT\n");
printf("Blocked on sigtimedwait() with s1\n");
if(sigtimedwait(&s1,NULL,NULL) < 0)
{
if(errno == EINTR)
printf("Some other signal caught\n");
}
printf("This is a process. You can pass signal to it\n");
while(1);
}
To be more clear with the question, I have called sigtimedwait in the above code with "s1" as the "set "parameter . This set contains only the signal SIGINT. As per the man page, sigtimedwait() blocks the process , until one of the signals in its set is delivered. I am all okay with that statement. But Why is the SIGINT handler not called when I pass SIGINT to unblock the process? On the other hand, when I pass SIGALRM or SIGTSTP, which are not there in the set, EINTR is returned as expected, and also the signal handler gets called.
For anyone, who wants to observe the scenario, can execute the above code and then pass SIGINT to it. They will observe that the process is unblocked without the handler being called . Why is the handler not called? Am I misinterpreting any part of the man page of sigtimedwait()??
sigtimedwait seems to return the signal value, instead of the signal handler getting caught:
switch(sigtimedwait(&s1,NULL,NULL))
{
default:
printf ("Some other signal???");
break;
case SIGINT:
printf ("We got SIGINT\n");
break;
case -1:
perror ("sigtimedwait");
break;
}

Linux: combing sleep() with signals

Do you know where I can see a list of signals and functions that cannot be used alongside sleep() command?
For example, you can see this code:
// this program presents how to block signal SIGINT
// while running in critical region
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
static void sig_int(int);
int main(void) //6
{
sigset_t newmask, oldmask, zeromask;
if (signal(SIGINT, sig_int) == SIG_ERR)
fprintf(stderr,"signal(SIGINT) error");
sigemptyset(&zeromask);
sigemptyset(&newmask);
sigaddset(&newmask, SIGINT);
/* block SIGINT and save current signal mask */
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
fprintf(stderr,"SIG_BLOCK error");
/* critical region of code */
printf("In critical region: SIGINT will be blocked for 3 sec.\n");
printf("Type Ctrl-C in first 3 secs and see what happens.\n");
printf("Then run this program again and type Ctrl-C when 3 secs elapsed.\n");
fflush(stdout);
sleep(3);
printf("end sleep");
/* allow all signals and pause */
if (sigsuspend(&zeromask) != -1)
fprintf(stderr,"sigsuspend error");
printf("after return from sigsuspend: ");
/* reset signal mask which unblocks SIGINT */
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
fprintf(stderr,"SIG_SETMASK error");
/* and continue processing ... */
exit(0);
}
static void sig_int(int signo)
{
printf("\nIn sig_int: SIGINT\n"); fflush(stdout);
return;
}
The program doesn't wake up after the sleep(). Do you know why?
If you use strace, you can see what your program actually does
strace ./my-sig-program
If sleep never returns, I guess that task has received a SIGSTOP (this one can not be intercepted) or SIGTSTP (this one you can intercept with a signal handler), causing the OS the halt the entire process, until a SIGCONT is received.

How to safely `waitpid()` in a plugin with `SIGCHLD` handler calling `wait()` setup in the main program

I am writing a module for a toolkit which need to execute some sub processes and read their output. However, the main program that uses the toolkit may also spawn some sub processes and set up a signal handler for SIGCHLD which calls wait(NULL) to get rid of zombie processes. As a result, if the subprocess I create exit inside my waitpid(), the child process is handled before the signal handler is called and therefore the wait() in the signal handler will wait for the next process to end (which could take for ever). This behavior is described in the man page of waitpid (See grantee 2) since the linux implementation doesn't seem to allow the wait() family to handle SIGCHLD. I have tried popen() and posix_spawn() and both of them have the same problem. I have also tried to use double fork() so that the direct child exist immediately but I still cannot garentee that waitpid() is called after SIGCHLD is recieved.
My question is, if other part of the program sets up a signal handler which calls wait() (maybe it should rather call waidpid but that is not sth I can control), is there a way to safely execute child processes without overwriting the SIGCHLD handler (since it might do sth useful in some programs) or any zombie processes.
A small program which shows the problem is here (Noted that the main program only exit after the long run child exit, instead of the short one which is what it is directly waiting for with waitpid()):
#include <signal.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
static void
signalHandler(int sig)
{
printf("%s: %d\n", __func__, sig);
int status;
int ret = waitpid(-1, &status, 0);
printf("%s, ret: %d, status: %d\n", __func__, ret, status);
}
int
main()
{
struct sigaction sig_act;
memset(&sig_act, 0, sizeof(sig_act));
sig_act.sa_handler = signalHandler;
sigaction(SIGCHLD, &sig_act, NULL);
if (!fork()) {
sleep(20);
printf("%s: long run child %d exit.\n", __func__, getpid());
_exit(0);
}
pid_t pid = fork();
if (!pid) {
sleep(4);
printf("%s: %d exit.\n", __func__, getpid());
_exit(0);
}
printf("%s: %d -> %d\n", __func__, getpid(), pid);
sleep(1);
printf("%s, start waiting for %d\n", __func__, pid);
int status;
int ret = waitpid(pid, &status, 0);
printf("%s, ret: %d, pid: %d, status: %d\n", __func__, ret, pid, status);
return 0;
}
If the process is single-threaded, you can block the CHLD signal temporarily (using sigprocmask), fork/waitpid, then unblock again.
Do not forget to unblock the signal in the forked child - although POSIX states the signal mask is undefined when a process starts, most existing programs expect it to be completely unset.

Resources