Skip to content
Advertisement

waitpid() gives incorrect exit code

I’ve got a function for starting a process, and then returning the stdout and exit code. However I’ve noticed that it claims that every process returns the exit code of 1. I control the executable being invoked and I had it print to stdout the exit code, so I’ve confirmed that when it “failed”, it in fact returned 0 from main. I also invoked the executable directly form the shell and confirmed the expected stdout and exit code (0). So the fault must lie on the side of the caller. I’ve also confirmed that WIFEXITED doesn’t return false- it returns true as if the child had exited normally (which it did).

This code worked fine before I needed to capture stdout, so it must have something to do with that. I tried looking into the “Child has already terminated” jobby, but that’s not occurring in this case- waitpid() behaves exactly like I’d expect and just doesn’t care that the child might have already terminated whilst I was nomming up the stdout.

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

Wide::Driver::ProcessResult Wide::Driver::StartAndWaitForProcess(std::string name, std::vector<std::string> args, Util::optional<unsigned> timeout) {
    int filedes[2];
    pipe(filedes);


    pid_t pid = fork();

    if (pid == 0) {
        while ((dup2(filedes[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {}
        freopen("/dev/null", "rw", stdin);
        freopen("/dev/null", "rw", stderr);
        //close(filedes[0]);

        std::vector<const char*> cargs;

        cargs.push_back(name.c_str());

        for (auto&& arg : args)

            cargs.push_back(arg.c_str());

        cargs.push_back(nullptr);

        execv(name.c_str(), const_cast<char* const*>(&cargs[0]));

    }
    std::string std_out;
    close(filedes[1]);
    char buffer[4096];
    while (1) {
        ssize_t count = read(filedes[0], buffer, sizeof(buffer));
        if (count == -1) {
            if (errno == EINTR) {
                continue;
            } else {
                perror("read");
                exit(1);
            }
        } else if (count == 0) {
            break;
        } else {
            std_out += std::string(buffer, buffer + count);
        }
    }
    close(filedes[0]);

    int status;
    ProcessResult result;
    result.std_out = std_out;
    waitpid(pid, &status, 0);

    if (!WIFEXITED(status))

        result.exitcode = 1;

    else {
        result.exitcode = WEXITSTATUS(status);
        if (result.exitcode != 0) {
            std::cout << name << " failed with code " << result.exitcode << "n";
            std::cout << "stdout: " << result.std_out;
        }
    }
    return result;
}

Why on earth is waitpid() giving me this strange result and how can I fix it?

Advertisement

Answer

I’ve confirmed in IRC that it is an LLVM issue. The exit code for the process I printed out is what I returned from main– a static destructor or other such code can still run and call exit(1). This is caused by redirecting stderr- so basically you can’t get the error, since if you don’t redirect stderr you don’t see the problem. So if you execute from the shell, since stderr is not redirected, it’s all good.

Therefore, despite the shell and my own return code agreeing, the process was in fact returning the exit code of 1.

Apparently the issue is resolved in trunk, or should be, but I am still using 3.6.

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement