Ignoring a signal on C - c

This is not a question about code but theory.
I had this code before:
void handler(int signal){
printf("Handler called\n");
}
struct sigaction act;
act.sa_handler = &handler;
act.sa_flags = 0;
if (sigaction(SIGINT, &act, NULL) < 0){
exit(1);
}
if (sigaction(SIGQUIT, &act, NULL) < 0){
exit(1);
}
When I was pressing ctrl + C, the handler was being called, but the program still exits.
And now, by curiosity, I tried changing the flags to SA_RESTART. Now it does not exit.
Why?

SA_RESTART causes EINTR-interruptible system calls (e.g., read) to be restarted after a handled signal is delivered.
So if you had:
#include <unistd.h>
#include <signal.h>
#include <errno.h>
void handler(int signal)
{
printf("Handler called\n");
}
int main()
{
struct sigaction act;
act.sa_handler = &handler;
act.sa_flags = 0;
if (sigaction(SIGINT, &act, NULL) < 0){ exit(1); }
char ch; ssize_t nr;
nr=read(0,&ch,1);
}
then with SA_RESTART it'll be as if the nr=read(0,&ch,1); line had been replaced with
while(0>(nr=read(0,&ch,1)) && errno==EINTR){}`
Note that you should be calling only async-signal-safe functions in signal handlers. printf in a handler is normally a recipe for undefined behavior, though in this particular case where you're only interrupting code that is async-signal-safe itself (read), it is fairly safe, though the POSIX doesn't explicitly sanction it (AFAIK).

Related

Restarting process when receiving a signal with sigaction

I'm trying to make my process restart when it receives SIGUSR1.
Since SIGINT is easier to test, I'm using it instead.
Here's the code:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void sig_handler(int signo){
if (signo == SIGINT){
char *args[] = { "./a", NULL };
write(1, "Restarting...\n", 14);
execv(args[0], args);
}
}
int main(void) {
printf("Starting...\n");
struct sigaction saStruct;
sigemptyset(&saStruct.sa_mask);
sigaddset(&saStruct.sa_mask, SIGINT);
saStruct.sa_flags = SA_NODEFER;
saStruct.sa_handler = sig_handler;
sigaction(SIGINT, &saStruct, NULL);
while (1)
sleep(1);
}
Unfortunately, this only works for the first time the signal is received. After that, it does nothing. I thought that the SA_NODEFER flag should make this work the way I wanted to, but it doesn't.
Also, when I try with SIGUSR1, it simply terminates the process.
The problem is here:
sigaddset(&saStruct.sa_mask, SIGINT);
The way NODEFER affects signals is:
If NODEFER is set, other signals in sa_mask are still blocked.
If NODEFER is set and the signal is in sa_mask, then the signal is
still blocked.
On the other hand (from Signals don't re-enable properly across execv()):
When using signal() to register a signal handler, that signal number
is blocked until the signal handler returns - in effect the kernel /
libc blocks that signal number when the signal handler is invoked, and
unblocks it after the signal handler returns. As you never return from
the signal handler (instead you execl a new binary), SIGUSR1 stays
blocked and so isn't caught the 2nd time.
Just remove the line:
sigaddset(&saStruct.sa_mask, SIGINT);
and you are done.
#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void sighandler(int signo)
{
if (signo == SIGUSR1)
{
char *args[] = {"./demo", NULL};
char str[] = "Restarting...\n";
write(1, str, sizeof(str) - 1);
execv(args[0], args);
}
}
int main(void)
{
printf("Starting...\n");
struct sigaction act;
act.sa_handler = sighandler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_NODEFER;
sigaction(SIGUSR1, &act, 0);
while (1)
{
sleep(1);
}
}

pthread_sigmask() not work in multithreaded program

I'm a newbie in c development. Recently, I noticed a problem when I was learning multi-threaded development, when I set a signal in the main thread of Action and when I try to block the signal action set by the main thread in the child thread, I find that it does not work.
Here is a brief description of the code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
void *thread_start(void *_arg) {
sleep(2);
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGUSR2);
pthread_sigmask(SIG_BLOCK, &mask, NULL);
printf("child-thread executed\n");
while (true) {
sleep(1);
}
return NULL;
}
void sig_handler(int _sig) {
printf("executed\n");
}
int main(int argc, char *argv[]) {
pthread_t t_id;
int s = pthread_create(&t_id, NULL, thread_start, NULL);
if (s != 0) {
char *msg = strerror(s);
printf("%s\n", msg);
}
printf("main-thread executed, create [%lu]\n", t_id);
signal(SIGUSR2, sig_handler);
while (true) {
sleep(1);
}
return EXIT_SUCCESS;
}
The signal mask is a per-thread property, a thread will inherit whatever the parent has at time of thread creation but, after that, it controls its own copy.
In other words, blocking a signal in a thread only affects the delivery of signals for that thread, not for any other.
In any case, even if it were shared (it's not), you would have a potential race condition since you start the child thread before setting up the signal in the main thread. Hence it would be indeterminate as to whether the order was "parent sets up signal, then child blocks" or vice versa. But, as stated, that's irrelevant due to the thread-specific nature of the signal mask.
If you want a thread to control the signal mask of another thread, you will need to use some form of inter-thread communication to let the other thread do it itself.
As I wrote in a comment, any USR1 signal sent to the process will be delivered using the main thread. It's output will not tell you exactly what happened, so it is not really a good way to test threads and signal masks. Additionally, it uses printf() in a signal handler, which may or may not work: printf() is not an async-signal safe function, so it must not be used in a signal handler.
Here is a better example:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <limits.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
/* This function writes a message directly to standard error,
without using the stderr stream. This is async-signal safe.
Returns 0 if success, errno error code if an error occurs.
errno is kept unchanged. */
static int write_stderr(const char *msg)
{
const char *end = msg;
const int saved_errno = errno;
int retval = 0;
ssize_t n;
/* If msg is non-NULL, find the string-terminating '\0'. */
if (msg)
while (*end)
end++;
/* Write the message to standard error. */
while (msg < end) {
n = write(STDERR_FILENO, msg, (size_t)(end - msg));
if (n > 0) {
msg += n;
} else
if (n != 0) {
/* Bug, should not occur */
retval = EIO;
break;
} else
if (errno != EINTR) {
retval = errno;
break;
}
}
/* Paranoid check that exactly the message was written */
if (!retval)
if (msg != end)
retval = EIO;
errno = saved_errno;
return retval;
}
static volatile sig_atomic_t done = 0;
pthread_t main_thread;
pthread_t other_thread;
static void signal_handler(int signum)
{
const pthread_t id = pthread_self();
const char *thread = (id == main_thread) ? "Main thread" :
(id == other_thread) ? "Other thread" : "Unknown thread";
const char *event = (signum == SIGHUP) ? "HUP" :
(signum == SIGUSR1) ? "USR1" :
(signum == SIGINT) ? "INT" :
(signum == SIGTERM) ? "TERM" : "Unknown signal";
if (signum == SIGTERM || signum == SIGINT)
done = 1;
write_stderr(thread);
write_stderr(": ");
write_stderr(event);
write_stderr(".\n");
}
static int install_handler(int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = signal_handler;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return -1;
return 0;
}
void *other(void *unused __attribute__((unused)))
{
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGHUP);
pthread_sigmask(SIG_BLOCK, &mask, NULL);
while (!done)
sleep(1);
return NULL;
}
int main(void)
{
pthread_attr_t attrs;
sigset_t mask;
int result;
main_thread = pthread_self();
other_thread = pthread_self(); /* Just to initialize it to a sane value */
/* Install HUP, USR1, INT, and TERM signal handlers. */
if (install_handler(SIGHUP) ||
install_handler(SIGUSR1) ||
install_handler(SIGINT) ||
install_handler(SIGTERM)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* Create the other thread. */
pthread_attr_init(&attrs);
pthread_attr_setstacksize(&attrs, 2*PTHREAD_STACK_MIN);
result = pthread_create(&other_thread, &attrs, other, NULL);
pthread_attr_destroy(&attrs);
if (result) {
fprintf(stderr, "Cannot create a thread: %s.\n", strerror(result));
return EXIT_FAILURE;
}
/* This thread blocks SIGUSR1. */
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
pthread_sigmask(SIG_BLOCK, &mask, NULL);
/* Ready to handle signals. */
printf("Send a HUP, USR1, or TERM signal to process %d.\n", (int)getpid());
fflush(stdout);
while (!done)
sleep(1);
pthread_join(other_thread, NULL);
return EXIT_SUCCESS;
}
Save it as e.g. example.c, and compile and run using
gcc -Wall -O2 example.c -pthread -o exprog
./exprog
It will block the USR1 signal in the main thread, and HUP and TERM in the other thread. It will also catch the INT signal (Ctrl+C), which is not blocked in either thread. When you send it the INT or TERM signal, the program will exit.
If you send the program the USR1 signal, you'll see that it will always be delivered using the other thread.
If you send the program a HUP signal, you'll see that it will always be delivered using the main thread.
If you send the program a TERM signal, it too will be delivered using the main thread, but it will also cause the program to exit (nicely).
If you send the program an INT signal, it will be delivered using one of the threads. It depends on several factors whether you'll always see it being delivered using the same thread or not, but at least in theory, it can be delivered using either thread. This signal too will cause the program to exit (nicely).

Can not receive all the SIGCHLD

In the following code, what I am expecting is the console prints ten SIGCHLD caught. I've already queued up the SIGCHLD by setting sa_flags to SA_SIGINFO and using sa_sigaction instead of sa_handler. However, it seems some of the SIGCHLD are lost. Why?
I'm thinking fork() might be interrupted by SIGCHLD so I use SA_RESTART to restart the fork(). I run the same piece of code on different computers. On my MacBook, it says [1] 24481 illegal hardware instruction. On the other Linux computer, less than 10 SIGCHLD caught are printed.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#define CHECK(syscall, msg) do { \
if ((syscall) == -1) { \
perror(msg); \
} \
} while(0)
void catch(int signo, siginfo_t *info, void *context) {
if (signo == SIGCHLD) {
printf("SIGCHLD caught\n");
}
}
int main () {
sigset_t new_set;
sigemptyset(&new_set);
sigaddset(&new_set, SIGCHLD);
struct sigaction act;
act.sa_sigaction = catch;
act.sa_mask = new_set;
act.sa_flags = SA_SIGINFO | SA_RESTART;
CHECK(sigaction(SIGCHLD, &act, NULL), "sigaction error");
int pid, i;
for (i = 0; i < 10; i++) {
pid = fork();
if (!pid) return;
}
while (1);
}
SIGCHLD is a standard signal, which means multiple occurrences of it get collapsed into one. Linux kernel maintains a bitset for standard signals, one bit per signal and supports queuing exactly one associated siginfo_t.
Fix:
void catch(int signo, siginfo_t*, void*) {
int status;
pid_t pid;
if(signo == SIGCHLD) {
while((pid = waitpid(-1, &status, WNOHANG)) > 0)
printf("child %u terminated.\n", (unsigned)pid);
}
}
Also note, that you do not need to explicitly block the signal you handle because it is automatically blocked for you, unless SA_NODEFER flag is used.
And, pedantically, only a limited number of async-signal safe functions (see man signal-safety) can be used in a signal handler, printf is not one of those.

catching all signals in linux

I'm trying to write a process in C/linux that ignores the SIGINT and SIGQUIT signals and exits for the SIGTERM. For the other signals it should write out the signal and the time. I'm having trouble cathing all the signals because i'm familiar only with catching 1 signal. If anyone could help me with this I'd appreciate it very much. Here is my code:
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
int done = 0;
void term(int signum)
{
if (signum == 15)
{
//printf("%d\n",signum);
printf("Received SIGTERM, exiting ... \n");
done = 1;
}
else
{
time_t mytime = time(0);
printf("%d: %s\n", signum, asctime(localtime(&mytime)));
printf("%d\n",signum);
}
}
int main(int argc, char *argv[])
{
struct sigaction action;
memset(&action, 0, sizeof(struct sigaction));
action.sa_handler = term;
sigaction(SIGTERM, &action, NULL);
struct sigaction act;
memset(&act, 0, sizeof(struct sigaction));
act.sa_handler = SIG_IGN;
sigaction(SIGQUIT, &act, NULL);
sigaction(SIGINT, &act, NULL);
int loop = 0;
while(!done)
{
sleep(1);
}
printf("done.\n");
return 0;
}
Here is the easy way
void sig_handler(int signo)
{
if (signo == SIGINT)
printf("received SIGINT\n");
}
int main(void)
{
if (signal(SIGINT, sig_handler) == SIG_ERR)
and so on.
signal() and sighandler() is the least complicated way to do this.
Call signal for each signal that you want to catch. But as some have said earlier you can only catch certain signals. Best to have a way to gracefully shut the program down.

Is it possible to use both custom sigaction signal handler and pthread_sigmask?

I am implementing a simple timer that throws a RT signal upon expiration. What I want to do is to register a signal handler (using sigaction) that gets called when the signal occurs. Meanwhile the main code waits until the signal is called using sigwaitinfo.
Implementing either a signal handler or sigwaitinfo exclusively works fine. However when both are used, the signal handler is never called. I tried switching the order; i.e. registering the handler before blocking the signal. Makes no difference.
Here is the code
// gcc -Wall -o sigwait_example sigwait_example.c -lrt
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <errno.h>
#include <string.h>
#define install_handler(sig,sa) if( sigaction(sig, &sa, NULL) == -1 ){ \
perror("sigaction"); }
#define SIG SIGRTMIN+1
volatile int flag=0;
void handler(int signum){
flag++;
}
int main(void){
struct itimerspec its;
sigset_t blocked;
siginfo_t si;
timer_t timerid;
struct sigevent evt;
struct sigaction sa;
evt.sigev_notify = SIGEV_SIGNAL;
evt.sigev_signo = SIG;
evt.sigev_value.sival_ptr = &timerid;
if ( timer_create(CLOCK_REALTIME, &evt, &timerid) ){
perror("timer_create");
}
//setup timer
its.it_value.tv_sec = 0;
its.it_value.tv_nsec = 0.1*1E9;
its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
//arm the timer
if ( timer_settime(timerid, 0, &its, NULL) )
perror("timer_settime");
sigemptyset(&blocked);
sigaddset(&blocked, SIG);
//add SIG to blocked signals
pthread_sigmask(SIG_BLOCK, &blocked, NULL);
sa.sa_flags = SA_SIGINFO; //use this flag to set custom handler
sa.sa_sigaction = handler;
sigemptyset(&sa.sa_mask);
install_handler(SIG,sa);
while ( sigwaitinfo(&blocked, &si) == -1 && errno == EINTR );
printf("received signal: %s, flag=%d\n",strsignal(si.si_signo),flag);
//while(flag==0) sleep(1); //use this when only signal handler is used
timer_delete(timerid);
return 0;
}
I am doing this mostly for educational purposes, since I need to learn as much as possible about how threads are sent/blocked as I will be using them in threads.
It's not possible because sigwaitinfo() removes the signal from the queue.
You can, however, use sigaction(SIG, NULL, &sa) to retrieve the sigaction struct of this signal and execute the handler.

Resources