I have a function that forks a process, duplicates file descriptors for input and output buffers, and then runs execl
on a command passed in via a string called cmd
:
static pid_t
c2b_popen4(const char* cmd, int pin[2], int pout[2], int perr[2], int flags)
{
pid_t ret = fork();
if (ret < 0) {
fprintf(stderr, "fork() failed!\n");
return ret;
}
else if (ret == 0) {
/*
Assign file descriptors to child pipes (not shown)...
*/
execl("/bin/sh", "/bin/sh", "-c", cmd, NULL);
fprintf(stderr, "execl() failed!\n");
exit(EXIT_FAILURE);
}
else {
/*
Close parent read and write pipes (not shown)...
*/
return ret;
}
return ret;
}
Each of the cmd
instances process my data correctly, so long as my test inputs are correct.
When bad data is passed to a child process, my parent program will run to completion and exit with a non-error status code of 0.
If I deliberately put in bad input — to purposefully try to get one of the cmd
instances to fail in an expected way — I'd like to know how to capture the exit status of that cmd
so that I can issue the correct error status code from the parent program, before termination.
How is this generally done?
You can get the exit status of the child via the first argument of
wait()
, or the second argument ofwaitpid()
, and then using the macrosWIFEXITED
andWEXITSTATUS
with it.For instance:
A simplified working example:
failprog.c
:shellex.c
:Output:
waitpid()
will block until the process with the supplied process ID exits. Since you're calling your function with apopen()
name and passing pipes to it, presumably your child process doesn't terminate quickly, so that probably wouldn't be the right place to check it, if the call succeeded. You can passWNOHANG
as the third parameter towaitpid()
to check if the process has terminated, and to return0
if the child has not yet exited, but you have to be careful about when you do this, since you get no guarantees about which process will run when. If you callwaitpid()
withWNOHANG
immediately after returning fromc2b_popen4()
, it may return0
before your child process has had a chance to execute and terminate with an error code, and make it look as if the execution was successful when it's just about to not be successful.If the process does die immediately, you'll have problems reading from and writing to your pipes, so one option would be to check
waitpid()
if you get an error from the first attempt to do that, to check if theread()
orwrite()
is failing because your child process died. If that turns out to be true, you can retrieve the exit status and exit your overall program then.There are other possible strategies, including catching the SIGCHLD signal, since that'll be raised whenever one of your child processes dies. It would be OK, for instance, to call
_exit()
right from your signal handler, after waiting for the child process (callingwaitpid()
in a signal handler is also safe) and getting its exit status.