My daemon (linux only) has the following signal handler:
static void signal_handler(int id, siginfo_t *si, void *context) {
if (id == SIGTERM) {
/* prevent suicide - see below */
if (si->si_pid == getpid()) {
printf("Warning: received SIGTERM from own process\n");
return;
}
/* rest of code omitted */
}
/* rest of code omitted */
}
... which is installed like this in main():
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = &signal_handler;
sa.sa_flags = SA_SIGINFO;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGHUP, &sa, NULL);
The reason for the suicide check in the signal handler is that from time to time (once in 4 weeks) my daemon terminated because it received a SIGTERM from itself.
I am unable to find the cause. The only single kill() call used in the program is this one:
int kill_wrapper(pid_t pid, int sig) {
if (pid <= 0 || pid == getpid())
return -1;
return kill(pid, sig);
}
The code has no single raise() or abort() calls.
I wonder which possible (maybe external) reasons might exist that can cause this program to receive SIGTERM from itself under Linux ?
See this discussion. The bottom line is that si_pid is meaningful in very few cases.
Related
I have the following code:
//includes...
#define SIG_INT 0
//prints out <Text> when SIG_INT is received from siginfo_t*
void handler(int, siginfo_t*, void*);
int main()
{
pid_t ambulance1 = 0;
pid_t ambulance2 = 0;
struct sigaction sigact;
sigact.sa_sigaction = handler;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = SA_SIGINFO;
sigaction(SIGUSR1, &sigact, NULL);
ambulance1 = fork();
if(ambulance1 > 0) {
ambulance2 = fork();
if(ambulance2 > 0) { // parent
int status;
waitpid(ambulance1, &status, 0);
waitpid(ambulance2, &status, 0);
printf("HQ went home!\n");
}
}
if(ambulance1 == 0 || ambulance2 == 0) {
union sigval signalValueInt;
signalValueInt.sival_int = SIG_INT;
sigqueue(getppid(), SIGUSR1, signalValueInt);
printf("Ambulance[%d] ended.\n", getpid());
}
return 0;
}
What happens is: sometimes the second ambulance's sigqueue(getppid(), SIGUSR1, signalValueInt); doesn't get received, and the output is something like the following:
Ambulance[20050] ended. // main() prints out this
Ambulance[20051] ended. // main() prints out this
// handler() prints out this with write() ONLY ONCE!
HQ went home! // main() prints out this
I know that the signal is lost, because the two signals arrived too quickly after one another, and the operating sys. thinks it's an error-duplicate, so it gets ignored.
My question is:
Is there a way to tell the operating system not to do that?
I wouldn't like to use two different signals (ex.: SIGUSR1 and SIGUSR2) for the same purpose, and I also wouldn't like to use delay in one of the child process.
The answer is in the manual page of signal(7).
But breafly:
if a standard signal arrives (like SIGUSR1) while the handler is running it will get ignored by the operating system.
if a real-time signal arrives (like SIGRTMIN) while handler is running it will be processed after the handler is done running.
I have tow handlers for each one of them (SIGTSTP, SIGCHLD), the thing is that when I pause a process using SIGTSTP the handler function of SIGCHLD run too. what should I do to prevent this .
signal handlers :
void signalHandler(int signal) {
int pid, cstatus;
if (signal == SIGCHLD) {
susp = 0;
pid = waitpid(-1, &cstatus, WNOHANG);
printf("[[child %d terminated]]\n", pid);
DelPID(&JobsList, pid);
}
}
void ctrlZsignal(int signal){
kill(Susp_Bg_Pid, SIGTSTP);
susp = 0;
printf("\nchild %d suspended\n", Susp_Bg_Pid);
}
Susp_Bg_Pid used to save the paused process id.
susp indicates the state of the "smash" the parent process if it is suspended or not .
Set up your SIGCHLD handler using sigaction with SA_NOCLDSTOP.
From sigaction (2)
SA_NOCLDSTOP - If signum is SIGCHLD, do not receive notification when child processes stop (i.e., when they receive one of SIGSTOP, SIGTSTP, SIGTTIN or SIGTTOU) or resume (i.e., they receive SIGCONT)(see wait(2)). This flag is only meaningful when establishing a handler for SIGCHLD.
update
void signalHandler(int sig)
{
//...
}
struct sigaction act;
act.sa_handler = signalHandler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_NOCLDSTOP;
if (sigaction(SIGCHLD, &act, 0) == -1)
{
perror("sigaction");
exit(1);
}
If you aren't familiar with sigaction you should read up on it because it has several different options and behaviors that are vastly superior to signal but come at the cost of complexity and confusion before you figure out how to use it. I took my best guess at the minimum of what you seem to want to do but you'll need to learn this sooner rather than later.
I'm working on a shell lab for a Systems course, and I've been having some really weird race condition bugs that I've been trying to solve since Friday night and can't seem to pin down.
My current code: http://buu700.com/tsh
Everything before START OF MY CODE and after END OF MY CODE is provided by the course instructors, so none of that should be the source of the issues.
We have a test script as well; here is the output of my current test results: http://buu700.com/sdriver
/*****************
* Signal handlers
*****************/
/*
* sigchld_handler - The kernel sends a SIGCHLD to the shell whenever
* a child job terminates (becomes a zombie), or stops because it
* received a SIGSTOP, SIGTSTP, SIGTTIN or SIGTTOU signal. The
* handler reaps all available zombie children, but doesn't wait
* for any other currently running children to terminate.
*/
void
sigchld_handler(int sig)
{
pid_t pid;
int status, termsig;
struct job_t *job;
sigset_t s;
sigemptyset(&s);
sigaddset(&s, SIGCHLD);
sigaddset(&s, SIGINT);
sigaddset(&s, SIGTSTP);
sigprocmask(SIG_BLOCK, &s, NULL);
while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) {
if (WIFEXITED(status)) {
deletejob(job_list, pid);
}
if ((termsig = WTERMSIG(status))) {
deletejob(job_list, pid);
safe_printf("Job [%i] (%i) %s by signal %i\n",
pid2jid(pid), pid, "terminated", termsig);
}
if (WIFSTOPPED(status)) {
job = getjobpid(job_list, pid);
job->state = ST;
safe_printf("Job [%i] (%i) %s by signal %i\n",
pid2jid(pid), pid, "stopped", SIGTSTP);
}
}
if (errno != ECHILD)
unix_error("waitpid error");
sigprocmask(SIG_UNBLOCK, &s, NULL);
return;
}
/*
* sigint_handler - The kernel sends a SIGINT to the shell whenver the
* user types ctrl-c at the keyboard. Catch it and send it along
* to the foreground job.
*/
void
sigint_handler(int sig)
{
sigset_t s;
sigemptyset(&s);
sigaddset(&s, SIGCHLD);
sigaddset(&s, SIGINT);
sigaddset(&s, SIGTSTP);
sigprocmask(SIG_BLOCK, &s, NULL);
kill(-1, sig);
sigprocmask(SIG_UNBLOCK, &s, NULL);
return;
}
/*
* sigtstp_handler - The kernel sends a SIGTSTP to the shell whenever
* the user types ctrl-z at the keyboard. Catch it and suspend the
* foreground job by sending it a SIGTSTP.
*/
void
sigtstp_handler(int sig)
{
sigset_t s;
sigemptyset(&s);
sigaddset(&s, SIGCHLD);
sigaddset(&s, SIGINT);
sigaddset(&s, SIGTSTP);
sigprocmask(SIG_BLOCK, &s, NULL);
kill(-1, sig);
sigprocmask(SIG_UNBLOCK, &s, NULL);
return;
}
I have a suspicion that the bug comes from your racing against other signals in your signal handlers:
sigint_handler(int sig)
{
sigset_t s;
sigemptyset(&s);
sigaddset(&s, SIGCHLD);
sigaddset(&s, SIGINT);
sigaddset(&s, SIGTSTP);
sigprocmask(SIG_BLOCK, &s, NULL);
When you register the signal handler with sigaction(2), you can provide an sa_mask that the kernel will use to block signals for you while your signal handler is running. This is done atomically and requires no extra work on your part. Simply populate this mask once when registering the signal handler.
Another possibility comes from the SIGTSTP signal; page 350 of my copy of APUE, 2nd edition says, in part:
Only a job-control shell should reset the disposition of [SIGTSTP, SIGTTIN, SIGTTOU] to SIG_DFL.
What's left unsaid in those paragraphs, but I think is fair to assume, is that the shell should set the signal disposition to SIG_DFL for the child processes -- it still needs to do something to handle the signals itself.
Did you properly handle the signal dispositions in the children?
I need to handle SIGCHLD properly. How can I use it with my existing code? at the moment I cant wait for the child process unless I use 0 instead of WNOHANG|WUNTRACED.
status = 0;
pid_t child, endID;
if(amp == 1)
signal( SIGCHLD, SIG_IGN );
child = fork();
if (child < 0) {
perror("fork() error\n");
exit(EXIT_FAILURE);
} else if (child == 0) {
// do sth here
perror("error\n");
} else {
//sleep(1)
If I remove sleep then parent is executed 1st.. why?
Here is a start (but read below):
static void
child_handler(int sig)
{
pid_t pid;
int status;
/* EEEEXTEERMINAAATE! */
while((pid = waitpid(-1, &status, WNOHANG)) > 0)
;
}
/* Establish handler. */
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = child_handler;
sigaction(SIGCHLD, &sa, NULL);
Of course, this is all pointless. If the parent simply ignores SIGCHLD, the children are silently reaped and won't turn into zombies.
Quoting TLPI:
Explicitly setting the disposition of SIGCHLD to SIG_IGN causes any
child process that subsequently terminates to be immediately removed
from the system instead of being converted into a zombie.
So something like this should do the trick for you:
signal(SIGCHLD, SIG_IGN); /* Silently (and portably) reap children. */
Suppose, i know the process of my parent id and would like to
kill(my_parent_id, SIGTERM)
As a parent process, how can i catch this signal?
Register to catch the signal:
void termination_handler(int sig)
{
/* do something */
}
struct sigaction handler;
handler.sa_handler = termination_handler;
sigemptyset (&handler.sa_mask);
handler.sa_flags = SA_RESTART;
sigaction(SIGTERM, &handler, NULL);
Here is a good example page.
You could use the old style, but it is not suggested:
void termination_handler()
{
/* do something */
}
signal(SIGTERM, termination_handler);