re-launch piped external program

re-launch piped external program

Post by riha » Sat, 22 Sep 2007 22:05:37


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?
 
 
 

re-launch piped external program

Post by Ben Morro » Sun, 23 Sep 2007 01:42:48


Quoth rihad < XXXX@XXXXX.COM >:

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

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


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


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

 
 
 

re-launch piped external program

Post by xhoste » Sun, 23 Sep 2007 01:54:50


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://www.yqcomputer.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
adverti *** t in accordance with 18 U.S.C. Section 1734 solely to indicate
this fact.
 
 
 

re-launch piped external program

Post by riha » Sun, 23 Sep 2007 03:46:28

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 *** 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?
 
 
 

re-launch piped external program

Post by xhoste » Sun, 23 Sep 2007 06:23:34


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://www.yqcomputer.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
adverti *** t in accordance with 18 U.S.C. Section 1734 solely to indicate
this fact.
 
 
 

re-launch piped external program

Post by riha » Sun, 23 Sep 2007 16:11:32

> > have passed. And I can see ipfw *** there during that time from

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

re-launch piped external program

Post by Peter Scot » Sun, 23 Sep 2007 20:39:51


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.yqcomputer.com/
http://www.yqcomputer.com/
 
 
 

re-launch piped external program

Post by Martien ve » Sun, 23 Sep 2007 20:58:54

On Sat, 22 Sep 2007 11:39:51 GMT,



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?"
|
 
 
 

re-launch piped external program

Post by riha » Sun, 23 Sep 2007 21:18:53

> 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!
 
 
 

re-launch piped external program

Post by riha » Wed, 26 Sep 2007 03:29:21

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.yqcomputer.com/
 
 
 

re-launch piped external program

Post by xhoste » Wed, 26 Sep 2007 04:06:31


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.



http://www.yqcomputer.com/


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


That is very very old FAQ.

Xho

--
-------------------- http://www.yqcomputer.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
adverti *** t in accordance with 18 U.S.C. Section 1734 solely to indicate
this fact.
 
 
 

re-launch piped external program

Post by Mumia W » Wed, 26 Sep 2007 10:08:20


Use the IPC::Run module.
 
 
 

re-launch piped external program

Post by riha » Wed, 26 Sep 2007 14:43:20

On Sep 25, 6:08 am, "Mumia W." <paduille.4061.mumia.w



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?
 
 
 

re-launch piped external program

Post by riha » Wed, 26 Sep 2007 15:28:33

> The aforementioned FAQ claims such open2 usage might cause deadlocks.

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.
 
 
 

re-launch piped external program

Post by Mumia W » Wed, 26 Sep 2007 17:54:20


Look at the IPC::Run documentation.

http://www.yqcomputer.com/ +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://www.yqcomputer.com/