#! /usr/bin/perl use Mail::Sendmail; use File::Spec; use Sys::Hostname; use strict; use Pod::Usage; ############################################################################# ## PARAMETERS YOU HAVE TO CUSTOMIZE: ############################################################################# # Don't forget to use ' instead of " to avoid interpretation of # @ as an array in the email addresses! my %mail = ( Smtp => 'localhost', To => 'someone@host.domain' ); ############################################################################# ## PARAMETERS YOU CAN CUSTOMIZE: ############################################################################# $mail{From} = 'Perl script <'.$mail{To}.'>'; ############################################################################# #print complete usage if first argument is '-h' #note that we don't use standard option like Getopt because we have #to process only the first arguments beginning by a '-'. #For example, in 'eml -O command -h' we have to ignore -h since it applies #to command and not to eml! my ($discard_output, $discard_error) = (0,0); my $comment; while($ARGV[0]=~m/^\-/) { my $arg = shift @ARGV; $arg =~ s/^\-//; if($arg eq "c" || $arg eq "-comment") { # the next argument conains a comment to add in the email $comment = shift(@ARGV); next; } if($arg eq "t" || $arg eq "-to") { # the next argument conains a comment to add in the email $mail{To} = shift(@ARGV); next; } if($arg eq "s" || $arg eq "-smtp") { # the next argument conains a comment to add in the email $mail{Smtp} = shift(@ARGV); next; } if($arg !~ m/[hOE]+/) { pod2usage({-msg => "Unknown option -".$arg, -exitval => 1, -verbose => 1}) } pod2usage({-exitval => 0, -verbose => 2}) if($arg =~ m/h/); $discard_output = 1 if($arg =~ m/O/); $discard_error = 1 if($arg =~ m/E/); } #print short usage if not argument is left (no command passed): pod2usage({-msg => "$0 is expecting a command as argument!", -exitval => 1, -verbose => 1}) unless(@ARGV); my $command = join " ", @ARGV; my $real_cmd = $command; my $devnull = File::Spec->devnull(); #What do we do with stderr? if($real_cmd !~ m/\s2>/) #otherwise, the user redirect stderr himself! { if($discard_error) { $real_cmd .= " 2>".$devnull; } else { #let's redirect stderr to stdout if($real_cmd =~ m/\s1>/) { # the stdout is already redirected => insert "2>&1" before! # so that stderr is not redirected to wherever stdout is redirected $real_cmd =~ s/\s1>/2>&1 1>/; } else { $real_cmd .= " 2>&1"; } } } my $output; #what do we do with stdout? if($real_cmd !~ m/\s1>/) #otherwise, the user redirect stdout himself! { if($discard_output) { $real_cmd .= " 1>".$devnull; $output = "*** DISCARDED ***\n"; } } my $beginning = time(); if($discard_error && $discard_output) { #since we are not interested in any output, let's use system(), #it is more efficient than backtits system($real_cmd); } else { $output = `$real_cmd`; } my $end = time(); my $exit_value = $? >> 8; my $signal_num = $? & 127; my $dumped_core = $? & 128; #Format the message to be send with the collected information: my $body = "Command <$command> executed on ".hostname().":\n\n"; $body .= " Comment : $comment\n"; $body .= " Return value : $exit_value\n"; $body .= " Killed by signal : $signal_num\n" if($signal_num); $body .= " !!! CORE DUMPED !!!\n" if($dumped_core); $body .= "\n"; $body .= " Beginning : ".format_time($beginning)."\n"; $body .= " End : ".format_time($end)."\n"; $body .= " Total duration : ".format_duration($end-$beginning)."\n"; $body .= "\n\n"; $body .= "=== Output: ".($discard_error? "(Error discarded)" : "") ."\n".$output."=== End of output\n" unless($discard_error && $discard_output); #the body is ready, let's format a subject: my $subj = "Command <$command> executed"; $mail{Message} = $body; $mail{Subject} = $subj; sendmail(%mail) or die $Mail::Sendmail::error; sub format_time { #customizable subroutine to format a date/time my $time = shift; my @t = localtime($time); return sprintf("%2d/%02d/%4d %2d:%02d:%02d",$t[4]+1,$t[3],1900+$t[5], $t[2],$t[1],$t[0]); } sub format_duration { #customizable subroutine to format a date/time my $duration= shift; my $s = $duration % 60; $duration = int ($duration/60); my $m = $duration % 60; $duration = int ($duration/60); my $h = $duration % 24; $duration = int ($duration/24); my $d = $duration; return sprintf("%2d days %2d:%02d:%02d",$d, $h,$m,$s); } __END__ =pod =head1 NAME eml.pl - send an email after a custom command is executed. =head1 SYNOPSIS eml.pl [options] command [argument1] [argument2] ... =head1 ARGUMENTS =over 2 =item The command line to be run by eml.pl. =back =head1 OPTIONS =over 2 =item B<-h> Produce full documentation. =item B<-O> Discard output - output in stdout is discarded. Otherwise, the output will show up in the email (Beware of large output!) =item B<-E> Discard error - errors in stderr are discarded. Otherwise, the errors will show up together with the output in the email. =item B<-c comment> or B<--comment comment> Allows the user to specify a comment that will appear in the email. Useful when running different programs with similar command lines. =item B<-s smtp-server> or B<--smtp smtp-server> Sets the smtp server to be used. You should edit the script and set your default smtp so that you don't have to specify this each time. =item B<-t email-address> or B<--to email-address> Sets the email address the email is sent to. You should edit the script and set your default address so that you don't have to specify this each time. =back =head1 EXAMPLES =over 2 =item eml -O scp -r someone@host.domain:~ . Will run the command 'scp -r someone@host.domain:~ .' which will download the entire home directory of user someone on the host host.domain and put it all in the current directory. When the operation finishes, an email is sent to the address set in the script with the errors if any (-O discards the normal output). =item eml -c "simulation of 10^6 events" genevents -n 1000000 Will run the command 'genevents -n 1000000'. When the operation finishes, an email is sent to the address set in the script with the errors and the ouput and mentionning the comment 'simulation of 10^6 events'. =head1 BUGS Could get in trouble if the output is very large and is not discarded. There are maybe a few other. Tell me if you find one: tmaterna@tmaterna.com =head1 COPYRIGHT Copyright Thomas Materna (http://www.tmaterna.com) Use and redistribution is allowed provided that this copyright and disclaimer is reproduced. This code is provided ``as is'', without B express or implied warranties. In no event should the author be liable for any consequence of any type of use of this software. =head1 AUTHORS Thomas Materna (L) =head1 README B wraps a user-defined command and, upon completion, sends a email with statistics, diagnostics (return value, kill signal, duration,...) and output and/or errors. =head1 PREREQUISITES C, C to send the emails, C to get the platform-independent null device, C to get the hostanme platform-independently and C for the documentation. =head1 OSNAMES any OS understanding stdout and stderr redirection (e.g. 2>&1) =head1 SCRIPT CATEGORIES Mail Win32/Utilities UNIX/System_administration =cut