While there's much that could be cleaned up, and in spite of my initial gut feeling in Open3 and bad gut feeling, This open 3 module has provided me what you seek in several large enterprise applications without any problems.
Here's the code as it's been running in production without change for the last 4 years
package OSify::Execute;
=head1 NAME
C<OSify::Execute> -- Provides Access to the system Shell
=head1 SYNOPSIS
C<B<use OSify::Execute;>>
=head1 DESCRIPTION
Purpose: Provide a consistent reusable loged common Perl command
interface to any supported system shell. Currently only supports solar
+is
and MSwin32 ( see L<C<OSify::OSify>|OSify> ).
=cut
use diagnostics;
use strict;
######################################################################
+########
# #
# This Module is the bottom line in package OSify:: This is where the
+ #
# duct tape directly acceses the OS. Consequently, for purposes of sec
+urity #
# and extensibility with other Teams || Departments, this code and in
+ #
# particular this module, are heavily annotated. (More notes than code
+) #
# #
# Needs: Currently lacks a security wrapper. This module at times has
+an #
# effective ID of root. A package OSify::SecurityAPI (LDAP?) should be
+ #
# pursued whenever possible. See Notes with each function as to short
+ and #
# longterm improvements. #
# #
######################################################################
+########
# Standard Perl libraries
use IPC::Open3; # for OSexecute
use IO::Handle '_IOFBF'; # for OSexecute
use IO::Handle '_IOLBF';
use IO::Select;
# This module currently requires the following functions
use Exporter();
our @EXPORT = qw[ ];
our @EXPORTOK = qw[ OSrun ];
=pod
=head2 C<B<OSrun()>>
Forks a process into the OS via C<OSify::Execute::OSexecute()>
It returns the pid, stdout and stderr in a hash, logging the
execution to the specified logfile.
Intended as the common interface to the execution of the command.
This is the only exported function which somewhat forces commands
requesting execution to be 'wrapped'. Currently it acts as an optional
wrapper to the logger but future functionality ( LDAP? ) should be
integrated either here or in a similiar module.
Any Calling program must handle the C<STDERR> to determine if it worke
+d or not.
=over 4
=item usage:
C<%result = B<OSexec("OSrun", "logfile");>>
C<%result = ( stdout =E<gt> $stdout, stderr =E<gt> $stderr pid =E<gt>
+$pid );>
=back
=cut
######################################################################
+########
# Potential problem. Learning how to either disable or take advantage
# of Perl's Filehandle buffering. While the latter would provide
# obvious performance benifits. Could run into problems with the
# writing to the logfile writing otherwise. ( Refer: Suffering from
# Buffering http://perl.plover.com/FAQs/Buffering.html )
sub OSrun {
my $execute = $_[0];
my $logfile = $_[1];
my $silent = 0;
if ( $_[2] ) {
$silent = $_[2];
}
# See if execution would be too large for the input
# buffer. If so Create and execute a script instead.
my @execute;
@execute = split /\s/, $execute;
# Interface the logger
if ( $logfile && $silent ) {
if ( $silent > 0 ) {
OSify::OSify::OSlogger( $logfile, "Executing $execute", 1
+);
}
}
# Send the command to the executioner
my %OSreturn;
%OSreturn = OSexecute( @execute );
# Return the output
return %OSreturn;
}
=pod
=head2 B<C<OSexecute()>>
Forks a process into the OS returning the pid, stdout and stderr in a
+hash.
Intended to be strictly the minimum required functionality to execute
+a command
and return all of its relevant information.
=over 4
=item usage:
C<%result = B<OSexecute("Commandline");>>
C<%result = ( stdout =E<gt> $stdout, stderr =E<gt> $stderr pid =E<gt>
+$pid );>
=back
=cut
# $@ should NEVER report errors! Termination (die) is written as manda
+ntory.
sub OSexecute {
my @execute = @_;
my $stdin = IO::Select->new();
$stdin->add("\*STDIN");
my $din = IO::Handle->new();
$din->autoflush(1);
$stdin->add($din);
my $stdout = IO::Select->new();
$stdout->add(\*STDOUT);
my $dout = IO::Handle->new();
$dout->autoflush(1);
$stdout->add($dout);
my $stderr = IO::Select->new();
$stderr->add(\*STDERR);
my $derr = IO::Handle->new();
$derr->autoflush(1);
$stderr->add($derr);
OSify::PerlFunc::debug("Executing @execute\n", 10 );
my $pid = 1;
# Here is the actual execution
my $val = -1;
# This eval is looped to allow for retries when intermitant
# Resouce temporarily unavailable problems occur.
my ($i, $retry, $napTime);
$napTime = 5;
$retry = 3;
for ( $i=1; $i<=$retry; $i++ ) {
eval {
$pid = open3( $din, $dout, $derr, @execute );
while ( $val != -1 ) {
# waitpid waits for the proces to exit
# $val could be used as a means to determine status
# while waiting if that functionality becomes needed.
$val = waitpid(-1,0); #wait's for process to comple
+te
}
};
if ( $@ && $i > $retry ) {
die "OSify::OSexecute died upon execution of\n@execute\nWi
+th $@";
} elsif ( $@ =~ /Resource temporarily unavailable/i ) {
OSify::PerlFunc::debug("Failed to execute\n@execute\non at
+tempt number $i\nResource temporarily unavailable.\n Retrying in $nap
+Time secs\n", 100);
undef($@);
sleep($napTime);
} elsif ( ! $@ ) {
$i = $retry;
undef($@);
}
}
# Gather the results
my $line;
# Standard Out
my @stdout = <$dout>;
my $out;
foreach $line (@stdout) {
OSify::PerlFunc::debug( "@execute STDOUT processing \$line = $
+line", 10);
$line = OSify::PerlFunc::trimSpaces($line);
$out = $out . $line . "\n";
}
if ( ! $out ) {
$out = 1;
}
# Standard Error
my @stderr = <$derr>;
my $err;
foreach $line ( @stderr ) {
OSify::PerlFunc::debug( "@execute STDERR processing \$line = $
+line", 10);
$line = OSify::PerlFunc::trimSpaces($line);
$err = $err . $line . "\n";
}
if ( ! $err ) {
$err = 1;
}
# Flush and close the Filehandles
$din->flush();
$din->close;
$dout->flush();
$dout->close;
$derr->flush();
$derr->close;
undef $stdin;
undef $stdout;
undef $stderr;
my %OSexec = (
stdout => $out,
stderr => $err,
pid => $pid,
);
return %OSexec;
}
1;
=pod
=head1 REQUIRES
Standard Perl Modules:
L<C<IPC::Open3>|Open3>, L<C<IO::Handle>|Handle>, L<C<IO::Select>|selec
+t>
OSify Modules:
L<C<OSify::PerlFunc>|PerlFunc>, L<C<OSify::OSify>|OSify>
=head1 Author
=head1 See Also
L<C<OSify::OSify>|OSify>, L<C<OSify::FileFunc>|FileFunc>,
L<C<OSify::PerlFunc>|PerlFunc>, L<C<OSify::Ultimail>|Ultimail>
=head1
=cut
=cut
Edited by planetscape - added readmore tags
( keep:0 edit:11 reap:0 )