Re: Should parent's WIFSIGNALED(siginfo->si_status) be true EVEN IF the SIGNAL was caught by the child?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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]
  Powered by Linux