SIGTERM signal from parent does not invoke signal handler in child? - c

I'm writing a program where both the child process and the parent process can send a SIGTERM signal to the child.
The signal handler is something like this:
void custom_signal_handler(int signum, siginfo_t* info, void* ptr) {
if (signum == SIGTERM) {
printf("1\n");
}
else if (signum == SIGCONT) {
printf("2\n");
}
}
(I have simplified the printing in the ifs to keep the code here simpler).
For the SIGCONTsignal - only the parent can call this signal with kill(childPid, SIGCONT). When this is happening, the signal handler for the child prints the "2" as intended.
However, for the SIGTERM signal - both the parent can invoke it by sending kill(childPid, SIGTERM) and the child by calling raise(SIGTERM). The problem is that "1" is printed only when the child raises the SIGTERM signal, but not when the parent calls it.
I have regiestered the signal handler to the child:
// set up signal handler
struct sigaction custom_action;
memset(&custom_action, 0, sizeof(custom_action));
custom_action.sa_sigaction = custom_signal_handler;
custom_action.sa_flags = SA_SIGINFO;
// assign signal handlers
if (0 != sigaction(SIGCONT, &custom_action, NULL)) {
printf("Signal registration failed: %s\n",strerror(errno));
return -1;
}
if (0 != sigaction(SIGTERM, &custom_action, NULL)) {
printf("Signal registration failed: %s\n",strerror(errno));
return -1;
}
Any ideas? Thanks!

In a comment to the question, OP states
I am sending the SIGTERM from the parent while the relevant child is at "raise(SIGSTOP)". I think that because the child is in SIGSTOP it doesn't run the signal handler.
Correct. When a process is stopped, it does not receive signals other than SIGCONT and SIGKILL (plus SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU are ignored). All other signals should become pending, delivered when the process is continued. (Standard POSIX signals are not queued, though, so you can rely on only one standard POSIX signal becoming pending.)
However, I do need to send the SIGTERM only when the child is in SIGSTOP, without sending SIGCONT before.
The target process will receive SIGTERM only after it is continued. That is how stopped processes behave.
Is there a workaround?
Perhaps; it depends on the requirements. But do note that your intended use case involves behaviour that does not comply with POSIX (i.e., you want a stopped process to react to something other than just being continued or killed outright); and that is the direct reason for the problems you have encountered.
The simplest is to use a variant of SIGCONT instead of SIGTERM, to control the terminating of the process; for example, via sigqueue(), providing a payload identifier that tells the SIGCONT signal handler to treat it as a SIGTERM signal instead (and thus distinguishing between normal SIGCONT signals, and those that are stand-ins for SIGTERM).
A more complicated one is to have the process fork a special monitoring child process, that regularly sends special "check for pending SIGTERM signals" SIGCONT signals, and dies when the parent dies. The child process can be connected to the parent via a pipe (parent having the write end, child the read end), so that when the parent dies, a read() on the child end returns 0, and the child can exit too. The parent process SIGCONT handler just needs to detect if the signal was sent by the child process — the si_pid field of the siginfo_t structure should only match the child process ID if sent by the child —, and if so, check if a SIGTERM is pending, handle it if yes; otherwise just raise SIGSTOP. This approach is very fragile, due to the many possibilities of race windows — especially raising SIGSTOP just after receiving SIGCONT. (Blocking SIGCONT in the signal handler is essential. Also, the monitoring child process should probably be in a separate process group, not attached to any terminal, to avoid being stopped by a SIGSTOP targeted at the entire process group.)
Note that one should only use async-safe functions in signal handlers, and retain errno unchanged, to keep everything working as expected.
For printing messages to standard error, I often use
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
static int wrerr(const char *msg)
{
const int saved_errno = errno;
const char *end = msg;
ssize_t count;
int retval = 0;
/* Find end of string. strlen() is not async-signal safe. */
if (end)
while (*end)
end++;
while (msg < end) {
count = write(STDERR_FILENO, msg, (size_t)(end - msg));
if (count > 0)
msg += count;
else
if (count != -1) {
retval = EIO;
break;
} else
if (errno != EINTR) {
retval = errno;
break;
}
}
errno = saved_errno;
return retval;
}
which not only is async-signal safe, but also keeps errno unchanged. It returns 0 if success, and an errno error code otherwise.
If we expand the prints a bit for clarity, OP's custom signal handler becomes for example
void custom_signal_handler(int signum, siginfo_t* info, void* context) {
if (signum == SIGTERM) {
wrerr("custom_signal_handler(): SIGTERM\n");
} else
if (signum == SIGCONT) {
wrerr("custom_signal_handler(): SIGCONT\n");
}
}
Do note that when this is used, ones program should not use stderr (from <stdio.h>) at all, to avoid confusion.

Related

How can waitpid() reap more than one child?

