I am trying to set up a counter for my programme to count how many times a signal was sent. I am trying to achieve the program to exit after ctrl +c was pressed twice. I have most of the code but just don't know how to link the counter to the if section. Here is my code.
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
// user-defined signal handler for alarm.
int cnt=0;
void my_handler(int signo)
{
while ( cnt < 2){
if (signo == SIGINT)
{
printf("Press ctrl c to stop\n");
exit(0);
}
}
}
int main(void)
{
signal(SIGINT,my_handler);
while(1){
printf(" I am running into infinite loop.., stop me if you dear..\n");
sleep(1); /* wait until alarm goes off */
}
} /* main */
I tried out the above code and it seems that the counter will stay at 0 forever and the programme exit immediately as soon as ctrl+c was pressed.
You can't do much safely in a signal handler, and definitely no I/O. For maximum portability, there are really only a handful of things a signal handler can do to affect global state:
Assign to a volatile sig_atomic_t variable
Make calls to signal
abort, exit, etc. on error
You can't do I/O safely. The safe thing to do is set a flag that the main thread can check and do your printing for you. If printing isn't necessary, the first call to your handler could just unregister itself, restoring the default behavior with SIG_DFL (or registering a new "second Ctrl-C" handler if you need to do something special) so the second Ctrl-C kills as normal.
Replace the while statement with an if one. If cnt is lower than 2, then print your message and count by using cnt++; else do something else
Signals are a little more involved to get right.
You should either use sigaction or a custom sigaction wrapper as signal doesn't have clearly defined semantics. Registering the handler may fail.
If you want to set a flag, it should be volatile sigatomic_t, you shouldn't do buffered IO in the handler.
With the wrapper and flag approach, you could do something like:
typedef void (Sigfunc)(int);
Sigfunc* reliableSignal(int signo, Sigfunc *func);
// user-defined signal handler for alarm.
volatile sig_atomic_t cnt=0;
void my_handler(int signo){
if(cnt++ == 1)
exit(0);
}
int main(void) {
if(reliableSignal(SIGINT,my_handler)<0){ perror("Signal"); exit(1); }
while(1){
printf(" I am running into infinite loop.., stop me if you dear..\n");
sleep(1); /* wait until alarm goes off */
}
} /* main */
Sigfunc* reliableSignal(int signo, Sigfunc *func)
{
struct sigaction act, oact;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if (signo == SIGALRM) {
#ifdef SA_INTERRUPT
act.sa_flags |= SA_INTERRUPT;
#endif
} else {
act.sa_flags |= SA_RESTART;
}
if (sigaction(signo, &act, &oact) < 0)
return(SIG_ERR);
return(oact.sa_handler);
}
In this example you must press twice Ctrl-C within 300ms.
So if you keep Ctrl-C pressed the program will stop otherwise not.
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
int ctrlcs=0;
void cchandler(int signum){
printf("Press again to quit.\n");
ctrlcs+=1;
if (ctrlcs==2) {
signal(SIGINT, SIG_DFL);
}
usleep(300000); // delay for the second Ctrl-C
}
int main(){
signal(SIGINT, cchandler);
while(1) {
printf(" I am running into infinite loop.., stop me if you dare..\n");
sleep (5);
ctrlcs=0;
}
}
Related
I have a program that runs a loop, each time at the end of the loop, the process should sleep for some seconds(the number of seconds is not constant and is calculated at each loop) or until the process receives SIGINT, I used alarm() and sigwait() to do this but it's blocking the ctrl+c signal(i.e SIGINT) which I don't want, I want SIGINT to be received and acted upon normally, sample code below (note that somefunction() below is just an example, in the original code it does real calculation instead of using rand())
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <sys/time.h>
#include <unistd.h>
sigset_t sigs;
void setup_alarm()
{
printf("setting up signals\n");
sigemptyset(&sigs);
sigaddset(&sigs, SIGALRM);
sigprocmask(SIG_BLOCK, &sigs, NULL);
}
void wait_for_alarm(int interval)
{
printf("setting up alarm for %d seconds\n", interval);
alarm(interval);
printf("waiting for signal\n");
int sig_num = sigwait(&sigs, NULL);
// sigwaitinfo()
if (sig_num == 0)
{
printf("I received the alarm signal, breaking the wait\n");
}
else if (sig_num == EINVAL)
{
printf("some other error occurred");
perror("signal wait failed unexpectedly");
exit(1);
}
}
int somefunction()
{
srand(time(NULL));
return (rand() % 4) + 1;
}
int main(int argc, char *argv[])
{
int alarm_wait = 0;
setup_alarm();
while (1)
{
// do somework here
alarm_wait = somefunction();
// sleep for $alarm_wait or untill we receive SIGALARM
wait_for_alarm(alarm_wait);
}
return 0;
}
The results I'm getting is that when the execution reaches sigwait and I send the SIGINT signal(through ctrl-c) the program is not interrupted instead it keeps waiting until $alarm_wait has elapsed or until I send SIGALRM, what I want to do is have the logic only handles SIGALRM and every other signal should be handled normally(i.e SIGINT should interrupt the program even while it's waiting for SIGALRM signal)
Thanks to #Shawn for pointing out the second argument of sigwait I was able to solve my issue by also blocking SIGINT and using the second argument of sigwait to determine if the signal is SIGINT then execute exit(0)
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <sys/time.h>
#include <unistd.h>
sigset_t sigs;
void setup_alarm()
{
printf("setting up signals\n");
sigemptyset(&sigs);
sigaddset(&sigs, SIGALRM);
sigaddset(&sigs, SIGINT);
sigprocmask(SIG_BLOCK, &sigs, NULL);
}
void wait_for_alarm(int interval)
{
printf("setting up alarm for %d seconds\n", interval);
alarm(interval);
printf("waiting for signal\n");
int sig;
int sig_num = sigwait(&sigs, &sig);
if(sig == SIGINT)
exit(0);
if (sig_num == 0)
{
printf("I received the alarm signal, breaking the wait\n");
}
else if (sig_num == EINVAL)
{
printf("some other error occurred");
perror("signal wait failed unexpectedly");
exit(1);
}
}
int somefunction()
{
srand(time(NULL));
return (rand() % 4) + 1;
}
int main(int argc, char *argv[])
{
int alarm_wait = 0;
setup_alarm();
while (1)
{
// do somework here
alarm_wait = somefunction();
// sleep for $alarm_wait or untill we receive SIGALARM
wait_for_alarm(alarm_wait);
}
return 0;
}
The code now works as i would expect, i'm not sure if this is the best solution since i'm only handling two signals and don't know how the rest of the signals are being handled(maybe some of these signals are important for the init system for example).
I will leave my answer unaccepted for sometime incase someone has a better solution.
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.
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);
}
}
I have this code:
#include <stdio.h>
#include <signal.h>
void signal_handler(int signal) {
printf("Caught signal in CHILD.\n");
}
int main(void) {
int s;
signal(SIGTSTP, signal_handler);
while(1){
printf("%s#%s/# ",getlogin(),get_current_dir_name());
scanf("%d",&s);
}
return 0;
}
when i run the code it prints:
something: ^ZCaught signal in CHILD.
As far i understand that the scanf doesn't execute when i press the ctr-z. Although after the printf inside my function it goes straight to the scanf, waits for input and then starts the loop again.Is there any way to avoid scanf when i press ctr-z and start the while loop again? I tried something like that
void signal_handler(int signal) {
printf("Caught signal in CHILD.\n");
printf("%s#%s/# ",getlogin(),get_current_dir_name());
}
but it didn't work. After the second printf goes straight to the scanf, waits for input and then starts the loop again. Can i, somehow, start the loop again?
The signal handler is interrupting scanf during its read of STDIN. However, because of the way you set signal disposition, the read system call restarts immediately upon return of the signal handler. That's why you are "stuck" in the scanf rather than back at the top of your loop.
One important thing you can do is to use sigaction rather than signal. This will force you to specify the behavior of interrupted calls: restart them or not?
The next thing to do is to limit your signal handlers to functions that are async-signal-safe, lest you risk misery.
As an aside, another change to make is to give us all the required includes (<unistd.h>?) and defines (_GNU_SOURCE ?) to make your program work.
As commented the worst solution should be:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
void signal_handler(int signal) {
printf("Caught signal in CHILD.\n");
exit(1);
}
int main(void) {
int s;
signal(SIGTSTP, signal_handler);
while(1){
printf("test\n");
scanf("%d",&s);
}
return 0;
}
Better solution
#include <stdio.h>
#include <signal.h>
static volatile int keepRunning = 1;
void signal_handler(int signal) {
printf("Caught signal in CHILD.\n");
keepRunning = 0;
}
int main(void) {
int s;
signal(SIGTSTP, signal_handler);
while(keepRunning){
printf("test\n");
scanf("%d",&s);
}
return 0;
}
EDIT after comments
#include <stdio.h>
#include <signal.h>
static volatile int skipPrintf= 1;
void signal_handler(int signal) {
printf("Caught signal in CHILD.\n");
skipPrintf= 1;
}
int main(void) {
int s;
signal(SIGTSTP, signal_handler);
while(1){
if (skipPrintf == 0)
{
printf("test\n");
}
else
{
skipPrintf = 0;
}
scanf("%d",&s);
}
return 0;
}
#include <stdio.h>
#include <signal.h>
void ALARMhandler(int sig)
{
signal(SIGALRM, SIG_IGN); /* ignore this signal */
printf("Hello");
signal(SIGALRM, ALARMhandler); /* reinstall the handler */
}
int main(int argc, char *argv[])
{
alarm(2); /* set alarm clock */
while (1)
;
printf("All done");
}
I expect the program to print "hello" after 2 seconds, but instead the output is "zsh: alarm ./a.out"
Any idea what is going on?
You're forgetting to set the alarm handler initially. Change the start of main() like:
int main(int argc, char *argv[])
{
signal(SIGALRM, ALARMhandler);
...
Also, the signal handler will probably print nothing. That's because the C library caches output until it sees an end of line. So:
void ALARMhandler(int sig)
{
signal(SIGALRM, SIG_IGN); /* ignore this signal */
printf("Hello\n");
signal(SIGALRM, ALARMhandler); /* reinstall the handler */
}
For a real-world program, printing from a signal handler is not very safe. A signal handler should do as little as it can, preferably only setting a flag here or there. And the flag should be declared volatile.
You're not setting the handler in your main function.
Before you do alarm(2), put the signal(SIGALRM, ALARMhandler); in your main.
It should work then.
Note that your "All Done" will never be printed, because you'll stay in the while(1) loop after the signal processor has run. If you want the loop to be broken, you'll need to have a flag that the signal handler changes.
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
/* number of times the handle will run: */
volatile int breakflag = 3;
void handle(int sig) {
printf("Hello\n");
--breakflag;
alarm(1);
}
int main() {
signal(SIGALRM, handle);
alarm(1);
while(breakflag) { sleep(1); }
printf("done\n");
return 0;
}
You are not installing the signal handler first.
You need to tell the system that you want to handle the signal before actually receiving it, so you need to call signal() from main before the signal comes.
int main(int argc, char *argv[])
{
signal(SIGALRM, ALARMhandler); /* install the handler */
alarm(2); /* set alarm clock */
while (1);
}
Andomar is rigth. I test this and,
version 1 prints (every second):
Hi...
Hi...
Hi...
Hi...
BYE
Hi...
...
version 2 prints (every five seconds):
Hi...Hi...Hi...Hi...BYE
Hi...Hi...Hi...Hi...BYE
...
So the code is:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
# define T 5
int flag = T;
void sigalrm_handler(int);
int main(void)
{
signal(SIGALRM, sigalrm_handler);
alarm(1);
while (1);
}
void sigalrm_handler(int sig)
{
if(--flag){
printf("Hi...\n"); /*version 1*/
/*printf("Hi...");*/ /*version 2*/
}else{
printf("BYE\n");
flag=T;
}
alarm(1);
}