execvp/fork -- how to catch unsuccessful executions?

Kris H. picture Kris H. · Dec 4, 2012 · Viewed 7.1k times · Source

Right now I'm writing a C program that must execute a child process. I'm not doing multiple child processes simultaneously or anything, so this is fairly straightforward. I am definitely executing the built-in shell programs (i.e. things like cat and echo) successfully, but I also need to be able to tell when one of these programs fails to execute successfully. I'm trying this with the following simplified code:

int returnStatus; // The return status of the child process.
pid_t pid = fork();

if (pid == -1) // error with forking.
{
   // Not really important for this question.
}
else if (pid == 0) // We're in the child process.
{
   execvp(programName, programNameAndCommandsArray); // vars declared above fork().

   // If this code executes the execution has failed.
   exit(127); // This exit code was taken from a exec tutorial -- why 127?
}
else // We're in the parent process.
{
   wait(&returnStatus); // Wait for the child process to exit.

   if (returnStatus == -1) // The child process execution failed.
   {
      // Log an error of execution.
   }
}

So for example, if I try to execute rm fileThatDoesntExist.txt, I would like to consider that a failure since the file didn't exist. How can I accomplish this? Also, while that execvp() call successfully executes built-in shell programs, it doesn't execute programs in the current directory of the executable (i.e. the program that this code is running inside of); Is there something else that I have to do in order to get it to run programs in the current directory?

Thanks!

Answer

This is a classic problem with a very elegant solution. Before forking, create a pipe in the parent. After fork, the parent should close the writing end of the pipe, and block attempting to read from the reading end. The child should close the reading end and set the close-on-exec flag, using fcntl, for the writing end.

Now, if the child calls execvp successfully, the writing end of the pipe will be closed with no data, and read in the parent will return 0. If execvp fails in the child, write the error code to the pipe, and read in the parent will return nonzero, having read the error code for the parent to handle.