Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hello, I could use some help in getting IPC::Open3 to read from a network socket. The goal is to stream filesystem data to an external binary (zfs receive). I can read from the socket handle using angle brackets, and I can get open3 to work using a normal filehandle, but I can't combine the two and get open3 to read from the socket handle. Here is some distilled code:
#!/usr/bin/perl use warnings; use strict; use IO::Socket; use IPC::Open3; my $sock = new IO::Socket::INET ( LocalHost => '127.0.0.1', LocalPort => '1818', Proto => 'tcp', Listen => 1, ReuseAddr => 1, Timeout => 20 ); die "Could not create socket: $!\n" unless $sock; ## THIS WORKS (I can telnet to port 1818 from another terminal & see t +he text come across): #my $new_sock = $sock->accept() or die "No one came!"; #while ( <$new_sock> ) { print; } ## THIS WORKS (If I type "cat | ./<this_script>, I can type and see it + echo back): #my $pid = open3( "<&STDIN", ">&STDOUT", ">&STDERR", "/bin/cat" ); #waitpid( $pid, 0 ); # THIS DOES NOT WORK (using open3 to read from a network handle). my $new_sock = $sock->accept() or die "No one came!"; #my $pid = open3( $new_sock, ">&STDOUT", ">&STDERR", "/bin/cat" ); # c +an telnet to port but immediately disconnected my $pid = open3( $new_sock, $new_sock, ">&STDERR", "/bin/cat" ); # can + telnet to port but immediately disconnected waitpid( $pid, 0 ); close($sock);
I'm not particularly attached to using open3, I just want a way to connect my stream to to the external program. Am I missing something obvious or is this a limitation of open3? I've been avoiding IPC::Run so far as this will be copied to several computers & I'd prefer not to use a separate module. It's even fine if I can't redirect zfs receive's STDOUT or STDERR, as long as I can just connect STDIN and get the exit status. Thanks!

Replies are listed 'Best First'.
Re: IPC::Open3 not connecting network socket handle
by ikegami (Patriarch) on Jul 23, 2009 at 22:23 UTC

    The first three arguments of open3 are outputs. open3 creates pipes and assigns them to the provided variables (causing the handle they previously held to be closed).

    That is unless they hold a string that starts with "<&" (1st arg) or ">&" (2nd and 3rd args). When these are used, open3 use the specified handle instead of creating a pipe. Like in the example that worked, you need to use "<&" to tell open3 to use an existing handle.

    open3( '<&'.fileno($new_sock), ... )

    There is also a special meaning for the third arg when it's undef, but it's unrelated to the issue at hand.

    Update: Added explanation.

      Thanks for the suggestion - I didn't realize it wanted a file descriptor number. I tried "<&".fileno($new_sock) and now (on 5.8.4) it connects but I'm still having an issue:
      open3: close(5) failed: Bad file number at ./s5 line 32
      The cat subprocess actually continues -- but I realized this was actually a side benefit of the bug that salva referenced. I tested on perl 5.8.8 (previously 5.8.4) and indeed the subprocess was killed upon the close(5) failure.
        IPC::Open3 is already closing it for you. Just remove the close.
Re: IPC::Open3 not connecting network socket handle
by salva (Canon) on Jul 24, 2009 at 07:00 UTC
    You can do that without using IPC::Open3:
    my $new_sock = $sock->accept() or die "No one came!"; my $pid = fork; defined $pid or die "unable to fork"; if (!$pid) { open STDIN, '<&', $new_sock or exit(1); open STDOUT, '>&', $new_sock or exit(1); exec "/bin/cat"; exit(1); } waitpid( $pid, 0 );
      Sure, but it's longer, it removes or reduces error checking, and it lowers portability. (Granted, the last was already lost in this case, but it means you have two things to fix instead of one if you ever want portability.) Also, reinventing the wheel leads to problems such as using exit instead of _exit.
        well, mostly, I agree with what you say, but...

        This bug in IPC::Open3 that was only solved during the 5.8.x series has beating me a couple of times. If you want your script to work with older perls you have to put in place a workaround that will remove the shorten code advantage.

        IPC::Open3 inners are very complex. If something goes wrong there, debugging it could be a nightmare.

        Finally, I don't like this reminiscent 2-args-open syntax "<&FOO" that doesn't play well with lexical file handles. Using "<&".fileno($new_sock) is, well, ugly at least.

        So, considering all of this, I don't see IPC::Open3 as a clear winner when compared with the code in my previous post.