510 lines
13 KiB
Plaintext
510 lines
13 KiB
Plaintext
|
#!/usr/bin/env perl
|
||
|
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html
|
||
|
#------------------------------------------------------------------------------
|
||
|
=head1 monaixsyslog
|
||
|
=head2
|
||
|
=cut
|
||
|
#------------------------------------------------------------------------------
|
||
|
use locale;
|
||
|
use Getopt::Long;
|
||
|
|
||
|
my $dirname = "xcat_aix_syslog";
|
||
|
my $vardir = "/var/opt/$dirname";
|
||
|
|
||
|
my $default_runfile = "$vardir/.monaixsyslog_run";
|
||
|
my $default_file = "$vardir/syslog.out";
|
||
|
my $default_pri = "*.warn";
|
||
|
|
||
|
$::MAX_SENSOR_STRING = 10240;
|
||
|
my ($facility_priority, $logfile, $runfile) = &getArgs();
|
||
|
|
||
|
my ($syslogconf, $embedinfo);
|
||
|
|
||
|
$syslogconf = "/etc/syslog.conf";
|
||
|
$embedinfo = "$facility_priority $logfile rotate size 4m files 1";
|
||
|
|
||
|
if (!-d $vardir) { mkdir($vardir); }
|
||
|
|
||
|
#check to see if this is the first time this script has been run
|
||
|
if (!-e $runfile)
|
||
|
{ #first time
|
||
|
if ($^O =~ /^aix/i)
|
||
|
{
|
||
|
runcmd("grep \"$embedinfo\" $syslogconf", -1);
|
||
|
if ($::RUNCMD_RC == 1)
|
||
|
{ #grep did not find embedinfo
|
||
|
#update syslog.conf
|
||
|
if (!-d $vardir) { mkdir($vardir); }
|
||
|
if (!-e $logfile)
|
||
|
{
|
||
|
touchFile($logfile);
|
||
|
}
|
||
|
runcmd("echo \"$embedinfo\" >> $syslogconf");
|
||
|
my $cmd = "refresh -s syslogd";
|
||
|
runcmd($cmd);
|
||
|
}
|
||
|
touchFile($runfile);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
print "non-AIX platform, this scripts should not be ran.\n";
|
||
|
exit 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#Check for errors
|
||
|
|
||
|
if ($^O =~ /^aix/i)
|
||
|
{
|
||
|
unless (open(RUNFILE, "<$runfile"))
|
||
|
{
|
||
|
print "Cannot open file $runfile\n";
|
||
|
exit 1;
|
||
|
}
|
||
|
|
||
|
my $marker = <RUNFILE>;
|
||
|
close(RUNFILE);
|
||
|
|
||
|
my ($new_number, $new_content, $to_rfile);
|
||
|
# If the $runfile is empty, then we should read the $logfile from the beginning.
|
||
|
if (!$marker)
|
||
|
{
|
||
|
($new_number, $new_content, $to_rfile) = &refreshSensorFromLogfile($logfile, 0, undef);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
my @info = split ':::', $marker;
|
||
|
my ($last_modified_time, $line_number, $mark_content) = @info;
|
||
|
|
||
|
my @stats = stat($logfile);
|
||
|
my $time = $stats[9];
|
||
|
if ($time == $last_modified_time)
|
||
|
{
|
||
|
# The log file has not been updated since last modified.
|
||
|
exit 0;
|
||
|
}
|
||
|
|
||
|
($new_number, $new_content, $to_rfile) = &refreshSensorFromLogfile($logfile, $line_number, $mark_content);
|
||
|
|
||
|
# If the $to_rfile is set, then we should refresh the info from rotated file to Sensor first.
|
||
|
if ($to_rfile)
|
||
|
{
|
||
|
# Read the rotated file first.
|
||
|
my $rotated_file = "$logfile" . ".0";
|
||
|
($new_number, $new_content, $to_rfile) = &refreshSensorFromLogfile($rotated_file, $line_number, $mark_content);
|
||
|
|
||
|
# Then read the log file just from the beginning to refresh the Sensor.
|
||
|
($new_number, $new_content, $to_rfile) = &refreshSensorFromLogfile($logfile, 0, undef);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
# Get the last modified time for this log file
|
||
|
my @stats = stat($logfile);
|
||
|
my $new_time = $stats[9];
|
||
|
|
||
|
&updateRunfile($new_time, $new_number, $new_content, $runfile);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
print "non-AIX platform, this scripts should not be ran.\n";
|
||
|
exit 1;
|
||
|
}
|
||
|
|
||
|
exit 0;
|
||
|
|
||
|
#-------------------------------------------------------------------------------
|
||
|
|
||
|
=head3 getArgs
|
||
|
|
||
|
parse the command line and check the values
|
||
|
|
||
|
paras:
|
||
|
-p : <facility>.<priority>, the default value is "*.warn"
|
||
|
-f : <fifo_name>, the default value is "/var/opt/xcat_aix_syslog/syslog_fifo"
|
||
|
|
||
|
=cut
|
||
|
|
||
|
#-------------------------------------------------------------------------------
|
||
|
sub getArgs()
|
||
|
{
|
||
|
my $routine = "getArgs";
|
||
|
print "ENTERING: $routine\n" if $::DEBUG;
|
||
|
|
||
|
my @command_line = ();
|
||
|
@command_line = @ARGV;
|
||
|
|
||
|
# Checks case in GetOptions
|
||
|
$Getopt::Long::ignorecase = 0;
|
||
|
|
||
|
my ($facility_priority, $file, $runfile);
|
||
|
|
||
|
if (
|
||
|
!GetOptions(
|
||
|
'p=s' => \$facility_priority,
|
||
|
'f=s' => \$file,
|
||
|
)
|
||
|
)
|
||
|
{
|
||
|
print "LEAVING: $routine\n" if $::DEBUG;
|
||
|
exit 1;
|
||
|
}
|
||
|
|
||
|
# Set runfile mark file
|
||
|
if ($facility_priority || $file)
|
||
|
{
|
||
|
my @para = split '/', $file;
|
||
|
my $newpara = join '-', @para;
|
||
|
$runfile = "$vardir/.monaixsyslog_run" . "-$facility_priority". "-$newpara";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$runfile = $default_runfile;
|
||
|
}
|
||
|
|
||
|
if (!$file)
|
||
|
{
|
||
|
$file = $default_file;
|
||
|
}
|
||
|
|
||
|
if (!$facility_priority)
|
||
|
{
|
||
|
$facility_priority = $default_pri;
|
||
|
}
|
||
|
|
||
|
return ($facility_priority, $file, $runfile);
|
||
|
}
|
||
|
|
||
|
#-------------------------------------------------------------------------------
|
||
|
|
||
|
=head3 refreshSensorFromLogfile
|
||
|
|
||
|
read the log file line by line to refresh the Sensor
|
||
|
|
||
|
Args:
|
||
|
$file - the log file
|
||
|
$bgnline - the beginning line number that we should read from
|
||
|
$bgncontent - the line content related to $line
|
||
|
|
||
|
Return:
|
||
|
$i - the line number that has been read and refreshed to Sensor.
|
||
|
$mark_content - the line content related to $i
|
||
|
$to_rfile - the flag that indicates whether we need to read from the
|
||
|
rotated file.
|
||
|
|
||
|
=cut
|
||
|
|
||
|
#-------------------------------------------------------------------------------
|
||
|
sub refreshSensorFromLogfile()
|
||
|
{
|
||
|
my ($file, $bgnline, $bgncontent) = @_;
|
||
|
unless (open(FILE, "<$file"))
|
||
|
{
|
||
|
# The file may be opened by syslogd.
|
||
|
exit 0;
|
||
|
}
|
||
|
|
||
|
my $i = 0;
|
||
|
my $matchflag = 0;
|
||
|
my $to_rfile = 0;
|
||
|
my $mark_content;
|
||
|
my $allinfo = "";
|
||
|
while (my $line = <FILE>)
|
||
|
{
|
||
|
if ($matchflag || $bgnline == 0)
|
||
|
{
|
||
|
# Start reading the file from this line and push it to the sensor
|
||
|
# and update the mark file
|
||
|
$allinfo .= $line;
|
||
|
$i = $i + 1;
|
||
|
$mark_content = $line;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ($i != $bgnline - 1)
|
||
|
{
|
||
|
$i = $i + 1;
|
||
|
next;
|
||
|
}
|
||
|
|
||
|
if ($line eq $bgncontent)
|
||
|
{
|
||
|
$matchflag = 1;
|
||
|
$i = $i + 1;
|
||
|
next;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
# The line number is the same, but the content is different
|
||
|
# that indicates the log file has been rotated.
|
||
|
$to_rfile = 1;
|
||
|
last;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if ($allinfo)
|
||
|
{
|
||
|
my $strlen = length($allinfo);
|
||
|
# The condition/response can not handle
|
||
|
# the long sensor String very well,
|
||
|
# use file to pass messages.
|
||
|
# file name: /var/opt/xcat_aix_syslog/tmplogmsg_$$
|
||
|
if ($strlen > $::MAX_SENSOR_STRING)
|
||
|
{
|
||
|
srand(time | $$);
|
||
|
my $filename = "$vardir/tmplogmsg_$$";
|
||
|
while (-e $filename)
|
||
|
{
|
||
|
$filename = createRandomName($filename);
|
||
|
}
|
||
|
if (open(TMPLOG, ">$filename"))
|
||
|
{
|
||
|
print TMPLOG $allinfo;
|
||
|
close TMPLOG;
|
||
|
print "XCAT_MONAIXSYSLOG_FILE:$filename";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
#open failed, why?
|
||
|
print "OPEN_FILE_FAILED: $filename";
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
print $allinfo;
|
||
|
}
|
||
|
}
|
||
|
close(FILE);
|
||
|
|
||
|
return ($i, $mark_content, $to_rfile);
|
||
|
}
|
||
|
|
||
|
#-------------------------------------------------------------------------------
|
||
|
|
||
|
=head3 updateRunfile
|
||
|
|
||
|
use the new marker line to update the runfile
|
||
|
|
||
|
Args:
|
||
|
$time - last mofidied time
|
||
|
$line - line number
|
||
|
$content - line content
|
||
|
$file - the run file
|
||
|
|
||
|
Return:
|
||
|
$i - the line number that has been read and refreshed to Sensor.
|
||
|
$mark_content - the line content related to $i
|
||
|
|
||
|
=cut
|
||
|
|
||
|
#-------------------------------------------------------------------------------
|
||
|
sub updateRunfile()
|
||
|
{
|
||
|
my ($time, $line, $content, $file) = @_;
|
||
|
|
||
|
# the marker line is something like "last_modified_time:::line_number:::mark_content"
|
||
|
my $new_marker = join(":::", $time, $line, $content);
|
||
|
runcmd("echo \"$new_marker\" > $file");
|
||
|
}
|
||
|
|
||
|
#--------------------------------------------------------------------------------
|
||
|
=head3 runcmd
|
||
|
Run the given cmd and return the output in an array (already chopped). Alternatively,
|
||
|
if this function is used in a scalar context, the output is joined into a single string
|
||
|
with the newlines separating the lines.
|
||
|
Arguments:
|
||
|
command, exitcode and reference to output
|
||
|
Returns:
|
||
|
see below
|
||
|
Error:
|
||
|
Normally, if there is an error running the cmd, it will display the error msg
|
||
|
and exit with the cmds exit code, unless exitcode is given one of the
|
||
|
following values:
|
||
|
0: display error msg, DO NOT exit on error, but set
|
||
|
$::RUNCMD_RC to the exit code.
|
||
|
-1: DO NOT display error msg and DO NOT exit on error, but set
|
||
|
$::RUNCMD_RC to the exit code.
|
||
|
-2: DO the default behavior (display error msg and exit with cmds
|
||
|
exit code.
|
||
|
number > 0: Display error msg and exit with the given code
|
||
|
Example:
|
||
|
my $outref = runcmd($cmd, -2, 1);
|
||
|
Comments:
|
||
|
If refoutput is true, then the output will be returned as a reference to
|
||
|
an array for efficiency.
|
||
|
=cut
|
||
|
#--------------------------------------------------------------------------------
|
||
|
sub runcmd
|
||
|
{
|
||
|
my ($cmd, $exitcode, $refoutput) = @_;
|
||
|
$::RUNCMD_RC = 0;
|
||
|
if (!($cmd =~ /2>&1$/)) { $cmd .= ' 2>&1'; }
|
||
|
|
||
|
my $outref = [];
|
||
|
@$outref = `$cmd`;
|
||
|
if ($?)
|
||
|
{
|
||
|
$::RUNCMD_RC = $? >> 8;
|
||
|
my $displayerror = 1;
|
||
|
my $rc;
|
||
|
if (defined($exitcode) && length($exitcode) && $exitcode != -2)
|
||
|
{
|
||
|
if ($exitcode > 0)
|
||
|
{
|
||
|
$rc = $exitcode;
|
||
|
} # if not zero, exit with specified code
|
||
|
elsif ($exitcode <= 0)
|
||
|
{
|
||
|
$rc = ''; # if zero or negative, do not exit
|
||
|
if ($exitcode < 0) { $displayerror = 0; }
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$rc = $::RUNCMD_RC;
|
||
|
} # if exitcode not specified, use cmd exit code
|
||
|
if ($displayerror)
|
||
|
{
|
||
|
my $errmsg = '';
|
||
|
if (($^O =~ /^linux/i) && $::RUNCMD_RC == 139)
|
||
|
{
|
||
|
$errmsg = "Segmentation fault $errmsg";
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
# The error msgs from the -api cmds are pretty messy. Clean them up a little.
|
||
|
filterRmcApiOutput($cmd, $outref);
|
||
|
$errmsg = join('', @$outref);
|
||
|
chomp $errmsg;
|
||
|
}
|
||
|
print "Exit code $::RUNCMD_RC from command: $cmd\nError message from cmd: $errmsg\n"
|
||
|
}
|
||
|
}
|
||
|
if ($refoutput)
|
||
|
{
|
||
|
chomp(@$outref);
|
||
|
return $outref;
|
||
|
}
|
||
|
elsif (wantarray)
|
||
|
{
|
||
|
chomp(@$outref);
|
||
|
return @$outref;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
my $line = join('', @$outref);
|
||
|
chomp $line;
|
||
|
return $line;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#--------------------------------------------------------------------------------
|
||
|
=head3 filterRmcApiOutput
|
||
|
filter RMC Api Output
|
||
|
Arguments:
|
||
|
RMC command
|
||
|
Output reference
|
||
|
Returns:
|
||
|
none
|
||
|
Globals:
|
||
|
none
|
||
|
Error:
|
||
|
none
|
||
|
Example:
|
||
|
filterRmcApiOutput($cmd, $outref);
|
||
|
Comments:
|
||
|
The error msgs from the RPM -api cmds are pretty messy.
|
||
|
This routine cleans them up a little bit.
|
||
|
=cut
|
||
|
#--------------------------------------------------------------------------------
|
||
|
sub filterRmcApiOutput
|
||
|
{
|
||
|
my ($cmd, $outref) = @_;
|
||
|
if (!($cmd =~ m|^/usr/bin/\S+-api |)) {
|
||
|
return;
|
||
|
} # give as much info as possible, if verbose
|
||
|
|
||
|
# Figure out the output delimiter
|
||
|
my ($d) = $cmd =~ / -D\s+(\S+)/;
|
||
|
if (length($d)) {
|
||
|
$d =~ s/^(\'|\")(.*)(\"|\')$/$2/; # remove any surrounding quotes
|
||
|
# escape any chars perl pattern matching would intepret as special chars
|
||
|
$d =~ s/([\|\^\*\+\?\.])/\\$1/g;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$d = '::';
|
||
|
} # this is the default output delimiter for the -api cmds
|
||
|
$$outref[0] =~ s/^ERROR${d}.*${d}.*${d}.*${d}.*${d}//;
|
||
|
}
|
||
|
|
||
|
#--------------------------------------------------------------------------------
|
||
|
|
||
|
=head3 touchFile
|
||
|
Arguments: $filename, $donotExit
|
||
|
Returns: non zero return code indicates error
|
||
|
Example: touchFile("/var/opt/xcat/touch");
|
||
|
=cut
|
||
|
|
||
|
#--------------------------------------------------------------------------------
|
||
|
sub touchFile
|
||
|
{
|
||
|
my ($filename, $donotExit) = @_;
|
||
|
my $fh;
|
||
|
my $rc = 0;
|
||
|
if (!-e $filename) {
|
||
|
#if the file doesn't exist we need to open and close it
|
||
|
open($fh, ">>$filename") or $rc++;
|
||
|
if ($rc > 0 && !$donotExit) {
|
||
|
print "Touch of file $filename failed with: $!\n";
|
||
|
return $rc;
|
||
|
}
|
||
|
close($fh) or $rc++;
|
||
|
}
|
||
|
else {
|
||
|
#if the file does exist we can just utime it (see the perlfunc man page entry on utime)
|
||
|
my $now = time;
|
||
|
utime($now, $now, $filename);
|
||
|
}
|
||
|
if ($rc > 0 && !$donotExit) {
|
||
|
print "Touch of file $filename failed with: $!\n";
|
||
|
return $rc;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
#--------------------------------------------------------------------------------
|
||
|
|
||
|
=head3 createRandomName
|
||
|
|
||
|
Create a randome file name.
|
||
|
|
||
|
Arguments:
|
||
|
Prefix of name
|
||
|
Returns:
|
||
|
Prefix with 8 random letters appended
|
||
|
Error:
|
||
|
none
|
||
|
Example:
|
||
|
$file = createRandomName($namePrefix);
|
||
|
Comments:
|
||
|
None
|
||
|
|
||
|
=cut
|
||
|
|
||
|
#--------------------------------------------------------------------------------
|
||
|
|
||
|
sub createRandomName
|
||
|
{
|
||
|
my $name=shift;
|
||
|
|
||
|
my $nI;
|
||
|
for ($nI = 0 ; $nI < 8 ; $nI++)
|
||
|
{
|
||
|
my $char = ('a' .. 'z', 'A' .. 'Z')[int(rand(52)) + 1];
|
||
|
$name .= $char;
|
||
|
}
|
||
|
$name;
|
||
|
}
|
||
|
|