I am writing a C library that at some point forks another process and then waits for its completion.
I'd like to write the code that waits for the child process completion in the most robust and generic way, to take care of all possible scenarios, such as the calling process spawning other child processes, receiving signals etc.
Does the following C code use waitpid properly, i.e. in the most robust way?
void waitForChildProcess(int child_pid) {
int rc, err;
do {
//waiting only for my own child and only for its termination.
//The status value is irrelevant (I think) because option '0' should mean
//to only wait for a child termination event
// and I don't care about the child's exit code:
rc = waitpid(child_pid, NULL, 0);
err = errno;
} while (rc == -1 && err == EINTR); //ignoring a signal
}
Yes, waitpid(child_pid, ...) is the most robust way.
It will return child_pid if the child process has exited, -1 with errno set if an error occurs (ECHILD if the child process does not exist (was never created or has already been reaped) or is not a child of this process, EINVAL if the options (third parameter) had an invalid value, or EINTR if a signal was delivered to a signal handler that was not installed with SA_RESTART flags), or 0 if WNOHANG option (third parameter) was specified and the child process has not yet exited.
I would recommend a slight change, however:
/* Wait for child process to exit.
* @child_pid Process ID of the child process
* @status Pointer to where the child status
* is stored; may be NULL
* @return 0 if success
* -1 if an error occurs, see errno.
*/
int waitForChildProcess(pid_t child_pid, int *status)
{
int rc;
if (child_pid <= 1) {
errno = EINVAL;
return -1;
}
do {
rc = waipid(child_pid, status, 0);
} while (rc == -1 && errno == EINTR);
if (rc == child_pid)
return 0;
/* This should not happen, but let's be careful. */
if (rc != -1)
errno = ECHILD;
return -1;
}
In Linux and POSIXy systems, process ID's are positive integers. As you can see in the man 2 waitpid man page, zero and negative PIDs refer to process groups, and -1 to any child process. Process 1 is special, init; it is the one that never exits and sets up the rest of the userspace. So, the smallest PID a child of the current process can ever have is 2.
I do consider it sensible to use the proper types for these: pid_t for process IDs, and for example size_t for memory sizes of objects (including the return value of say strlen().)
Providing the status pointer (so that the caller can check it with WIFEXITED()+WEXITSTATUS() or WIFSIGNALED()+WTERMSIG()) is a convenience, since any callers not interested in it can provide a NULL. (NULL is explicitly allowed for the status pointer for wait() and waitpid().)
Technically, with options==0, waitpid() should only ever return either the child PID, or -1 (with errno set). However, since the check is so cheap, I prefer to treat everything else as an ECHILD error, since that gives the most robust results.
The caller is free to ignore the return value. However, if they want to know, the return value is 0 if successful, otherwise -1 with errno set (and strerror(errno) provides the textual reason).