Search code examples
clinuxprocessfork

Kill child process when execvp fails in C function


I have a function in C that creates a child process and uses execvp() to execute a certain task.

pid_t pid = fork();
if (pid == 0) {
  if (execvp(cmd->arg_array[0], cmd->arg_array) == -1) {
    exit(-1);
    return ERROR;
  }
} else if (pid < 0)
  return ERROR;

//Some more code...
return NO_ERROR;

As you can see, if the task fails, the process containing it should be deleted and the function returned with ERROR value.

I tried implementing this functionality with an exit(-1) (and _exit() also), but the return statement is never reached, since the code is in the parent process zone. Also, if I remove the exit() and let the function return the error, the process keeps existing and interferes with the program functioning. How can I kill the created child process before the function returns when execvp task fails?


Solution

  • the process containing it should be deleted and the function returned with ERROR value.

    No, that's not what happens. A successful fork() creates a new process and returns 0 to this new process. In you example, execvp is executed in this new process and if it fails, you immediately do exit(-1);, at which point the process terminates and the line return ERROR; will never be reached.

    You can collect the child's exit status in the parent process by using wait or waitpid. Use WIFEXITED/WEXITSTATUS, WIFSIGNALED/WTERMSIG, WIFSTOPPED/WSTOPSIG and WIFCONTINUED to unpack the information about how it exited (or stopped/continued).

    Example:

    pid_t pid = fork();
    
    if (pid == 0) {
        execvp(cmd->arg_array[0], cmd->arg_array);
        // there's no need to check the return value from execvp:
        // if execvp returns, it means that it failed.
        _exit(1); // use _exit() instead of exit() to abort when exec fails
    } else if (pid == -1)
        return ERROR;
    }
    
    // parent
    int wstatus;
    pid_t child;
    
    while( (child = wait(&wstatus)) != -1 ) {
        printf("child %ld died with status %d\n", (long)child, wstatus);
    }
    

    Note: The function _exit() is like exit(), but does not call any functions registered with atexit() or on_exit() and you probably wouldn't want those to be called just because execvp failed.

    When a child process is created via fork(), it inherits copies of its parent's on_exit registrations. Upon a successful call to one of the exec() functions (like execvp), all registrations are removed.