package OSify::Execute; =head1 NAME C -- Provides Access to the system Shell =head1 SYNOPSIS C> =head1 DESCRIPTION Purpose: Provide a consistent reusable loged common Perl command interface to any supported system shell. Currently only supports solaris and MSwin32 ( see L|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 security # # 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> Forks a process into the OS via C 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 to determine if it worked or not. =over 4 =item usage: C<%result = B> C<%result = ( stdout =E $stdout, stderr =E $stderr pid =E $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> 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> C<%result = ( stdout =E $stdout, stderr =E $stderr pid =E $pid );> =back =cut # $@ should NEVER report errors! Termination (die) is written as mandantory. 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 complete } }; if ( $@ && $i > $retry ) { die "OSify::OSexecute died upon execution of\n@execute\nWith $@"; } elsif ( $@ =~ /Resource temporarily unavailable/i ) { OSify::PerlFunc::debug("Failed to execute\n@execute\non attempt number $i\nResource temporarily unavailable.\n Retrying in $napTime 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|Open3>, L|Handle>, L|select> OSify Modules: L|PerlFunc>, L|OSify> =head1 Author =head1 See Also L|OSify>, L|FileFunc>, L|PerlFunc>, L|Ultimail> =head1 =cut =cut