#!C:\Perl\bin\perl.exe # gus_mpt_sendmail.pl version 2004-03-16 # Log File Watchdog with Email Notification # See POD at EOF for full details. use strict; use warnings; use Tk; use Mail::Sendmail; use vars qw( $mw %mail $label_width $entry_width $minutes $file_log $mtime_log $file_dat $mtime_dat $title $smtp_url $email_to_addrs $email_cc_addrs $email_from_addr $email_subject $debug_flag ); ###################### # Begin stuff the user can (and should) change. ###################### $smtp_url = '198.137.240.92'; # Change this! $email_to_addrs = ''; $email_cc_addrs = ''; $email_from_addr = 'MultiPurpose_TestWare@FlexTest.us'; $email_subject = 'MultiPurpose TestWare Log'; ###################### # End stuff the user can (and should) change. ###################### unshift @{ $Mail::Sendmail::mailcfg{'smtp'} }, $smtp_url; my $last_checked = time(); my $bookmark_log = 0; my $bookmark_dat = 0; my $feedback = 'Click any button.'; my $check_flag = 0; # Don't start checking immediately. $minutes = 15; # Default interval between checks. ###################### # Begin GUI stuff ###################### $debug_flag = 1; $label_width = 12; $entry_width = 30; $title = 'Log File Watchdog with Email Notification'; $mtime_log = $mtime_dat = 0; # Avoid warning of uninitialized scalar. # Debug mode turns of actual email and just writes to screen. if ( $debug_flag ) { $title = 'DEBUG MODE: Not really running!'; print "$title \n"; $minutes = 5; } # First declare the main GUI frame and all her daughters. $mw = MainWindow->new( -title => $title ); # Begin MENU BAR $mw->configure( -menu => my $menubar = $mw->Menu ); # Begin MENU CONFIG my $menu_config = $menubar->cascade( -label => '~Config' ); $menu_config->command( -label => "Configure", -command => sub { configure::start_MainLoop() } ); # Begin MENU HELP my $menu_help = $menubar->cascade( -label => '~Help' ); $menu_help->command( -label => "About", -command => sub { menu_help_about::start_MainLoop() } ); my $frame_sunken = $mw->Frame( -relief => 'sunken', -borderwidth => 5 )->pack( -side => 'top', -expand => 1, -fill => 'x' ); my $frame_1 = $frame_sunken->Frame( -relief => 'flat', -borderwidth => 5 )->pack( -side => 'top', -expand => 1, -fill => 'x' ); $frame_1->Label( -width => $label_width, -text => "Email To:" )->pack( -side => 'left' ); $frame_1->Entry( -textvariable => \$email_to_addrs, -width => $entry_width, -background => "white", -foreground => 'blue', -relief => 'sunken', -font => 'courier' )->pack( -side => 'left', -expand => 1, -fill => 'x' ); my $frame_2 = $frame_sunken->Frame( -relief => 'flat', -borderwidth => 5 )->pack( -side => 'top', -expand => 1, -fill => 'x' ); $frame_2->Label( -width => $label_width, -text => "Email Cc:" )->pack( -side => 'left' ); $frame_2->Entry( -textvariable => \$email_cc_addrs, -width => $entry_width, -background => "white", -foreground => 'blue', -relief => 'sunken', -font => 'courier' )->pack( -side => 'left', -expand => 1, -fill => 'x' ); my $frame_3 = $frame_sunken->Frame( -relief => 'flat', -borderwidth => 5 )->pack( -side => 'top', -expand => 1, -fill => 'x' ); $frame_3->Label( -width => $label_width, -text => "Email Subj:" )->pack( -side => 'left' ); $frame_3->Entry( -textvariable => \$email_subject, -width => $entry_width, -background => "white", -foreground => 'blue', -relief => 'sunken', -font => 'courier' )->pack( -side => 'left', -expand => 1, -fill => 'x' ); my $frame_4 = $frame_sunken->Frame( -relief => 'flat', -borderwidth => 5 )->pack( -side => 'top', -expand => 1, -fill => 'x' ); $frame_4->Label( -width => $label_width, -text => "Log file:" )->pack( -side => 'left' ); $frame_4->Scrolled( 'Entry', -textvariable => \$file_log, -width => $entry_width, -background => "white", -foreground => 'blue', -relief => 'sunken', -font => 'courier' )->pack( -side => 'left', -expand => 1, -fill => 'x' ); $frame_4->Button( -width => 7, -relief => 'raised', -foreground => 'blue', -activebackground => 'green', -command => sub { $file_log = $mw->getOpenFile( -filetypes => [ [ 'Data files', '.log', 'TEXT' ] ] ); # $file_dat = $file_log; $file_dat =~ s/log$/dat/ }, -text => 'browse' )->pack( -side => 'left' ); my $frame_5 = $frame_sunken->Frame( -relief => 'flat', -borderwidth => 5 )->pack( -side => 'top', -expand => 1, -fill => 'x' ); $frame_5->Label( -width => $label_width, -text => "Data File:" )->pack( -side => 'left' ); $frame_5->Scrolled( 'Entry', -textvariable => \$file_dat, -width => $entry_width, -background => "white", -foreground => 'blue', -relief => 'sunken', -font => 'courier' )->pack( -side => 'left', -expand => 1, -fill => 'x' ); $frame_5->Button( -width => 7, -relief => 'raised', -foreground => 'blue', -activebackground => 'green', -command => sub { $file_dat = $mw->getOpenFile( -filetypes => [ [ 'Data files', '.dat', 'TEXT' ] ] ); # $file_log = $file_dat; $file_log =~ s/dat$/log/ }, -text => 'Browse' )->pack( -side => 'left' ); my $frame_7 = $mw->Frame( -relief => 'flat', -borderwidth => 5 )->pack( -side => 'top', -expand => 1, -fill => 'x' ); $frame_7->Label( -width => $label_width, -text => "Action" )->pack( -side => 'left' ); $frame_7->Button( -text => ' Start Checking ', -command => \&begin_checking, -background => 'gray', -activebackground => 'red', -relief => 'raised', )->pack( -side => 'left', -expand => 1, -fill => 'x' ); $frame_7->Button( -text => ' Pause ', -command => \&pause_checking, -background => 'gray', -activebackground => 'blue', -relief => 'raised', )->pack( -side => 'left', -expand => 1, -fill => 'x' ); $frame_7->Button( -text => ' Quit ', -command => \&quit_MainLoop, -background => 'gray', -activebackground => 'green', -relief => 'raised', )->pack( -side => 'left', -expand => 1, -fill => 'x' ); my $frame_btm = $mw->Frame( -relief => 'flat', -borderwidth => 5 )->pack( -side => 'top', -expand => 1, -fill => 'x' ); $frame_btm->Label( -width => $label_width, -text => "Feedback:" )->pack( -side => 'left' ); $frame_btm->Scrolled( 'Entry', -textvariable => \$feedback, -width => $entry_width, -background => "white", -foreground => 'blue', -relief => 'sunken', -font => 'courier' )->pack( -side => 'left', -expand => 1, -fill => 'x' ); sub notify_by_email { my ( $email_message, ) = @_; %mail = ( To => $email_to_addrs, Cc => $email_cc_addrs, From => $email_from_addr, Subject => $email_subject, Message => $email_message ); sendmail(%mail) or warn $Mail::Sendmail::error; print "Email sent to $email_to_addrs. \n"; print "Log says: ", $Mail::Sendmail::log, "\n"; } sub parse_file { my ( $file, $bookmark, $mtime_prior ) = @_; my $line = ''; if ($file) { if ( open( MTS, "<$file" ) ) { my @file_stats = stat MTS; if ( $mtime_prior < $file_stats[9] ) { my @lines = (); my $line_ptr = 1; while ( $line = ) { $line_ptr++; # Accumulate only lines not read before. if ( $line_ptr > $bookmark ) { push @lines, $line; } } close MTS; $bookmark = $line_ptr; # Search for keywords, good or bad. while ( $line = pop @lines ) { $bookmark--; # Key words for *.log file. last if $line =~ /Running|Error|Warning \[Stmgr\]/; # Key workds for *.dat file. last if $line =~ /Fail/; } return $line, $bookmark, $file_stats[9]; # New info, keep track. } else { return '', $bookmark, $mtime_prior; } # No change, keep same. } else { $feedback = "Oops! No such file: $file"; } # Just in case. } else { $feedback = "Oops! No files selected for parsing."; } } sub check_status { my ( $status, $status_log, $status_dat ) = ( '', '', '' ); ( $status_log, $bookmark_log, $mtime_log ) = parse_file( $file_log, $bookmark_log, $mtime_log ); $status_log =~ s/\s+/ /g; ( $status_dat, $bookmark_dat, $mtime_dat ) = parse_file( $file_dat, $bookmark_log, $mtime_dat ); $status_dat =~ s/\s+/ /g; $status .= "Keyword phrase...\n$status_log\n...was found on line $bookmark_log in file...\n$file_log\n\n" if ( ($status_log !~ /Running/) && ($status_log =~ /\S/) ); $status .= "Keyword phrase...\n$status_dat\n...was found on line $bookmark_dat in file...\n$file_dat\n\n" if ( $status_log =~ /\S/ ); if ($status =~ /\S/) { $status =~ s/ / /g; # For testing off-site where SMTP has different URL or may not even exist. unless ( $debug_flag ) { notify_by_email("$title reports as follows:\n$status\n") } else { print $status; } $status =~ s/\n/ /g; $feedback = $status; } else { $feedback = 'Last checked on ' . update_DTG(); } $last_checked = time(); } sub begin_checking { $feedback = "Now checking every $minutes minutes since " . update_DTG(); $check_flag = 1; } sub pause_checking { $feedback = "Checking paused since " . update_DTG(); $check_flag = 0; } # Close down the Perl/Tk GUI sub quit_MainLoop { $mw->destroy() if Tk::Exists($mw); menu_help_about::quit_MainLoop(); configure::quit_MainLoop(); } # Every 10 seconds, if so flaged, and if N minutes have passed, parse the selected # files backwards looking for keywords. $mw->repeat( 10000, sub { check_status() if $check_flag && ( time() - $last_checked > $minutes * 60 ); } ); MainLoop; ###################### # End GUI stuff ###################### # Return Date Time Group in ISO 8601 approved fashion. sub update_DTG { my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime(time); my $DTG = sprintf( "%04d-%02d-%02d_%02d:%02d:%02d", $year + 1900, $mon + 1, $mday, $hour, $min, $sec ); return ("$DTG"); } ##################### # Begin Menu Help About Package ##################### # This is a separate package for convenient use as a template. package menu_help_about; BEGIN { } use Tk; use strict; no strict "refs"; # Declare variables for strict. use vars qw( $mw_about ); sub start_MainLoop { $mw_about = MainWindow->new( -title => 'About' ); my $text = $mw_about->Label( -text => "MPT Sendmail\n" . "Release 2004-02-17\n\n" . "Copyright 2004, Gan Uesli Starling\n\nTrailing Edge Technologies\n" . "http://starling.us/tet\n" . "email gan\@starling.us" . "\n" )->pack(); my $bn_okay = $mw_about->Button( -width => 8, -relief => 'raised', -foreground => 'blue', -activebackground => 'green', -command => \&quit_MainLoop, -text => 'Okay' )->pack( -side => 'top' ); MainLoop; } # Close down the Perl/Tk GUI sub quit_MainLoop { $mw_about->destroy() if Tk::Exists($mw_about); } END { } ##################### # End Menu Help About Package ##################### ##################### # Begin Configure Package ##################### # This is a separate package for convenience. package configure; BEGIN { } use Tk; use strict; use warnings; # Declare variables for strict. use vars qw( $mw_configure %frame_label_entry $minutes_scale ); # Automate the build of a lable & entry wiget set inside a frame. sub mk_frame_label_entry { my ( $foo, $parent_frame, $label_text, $text_var_ref ) = @_; $frame_label_entry{"frame_$foo"} = $parent_frame->Frame( -relief => 'flat', -borderwidth => 5 )->pack( -side => 'top', -expand => 1, -fill => 'x' ); $frame_label_entry{"label_$foo"} = $frame_label_entry{"frame_$foo"}->Label( -width => $main::label_width, -text => " $label_text " )->pack( -side => 'left' ); $frame_label_entry{"entry_$foo"} = $frame_label_entry{"frame_$foo"}->Scrolled( 'Entry', -textvariable => $text_var_ref, -width => $main::entry_width, -background => "white", -foreground => 'blue', -relief => 'sunken', -font => 'courier' )->pack( -side => 'left', -expand => 1, -fill => 'x' ); } sub start_MainLoop { print "\n\n\n"; # Debugging aid for use with T-Pad.. $mw_configure = MainWindow->new( -title => ' Configure' ); # Make a framed lable/entry for each default that user may configure. mk_frame_label_entry( 'title', $mw_configure, 'Title:', \$main::title ); my $frame_minutes = $mw_configure->Frame( -relief => 'flat', -borderwidth => 5 )->pack( -side => 'top', -expand => 1, -fill => 'x' ); $frame_minutes->Label( -width => $main::label_width, -text => "Minutes:" )->pack( -side => 'left' ); $minutes_scale = $frame_minutes->Scale( -from => 5, -to => 120, -variable => \$main::minutes, -orient => 'horizontal' )->pack( -side => 'left', -expand => 1, -fill => 'x' ); # Make a framed lable/entry for each default that user may configure. mk_frame_label_entry( 'smtp_url', $mw_configure, 'SMTP:', \$main::smtp_url ); my $frame_btns = $mw_configure->Frame( -relief => 'flat', -borderwidth => 5 )->pack( -side => 'bottom', -expand => 1, -fill => 'x' ); $frame_btns->Button( -width => 10, -relief => 'raised', -foreground => 'blue', -activebackground => 'green', -command => \&accept_config, -text => 'Okay' )->pack( -side => 'left', -expand => 1, -fill => 'x' ); MainLoop; } # Close down the Perl/Tk GUI sub accept_config { # In case the config gets fancier. $main::mw->configure( -title => $main::title ); quit_MainLoop(); } sub quit_MainLoop { $mw_configure->destroy() if Tk::Exists($mw_configure); } END { } ##################### # End Configure Package ##################### __END__ =head1 NAME Log File Watchdog with Email Notification =head1 SYNOPSIS perl C =head1 DESCRIPTION A file to notify operators of MTS equipment when their machine has stopped.. =head1 PREREQUISITES For use especially (but not exclusively) with MultipPurpose TestWare by MTS. =head1 AUTHOR Gan Uesli Starling > =head1 COPYRIGHT Copyright (c) 2004, Gan Uesli Starling. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SCRIPT CATEGORIES MTS/MultiPurpose Test =cut