In this example from the CSAPP book chap.8:
\#include "csapp.h"
/* WARNING: This code is buggy! \*/
void handler1(int sig)
{
int olderrno = errno;
if ((waitpid(-1, NULL, 0)) < 0)
sio_error("waitpid error");
Sio_puts("Handler reaped child\n");
Sleep(1);
errno = olderrno;
}
int main()
{
int i, n;
char buf[MAXBUF];
if (signal(SIGCHLD, handler1) == SIG_ERR)
unix_error("signal error");
/* Parent creates children */
for (i = 0; i < 3; i++) {
if (Fork() == 0) {
printf("Hello from child %d\n", (int)getpid());
exit(0);
}
}
/* Parent waits for terminal input and then processes it */
if ((n = read(STDIN_FILENO, buf, sizeof(buf))) < 0)
unix_error("read");
printf("Parent processing input\n");
while (1)
;
exit(0);
}
It generates the following output:
......
Hello from child 14073
Hello from child 14074
Hello from child 14075
Handler reaped child
Handler reaped child //more than one child reaped
......
The if block used for waitpid() is used to generate a mistake that waitpid() is not able to reap all children. While I understand that waitpid() is to be put in a while() loop to ensure reaping all children, what I don't understand is that why only one waitpid() call is made, yet was able to reap more than one children(Note in the output more than one child is reaped by handler)? According to this answer: Why does waitpid in a signal handler need to loop?
waitpid() is only able to reap one child.
Thanks!
update:
this is irrelevant, but the handler is corrected in the following way(also taken from the CSAPP book):
void handler2(int sig)
{
int olderrno = errno;
while (waitpid(-1, NULL, 0) > 0) {
Sio_puts("Handler reaped child\n");
}
if (errno != ECHILD)
Sio_error("waitpid error");
Sleep(1);
errno = olderrno;
}
Running this code on my linux computer.
The signal handler you designated runs every time the signal you assigned to it (SIGCHLD in this case) is received. While it is true that waitpid is only executed once per signal receival, the handler still executes it multiple times because it gets called every time a child terminates.
Child n terminates (SIGCHLD), the handler springs into action and uses waitpid to "reap" the just exited child.
Child n+1 terminates and its behaviour follows the same as Child n. This goes on for every child there is.
There is no need to loop it as it gets called only when needed in the first place.
Edit: As pointed out below, the reason as to why the book later corrects it with the intended loop is because if multiple children send their termination signal at the same time, the handler may only end up getting one of them.
signal(7):
Standard signals do not queue. If multiple instances of a
standard signal are generated while that signal is blocked, then
only one instance of the signal is marked as pending (and the
signal will be delivered just once when it is unblocked).
Looping waitpid assures the reaping of all exited children and not just one of them as is the case right now.
Why is looping solving the issue of multiple signals?
Picture this: you are currently inside the handler, handling a SIGCHLD signal you have received and whilst you are doing that, you receive more signals from other children that have terminated in the meantime. These signals cannot queue up. By constantly looping waitpid, you are making sure that even if the handler itself can't deal with the multiple signals being sent, waitpid still picks them up as it's constantly running, rather than only running when the handler activates, which can or can't work as intended depending on whether signals have been merged or not.
waitpid still exits correctly once there are no more children to reap. It is important to understand that the loop is only there to catch signals that are sent when you are already in the signal handler and not during normal code execution as in that case the signal handler will take care of it as normal.
If you are still in doubt, try reading these two answers to your question.
How to make sure that `waitpid(-1, &stat, WNOHANG)` collect all children processes
Why does waitpid in a signal handler need to loop? (first two paragraphs)
The first one uses flags such as WNOHANG, but this only makes waitpid return immediately instead of waiting, if there is no child process ready to be reaped.

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.

Why SIGKILL is not handled by my signal handler and sometime leads to account logout

I wrote the following code with the following intention:
The child sends SIGKILL signal to the parent and then the parent will handle it using the handler. The handler just increment the counter.
int counter = 0;
void handler(int sig) {
counter++;
sleep(1); /* Do some work in the handler */
return;
}
int main() {
int i;
//signal(SIGUSR2, handler);
signal(SIGKILL, handler);
if (fork() == 0) { /* Child */
for (i = 0; i < 5; i++) {
//kill(getppid(), SIGUSR2);
kill(getppid(), SIGKILL);
printf("sent SIGKILL to parent %d\n", getppid());
}
exit(0);
}
wait(NULL);
printf("counter=%d\n", counter);
exit(0);
}
However, the first kind of output I get is:
sent SIGKILL to parent 8371
sent SIGKILL to parent 8371
sent SIGKILL to parent 8371
sent SIGKILL to parent 8371
sent SIGKILL to parent 8371
Killed
Apparently, the SIGKILL kills the parent process. Why does this happen since my handler does not kill the parent?
Another non-deterministic, but very high probability event (~50%) is that on my ubuntu 14.04 64-bit machine. If I execute this code, I will get automatically logout from the account. Why would this happen?
Have your read the manual of signal() ?
First:
The behavior of signal() varies across UNIX versions, and has also varied historically across different versions of Linux. Avoid its use: use sigaction(2) instead. See Portability below.
Second:
The signals SIGKILL and SIGSTOP cannot be caught or ignored.

