Capturing exit status code of child process

后端 未结 1 1898
礼貌的吻别
礼貌的吻别 2021-02-07 10:34

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

相关标签:
1条回答
  • 2021-02-07 10:43

    You can get the exit status of the child via the first argument of wait(), or the second argument of waitpid(), and then using the macros WIFEXITED and WEXITSTATUS with it.

    For instance:

    pid_t ret = c2b_popen4("myprog", pin, pout, perr, 0);
    
    if ( ret > 0 ) {
        int status;
    
        if ( waitpid(ret, &status, 0) == -1 ) {
            perror("waitpid() failed");
            exit(EXIT_FAILURE);
        }
    
        if ( WIFEXITED(status) ) {
            int es = WEXITSTATUS(status);
            printf("Exit status was %d\n", es);
        }
    }
    

    A simplified working example:

    failprog.c:

    int main(void) {
        return 53;
    }
    

    shellex.c:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
    int main(void)
    {
        pid_t p = fork();
        if ( p == -1 ) {
            perror("fork failed");
            return EXIT_FAILURE;
        }
        else if ( p == 0 ) {
            execl("/bin/sh", "bin/sh", "-c", "./failprog", "NULL");
            return EXIT_FAILURE;
        }
    
        int status;
        if ( waitpid(p, &status, 0) == -1 ) {
            perror("waitpid failed");
            return EXIT_FAILURE;
        }
    
        if ( WIFEXITED(status) ) {
            const int es = WEXITSTATUS(status);
            printf("exit status was %d\n", es);
        }
    
        return EXIT_SUCCESS;
    }
    

    Output:

    paul@thoth:~/src/sandbox$ ./shellex
    exit status was 53
    paul@thoth:~/src/sandbox$ 
    

    waitpid() will block until the process with the supplied process ID exits. Since you're calling your function with a popen() 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 pass WNOHANG as the third parameter to waitpid() to check if the process has terminated, and to return 0 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 call waitpid() with WNOHANG immediately after returning from c2b_popen4(), it may return 0 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 the read() or write() 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 (calling waitpid() in a signal handler is also safe) and getting its exit status.

    0 讨论(0)
提交回复
热议问题