Capturing signals in C - c

I'm struggling to implement the ability to capture signals in a process using C language.
Can anyone help-me with a working example?
Thanks.

You'll need to use the signal.h library.
Here's a working example in which I capture SIGINT and print a message to STDOUT:
#include<stdio.h>
#include<signal.h>
void sig_handler(int signo)
{
if (signo == SIGINT)
write(0, "Hello\n", 6);
}
int main(void)
{
signal(SIGINT, sig_handler);
// Just to testing purposes
while(1)
sleep(1);
return 0;
}

Related

C Language - Fail to reset SIGINT to default

I am learning concepts of signals in the C language and met a problem when building a program for practices.
In the codes below, I am trying to reset SIGINT each time after the user press "ctrl-c" and to record how many times the user press "ctrl-c".
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<setjmp.h>
void handler(int signo);
jmp_buf buf;
int int_counting = 1;
void handler(int signo)
{
signal(SIGINT, SIG_DFL);
int_counting++;
// just tried to add another "signal(SIGINT, handler)" here
// but saw the same result
longjmp(buf, 1);
}
int main()
{
if ((signal(SIGINT, handler) == SIG_ERR))
{
printf("Fail to catch the signal\n");
}
if (!setjmp(buf))
{
printf("Waiting for any signals ... \n");
}
else
{
if (!setjmp(buf)){} // to reset "setjmp" to zero
printf("Pressed 'ctrl-c' for %d times\n", int_counting);
printf("Waiting for another signal\n");
signal(SIGINT, handler);
}
while (int_counting <= 5)
{
sleep(1);
printf("Processing ...\n");
}
}
However, after the first signal no other signals can be sent to handler and the output looks like:
Could you anyone explains the reason?
Below are examples where it seems like the signal will not be masked.
// Examples for SIGALRM
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
int counting = 0;
void handler(int signo)
{
printf("%d\n", counting);
while (counting < 5)
{
signal(SIGALRM, handler);
printf("%d\n", beeps);
counting++
alarm(1);
}
}
void main(void)
{
if (signal(SIGALRM, handler) == SIG_ERR)
{
printf("cannot catch SIGALRM\n");
}
alarm(1);
while (counting < 5)
{
pause();
}
return;
}
// Example for SIGQUIT
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<setjmp.h>
jump_buf buf;
void handler(int signo)
{
signal(SIQQUIT, handler);
longjmp(buf, 1);
}
int main()
{
signal(SIQQUIT, handler);
if (!setjmp(buf))
{
printf("begin ...\n");
}
else
{
print("restart ...\n");
}
while (1)
{
sleep(1);
printf("waiting for sinals ...\n");
}
}
Although my original question is answered but if any further explanation about
why those signals will not be masked (or please tell me that is how they work in C), it would be greatly helpful.
You need to save the signal mask and therefore use siglongjmp() and sigsetjmp().
This works as expected:
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<setjmp.h>
void handler(int signo);
sigjmp_buf buf;
int int_counting = 0;
void handler(int signo)
{
signal(SIGINT, SIG_DFL);
int_counting++;
// just tried to add another "signal(SIGINT, handler)" here
// but saw the same result
siglongjmp(buf, 1); // 1: "fake" return value
}
int main()
{
if ((signal(SIGINT, handler) == SIG_ERR))
{
printf("Fail to catch the signal\n");
}
if (!sigsetjmp(buf, 1)) // 1 (or any non-zero value): save sigmask
{
printf("Waiting for any signals ... \n");
}
else // this code is executed when the "fake" return value of sigsetjmp is non-zero
{
printf("Pressed 'ctrl-c' for %d times\n", int_counting);
printf("Waiting for another signal\n");
signal(SIGINT, handler);
}
while (int_counting <= 5)
{
sleep(1);
printf("Processing ...\n");
}
}
It is described in the man-page e.g. man setjmp:
sigsetjmp() and siglongjmp() also perform nonlocal gotos, but provide predictable handling of the process signal mask
SIGINT is masked (blocked) during execution of your signal handler, and remains masked when you longjmp out of it. That's why you don't see subsequent SIGINTs — the signal mask prevents their delivery.
The fix is three-fold.
First, use sigsetjmp(buf, 1) to save the calling mask and siglongjmp make the jump. That will restore the signal mask to its expected value at that point in execution.
Second, use sigaction rather than signal. sigaction will force you to explicitly choose behavior like masking out signals during handler execution. signal, on the other hand, does not mandate consistent behavior across platforms.
Third, don't use (sig)longjmp at all. You got into this trouble because you are issuing a non-local goto from within asynchronously executed user code (your handler). It's very easy to make mistakes when reasoning about that kind of code.

Using signals in a child process

