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

I'm using a slightly modified version the example POE forking job server found here http://poe.perl.org/?POE_Cookbook/Child_Processes_3 (added statements to print responses to a irc user or channel). Running Perl 5.8.8 on Suse Linux 10. The 1st sub returns its data from a sql query just fine, the 2nd bit of code appears to work except no data structure seems to be returned. This code works fine.
{ binmode(STDOUT); # Required for this to work on MSWin32 my $cmd = shift; my $nick = shift; my $who = shift; my $where = shift; my $channel=shift; my $args=shift; my $msg=shift; my %hash = %$args; my $data; my $filter = POE::Filter::Reference->new(); my $debug="true"; # Code goes Below # use DBI; use Date::Manip; # You need to return $status for the bot log and # $result for the user. You don't need any Print commands. my ($search) = $msg =~ /^!$cmd (.+)/; $search = trim($search); my $date = UnixDate(ParseDate($search), '%m/%d/%y %I:%M:%S%p %Z (% +z)'); if ($debug){print "cmd[$cmd] nick[$nick] who[$who] where[$where] c +hannel[$channel] args[$args] search[$search] date[$date]\n"}; my $title = $public_handlers{$cmd}->{'args'}->{'title'}; my $database = $public_handlers{$cmd}->{'args'}->{'database'}; my $user = $public_handlers{$cmd}->{'args'}->{'user'}; my $password = $public_handlers{$cmd}->{'args'}->{'password'}; my $table = $public_handlers{$cmd}->{'args'}->{'table'}; my $select = $public_handlers{$cmd}->{'args'}->{'select'}; my $header = $public_handlers{$cmd}->{'args'}->{'header'}; my $input = $public_handlers{$cmd}->{'args'}->{'input'}; my $sqlfilter = $public_handlers{$cmd}->{'args'}->{'filter'}; my $datenow = `date +"%Y-%m-%d %H:%M:%S"`; chomp($datenow); $select =~ s/\$date_now/$datenow/g; if ($debug){print "title[$title] database[$database] user[$user] p +assword[$password] table[$table] select[$select] header[$header] inpu +t[$input] sqlfilter[$sqlfilter]\n"}; my $result = "Performing SQL Query for [$select]\n $title\n"; my $status = "Performing SQL Query for [$select]\n $title\n"; # return results my %result = ( task => $cmd, status => $status, reply_to => $nick, + result => $result ); my $output = $filter->put( [ \%result ] ); print @$output; # query db my @row; my $dbh = DBI->connect("DBI:mysql:$database:localhost:3306","$user +", "$password", { RaiseError => 1, AutoCommit => 1 }); my $sth = $dbh->prepare(qq!SELECT * FROM $table WHERE $select!); $sth->execute; my $x=0; while ((@row) = $sth->fetchrow_array) { my $line= join(" ", map {defined $_ ? $_ : ""} @row); $status = "[$x] $line\n"; $result = "[$x] $line\n"; # return results %result = ( task => $cmd, status => $status, reply_to => $nick +, result => $result ); $output = $filter->put( [ \%result ] ); print @$output; $x++; if ($x > 9){last}; } $sth->finish; $dbh->disconnect; $status = "Done.\n"; $result = "Done.\n"; # Code Goes Above # # return results %result = ( task => $cmd, status => $status, reply_to => $nick, re +sult => $result ); $output = $filter->put( [ \%result ] ); print @$output; }
This code does not seem to return its data... I can see in the log that the ssh command does seem to be successful.
{ # forking plugin to run a command on a remote server via ssh # non interactive and buffered. output returns after command completes binmode(STDOUT); # Required for this to work on MSWin32 my $cmd = shift; my $nick = shift; my $who = shift; my $where = shift; my $channel=shift; my $args=shift; my $msg=shift; my %hash = %$args; my $data; my $filter = POE::Filter::Reference->new(); my $cmdargs=""; my ($plugin, $user, $pass, $host, $cmdx, @args)=split /\s+/, $msg; # print "nick[$nick] who[$who]\n"; # print "got ($plugin, $user, $pass, $host, $cmdx, @args)\n"; for my $arg (@args){ $cmdargs .= " " . $arg; } use Net::SSH::Perl; my $ssh = Net::SSH::Perl->new($host, options => [ "BatchMode yes", "RhostsAuthentication no", "Debug true" ]); $ssh->login($user, $pass); my ($stdout, $stderr, $exit) = $ssh->cmd($cmdx, $cmdargs); chomp($stdout);chomp($stderr); # remove (translate) non-printable chrs $exit =~ tr/\x80-\xFF//d; $stderr =~ tr/\x80-\xFF//d; $stdout =~ tr/\x80-\xFF//d; my $status = "exit[$exit] stderr[$stderr] stdout[$stdout]\n"; my $result = $status; # Code Goes Above # # return results print "task[$cmd] status[$status] reply_to[$nick] Result[$resu +lt]\n"; my %result = ( task => $cmd, status => $status, reply_to => $n +ick, result => $result ); my $output = $filter->put( [ \%result ] ); print @$output; }
Any ideas? Did I make some dumb error in the coding or do I need some POE filter? I'm just not seeing it. Sorry if the code is ugly... its a prototype. Thanks

Replies are listed 'Best First'.
Re: Data structures not returned via POE::Filter::Reference
by rcaputo (Chaplain) on Apr 01, 2008 at 18:16 UTC

    As I see it, your plugin is calling print("task....") which is not in the POE::Filter::Reference format. The parent process is trying to parse the child's output in a particular format, which it's not getting. As a result, the parent isn't displaying the output.

    You can use STDERR as a plaintext back channel. Change the print() to warn(), and set up the POE::Wheel::Run object to use POE::Filter::Line for its StderrFilter:

    $_[HEAP]{child} = POE::Wheel::Run( ... stuff ..., StdinFilter => POE::Filter::Reference->new(), StdoutFilter => POE::Filter::Reference->new(), StderrFilter => POE::Filter::Line->new(), );
    Untested, of course. :)

    Then handle StderrEvent in the parent. As an added bonus, any errors or warnings in the child will be displayed by the parent. It's great for debugging the child process.

      With the print statements commented it is still not returning data from certain plugins (LDAP queries and SSH sessions) but I do see all parts of the data were printed into the nohup.out log. Here is an example of the return data (I did a cat on a remote /etc/passwd file) as viewed with vi. The data is returned from a plugin using Net::SSH2 since Net::SSH::Perl seems to be broken now on my dev boxes.
      ^@^@^@^Fresult147^@^E^G^C^@^@^@^D ^Fswares^@^@^@^Hreply_to )[ssh2] halt:x:7:0:halt:/sbin:/sbin/halt ^@^@^@^Fstatus ^Dssh2^@^@^@^Dtask )[ssh2] halt:x:7:0:halt:/sbin:/sbin/halt