Skip to content

Instantly share code, notes, and snippets.

@bfg bfg/gist:668326
Created Nov 8, 2010

Embed
What would you like to do?
Mojolicious async external command execution using POE (this blocks for no good reason)
#!/usr/bin/perl
# $Id: receiver_manage.pl 1977 2010-10-14 09:12:15Z bfg $
# $Date: 2010-10-14 11:12:15 +0200 (Thu, 14 Oct 2010) $
# $Author: bfg $
# $Revision: 1977 $
# $LastChangedRevision: 1977 $
# $LastChangedBy: bfg $
# $LastChangedDate: 2010-10-14 11:12:15 +0200 (Thu, 14 Oct 2010) $
# $URL: https://svn.interseek.com/repositories/admin/misc/cic/receiver_manage.pl $
use strict;
use warnings;
# Make reloading work
BEGIN { $INC{$0} = $0 }
# POE suff
sub POE::Kernel::USE_SIGCHLD () { 1 }
use POE;
use POE::Wheel::Run;
use POE::Filter::Stream;
use FindBin;
use IO::File;
use IPC::Open3;
use Mojo::IOLoop;
use Mojolicious::Lite;
use Time::HiRes qw(time);
my $_bin_dir = $FindBin::RealBin;
$ENV{PATH} .= ":" . $_bin_dir;
my $Error = '';
my $loop = Mojo::IOLoop->singleton();
# integrate POE with Mojo; this is sick!
$loop->tick_cb(sub { POE::Kernel->run_one_timeslice });
app->renderer->types->type(json => 'application/json; charset=utf-8');
####################################################
# FUNCTIONS #
####################################################
# returns siol_box_manage.pl configuration filename
# if it exists, otherwise undef.
sub manage_conf_get {
my $file = $FindBin::RealBin . '/config/siol_box_manage.conf';
return undef unless (-f $file && -r $file);
return $file;
}
# returns full command to spawn according to target
# and host
sub command_get {
my ($target, $host) = @_;
# sanitize vars (i know that mojo does this for me, but you can never be careful enough)
{
no warnings;
$target =~ s/[;\|\<\>]//g;
$host =~ s/[;\|\<\>]//g;
}
unless (length($target) > 0) {
$Error = "Undefined target.";
return undef;
}
unless (length($host) > 0) {
$Error = "Undefined host.";
return undef;
}
# command to spawn
my $cmd = 'siol_box_manage.pl';
# do we have configuration file?
my $conf = manage_conf_get();
$cmd .= ' -c ' . $conf if (defined $conf);
# add target and host
$cmd .= ' ' . $target . ' ' . $host;
return $cmd;
}
# renders fatal rest error message
sub render_err {
my ($self, $data) = @_;
my $res = $self->res();
$res->headers->header('Cache-Control', 'no-cache; max-age=0');
$res->code(503);
return $self->render_json($data);
}
####################################################
# URL HANDLERS #
####################################################
get '/rest/:host/:target' => [
host => qr/[\w\.\-]+/,
target => qr/[a-z_]+/
], => sub {
my ($self) = @_;
my $host = $self->param('host');
my $target = $self->param('target');
my $cmd = command_get($target, $host);
#my $cmd = "/tmp/test.sh";
if (! defined $cmd) {
return render_err(
$self,
{ ok => 0, error => $Error}
);
}
# data structure (holding stdout/stderr && stuff...)
my $s = {
time_started => time(),
stdout => '',
stderr => '',
exit_status => 1,
};
# create POE Session
POE::Session->create(
inline_states => {
_start => sub {
my $wheel = undef;
eval {
$wheel = POE::Wheel::Run->new(
Program => $cmd,
StdioFilter => POE::Filter::Stream->new(),
StdoutEvent => 'stdout',
StderrEvent => 'stderr',
CloseEvent => 'close',
);
};
# error starting subprocess?
if ($@) {
return render_err(
$self,
{ ok => 0, error => "Error starting external command: $@", }
);
}
app->log->debug("Spawned program as poe run wheel " . $wheel->ID() . " as pid: " . $wheel->PID());
$poe_kernel->sig_child($wheel->PID(), 'chld');
# save the run wheel to heap
$_[HEAP]->{wheel} = $wheel;
},
stdout => sub {
my ($data, $id) = @_[ARG0, ARG1];
$s->{stdout} .= $data;
},
stderr => sub {
my ($data, $id) = @_[ARG0, ARG1];
$s->{stderr} .= $data;
},
chld => sub {
my ($name, $pid, $exit_val) = @_[ ARG0, ARG1, ARG2 ];
# destroy poe run wheel
delete($_[HEAP]->{wheel});
my $duration = time() - $s->{time_started};
app->log->debug(
"External process $pid exited with exit status $exit_val after " .
sprintf("%-.3f msec.", $duration * 1000)
);
my $len_stdout = length($s->{stdout});
my $len_stderr = length($s->{stderr});
app->log->debug(" Read $len_stdout bytes of stdout and $len_stderr bytes of stderr.");
# do we have anything on stdout?
if ($len_stdout > 0) {
# stdout output is perl eval() compatible;
# convert stdout string to perl hashref...
my $struct = eval $s->{stdout};
if ($@) {
return render_err(
$self,
{ ok => 0, error => "Error evaluating external command output: $@"}
);
}
# we have valid output, YAY!!!
return $self->render_json($struct);
} else {
# nope... this is completely fucked up.
return render_err(
$self,
{ ok => 0, error => "No data in stdout, external command exit status $exit_val; stderr: $s->{stderr}"}
);
}
},
}
);
# this is it...
};
get '/' => sub {} => "/index";
# Start the Mojolicious command system
app->secret(rand());
app->start;
# EOF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.