Diagnosis
I think you must be running the process with the SIGCHLD signal ignored.
When I run your code normally, I get output such as:
$ ./wp0
I am the child
I am the parent
My pid is 80161
My childs's pid is 80162
My childs's exit status is 0
$
But, if I run it with the SIGCHLD signal ignored, I get:
$ (trap '' CHLD; ./wp0)
I am the child
I am the parent
My pid is 80435
My childs's pid is -1
My childs's exit status is 0
$
You can check this, perhaps, by running 'trap' at the shell command line. For example, I get:
$ (trap '' CHLD; trap)
trap -- '' SIGCHLD
$
If using trap
(with no arguments) doesn't produce an answer analogous to that, then you may need to write a program to interrogate the SIGCHLD status.
Incidentally, one of the first things I did (before going back to your original code), was change the parent's code to:
int corpse = wait(&status);
printf("I am the parent\n");
printf("My pid is %i\nMy child's pid was %i\nMy child's exit status is %i\n", getpid(), pid, status);
printf("My child's corpse was %i\n", corpse);
It captures the return value from wait()
separately from the return value from fork()
. Also, I normally print the exit status using 0x%.4X
— in part because I'm anal-retentive about the format for hex values but mainly because the hex value is easier to understand. You can experiment with the child exiting with different values (perhaps interpreting a command-line argument), and even with it committing suicide with different signals. While the WIFEXITED
, WEXITSTATUS
, WIFSIGNALED
, WTERMSIG
etc macros are good, they also are also quite simple and you can see what they do by looking at the hex status
.
Creating a test bed for exit statuses and signals
I extended your program to create a working test harness that allows you to experiment with exit values and signal handling like this:
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int c_status = 0;
int c_signal = 0;
int opt;
while ((opt = getopt(argc, argv, "s:e:")) != EOF)
{
switch (opt)
{
case 's':
c_signal = atoi(optarg);
break;
case 'e':
c_status = atoi(optarg);
break;
default:
fprintf(stderr, "Usage: %s [-e status][-s signal]", argv[0]);
exit(EXIT_FAILURE);
}
}
sigset_t oset;
if (pthread_sigmask(SIG_BLOCK, 0, &oset) != 0)
{
fprintf(stderr, "%s: pthread_sigmask() failed (%d: %s)\n",
argv[0], errno, strerror(errno));
exit(EXIT_FAILURE);
}
if (sigismember(&oset, SIGCHLD))
printf("SIGCHLD is listed in the signal mask\n");
struct sigaction oact;
if (sigaction(SIGCHLD, 0, &oact) != 0)
{
fprintf(stderr, "%s: pthread_sigmask() failed (%d: %s)\n",
argv[0], errno, strerror(errno));
exit(EXIT_FAILURE);
}
if (oact.sa_handler == SIG_DFL)
printf("SIGCHLD handling is the default\n");
else if (oact.sa_handler == SIG_IGN)
printf("SIGCHLD handling is ignoring the signal\n");
else
printf("SIGCHLD has a signal handler installed (%p)\n", (void *)oact.sa_handler);
if (oact.sa_flags & SA_NOCLDSTOP)
printf("SA_NOCLDSTOP is set\n");
if (oact.sa_flags & SA_NOCLDWAIT)
printf("SA_NOCLDWAIT is set\n");
int pid = fork();
if (pid < 0)
{
return -1;
}
else if (pid == 0)
{
printf("I am the child - PID %d\n", (int)getpid());
if (c_signal == 0)
{
printf("I am exiting with status %d\n", c_status);
exit(c_status);
}
else
{
printf("I am committing suicide with signal %d\n", c_signal);
raise(c_signal);
printf("That didn't work - I'm still here. Oh well!\n");
exit(EXIT_FAILURE);
}
}
else if (pid > 0)
{
int status;
int corpse = wait(&status);
printf("I am the parent\n");
printf("My pid is %i\n", getpid());
printf("My child's pid was %i\n", pid);
printf("My child's corpse was %i\n", corpse);
printf("My child's exit status is 0x%.4X\n", status);
if (WIFEXITED(status))
printf("My child exited normally with status %d\n", WEXITSTATUS(status));
if (WIFSIGNALED(status))
{
printf("My child died from signal %d\n", WTERMSIG(status));
#ifdef WCOREDUMP
if (WCOREDUMP(status))
printf("My child produced a core dump\n");
#endif
}
}
return 0;
}
I called the program wp2
. Running it a few times with different options gave output like this:
$ ulimit -c unlimited
$ ./wp2 -s 4
SIGCHLD handling is the default
I am the child - PID 81188
I am committing suicide with signal 4
I am the parent
My pid is 81187
My child's pid was 81188
My child's corpse was 81188
My child's exit status is 0x0004
My child died from signal 4
$ ./wp2 -s 6
SIGCHLD handling is the default
I am the child - PID 81224
I am committing suicide with signal 6
I am the parent
My pid is 81223
My child's pid was 81224
My child's corpse was 81224
My child's exit status is 0x0086
My child died from signal 6
My child produced a core dump
$ ulimit -c 0
$ ./wp2 -s 6
SIGCHLD handling is the default
I am the child - PID 81489
I am committing suicide with signal 6
I am the parent
My pid is 81488
My child's pid was 81489
My child's corpse was 81489
My child's exit status is 0x0006
My child died from signal 6
$ (trap '' CHLD; ./wp2 -s 6)
SIGCHLD handling is ignoring the signal
SA_NOCLDWAIT is set
I am the child - PID 82300
I am committing suicide with signal 6
I am the parent
My pid is 82299
My child's pid was 82300
My child's corpse was -1
My child's exit status is 0x0000
My child exited normally with status 0
$ ./wp2 -e 6
SIGCHLD handling is the default
I am the child - PID 82434
I am exiting with status 6
I am the parent
My pid is 82433
My child's pid was 82434
My child's corpse was 82434
My child's exit status is 0x0600
My child exited normally with status 6
$
The fact that it is easy to interpret the raw status is not an excuse to avoid using the macros provided by POSIX. Note that WCOREDUMP
is not specified by POSIX, but is usually available.