* Should parent's WIFSIGNALED(siginfo->si_status) be true EVEN IF the SIGNAL was caught by the child?
@ 2007-09-22 18:22 John Z. Bohach
2007-09-22 18:49 ` Michael Kerrisk
2007-09-22 18:52 ` Nicholas Miell
0 siblings, 2 replies; 6+ messages in thread
From: John Z. Bohach @ 2007-09-22 18:22 UTC (permalink / raw)
To: linux-kernel
Hello,
It is unclear from the various documentions in the kernel and glibc what
the proper behaviour should be for the case when a child process
catches a SIGNAL (say for instance, SIGTERM), and then calls exit()
from within its caught SIGNAL handler.
Since the exit() will cause a SIGCHLD to the parent, and the parent
(let's say) has a SIGCHLD sigaction (SA_SIGINFO sa_flags set), should
the parent's WIFSIGNALED(siginfo->si_status) be true?
To recap, the WIFSIGNALED section of the waitpid() manpage says:
WIFSIGNALED(status)
returns true if the child process was terminated by a signal.
So the dilemna: the child caught the signal, so it wasn't terminated by
a signal, but rather its signal handler (let's say) called exit.
Furthermore:
WTERMSIG(status)
returns the number of the signal that caused the child process
to terminate. This macro should only be employed if WIFSIGNALED
returned true.
Observered behaviour with 2.6.20.6 is that is WIFSIGNALED(status)
returns true (possibly incorrect), and furthermore, WTERMSIG(status)
returns the exit(VALUE) VALUE from the child's exit() call, and not the
SIGNAL (let's say SIGTERM that the child caught). This may be correct,
since the siginfo_t * si_status member is:
int si_status; /* Exit value or signal */
but there's no clarity on which: exit value or signal. Since the child
exited, I'm likely to assume exit status, which is current observed
behaviour, but then, WIFSIGNALED(status) should be FALSE, which its not
(observed with 2.6.20.6).
So could someone clarify the kernel's intent?
I can provide a short C program to illustrate above behaviour, if
needed. It also could be that I'm just misinterpreting the intent,
which is why I'm not calling this a bug, despite a possible
inconsistency in behaviour.
Thanks,
John
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: Should parent's WIFSIGNALED(siginfo->si_status) be true EVEN IF the SIGNAL was caught by the child?
2007-09-22 18:22 Should parent's WIFSIGNALED(siginfo->si_status) be true EVEN IF the SIGNAL was caught by the child? John Z. Bohach
@ 2007-09-22 18:49 ` Michael Kerrisk
2007-09-22 20:18 ` John Z. Bohach
2007-09-22 18:52 ` Nicholas Miell
1 sibling, 1 reply; 6+ messages in thread
From: Michael Kerrisk @ 2007-09-22 18:49 UTC (permalink / raw)
To: John Z. Bohach; +Cc: linux-kernel, mtk-manpages
John,
> It is unclear from the various documentions in the kernel and glibc what
> the proper behaviour should be for the case when a child process
> catches a SIGNAL (say for instance, SIGTERM), and then calls exit()
> from within its caught SIGNAL handler.
>
> Since the exit() will cause a SIGCHLD to the parent, and the parent
> (let's say) has a SIGCHLD sigaction (SA_SIGINFO sa_flags set), should
> the parent's WIFSIGNALED(siginfo->si_status) be true?
>
> To recap, the WIFSIGNALED section of the waitpid() manpage says:
>
> WIFSIGNALED(status)
> returns true if the child process was terminated by a signal.
>
> So the dilemna: the child caught the signal, so it wasn't terminated by
> a signal, but rather its signal handler (let's say) called exit.
>
> Furthermore:
>
> WTERMSIG(status)
> returns the number of the signal that caused the child process
> to terminate. This macro should only be employed if WIFSIGNALED
> returned true.
>
> Observered behaviour with 2.6.20.6 is that is WIFSIGNALED(status)
> returns true (possibly incorrect), and furthermore, WTERMSIG(status)
> returns the exit(VALUE) VALUE from the child's exit() call, and not the
> SIGNAL (let's say SIGTERM that the child caught). This may be correct,
> since the siginfo_t * si_status member is:
>
> int si_status; /* Exit value or signal */
>
> but there's no clarity on which: exit value or signal. Since the child
> exited, I'm likely to assume exit status, which is current observed
> behaviour, but then, WIFSIGNALED(status) should be FALSE, which its not
> (observed with 2.6.20.6).
>
> So could someone clarify the kernel's intent?
>
> I can provide a short C program to illustrate above behaviour, if
> needed. It also could be that I'm just misinterpreting the intent,
> which is why I'm not calling this a bug, despite a possible
> inconsistency in behaviour.
If the child terminated by calling exit(), regardless of whether it
was done from inside a signal handler, then WIFEXITED() should test
true, but WIFSIGNALED() will not. If you are seeing otherwise, then
show a *short* program that demonstrates the behavior. (But it seems
unlikely that there would be a kernel bug on this point, so do check
your program carefully!)
Cheers,
Michael
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: Should parent's WIFSIGNALED(siginfo->si_status) be true EVEN IF the SIGNAL was caught by the child?
2007-09-22 18:22 Should parent's WIFSIGNALED(siginfo->si_status) be true EVEN IF the SIGNAL was caught by the child? John Z. Bohach
2007-09-22 18:49 ` Michael Kerrisk
@ 2007-09-22 18:52 ` Nicholas Miell
1 sibling, 0 replies; 6+ messages in thread
From: Nicholas Miell @ 2007-09-22 18:52 UTC (permalink / raw)
To: John Z. Bohach; +Cc: linux-kernel
On Sat, 2007-09-22 at 11:22 -0700, John Z. Bohach wrote:
> Hello,
>
> It is unclear from the various documentions in the kernel and glibc what
> the proper behaviour should be for the case when a child process
> catches a SIGNAL (say for instance, SIGTERM), and then calls exit()
> from within its caught SIGNAL handler.
>
> Since the exit() will cause a SIGCHLD to the parent, and the parent
> (let's say) has a SIGCHLD sigaction (SA_SIGINFO sa_flags set), should
> the parent's WIFSIGNALED(siginfo->si_status) be true?
>
> To recap, the WIFSIGNALED section of the waitpid() manpage says:
>
> WIFSIGNALED(status)
> returns true if the child process was terminated by a signal.
>
> So the dilemna: the child caught the signal, so it wasn't terminated by
> a signal, but rather its signal handler (let's say) called exit.
POSIX says
WIFSIGNALED(stat_val)
Evaluates to a non-zero value if status was returned for a child
process that terminated due to the receipt of a signal that was
not caught (see <signal.h>).
So there's no dilemma at all and Linux is non-conformant.
--
Nicholas Miell <nmiell@comcast.net>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: Should parent's WIFSIGNALED(siginfo->si_status) be true EVEN IF the SIGNAL was caught by the child?
2007-09-22 18:49 ` Michael Kerrisk
@ 2007-09-22 20:18 ` John Z. Bohach
2007-09-22 23:35 ` Andreas Schwab
0 siblings, 1 reply; 6+ messages in thread
From: John Z. Bohach @ 2007-09-22 20:18 UTC (permalink / raw)
To: linux-kernel
[-- Attachment #1: Type: text/plain, Size: 1806 bytes --]
On Saturday 22 September 2007 11:49:09 Michael Kerrisk wrote:
> John,
>
...snip...
>
> If the child terminated by calling exit(), regardless of whether it
> was done from inside a signal handler, then WIFEXITED() should test
> true, but WIFSIGNALED() will not. If you are seeing otherwise, then
> show a *short* program that demonstrates the behavior. (But it seems
> unlikely that there would be a kernel bug on this point, so do check
> your program carefully!)
Attached is a (somewhat) short program that demonstates the behavior. I
simply compile it with 'make sigtest'.
My observed behavior is:
$ ./sigtest
sigtest started
child1 started
child2 started
selecting...
sigCaught: 3366 receieved signal 15
sigtest 3366 exiting
sigChld: 3365 receieved signal 17
sigChld: 3365 child 3366 WIFEXITED with childStat 15
sigChld: 3365 child 3366 WIFSIGNALED with si_status 15
select error: Interrupted system call
selecting...
sigCaught: 3367 receieved signal 15
sigtest 3367 exiting
sigChld: 3365 receieved signal 17
sigChld: 3365 child 3367 WIFEXITED with childStat 15
sigChld: 3365 child 3367 WIFSIGNALED with si_status 15
select error: Interrupted system call
selecting...
sigCaught: 3365 receieved signal 15
sigtest 3365 exiting
$
To get this output, I ran, from another shell, the following sequence:
$ ps -eaf | grep sigtest
zoltan 3365 2307 0 13:04 pts/0 00:00:00 ./sigtest
zoltan 3366 3365 98 13:04 pts/0 00:00:06 ./sigtest
zoltan 3367 3365 98 13:04 pts/0 00:00:06 ./sigtest
$ kill -SIGTERM 3366
$ kill -SIGTERM 3367
$ kill -SIGTERM 3365
That's it. What I find odd is that the wait_pid() status and the
si_status are the same for both a WIFEXITED and a WIFSIGNALED, which
should be impossible, if I read the documentation right.
Thanks for your responses,
John
[-- Attachment #2: sigtest.c --]
[-- Type: text/x-csrc, Size: 9801 bytes --]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <dirent.h>
#include <errno.h>
#include <time.h>
#include <pwd.h>
#include <signal.h>
#include <syslog.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/inotify.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/wait.h>
#define SER_PORT_ID 1234 /* The server's port */
static int child; /* child = 0, parent != 0 */
static void child1(void);
static void child2(void);
static void sigChld(int signum, siginfo_t * siginfo, void * ucontext);
static void sigCaught(int signum, siginfo_t * siginfo, void * ucontext);
static void setupEnv(void);
static void serverLoop(void);
static void processConnect(int csfd);
static void sigtestExit();
/**
* main:
* @argc: The count of command line data
* @argv: The array of command line strings
*
* The entry point of the executable.
*/
int
main(int argc, char * argv[])
{
setupEnv();
/*
* spawn two child processes so they (the children) can be sent SIGNAL's
* and the behaviour can be observed...
*/
if ((child = fork()) < 0) /* fork error */
{
perror("Child1 could not be created");
exit(1);
}
else
if (child == 0) /* In child */
{
child1(); /* never returns... */
}
if ((child = fork()) < 0) /* fork error */
{
perror("Monitor child could not be created");
exit(1);
}
else
if (child == 0) /* In child */
{
child2(); /* never returns... */
}
serverLoop(); /* Never returns */
return 0;
}
/**
* child1:
*
* This is the first child.
*/
static void
child1(void)
{
printf("child1 started\n");
for ( ; ; );
}
/**
* child2:
*
* This is the second child.
*/
static void
child2(void)
{
printf("child2 started\n");
for ( ; ; );
}
/**
* sigChild:
* @signum: The signal that got us here.
* @siginfo: Additional data associated with the signal.
* @ucontext: Pointer to ucontext_t struct. Unused here.
*
* Signal handler for catching SIGCHLD. Is re-entrant...
*/
static void
sigChld(int signum, siginfo_t * siginfo, void * ucontext)
{
int childStat;
printf("sigChld: %d receieved signal %d\n", getpid(), signum);
if (signum != SIGCHLD) /* technically can't happen here... */
return; /* but ignore it if it does */
waitpid(siginfo->si_pid, &childStat, 0); /* orderly child shutdown... */
/*
* These next 4 if blocks are where it gets interesting...this I believe
* demonstrates the inconsistency.
*/
if (WIFEXITED(childStat))
printf("sigChld: %d child %d WIFEXITED with childStat %d\n",
getpid(), siginfo->si_pid, WEXITSTATUS(childStat));
if (WIFEXITED(siginfo->si_status))
printf("sigChld: %d child %d WIFEXITED with si_status %d\n",
getpid(), siginfo->si_pid, WEXITSTATUS(siginfo->si_status));
if (WIFSIGNALED(childStat))
{
printf("sigChld: %d child %d WIFSIGNALED with childStat %d\n",
getpid(), siginfo->si_pid, WTERMSIG(childStat));
}
if (WIFSIGNALED(siginfo->si_status))
{
printf("sigChld: %d child %d WIFSIGNALED with si_status %d\n",
getpid(), siginfo->si_pid, WTERMSIG(siginfo->si_status));
}
return;
}
/**
* sigCaught:
* @signum: The signal that got us here.
* @siginfo: Additional data associated with the signal.
* @ucontext: Pointer to ucontext_t struct. Unused here.
*
* Signal handler for catching all but SIGCHLD. Is re-entrant...
*/
static void
sigCaught(int signum, siginfo_t * siginfo, void * ucontext)
{
int childStat;
printf("sigCaught: %d receieved signal %d\n", getpid(), signum);
if (signum == SIGCHLD) /* technically can't happen here... */
return; /* but ignore it if it does */
exit(signum);
}
/**
* setupEnv:
*
* Setup the environment, i.e, signal handlers, etc.
*/
static void
setupEnv(void)
{
struct sigaction action;
printf("sigtest started\n");
atexit(sigtestExit);
action.sa_handler = NULL;
action.sa_sigaction = sigChld;
sigemptyset(&action.sa_mask);
/*
* Since sigChld is re-entrant, its okay to have SA_NODEFER, and necessary,
* too, since I want to waitpid() on ALL child processes, and since
* waitpid() won't block if called after the child croaked, its just simply
* a way to affect an orderly shutdown of child processes, avoiding the
* zombie state...of course, I could be wrong, and without SA_NODEFER, the
* SIGCHLD is only _temporarily_ blocked, then I'll get subsequent SIGCHLDs
* after this handler completes...either way, in this case its irrelevant.
*/
action.sa_flags = SA_SIGINFO | SA_NOCLDSTOP | SA_RESTART | SA_NODEFER;
sigaction(SIGCHLD, &action, NULL);
/*
* Now catch the rest of the signals...
*/
action.sa_handler = NULL;
action.sa_sigaction = sigCaught;
sigfillset(&action.sa_mask);
action.sa_flags = SA_SIGINFO;
sigaction(SIGINT, &action, NULL);
sigaction(SIGHUP, &action, NULL);
sigaction(SIGTERM, &action, NULL);
sigaction(SIGQUIT, &action, NULL);
sigaction(SIGTSTP, &action, NULL);
sigaction(SIGSTOP, &action, NULL);
return;
}
/**
* serverLoop:
*
* The main loop for listening for and handling connections.
*/
static void
serverLoop(void)
{
fd_set read_fs;
int sfd, csfd, n, from_len, saved_errno;
struct sockaddr_in sin;
FD_ZERO(&read_fs); /* Clear the file descriptors */
if ((sfd = socket(AF_INET,SOCK_STREAM,0)) < 0) /* Create the socket */
{
perror("Socket creation failed");
exit(1);
}
n = 1;
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) < 0)
{
perror("setsockopt failed");
exit(1);
}
bzero((caddr_t)&sin,sizeof(struct sockaddr_in));/* Zero out the sock_addr */
sin.sin_family = AF_INET; /* Internet protocol */
sin.sin_addr.s_addr = INADDR_ANY; /* bind all local interfaces */
sin.sin_port = htons(SER_PORT_ID); /* Receive on the server port */
if (bind(sfd, (struct sockaddr *)&sin, sizeof(struct sockaddr_in)) < 0) /* Bind socket to port */
{
perror("Socket binding failed");
exit(1);
}
if (listen(sfd, 10) < 0) /* max. 10 q'd connections */
{
perror("Listen failed");
exit(1);
}
for ( ; ; ) /* main server loop */
{
FD_SET(sfd,&read_fs);
do
{
printf("selecting...\n");
n = select(sfd+1,&read_fs,NULL,NULL,NULL);
if (n < 0)
{
saved_errno = errno;
perror("select error");
if (saved_errno != EINTR)
{
perror("Server select error");
exit(1);
}
}
else
break; /* no error, so handle it... */
} while (saved_errno == EINTR); /* restart select on EINTR */
if (FD_ISSET(sfd, &read_fs))
{
from_len = sizeof(struct sockaddr_in);
printf("accepting...\n");
if ((csfd = accept(sfd, (struct sockaddr *)&sin,
(socklen_t *)&from_len)) < 0)
{
perror("Accept error");
exit(1);
}
if ((child = fork()) < 0) /* fork error */
{
perror("Child could not be created");
exit(1);
}
else
if (child == 0) /* In child */
{
close(sfd); /* Child doesn't need this */
processConnect(csfd); /* never returns... */
}
close(csfd); /* Parent doesn't need this one */
} /* if (FD_ISSET(sfd,&read_fs)) */
} /* for ( ; ; ) */
exit(99); /* Never gets here... */
}
/**
* processConnect:
* @csfd: The socket for this connection
*
* This is the child process dedicated to this connection.
*/
static void
processConnect(int csfd)
{
uint8_t * buf;
int n = 0;
if (!(buf = calloc((size_t)1024, sizeof(uint8_t))))
{
perror("calloc failed");
exit(1);
}
/*
* This means we're in the child, and have an active connection...
*/
printf("\nReading csfd %d...\n", csfd);
if ((n = read(csfd, buf, sizeof(buf))) < 0)
{
perror("Server read error");
exit(1);
}
if (n == 0)
{
printf("Connection closed...exiting\n");
exit(2);
}
printf("Read 0x%x bytes...\n", n);
buf[n] = 0;
printf("%s", buf);
// would do something with the data here, but this is just for testing...
close(csfd);
exit(0); /* child exits... */
}
/**
* sigtestExit:
*
* Since master server is exiting, everyone must be killed...
*/
static void
sigtestExit()
{
sigset_t sigset;
printf("sigtest %d exiting\n", getpid());
if (child) /* if the main server is exiting... */
{
sigfillset(&sigset);
sigprocmask(SIG_BLOCK,&sigset,NULL); /* block all sigs... */
kill(0,SIGTERM); /* everybody in this proc. group */
}
}
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: Should parent's WIFSIGNALED(siginfo->si_status) be true EVEN IF the SIGNAL was caught by the child?
2007-09-22 20:18 ` John Z. Bohach
@ 2007-09-22 23:35 ` Andreas Schwab
2007-09-23 1:05 ` John Z. Bohach
0 siblings, 1 reply; 6+ messages in thread
From: Andreas Schwab @ 2007-09-22 23:35 UTC (permalink / raw)
To: John Z. Bohach; +Cc: linux-kernel
"John Z. Bohach" <jzb2@aexorsyst.com> writes:
> if (WIFEXITED(siginfo->si_status))
That does not make any sense. si_status is _not_ a wait status.
Andreas.
--
Andreas Schwab, SuSE Labs, schwab@suse.de
SuSE Linux Products GmbH, Maxfeldstraße 5, 90409 Nürnberg, Germany
PGP key fingerprint = 58CA 54C7 6D53 942B 1756 01D3 44D5 214B 8276 4ED5
"And now for something completely different."
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: Should parent's WIFSIGNALED(siginfo->si_status) be true EVEN IF the SIGNAL was caught by the child?
2007-09-22 23:35 ` Andreas Schwab
@ 2007-09-23 1:05 ` John Z. Bohach
0 siblings, 0 replies; 6+ messages in thread
From: John Z. Bohach @ 2007-09-23 1:05 UTC (permalink / raw)
To: linux-kernel
On Saturday 22 September 2007 16:35:56 Andreas Schwab wrote:
> "John Z. Bohach" <jzb2@aexorsyst.com> writes:
> > if (WIFEXITED(siginfo->si_status))
>
> That does not make any sense. si_status is _not_ a wait status.
>
> Andreas.
Thank you for clearing that up. That explains it.
--john
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2007-09-23 1:05 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-09-22 18:22 Should parent's WIFSIGNALED(siginfo->si_status) be true EVEN IF the SIGNAL was caught by the child? John Z. Bohach
2007-09-22 18:49 ` Michael Kerrisk
2007-09-22 20:18 ` John Z. Bohach
2007-09-22 23:35 ` Andreas Schwab
2007-09-23 1:05 ` John Z. Bohach
2007-09-22 18:52 ` Nicholas Miell
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox