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
#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 */
}
}
[Index of Archives]
[Kernel Newbies]
[Netfilter]
[Bugtraq]
[Photo]
[Stuff]
[Gimp]
[Yosemite News]
[MIPS Linux]
[ARM Linux]
[Linux Security]
[Linux RAID]
[Video 4 Linux]
[Linux for the blind]
[Linux Resources]