Search code examples
clinuxforkwaitpid

How to wait for a child process and get its return value


I am trying to fork my C application, running on an embedded linux environment, and get its return value for failure/success analysis.

I looked at similar questions (e.g. this, this, this, this and some other Q/A..) but still can't get it to work.

code:

static int fork_test(const char *src, const char *dst)
{
int childExitStatus;
pid_t pid;
int status;

DEBUG_PRINT("forking");

pid = fork();

if (pid == 0) { // child
    sleep(2);
    DEBUG_PRINT("child - exiting");
    exit(1);
}
else if (pid < 0) {
    return -1;
}
else { // parent
    int i;
    for (i = 0 ; i < 10 ; i++)
    {
        pid_t ws = waitpid(pid, &childExitStatus, WNOHANG);
        if (-1 == ws)
        {
            DEBUG_PRINT("parent - failed wait. errno = %d", errno);
            return -1;
        }
        if (0 == ws)
        {
            DEBUG_PRINT("parent - child is still running");
            sleep(1);
            continue;
        }
    }
    if (10 == i)
        return -1;

    DEBUG_PRINT("parent - done waiting");

    if (WIFEXITED(childExitStatus)) /* exit code in childExitStatus */
    {
        DEBUG_PRINT("parent - got status %d", childExitStatus);
        status = WEXITSTATUS(childExitStatus); /* zero is normal exit */
        if (0 != status)
        {
            DEBUG_PRINT("parent - picked up bad exit status");
            return status;
        }
        return 0;
    }
    else
    {
        DEBUG_PRINT("parent - bad exit route");
        return -1;
    }
}
} 

This provided this output:

forking
parent - child is still running
parent - child is still running
parent - child is still running
child - exiting
parent - failed wait. errno = 10

note that errno=10 means ECHILD.

so i tried to add:

...
DEBUG_PRINT("forking");
signal(SIGCHLD,SIG_DFL);
pid = fork();
...

(or with SIG_IGN) with no difference. I can successfully add a signal handler for SIGCHLD, and might be able to wait for signal, instead of the child process, with sigwait() or the likes, but it seems like a bad solution..

Any idea what I'm missing here?

$ uname -mrso
Linux 3.18.20 armv7l GNU/Linux


Solution

  • Your code nicely tests for "errors". Good.

    But the code unfortunately misses to catch the case you are after, the one where waitpid() actually returns the child's PID.

    You could achieve this like so:

    for (i = 0 ; i < 10 ; i++)
    {
        pid_t ws = waitpid(pid, &childExitStatus, WNOHANG);
        if (-1 == ws)
        {
            DEBUG_PRINT("parent - failed wait. errno = %d", errno);
            return -1;
        }
        else if (0 == ws)
        {
            DEBUG_PRINT("parent - child is still running");
            sleep(1);
            continue;
        }
    
        DEBUG_PRINT("parent - successfully waited for child with PID %d", (int) ws);
    
        break;
    }