Detecting SIGTTIN when a child background process runs "cat"

I have the following program where I set the parent's process group and the child's process group, as well as giving the terminal control to the parent. Then, I run "cat" in the "background" child, which is supposed to generate SIGTTIN. However, the printf line in sighandler is not printed. Any ideas how to properly detect SIGTTIN in this case?
void sighandler(int signo){
printf("SIGTTIN detected\n");
}
int main() {
int status;
pid_t pid;
pid = fork ();
setpgid(0,0);
tcsetpgrp (STDIN_FILENO, 0);
signal(SIGTTIN, sighandler);
if (pid == 0)
{
setpgid(0,0);
execl ("cat", NULL);
_exit (EXIT_FAILURE);
}
else{
int status;
setpgid(pid,pid);
waitpid(-1, &status, 0);
}
return status;
}
Mariska,
For Parent Processes
As explained in the Stack Overflow post titled, "Catch Ctrl-C in C,":
The behavior of signal() varies across UNIX versions, and has also
varied historically across different versions of Linux. Avoid its use:
use sigaction(2) instead.
As described in the Linux Programmer's Manual, you should use sigaction():
The sigaction() system call is used to change the action taken by a
process on receipt of a specific signal.
Try this:
#include<stdio.h>
#include <signal.h>
static void handler(int signum)
{
/* Take appropriate actions for signal delivery */
printf("SIGTTIN detected\n");
}
int main()
{
struct sigaction sa;
sa.sa_handler = handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART; /* Restart functions if
interrupted by handler */
if (sigaction(SIGINT, &sa, NULL) == -1)
/* Handle error */;
/* Further code */
}
For Child Processes
There are a couple of points you should know when dealing with signal handlers for the child processes:
A forked child inherits the signal handlers from the parent
Because of the above, you need to implement some sort of signal handler for the parent and then change the signal handler before and after executing a child.
As explained in the Linux Programmer's Manual:
All process attributes are preserved during an execve(), except the following:
a. The set of pending signals is cleared (sigpending(2)).
b. The dispositions of any signals that are being caught are
reset to being ignored.
c. Any alternate signal stack is not preserved (sigaltstack(2)).
Thus, the exec() functions do not preserve signal handlers.
From the above, I am trying to show you that pressing Ctrl-C sends the signal to the parent process (unless you use exec()), and then the signals are automatically propagated to children. This is why we need to change the signal handler. Even when the child is currently "active", the parent will still receive signals before the child will.
Please let me know if you have any questions!

How can I be sure I'm not losing signals when using pause()?

I'm writing a program that uses fork to create child processes and count them when they're done.
How can I be sure I'm not losing signals?
what will happen if a child sends the signal while the main program still handles the previous signal? is the signal "lost"? how can I avoid this situation?
void my_prog()
{
for(i = 0; i<numberOfDirectChildrenGlobal; ++i) {
pid = fork();
if(pid > 0)//parent
//do parent thing
else if(0 == pid) //child
//do child thing
else
//exit with error
}
while(numberOfDirectChildrenGlobal > 0) {
pause(); //waiting for signal as many times as number of direct children
}
kill(getppid(),SIGUSR1);
exit(0);
}
void sigUsrHandler(int signum)
{
//re-register to SIGUSR1
signal(SIGUSR1, sigUsrHandler);
//update number of children that finished
--numberOfDirectChildrenGlobal;
}
It's recommended to use sigaction instead of signal, but in both cases it won't provide what you need. If a child sends a signal while the previous signal is still being handled, it will become a pending signal, but if more signals are sent they will be discarded (on systems that are not blocking incoming signals, the signals can be delivered before reestablishment of the handler and again resulting in missing signals). There is no workaround for this.
What one usually does is to assume that some signals are missing, and lets the handler take care of exiting children.
In your case, instead of sending a signal from your children, just let the children terminate. Once they terminate, the parent's SIGCHLD handler should be used to reap them. Using waitpid with WNOHANG option ensures that the parent will catch all the children even if they all terminate at the same time.
For example, a SIGCHLD handler that counts the number of exited children can be :
pid_t pid;
while((pid = waitpid(-1, NULL, WNOHANG)) > 0) {
nrOfChildrenHandled++;
}
To avoid this situation you can use the posix real-time signals.
Use sigaction instead of signal to register your handlers, and the delivery of the signals is assured.

Resources