I am currently learning about signals in C and have a small program, that is supposed to not terminate upon receiving the SIGINT signal using sigaction().
The code written below however does terminate, even though it looks fine to me. Maybe I am something missing. Does someone see, why it still terminates?
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void handle() { printf("%s%d%s", "I won't die....: ", getpid(), "\n"); }
int main() {
struct sigaction sa;
sa.sa_handler = handle;
int k = 0;
sigaction(SIGINT, &sa, NULL);
// signal(SIGINT, handle); //signal works just fine
while (k < 60) {
printf("%s", "sleeping... \n");
sleep(1);
k = k + 1;
}
}
Additional info: My OS is Windows, however I compile and execute the program in the Bash of a Linux subsystem.
Open the manual page of sigaction() and understand all the members of struct sigaction and fill all the members of struct sigaction.
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
Here
struct sigaction sa;
As pointed by #AnttiHaapala here sa is a variable of automatic storage duration and you didn't initialize its members, it invokes undefined behavior as all the other fields contain garbage except sa.sa_handler
So you need to fill the other members of struct sigaction like
sa.sa_handler = handle;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
Or initialize sa itself like
struct sigaction sa = {0};
Also it's not a good practice to write printf() statement inside signal handler, read here How to avoid using printf in a signal handler? .
Related
I'm trying to use an exception handler to catch bad memory access but I'm not entirely sure how to go about doing it. I tried registering it with sigaction but my handler isn't triggering..
Old code
#include <stdio.h>
#include <signal.h>
void handler(int sig)
{
//exception should land here
printf("caught exception");
}
int main(int argc, const char * argv[]) {
struct sigaction act;
act.sa_flags = SA_SIGINFO;
sigemptyset(&act.sa_mask);
act.sa_handler = handler;
if(sigaction(SIGSEGV, &act, NULL)==-1){
printf("Could not register handler");
}else{
printf("handler registered\n");
}
*(int*)0 = 0;//throw exception
return 0;
}
And once inside the handler, how can I read the thread context registers?
I'm also on a MacOS so I'm unsure if there's any OS specific implementations.
Edit: New Code
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#define _XOPEN_SOURCE 600
#include <ucontext.h>
void handler(int sig, siginfo_t *info, void *uc)
{
(void) sig;
write (STDOUT_FILENO, "Caught exception\n", 17);
struct ucontext* mc = (struct ucontext*)uc;
}
int main(int argc, const char * argv[]) {
struct sigaction act;
act.sa_flags = SA_SIGINFO;
sigemptyset(&act.sa_mask);
act.sa_handler = handler;
if(sigaction(SIGSEGV, &act, NULL)==-1){
printf("Could not register handler");
}else{
printf("handler registered\n");
}
raise (SIGSEGV);
return 0;
}
When I included ucontext.h my compiler through this error
#else /* !_XOPEN_SOURCE */
#error The deprecated ucontext routines require _XOPEN_SOURCE to be defined
#endif /* _XOPEN_SOURCE */
Which I resolved by defining _XOPEN_SOURCE
But the compiler still doesn't know what ucontext is because I'm not getting any intellisence.. I might have to define the structure myself
Edit: Since I was on M1 I was compiling form ARM instead of x86_64 and ucontext and mcontext both have to use the 64bit variants..
Undefined behavior is not reliable:
Instead of relying on it, send a signal to the calling process with raise():
raise (SIGSEGV);
So the code becomes:
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
static void func (int signo, siginfo_t *info, void *context);
{
write (STDOUT_FILENO, "Caught exception\n", 17);
/* Restores the default handler. This is the only
* portable use of signal().
*/
signal (sig, SIG_DFL);
raise (sig);
}
int main (int argc, char *argv[])
{
struct sigaction act;
act.sa_flags = SA_SIGINFO;
/* Upon successful completion, sigemptyset() shall return 0;
* otherwise, it shall return -1 and set errno to indicate the error.
*/
if (sigemptyset (&act.sa_mask) == -1) {
perror ("sigemptyset()");
return EXIT_FAILURE;
}
act.sa_sigaction = handler;
if (sigaction (SIGSEGV, &act, NULL) == -1) {
fprintf (stderr, "Could not register handler\n");
} else {
fprintf (stderr, "handler registered\n");
}
raise (SIGSEGV);
return EXIT_SUCCESS;
}
Output:
handler registered
caught exception
Print error messages to stderr:
//printf ("Could not register handler\n");
fprintf (stderr, "Could not register handler.\n");
Do not call async-signal-unsafe functions in signal handlers:
Neither the C standard, nor the POSIX standard specifies printf() to be async-signal-safe, which means that it can not be safely called inside a signal handler.
Though,the POSIX standard does specify write() to be async-signal safe. So printf() should be replaced with it.
// printf ("Caught exception\n");
write (STDOUT_FILENO, "Caught exception\n", 17);
Incorrect declaration of handler():
void handler(int sig) is not correct if SA_SIGINFO is set: "If SA_SIGINFO is set and the signal is caught, the signal-catching function shall be entered as: void func(int signo, siginfo_t *info, void *context);" - #AndrewHenle
//void func (int signo);
void func(int signo, siginfo_t *info, void *context);
Assign handler() to the correct member:
The sigaction structure is defined as something like:
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
When you use the SA_SIGINFO flag, you need to assign the signal handling function to .sa_sigaction instead of .sa_handler.
// act.sa_handler = handler;
sig.sa_sigaction = handler;
Your first question appears to have been answered so I won't get further into it. As for your second issue, Apple has its own definition of mcontext and ucontext specifically mcontext64 and ucontex64. Additionally, You're compiling for ARM64 instead of x86_64, hence why those registers would no longer exist in your compiled binary.
Go into your Build Settings->Architectures
Remove the standard architecture and replace it with x86_64, your handler should then be able to access the registers.
This question already has answers here:
Meaning of "referencing" and "dereferencing" in C
(7 answers)
Closed 5 years ago.
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
void handler (int sig)
{
printf ("Got signal %d\n", sig);
}
int main (int argc, char *argv[])
{
struct sigaction act;
memset (&act, '\0', sizeof(act));
// Use the sa_sigaction field because
// the handler has two additional parameters
act.sa_handler = &handler;
if (sigaction(SIGHUP, &act, NULL) < 0) {
perror ("sigaction");
return EXIT_FAILURE;
}
if (sigaction(SIGTERM, &act, NULL) < 0) {
perror ("sigaction");
return EXIT_FAILURE;
}
while (1) sleep (10);
return EXIT_SUCCESS;
}
I am a bit confused about "&handler" . What does it mean here? I am new to signal and really hope someone can give me a hint on how it works. Any help would be appreciated. Thx
Man page of SIGACTION :
The sigaction structure is defined as something like:
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
[...]
`sa_handler` specifies the action to be associated with signum and may
be SIG_DFL for the default action, SIG_IGN to ignore this signal, or
a pointer to a signal handling function. This function receives the
signal number as its only argument.
[...]
Here, sa_handler is a pointer to a function and hold the address of handler() function.
As mentioned here,
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
The sa_handler is a function pointer that must point to a signal handling function.
sa_handler specifies the action to be associated with signum and may
be SIG_DFL for the default action, SIG_IGN to ignore this signal, or
a pointer to a signal handling function. This function receives the
signal number as its only argument.
I compiled the program. Starting it and waiting. I open the other terminal, and kill the any running program with command "kill pid" or "kill -15 pid" or "kill -SIGTERM pid" (replace PID with the actual process ID). The killed program is exit, but can't trap SIGTERM to print "done.".
I copy code here: https://airtower.wordpress.com/2010/06/16/catch-sigterm-exit-gracefully/.
Can I help you? I am appreciated all answers.
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
volatile sig_atomic_t done = 0;
void term(int signum)
{
done = 1;
}
int main(int argc, char *argv[])
{
struct sigaction action;
memset(&action, 0, sizeof(struct sigaction));
action.sa_handler = term;
sigaction(SIGTERM, &action, NULL);
int loop = 0;
while (!done)
{
int t = sleep(3);
/* sleep returns the number of seconds left if
* interrupted */
while (t > 0)
{
printf("Loop run was interrupted with %d "
"sec to go, finishing...\n", t);
t = sleep(t);
}
printf("Finished loop run %d.\n", loop++);
}
printf("done.\n");
return 0;
}
You need to setup your signal handler correctly in order to handle signals you want to catch. This is how I do my signal handler:
static void handle_signal(int signum); //in header, then implement
//in the source file
struct sigaction myaction;
myaction.sa_handler = handle_signal;
myaction.sa_flags = 0; //or whatever flags you want but do it here so the signals you register see these flags
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGTERM);
sigaction(SIGTERM, &myaction, NULL);
myaction.sa_mask = mask;
I am able to catch SIGTERM as well as all the other signals I register there (to sigaddset and sigaction).
I'm attempting to interrupt readline with signals (SIGUSR1), but obviously if the signal isn't handled, the program exits, when handling, it readline proceeds as though nothing has happened. Is readline supposed to be able to be interrupted using signals.
I got the idea from this other question: force exit from readline() function
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <readline/readline.h>
#include <pthread.h>
pthread_t main_id;
void *thread_main(void* arg)
{
sleep(10);
pthread_kill(main_id, SIGUSR1);
}
void signal_handler(int sig)
{
puts("got signal");
(void) sig;
}
int main(int argc, char** argv)
{
struct sigaction sa;
sa.sa_handler = signal_handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(SIGUSR1, &sa, NULL);
main_id = pthread_self();
pthread_t id;
pthread_create(&id, NULL, thread_main, NULL);
char *input = readline("prompt> ");
puts("main thread done");
return 0;
}
The output:
$ ./test
prompt> got signal
enter something
main thread done
$
Thanks.
Joachim Pileborg answered this question in a comment.
The best solution seems to be using the readline alternate interface. The docs are at http://www.delorie.com/gnu/docs/readline/rlman_41.html
Also an extremely basic example at http://www.mcld.co.uk/blog/blog.php?274 that just needs to be adapted to use select instead of polling with sleep.
Much better than using signals!
Change your signal_handler function:
void signal_handler(int sig)
{
puts("got signal");
//(void) sig;
exit(sig);
}
libreadline's default implementation of reading a character (int rl_getc(FILE *)) does handle EINTR (returned by read() if signalled) in a way of simply re-read()ing. Due to this receiving a signal does not cancel the readline().
To work around this you might set the function pointer rl_getc_function to your own implementation to read a character (rl_getc_function points to rl_getc() by default).
I have a program where i invoke a signal sigkill(getpid(), SIGUSR1). I wish when the signal comes, instead of the signal handler the thread function should be invoked, or both.
For this i have populated the sigev_notify with SIGEV_THREAD.
But unfortunately, the thread function is not called. Why is it so?
Here is the code below:
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
static void ThreadhandlerTimer1(int);
void sig_handlerTimer1(int);
static void threadFunction(union sigval sv) // Should be invoked on receipt of signal "SIGEV_THREAD"
{
printf("Thread function invoked");
}
int main()
{
int i;
static struct sigaction sa;
static struct sigevent sevp;
memset (&sevp, 0, sizeof (struct sigevent));
sevp.sigev_value.sival_ptr = NULL;
sevp.sigev_notify = SIGEV_THREAD;
sevp.sigev_notify_attributes = NULL;
sevp.sigev_signo = SIGUSR1;
sevp.sigev_notify_function=threadFunction;
/* Setting the signal handlers */
sa.sa_handler = sig_handlerTimer1;
sa.sa_flags = 0;
sigaction(SIGUSR1, &sa, NULL);
for(i=0; i<10; i++)
{
if((i==3) || (i==6)){
kill(getpid(), SIGUSR1);
}
printf("%d\n",i);
sleep(1);
}
}
void sig_handlerTimer1(int signum)
{
printf("Caught signal: %d\n",signum);
}
According to this documentation, the sigevent structure is only supported by "Some signal-generating functions, such as high-resolution timer expiration, asynchronous I/O completion, interprocess message arrival, and the sigqueue() function."
I don't know what your real plan for this code is (maybe you can tell us), but as it is, you are raising the signal directly which probably is not one of the supported cases for using SIGEV. If this code is fairly close to what you want in production you could simply call sigqueue() instead of kill() and it may just work.
From your code, it seems you have just assigned values to sigevent, instead of using any where in code.
static struct sigevent sevp;
memset (&sevp, 0, sizeof (struct sigevent));
sevp.sigev_value.sival_ptr = NULL;
sevp.sigev_notify = SIGEV_THREAD;
sevp.sigev_notify_attributes = NULL;
sevp.sigev_signo = SIGUSR1;
sevp.sigev_notify_function=threadFunction;
To invoke threadFunction, call this from your signal handler.
> void sig_handlerTimer1(int signum)
> {
> printf("Caught signal: %d\n",signum);
> threadFunction(signum);
> }
If you want to use sevp, use something like timer_create() and timer_settime().
Check this link:
http://ptgmedia.pearsoncmg.com/images/0201633922/sourcecode/sigev_thread.c