#!/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 = 65535; 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 = ; 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 : ., the default value is "*.warn" -f : , 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 = ) { 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; }