I want to create a simple program that uses fork and creates a child process which with the use of pause is waiting. I want this child process to start after it gets a specific signal from father process. Code I've written:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main() {
pid_t c = fork();
if (c == 0) {
pause();
printf("signal was given");
}
if (c > 0)
kill(c, SIGINT);
return 0;
}
I think kill gives a specific signal to a process with pid c(child) and I thought that pause just waits for a signal to unpause that process. However in this case running this program has no results. I have also tried adding a signal catching function to the child using signal(SIGINT, handler) and creating a handler function that prints the desired result but it is still not working. Any ideas?
If you send SIGINT, whose default disposition is to kill the process, to a process that neither blocks it nor handles it, the process will die.
If you want the signal to interrupt blocking calls like pause(), it needs to have a handler.
But simply installing a handler introduces race conditions:
if (c == 0 ){
//< if the signal arrives here the child dies
signal(SIGINT, handler);
//< if the signal arrives here then nothing happens except the handler is run
pause(); //< if the handler arrives here then pause gets interrupted
printf("signal was given\n");
exit(0);
}
To eliminate the race conditions, you need to
block the signal in the parent so that the child starts with the signal blocked
install the handler in the child
unblock the signal and pause() in one atomic step
To achieve 3. in one step, you need sigsuspend() instead of pause().
#include <stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<signal.h>
void handler(int Sig){}
int main()
{
sigset_t sigint, oldmask; sigemptyset(&sigint); sigaddset(&sigint, SIGINT);
sigprocmask(SIG_BLOCK, &sigint, &oldmask);
pid_t c=fork();
if(0>c) return perror(0),1;
if (c==0){
signal(SIGINT, handler);
sigdelset(&oldmask,SIGINT); /*in (the unlikely) case the process started with SIGINT blocked*/
sigsuspend(&oldmask);
printf("signal was given\n");
exit(0);
}
kill(c,SIGINT);
wait(0);
return 0;
}
Alternatively, you can use sigwait() and drop the need for a handler altogether:
#include <stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<signal.h>
int main()
{
sigset_t sigint, oldmask; sigemptyset(&sigint); sigaddset(&sigint, SIGINT);
sigprocmask(SIG_BLOCK, &sigint, &oldmask);
pid_t c=fork();
if(0>c) return perror(0),1;
if (c==0){
int sig; sigwait(&sigint,&sig);
printf("signal was given\n");
exit(0);
}
kill(c,SIGINT);
wait(0);
return 0;
}
You have two issues:
The child process is getting a signal before it calls pause().
SIGINT by default would kill a process so printf will never be executed.
Try this:
void handler(int signum)
{
//nothing here
}
int main()
{
pid_t c = fork();
if (c == 0) {
signal(SIGINT, handler);
pause();
printf("signal was given");
}
if (c > 0) {
sleep(1); // <-- give the child process some time to pause()
kill(c, SIGINT);
}
return 0;
}

My signal handler sample is not working

1.I have written a piece of sample code and that will catch SIGALRM signal exit the main process.
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<sys/signal.h>
static int sig_flag=0;
static void mysignal(int sig)
{
sig_flag=1;
}
void installsignal(int sig,void( *signalhandler)(int))
{
struct sigaction action={0};
action.sa_handler=signalhandler;
action.sa_flags=0;
sigemptyset(&action.sa_mask);
if(sigaction(sig,&action,NULL)<0)
{
printf("can not catch signal signum:%d\n",sig);
}
}
main()
{
installsignal(SIGALRM,mysignal);
if(sig_flag==1)
{
printf("\n Signal has been caought\n");
exit(0);
}
while(1)
{
printf("\nHello world\n");
sleep(1);
}
exit(0);
}
2.When i have changed my program as given below then its works:-
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<sys/signal.h>
static int sig_flag=0;
static void mysignal(int sig)
{
sig_flag=1;
}
void installsignal(int sig,void( *signalhandler)(int))
{
struct sigaction action={0};
action.sa_handler=signalhandler;
action.sa_flags=0;
sigemptyset(&action.sa_mask);
if(sigaction(sig,&action,NULL)<0)
{
printf("can not catch signal signum:%d\n",sig);
}
}
main()
{
installsignal(SIGALRM,mysignal);
while(1)
{
printf("\nHello world\n");
if(sig_flag==1)
{
printf("\n Signal has been caought\n");
exit(0);
}
sleep(1);
}
exit(0);
}
Why first one sample code is not working
You got the control loop wrong. You never ask for the flag inside, so how would you want that you notice that the signal has been caught.
Besides that Basile is correct, only use sig_atomic_t for communication with a signal handler.
You should define
static volatile sig_atomic_t sig_flag=0;
Read about volatile variables and sig_atomic_t and read carefully signal(7)
Of course you need to test sig_flag inside your loop or use pause(2) or sigsuspend(2). Maybe you want an event loop using poll(2). Read Advanced Linux Programming, time(7), and about the Linux specific signalfd(2) and timerfd_create(2)....

