Search code examples
cforkwaitpidexecv

execv and fork: inform parent that child failed to execute the file


How can the master process know that the child process failed to execute the file (e.g. no such file or directory)? For example, in the following code, how can we get run() to return something other than 0? Thanks!

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

enum ErrorType { noError, immediateFailure, returnFailure, forkFailure };

using namespace std;

int run(void)
{
    int error = noError;
    //char proc[] = "/bin/uname";
    char proc[] = "/usr/bin/Idontexist";
    char *const params[] = {proc, NULL};

    pid_t pid = fork();

    printf("pid = %d\n",pid);

    if (pid == 0)
    {
        if ( execv(proc,params)==-1 )
        {
            error = immediateFailure;
        }
        printf("child: error = %d\n",error);
    }
    else if (pid > 0)
    {
        /*  This is the parent process
         *  incase you want to do something
         *  like wait for the child process to finish
         */
        int waitError;
        waitpid(pid, &waitError, 0);

        if ( waitError )
            error = returnFailure;

        printf("parent: error = %d, waitError = %d\n",error,waitError);
    }
    else
    {
        error = forkFailure;
    }

    return error;
}

int main(void)
{
    printf("run() = %d\n",run());

    return 0;
}

output:

pid = 4286
pid = 0
child: error = 1
run() = 1
parent: error = 0, waitError = 0
run() = 0

Solution

  • You need to return the error code from main, or exit() with the value. Now you're returning 0 from main in all cases, and that will be the exit code of the child that the parent receives from waitpid.

    The best fix is to add _exit(error) in the child process:

    printf("child: error = %d\n",error);
    _exit(error);
    

    In any case use integer constants instead of random enum constants enum. 1 and 2 are common return values from many unix commands with 1 meaning that the command was not successful (for example grep didn't found any matches), and 2+ meaning that the command really failed (wrong arguments or such)`.

    bash, zsh and other shells use 127 as the exit code to signal command not found / not runnable; thus this would be recommended:

    #define COMMAND_NOT_RUNNABLE 127
    
    /* ... */
    
    execv(proc, params);  // exec never returns if successful.
    perror(proc);
    _exit(COMMAND_NOT_RUNNABLE);
    

    The reason to use _exit in the child process is that it doesn't flush the stdio buffers (that would exist on the parent side too) and that it doesn't run the atexit hooks. (Thanks Andrew Henle)