re-launch piped external program

Discussion in 'Perl Misc' started by rihad, Sep 21, 2007.

  1. rihad

    rihad Guest

    Hello, Perl hackers,

    I'm writing a Perl script that runs FreeBSD's ipfw(8) and writes
    certain commands to it through a pipe, line by line:

    open OUT, "|-", "ipfw", "/dev/stdin" or die "$! $?";
    while (1) {
    print OUT $command, "\n";
    }

    Sometimes $command causes parse errors (due to manipulation of a
    nonexistent rule, or similar), and ipfw dies... Is there any way to
    catch and acknowledge that fact, and re-run myprog for subsequent
    iterations?
    rihad, Sep 21, 2007
    #1
    1. Advertising

  2. rihad

    Ben Morrow Guest

    Quoth rihad <>:
    > Hello, Perl hackers,
    >
    > I'm writing a Perl script that runs FreeBSD's ipfw(8) and writes
    > certain commands to it through a pipe, line by line:
    >
    > open OUT, "|-", "ipfw", "/dev/stdin" or die "$! $?";


    Don't use global filehandles. Use variable instead, and give them proper
    names:

    open my $IPFW, "|-", "ipfw", "/dev/stdin" or...

    > while (1) {
    > print OUT $command, "\n";


    You can avoid the need for this "\n" by setting $\.

    > }
    >
    > Sometimes $command causes parse errors (due to manipulation of a
    > nonexistent rule, or similar), and ipfw dies... Is there any way to
    > catch and acknowledge that fact, and re-run myprog for subsequent
    > iterations?


    If you attempt to write to a pipe which has been closed (say, because
    the process on the other end has died) your program will be sent a
    SIGPIPE. The default action for this is to terminate your program, but
    you can catch it (see perldoc perlipc) and recover. Alternatively, you
    can ignore it ($SIG{PIPE} = 'IGNORE') and check the return value from
    print. If it failed, and $! == EPIPE (EPIPE can be imported from the
    POSIX module), then your process has died. This may be simpler than
    trying to use a signal handler (signals don't really behave terribly
    well).

    Note that you should explicitly close the filehandle if the process
    dies, as that causes perl to wait for the child; if you don't, you'll
    start accumulating zombie processes.

    Ben
    Ben Morrow, Sep 21, 2007
    #2
    1. Advertising

  3. rihad

    Guest

    rihad <> wrote:
    > Hello, Perl hackers,
    >
    > I'm writing a Perl script that runs FreeBSD's ipfw(8) and writes
    > certain commands to it through a pipe, line by line:
    >
    > open OUT, "|-", "ipfw", "/dev/stdin" or die "$! $?";
    > while (1) {
    > print OUT $command, "\n";
    > }
    >
    > Sometimes $command causes parse errors (due to manipulation of a
    > nonexistent rule, or similar), and ipfw dies... Is there any way to
    > catch and acknowledge that fact, and re-run myprog for subsequent
    > iterations?


    If ipfw dies, then your process should be delivered a CHLD signal,
    which can be captured by putting code in $SIG{CHLD};

    my $foo;
    while (1) {
    $foo=1;
    $SIG{CHLD}=sub {$foo=0};
    open OUT, "|-", "ipfw", "/dev/stdin" or die "$! $?";
    while ($foo) {
    print OUT $command, "\n";
    }
    close OUT or warn "failed with $? $!";
    };

    There is no general reason to think that the program will die instantly
    or that the signal will be delivered instantly, so you may not fall out of
    the loop until several iterations after the $command that actually
    triggered the error. So you are playing with fire. (especially since you
    don't show us how $command ever changes, so there may be hidden problems
    there as well).

    Xho

    --
    -------------------- http://NewsReader.Com/ --------------------
    The costs of publication of this article were defrayed in part by the
    payment of page charges. This article must therefore be hereby marked
    advertisement in accordance with 18 U.S.C. Section 1734 solely to indicate
    this fact.
    , Sep 21, 2007
    #3
  4. rihad

    rihad Guest

    Thanks to you both, guys. I'll try the EPIPE trick as soon as I get
    over this small problem:

    $\ = "\n";
    $| = 1;
    open my $FW, "|-", "sudo", "ipfw", "/dev/stdin" or die "can't run
    ipfw: $!";
    $| = 1;
    print $FW 'add';
    sleep 10;
    exit;

    I gave ipfw erroneous input on purpose ('add' requires argument list).
    No matter what I tried: ipfw's pre-mortem line on stderr "Line 1:
    missing action" won't show up on the screen until after 10 seconds
    have passed. And I can see ipfw hanging there during that time from
    another console using ps. Looks like Perl holds on to the line for
    some reason. Someone care to explain what I did wrong?
    rihad, Sep 21, 2007
    #4
  5. rihad

    Guest

    rihad <> wrote:
    > Thanks to you both, guys. I'll try the EPIPE trick as soon as I get
    > over this small problem:
    >
    > $\ = "\n";
    > $| = 1;
    > open my $FW, "|-", "sudo", "ipfw", "/dev/stdin" or die "can't run
    > ipfw: $!";
    > $| = 1;
    > print $FW 'add';
    > sleep 10;
    > exit;
    >
    > I gave ipfw erroneous input on purpose ('add' requires argument list).
    > No matter what I tried: ipfw's pre-mortem line on stderr "Line 1:
    > missing action" won't show up on the screen until after 10 seconds
    > have passed. And I can see ipfw hanging there during that time from
    > another console using ps. Looks like Perl holds on to the line for
    > some reason. Someone care to explain what I did wrong?


    Maybe ipfw doesn't flush stderr until either the buffer is full or until
    its stdin gets closed, which doesn't happen here until the parent program
    exits.

    Xho

    --
    -------------------- http://NewsReader.Com/ --------------------
    The costs of publication of this article were defrayed in part by the
    payment of page charges. This article must therefore be hereby marked
    advertisement in accordance with 18 U.S.C. Section 1734 solely to indicate
    this fact.
    , Sep 21, 2007
    #5
  6. rihad

    rihad Guest

    > > have passed. And I can see ipfw hanging there during that time from
    > > another console using ps. Looks like Perl holds on to the line for
    > > some reason. Someone care to explain what I did wrong?

    >
    > Maybe ipfw doesn't flush stderr until either the buffer is full or until
    > its stdin gets closed, which doesn't happen here until the parent program
    > exits.
    >


    *Perl* holds on to the $command line even though I turned auto-flush
    on with $| = 1;
    As I said ipfw is hanging there all along, which wouldn't be the case
    if it got the flawed command from the pipe immediately.
    rihad, Sep 22, 2007
    #6
  7. rihad

    Peter Scott Guest

    On Fri, 21 Sep 2007 11:46:28 -0700, rihad wrote:
    > Thanks to you both, guys. I'll try the EPIPE trick as soon as I get
    > over this small problem:
    >
    > $\ = "\n";
    > $| = 1;
    > open my $FW, "|-", "sudo", "ipfw", "/dev/stdin" or die "can't run
    > ipfw: $!";
    > $| = 1;
    > print $FW 'add';
    > sleep 10;
    > exit;


    perldoc perlvar:

    $| If set to nonzero, forces a flush right away and after every
    write or print on the currently selected output channel.
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    You did not select $FW as the current output channel.

    --
    Peter Scott
    http://www.perlmedic.com/
    http://www.perldebugged.com/
    Peter Scott, Sep 22, 2007
    #7
  8. On Sat, 22 Sep 2007 11:39:51 GMT,
    Peter Scott <> wrote:
    > On Fri, 21 Sep 2007 11:46:28 -0700, rihad wrote:
    >> Thanks to you both, guys. I'll try the EPIPE trick as soon as I get
    >> over this small problem:
    >>
    >> $\ = "\n";
    >> $| = 1;
    >> open my $FW, "|-", "sudo", "ipfw", "/dev/stdin" or die "can't run
    >> ipfw: $!";
    >> $| = 1;
    >> print $FW 'add';
    >> sleep 10;
    >> exit;

    >
    > perldoc perlvar:
    >
    > $| If set to nonzero, forces a flush right away and after every
    > write or print on the currently selected output channel.
    >
    > You did not select $FW as the current output channel.


    This probably is a good spot to draw attention to IO::Handle's autoflush
    method. rather than selecting the file handle you want to flush, you set
    the autoflush attribute on those filehandles you want to flush. I find
    that generally fits better with the way I think it should work.

    Regards,
    Martien
    --
    |
    Martien Verbruggen | "In a world without fences,
    | who needs Gates?"
    |
    Martien verbruggen, Sep 22, 2007
    #8
  9. rihad

    rihad Guest

    > You did not select $FW as the current output channel.
    >


    Thank you! Worked like a charm. On a sidenote: I'm a bit new to Perl
    (surprise!), and I'm at chapter 4 of "Learning Perl" ("The Llama
    book") right now. Then I will read Programming Perl ("The Camel book")
    and tons of perldocs. I think perldocs are best at giving minute
    technical details, as you mentioned. Thanks again!
    rihad, Sep 22, 2007
    #9
  10. rihad

    rihad Guest

    How can I *easily* run an external program, writing commands to it on
    a filehandle, and reading its replies from another? Ironically,
    reading Perl FAQ [*] left one question unanswered: where's chat2.pl so
    much talked about? I'm asking someone to give a modern (v5.8.8)
    example.

    [*] http://www.faqs.org/faqs/perl-faq/part5/
    rihad, Sep 24, 2007
    #10
  11. rihad

    Guest

    rihad <> wrote:
    > How can I *easily* run an external program, writing commands to it on
    > a filehandle, and reading its replies from another?


    That is kind of like asking "How can I easily solve the 3 body problem
    of classical gravitational mechanics". You can't, in general. In this
    case, that is because it depends on the details of how the external command
    does its buffering, and whether it produces anything on stderr, and other
    things. If all ducks line up in a row properly, then it might be easy, but
    unless you know what "properly" means, which itself is not easy, then you
    won't know if it is easy or not.


    > Ironically,
    > reading Perl FAQ [*] left one question unanswered: where's chat2.pl so
    > much talked about?


    http://search.cpan.org/src/ANDYD/perl5.003_07/lib/chat2.pl

    > I'm asking someone to give a modern (v5.8.8)
    > example.


    Since the v5.8.8 doesn't talk about chat2, you shouldn't expect
    to find a 5.8.8 example of chat2.pl.

    > [*] http://www.faqs.org/faqs/perl-faq/part5/


    That is very very old FAQ.

    Xho

    --
    -------------------- http://NewsReader.Com/ --------------------
    The costs of publication of this article were defrayed in part by the
    payment of page charges. This article must therefore be hereby marked
    advertisement in accordance with 18 U.S.C. Section 1734 solely to indicate
    this fact.
    , Sep 24, 2007
    #11
  12. rihad

    Mumia W. Guest

    On 09/24/2007 01:29 PM, rihad wrote:
    > How can I *easily* run an external program, writing commands to it on
    > a filehandle, and reading its replies from another? Ironically,
    > reading Perl FAQ [*] left one question unanswered: where's chat2.pl so
    > much talked about? I'm asking someone to give a modern (v5.8.8)
    > example.
    >
    > [*] http://www.faqs.org/faqs/perl-faq/part5/
    >


    Use the IPC::Run module.
    Mumia W., Sep 25, 2007
    #12
  13. rihad

    rihad Guest

    On Sep 25, 6:08 am, "Mumia W." <paduille.4061.mumia.w
    > wrote:
    > On 09/24/2007 01:29 PM, rihad wrote:
    >
    > > How can I *easily* run an external program, writing commands to it on
    > > a filehandle, and reading its replies from another? Ironically,
    > > reading Perl FAQ [*] left one question unanswered: where's chat2.pl so
    > > much talked about? I'm asking someone to give a modern (v5.8.8)
    > > example.

    >
    > > [*]http://www.faqs.org/faqs/perl-faq/part5/

    >
    > Use the IPC::Run module.


    The aforementioned FAQ claims such open2 usage might cause deadlocks.
    But the FAQ's old (written in 1996). Is it still risky to use open2
    under FreeBSD?
    rihad, Sep 25, 2007
    #13
  14. rihad

    rihad Guest

    > The aforementioned FAQ claims such open2 usage might cause deadlocks.
    > But the FAQ's old (written in 1996). Is it still risky to use open2
    > under FreeBSD?


    Answering to myself: yes, it is :) I'm able to set things up with
    use IPC::Open2;
    my ($ipfw_in, $ipfw_out);
    my $ipfw_pid = open2($ipfw_in, $ipfw_out, 'sudo', 'ipfw', '/dev/
    stdin');
    print $ipfw_out 'add pipe 3 ip from 5.6.7.8 to any out' . "\n";
    print <$ipfw_in>;


    The last line hangs and deadlocks! Looks like ipfw doesn't flush its
    output. Luckily its sources are right here ready to be hacked.
    rihad, Sep 25, 2007
    #14
  15. rihad

    Mumia W. Guest

    On 09/25/2007 12:43 AM, rihad wrote:
    > On Sep 25, 6:08 am, "Mumia W." <paduille.4061.mumia.w
    > > wrote:
    >> On 09/24/2007 01:29 PM, rihad wrote:
    >>
    >>> How can I *easily* run an external program, writing commands to it on
    >>> a filehandle, and reading its replies from another? Ironically,
    >>> reading Perl FAQ [*] left one question unanswered: where's chat2.pl so
    >>> much talked about? I'm asking someone to give a modern (v5.8.8)
    >>> example.
    >>> [*]http://www.faqs.org/faqs/perl-faq/part5/

    >> Use the IPC::Run module.

    >
    > The aforementioned FAQ claims such open2 usage might cause deadlocks.
    > But the FAQ's old (written in 1996). Is it still risky to use open2
    > under FreeBSD?
    >


    Look at the IPC::Run documentation.

    http://search.cpan.org/search?query=IPC Run&mode=module

    Open2 can cause deadlocks depending upon what you're trying to do;
    IPC::Run has ways to get around that problem. Never read that FAQ file
    again; this is a much better Perl FAQ list.

    http://perldoc.perl.org/perlfaq.html
    Mumia W., Sep 25, 2007
    #15
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Andrew Tucker

    Piped stream help.

    Andrew Tucker, Oct 5, 2003, in forum: Java
    Replies:
    3
    Views:
    496
    Harald Hein
    Oct 6, 2003
  2. Ryan Stewart

    Wrapping Piped Streams?

    Ryan Stewart, Jan 11, 2004, in forum: Java
    Replies:
    10
    Views:
    1,859
    Ryan Stewart
    Jan 11, 2004
  3. joshivaibhav
    Replies:
    3
    Views:
    618
    joshivaibhav
    Nov 21, 2006
  4. Andrey Demidov

    launch external program

    Andrey Demidov, Jun 17, 2009, in forum: Ruby
    Replies:
    7
    Views:
    109
    Bertram Scharpf
    Jun 18, 2009
  5. Tim
    Replies:
    6
    Views:
    86
Loading...

Share This Page