How to send a signal to a process in C?

I need to send a signal to a process and when the process receives this signal it does some things, how is this best achieved in C?
The way to send a signal to a process is kill(pid, signal); However, you should be aware that signals are not a robust means of inter-process communication except for parent-to-direct-child messages due to inherent race conditions. Pipes, files, directories, named semaphores, sockets, shared memory, etc. all provide greatly superior approaches to inter-process communication.
If you happen to be on one of the Unix variants, the following man pages will help:
man 2 kill
man 2 signal
man 2 sigvec
kill + fork runnable POSIX example
Time for some fun:
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <signal.h>
#include <stdbool.h> /* false */
#include <stdio.h> /* perror */
#include <stdlib.h> /* EXIT_SUCCESS, EXIT_FAILURE */
#include <sys/wait.h> /* wait, sleep */
#include <unistd.h> /* fork, write */
void signal_handler(int sig) {
char s1[] = "SIGUSR1\n";
char s2[] = "SIGUSR2\n";
if (sig == SIGUSR1) {
write(STDOUT_FILENO, s1, sizeof(s1));
} else if (sig == SIGUSR2) {
write(STDOUT_FILENO, s2, sizeof(s2));
}
signal(sig, signal_handler);
}
int main() {
pid_t pid;
signal(SIGUSR1, signal_handler);
signal(SIGUSR2, signal_handler);
pid = fork();
if (pid == -1) {
perror("fork");
assert(false);
} else {
if (pid == 0) {
while (1);
exit(EXIT_SUCCESS);
}
while (1) {
kill(pid, SIGUSR1);
sleep(1);
kill(pid, SIGUSR2);
sleep(1);
}
}
return EXIT_SUCCESS;
}
Compile and run:
gcc -std=c99 signal_fork.c
./a.out
Outcome:
SIGUSR1
SIGUSR2
SIGUSR1
SIGUSR2
....
But beware that there are many complexities when dealing with signals:
only certain functions can be called from signal handlers: Why only async-signal-safe functions can be called from signal handlers safely?
different functions have different behaviors when interrupted by signals: man 7 signal, SA_RESTART
global variables can only be accessed from the signal handler if they have type sig_atomic_t: How does sig_atomic_t actually work?
Tested in Ubuntu 17.10, GitHub upstream.

how to intercept linux signals ? (in C)

I need to intercept and trace signals from any binaries, like strace does it under linux.
I don't need a so verbose output like the real one strace.
I just want to know how it works, how can I intercept signal and how can I trace them.
Thanks in advance :)
strace uses the ptrace() system call for tracing, which also allows you to intercept (and possibly manipulate) signals sent to the process.
Here's a tiny example:
#include <sys/ptrace.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
/* simple example, child is traced, uses alarm which causes a signal to be
* set up */
pid_t child;
child = fork();
if (child == 0)
{
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
alarm(3);
while(1)
{
}
exit(0);
}
/* parent */
while(1)
{
int wstatus;
int signum;
wait(&wstatus);
if (WIFEXITED(wstatus) || WIFSIGNALED(wstatus))
break;
signum = WSTOPSIG(wstatus);
printf("child stopped with signal %d\n", signum);
/* resume execution */
ptrace(PTRACE_CONT, child, NULL, signum);
}
return 0;
}
This is a simple implementation:
Put somewhere in your int main() several calls to signal(), one for each signal you want to catch. The first argument is the signal name; the second is the signal handler function (more on that below):
signal(SIGFPE, SignalHandler);
signal(SIGILL, SignalHandler);
signal(SIGINT, SignalHandler);
signal(SIGSEGV, SignalHandler);
signal(SIGTERM, SignalHandler);
#ifndef WIN32
signal(SIGHUP, SignalHandler);
signal(SIGQUIT, SignalHandler);
signal(SIGKILL, SignalHandler);
signal(SIGPIPE, SignalHandler);
signal(SIGCHLD, SignalHandler);
#endif
Now, write a signal function. It must return void and accept an int: void SignalHandler(int signal_number):
void SignalHandler(int signal_number)
{
printf("Received signal: %s\n", strsignal(signal_number);
// Do something
}
That's it! You can also test it by sending a signal to yourself with the function raise(SIGNAL_NAME); for example, try raise(SIGTERM);!
Intercepting signals to other processes is something you should not do for any reason other than debugging them. This is what strace is intended for. Processes should be capable of handling their own signals.
Needless to say, if you are writing a debugger, understand ptrace().

Resources