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

I have a legacy application which is reached on tcp port 7000.
The connection is normally handled by inetd launching a shellscript with all the right arguments to start this app.
And I thought an easy way to test different arguments without inflicting production use would be to simply write a perl script doing:

1. use IPC::Open2
2. start the app with open2(), thereby grabbing its STDIN/STDOUT
3. communicating with the app through the handles I got from open2

Well, I was overly optimistic. It seems it can not be accomplished that way. My perl script just hangs on, waiting...

The applications is using 0x0D as input (and output) record separator. So I have changed $/ to that. I have also asked for autoflush:
select $writer;
$|++;
But no.

For what it is worth: When I try connecting through a ssh connection I can communicate with the application. I am on Solaris 10, if that matters.

Am I missing something obvious?
Or should this commanl line connection be done in a different way?
Or is it not even possible?

Thanks in advance for your insights,
/L

Replies are listed 'Best First'.
Re: Networking without a network
by zentara (Cardinal) on Feb 23, 2010 at 11:49 UTC
    From my limited experience in simple network connections, the first problem you hit, is why isn't this bi-directional? why is it locked up?

    See UNO perl examples for a great commented walkthru of basic socket code.

    Here is a basic bidirectional socket connection.

    #!/usr/bin/perl -w use strict; use IO::Socket; my ( $host, $port, $kidpid, $handle, $line ); #unless ( @ARGV == 2 ) { die "usage: $0 host port" } ( $host, $port ) = @ARGV || ('localhost',4242); # create a tcp connection to the specified host and port $handle = IO::Socket::INET->new( Proto => "tcp", PeerAddr => $host, PeerPort => $port ) or die "can't connect to port $port on $host: $!"; $handle->autoflush(1); # so output gets there right away print STDERR "[Connected to $host:$port]\n"; # split the program into two processes, idetical twins die "can't fork: $!" unless defined( $kidpid = fork() ); # the if{} block runs only in the parent process if ($kidpid) { # copy the socket to standard output while ( defined( $line = <$handle> ) ) { print STDOUT $line; } kill( "TERM", $kidpid ); # send SIGTERM to child } # the else{} block runs only in the child process else { # copy standard input to the socket while ( defined( $line = <STDIN> ) ) { print $handle $line; } }

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku
      Thank you zentara, a good link and a good example concerning socket programming. I'll save it for future reference.
      But I must have stated my problem badly. What I really want to do is running our app from the command line. Not through inetd, i.e. not by connecting to port 7000 (in this case).

      I can write a small perl script communicating to this application connecting to tcp port 7000 etc etc. But I am trying to do something different now.

      I may have misunderstood almost everything, but my approach was to hook inte the app's STDIN/STDOUT assuming those are the handles to hook. Let me illustrate with some code.

      #!/usr/local/bin/perl use strict; use warnings; use IPC::Open2; use IO::Handle; $/ = "\r"; my $CR = "\r"; my $NL = "\n"; my @msg = ('INIT', 'STATUS', 'QUIT' ); my ($reader, $writer) = (IO::Handle->new, IO::Handle->new); my @program_and_arguments = ('/usr/local/script/netdiag'); eval { open2($reader, $writer, @program_and_arguments); }; if ($@) { if ($@ =~ /^open2/) { warn "open2 failed: $!\n$@\n"; return; } die; } select $writer; $|++; select STDERR; $|++; select STDOUT; $|++; print STDERR "Here 1\n"; my $req = shift @msg; print $writer $req, $CR; print STDERR "Here 2\n"; # We come here, than it han +gs my $answer = <$reader>; die "1" unless $answer; print STDERR "SVR:$answer$NL"; [... keep up the dialog...] print STDOUT "Done$NL"; __END__
      /L
        There are a few programs around, that don't behave well with pipes, when they were meant to be run from a terminal. Maybe you have one there?

        Some commonly tried approaches are usually attempted. First try a sysread instead of <> on $reader, and use IO::Select to detect data in the pipeline.

        #pseudocode my $data while ($select->can_read){ if ( (my $length = sysread $reader, $data, 4096) > 0) { # do something with $data } }

        You might also want to test out a different IPC module, which may work better than IPC2. See I got IPC::Open3 to work! and Script hangs when called via IPC::Open3 and IPC3 buffer limit problem and Script hangs when called via IPC::Open3 for example.

        The IPC3 links above have code showing how to combine IO::Select with sysread....... that is your best first shot. The basic idea should work with IPC2 also. The last resort, if select and sysread fail, is to use a pty, like in Re: monitoring memory load.

        We really need to know what this program does. What does it expect on input, and what output does it deliver? Somehow, your data just sits in the pipeline buffer. You might also try the Expect module.


        I'm not really a human, but I play one on earth.
        Old Perl Programmer Haiku
        Your comment suggests that the program is waiting for /usr/local/script/netdiag to write something.

        Which may mean the "problem" (or rather, the fix) lies in said program - not in your small script.

        Considering this is a networking program - perhaps netdiag is expecting $CR$LF as line endings, not just $CR (why $CR there, not $LF?). It hasn't seen a full line yet, so it's not going to write anything.

        But I must have stated my problem badly.

        No.... I must have read your problem based only on the first paragraph.... sorry. my bad. But my other answer is good. :-) use sysread + select


        I'm not really a human, but I play one on earth.
        Old Perl Programmer Haiku
Re: Networking without a network
by salva (Canon) on Feb 23, 2010 at 12:42 UTC
    You are probably been affected by some kind of buffering that does not happen when talking to a real socket.

    Instead of using IPC::Open2::open2, you can try rolling your own version of open2 on top of socketpair (IPC::Open2 uses pipes instead of sockets).

      Thanks for the suggestion, salva.
      And when you say
      rolling your own version of open2
      does that involve some C programming?
      (Then I will be out on even thinner ice than I am now!)
      /L
        You can do it in Perl. perlipc has a sample of socketpair usage.