#!/usr/local/bin/perl -w #===================================================================== # cinnamon -- a perl script that translates the sense data from SIM # and MIM messages posted by IBM 3590 tape drives into # human-readable format, and sends the messages via email #--------------------------------------------------------------------- # $Id: cinnamon,v 1.1 2001/08/21 04:48:51 ssklar Exp $ #===================================================================== use strict; $|++; unless ($^O =~ /aix/) { die "Cinnamon is only useful on AIX systems. Sorry.\n" }; #---------------------------------------------------------------------- # the email address(es) that the formatted error message is to be sent # to. Don't forget to backslash any "@" signs ... #---------------------------------------------------------------------- my $recipient = "root"; unless (defined($recipient)) { $recipient = "root" } #---------------------------------------------------------------------- # the sequence number of the error log entry that we were invoked for # will be passed as the single argument ... #---------------------------------------------------------------------- my $sequence_number = shift || die "cinnamon: sequence number needed as argument\n"; #---------------------------------------------------------------------- # read in the full unformatted error log entry with the specified # sequence number ... #---------------------------------------------------------------------- open (ERROR, "/usr/bin/errpt -g -l $sequence_number |"); #------------------------------------------------------------------ # pull out the detail data from the error log entry ... #------------------------------------------------------------------ my %message; while () { if (/^el_nodeid/) { (undef, $message{host}) = split (/\s+/, $_); next }; if (/^el_resource/) { (undef, $message{drive}) = split (/\s+/, $_); next }; if (/^el_detail_data/) { (undef, $message{detail}) = split (/\s+/, $_); last }; }; close (ERROR); #------------------------------------------------------------------ # get the "Machine Type" and convert it from hex to ascii ... #------------------------------------------------------------------ substr($message{detail}, 128, 10) =~ /(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)/; $message{machine_type} = pack ("CCCCC", hex($1), hex($2), hex($3), hex($4), hex($5)); #------------------------------------------------------------------ # get the "Model" and convert it from hex to ascii ... #------------------------------------------------------------------ substr($message{detail}, 138, 6) =~ /(\w\w)(\w\w)(\w\w)/; $message{model} = pack ("CCC", hex($1), hex($2), hex($3)); #------------------------------------------------------------------ # get the "Model and Microcode Level" and convert it from hex # to ascii ... #------------------------------------------------------------------ substr($message{detail}, 32, 8) =~ /(\w\w)(\w\w)(\w\w)(\w\w)/; $message{mml} = pack ("CCCC", hex($1), hex($2), hex($3), hex($4)); #------------------------------------------------------------------ # get the "Message Code" and look up it's meaning ... #------------------------------------------------------------------ my %message_code = ( 3030 => "No Message", 3430 => "Operator Intervention Required", 3431 => "Device Degraded", 3432 => "Device Hardware Failure", 3433 => "Service Circuits Failed, Operations not Affected", 3535 => "Clean Device", 3537 => "Device has been cleaned", 3630 => "Bad Media, Read-Only Permitted", 3631 => "Rewrite Data if Possible", 3632 => "Read Data if Possible", 3634 => "Bad Media, Cannot Read or Write", 3732 => "Replace Cleaner Cartridge" ); $message{code} = $message_code{substr($message{detail}, 40, 4)} || "UNKNOWN"; #------------------------------------------------------------------ # determine if we're dealing with a SIM or a MIM ... #------------------------------------------------------------------ if (substr($message{detail}, 16, 2) eq "01") { #-------------------------------------------------------------- # it's a SIM ... #-------------------------------------------------------------- $message{type} = "SIM"; #-------------------------------------------------------------- # convert the FID Severity Code into something meaningful ... #-------------------------------------------------------------- my %fid_severity_code = ( 33 => "1 -- Acute", 32 => "2 -- Serious", 31 => "3 -- Moderate", 30 => "4 -- Service" ); $message{severity} = $fid_severity_code{substr($message{detail}, 52, 2)} || "UNKNOWN"; #-------------------------------------------------------------- # get the FID (FRU Identification Number), and convert it from # hex to ascii ... #-------------------------------------------------------------- substr($message{detail}, 64, 4) =~ /(\w\w)(\w\w)(\w\w)/; $message{fid} = pack ("CC", hex($1), hex($2)); #-------------------------------------------------------------- # get the "First FSC" (Fault Symptom Code), and convert it from # hex to ascii ... #-------------------------------------------------------------- substr($message{detail}, 68, 8) =~ /(\w\w)(\w\w)(\w\w)(\w\w)/; $message{first_fsc} = pack ("CCCC", hex($1), hex($2), hex($3), hex($4)); #-------------------------------------------------------------- # get the "Last FSC" (Fault Symptom Code), and convert it from # hex to ascii ... #-------------------------------------------------------------- substr($message{detail}, 76, 8) =~ /(\w\w)(\w\w)(\w\w)(\w\w)/; $message{last_fsc} = pack ("CCCC", hex($1), hex($2), hex($3), hex($4)); } else { #-------------------------------------------------------------- # it's a MIM ... #-------------------------------------------------------------- $message{type} = "MIM"; #-------------------------------------------------------------- # convert the MIM Severity Code into something meaningful ... #-------------------------------------------------------------- my %mim_severity_code = ( 31 => "3 -- Moderate: high temporary read or write errors have occurred", 32 => "2 -- Serious: permanent read or write errors have occurred", 33 => "1 -- Acute: tape directory errors have occurred" ); $message{severity} = $mim_severity_code{substr($message{detail}, 52, 2)} || "UNKNOWN"; #-------------------------------------------------------------- # get the VOLSER (Volume Serial Number), and convert it from # hex to ascii ... #-------------------------------------------------------------- substr($message{detail}, 68, 12) =~ /(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)(\w\w)/; $message{volser} = pack ("CCCCCC", hex($1), hex($2), hex($3), hex($4), hex($5), hex($6)); }; #------------------------------------------------------------------ # format the data and store it in the array @mail ... #------------------------------------------------------------------ my @mail; push (@mail, sprintf("Subject: %s posted by %s: %s\n", $message{type}, $message{drive}, $message{code})); push (@mail, sprintf("%-16s: %-20s\n", "Sequence Number", $sequence_number)); push (@mail, sprintf("%-16s: %-20s\n", "Host", $message{host})); push (@mail, sprintf("%-16s: %-20s\n", "Drive", $message{drive})); push (@mail, sprintf("%-16s: %-20s\n", "Model", $message{model})); push (@mail, sprintf("%-16s: %-20s\n", "Microcode", $message{mml})); push (@mail, sprintf("%-16s: %-20s\n", "Message Type", $message{type})); push (@mail, sprintf("%-16s: %-20s\n", "Message Code", $message{code})); push (@mail, sprintf("%-16s: %-20s\n", "Severity", $message{severity})); if ($message{type} eq "SIM") { push (@mail, sprintf("%-16s: %-20s\n", "First FSC", $message{first_fsc})); push (@mail, sprintf("%-16s: %-20s\n", "Last FSC", $message{last_fsc})); } else { push (@mail, sprintf("%-16s: %-20s\n", "VOLSER", $message{volser})); }; push (@mail, "\n\nRaw Sense Data:\n$message{detail}\n" . "-" x 72 . "\n\n"); #------------------------------------------------------------------ # open a pipe to sendmail and sent the message ... #------------------------------------------------------------------ open (SENDMAIL, "|/usr/sbin/sendmail $recipient") or die "cinnamon: couldn't open sendmail: $!"; print SENDMAIL @mail; close (SENDMAIL); exit 0; #---------------------------------------------------------------------- # documentation #---------------------------------------------------------------------- =pod =head1 NAME cinnamon -- translates the sense data from SIM and MIM messages posted by IBM 3590 tape drives to the AIX error log into human- readable format, and sends the messages via email. =head1 DESCRIPTION B (so named because I thought it sounded like "sim-mim- mon", which was my original name for the program) parses and mails AIX error log entries posted with the identifier B, which is the ERROR ID for B. SIM and MIM records are part of the "Statistical Analysis and Reporting System" (SARS), and are messages created by IBM 3590 tape drives that report on the condition of the drive (a SIM) or of the medium (a MIM). These records are presented by the operating system in different ways. In AIX, they are recorded in the error log, but in a difficult-to-read format. This script breaks down the sense data where the information is stored, parses it into a human-understandable format, and mails it to root. =head1 SETUP/USAGE If the user wants emails sent by this program to be sent to any other address than B, the variable B<$recipient> should be changed (about line number 26) to that address (or those addresses, comma- separated but all contained within double-quotes, with any "@" signs back-slashed. Additionally, this program is meant to be used as B, added to the ODM, so that B will be invoked each time an error log entry that matches the descriptor values of a 3590 SIM or MIM message. To create the B, save the following text to the file B: errnotify: en_name = "cinnamon" en_persistenceflg = 1 en_label = "SIM_MIM_RECORD_3590" en_class = "H" en_type = "INFO" en_method = "/usr/local/bin/perl /usr/local/sbin/cinnamon $1" (Note: use the proper paths to your perl executible and to this program in the above "en_method" line.) After saving the above text to a file, run the command: odmadd /tmp/cinnamon.add The error notification object will be added to the ODM. To verify that the object was added to the ODM properly, run the command: odmget -q"en_name='cinnamon'" errnotify To remove the object from the ODM (why would you want to do that?), run the command: odmdelete -q"en_name='cinnamon'" -o errnotify =head1 AUTHOR Sandor W. Sklar Unix Systems Administrator Stanford University ITSS mailto:ssklar@stanford.edu http://whippet.stanford.edu/~ssklar/ If this script is useful to you, or even if it is useless to you, or you have some changes/improvements/questions/large sums of cash and nothing to do with it, please send me an email. =head1 FOR MORE INFORMATION Most of the things that this script does were taken from the IBM publication "Statistical Analysis and Reporting System User Guide", which can be downloaded from . Information about creating custom error notification objects can be found in Chapter 4 of the IBM manual "General Programming Concepts: Writing and Debugging Programs", available online at < http://www.rs6000.ibm.com/doc_link/en_US/a_doc_lib/aixprggd/genprogc/error_notice.htm> =head1 COPYRIGHT This program is free software; you may redistribute it and/or modify it under the same terms as Perl itself. =cut #---------------------------------------------------------------------- # version history #---------------------------------------------------------------------- # $Log: cinnamon,v $ # Revision 1.1 2001/08/21 04:48:51 ssklar # Initial revision #