Below is a (very dirty) code that illustrates a bug on cygwin+POE,
related to sockets/filehandles. What is does is listen on a port, and
when it receives an input called "doit", it will reply with a short
message, shut down the connection, and execute an external program
with open3(). In other words, the program should continue to be
executed even after the connection is closed.
This works OK in linux, but under Cygwin, the the connection filehandle/socket
and the filehandles that are used to execute the external program are somehow
linked... the connection is only terminated after the program ends. In the
test case below, the program to be executed is just "/bin/ls; sleep 10;
/bin/ls".
Under cygwin, the socket stays alive until all the execution is finished. Under
linux, everything works OK, with the connection being close, and the execution
happenning independently.
Am i doing something wrong with the handles, or is this a cygwin bug ? Any
help appreciated.
#!/usr/local/bin/perl -w
#
use strict;
use POE;
use POE::Kernel;
use POE::Session;
use POE::Wheel::ReadWrite;
use POE::Driver::SysRW;
use POE::Filter::Stream;
use POE::Wheel::SocketFactory;
use Data::Dumper;
use Socket qw(inet_ntoa);
use IPC::Open3;
print "Beginning...\n";
POE::Session->create (
inline_states => {
_start => \&_start,
_stop => \&_stop,
gotConnectionInput => \&gotConnectionInput,
flushedConnection => \&flushedConnection,
errorConnection => \&errorConnection,
newConnection => \&newConnection,
runJob => \&runJob,
failedListening => \&failedListening,
Joberror => \&Joberror,
JobstdinFlushed => \&JobstdinFlushed,
JobstdoutInput => \&JobstdoutInput,
JobstderrInput => \&JobstderrInput
}
);
sub _start {
my ($heap, $kernel) = @_[HEAP, KERNEL];
print "yep, here\n";
my $listener = new POE::Wheel::SocketFactory (
BindPort => 2000,
Reuse => "yes",
SuccessEvent =>
"newConnection",
FailureEvent =>
"failedListening"
);
$heap->{listener} = $listener;
print "Created wheel\n";
}
sub _stop {
my ($heap, $kernel) = @_[HEAP, KERNEL];
print "Stopping\n";
}
sub gotConnectionInput {
my ($heap, $kernel) = @_[HEAP, KERNEL];
my ($input) = $_[ARG0];
print "[CONN] => $input\n";
if ($input =~ /doit/) {
$heap->{connection}->put("Got it, executing... bye\n");
$heap->{shutdownOK} = 1;
$kernel->yield("runJob");
}
}
sub runJob {
my ($heap, $kernel) = @_[HEAP, KERNEL];
print "Running job...\n";
local(*WRT);
local(*RD);
local(*ERR);
my $path = "/bin/ls -laF /; sleep 5; /bin/ls";
my $pid = open3(*WRT, *RD, *ERR, $path);
my $wrt = *WRT;
my $rd = *RD;
my $err = *ERR;
my $w;
my $w2;
$w = POE::Wheel::ReadWrite->new(
InputHandle => $rd,
OutputHandle => $wrt,
Driver => POE::Driver::SysRW->new(),
Filter => POE::Filter::Stream->new(),
ErrorEvent => "Joberror",
FlushedEvent => "JobstdinFlushed",
InputEvent => "JobstdoutInput",
);
$w2 = POE::Wheel::ReadWrite->new(
Handle => $err,
Driver => POE::Driver::SysRW->new(),
Filter => POE::Filter::Stream->new(),
ErrorEvent => "Joberror",
InputEvent => "JobstderrInput",
);
$heap->{stdin}{Wheel} = $w;
$heap->{stderr}{Wheel} = $w2;
}
sub JobstdoutInput {
my ($heap, $kernel) = @_[HEAP, KERNEL];
my ($input) = $_[ARG0];
print "[JOB-stdin] => $input";
}
sub JobstderrInput {
my ($heap, $kernel) = @_[HEAP, KERNEL];
my ($input) = $_[ARG0];
print "[JOB-stderr] => $input";
}
sub JobstdinFlushed {
my ($heap, $kernel) = @_[HEAP, KERNEL];
print "[JOB] Flushed.\n";
}
sub Joberror {
my ($heap, $kernel) = @_[HEAP, KERNEL];
print "[JOB] Error - the program probably ended.\n";
delete $heap->{stdin}{Wheel};
delete $heap->{stderr}{Wheel};
delete $heap->{listener};
}
sub flushedConnection {
my ($heap, $kernel) = @_[HEAP, KERNEL];
print "[CONN] Flushed.\n";
if ($heap->{shutdownOK}) {
print "The client connection should be closed now.\n";
delete $heap->{connection};
}
}
sub errorConnection {
my ($heap, $kernel) = @_[HEAP, KERNEL];
print "[CONN] Error.\n"; delete $heap->{wheel};
}
sub failedListening {
my ($self, $session, $heap, $kernel) = @_[OBJECT, SESSION, HEAP, KERNEL];
print "Inside failedListening\n";
exit(1);
}
sub newConnection {
my ($self, $session, $heap, $kernel) = @_[OBJECT, SESSION, HEAP, KERNEL];
my ($socket, $peeraddr, $peerport) = @_[ARG0 .. ARG2];
$peeraddr = inet_ntoa($peeraddr);
print "Inside newConnection: $peeraddr:$peerport\n";
my $rw = new POE::Wheel::ReadWrite (
Handle => $socket,
Driver => new POE::Driver::SysRW(),
Filter => new POE::Filter::Stream(),
InputEvent => "gotConnectionInput",
FlushedEvent => "flushedConnection",
ErrorEvent => "errorConnection"
);
$heap->{connection} = $rw;
}
$poe_kernel->run();
Cristiano Lincoln Mattos
Tempest Security Technologies - www.tempest.com.br
CESAR - Centro de Estudos e Sistemas Avan�ados do Recife - www.cesar.org.br