2

I have some code that forks a third-party application and redirects its standard output to the parent process, roughly as follows (no error handling here for brevity):

char* args[] = {"/path/to/3rd/party/binary", "with", "args", NULL};

int fds[2];
pipe2(fds, O_CLOEXEC);

pid_t pid = fork();
if (pid == 0)
{
    dup2(fds[1], STDOUT_FILENO);
    execvp(args[0], args);
    _exit(1);
}
else
{
    close(fds[1]);

    char buf[1024];
    int bytes_read;
    while ((bytes_read = read(fds[0], buf, sizeof buf - 1)) > 0)
    {
        buf[bytes_read] = '\0';
        printf("%s", buf);
    }

    close(fds[0]);
    waitpid(pid, NULL, 0);
}

I have no code for the third-party application, it is a proprietary binary. When running the third-party application in a terminal with the same arguments as used in the code above, it eventually finishes. However, when forking the third-party binary using the code above, it does not finish, but becomes a zombie process, and the code above hangs on the read() call.

The third-party binary that is forked itself forks two daemon processes (again, proprietary binaries I do not control), which I think is causing the problem here. The forked daemon processes will have a copy of the duplicated file descriptor, preventing the read() from finishing. Indeed, in case the dup2() call is replaced with:

dup3(fds[1], STDOUT_FILENO, O_CLOEXEC);

the child process finishes, but there is no output redirection to the parent process of course. Also, when the code above is modified to not do any output redirection to the parent over a pipe, the child process finishes correctly.

Is it possible to somehow prevent this hang on the read() call in this situation, or do I need to resort to some form of non-blocking I/O?

Update; using a simple popen() suffers from the same problem.

(Follow-up from: read() hangs on zombie process)

Ton van den Heuvel
  • 10,157
  • 6
  • 43
  • 82
  • 1
    An awful situation to be in. I'm not aware of any way (short of attaching a debugger) to prevent an unmodifiable application from leaking a FD. How about reading with a timeout and adding an angry comment? – that other guy Nov 30 '18 at 17:57
  • @thatotherguy, I am still hoping to avoid that :) A typical shell is able to handle this situation, so one last resort is to dig through some shell code to see what they do.. – Ton van den Heuvel Nov 30 '18 at 18:03
  • 1
    Perhaps you forgot to ignore SIGCHLD? It's your responsibility to reap the zombie. – David Schwartz Nov 30 '18 at 18:16

1 Answers1

2

You need to specifically ignore SIGCHLD. It's your responsibility to reap the zombie, but you can't do it when blocked in read. If you call read after the SIGCHLD has been swallowed, you'll stay in read forever.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • Works perfectly, thanks. For future reference, see also: https://stackoverflow.com/questions/40601337/what-is-the-use-of-ignoring-sigchld-signal-with-sigaction2 – Ton van den Heuvel Nov 30 '18 at 19:04