######################################################## # Please file all bug reports, patches, and feature # requests under: # https://sourceforge.net/p/logwatch/_list/tickets # Help requests and discusion can be filed under: # https://sourceforge.net/p/logwatch/discussion/ ######################################################## ######################################################## ## Copyright (c) 2019-2022 Orion Poplawski ## Covered under the included MIT/X-Consortium License: ## http://www.opensource.org/licenses/mit-license.php ## All modifications and contributions by other persons to ## this script are assumed to have been donated to the ## Logwatch project and thus assume the above copyright ## and licensing terms. If you want to make contributions ## under your own copyright or a different license this ## must be explicitly stated in the contribution an the ## Logwatch project reserves the right to not accept such ## contributions. If you have made significant ## contributions to this script and want to claim ## copyright please contact logwatch-devel@lists.sourceforge.net. ######################################################### use strict; use warnings; my $Detail = $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0; my ($Hostname) = ($ENV{'HOSTNAME'} =~ /^([^.]+)/); my $CannotConnectThreshold = $ENV{'cannot_connect_threshold'} || 0; my %BatteryLow; my %CannotConnect; my %Commands; my %CommunicationLost; my %CommunicationState; my %Connected; my %ConnectionFailure; my %DataStale; my %DataStaleState; my %Logins; my %OnBattery; my %State; my %OtherList; my %Unavailable; my $SelfTestPassed = 0; my $SelfTestFailed = 0; my $UpsdrvctlMessages; while (defined(my $ThisLine = )) { chomp($ThisLine); # Strip PID $ThisLine =~ s/^([^[]+)\[\d+\]:/$1:/; my $ups; my $state; my $user; my $command; if ($ThisLine =~ /^(?:nut-server|upsd): User .* logged out/ # CUPS matches our generous service regex or $ThisLine =~ /^cupsd:/ or $ThisLine =~ /: Could not find PID file/ # TODO - count start/stops or $ThisLine =~ /Signal 15: exiting/ or $ThisLine =~ /Startup successful/ or $ThisLine =~ /^upsdrvctl: Detected .* on host / or $ThisLine =~ /^upsdrvctl: Network UPS Tools - / or $ThisLine =~ /^upsdrvctl: USB communication driver/ or $ThisLine =~ /^upsdrvctl: Using subdriver:/ or $ThisLine =~ /^upsdrvctl: using '.*' to set battery low state/ or $ThisLine =~ /^(?:nut-server|upsd): \/etc\/pki\/nssdb is world readable/ or $ThisLine =~ /^(?:nut-server|upsd): Intend to retrieve password for NSS User Private Key and Certificate Services \/ NSS Certificate DB: password configured/ or $ThisLine =~ /^(?:nut-server|upsd): listening on / or $ThisLine =~ /^(?:nut-server|upsd): mainloop: Interrupted system call/ or $ThisLine =~ /^(?:nut-server|upsd): Network UPS Tools upsd/ or $ThisLine =~ /^(?:nut-server|upsd): fopen \S+\/upsd.pid: No such file or directory/ or $ThisLine =~ /^(?:nut-server|upsd): Running as foreground process, not saving a PID file/ or $ThisLine =~ /^(?:nut-server|upsd): SSL handshake done successfully with client/ or $ThisLine =~ /^(?:nut-monitor|upsmon): Connected to/ or $ThisLine =~ /^(?:nut-monitor|upsmon): Connecting in SSL to/ or $ThisLine =~ /^(?:nut-monitor|upsmon): Certificate verification is disabled/ or $ThisLine =~ /^(?:nut-monitor|upsmon): Do not intend to authenticate server/ or $ThisLine =~ /^(?:nut-monitor|upsmon): fopen \S+\/upsmon.pid: No such file or directory/ or $ThisLine =~ /^(?:nut-monitor|upsmon): Init SSL without certificate database/ or $ThisLine =~ /^(?:nut-monitor|upsmon): Network UPS Tools upsmon/ # This will generate a communication lost message - TODO - track reasons? or $ThisLine =~ /^(?:nut-monitor|upsmon): Poll UPS \[(\S+)\] failed -/ or $ThisLine =~ /^(?:nut-monitor|upsmon): SSL handshake done successfully with server/ or $ThisLine =~ /^(?:nut-monitor|upsmon): UPS: \S+ \((?:master:primary)\)/ or $ThisLine =~ /^(?:nut-monitor|upsmon): UPS: \S+ \((?:slave:secondary)\)/ or $ThisLine =~ /^(?:nut-monitor|upsmon): Using power down flag file/ # This is going away - https://bugzilla.redhat.com/show_bug.cgi?id=1608176 or $ThisLine =~ /^(?:nut-monitor|upsmon): wall: cannot get tty name: Inappropriate ioctl for device/ or $ThisLine =~ /^upssched: (?:Cancelling|New) timer:/ or $ThisLine =~ /^upssched: Timer daemon started/ or $ThisLine =~ /^upssched: Timer queue empty/ or $ThisLine =~ /^snmp-ups: \[(\S+)\] snmp_ups_walk: data (:?resumed|stale)/ or $ThisLine =~ /^UPS: No longer on battery power/ # Should get a particular ups on battery power message or $ThisLine =~ /^UPS: On battery power in response to an input power problem/ or $ThisLine =~ /^UPS: UPS: Restored the local network management interface-to-UPS communication/ or $ThisLine =~ /^UPS: Started a self-test/ ) { # Ignore these } elsif (($ups) = ($ThisLine =~ /^(?:nut-monitor|upsmon): UPS (\S+) battery is low/)) { $BatteryLow{$ups}++; } elsif (($ups) = ($ThisLine =~ /^(?:nut-server|upsd): Can't connect to UPS \[(\S+)\]/)) { $CannotConnect{$ups}++; } elsif (($user, $command, $ups) = ($ThisLine =~ /^(?:nut-server|upsd): Instant command: (\S+) did (\S+) on (\S+)/)) { $Commands{$ups}->{$user}->{$command}++; } elsif (($ups) = ($ThisLine =~ /^(?:nut-server|upsd): Connected to UPS \[(\S+)\]/)) { $Connected{$ups}++; } elsif (($ups) = ($ThisLine =~ /^(?:nut-monitor|upsmon): Communications with UPS (\S+) lost/)) { $CommunicationLost{$ups}++; $CommunicationState{$ups} = "lost"; } elsif (($ups) = ($ThisLine =~ /^(?:nut-monitor|upsmon): Communications with UPS (\S+) established/)) { $CommunicationState{$ups} = "established"; # At Detail 0, we don't want to know about recovered disconnects if ($Detail == 0) { $Unavailable{$ups}--; delete $Unavailable{$ups} if $Unavailable{$ups} <= 0; } # This may always be paired with the "unavailable" message below - so may want to ignore or move to higher detail } elsif (($ups) = ($ThisLine =~ /^(?:nut-monitor|upsmon): UPS \[(.+)\]: connect failed:/)) { $ConnectionFailure{$ups}++; } elsif (($ups) = ($ThisLine =~ /^(?:nut-monitor|upsmon): UPS (.+)\ is unavailable/)) { $Unavailable{$ups}++; } elsif (($ups, $state) = ($ThisLine =~ /^(?:nut-monitor|upsmon): UPS (\S+) on (.*)/)) { my ($host) = ($ups =~ /@([^.]+)/); next unless $host eq "localhost" or $host eq $Hostname; $State{$ups} = $state; $OnBattery{$ups}++ if $state eq "battery"; } elsif (($ups, $state) = ($ThisLine =~ /^(?:nut-monitor|upsmon): UPS: (\S+) \(\S+\) \((.*)\)/)) { my ($host) = ($ups =~ /@([^.]+)/); next unless $host eq "localhost" or $host eq $Hostname; $State{$ups} = $state; #$OnBattery{$ups}++ if $state eq "battery"; } elsif (($ups) = ($ThisLine =~ /^(?:nut-server|upsd): Data for UPS \[(\S+)\] is stale/)) { $DataStale{$ups}++; $DataStaleState{$ups}++; } elsif (($ups) = ($ThisLine =~ /^(?:nut-server|upsd): UPS \[(\S+)\] data is no longer stale/)) { $DataStaleState{$ups}--; } elsif (($user, $ups) = ($ThisLine =~ /^(?:nut-server|upsd): User (\S+) logged into UPS \[(\S+)\]/)) { $Logins{$user}->{$ups}++; } elsif (my ($msg) = ($ThisLine =~ /^upsdrvctl: (.*)/)) { $UpsdrvctlMessages .= " $msg\n"; } elsif ($ThisLine =~ /^UPS: Passed a self-test/) { $SelfTestPassed++; } else { $OtherList{$ThisLine}++; } } if (keys %CannotConnect) { my $first = 1; foreach my $ups (sort {$a cmp $b} keys %CannotConnect) { if ($CannotConnect{$ups} >= $CannotConnectThreshold) { if ($first) { print "Cannot connect to UPS:\n"; print " (with threshold >= $CannotConnectThreshold)" if $CannotConnectThreshold; print "\n"; $first = 0; } print " $ups: $CannotConnect{$ups} Time(s)\n"; } } print "\n"; } if (keys %ConnectionFailure) { my $first = 1; foreach my $ups (sort {$a cmp $b} keys %ConnectionFailure) { if ($ConnectionFailure{$ups} >= $CannotConnectThreshold) { if ($first) { print "Cannot connect to UPS server:\n"; print " (with threshold >= $CannotConnectThreshold)" if $CannotConnectThreshold; print "\n"; $first = 0; } print " $ups: $ConnectionFailure{$ups} Time(s)\n"; } } print "\n"; } if (keys %Unavailable) { print "UPS is unavailable:\n"; foreach my $ups (sort {$a cmp $b} keys %Unavailable) { print " $ups: $Unavailable{$ups} Time(s)\n"; } print "\n"; } if ($UpsdrvctlMessages) { print "upsdrvctl Messages:\n"; print $UpsdrvctlMessages; } if (keys %BatteryLow) { print "UPS battery low:\n"; foreach my $ups (sort {$a cmp $b} keys %BatteryLow) { print " $ups: $BatteryLow{$ups} Time(s)\n"; } print "\n"; } if (keys %OnBattery) { print "UPS on battery:\n"; foreach my $ups (sort {$a cmp $b} keys %OnBattery) { print " $ups: $OnBattery{$ups} Time(s)\n"; } print "\n"; } # TODO - Alert if too many disconnects? my $CommunicationLostCurrent = 0; foreach my $ups (keys %CommunicationLost) { $CommunicationLostCurrent += 1 if $CommunicationState{$ups} eq "lost"; } if (keys %CommunicationLost and ($Detail or $CommunicationLostCurrent)) { print "Communication lost:\n"; foreach my $ups (sort {$a cmp $b} keys %CommunicationLost) { print " $ups: $CommunicationLost{$ups} Time(s)"; print " * Currently " . $CommunicationState{$ups}; print "\n"; } print "\n"; } # TODO - Alert if too many? my $DataStaleStateTotal = 0; foreach my $ups (keys %DataStale) { $DataStaleStateTotal += $DataStaleState{$ups}; } if (keys %DataStale and ($Detail or $DataStaleStateTotal)) { print "Data is stale:\n"; foreach my $ups (sort {$a cmp $b} keys %DataStale) { print " $ups: $DataStale{$ups} Time(s)"; print " * Currently stale" if $DataStaleState{$ups}; print "\n"; } print "\n"; } if ($SelfTestPassed and ($Detail >= 5)) { print "UPS Self-Test Passed $SelfTestPassed Time(s)\n\n"; } if (keys %Connected and ($Detail >= 5)) { print "Connected to UPS:\n"; foreach my $ups (sort {$a cmp $b} keys %Connected) { print " $ups: $Connected{$ups} Time(s)\n"; } print "\n"; } if (keys %Commands and $Detail) { print "Commands run:\n"; foreach my $ups (sort {$a cmp $b} keys %Commands) { print " UPS $ups:\n"; foreach my $user (sort {$a cmp $b} keys %{$Commands{$ups}}) { print " User $user:\n"; foreach my $command (sort {$a cmp $b} keys %{$Commands{$ups}{$user}}) { print " $command: $Commands{$ups}->{$user}->{$command} Time(s)\n"; } } } print "\n"; } if (keys %Logins and ($Detail >= 10)) { print "Logins:\n"; foreach my $user (sort {$a cmp $b} keys %Logins) { print " $user:\n"; foreach my $ups (sort {$a cmp $b} keys %{$Logins{$user}}) { print " $ups: $Logins{$user}{$ups} Time(s)\n"; } } print "\n"; } if (keys %OtherList) { print "\n\n**Unmatched Entries**\n"; foreach my $line (sort {$a cmp $b} keys %OtherList) { print " $line: $OtherList{$line} Time(s)\n"; } } exit(0); # vi: shiftwidth=3 tabstop=3 syntax=perl et # Local Variables: # mode: perl # perl-indent-level: 3 # indent-tabs-mode: nil # End: