Gracefully Exiting a C Application - c

I currently have a program I have written in C on a server that has an infinite loop that processes information, each loop takes about 5 minutes to complete. I would like to have the following functionality in a shell script:
Terminate C program
Make source
Run program
The problem is, I don't know how to tell my C program to exit without doing something like ctrl+c, I would rather it finished processing the information it is currently working on before terminating itself.

The POSIX standard way to tell a process to finish its business and exit cleanly is to send it a SIGTERM signal. Depending on your application it may or may not be appropriate to exit on SIGINT, which is meant to interrupt a process, not terminate it. (Control-c sends SIGINT.)
Try putting a flag in your tight loop; check the flag at a time when it is easy to exit, but still frequently enough to exit promptly. In your case, receipt of a SIGTERM might put a message on the system log right away, then promise to exit within the next 5 minutes.
Your signal handler will look like this:
static int signalled; // if nonzero, what signal have we been sent?
static void SignalHandler(int signum) {
signalled = signum;
}
I check the global static variable signalled after every I/O operation, which means many times per second.
Here's my code to catch and restore signals:
static __sighandler_t sh, si, st;
static void catch_signals(void) {
if ((sh = signal(SIGHUP, SignalHandler)) == SIG_IGN) signal(SIGHUP, SIG_IGN);
if ((si = signal(SIGINT, SignalHandler)) == SIG_IGN) signal(SIGINT, SIG_IGN);
if ((st = signal(SIGTERM, SignalHandler)) == SIG_IGN) signal(SIGTERM, SIG_IGN);
signalled = 0;
}
static void restore_signals(void) {
signal(SIGHUP, sh);
signal(SIGINT, si);
signal(SIGTERM, st);
}
(This code is from a library, so I'm being extra careful to leave things the way I found them.)
Bonus trick: when time expires (this is a TV recording library), the timer just sets signalled = SIGTERM, and the same logic is used to exit the recorder normally.

like ctrl+c, I would rather it finished processing the information it
is currently working on before terminating itsel
Establish a signal handler for SIGINT or whatever you want and do your cleanup after you receive it. You shouldn't do the cleanup in the handler itself however.
volatile sig_atomic_t do_cleanup = 0;
void handler(int sig)
{
do_cleanup = 1;
}
Then in your main loop you just have to test do_cleanup and exit when you please. You must also be careful in properly treating EINTR errors if you're not already doing so.

Here is how to send signal from shell:
http://bash.cyberciti.biz/guide/Sending_signal_to_Processes
or simply man kill
Here is how to react to signal:
http://www.cs.cf.ac.uk/Dave/C/node24.html#SECTION002400000000000000000

Related

master error when multiple signal are sent

I got this issue:
I made a program in c, where the main process creates some child process, and these, after a while, are able to send a signal to the main process:
the signal is sent with this code:
kill(getppid(), SIGUSR1);
and the main process, in the while loop is waiting the SIGUSR1 message...
everything is fine, but if I increase the child number and automatically the possibility to have more signals in the same time, the program crash printing the message:
User defined signal 1
the main code is like this:
void signalHandler(int sig, siginfo_t* info, void* vp) {
if (sig == SIGUSR1) {
printf("SIGUSR1 has arrived\n");
} else if (sig == SIGUSR2) {
printf("SIGUSR2 has arrived\n");
}
}
int main(int argc, char const *argv[]) {
struct sigaction action, old_action;
memset(&action, 0, sizeof(struct sigaction));
action.sa_sigaction = signalHandler;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_RESTART | SA_NODEFER;
while (1) {
sigaction(SIGUSR1, &action, &old_action);
sigaction(SIGUSR2, &action, &old_action);
}
}
I think the problem is that the signal is sent when the master is still working on the previous signal...but how can I do to fix this thing
thank you very much
It means that the child is sending the signal before the parent process was able to call sigaction() to configure the signal handler. When this happens, the default signal reaction to SIGUSR1 terminates the program:
SIGUSR1 P1990 Term User-defined signal 1
https://man7.org/linux/man-pages/man7/signal.7.html
However, there are many problems with your code. printf() is not safe to be called inside a signal handler (it's AS-Unsafe as defined by POSIX):
https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/V2_chap02.html#tag_15_04_03
Also, using SA_NODEFER may create nested signals (another signal handler is called while some signal handler is running) but your program does not protect against a flood. Given enough children this will generate a stack overflow. Finally, the main program keeps running a non-stop infinite loop reconfiguring the signals, while it should have configured them only once outside the loop and blocked inside the loop (for example sigwait() or pselect()):
https://man7.org/linux/man-pages/man2/select.2.html
Finally, if you expect to run a large number of children that might flood the parent with signals, then it would be better to use the real time signal generation function (sigqueue()) rather than kill(). The difference is that with sigqueue(), all signals are queued and SA_NODEFER is not necessary to avoid discarding signals while some other signal handler is running:
https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/V2_chap02.html#tag_15_04_02
Final conclusion: the code should be completely rewritten.

Why my sig_int() function can't prevent my function from exit in c?

The codes is as below, and is the same as the one in book apue3e:
#include "apue.h"
#include "sys/wait.h"
static void sig_int(int);
int
main(int argc, char *argv[]) {
pid_t pid;
char buf[MAXLINE];
int status;
if (signal(SIGINT, sig_int) == SIG_ERR) {
err_sys("signal error");
}
printf("%% ");
while (fgets(buf, MAXLINE, stdin) != NULL) {
if (buf[strlen(buf)-1] == '\n') {
buf[strlen(buf)-1] = '\0';
}
if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid == 0) {
execlp(buf, buf, (char *)NULL);
err_ret("couldn't execlvp: %s\n", buf);
exit(127);
}
if ((pid = waitpid(pid, &status, 0)) < 0) {
err_sys("waitpid_error");
}
printf("%% ");
}
exit(0);
}
static void
sig_int(int signo/* arguments */) {
/* code */
printf("Interrupted\n%%3 ");
}
So, my question is why this signal handler doesn't handle the SIGINT signal and exit immediately after pressing the Ctrl+c which i was testing on archlinux.
[W]hy this signal handler doesn't handle the SIGINT signal and exit immediately after pressing the Ctrl+c which i was testing on archlinux.
Given
static void
sig_int(int signo/* arguments */) {
/* code */
printf("Interrupted\n%%3 ");
}
and
signal(SIGINT, sig_int)
Your process doesn't exit when you press CTRL-C for the simple reason your signal handler doesn't cause the process to exit.
You replaced the default SIGINT handler with your own, so the default action of exiting the process no longer happens.
Since you're running on Linux, I'll refer to the GNU glibc documentation on termination signals:
24.2.2 Termination Signals
These signals are all used to tell a process to terminate, in one way
or another. They have different names because they’re used for
slightly different purposes, and programs might want to handle them
differently.
The reason for handling these signals is usually so your program can
tidy up as appropriate before actually terminating. For example, you
might want to save state information, delete temporary files, or
restore the previous terminal modes. Such a handler should end by
specifying the default action for the signal that happened and then
reraising it; this will cause the program to terminate with that
signal, as if it had not had a handler. (See Termination in
Handler.)
The (obvious) default action for all of these signals is to cause the
process to terminate.
...
Macro: int SIGINT
The SIGINT (“program interrupt”) signal is sent when the user types
the INTR character (normally C-c).
The Termination in Handler glibc documentation states:
24.4.2 Handlers That Terminate the Process
Handler functions that terminate the program are typically used to
cause orderly cleanup or recovery from program error signals and
interactive interrupts.
The cleanest way for a handler to terminate the process is to raise
the same signal that ran the handler in the first place. Here is how
to do this:
volatile sig_atomic_t fatal_error_in_progress = 0;
void
fatal_error_signal (int sig)
{
/* Since this handler is established for more than one kind of signal,
it might still get invoked recursively by delivery of some other kind
of signal. Use a static variable to keep track of that. */
if (fatal_error_in_progress)
raise (sig);
fatal_error_in_progress = 1;
/* Now do the clean up actions:
- reset terminal modes
- kill child processes
- remove lock files */
…
/* Now reraise the signal. We reactivate the signal’s
default handling, which is to terminate the process.
We could just call exit or abort,
but reraising the signal sets the return status
from the process correctly. */
signal (sig, SIG_DFL);
raise (sig);
}
Also, note that there can be significant differences between signal() and sigaction(). See What is the difference between sigaction and signal?
Finally, calling printf() from with a signal handler is undefined behavior. Only async-signal-safe functions can be safely called from within a signal handler. See POSIX 2.4 Signal Concepts for the gory details.

Signals and sleep not working properly

i have an assignment to make, for university, it is almost done, most thing working, there is just one aspect that is not working and i'm not quite sure how to fix it..
The objetivo is to make the problem wait for 2 ctrl+C and close.. But if he catch a first ctrl+C and pass more then 3 seconds the program must forget about it and wait again for another 2 ctrl+C. This is how i'm doing it:
/*Problem 2. Write a program that sleeps forever until the user interrupts it twice with a Ctrl-C, and
then exits. Once the first interrupt is received, tell the user: “Interrupt again to exit.”. The first
interrupt should be forgotten 3 seconds after it has occurred. Additionally, the program should block
the SIGQUIT signal, and ignore the SIGTSTP signal. The program should start by printing “Interrupt
twice with Ctrl-C to quit.” on the screen.*/
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
//handler to catch the first ctrl_c and ask user to do it another time(no reference to time limit)
void ctrl_c(int sig){
signal(sig, SIG_IGN);
printf("\nInterrupt again to exit.\n");
}
//handler for second ctrl_c. If called, program will end
void second_catch(int sig){
if(sig == SIGINT){
printf("\n");
exit(0);
}
}
//handler to always ignore ctrl_z
void ctrl_z(int sig){
signal(sig, SIG_IGN);
}
int main(){
//blocking SIQUIT (Ctrl+\) using series of command to change the mask value of SIGQUIT
sigset_t sg;
sigemptyset (&sg);
sigaddset(&sg, SIGQUIT);
sigprocmask(SIG_BLOCK, &sg, NULL);
//installing handler to ignore SIGTSTP (Ctrl+Z)
signal(SIGTSTP, ctrl_z);
//two part SIGINT handling
printf("\nInterrupt twice with Ctrl+C to quit.\n");
signal(SIGINT, ctrl_c); //first handler install
do{ //cycle for second hanler install and 3 second timer
if(sleep(3) == 0){
main(); //if second_catch handler is not called within 3 seconds, program will restart
}
else {
signal(SIGINT, second_catch); //upon call, program will end
}
}while(1);
return 0;
}
What's happening is that it keeps reseting after 3 seconds, in a loop.. But i want to reset only 1 time after i click ctrl+c and 3 seconds passed..
What must i change?
Your approach is unlikely to lead to a working program.
First, use a signal handler that only sets a global variable (of volatile sig_atomic_t type) whenever a SIGINT signal is caught. Do not try to print anything from the signal handler, as standard I/O is not async-signal safe.
Second, use sigaction() to install the signal handler. Use zero flags. In other words, do NOT use SA_RESTART flag when installing the handler. This way, when a signal is delivered to your handler, it will interrupt most syscalls (including sleeps). (The functions will return -1 with errno == EINTR.)
This way, after your main() has installed the signal handler, you can have it print the instruction, and enter into a loop.
In the loop, clear the interrupt flag, and sleep for a few seconds. It does not matter how long. If the interrupt flag is not set after the sleep completes, continue (at the beginning of the loop).
Otherwise, you know that the user has pressed Ctrl+C. So, clear the interrupt flag, and sleep for another three seconds. If the flag is set after the sleep completes, you know the user supplied another Ctrl+C, and you can break out of the loop. Otherwise, you just continue the loop again.
Technically, there is a race condition here, as the user might press Ctrl+C twice in a row, rapidly enough so that the main() only sees one.
Unfortunately, increments (flag++) are not atomic; the compiler or the hardware may actually do temp = flag; temp = temp + 1; flag = temp; and the signal may be delivered just before the third step, leading to the signal handler and main() seeing different values of flag.
One way around that is to use C11 atomics (if the architecture and C library provides them, in <stdatomic.h>, with macro ATOMIC_INT_LOCK_FREE defined): volatile atomic_int flag; for the flag, __atomic_add_fetch(&flag, 1, __ATOMIC_SEQ_CST) to increment it, and __atomic_sub_fetch(&flag, 1, __ATOMIC_SEQ_CST) to decrement it.
Another way would be to use a POSIX semaphore. The signal handler can increment it (using sem_post()) safely. In main(), you can use sem_timedwait() to wait for the signal for a limited time, and sem_trywait() to decrement it.
A third way would be to use sigtimedwait() to catch the signal in main() with a timeout, without any signal handlers. This last one is, I believe, the most robust and simple to implement, so that's what I'd use in a real application.
It turns out that there is another way to achieve this, one that responds to two consecutive Ctrl+C presses within three seconds, without leaving any nasty corner cases.
This is NOT exactly what was asked of OP, and as such is not a valid answer to their exercise, but this would be a good approach otherwise.
The idea is to use alarm() and a SIGALRM handler, and two sig_atomic_t flags: one that counts the Ctrl+C keypresses, and one that flags the case when there have been two in a three-second period.
Unfortunately, sleep() cannot be used in this case -- you have to use nanosleep() instead --, as sleep(), alarm(), and SIGALRM signal handling may interfere with each other.
Essentially, we use
#define INTR_SECONDS 3
static volatile sig_atomic_t done = 0;
static volatile sig_atomic_t interrupted = 0;
static void handle_sigalrm(int signum)
{
if (interrupted > 1)
done = 1;
interrupted = 0;
}
static void handle_sigint(int signum)
{
interrupted++;
if (interrupted > 1) {
done = 1;
alarm(1);
} else
alarm(INTR_SECONDS);
}
handle_sigalrm() is installed as the SIGALRM handler, with SIGINT in its signal mask; handle_sigint() is installed as the SIGINT handler, with SIGALRM in its signal mask. This way the two signal handlers block each other, and won't be interrupted by each other.
When a first SIGINT is received, the alarm is primed. If this is the second (or third etc.) SIGINT without an intervening SIGALRM, we also set the done flag, and prime the alarm to occur in one second, to ensure we catch the state change in at most one second.
When a SIGALRM is received, the interrupt count is zeroed. If it was two or more, the done flag is also set.
In main(), we only check done and interrupted, never modify them. This avoids the corner cases I was worried about.
In the worst case, there is one second delay to quitting, if the second Ctrl+C is delivered after we check, but just before we sleep. The alarm(1) in handle_sigint() is for just that case.
The loop in main is then just
while (!done) {
while (!done && !interrupted)
nanosleep(&naptime, NULL);
if (done)
break;
printf("Ctrl+C again to quit!\n");
fflush(stdout);
while (interrupted == 1 && !done)
nanosleep(&naptime, NULL);
}
The first inner loop only sleeps when it has been over three seconds since the last SIGINT (or we never received one). It will be interrupted by both SIGINT and SIGALRM, so the duration does not matter.
The if (done) break; case just avoids printing anything if the user had lightning hands and typed Ctrl+C twice really fast.
The second inner loop only sleep when we are waiting for a second Ctrl+C. It too will be interrupted by both signals, so the duration here does not matter either. Note, however, that we do wish to check interrupted first, to ensure we catch all changes reliably. (If we checked done first, we might be interrupted before we check interrupted, and it is possible, in theory, that done changes to nonzero and interrupt to zero and then to 1 in the mean time. But, if we check interrupted first, and it is 1, any additional interrupts will just set done, which we'll catch. So, interrupted == 1 && done == 0 is the correct check in the correct order here.)
As noted above, the duration specified for nanosleep() does not actually matter, as it will be interrupted by the signal delivery anyway. Something like ten seconds should be fine,
struct timespec naptime = { .tv_sec = 10, .tv_nsec = 0L };
If the lecturer had recommended POSIX.1 functions (sigaction(), nanosleep()), this would have been surprisingly interesting exercise.

Blocking new signals while in handler

I have a parent process that manages a child (fork, execve). I created a handler in the parent to catch SIGCHLD signals from the child in order to call waitpid() and take appropriate action such as restarting the child.
I understood from the manual page for sigaction() that, while inside a signal handler, further signals of the same type would be blocked by default. I definitely wish for this behaviour so I decided to test it.
I put a sleep (my own implementation using clock_nanosleep() in a loop which resumes when interrupted) at the end of the signal handler and sent a SIGINT to the child. This duly made it quit and sent SIGCHLD to the parent. I logged the fact and started my sleep for 10 seconds. Now, I sent another SIGINT to the new child (sighandler restarted it first time) and was surprised to see another log and sleep happen.
How can this be? When I attached using a debugger to the parent it clearly showed two different threads interrupted to call my signal handler, both now sat in sleep. If that keeps up I will run out of threads!
I understand putting long sleeps into a signal handler is a daft thing to do but it does illustrate the point; I expected to see the second signal marked as pending in /proc/[PID]/status but instead it's delivered.
Here's the relevant bits of my code:
Set up the SIGCHLD handler:
typedef struct SigActType {
struct sigaction act;
int retval;
void (*func)(int);
}SigActType;
static SigActType sigActList[64];
public void setChildHandler(void (*func)(int)) {
SigActType *sat = &sigActList[SIGCHLD];
sat->act.sa_sigaction = sigchldHandler;
sigemptyset(&sat->act.sa_mask);
sigaddset (&sat->act.sa_mask, SIGTERM);
sigaddset (&sat->act.sa_mask, SIGINT);
sigaddset (&sat->act.sa_mask, SIGCHLD);
sat->act.sa_flags = SA_SIGINFO;
sat->retval = 0;
sat->func = func;
sigaction(SIGCHLD, &sat->act, NULL);
}
static void sigchldHandler(int sig, siginfo_t *si, void *thing) {
SigActType *sat = &sigActList[SIGCHLD];
if (sat->func) {
sat->func(si->si_pid);
}
}
and using this:
int main(int argc, char **argv) {
setChildHandler(manageChildSignals);
...
}
static void manageChildSignals(int d) {
if ((pid = waitpid(-1, &stat, WAIT_MYPGRP)) > 0) {
... restart child if appropriate
}
printf("start of pause...\n");
mySleep(10);
printf("end of pause...\n");
}
Stdout clearly shows:
(when I type kill -2 [PID]
start of pause
(when the new child is started and I type kill -2 [NEWPID]
start of pause
...10 seconds slide past...
end of pause
end of pause
I am puzzled as to why this happens. As you can see I even added SIGCHLD to the block mask for sigaction() to try to encourage it to do the right thing.
Any pointers most welcome!
signals of the same type would be blocked by default.
Yes, but only for the thread sigaction() is called from.
From man sigaction (bold emphasis by me):
sa_mask specifies a mask of signals which should be blocked (i.e.,
added to the signal mask of the thread in which the signal handler is
invoked) during execution of the signal handler.
As signal dispostion is per process any other thread not blocking the signal in question might receive it, that is get interupted and process it.
If this behaviour is not what you want you should perhaps modify the design of the way your program handles signals in such a way that per default all signals are blocked for each thread, and only one specifiy thread has signal reception unblocked.
Update:
Signals masks are inherited from the parent thread by the child thread.
If signal handling shall be done by one specific thread only, have the main thread block all signals prior to creating any other thread. Then create one specfic thread to do the signal handling, and have this thread unblock the signals to be handled. This concept also allows models like one thread per signal.
In a mutlithreaded environment use pthread_sigmask() to mask signals on a per thread base.
Please note that the behaviour of sigprocmask() in a multithreaded process is unspecified, use pthread_sigmask() then.

C using Signals to stop child processes

My current program is creating child processes and giving them work (CPU intensive work). The main() sits there and waits for the child processes to send data via pipes (using select).
What I wanted to do is when the program is processing data I could press CTRL+C to stop the child processes from working and asking the user if he wants to quit or resume work.
If user wants to quit, the program would kill all the processes. If user wants to resume work, it would tell the child processes to resume the computation.
I already have the code in place but it's not quite working right.
In main I have signal(SIGINT, pausar); to handle SIGINT (CTRL+C).
This is the pausar() function:
void pausar(int signum){
signal(SIGINT, pausar);
int i;
// pid[] contains all the child processes
for(i = 0; i<CORES; i++)
{
kill(pid[i], SIGSTOP);
}
char option[2];
printf("\n Computacao pausada.\n'S' para sair ou 'C' para continuar: ");
scanf("%1s", option);
if (option[0] == 's' || option[0] == 'S') {
printf("A desligar...\n");
//if user wants to quit, kill all the child processes
for(i = 0; i<CORES; i++)
{
kill(pid[i], SIGKILL);
}
exit(0);
}
else
{
printf("[%d] A resumir computacao...\n",getpid());
kill(getpid(), SIGCONT);
//if user wants to resume work, send signal to continue
for(i = 0; i<CORES; i++)
{
kill(pid[i], SIGCONT);
printf("%d resumiu\n", pid[i]);
}
}
}
The problem is that sometimes I press CTRL+C and nothing shows in the console (but the processes STOP because I'm paying attention to the process manager). The other problem is that after I enter 'C' to resume work, I get errors in select() and the children never resume work.
Using select() and signal-handler at the same time is prone to race conditions - a signal could occur during the select() call, but also in every other line of code.
If your are on linux: create an event socket with signalfd() and add this socket to the read set passed to select(). Signals are then handled at a fixed point in your code and you do not need to worry about race conditions.
First, for what you're trying to-do, your signal handler is way too complex. Secondly, calling signal() inside your signal handler is not a good idea ... it's not an asynchronous signal-safe function.
What you can do is the following:
In your main, set the signal handler function using signal() like you've done.
Block the SIGINT signal via sigprocmask(). This prevents a spurious signal from arriving before the call to pselect().
Inside your signal handler only set a simple global flag that is a sig_atomic_t
Use pselect() instead of select(). This will allow you to change the process signal mask to allow a SIGINT signal to arrive, and it will do-so in an atomic manner with respect to signals. Otherwise, you could have your SIGINT arrive before the call to select(), and then you have "lost" that signal, even though it does set the flag in the handler.
When the pselect() call returns, detect whether the flag has been set.
If the global sig_atomic_t flag was set, and you returned from pselect because of a caught signal, then launch another function that will actually do all the ending of the child-processes and prompt the user, etc.
Doing these steps will simplify your signal-handling code and reduce the chances of race-conditions or other unexpected results because of the asynchronous nature of signal arrival.
If you'd like some more information on pselect(), you there is a nice article on that here.

Resources