http://qs321.pair.com?node_id=489898

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

I recently read a great article on production-level scripting in the August 2005 issue of Sys Admin Magazine. In it, the author advocated the use of a template to make production-script rollout much faster. In an example template, the author included multiple levels of verbosity, options to email execution reports and error reports, and a test mode that would perform a test run of the code without actually committing to any changes. The example the author provided in the article was a bash shell script, but said the same techniques could be applied to perl or other administrative languages.

I have attempted to do the same thing in perl, but I have encountered 2 problems:

  1. I'm not happy with the method of process monitoring I've devised, but I cannot seem to come up with another. My method basically just prints out debugging messages (using a subroutine I'm calling 'debugger') at critical points during the script operation. I was hoping there might be some way that was less clunky and perhaps a bit more deliberate than that.
  2. My method to test whether the script ends cleanly or as the result of a die seems flawed. I'm checking the OS_ERROR and CHILD_ERROR variables for contents, but I'm finding that the OS_ERROR variable seems to always have some string in it whether the script ends cleanly or not. My objective is to only kick off the error email when the script encounters a die or a defined signal. Is there another way of seeing whether a script hits the END block because of a die or smoothly?

Any suggestions you have would be greatly appreciated! My template script in it's entirety is below:

#!/usr/bin/perl ############################################################## # Script : template # Author : roswell1329 # Date : 08/31/2005 # Last Edited: 09/07/2005, roswell1329 # Description: working template for fast production rollout ############################################################## # Purpose: # - create a working template with logging, and emailed # reports to make production development of scripts # faster # Requirements: # - error code checking # - different debugging/logging levels # - option for email execution report delivery # - option for email error delivery # - option for TEST (go through motions and logging with no # actual changes made # Method: # - make a script 'shell' with some of the standard options # already implemented # Syntax: template [-h] # template [-m email] [-e email] [-t] [-d] # template [-m email] [-e email] [-t] [-v] # Notes: # - not happy with execution logging or failure checking ############################################################## ###################################### #### Opening Initializations #### ###################################### use strict; use Getopt::Std; my $ex_status = 0; delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; $ENV{PATH} = '/bin:/usr/bin'; $SIG{INT} = sub { $ex_status = 1; die }; $SIG{PIPE} = sub { $ex_status = 2; die }; my $cmdname = (split /\//,$0)[-1]; my $basedir = (split /\/$cmdname/,$0)[0]; my $cfgfile = "$basedir/$cmdname.cfg"; my $logfile = "$basedir/$cmdname.log"; my $maillog = ""; my $thread = $$; my $user = (getpwuid $<)[0]; my %opts = (); my %configs = (); getopts('de:hm:tv', \%opts); &usage if ($opts{h}); &usage if (exists($opts{m}) && (!$opts{m})); &usage if (exists($opts{e}) && (!$opts{e})); &usage if (exists($opts{d}) && exists($opts{v})); if(-e $cfgfile) { open(CONFIG,"$cfgfile") or die "Cannot open config file $cfgfile\n"; foreach my $attrib (<CONFIG>) { next if ($attrib =~ /^$/); next if ($attrib =~ /^#/); my ($attrib_name,$attrib_val) = split /\s*=\s*/,$attrib; chomp($attrib_name,$attrib_val); $configs{$attrib_name} = $attrib_val; } &debugger("Config file found. Values loaded"); while(my($k,$v) = each %configs) { &debugger("|- $k attribute loaded with '$v'"); } } else { &debugger("No config file found. Proceeding with internal values"); } ###################################### #### Main Loop #### ###################################### &debugger("Initializations complete"); &debugger("Starting main routine"); ###################################### #### Support Functions #### ###################################### sub debugger($) { if(!exists($opts{v}) && !exists($opts{d})) { return; } my $dbstring = shift @_; my ($dsec,$dmin,$dhour,$dday,$dmon,$dyear,$dwday)=(localtime)[0..6]; my $monname = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug', 'Sep','Oct','Nov','Dec')[$dmon]; my $dayname = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat')[$dwday]; open(LOG,">>$logfile") or die "Cannot open logfile $logfile\n"; if(exists($opts{d})) { warn "[DEBUG]: $dbstring\n"; $maillog .= $dbstring . "\n" if ($opts{m}); printf LOG "%s %s %02d %02d:%02d:%02d thread[%5d]: %s\n", + ($dayname,$monname,$dday,$dhour,$dmin,$dsec,$thread,$dbstring); } elsif(exists($opts{v})) { next if($dbstring =~ /\|-/); $maillog .= $dbstring . "\n" if ($opts{m}); printf LOG "%s %s %02d %02d:%02d:%02d thread[%5d]: %s\n", ($dayname,$monname,$dday,$dhour,$dmin,$dsec,$thread,$dbstring); } close(LOG); } sub mail_report($$) { my ($mesg,$mailsubj) = @_; my $mailfrom = $user; open(MAIL, "| sendmail -oi -t") or die "Cannot start sendmail\n"; print MAIL "To:$opts{m}\nFrom:$mailfrom\nSubject:$mailsubj\n\n"; print MAIL $mesg; close(MAIL); } sub usage { print <<EOF; Usage: $cmdname [-h] $cmdname [-m email] [-e email] [-t] [-v] $cmdname [-m email] [-e email] [-t] [-d] Options: -d debugging mode. This mode will verbosely describe the current execution of $cmdname. -e [EMAIL] error reporting. This option will send any errors detected by $cmdname to email address EMAIL. -h help. This option will display this help message. -m [EMAIL] execution reporting. This option will send a $cmdname execution report to the supplied email address. -t test mode. This allows you to run the script without making any critical changes. -v verbose mode. This option will enable logging for this execution of $cmdname EOF } END { $ex_status > 0 ? &debugger("Program killed!") : &debugger("Exiting normally"); &debugger("Mailing reports"); &mail_report($maillog,"$cmdname Execution Report") if ($opts{m}); if($opts{e}) { if(($!) || ($?)) { my $mesg = "Internal Failures:\n------------------\n$!\n\n" . "External Failures:\n-----------------\n$?\n"; &mail_report($mesg,"$cmdname Error Report"); } } exit; }