in reply to Re^2: parent process stuck in read(2) on pipe opened to child process using backtick
in thread parent process stuck in read(2) on pipe opened to child process using backtick

Posting my findings with the hope that it would be of help to someone out there.

system and backtick both fork a child process. Backtick differs from system in that it opens a pipe for gathering the child process/command’s output. And this is what perl doc on fork has to say about file descriptors:

Any filehandles open at the time of the fork() will be dup()-ed. Thus, the files can be closed independently in the parent and child, but beware that the dup()-ed handles will still share the same seek pointer. Changing the seek position in the parent will change it in the child and vice-versa. One can avoid this by opening files that need distinct seek pointers separately in the child. On some operating systems, notably Solaris and Unixware, calling exit() from a child process will flush and close open filehandles in the parent, thereby corrupting the filehandles. On these systems, calling_exit() is suggested instead. _exit() is available in Perl through the POSIX module. Please consult your system's manpages for more information on this

So, whatever file descriptors/handles were open at the time of executing system/backtick are inherited by the child process/command. Which means processes like ntpd/dhcpd/named etc inherit all of the parent perl process' open file descriptors including any pipes opened as part of backtick.

Somehow ntpd, in my case, also inherited the write-end of the pipe which is the reason the parent process, which had the read-end of the pipe, was stuck in read(2) indefinitely because ntpd is a long running process. Now, the parent process closes the write-end of the pipe before forking but the whole operation of opening the pipe and closing the write-end is not atomic so .......

perl process

backtick

pipe()

<---------- GAP HERE

close() write-end of pipe

The solution I used was to write a perl wrapper script which closes all fds except 0/1/2 and then exec the command, like this:

`closed.pl service xyz restart`;

There are multiple ways of closing fds. I used the tips from the following discussion : http://www.perlmonks.org/?node_id=476086

Here's a link which describes a similar issue : http://tdistler.com/2010/06/18/stop-stealing-my-file-descriptors

  • Comment on Re^3: parent process stuck in read(2) on pipe opened to child process using backtick