Monitorix/monitorix

7006 lines
186 KiB
Perl
Executable File

#!/usr/bin/env perl
#
# Monitorix - A lightweight system monitoring tool.
#
# Copyright (C) 2005-2012 by Jordi Sanfeliu <jordi@fibranet.cat>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
require 5.006;
no strict "vars";
no warnings "once";
#use warnings;
use RRDs;
use POSIX;
use File::Basename;
use LWP::UserAgent;
use XML::Simple;
use Socket;
use DBI;
use Getopt::Std;
use Cwd 'abs_path';
use locale;
# Force a standard locale
$ENV{LANG} = "";
setlocale(LC_TIME, "C");
# Signals to Trap and Handle
$SIG{'INT' } = 'INT_handler';
$SIG{'ABRT'} = 'INT_handler';
$SIG{'QUIT'} = 'INT_handler';
$SIG{'TRAP'} = 'INT_handler';
$SIG{'STOP'} = 'INT_handler';
$SIG{'TERM'} = 'INT_handler';
$SIG{'CHLD'} = 'CHLD_handler';
$SIG{'HUP' } = 'HUP_handler';
$SIG{'ALRM'} = 'ALRM_handler';
use constant VERSION => "2.6.0";
use constant RELDATE => "19-Sep-2012";
my @suppsys = ("Linux", "FreeBSD", "OpenBSD", "NetBSD");
my @graphs;
my @graphs_debug;
my $os;
my $kernel_branch;
sub INT_handler {
my ($signal) = @_;
logger("SIG$signal caught.");
flush_accounting_rules();
logger("Exiting.");
exit(0);
}
sub CHLD_handler {
my $pid = waitpid(-1, WNOHANG);
}
sub HUP_handler {
my ($signal) = @_;
my $myself = (caller(0))[3];
logger("SIG$signal caught.");
close(STDOUT);
close(STDERR);
unless(open(STDOUT, ">> $LOG_FILE")) {
logger("Can't write to LOG: $!");
}
unless(open(STDERR, ">> $LOG_FILE")) { # >>&STDOUT XXX
logger("Can't write to LOG: $!");
}
logger("$myself: reopening log file.");
}
sub ALRM_handler {
my $myself = (caller(0))[3];
my ($sec, $min, $hour, $mday) = localtime(time);
if($sec == 0) {
foreach my $g (@graphs) {
my ($t) = split('_', $g);
logger("$myself: calling $g()") unless !$opt_d;
if(!grep {$_ eq $t} (@graphs_debug)) {
undef($t);
}
eval {&$g($t);};
if($@) {
logger("$g(): $@");
}
}
if($PC_LAN eq "Y" && $PC_ENABLE_MONTHLY_REPORTS eq "Y") {
if($min == 0 && $hour == 0) {
get_counters();
if($mday == 1) {
send_reports();
}
}
}
}
alarm(1);
}
sub logger {
my ($msg) = @_;
$msg = localtime() . " - " . $msg;
print("$msg\n");
}
sub daemonize {
if(fork) {
exit(0); # parent exits
}
setsid();
foreach(0 .. (sysconf(&POSIX::_SC_OPEN_MAX) || 1024)) {
close($_);
}
unless(chdir("/")) {
die("Can't chdir to /: $!");
}
unless(umask(022)) {
die("Unable to umask 022: $!");
}
# unless(open(STDIN, "< /dev/null")) {
# die("Can't read /dev/null: $!");
# }
unless(open(STDOUT, ">> $LOG_FILE")) {
die("Can't write to LOG: $!");
}
unless(open(STDERR, ">> $LOG_FILE")) { # >>&STDOUT XXX
die("Can't write to LOG: $!");
}
}
sub usage {
print(STDERR << "EOF");
Usage: monitorix -c configfile [-p pidfile] [-d none | graph[,graph] | all ] [-v]
EOF
exit(1);
}
sub create_index {
my $myself = (caller(0))[3];
my $n;
my $gname;
my %rgraphs = reverse %GRAPHS;
if($THEME_COLOR eq "black") {
$bgcolor = $BLACK{main_bg};
$table_back_color = $BLACK{graph_bg};
$title_back_color = $BLACK{title_bg};
$title_fore_color = $BLACK{title_fg};
} elsif(!$THEME_COLOR || $THEME_COLOR eq "white") {
$bgcolor = "#ffffff";
$table_back_color = "#CCCCCC";
$title_back_color = "#777777";
$title_fore_color = "#CCCC00";
} else {
logger("$myself: ERROR: invalid value in \$THEME_COLOR") unless !$opt_d;
}
if(!open(OUT, "> $BASE_DIR/index.html")) {
die "unable to create ${BASE_DIR}index.html. $!";
}
print(OUT <<EOF);
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>$TITLE</title>
<link rel="shortcut icon" href="$FAVICON">
</head>
<body bgcolor="$bgcolor" text="#888888" vlink="#888888" link="#888888">
<center>
<p>
<br>
<font face="Verdana, sans-serif">
<table>
<tr>
<td>
<a href="http://www.monitorix.org/"><img src="logo_top.png" border="0"></a>
</td>
</tr>
<tr>
<td>
<h2 align="right">v@{[VERSION]}</h2>
</td>
</tr>
</table>
<p>
<form action="$BASE_CGI/monitorix.cgi" method="get">
<table cellspacing="5" cellpadding="0" bgcolor="$table_back_color" border="1">
<tr>
<td bgcolor="$title_back_color">
<font color="$title_fore_color">
<b>&nbsp;Hostname&nbsp;</b>
</font>
</td>
<td bgcolor="$title_back_color">
<font color="$title_fore_color">
<b>&nbsp;Graph&nbsp;</b>
</font>
</td>
</tr>
<tr>
EOF
if(!$FAVICON) {
logger("WARNING: the option \$FAVICON is missing in your configuration file. Do you really have the latest version?");
}
print(OUT " <td bgcolor='$bgcolor'>\n");
print(OUT " <select name='mode' size='1'>\n");
print(OUT " <option value='localhost'>Local Host</option>\n");
if(scalar(@REMOTEHOST_LIST) && $MULTIHOST eq "Y") {
print(OUT " <optgroup label='Multihost'>\n");
print(OUT " <option value='multihost.all'>All Hosts</option>\n");
for($n = 0; $n < scalar(@REMOTEHOST_LIST); $n += 2) {
print(OUT " <option value='multihost." . ($n / 2). "'>" . $REMOTEHOST_LIST[$n] . "</option>\n");
}
print(OUT " </optgroup>\n");
}
if(scalar(@REMOTEGROUP_LIST) && $MULTIHOST eq "Y" && $GROUPS eq "Y" ) {
print(OUT " <optgroup label='Multihost-Groups'>\n");
print(OUT " <option value='multihost.group'>All Groups</option>\n");
for($n = 0; $n < scalar(@REMOTEGROUP_LIST); $n += 2) {
print(OUT " <option value='multihost.group" . ($n / 2). "'>" . $REMOTEGROUP_LIST[$n] . "</option>\n");
}
print(OUT " </optgroup>\n");
}
if(scalar(@PC_LIST) && $PC_LAN eq "Y") {
print(OUT " <optgroup label='LAN PCs'>\n");
print(OUT " <option value='pc.all'>All LAN PCs</option>\n");
for($n = 0; $n < scalar(@PC_LIST); $n++) {
print(OUT " <option value='pc." . $n . "'>" . $PC_LIST[$n] . "</option>\n");
}
print(OUT " </optgroup>\n");
}
print(OUT " </select>\n");
print(OUT " </td>\n");
print(OUT " <td bgcolor='$bgcolor'>\n");
print(OUT " <select name='graph' size='1'>\n");
print(OUT " <option value='all'>All graphs</option>\n");
foreach my $g (@GRAPH_NAME) {
if($GRAPH_ENABLE{$g} eq "Y") {
print(OUT " <optgroup label='" . $GRAPH_TITLE{$g} . "'>\n");
if($g eq "proc") {
for($n = 0; $n < $PROC_MAX; $n++) {
$gname = "_" . $g;
print(OUT " <option value='" . $gname . $n . "'>" . $rgraphs{$gname} . " " . $n . "</option>\n");
}
next;
}
if($g eq "net") {
my $n2;
for($n = 0; $n < scalar(@NET_LIST); $n++) {
$gname = "_" . $g;
for($n2 = 1; $n2 <= 3; $n2++) {
$str = $rgraphs{$gname . $n2};
$str =~ s/^/$NET_LIST[$n] /;
print(OUT " <option value='" . $gname . $n . $n2 . "'>" . $str . "</option>\n");
}
}
next;
}
if($g eq "port") {
for($n = 0; $n < $PORT_MAX && $n < scalar(@PORT_LIST); $n++) {
$gname = "_" . $g;
print(OUT " <option value='" . $gname . $n . "'>" . $rgraphs{$gname} . " " . $PORT_LIST[$n] . " (" . $PORT_NAME[$n] . ")" . "</option>\n");
}
next;
}
if($g eq "fail2ban") {
for($n = 0; $n < scalar(@FAIL2BAN_LIST); $n++) {
$gname = "_" . $g;
print(OUT " <option value='" . $gname . $n . "'>" . ($FAIL2BAN_DESC[$n] ? $FAIL2BAN_DESC[$n] : $rgraphs{$gname}) . "</option>\n");
}
next;
}
$n = 0;
foreach my $k (sort keys %rgraphs) {
if($k =~ m/$g/) {
$gname = "_" . $g . ++$n;
if($rgraphs{$gname}) {
print(OUT " <option value='" . $gname ."'>" . $rgraphs{$gname} . "</option>\n");
}
}
}
print(OUT " </optgroup>\n");
}
}
print(OUT " </select>\n");
print(OUT " </td>\n");
print(OUT <<EOF);
</tr>
</table>
<p>
<table cellspacing="5" cellpadding="0" bgcolor="$table_back_color" border="1">
<tr>
<td bgcolor="$title_back_color">
<input type="radio" checked name="when" value="1day">
<font color="$title_fore_color"><b>Daily&nbsp;</b></font>
</td>
<td bgcolor="$title_back_color">
<input type="radio" name="when" value="1week">
<font color="$title_fore_color"><b>Weekly&nbsp;</b></font>
</td>
<td bgcolor="$title_back_color">
<input type="radio" name="when" value="1month">
<font color="$title_fore_color"><b>Monthly&nbsp;</b></font>
</td>
<td bgcolor="$title_back_color">
<input type="radio" name="when" value="1year">
<font color="$title_fore_color"><b>Yearly&nbsp;</b></font>
</td>
</tr>
</table>
<p>
<input type="hidden" name="color" value="$THEME_COLOR">
<input type="submit" value=" Ok ">
</form>
</font>
</center>
</body>
</html>
EOF
close(OUT);
}
sub flush_accounting_rules {
my $num = 0;
# flushes out any Monitorix iptables/ipfw rules
if($os eq "Linux") {
logger("Flushing out iptables rules.") unless !$opt_d;
if(open(IN, "iptables -nxvL INPUT --line-numbers |")) {
my @rules;
my @names;
while(<IN>) {
my ($rule, undef, undef, $name) = split(' ', $_);
if($name =~ /MONITORIX_IN/ || /NGINX_IN/) {
push(@rules, $rule);
push(@names, $name);
}
}
close(IN);
@rules = reverse(@rules);
foreach(@rules) {
system("iptables -D INPUT $_");
$num++;
}
foreach(@names) {
system("iptables -X $_");
}
}
if(open(IN, "iptables -nxvL OUTPUT --line-numbers |")) {
my @rules;
my @names;
while(<IN>) {
my ($rule, undef, undef, $name) = split(' ', $_);
if($name =~ /MONITORIX_OUT/ || /NGINX_OUT/) {
push(@rules, $rule);
push(@names, $name);
}
}
close(IN);
@rules = reverse(@rules);
foreach(@rules) {
system("iptables -D OUTPUT $_");
$num++;
}
foreach(@names) {
system("iptables -X $_");
}
}
logger("$num iptables rules have been flushed.") unless !$opt_d;
}
if($os eq "FreeBSD" || $os eq "OpenBSD") {
logger("Flushing out ipfw rules.") unless !$opt_d;
system("ipfw delete $PORT_RULE 2>/dev/null");
system("ipfw delete $NGINX_RULE 2>/dev/null");
}
}
# SYSTEM graph
# ----------------------------------------------------------------------------
sub system_init {
my $myself = (caller(0))[3];
if(!(-e $SYSTEM_RRD)) {
logger("Creating '$SYSTEM_RRD' file.");
eval {
RRDs::create($SYSTEM_RRD,
"--step=60",
"DS:system_load1:GAUGE:120:0:U",
"DS:system_load5:GAUGE:120:0:U",
"DS:system_load15:GAUGE:120:0:U",
"DS:system_nproc:GAUGE:120:0:U",
"DS:system_npslp:GAUGE:120:0:U",
"DS:system_nprun:GAUGE:120:0:U",
"DS:system_npwio:GAUGE:120:0:U",
"DS:system_npzom:GAUGE:120:0:U",
"DS:system_npstp:GAUGE:120:0:U",
"DS:system_npswp:GAUGE:120:0:U",
"DS:system_mtotl:GAUGE:120:0:U",
"DS:system_mbuff:GAUGE:120:0:U",
"DS:system_mcach:GAUGE:120:0:U",
"DS:system_mfree:GAUGE:120:0:U",
"DS:system_macti:GAUGE:120:0:U",
"DS:system_minac:GAUGE:120:0:U",
"DS:system_val01:GAUGE:120:0:U",
"DS:system_val02:GAUGE:120:0:U",
"DS:system_val03:GAUGE:120:0:U",
"DS:system_val04:GAUGE:120:0:U",
"DS:system_val05:GAUGE:120:0:U",
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $SYSTEM_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
if($ENABLE_ALERTS eq "Y") {
if(! -x $ALERT_LOADAVG_SCRIPT) {
logger("$myself: ERROR: script '$ALERT_LOADAVG_SCRIPT' doesn't exist or don't has execute permissions.");
}
}
our $system_hist = 0;
push(@graphs, "system_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub system_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my $load1;
my $load5;
my $load15;
my $nproc;
my $npslp;
my $nprun;
my $npwio;
my $npzom;
my $npstp;
my $npswp;
my $mtotl;
my $mbuff;
my $mcach;
my $mfree;
my $macti;
my $minac;
my $val01 = 0;
my $val02 = 0;
my $val03 = 0;
my $val04 = 0;
my $val05 = 0;
my $rrdata = "N";
$npwio = $npzom = $npstp = $npswp = 0;
if($os eq "Linux") {
open(IN, "/proc/loadavg");
while(<IN>) {
if(/^(\d+\.\d+) (\d+\.\d+) (\d+\.\d+) (\d+)\/(\d+)/) {
$load1 = $1;
$load5 = $2;
$load15 = $3;
$nprun = $4;
$npslp = $5;
}
}
close(IN);
$nproc = $npslp + $nprun;
open(IN, "/proc/meminfo");
while(<IN>) {
if(/^MemTotal:\s+(\d+) kB$/) {
$mtotl = $1;
next;
}
if(/^MemFree:\s+(\d+) kB$/) {
$mfree = $1;
next;
}
if(/^Buffers:\s+(\d+) kB$/) {
$mbuff = $1;
next;
}
if(/^Cached:\s+(\d+) kB$/) {
$mcach = $1;
last;
}
}
close(IN);
$macti = $minac = "";
} elsif($os eq "FreeBSD") {
my $page_size;
open(IN, "sysctl -n vm.loadavg |");
while(<IN>) {
if(/^\{ (\d+\.\d+) (\d+\.\d+) (\d+\.\d+) \}$/) {
$load1 = $1;
$load5 = $2;
$load15 = $3;
}
}
close(IN);
open(IN, "sysctl vm.vmtotal |");
while(<IN>) {
if(/^Processes:\s+\(RUNQ:\s+(\d+) Disk.*? Sleep:\s+(\d+)\)$/) {
$nprun = $1;
$npslp = $2;
}
if(/^Free Memory Pages:\s+(\d+)K$/) {
$mfree = $1;
}
}
close(IN);
$nproc = $npslp + $nprun;
$mtotl = `sysctl -n hw.realmem`;
$mbuff = `sysctl -n vfs.bufspace`;
$mcach = `sysctl -n vm.stats.vm.v_cache_count`;
chomp($mbuff);
$mbuff = $mbuff / 1024;
chomp($mtotl);
$mtotl = $mtotl / 1024;
$page_size = `sysctl -n vm.stats.vm.v_page_size`;
$macti = `sysctl -n vm.stats.vm.v_active_count`;
$minac = `sysctl -n vm.stats.vm.v_inactive_count`;
chomp($page_size, $mcach, $macti, $minac);
$mcach = ($page_size * $mcach) / 1024;
$macti = ($page_size * $macti) / 1024;
$minac = ($page_size * $minac) / 1024;
} elsif($os eq "OpenBSD" || $os eq "NetBSD") {
open(IN, "sysctl -n vm.loadavg |");
while(<IN>) {
if(/^(\d+\.\d+) (\d+\.\d+) (\d+\.\d+)$/) {
$load1 = $1;
$load5 = $2;
$load15 = $3;
}
}
close(IN);
open(IN, "top -b |");
while(<IN>) {
if(/ processes:/) {
$_ =~ s/:/,/;
my (@tmp) = split(',', $_);
foreach(@tmp) {
my ($num, $desc) = split(' ', $_);
$nproc = $num unless $desc ne "processes";
if(grep {$_ eq $desc} ("idle", "sleeping", "stopped", "zombie")) {
$npslp += $num;
}
if($desc eq "running" || $desc eq "on") {
$nprun += $num;
}
}
}
if($os eq "OpenBSD") {
if(/^Memory: Real: (\d+)\w\/\d+\w act\/tot Free: (\d+)\w /) {
$macti = $1;
$mfree = $2;
$macti = int($macti) * 1024;
$mfree = int($mfree) * 1024;
last;
}
}
if($os eq "NetBSD") {
if(/^Memory: (\d+)\w Act, .*, (\d+)\w Free/) {
$macti = $1;
$mfree = $2;
$macti = int($macti) * 1024;
$mfree = int($mfree) * 1024;
last;
}
}
}
close(IN);
$mtotl = `sysctl -n hw.physmem`;
chomp($mtotl);
$mtotl = $mtotl / 1024;
}
chomp(
$load1,
$load5,
$load15,
$nproc,
$npslp,
$nprun,
$npwio,
$npzom,
$npstp,
$npswp,
$mtotl,
$mbuff,
$mcach,
$mfree,
$macti,
$minac,
);
# SYSTEM alert
if($ENABLE_ALERTS eq "Y") {
if(!$ALERT_LOADAVG_THRESHOLD || $load15 < $ALERT_LOADAVG_THRESHOLD) {
$system_hist = 0;
} else {
if(!$system_hist) {
$system_hist = time;
}
if($system_hist > 0 && (time - $system_hist) > $ALERT_LOADAVG_TIMEINTVL) {
if(-x $ALERT_LOADAVG_SCRIPT) {
system($ALERT_LOADAVG_SCRIPT . " " .$ALERT_LOADAVG_TIMEINTVL . " " . $ALERT_LOADAVG_THRESHOLD . " " . $load15);
}
$system_hist = time;
}
}
}
$rrdata .= ":$load1:$load5:$load15:$nproc:$npslp:$nprun:$npwio:$npzom:$npstp:$npswp:$mtotl:$mbuff:$mcach:$mfree:$macti:$minac:$val01:$val02:$val03:$val04:$val05";
RRDs::update($SYSTEM_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $SYSTEM_RRD: $err") if $err;
}
# KERN graph
# ----------------------------------------------------------------------------
sub kern_init {
my $myself = (caller(0))[3];
if(!(-e $KERN_RRD)) {
logger("Creating '$KERN_RRD' file.");
eval {
RRDs::create($KERN_RRD,
"--step=60",
"DS:kern_user:GAUGE:120:0:100",
"DS:kern_nice:GAUGE:120:0:100",
"DS:kern_sys:GAUGE:120:0:100",
"DS:kern_idle:GAUGE:120:0:100",
"DS:kern_iow:GAUGE:120:0:100",
"DS:kern_irq:GAUGE:120:0:100",
"DS:kern_sirq:GAUGE:120:0:100",
"DS:kern_steal:GAUGE:120:0:100",
"DS:kern_guest:GAUGE:120:0:100",
"DS:kern_cs:COUNTER:120:0:U",
"DS:kern_dentry:GAUGE:120:0:100",
"DS:kern_file:GAUGE:120:0:100",
"DS:kern_inode:GAUGE:120:0:100",
"DS:kern_forks:COUNTER:120:0:U",
"DS:kern_vforks:COUNTER:120:0:U",
"DS:kern_val03:GAUGE:120:0:100",
"DS:kern_val04:GAUGE:120:0:100",
"DS:kern_val05:GAUGE:120:0:100",
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $KERN_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
# Since 2.2.0 two new values are used (kern_val01 and kern_val02) to
# support 'forks' and 'vforks'. These two values need to be converted
# to COUNTER and renamed.
RRDs::tune($KERN_RRD,
"--data-source-rename=kern_val01:kern_forks",
"--data-source-type=kern_forks:COUNTER",
"--maximum=kern_forks:U",
);
RRDs::tune($KERN_RRD,
"--data-source-rename=kern_val02:kern_vforks",
"--data-source-type=kern_vforks:COUNTER",
"--maximum=kern_vforks:U",
);
our %kernel_hist = ();
push(@graphs, "kern_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub kern_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my $user;
my $nice;
my $sys;
my $idle;
my $iow;
my $irq;
my $sirq;
my $steal;
my $guest;
my $cs;
my $dentry;
my $file;
my $inode;
my $forks;
my $vforks = 0;
my $val03 = 0;
my $val04 = 0;
my $val05 = 0;
my $lastuser = 0;
my $lastnice = 0;
my $lastsys = 0;
my $lastidle = 0;
my $lastiow = 0;
my $lastirq = 0;
my $lastsirq = 0;
my $laststeal = 0;
my $lastguest = 0;
my $rrdata = "N";
if($kernel_hist{'kernel'}) {
(undef, $lastuser, $lastnice, $lastsys, $lastidle, $lastiow, $lastirq, $lastsirq, $laststeal, $lastguest) = split(' ', $kernel_hist{'kernel'});
}
if($os eq "Linux") {
open(IN, "/proc/stat");
while(<IN>) {
if(/^cpu /) {
(undef, $user, $nice, $sys, $idle, $iow, $irq, $sirq, $steal, $guest) = split(' ', $_);
$kernel_hist{'kernel'} = $_;
next;
}
if(/^ctxt (\d+)$/) {
# avoid initial spike
$cs = int($1) unless !$kernel_hist{'cs'};
$kernel_hist{'cs'} = int($1) unless $kernel_hist{'cs'};
next;
}
if(/^processes (\d+)$/) {
# avoid initial spike
$forks = int($1) unless !$kernel_hist{'forks'};
$kernel_hist{'forks'} = int($1) unless $kernel_hist{'forks'};
$vforks = 0;
last;
}
}
close(IN);
open(IN, "/proc/sys/fs/dentry-state");
while(<IN>) {
if(/^(\d+)\s+(\d+)\s+/) {
$dentry = ($1 * 100) / ($1 + $2);
}
}
close(IN);
open(IN, "/proc/sys/fs/file-nr");
while(<IN>) {
if(/^(\d+)\s+\d+\s+(\d+)$/) {
$file = ($1 * 100) / $2;
}
}
close(IN);
open(IN, "/proc/sys/fs/inode-nr");
while(<IN>) {
if(/^(\d+)\s+(\d+)$/) {
$inode = ($1 * 100) / ($1 + $2);
}
}
close(IN);
} elsif($os eq "FreeBSD") {
my $max;
my $num;
my $data;
my $cptime = `sysctl -n kern.cp_time`;
chomp($cptime);
my @tmp = split(' ', $cptime);
($user, $nice, $sys, $iow, $idle) = @tmp;
$kernel_hist{'kernel'} = join(' ', "cpu", $user, $nice, $sys, $idle, $iow);
$data = `sysctl -n vm.stats.sys.v_swtch`;
chomp($data);
$cs = int($data) unless !$kernel_hist{'cs'};
$kernel_hist{'cs'} = int($data) unless $kernel_hist{'cs'};
$data = `sysctl -n vm.stats.vm.v_forks`;
chomp($data);
$forks = int($data) unless !$kernel_hist{'forks'};
$kernel_hist{'forks'} = int($data) unless $kernel_hist{'forks'};
$data = `sysctl -n vm.stats.vm.v_vforks`;
chomp($data);
$vforks = int($data) unless !$kernel_hist{'vforks'};
$kernel_hist{'vforks'} = int($data) unless $kernel_hist{'vforks'};
$max = `sysctl -n kern.maxfiles`;
chomp($max);
$num = `sysctl -n kern.openfiles`;
chomp($num);
$file = ($num * 100) / $max;
$max = `sysctl -n kern.maxvnodes`;
chomp($max);
$num = `sysctl -n vfs.numvnodes`;
chomp($num);
$inode = ($num * 100) / $max;
} elsif($os eq "OpenBSD") {
my $max;
my $num;
my $data;
my $cptime = `sysctl -n kern.cp_time`;
chomp($cptime);
my @tmp = split(',', $cptime);
($user, $nice, $sys, $iow, $idle) = @tmp;
$kernel_hist{'kernel'} = join(' ', "cpu", $user, $nice, $sys, $idle, $iow);
open(IN, "vmstat -s |");
while(<IN>) {
if(/^\s*(\d+) cpu context switches$/) {
$cs = int($1) unless !$kernel_hist{'cs'};
$kernel_hist{'cs'} = int($1) unless $kernel_hist{'cs'};
last;
}
}
close(IN);
$data = `sysctl -n kern.forkstat.forks`;
chomp($data);
$forks = int($data) unless !$kernel_hist{'forks'};
$kernel_hist{'forks'} = int($data) unless $kernel_hist{'forks'};
$data = `sysctl -n kern.forkstat.vforks`;
chomp($data);
$vforks = int($data) unless !$kernel_hist{'vforks'};
$kernel_hist{'vforks'} = int($data) unless $kernel_hist{'vforks'};
$max = `sysctl -n kern.maxfiles`;
chomp($max);
$num = `sysctl -n kern.nfiles`;
chomp($num);
$file = ($num * 100) / $max;
$max = `sysctl -n kern.maxvnodes`;
chomp($max);
$data = `sysctl -n kern.malloc.kmemstat.vnodes`;
($num) = ($data =~ m/^\(inuse = (\d+), /);
$inode = ($num * 100) / $max;
} elsif($os eq "NetBSD") {
my $max;
my $num;
my $data;
my $cptime = `sysctl -n kern.cp_time`;
chomp($cptime);
my @tmp = ($cptime =~ m/user = (\d+), nice = (\d+), sys = (\d+), intr = (\d+), idle = (\d+)/);
($user, $nice, $sys, $iow, $idle) = @tmp;
$kernel_hist{'kernel'} = join(' ', "cpu", $user, $nice, $sys, $idle, $iow);
open(IN, "vmstat -s |");
while(<IN>) {
if(/^\s*(\d+) CPU context switches$/) {
$cs = int($1) unless !$kernel_hist{'cs'};
$kernel_hist{'cs'} = int($1) unless $kernel_hist{'cs'};
next;
}
if(/^\s*(\d+) forks total$/) {
$forks = int($1) unless !$kernel_hist{'forks'};
$kernel_hist{'forks'} = int($1) unless $kernel_hist{'forks'};
next;
}
}
close(IN);
$vforks = 0;
open(IN, "pstat -T |");
while(<IN>) {
if(/^(\d+)\/(\d+) files$/) {
$file = ($1 * 100) / $2;
}
}
close(IN);
$inode = 0;
}
# Linux 2.4, early Linux 2.6 versions and other systems don't have
# these values.
$iow = 0 unless $iow;
$irq = 0 unless $irq;
$sirq = 0 unless $sirq;
$steal = 0 unless $sirq;
$guest = 0 unless $guest;
$lastiow = 0 unless $lastiow;
$lastirq = 0 unless $lastirq;
$lastsirq = 0 unless $lastsirq;
$laststeal = 0 unless $lastsirq;
$lastguest = 0 unless $lastguest;
if($user >= $lastuser && $nice >= $lastnice && $sys >= $lastsys && $idle >= $lastidle && $iow >= $lastiow && $irq >= $lastirq && $sirq >= $lastsirq && $steal >= $laststeal && $guest >= $lastguest) {
my $user_ = $user - $lastuser;
my $nice_ = $nice - $lastnice;
my $sys_ = $sys - $lastsys;
my $idle_ = $idle - $lastidle;
my $iow_ = $iow - $lastiow;
my $irq_ = $irq - $lastirq;
my $sirq_ = $sirq - $lastsirq;
my $steal_ = $steal - $laststeal;
my $guest_ = $guest - $lastguest;
my $total = $user_ + $nice_ + $sys_ + $idle_ + $iow_ + $irq_ + $sirq_ + $steal_ + $guest_;
$user = ($user_ * 100) / $total;
$nice = ($nice_ * 100) / $total;
$sys = ($sys_ * 100) / $total;
$idle = ($idle_ * 100) / $total;
$iow = ($iow_ * 100) / $total;
$irq = ($irq_ * 100) / $total;
$sirq = ($sirq_ * 100) / $total;
$steal = ($steal_ * 100) / $total;
$guest = ($guest_ * 100) / $total;
} else {
$user = "nan";
$nice = "nan";
$sys = "nan";
$idle = "nan";
$iow = "nan";
$irq = "nan";
$sirq = "nan";
$steal = "nan";
$guest = "nan";
}
$rrdata .= ":$user:$nice:$sys:$idle:$iow:$irq:$sirq:$steal:$guest:$cs:$dentry:$file:$inode:$forks:$vforks:$val03:$val04:$val05";
RRDs::update($KERN_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $KERN_RRD: $err") if $err;
}
# PROC graph
# ----------------------------------------------------------------------------
sub proc_init {
my $myself = (caller(0))[3];
my $info;
my @ds;
my @tmp;
my $n;
if(!grep {$_ eq $os} ("Linux", "FreeBSD")) {
logger("$myself is not supported yet by your operating system ($os).");
return;
}
if(-e $PROC_RRD) {
$info = RRDs::info($PROC_RRD);
for my $key (keys %$info) {
if(index($key, 'ds[') == 0) {
if(index($key, '.type') != -1) {
# print("$key\n");
push(@ds, substr($key, 3, index($key, ']') - 3));
}
}
}
# foreach(@ds) {
# print($_ . "\n");
# }
# print(scalar(@ds) . "\n");
if(scalar(@ds) / 9 != $PROC_MAX) {
logger("Detected size mismatch between \$PROC_MAX ($PROC_MAX) and $PROC_RRD (" . scalar(@ds) / 9 . "). Resizing it accordingly. All historic data will be lost. Backup file created.");
rename($PROC_RRD, "$PROC_RRD.bak");
}
}
if(!(-e $PROC_RRD)) {
logger("Creating '$PROC_RRD' file.");
for($n = 0; $n < $PROC_MAX; $n++) {
push(@tmp, "DS:proc" . $n . "_user:GAUGE:120:0:100");
push(@tmp, "DS:proc" . $n . "_nice:GAUGE:120:0:100");
push(@tmp, "DS:proc" . $n . "_sys:GAUGE:120:0:100");
push(@tmp, "DS:proc" . $n . "_idle:GAUGE:120:0:100");
push(@tmp, "DS:proc" . $n . "_iow:GAUGE:120:0:100");
push(@tmp, "DS:proc" . $n . "_irq:GAUGE:120:0:100");
push(@tmp, "DS:proc" . $n . "_sirq:GAUGE:120:0:100");
push(@tmp, "DS:proc" . $n . "_steal:GAUGE:120:0:100");
push(@tmp, "DS:proc" . $n . "_guest:GAUGE:120:0:100");
}
eval {
RRDs::create($PROC_RRD,
"--step=60",
@tmp,
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $PROC_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
our %proc_hist = ();
push(@graphs, "proc_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub proc_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my @proc;
my $total;
my $n;
my @lastproc;
my @p;
my @l;
my $rrdata = "N";
# Read last processor usage data
my $str;
for($n = 0; $n < $PROC_MAX; $n++) {
$str = "cpu" . $n;
if($proc_hist{$str}) {
push(@lastproc, $proc_hist{$str});
}
}
if($os eq "Linux") {
open(IN, "/proc/stat");
while(<IN>) {
for($n = 0; $n < $PROC_MAX; $n++) {
$str = "cpu" . $n;
if(/^cpu$n /) {
$proc_hist{$str} = $_;
chomp($proc_hist{$str});
push(@proc, $proc_hist{$str});
}
}
}
close(IN);
} elsif($os eq "FreeBSD") {
my $cptimes;
my @tmp;
my $from;
my $to;
my $ncpu = `sysctl -n hw.ncpu`;
open(IN, "sysctl -n kern.cp_times |");
my @data = split(' ', <IN>);
close(IN);
chomp($ncpu);
for($n = 0; $n < $PROC_MAX; $n++) {
$str = "cpu" . $n;
$from = $n * 5;
$to = $from + 4;
@tmp = @data[$from..$to];
@tmp[0, 1, 2, 3, 4] = @tmp[0, 1, 2, 4, 3];
$cptimes = join(' ', @tmp);
chomp($cptimes);
$cptimes = $str . " " . $cptimes;
$proc_hist{$str} = $cptimes;
push(@proc, $cptimes);
}
}
my @deltas;
for($n = 0; $n < $PROC_MAX; $n++) {
if($proc[$n]) {
@p = split(' ', $proc[$n]);
@l = split(' ', $lastproc[$n]);
@deltas = (
$p[1] - $l[1], # user
$p[2] - $l[2], # nice
$p[3] - $l[3], # sys
$p[4] - $l[4], # idle
$p[5] - $l[5], # iow
$p[6] - $l[6], # irq
$p[7] - $l[7], # sirq
$p[8] - $l[8], # steal
$p[9] - $l[9], # guest
);
$total = $deltas[0] + $deltas[1] + $deltas[2] + $deltas[3] + $deltas[4] + $deltas[5] + $deltas[6] + $deltas[7] + $deltas[8];
undef(@p);
push(@p, $deltas[0] ? ($deltas[0] * 100) / $total : 0);
push(@p, $deltas[1] ? ($deltas[1] * 100) / $total : 0);
push(@p, $deltas[2] ? ($deltas[2] * 100) / $total : 0);
push(@p, $deltas[3] ? ($deltas[3] * 100) / $total : 0);
push(@p, $deltas[4] ? ($deltas[4] * 100) / $total : 0);
push(@p, $deltas[5] ? ($deltas[5] * 100) / $total : 0);
push(@p, $deltas[6] ? ($deltas[6] * 100) / $total : 0);
push(@p, $deltas[7] ? ($deltas[7] * 100) / $total : 0);
push(@p, $deltas[8] ? ($deltas[8] * 100) / $total : 0);
$proc[$n] = join(' ', @p);
} else {
$proc[$n] = join(' ', (0, 0, 0, 0, 0, 0, 0, 0, 0));
}
}
for($n = 0; $n < $PROC_MAX; $n++) {
@p = split(' ', $proc[$n]);
$rrdata .= ":$p[0]:$p[1]:$p[2]:$p[3]:$p[4]:$p[5]:$p[6]:$p[7]:$p[8]";
}
RRDs::update($PROC_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $PROC_RRD: $err") if $err;
}
# HPTEMP graph
# ----------------------------------------------------------------------------
sub hptemp_init {
my $myself = (caller(0))[3];
# save the contents of 'hplog -t' since only 'root' is able to run it
# it also checks if 'hplog' does exists.
if(!open(IN, "hplog -t |")) {
logger("$myself: unable to execute 'hplog'. $!");
return;
}
my @data = <IN>;
close(IN);
open(OUT, "> $BASE_DIR/cgi-bin/monitorix.hplog");
print(OUT @data);
close(OUT);
if(!(-e $HPTEMP_RRD)) {
logger("Creating '$HPTEMP_RRD' file.");
eval {
RRDs::create($HPTEMP_RRD,
"--step=60",
"DS:hptemp1_1:GAUGE:120:0:100",
"DS:hptemp1_2:GAUGE:120:0:100",
"DS:hptemp1_3:GAUGE:120:0:100",
"DS:hptemp1_4:GAUGE:120:0:100",
"DS:hptemp1_5:GAUGE:120:0:100",
"DS:hptemp1_6:GAUGE:120:0:100",
"DS:hptemp1_7:GAUGE:120:0:100",
"DS:hptemp1_8:GAUGE:120:0:100",
"DS:hptemp2_1:GAUGE:120:0:100",
"DS:hptemp2_2:GAUGE:120:0:100",
"DS:hptemp2_3:GAUGE:120:0:100",
"DS:hptemp2_4:GAUGE:120:0:100",
"DS:hptemp2_5:GAUGE:120:0:100",
"DS:hptemp2_6:GAUGE:120:0:100",
"DS:hptemp3_1:GAUGE:120:0:100",
"DS:hptemp3_2:GAUGE:120:0:100",
"DS:hptemp3_3:GAUGE:120:0:100",
"DS:hptemp3_4:GAUGE:120:0:100",
"DS:hptemp3_5:GAUGE:120:0:100",
"DS:hptemp3_6:GAUGE:120:0:100",
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $HPTEMP_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
push(@graphs, "hptemp_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub hptemp_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my @hptemp1;
my @hptemp2;
my @hptemp3;
my $l;
my $n;
my $rrdata = "N";
open(IN, "hplog -t |");
my @data = <IN>;
close(IN);
my $str;
for($l = 0; $l < scalar(@data); $l++) {
$_ = @data[$l];
foreach my $t (@HPTEMP_1) {
$str = sprintf("%2d", $t);
if(/^$str /) {
my $temp = substr($_, 47, 3);
chomp($temp);
push(@hptemp1, int($temp));
}
}
foreach my $t (@HPTEMP_2) {
$str = sprintf("%2d", $t);
if(/^$str /) {
my $temp = substr($_, 47, 3);
chomp($temp);
push(@hptemp2, int($temp));
}
}
foreach my $t (@HPTEMP_3) {
$str = sprintf("%2d", $t);
if(/^$str /) {
my $temp = substr($_, 47, 3);
chomp($temp);
push(@hptemp3, int($temp));
}
}
}
for($n = 0; $n < 8; $n++) {
$hptemp1[$n] = 0 unless $hptemp1[$n];
$rrdata .= ":$hptemp1[$n]";
}
for($n = 0; $n < 6; $n++) {
$hptemp2[$n] = 0 unless $hptemp2[$n];
$rrdata .= ":$hptemp2[$n]";
}
for($n = 0; $n < 6; $n++) {
$hptemp3[$n] = 0 unless $hptemp3[$n];
$rrdata .= ":$hptemp3[$n]";
}
RRDs::update($HPTEMP_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $HPTEMP_RRD: $err") if $err;
}
# LMSENS graph
# ----------------------------------------------------------------------------
sub lmsens_init {
my $myself = (caller(0))[3];
if(!(-e $LMSENS_RRD)) {
logger("Creating '$LMSENS_RRD' file.");
eval {
RRDs::create($LMSENS_RRD,
"--step=60",
"DS:lmsens_mb0:GAUGE:120:0:100",
"DS:lmsens_mb1:GAUGE:120:0:100",
"DS:lmsens_cpu0:GAUGE:120:0:100",
"DS:lmsens_cpu1:GAUGE:120:0:100",
"DS:lmsens_cpu2:GAUGE:120:0:100",
"DS:lmsens_cpu3:GAUGE:120:0:100",
"DS:lmsens_fan0:GAUGE:120:0:U",
"DS:lmsens_fan1:GAUGE:120:0:U",
"DS:lmsens_fan2:GAUGE:120:0:U",
"DS:lmsens_fan3:GAUGE:120:0:U",
"DS:lmsens_fan4:GAUGE:120:0:U",
"DS:lmsens_fan5:GAUGE:120:0:U",
"DS:lmsens_fan6:GAUGE:120:0:U",
"DS:lmsens_fan7:GAUGE:120:0:U",
"DS:lmsens_fan8:GAUGE:120:0:U",
"DS:lmsens_core0:GAUGE:120:0:100",
"DS:lmsens_core1:GAUGE:120:0:100",
"DS:lmsens_core2:GAUGE:120:0:100",
"DS:lmsens_core3:GAUGE:120:0:100",
"DS:lmsens_core4:GAUGE:120:0:100",
"DS:lmsens_core5:GAUGE:120:0:100",
"DS:lmsens_core6:GAUGE:120:0:100",
"DS:lmsens_core7:GAUGE:120:0:100",
"DS:lmsens_core8:GAUGE:120:0:100",
"DS:lmsens_core9:GAUGE:120:0:100",
"DS:lmsens_core10:GAUGE:120:0:100",
"DS:lmsens_core11:GAUGE:120:0:100",
"DS:lmsens_core12:GAUGE:120:0:100",
"DS:lmsens_core13:GAUGE:120:0:100",
"DS:lmsens_core14:GAUGE:120:0:100",
"DS:lmsens_core15:GAUGE:120:0:100",
"DS:lmsens_volt0:GAUGE:120:U:U",
"DS:lmsens_volt1:GAUGE:120:U:U",
"DS:lmsens_volt2:GAUGE:120:U:U",
"DS:lmsens_volt3:GAUGE:120:U:U",
"DS:lmsens_volt4:GAUGE:120:U:U",
"DS:lmsens_volt5:GAUGE:120:U:U",
"DS:lmsens_volt6:GAUGE:120:U:U",
"DS:lmsens_volt7:GAUGE:120:U:U",
"DS:lmsens_volt8:GAUGE:120:U:U",
"DS:lmsens_volt9:GAUGE:120:U:U",
"DS:lmsens_volt10:GAUGE:120:U:U",
"DS:lmsens_volt11:GAUGE:120:U:U",
"DS:lmsens_gpu0:GAUGE:120:0:100",
"DS:lmsens_gpu1:GAUGE:120:0:100",
"DS:lmsens_gpu2:GAUGE:120:0:100",
"DS:lmsens_gpu3:GAUGE:120:0:100",
"DS:lmsens_gpu4:GAUGE:120:0:100",
"DS:lmsens_gpu5:GAUGE:120:0:100",
"DS:lmsens_gpu6:GAUGE:120:0:100",
"DS:lmsens_gpu7:GAUGE:120:0:100",
"DS:lmsens_gpu8:GAUGE:120:0:100",
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $LMSENS_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
push(@graphs, "lmsens_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub get_nvidia_data {
my ($gpu) = @_;
my $total = 0;
my $used = 0;
my $mem = 0;
my $cpu = 0;
my $temp = 0;
my $check_mem = 0;
my $check_cpu = 0;
my $check_temp = 0;
open(IN, "nvidia-smi -q -i $gpu -d MEMORY,UTILIZATION,TEMPERATURE |");
my @data = <IN>;
close(IN);
for($l = 0; $l < scalar(@data); $l++) {
$_ = @data[$l];
if(/Memory Usage/) {
$check_mem = 1;
next;
}
if($check_mem) {
if(/Total/) {
my (undef, $tmp) = split(':', $_);
if($tmp eq "\n") {
$l++;
$tmp = $data[$l];
}
my ($value, undef) = split(' ', $tmp);
$value =~ s/[-]/./;
$value =~ s/[^0-9.]//g;
if(int($value) > 0) {
$total = int($value);
}
}
if(/Used/) {
my (undef, $tmp) = split(':', $_);
if($tmp eq "\n") {
$l++;
$tmp = $data[$l];
}
my ($value, undef) = split(' ', $tmp);
$value =~ s/[-]/./;
$value =~ s/[^0-9.]//g;
if(int($value) > 0) {
$used = int($value);
}
$check_mem = 0;
}
}
if(/Utilization/) {
$check_cpu = 1;
next;
}
if($check_cpu) {
if(/Gpu/) {
my (undef, $tmp) = split(':', $_);
if($tmp eq "\n") {
$l++;
$tmp = $data[$l];
}
my ($value, undef) = split(' ', $tmp);
$value =~ s/[-]/./;
$value =~ s/[^0-9.]//g;
if(int($value) > 0) {
$cpu = int($value);
}
}
if(/Memory/) {
my (undef, $tmp) = split(':', $_);
if($tmp eq "\n") {
$l++;
$tmp = $data[$l];
}
my ($value, undef) = split(' ', $tmp);
$value =~ s/[-]/./;
$value =~ s/[^0-9.]//g;
if(int($value) > 0) {
$mem = int($value);
}
}
$check_cpu = 0;
}
if(/Temperature/) {
$check_temp = 1;
next;
}
if($check_temp) {
if(/Gpu/) {
my (undef, $tmp) = split(':', $_);
if($tmp eq "\n") {
$l++;
$tmp = $data[$l];
}
my ($value, undef) = split(' ', $tmp);
$value =~ s/[-]/./;
$value =~ s/[^0-9.]//g;
if(int($value) > 0) {
$temp = int($value);
}
}
$check_temp = 0;
}
}
# NVIDIA driver v285.+ not supported (needs new output parsing).
# This is to avoid a divide by zero message.
if($total) {
$mem = ($used * 100) / $total;
} else {
$mem = $used = $total = 0;
}
return join(" ", $mem, $cpu, $temp);
}
sub lmsens_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my @mb;
my @cpu;
my @fan;
my @core;
my @volt;
my @gpu;
my $l;
my $n;
my $rrdata = "N";
if($os eq "Linux") {
if(scalar(%SENSORS_LIST)) {
open(IN, "sensors |");
my @data = <IN>;
close(IN);
my $str;
for($l = 0; $l < scalar(@data); $l++) {
$_ = @data[$l];
for($n = 0; $n < 2; $n++) {
$str = "MB" . $n;
$mb[$n] = 0 unless $mb[$n];
if($SENSORS_LIST{$str} && (/^$SENSORS_LIST{$str}:/) && (!/RPM/)) {
my (undef, $tmp) = split(':', $_);
if($tmp eq "\n") {
$l++;
$tmp = $data[$l];
}
my ($value, undef) = split(' ', $tmp);
$mb[$n] = int($value);
}
}
for($n = 0; $n < 4; $n++) {
$str = "CPU" . $n;
$cpu[$n] = 0 unless $cpu[$n];
if($SENSORS_LIST{$str} && (/^$SENSORS_LIST{$str}:/) && (!/RPM/)) {
my (undef, $tmp) = split(':', $_);
if($tmp eq "\n") {
$l++;
$tmp = $data[$l];
}
my ($value, undef) = split(' ', $tmp);
$cpu[$n] = int($value);
}
}
for($n = 0; $n < 9; $n++) {
$str = "FAN" . $n;
$fan[$n] = 0 unless $fan[$n];
if($SENSORS_LIST{$str} && (/^$SENSORS_LIST{$str}:/) && (/RPM/)) {
my (undef, $tmp) = split(':', $_);
if($tmp eq "\n") {
$l++;
$tmp = $data[$l];
}
my ($value, undef) = split(' ', $tmp);
$fan[$n] = int($value);
}
}
for($n = 0; $n < 16; $n++) {
$str = "CORE" . $n;
$core[$n] = 0 unless $core[$n];
if($SENSORS_LIST{$str} && (/^$SENSORS_LIST{$str}:/) && (!/RPM/)) {
my (undef, $tmp) = split(':', $_);
if($tmp eq "\n") {
$l++;
$tmp = $data[$l];
}
my ($value, undef) = split(' ', $tmp);
$core[$n] = int($value);
}
}
for($n = 0; $n < 12; $n++) {
$str = "VOLT" . $n;
$volt[$n] = 0 unless $volt[$n];
if($SENSORS_LIST{$str} && (/^$SENSORS_LIST{$str}:/) && (!/RPM/)) {
my (undef, $tmp) = split(':', $_);
if($tmp eq "\n") {
$l++;
$tmp = $data[$l];
}
my ($value, undef) = split(' ', $tmp);
$volt[$n] = $value;
}
}
}
for($n = 0; $n < 9; $n++) {
$str = "GPU" . $n;
$gpu[$n] = 0 unless $gpu[$n];
if($SENSORS_LIST{$str} && $SENSORS_LIST{$str} eq "nvidia") {
(undef, undef, $gpu[$n]) = split(' ', get_nvidia_data($n));
if(!$gpu[$n]) {
# attempt to get data using the old driver version
open(IN, "nvidia-smi -g $n |");
my @data = <IN>;
close(IN);
for($l = 0; $l < scalar(@data); $l++) {
$_ = @data[$l];
if(/Temperature/) {
my (undef, $tmp) = split(':', $_);
if($tmp eq "\n") {
$l++;
$tmp = $data[$l];
}
my ($value, undef) = split(' ', $tmp);
$value =~ s/[-]/./;
$value =~ s/[^0-9.]//g;
if(int($value) > 0) {
$gpu[$n] = int($value);
}
}
}
}
}
}
}
for($n = 0; $n < scalar(@mb); $n++) {
$rrdata .= ":$mb[$n]";
}
for($n = 0; $n < scalar(@cpu); $n++) {
$rrdata .= ":$cpu[$n]";
}
for($n = 0; $n < scalar(@fan); $n++) {
$rrdata .= ":$fan[$n]";
}
for($n = 0; $n < scalar(@core); $n++) {
$rrdata .= ":$core[$n]";
}
for($n = 0; $n < scalar(@volt); $n++) {
$rrdata .= ":$volt[$n]";
}
for($n = 0; $n < scalar(@gpu); $n++) {
$rrdata .= ":$gpu[$n]";
}
}
RRDs::update($LMSENS_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $LMSENS_RRD: $err") if $err;
}
# NVIDIA graph
# ----------------------------------------------------------------------------
sub nvidia_init {
my $myself = (caller(0))[3];
if(!(-e $NVIDIA_RRD)) {
logger("Creating '$NVIDIA_RRD' file.");
eval {
RRDs::create($NVIDIA_RRD,
"--step=60",
"DS:nvidia_temp0:GAUGE:120:0:U",
"DS:nvidia_temp1:GAUGE:120:0:U",
"DS:nvidia_temp2:GAUGE:120:0:U",
"DS:nvidia_temp3:GAUGE:120:0:U",
"DS:nvidia_temp4:GAUGE:120:0:U",
"DS:nvidia_temp5:GAUGE:120:0:U",
"DS:nvidia_temp6:GAUGE:120:0:U",
"DS:nvidia_temp7:GAUGE:120:0:U",
"DS:nvidia_temp8:GAUGE:120:0:U",
"DS:nvidia_gpu0:GAUGE:120:0:100",
"DS:nvidia_gpu1:GAUGE:120:0:100",
"DS:nvidia_gpu2:GAUGE:120:0:100",
"DS:nvidia_gpu3:GAUGE:120:0:100",
"DS:nvidia_gpu4:GAUGE:120:0:100",
"DS:nvidia_gpu5:GAUGE:120:0:100",
"DS:nvidia_gpu6:GAUGE:120:0:100",
"DS:nvidia_gpu7:GAUGE:120:0:100",
"DS:nvidia_gpu8:GAUGE:120:0:100",
"DS:nvidia_mem0:GAUGE:120:0:100",
"DS:nvidia_mem1:GAUGE:120:0:100",
"DS:nvidia_mem2:GAUGE:120:0:100",
"DS:nvidia_mem3:GAUGE:120:0:100",
"DS:nvidia_mem4:GAUGE:120:0:100",
"DS:nvidia_mem5:GAUGE:120:0:100",
"DS:nvidia_mem6:GAUGE:120:0:100",
"DS:nvidia_mem7:GAUGE:120:0:100",
"DS:nvidia_mem8:GAUGE:120:0:100",
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $NVIDIA_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
push(@graphs, "nvidia_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub nvidia_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my @temp;
my @gpu;
my @mem;
my @data;
my $utilization;
my $l;
my $n;
my $rrdata = "N";
for($n = 0; $n < 9; $n++) {
$temp[$n] = 0;
$gpu[$n] = 0;
$mem[$n] = 0;
if($n < $NVIDIA_MAX) {
($mem[$n], $gpu[$n], $temp[$n]) = split(' ', get_nvidia_data($n));
if(!$temp[$n] && !$gpu[$n] && !$mem[$n]) {
# attempt to get data using the old driver version
$utilization = 0;
open(IN, "nvidia-smi -g $n |");
@data = <IN>;
close(IN);
for($l = 0; $l < scalar(@data); $l++) {
$_ = @data[$l];
if(/Temperature/) {
my (undef, $tmp) = split(':', $_);
if($tmp eq "\n") {
$l++;
$tmp = $data[$l];
}
my ($value, undef) = split(' ', $tmp);
$value =~ s/[-]/./;
$value =~ s/[^0-9.]//g;
if(int($value) > 0) {
$temp[$n] = int($value);
}
}
if(/Utilization/) {
$utilization = 1;
}
if($utilization == 1) {
if(/GPU/) {
my (undef, $tmp) = split(':', $_);
if($tmp eq "\n") {
$l++;
$tmp = $data[$l];
}
my ($value, undef) = split(' ', $tmp);
$value =~ s/[-]/./;
$value =~ s/[^0-9.]//g;
if(int($value) > 0) {
$gpu[$n] = int($value);
}
}
if(/Memory/) {
my (undef, $tmp) = split(':', $_);
if($tmp eq "\n") {
$l++;
$tmp = $data[$l];
}
my ($value, undef) = split(' ', $tmp);
$value =~ s/[-]/./;
$value =~ s/[^0-9.]//g;
if(int($value) > 0) {
$mem[$n] = int($value);
}
}
}
}
}
}
}
for($n = 0; $n < scalar(@temp); $n++) {
$rrdata .= ":$temp[$n]";
}
for($n = 0; $n < scalar(@gpu); $n++) {
$rrdata .= ":$gpu[$n]";
}
for($n = 0; $n < scalar(@mem); $n++) {
$rrdata .= ":$mem[$n]";
}
RRDs::update($NVIDIA_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $NVIDIA_RRD: $err") if $err;
}
# DISK graph
# ----------------------------------------------------------------------------
sub disk_init {
my $myself = (caller(0))[3];
my $info;
my @ds;
my @tmp;
my $n;
# This warns and transforms an old @DISK_LIST array (before 2.6) into
# the new two-dimensional array to keep backwards compatibility.
my @tmp2;
if(!(ref($DISK_LIST[0]) eq "ARRAY")) {
logger("$myself: \@DISK_LIST array format is outdated. Please check monitorix.conf(5) man page to see how this array must be defined.");
push(@{$tmp2[0]}, @DISK_LIST);
@DISK_LIST = @tmp2;
}
if(-e $DISK_RRD) {
$info = RRDs::info($DISK_RRD);
for my $key (keys %$info) {
if(index($key, 'ds[') == 0) {
if(index($key, '.type') != -1) {
push(@ds, substr($key, 3, index($key, ']') - 3));
}
}
}
if(scalar(@ds) / 24 != scalar(@DISK_LIST)) {
logger("Detected size mismatch between \@DISK_LIST (" . scalar(@DISK_LIST) . ") and $DISK_RRD (" . scalar(@ds) / 24 . "). Resizing it accordingly. All historic data will be lost. Backup file created.");
rename($DISK_RRD, "$DISK_RRD.bak");
}
}
if(!(-e $DISK_RRD)) {
logger("Creating '$DISK_RRD' file.");
for($n = 0; $n < scalar(@DISK_LIST); $n++) {
push(@tmp, "DS:disk" . $n . "_hd0_temp:GAUGE:120:0:100");
push(@tmp, "DS:disk" . $n . "_hd0_smart1:GAUGE:120:0:U");
push(@tmp, "DS:disk" . $n . "_hd0_smart2:GAUGE:120:0:U");
push(@tmp, "DS:disk" . $n . "_hd1_temp:GAUGE:120:0:100");
push(@tmp, "DS:disk" . $n . "_hd1_smart1:GAUGE:120:0:U");
push(@tmp, "DS:disk" . $n . "_hd1_smart2:GAUGE:120:0:U");
push(@tmp, "DS:disk" . $n . "_hd2_temp:GAUGE:120:0:100");
push(@tmp, "DS:disk" . $n . "_hd2_smart1:GAUGE:120:0:U");
push(@tmp, "DS:disk" . $n . "_hd2_smart2:GAUGE:120:0:U");
push(@tmp, "DS:disk" . $n . "_hd3_temp:GAUGE:120:0:100");
push(@tmp, "DS:disk" . $n . "_hd3_smart1:GAUGE:120:0:U");
push(@tmp, "DS:disk" . $n . "_hd3_smart2:GAUGE:120:0:U");
push(@tmp, "DS:disk" . $n . "_hd4_temp:GAUGE:120:0:100");
push(@tmp, "DS:disk" . $n . "_hd4_smart1:GAUGE:120:0:U");
push(@tmp, "DS:disk" . $n . "_hd4_smart2:GAUGE:120:0:U");
push(@tmp, "DS:disk" . $n . "_hd5_temp:GAUGE:120:0:100");
push(@tmp, "DS:disk" . $n . "_hd5_smart1:GAUGE:120:0:U");
push(@tmp, "DS:disk" . $n . "_hd5_smart2:GAUGE:120:0:U");
push(@tmp, "DS:disk" . $n . "_hd6_temp:GAUGE:120:0:100");
push(@tmp, "DS:disk" . $n . "_hd6_smart1:GAUGE:120:0:U");
push(@tmp, "DS:disk" . $n . "_hd6_smart2:GAUGE:120:0:U");
push(@tmp, "DS:disk" . $n . "_hd7_temp:GAUGE:120:0:100");
push(@tmp, "DS:disk" . $n . "_hd7_smart1:GAUGE:120:0:U");
push(@tmp, "DS:disk" . $n . "_hd7_smart2:GAUGE:120:0:U");
}
eval {
RRDs::create($DISK_RRD,
"--step=60",
@tmp,
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $DISK_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
# Since 2.6.0 disk.rrd supports unlimited disk device monitoring, this
# forces to rename the DS of the first disk accordingly.
for($n = 0; $n < 8; $n++) {
RRDs::tune($DISK_RRD,
"--data-source-rename=disk_hd" . $n . "_temp:disk0_hd" . $n . "_temp",
"--data-source-rename=disk_hd" . $n . "_smart1:disk0_hd" . $n . "_smart1",
"--data-source-rename=disk_hd" . $n . "_smart2:disk0_hd" . $n . "_smart2",
);
}
push(@graphs, "disk_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub disk_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my $temp;
my $smart1;
my $smart2;
my $n;
my $rrdata = "N";
foreach my $i (@DISK_LIST) {
for($n = 0; $n < 8; $n++) {
$temp = 0;
$smart1 = 0;
$smart2 = 0;
if(@$i[$n]) {
my $d = @$i[$n];
open(IN, "smartctl -A $d |");
while(<IN>) {
if(/^ 5/ && /Reallocated_Sector_Ct/) {
my @tmp = split(' ', $_);
$smart1 = $tmp[9];
chomp($smart1);
}
if(/^194/ && /Temperature_Celsius/) {
my @tmp = split(' ', $_);
$temp = $tmp[9];
chomp($temp);
}
if(/^197/ && /Current_Pending_Sector/) {
my @tmp = split(' ', $_);
$smart2 = $tmp[9];
chomp($smart2);
}
if(/^Current Drive Temperature: /) {
my @tmp = split(' ', $_);
$temp = $tmp[3] unless $temp;
chomp($temp);
}
}
close(IN);
$temp = `hddtemp -wqn $d` unless $temp;
chomp($temp);
}
$rrdata .= ":$temp";
$rrdata .= ":$smart1";
$rrdata .= ":$smart2";
}
}
RRDs::update($DISK_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $DISK_RRD: $err") if $err;
}
# FS graph
# ----------------------------------------------------------------------------
sub is_in_diskstats {
my ($d, $major, $minor) = @_;
open(IN, "/proc/diskstats");
my @data = <IN>;
close(IN);
foreach(@data) {
my ($maj, $min, $device) = split(' ', $_);
return $device unless $d ne $device;
if($maj == $major && $min == $minor) {
return $device;
}
}
}
sub is_luks {
my ($d) = @_;
if($d =~ m/luks/) {
$d =~ s/luks-//;
$d = `blkid -t UUID=$d | awk -F ":" '{ print \$1 }'`;
chomp($d);
$d =~ s/^.*dev\///; # remove the /dev/ prefix
$d =~ s/^.*mapper\///; # remove the mapper/ prefix
return $d;
}
}
sub fs_init {
my $myself = (caller(0))[3];
our %FS_DEV;
my $info;
my @ds;
my @tmp;
my $n;
# This warns if an old @FS_LIST array (before 2.6) is still used.
my @tmp2;
if(!(ref($FS_LIST[0]) eq "ARRAY")) {
logger("$myself: \@FS_LIST array format is outdated. Please check monitorix.conf(5) man page to see how this array must be defined.");
push(@{$tmp2[0]}, @FS_LIST);
@FS_LIST = @tmp2;
}
if(-e $FS_RRD) {
$info = RRDs::info($FS_RRD);
for my $key (keys %$info) {
if(index($key, 'ds[') == 0) {
if(index($key, '.type') != -1) {
push(@ds, substr($key, 3, index($key, ']') - 3));
}
}
}
if(scalar(@ds) / 24 != scalar(@FS_LIST)) {
logger("Detected size mismatch between \@FS_LIST (" . scalar(@FS_LIST) . ") and $FS_RRD (" . scalar(@ds) / 24 . "). Resizing it accordingly. All historic data will be lost. Backup file created.");
rename($FS_RRD, "$FS_RRD.bak");
}
}
if(!(-e $FS_RRD)) {
logger("Creating '$FS_RRD' file.");
for($n = 0; $n < scalar(@FS_LIST); $n++) {
push(@tmp, "DS:fs" . $n . "_use0:GAUGE:120:0:100");
push(@tmp, "DS:fs" . $n . "_ioa0:GAUGE:120:0:U");
push(@tmp, "DS:fs" . $n . "_tim0:GAUGE:120:0:U");
push(@tmp, "DS:fs" . $n . "_use1:GAUGE:120:0:100");
push(@tmp, "DS:fs" . $n . "_ioa1:GAUGE:120:0:U");
push(@tmp, "DS:fs" . $n . "_tim1:GAUGE:120:0:U");
push(@tmp, "DS:fs" . $n . "_use2:GAUGE:120:0:100");
push(@tmp, "DS:fs" . $n . "_ioa2:GAUGE:120:0:U");
push(@tmp, "DS:fs" . $n . "_tim2:GAUGE:120:0:U");
push(@tmp, "DS:fs" . $n . "_use3:GAUGE:120:0:100");
push(@tmp, "DS:fs" . $n . "_ioa3:GAUGE:120:0:U");
push(@tmp, "DS:fs" . $n . "_tim3:GAUGE:120:0:U");
push(@tmp, "DS:fs" . $n . "_use4:GAUGE:120:0:100");
push(@tmp, "DS:fs" . $n . "_ioa4:GAUGE:120:0:U");
push(@tmp, "DS:fs" . $n . "_tim4:GAUGE:120:0:U");
push(@tmp, "DS:fs" . $n . "_use5:GAUGE:120:0:100");
push(@tmp, "DS:fs" . $n . "_ioa5:GAUGE:120:0:U");
push(@tmp, "DS:fs" . $n . "_tim5:GAUGE:120:0:U");
push(@tmp, "DS:fs" . $n . "_use6:GAUGE:120:0:100");
push(@tmp, "DS:fs" . $n . "_ioa6:GAUGE:120:0:U");
push(@tmp, "DS:fs" . $n . "_tim6:GAUGE:120:0:U");
push(@tmp, "DS:fs" . $n . "_use7:GAUGE:120:0:100");
push(@tmp, "DS:fs" . $n . "_ioa7:GAUGE:120:0:U");
push(@tmp, "DS:fs" . $n . "_tim7:GAUGE:120:0:U");
}
eval {
RRDs::create($FS_RRD,
"--step=60",
@tmp,
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $FS_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
# This tries to find out the physical device name of each fs.
foreach my $i (@FS_LIST) {
foreach my $fs (@$i) {
my $d = $FS_DEV{$fs};
next unless !$d;
if($fs ne "swap") {
eval {
alarm $TIMEOUT;
open(IN, "df -P $fs |");
while(<IN>) {
if(/ $fs$/) {
($d) = split(' ', $_);
last;
}
}
close(IN);
alarm 0;
chomp($d);
};
}
if($os eq "Linux" && $kernel_branch > 2.4) {
my $lvm;
my $lvm_disk;
my $is_md;
my $found;
if($fs eq "swap") {
$d = `cat /proc/swaps | tail -1 | awk -F " " '{ print \$1 }'`;
chomp($d);
}
# check for device names using symbolic links
# e.g. /dev/disk/by-uuid/db312d12-0da6-44e5-a354-4c82118f4b66
if(-l $d) {
$link = readlink($d);
$d = abs_path(dirname($d) . "/" . $link);
chomp($d);
}
# get the major and minor of $d
my $rdev = (stat($d))[6];
my $minor = $rdev % 256;
my $major = int($rdev / 256);
# do exists in /proc/diskstats?
if($found = is_in_diskstats($d, $major, $minor)) {
$d = $found;
$FS_DEV{$fs} = $d;
logger("$myself: Detected physical device name for $fs in '$d'.") unless !$opt_d;
next;
}
logger("$myself: Unable to find major/minor in /proc/diskstats.") unless !$opt_d;
# check if device is using EVMS <http://evms.sourceforge.net/>
if($d =~ m/\/dev\/evms\//) {
$d = `evms_query disks $d`;
if($found = is_in_diskstats($d)) {
$d = $found;
$FS_DEV{$fs} = $d;
logger("$myself: Detected physical device name for $fs in '$d'.") unless !$opt_d;
next;
}
}
$d =~ s/^.*dev\///; # remove the /dev/ prefix
$d =~ s/^.*mapper\///; # remove the mapper/ prefix
# check if the device is under a crypt LUKS (encrypted fs)
if($dev = is_luks($d)) {
$d = $dev;
}
# do exists in /proc/diskstats?
if($found = is_in_diskstats($d)) {
$d = $found;
$FS_DEV{$fs} = $d;
logger("$myself: Detected physical device name for $fs in '$d'.") unless !$opt_d;
next;
}
# check if the device is in a LVM
$lvm = $d;
$lvm =~ s/-.*//;
if($lvm ne $d) { # probably LVM
if(system("pvs >/dev/null 2>&1") == 0 && $lvm) {
$lvm_disk = `pvs --noheadings | grep $lvm | tail -1 | awk -F " " '{ print \$1 }'`;
chomp($lvm_disk);
$lvm_disk =~ s/^.*dev\///; # remove the /dev/ prefix
$lvm_disk =~ s/^.*mapper\///; # remove the mapper/ prefix
if(!($lvm_disk =~ m/md/)) {
if($lvm_disk =~ m/cciss/) {
# LVM over a CCISS disk (/dev/cciss/c0d0)
$d = $lvm_disk;
chomp($d);
} elsif($dev = is_luks($lvm_disk)) {
$d = $dev;
} else {
# LVM over a direct disk (/dev/sda1)
$d = $lvm_disk;
chomp($d);
}
} else {
# LVM over Linux RAID combination (/dev/md1)
$d = $lvm_disk;
chomp($d);
}
}
}
} elsif($os eq "FreeBSD" || $os eq "OpenBSD" || $os eq "NetBSD") {
# remove the /dev/ prefix
if ($d =~ s/^.*dev\///) {
# not ZFS; get the device name, eg ada0; md0; ad10
$d =~ s/^(\D+\d*)\D.*/\1/;
} else {
# Just take ZFS pool name
$d =~ s,^([^/]*)/.*,\1,;
}
}
$FS_DEV{$fs} = $d;
logger("$myself: Detected physical device name for $fs in '$d'.") unless !$opt_d;
}
}
our %fs_hist = ();
push(@graphs, "fs_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub fs_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my @tmp;
my $val;
my $str;
my $n;
my $rrdata = "N";
my $e = 0;
foreach my $i (@FS_LIST) {
for($n = 0; $n < 8; $n++) {
my $use = 0;
my $ioa = 0;
my $tim = 0;
my $used = 0;
my $free = 0;
$fs = @$i[$n];
if($fs eq "swap") {
if($os eq "Linux") {
open(IN, "free |");
while(<IN>) {
if(/^Swap:\s+\d+\s+(\d+)\s+(\d+)$/) {
$used = $1;
$free = $2;
}
}
close(IN);
} elsif($os eq "FreeBSD") {
open(IN, "swapinfo -k |");
while(<IN>) {
if(/^.*?\s+\d+\s+(\d+)\s+(\d+)\s+\d+\%$/) {
$used = $1;
$free = $2;
}
}
close(IN);
} elsif($os eq "OpenBSD" || $os eq "NetBSD") {
open(IN, "pstat -sk |");
while(<IN>) {
if(/^swap_device\s+\d+\s+(\d+)\s+(\d+) /) {
$used = $1;
$free = $2;
}
}
close(IN);
}
chomp($used, $free);
# prevents a division by 0 if swap device is not used
$use = ($used * 100) / ($used + $free) unless $used + $free == 0;
} elsif($fs) {
eval {
alarm $TIMEOUT;
open(IN, "df -P $fs |");
while(<IN>) {
if(/ $fs$/) {
@tmp = split(' ', $_);
last;
}
}
close(IN);
alarm 0;
};
(undef, undef, $used, $free) = @tmp;
chomp($used, $free);
$use = ($used * 100) / ($used + $free);
# FS alert
if($fs eq "/" && $ENABLE_ALERTS eq "Y") {
if(!$ALERT_ROOTFS_THRESHOLD || $pcnt < $ALERT_ROOTFS_THRESHOLD) {
$fs_hist{'rootalert'} = 0;
} else {
if(!$fs_hist{'rootalert'}) {
$fs_hist{'rootalert'} = time;
}
if($fs_hist{'rootalert'} > 0 && (time - $fs_hist{'rootalert'}) > $ALERT_ROOTFS_TIMEINTVL) {
if(-x $ALERT_ROOTFS_SCRIPT) {
system($ALERT_ROOTFS_SCRIPT . " " . $ALERT_ROOTFS_TIMEINTVL . " " . $ALERT_ROOTFS_THRESHOLD . " " . $pcnt);
}
$fs_hist{'rootalert'} = time;
}
}
}
}
my $read_cnt = 0;
my $read_sec = 0;
my $write_cnt = 0;
my $write_sec = 0;
my $d = $FS_DEV{$fs};
if($d) {
if($os eq "Linux") {
if($kernel_branch > 2.4) {
open(IN, "/proc/diskstats");
while(<IN>) {
if(/ $d /) {
@tmp = split(' ', $_);
last;
}
}
close(IN);
(undef, undef, undef, $read_cnt, undef, undef, $read_sec, $write_cnt, undef, undef, $write_sec) = @tmp;
} else {
my $io;
open(IN, "/proc/stat");
while(<IN>) {
if(/^disk_io/) {
(undef, undef, $io) = split(':', $_);
last;
}
}
close(IN);
(undef, $read_cnt, $read_sec, $write_cnt, $write_sec) = split(',', $io);
$write_sec =~ s/\).*$//;
}
} elsif($os eq "FreeBSD") {
@tmp = split(' ', `iostat -xI $d | grep -w $d`);
if(@tmp) {
(undef, $read_cnt, $write_cnt, $read_sec, $write_sec) = @tmp;
$read_cnt = int($read_cnt);
$write_cnt = int($write_cnt);
$read_sec = int($read_sec);
$write_sec = int($write_sec);
} else {
@tmp = split(' ', `iostat -dI | tail -1`);
(undef, $read_cnt, $read_sec) = @tmp;
$write_cnt = "";
$write_sec = "";
chomp($read_sec);
$read_sec = int($read_sec);
}
} elsif($os eq "OpenBSD" || $os eq "NetBSD") {
@tmp = split(' ', `iostat -DI | tail -1`);
($read_cnt, $read_sec) = @tmp;
$write_cnt = "";
$write_sec = "";
chomp($read_sec);
$read_sec = int($read_sec);
}
}
$ioa = $read_cnt + $write_cnt;
$tim = $read_sec + $write_sec;
$str = $e . "_ioa" . $n;
$val = $ioa;
$ioa = $val - $fs_hist{$str};
$ioa = 0 unless $val != $ioa;
$ioa /= 60;
$fs_hist{$str} = $val;
$str = $e . "_tim" . $n;
$val = $tim;
$tim = $val - $fs_hist{$str};
$tim = 0 unless $val != $tim;
$tim /= 60;
$fs_hist{$str} = $val;
$rrdata .= ":$use:$ioa:$tim";
}
$e++;
}
RRDs::update($FS_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $FS_RRD: $err") if $err;
}
# NET graph
# ----------------------------------------------------------------------------
sub net_init {
my $myself = (caller(0))[3];
if(!(-e $NET_RRD)) {
logger("Creating '$NET_RRD' file.");
eval {
RRDs::create($NET_RRD,
"--step=60",
"DS:net0_bytes_in:COUNTER:120:0:U",
"DS:net0_bytes_out:COUNTER:120:0:U",
"DS:net0_packs_in:COUNTER:120:0:U",
"DS:net0_packs_out:COUNTER:120:0:U",
"DS:net0_error_in:COUNTER:120:0:U",
"DS:net0_error_out:COUNTER:120:0:U",
"DS:net1_bytes_in:COUNTER:120:0:U",
"DS:net1_bytes_out:COUNTER:120:0:U",
"DS:net1_packs_in:COUNTER:120:0:U",
"DS:net1_packs_out:COUNTER:120:0:U",
"DS:net1_error_in:COUNTER:120:0:U",
"DS:net1_error_out:COUNTER:120:0:U",
"DS:net2_bytes_in:COUNTER:120:0:U",
"DS:net2_bytes_out:COUNTER:120:0:U",
"DS:net2_packs_in:COUNTER:120:0:U",
"DS:net2_packs_out:COUNTER:120:0:U",
"DS:net2_error_in:COUNTER:120:0:U",
"DS:net2_error_out:COUNTER:120:0:U",
"DS:net3_bytes_in:COUNTER:120:0:U",
"DS:net3_bytes_out:COUNTER:120:0:U",
"DS:net3_packs_in:COUNTER:120:0:U",
"DS:net3_packs_out:COUNTER:120:0:U",
"DS:net3_error_in:COUNTER:120:0:U",
"DS:net3_error_out:COUNTER:120:0:U",
"DS:net4_bytes_in:COUNTER:120:0:U",
"DS:net4_bytes_out:COUNTER:120:0:U",
"DS:net4_packs_in:COUNTER:120:0:U",
"DS:net4_packs_out:COUNTER:120:0:U",
"DS:net4_error_in:COUNTER:120:0:U",
"DS:net4_error_out:COUNTER:120:0:U",
"DS:net5_bytes_in:COUNTER:120:0:U",
"DS:net5_bytes_out:COUNTER:120:0:U",
"DS:net5_packs_in:COUNTER:120:0:U",
"DS:net5_packs_out:COUNTER:120:0:U",
"DS:net5_error_in:COUNTER:120:0:U",
"DS:net5_error_out:COUNTER:120:0:U",
"DS:net6_bytes_in:COUNTER:120:0:U",
"DS:net6_bytes_out:COUNTER:120:0:U",
"DS:net6_packs_in:COUNTER:120:0:U",
"DS:net6_packs_out:COUNTER:120:0:U",
"DS:net6_error_in:COUNTER:120:0:U",
"DS:net6_error_out:COUNTER:120:0:U",
"DS:net7_bytes_in:COUNTER:120:0:U",
"DS:net7_bytes_out:COUNTER:120:0:U",
"DS:net7_packs_in:COUNTER:120:0:U",
"DS:net7_packs_out:COUNTER:120:0:U",
"DS:net7_error_in:COUNTER:120:0:U",
"DS:net7_error_out:COUNTER:120:0:U",
"DS:net8_bytes_in:COUNTER:120:0:U",
"DS:net8_bytes_out:COUNTER:120:0:U",
"DS:net8_packs_in:COUNTER:120:0:U",
"DS:net8_packs_out:COUNTER:120:0:U",
"DS:net8_error_in:COUNTER:120:0:U",
"DS:net8_error_out:COUNTER:120:0:U",
"DS:net9_bytes_in:COUNTER:120:0:U",
"DS:net9_bytes_out:COUNTER:120:0:U",
"DS:net9_packs_in:COUNTER:120:0:U",
"DS:net9_packs_out:COUNTER:120:0:U",
"DS:net9_error_in:COUNTER:120:0:U",
"DS:net9_error_out:COUNTER:120:0:U",
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $NET_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
push(@graphs, "net_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub net_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my @net_bytes_in;
my @net_bytes_out;
my @net_packs_in;
my @net_packs_out;
my @net_error_in;
my @net_error_out;
my $n;
my $rrdata = "N";
for($n = 0; $n < 10 ; $n++) {
$net_bytes_in[$n] = 0;
$net_bytes_out[$n] = 0;
$net_packs_in[$n] = 0;
$net_packs_out[$n] = 0;
$net_error_in[$n] = 0;
$net_error_out[$n] = 0;
if($n < scalar(@NET_LIST)) {
if($os eq "Linux") {
open(IN, "/proc/net/dev");
my $dev;
while(<IN>) {
($dev, $data) = split(':', $_);
$_ = $dev;
if(/$NET_LIST[$n]/) {
($net_bytes_in[$n], $net_packs_in[$n], $net_error_in[$n], undef, undef, undef, undef, undef, $net_bytes_out[$n], $net_packs_out[$n], $net_error_out[$n]) = split(' ', $data);
last;
}
}
close(IN);
} elsif($os eq "FreeBSD") {
open(IN, "netstat -nibd |");
while(<IN>) {
if(/Link/ && /$NET_LIST[$n]/) {
# Idrop column added in 8.0
if($kernel_branch > 7.2) {
(undef, undef, undef, undef, $net_packs_in[$n], $net_error_in[$n], undef, $net_bytes_in[$n], $net_packs_out[$n], $net_error_out[$n], $net_bytes_out[$n]) = split(' ', $_);
} else {
(undef, undef, undef, undef, $net_packs_in[$n], $net_error_in[$n], $net_bytes_in[$n], $net_packs_out[$n], $net_error_out[$n], $net_bytes_out[$n]) = split(' ', $_);
}
last;
}
}
close(IN);
} elsif($os eq "OpenBSD" || $os eq "NetBSD") {
open(IN, "netstat -nibd |");
while(<IN>) {
if(/Link/ && /^$NET_LIST[$n]/) {
(undef, undef, undef, undef, $net_bytes_in[$n], $net_bytes_out[$n]) = split(' ', $_);
$net_packs_in[$n] = 0;
$net_error_in[$n] = 0;
$net_packs_out[$n] = 0;
$net_error_out[$n] = 0;
last;
}
}
close(IN);
}
}
chomp($net_bytes_in[$n],
$net_bytes_out[$n],
$net_packs_in[$n],
$net_packs_out[$n],
$net_error_in[$n],
$net_error_out[$n]);
$rrdata .= ":$net_bytes_in[$n]:$net_bytes_out[$n]:$net_packs_in[$n]:$net_packs_out[$n]:$net_error_in[$n]:$net_error_out[$n]";
}
RRDs::update($NET_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $NET_RRD: $err") if $err;
}
# SERV graph
# ----------------------------------------------------------------------------
sub serv_init {
my $myself = (caller(0))[3];
if(!(-e $SERV_RRD)) {
logger("Creating '$SERV_RRD' file.");
eval {
RRDs::create($SERV_RRD,
"--step=300",
"DS:serv_i_ssh:GAUGE:600:0:U",
"DS:serv_i_ftp:GAUGE:600:0:U",
"DS:serv_i_telnet:GAUGE:600:0:U",
"DS:serv_i_imap:GAUGE:600:0:U",
"DS:serv_i_smb:GAUGE:600:0:U",
"DS:serv_i_fax:GAUGE:600:0:U",
"DS:serv_i_cups:GAUGE:600:0:U",
"DS:serv_i_pop3:GAUGE:600:0:U",
"DS:serv_i_smtp:GAUGE:600:0:U",
"DS:serv_i_spam:GAUGE:600:0:U",
"DS:serv_i_virus:GAUGE:600:0:U",
"DS:serv_i_f2b:GAUGE:600:0:U",
"DS:serv_i_val02:GAUGE:600:0:U",
"DS:serv_i_val03:GAUGE:600:0:U",
"DS:serv_i_val04:GAUGE:600:0:U",
"DS:serv_i_val05:GAUGE:600:0:U",
"DS:serv_l_ssh:GAUGE:600:0:U",
"DS:serv_l_ftp:GAUGE:600:0:U",
"DS:serv_l_telnet:GAUGE:600:0:U",
"DS:serv_l_imap:GAUGE:600:0:U",
"DS:serv_l_smb:GAUGE:600:0:U",
"DS:serv_l_fax:GAUGE:600:0:U",
"DS:serv_l_cups:GAUGE:600:0:U",
"DS:serv_l_pop3:GAUGE:600:0:U",
"DS:serv_l_smtp:GAUGE:600:0:U",
"DS:serv_l_spam:GAUGE:600:0:U",
"DS:serv_l_virus:GAUGE:600:0:U",
"DS:serv_l_f2b:GAUGE:600:0:U",
"DS:serv_l_val02:GAUGE:600:0:U",
"DS:serv_l_val03:GAUGE:600:0:U",
"DS:serv_l_val04:GAUGE:600:0:U",
"DS:serv_l_val05:GAUGE:600:0:U",
"RRA:AVERAGE:0.5:1:288",
"RRA:AVERAGE:0.5:6:336",
"RRA:AVERAGE:0.5:12:744",
"RRA:AVERAGE:0.5:288:365",
"RRA:MIN:0.5:1:288",
"RRA:MIN:0.5:6:336",
"RRA:MIN:0.5:12:744",
"RRA:MIN:0.5:288:365",
"RRA:MAX:0.5:1:288",
"RRA:MAX:0.5:6:336",
"RRA:MAX:0.5:12:744",
"RRA:MAX:0.5:288:365",
"RRA:LAST:0.5:1:288",
"RRA:LAST:0.5:6:336",
"RRA:LAST:0.5:12:744",
"RRA:LAST:0.5:288:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $SERV_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
# Since 2.4.1 new values are used (val_01) to support 'fail2ban' hits.
# These new values need to be renamed.
RRDs::tune($SERV_RRD,
"--data-source-rename=serv_i_val01:serv_i_f2b",
"--data-source-rename=serv_l_val01:serv_l_f2b",
);
our %serv_hist = ();
push(@graphs, "serv_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub serv_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my $ssh = 0;
my $ftp = 0;
my $telnet = 0;
my $imap = 0;
my $smb = 0;
my $fax = 0;
my $cups = 0;
my $pop3 = 0;
my $smtp = 0;
my $spam = 0;
my $virus = 0;
my $f2b = 0;
my $val02 = 0;
my $val03 = 0;
my $val04 = 0;
my $val05 = 0;
my $date;
my $rrdata = "N";
# This graph is refreshed only every 5 minutes
my (undef, $min) = localtime(time);
if($min % 5) {
return;
}
if(-r $SECURE_LOG) {
$date = strftime("%b %e", localtime);
open(IN, "$SECURE_LOG");
while(<IN>) {
if(/^$date/) {
if(/ sshd\[/ && /Accepted /) {
$ssh++;
}
if($os eq "Linux") {
if(/START: pop3/) {
$pop3++;
}
if(/START: ftp/ ||
(/ proftpd\[/ && /Login successful./)) {
$ftp++;
}
if(/START: telnet/) {
$telnet++;
}
} elsif($os eq "FreeBSD") {
if(/login:/ && /login from /) {
$telnet++;
}
}
}
}
close(IN);
}
if(-r $IMAP_LOG) {
$IMAP_DATE_LOG_FORMAT = $IMAP_DATE_LOG_FORMAT || "%b %d";
$date_dovecot = strftime($IMAP_DATE_LOG_FORMAT, localtime);
$date_uw = strftime("%b %e %T", localtime);
open(IN, "$IMAP_LOG");
while(<IN>) {
# UW-IMAP log
if(/$date_uw/) {
if(/ imapd\[/ && / Login user=/) {
$imap++;
}
}
# Dovecot log
if(/$date_dovecot /) {
if(/ imap-login: / && / Login: /) {
$imap++;
}
if(/ pop3-login: / && / Login: /) {
$pop3++;
}
}
}
close(IN);
}
my $smb_L = 0;
open(IN, "smbstatus -L 2>/dev/null |");
while(<IN>) {
if(/^----------/) {
$smb_L++;
next;
}
if($smb_L) {
$smb_L++ unless !$_;
}
}
close(IN);
$smb_L--;
my $smb_S = 0;
open(IN, "smbstatus -S 2>/dev/null |");
while(<IN>) {
if(/^----------/) {
$smb_S++;
next;
}
if($smb_S) {
$smb_S++ unless !$_;
}
}
close(IN);
$smb_S--;
$smb = $smb_L + $smb_S;
if(-r $HYLAFAX_LOG) {
$date = strftime("%m/%d/%y", localtime);
open(IN, "$HYLAFAX_LOG");
while(<IN>) {
if(/^$date/ && /SEND/) {
$fax++;
}
}
close(IN);
}
if(-r $CUPS_LOG) {
$date = strftime("%d/%b/%Y", localtime);
open(IN, "$CUPS_LOG");
while(<IN>) {
if(/\[$date:/) {
$cups++;
}
}
close(IN);
}
if(-r $FAIL2BAN_LOG) {
$date = strftime("%Y-%m-%d", localtime);
open(IN, $FAIL2BAN_LOG);
while(<IN>) {
if(/^$date/ && / fail2ban/ && / WARNING / && / Ban /) {
$f2b++;
}
}
close(IN);
}
if(-r $MAIL_LOG) {
$date = strftime("%b %e", localtime);
open(IN, "$MAIL_LOG");
while(<IN>) {
if(/^$date/) {
if(/to=/ && /stat(us)?=sent/i) {
$smtp++;
}
if(/MailScanner/ && /Spam Checks:/ && /Found/ && /spam messages/) {
$spam++;
}
if(/MailScanner/ && /Virus Scanning:/ && /Found/ && /viruses/) {
$virus++;
}
}
}
close(IN);
}
$date = strftime("%Y-%m-%d", localtime);
if(-r "$CG_LOGDIR/$date.log") {
open(IN, "$CG_LOGDIR/$date.log");
while(<IN>) {
if(/DEQUEUER \[\d+\] (LOCAL\(.+\) delivered|SMTP.+ relayed)\:/) {
$smtp++;
}
if(/IMAP/ && / connected from /) {
$imap++;
}
if(/POP/ && / connected from /) {
$pop3++;
}
}
close(IN);
}
if(-r $SPAMASSASSIN_LOG) {
$date = strftime("%b %e", localtime);
open(IN, $SPAMASSASSIN_LOG);
while(<IN>) {
if(/^$date/ && /spamd: identified spam/) {
$spam++;
}
}
close(IN);
}
if(-r $CLAMAV_LOG) {
$date = strftime("%a %b %e", localtime);
open(IN, $CLAMAV_LOG);
while(<IN>) {
if(/^$date/ && / FOUND/) {
$virus++;
}
}
close(IN);
}
# I data (incremental)
$rrdata .= ":$ssh:$ftp:$telnet:$imap:$smb:$fax:$cups:$pop3:$smtp:$spam:$virus:$f2b:$val02:$val03:$val04:$val05";
# L data (load)
my $l_ssh = 0;
my $l_ftp = 0;
my $l_telnet = 0;
my $l_imap = 0;
my $l_smb = 0;
my $l_fax = 0;
my $l_cups = 0;
my $l_pop3 = 0;
my $l_smtp = 0;
my $l_spam = 0;
my $l_virus = 0;
my $l_f2b = 0;
my $l_val02 = 0;
my $l_val03 = 0;
my $l_val04 = 0;
my $l_val05 = 0;
$l_ssh = $ssh - $serv_hist{'ssh'};
$l_ssh = 0 unless $l_ssh != $ssh;
$l_ssh /= 300;
$serv_hist{'ssh'} = $ssh;
$l_ftp = $ftp - $serv_hist{'ftp'};
$l_ftp = 0 unless $l_ftp != $ftp;
$l_ftp /= 300;
$serv_hist{'ftp'} = $ftp;
$l_telnet = $telnet - $serv_hist{'telnet'};
$l_telnet = 0 unless $l_telnet != $telnet;
$l_telnet /= 300;
$serv_hist{'telnet'} = $telnet;
$l_imap = $imap - $serv_hist{'imap'};
$l_imap = 0 unless $l_imap != $imap;
$l_imap /= 300;
$serv_hist{'imap'} = $imap;
$l_smb = $smb - $serv_hist{'smb'};
$l_smb = 0 unless $l_smb != $smb;
$l_smb /= 300;
$serv_hist{'smb'} = $smb;
$l_fax = $fax - $serv_hist{'fax'};
$l_fax = 0 unless $l_fax != $fax;
$l_fax /= 300;
$serv_hist{'fax'} = $fax;
$l_cups = $cups - $serv_hist{'cups'};
$l_cups = 0 unless $l_cups != $cups;
$l_cups /= 300;
$serv_hist{'cups'} = $cups;
$l_pop3 = $pop3 - $serv_hist{'pop3'};
$l_pop3 = 0 unless $l_pop3 != $pop3;
$l_pop3 /= 300;
$serv_hist{'pop3'} = $pop3;
$l_smtp = $smtp - $serv_hist{'smtp'};
$l_smtp = 0 unless $l_smtp != $smtp;
$l_smtp /= 300;
$serv_hist{'smtp'} = $smtp;
$l_spam = $spam - $serv_hist{'spam'};
$l_spam = 0 unless $l_spam != $spam;
$l_spam /= 300;
$serv_hist{'spam'} = $spam;
$l_virus = $virus - $serv_hist{'virus'};
$l_virus = 0 unless $l_virus != $virus;
$l_virus /= 300;
$serv_hist{'virus'} = $virus;
$l_f2b = $f2b - $serv_hist{'f2b'};
$l_f2b = 0 unless $l_f2b != $f2b;
$l_f2b /= 300;
$serv_hist{'f2b'} = $f2b;
$l_val02 = $val02 - $serv_hist{'val02'};
$l_val02 = 0 unless $l_val02 != $val02;
$l_val02 /= 300;
$serv_hist{'val02'} = $val02;
$l_val03 = $val03 - $serv_hist{'val03'};
$l_val03 = 0 unless $l_val03 != $val03;
$l_val03 /= 300;
$serv_hist{'val03'} = $val03;
$l_val04 = $val04 - $serv_hist{'val04'};
$l_val04 = 0 unless $l_val04 != $val04;
$l_val04 /= 300;
$serv_hist{'val04'} = $val04;
$l_val05 = $val05 - $serv_hist{'val05'};
$l_val05 = 0 unless $l_val05 != $val05;
$l_val05 /= 300;
$serv_hist{'val05'} = $val05;
$rrdata .= ":$l_ssh:$l_ftp:$l_telnet:$l_imap:$l_smb:$l_fax:$l_cups:$l_pop3:$l_smtp:$l_spam:$l_virus:$l_f2b:$l_val02:$l_val03:$l_val04:$l_val05";
RRDs::update($SERV_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $SERV_RRD: $err") if $err;
}
# MAIL graph
# ----------------------------------------------------------------------------
sub mail_init {
my $myself = (caller(0))[3];
if(!(-e $MAIL_RRD)) {
logger("Creating '$MAIL_RRD' file.");
eval {
RRDs::create($MAIL_RRD,
"--step=60",
"DS:mail_in:GAUGE:120:0:U",
"DS:mail_out:GAUGE:120:0:U",
"DS:mail_recvd:GAUGE:120:0:U",
"DS:mail_delvd:GAUGE:120:0:U",
"DS:mail_bytes_recvd:GAUGE:120:0:U",
"DS:mail_bytes_delvd:GAUGE:120:0:U",
"DS:mail_rejtd:GAUGE:120:0:U",
"DS:mail_spam:GAUGE:120:0:U",
"DS:mail_virus:GAUGE:120:0:U",
"DS:mail_bouncd:GAUGE:120:0:U",
"DS:mail_queued:GAUGE:120:0:U",
"DS:mail_discrd:GAUGE:120:0:U",
"DS:mail_held:GAUGE:120:0:U",
"DS:mail_forwrd:GAUGE:120:0:U",
"DS:mail_queues:GAUGE:120:0:U",
"DS:mail_val01:COUNTER:120:0:U",
"DS:mail_val02:COUNTER:120:0:U",
"DS:mail_val03:COUNTER:120:0:U",
"DS:mail_val04:COUNTER:120:0:U",
"DS:mail_val05:COUNTER:120:0:U",
"DS:mail_val06:GAUGE:120:0:U",
"DS:mail_val07:GAUGE:120:0:U",
"DS:mail_val08:GAUGE:120:0:U",
"DS:mail_val09:GAUGE:120:0:U",
"DS:mail_val10:GAUGE:120:0:U",
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $MAIL_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
# Since 2.5.0 there is support for more message status.
# Some values need to be renamed.
RRDs::tune($MAIL_RRD,
"--data-source-rename=mail_mta_val10:mail_bouncd",
"--data-source-rename=mail_mta_val12:mail_discrd",
"--data-source-rename=mail_mta_val13:mail_held",
"--data-source-rename=mail_mta_val14:mail_forwrd",
"--data-source-rename=mail_mta_val15:mail_queues",
);
our $mail_hist = 0;
push(@graphs, "mail_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub mail_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my $in_conn;
my $out_conn;
my $recvd;
my $delvd;
my $bytes_recvd;
my $bytes_delvd;
my $rejected;
my $queued;
my $spam;
my $virus;
my $gl_records;
my $gl_greylisted;
my $gl_whitelisted;
my @mta_h = (0) x 15;
my @mta = (0) x 15;
my @gen = (0) x 10;
my $n;
my $mail_log_seekpos;
my $mail_log_size;
my $sa_log_seekpos;
my $sa_log_size;
my $clamav_log_seekpos;
my $clamav_log_size;
my $rrdata = "N";
# Read last MAIL data from historic
($mail_log_seekpos, $sa_log_seekpos, $clamav_log_seekpos, @mta_h[0..15-1], @gen[0..10-1]) = split(';', $mail_hist);
$mail_log_seekpos = defined($mail_log_seekpos) ? int($mail_log_seekpos) : 0;
$sa_log_seekpos = defined($sa_log_seekpos) ? int($sa_log_seekpos) : 0;
$clamav_log_seekpos = defined($clamav_log_seekpos) ? int($clamav_log_seekpos) : 0;
$recvd = $delvd = $bytes_recvd = $bytes_delvd = 0;
$in_conn = $out_conn = $rejected = 0;
$bounced = $discarded = $held = $forwarded = 0;
$queued = $queues = 0;
if(lc($MAIL_MTA) eq "sendmail") {
if(open(IN, "mailstats -P |")) {
while(<IN>) {
if(/^ T\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/) {
$recvd = $1;
$bytes_recvd = $2;
$delvd = $3;
$bytes_delvd = $4;
}
if(/^ C\s+(\d+)\s+(\d+)\s+(\d+)/) {
$in_conn = $1;
$out_conn = $2;
$rejected = $3;
}
}
close(IN);
$bytes_recvd *= 1024;
$bytes_delvd *= 1024;
}
if(open(IN, "mailq |")) {
while(<IN>) {
my ($tmp) = ($_ =~ m/^\w{14}[ *X-]\s*(\d{1,8}) /);
$queues += $tmp if $tmp;
if(/^\s+Total requests: (\d+)$/) {
$queued = $1;
}
}
close(IN);
}
} elsif(lc($MAIL_MTA) eq "postfix") {
my @data;
for my $path (split /:/, $ENV{PATH}) {
if(-f "$path/pflogsumm" && -x _) {
open(IN, "pflogsumm -d today -h 0 -u 0 --smtpd_stats --no_bounce_detail --no_deferral_detail --no_reject_detail --no_no_msg_size --no_smtpd_warnings $MAIL_LOG 2>/dev/null |");
}
if(-f "$path/pflogsumm.pl" && -x _) {
open(IN, "pflogsumm.pl -d today -h 0 -u 0 --smtpd_stats --no_bounce_detail --no_deferral_detail --no_reject_detail --no_no_msg_size --no_smtpd_warnings $MAIL_LOG 2>/dev/null |");
}
}
@data = <IN>;
close(IN);
foreach(@data) {
if(/^\s*(\d{1,7})([ km])\s*received$/) {
$recvd = $1;
$recvd = $1 * 1024 if $2 eq "k";
$recvd = $1 * 1024 * 1024 if $2 eq "m";
}
if(/^\s*(\d{1,7})([ km])\s*delivered$/) {
$delvd = $1;
$delvd = $1 * 1024 if $2 eq "k";
$delvd = $1 * 1024 * 1024 if $2 eq "m";
}
if(/^\s*(\d{1,7})([ km])\s*forwarded$/) {
$forwarded = $1;
$forwarded = $1 * 1024 if $2 eq "k";
$forwarded = $1 * 1024 * 1024 if $2 eq "m";
}
if(/^\s*(\d{1,7})([ km])\s*bounced$/) {
$bounced = $1;
$bounced = $1 * 1024 if $2 eq "k";
$bounced = $1 * 1024 * 1024 if $2 eq "m";
}
if(/^\s*(\d{1,7})([ km])\s*rejected \(/) {
$rejected = $1;
$rejected = $1 * 1024 if $2 eq "k";
$rejected = $1 * 1024 * 1024 if $2 eq "m";
}
if(/^\s*(\d{1,7})([ km])\s*held/) {
$held = $1;
$held = $1 * 1024 if $2 eq "k";
$held = $1 * 1024 * 1024 if $2 eq "m";
}
if(/^\s*(\d{1,7})([ km])\s*discarded \(/) {
$discarded = $1;
$discarded = $1 * 1024 if $2 eq "k";
$discarded = $1 * 1024 * 1024 if $2 eq "m";
}
if(/^\s*(\d{1,7})([ km])\s*bytes received$/) {
$bytes_recvd = $1;
$bytes_recvd = $1 * 1024 if $2 eq "k";
$bytes_recvd = $1 * 1024 * 1024 if $2 eq "m";
}
if(/^\s*(\d{1,7})([ km])\s*bytes delivered$/) {
$bytes_delvd = $1;
$bytes_delvd = $1 * 1024 if $2 eq "k";
$bytes_delvd = $1 * 1024 * 1024 if $2 eq "m";
}
}
if(open(IN, "mailq |")) {
while(<IN>) {
if(/^-- (\d+) Kbytes in (\d+) Request/) {
$queues = $1;
$queued = $2;
}
}
close(IN);
}
}
$gl_records = $gl_greylisted = $gl_whitelisted = 0;
if(lc($MAIL_GREYLIST) eq "milter-greylist") {
if(-r $MILTER_GL) {
open(IN, $MILTER_GL);
if(!seek(IN, -80, 2)) {
logger("Couldn't seek to the end ($MILTER_GL): $!");
return;
}
while(<IN>) {
if(/^# Summary:\s+(\d+) records,\s+(\d+) greylisted,\s+(\d+) whitelisted$/) {
$gl_records = $1;
$gl_greylisted = $2;
$gl_whitelisted = $3;
}
}
close(IN);
}
}
$spam = $virus = 0;
if(-r $MAIL_LOG) {
open(IN, $MAIL_LOG);
if(!seek(IN, 0, 2)) {
logger("Couldn't seek to the end ($MAIL_LOG): $!");
return;
}
$mail_log_size = tell(IN);
if($mail_log_size < $mail_log_seekpos) {
$mail_log_seekpos = 0;
}
if(!seek(IN, $mail_log_seekpos, 0)) {
logger("Couldn't seek to $mail_log_seekpos ($MAIL_LOG): $!");
return;
}
while(<IN>) {
my @line;
if(/MailScanner/ && /Spam Checks:/ && /Found/ && /spam messages/) {
@line = split(' ', $_);
$spam += int($line[8]);
}
if(/MailScanner/ && /Virus Scanning:/ && /Found/ && /viruses/) {
@line = split(' ', $_);
$virus += int($line[8]);
}
}
close(IN);
}
if(-r $SPAMASSASSIN_LOG) {
$date = strftime("%b %e", localtime);
open(IN, $SPAMASSASSIN_LOG);
if(!seek(IN, 0, 2)) {
logger("Couldn't seek to the end ($SPAMASSASSIN_LOG): $!");
return;
}
$sa_log_size = tell(IN);
if($sa_log_size < $sa_log_seekpos) {
$sa_log_seekpos = 0;
}
if(!seek(IN, $sa_log_seekpos, 0)) {
logger("Couldn't seek to $sa_log_seekpos ($SPAMASSASSIN_LOG): $!");
return;
}
while(<IN>) {
if(/^$date/ && /spamd: identified spam/) {
$spam++;
}
}
close(IN);
}
if(-r $CLAMAV_LOG) {
$date = strftime("%a %b %e", localtime);
open(IN, $CLAMAV_LOG);
if(!seek(IN, 0, 2)) {
logger("Couldn't seek to the end ($CLAMAV_LOG): $!");
return;
}
$clamav_log_size = tell(IN);
if($clamav_log_size < $clamav_log_seekpos) {
$clamav_log_seekpos = 0;
}
if(!seek(IN, $clamav_log_seekpos, 0)) {
logger("Couldn't seek to $clamav_log_seekpos ($CLAMAV_LOG): $!");
return;
}
while(<IN>) {
if(/^$date/ && / FOUND/) {
$virus++;
}
}
close(IN);
}
$mta[0] = int($in_conn) - $mta_h[0];
$mta[0] = 0 unless $mta[0] != int($in_conn);
$mta[0] /= 60;
$mta_h[0] = int($in_conn);
$mta[1] = int($out_conn) - $mta_h[1];
$mta[1] = 0 unless $mta[1] != int($out_conn);
$mta[1] /= 60;
$mta_h[1] = int($out_conn);
$mta[2] = int($recvd) - $mta_h[2];
$mta[2] = 0 unless $mta[2] != int($recvd);
$mta[2] /= 60;
$mta_h[2] = int($recvd);
$mta[3] = int($delvd) - $mta_h[3];
$mta[3] = 0 unless $mta[3] != int($delvd);
$mta[3] /= 60;
$mta_h[3] = int($delvd);
$mta[4] = int($bytes_recvd) - $mta_h[4];
$mta[4] = 0 unless $mta[4] != int($bytes_recvd);
$mta[4] /= 60;
$mta_h[4] = int($bytes_recvd);
$mta[5] = int($bytes_delvd) - $mta_h[5];
$mta[5] = 0 unless $mta[5] != int($bytes_delvd);
$mta[5] /= 60;
$mta_h[5] = int($bytes_delvd);
$mta[6] = int($rejected) - $mta_h[6];
$mta[6] = 0 unless $mta[6] != int($rejected);
$mta[6] /= 60;
$mta_h[6] = int($rejected);
# avoid initial spike
$mta[7] = int($spam) unless !$mta_h[7];
$mta_h[7] = int($spam) unless $mta_h[7];
$mta[7] /= 60;
# avoid initial spike
$mta[8] = int($virus) unless !$mta_h[8];
$mta_h[8] = int($virus) unless $mta_h[8];
$mta[8] /= 60;
$mta[9] = int($bouncd) - $mta_h[9];
$mta[9] = 0 unless $mta[9] != int($bouncd);
$mta[9] /= 60;
$mta_h[9] = int($bouncd);
$mta[10] = int($queued);
$mta[11] = int($discrd) - $mta_h[11];
$mta[11] = 0 unless $mta[11] != int($discrd);
$mta[11] /= 60;
$mta_h[11] = int($discrd);
$mta[12] = int($held) - $mta_h[12];
$mta[12] = 0 unless $mta[12] != int($held);
$mta[12] /= 60;
$mta_h[12] = int($held);
$mta[13] = int($forwrd) - $mta_h[13];
$mta[13] = 0 unless $mta[13] != int($forwrd);
$mta[13] /= 60;
$mta_h[13] = int($forwrd);
$mta[14] = int($queues);
$gen[6] = int($gl_records);
$gen[7] = int($gl_greylisted);
$gen[8] = int($gl_whitelisted);
$mail_hist = join(";", $mail_log_size, $sa_log_size, $clamav_log_size, @mta_h, @gen);
for($n = 0; $n < 15; $n++) {
$rrdata .= ":" . $mta[$n];
}
for($n = 0; $n < 10; $n++) {
$rrdata .= ":" . $gen[$n];
}
RRDs::update($MAIL_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $MAIL_RRD: $err") if $err;
}
# PORT graph
# ----------------------------------------------------------------------------
sub port_init {
my $myself = (caller(0))[3];
my $info;
my @ds;
my @tmp;
my @data;
my $n;
my $p;
if(-e $PORT_RRD) {
$info = RRDs::info($PORT_RRD);
for my $key (keys %$info) {
if(index($key, 'ds[') == 0) {
if(index($key, '.type') != -1) {
push(@ds, substr($key, 3, index($key, ']') - 3));
}
}
}
if(scalar(@ds) / 2 != $PORT_MAX) {
logger("Detected size mismatch between \$PORT_MAX ($PORT_MAX) and $PORT_RRD (" . scalar(@ds) / 2 . "). Resizing it accordingly. All historic data will be lost. Backup file created.");
rename($PORT_RRD, "$PORT_RRD.bak");
}
}
if(!(-e $PORT_RRD)) {
logger("Creating '$PORT_RRD' file.");
for($n = 0; $n < $PORT_MAX; $n++) {
push(@tmp, "DS:port" . $n . "_in:GAUGE:120:0:U");
push(@tmp, "DS:port" . $n . "_out:GAUGE:120:0:U");
}
eval {
RRDs::create($PORT_RRD,
"--step=60",
@tmp,
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $PORT_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
if($os eq "Linux") {
my $num;
my @line;
# set the iptables rules for each defined port
for($n = 0; $n < $PORT_MAX; $n++) {
if($PORT_LIST[$n]) {
$p = lc($PORT_PROT[$n]);
$p = "all" unless $PORT_PROT[$n];
system("iptables -N MONITORIX_IN_$n 2>/dev/null");
system("iptables -I INPUT -p $p --dport $PORT_LIST[$n] -j MONITORIX_IN_$n -c 0 0");
system("iptables -N MONITORIX_OUT_$n 2>/dev/null");
system("iptables -I OUTPUT -p $p --sport $PORT_LIST[$n] -j MONITORIX_OUT_$n -c 0 0");
}
}
}
if($os eq "FreeBSD" || $os eq "OpenBSD") {
# set the ipfw rules for each defined port
for($n = 0; $n < $PORT_MAX; $n++) {
if($PORT_LIST[$n]) {
$p = lc($PORT_PROT[$n]);
$p = "ip" unless $PORT_PROT[$n];
system("ipfw -q add $PORT_RULE count $p from me $PORT_LIST[$n] to any");
system("ipfw -q add $PORT_RULE count $p from any to me $PORT_LIST[$n]");
}
}
}
# Since 2.5.2 port values need to be converted to GAUGE.
for($n = 0; $n < $PORT_MAX; $n++) {
RRDs::tune($PORT_RRD,
"--data-source-type=port" . $n . "_in:GAUGE",
"--data-source-type=port" . $n . "_out:GAUGE",
);
}
our @port_hist_in = ();
our @port_hist_out = ();
push(@graphs, "port_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub port_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my @in;
my @out;
my $n;
my $rrdata = "N";
if($os eq "Linux") {
open(IN, "iptables -nxvL INPUT |");
while(<IN>) {
for($n = 0; $n < $PORT_MAX; $n++) {
$in[$n] = 0 unless $in[$n];
if(/ MONITORIX_IN_$n /) {
my (undef, $bytes) = split(' ', $_);
chomp($bytes);
$in[$n] = $bytes - $port_hist_in[$n];
$in[$n] = 0 unless $in[$n] != $bytes;
$port_hist_in[$n] = $bytes;
$in[$n] /= 60;
}
}
}
close(IN);
open(IN, "iptables -nxvL OUTPUT |");
while(<IN>) {
for($n = 0; $n < $PORT_MAX; $n++) {
$out[$n] = 0 unless $out[$n];
if(/ MONITORIX_OUT_$n /) {
my (undef, $bytes) = split(' ', $_);
chomp($bytes);
$out[$n] = $bytes - $port_hist_out[$n];
$out[$n] = 0 unless $out[$n] != $bytes;
$port_hist_out[$n] = $bytes;
$out[$n] /= 60;
}
}
}
close(IN);
}
if($os eq "FreeBSD" || $os eq "OpenBSD") {
open(IN, "ipfw show $PORT_RULE 2>/dev/null |");
while(<IN>) {
for($n = 0; $n < $PORT_MAX; $n++) {
$in[$n] = 0 unless $in[$n];
if(/ from any to me dst-port $PORT_LIST[$n]$/) {
my (undef, undef, $bytes) = split(' ', $_);
chomp($bytes);
$in[$n] = $bytes;
}
$out[$n] = 0 unless $out[$n];
if(/ from me $PORT_LIST[$n] to any$/) {
my (undef, undef, $bytes) = split(' ', $_);
chomp($bytes);
$out[$n] = $bytes;
}
}
}
close(IN);
}
for($n = 0; $n < $PORT_MAX; $n++) {
$rrdata .= ":$in[$n]:$out[$n]";
}
RRDs::update($PORT_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $PORT_RRD: $err") if $err;
}
# USER graph
# ----------------------------------------------------------------------------
sub user_init {
my $myself = (caller(0))[3];
if(!(-e $USER_RRD)) {
logger("Creating '$USER_RRD' file.");
eval {
RRDs::create($USER_RRD,
"--step=60",
"DS:user_sys:GAUGE:120:0:U",
"DS:user_smb:GAUGE:120:0:U",
"DS:user_mac:GAUGE:120:0:U",
"DS:user_val1:GAUGE:120:0:U",
"DS:user_val2:GAUGE:120:0:U",
"DS:user_val3:GAUGE:120:0:U",
"DS:user_val4:GAUGE:120:0:U",
"DS:user_val5:GAUGE:120:0:U",
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $USER_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
push(@graphs, "user_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub user_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my $sys;
my $smb;
my $mac;
my @data;
my $rrdata = "N";
open(IN, "who -q |");
while(<IN>) {
if(/^#/) {
my @tmp = split('=', $_);
$sys = $tmp[scalar(@tmp) - 1];
chomp($sys);
$sys = int($sys);
last;
}
}
close(IN);
$smb = 0;
open(IN, "smbstatus -b 2>/dev/null |");
while(<IN>) {
if(/^----------/) {
$smb++;
next;
}
if($smb) {
$smb++ unless !$_;
}
}
close(IN);
$smb--;
open(IN, "macusers 2>/dev/null |");
@data = <IN>;
close(IN);
$mac = scalar(@data) - 1;
$mac = 0 unless @data;
$rrdata .= ":$sys:$smb:$mac:0:0:0:0:0";
RRDs::update($USER_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $USER_RRD: $err") if $err;
}
# APACHE graph
# ----------------------------------------------------------------------------
sub apache_init {
my $myself = (caller(0))[3];
my $info;
my @ds;
my @tmp;
my $n;
if(!defined(@APACHE_LIST)) {
logger("$myself: ERROR: missing or not defined APACHE_LIST option.");
return 0;
}
if(-e $APACHE_RRD) {
$info = RRDs::info($APACHE_RRD);
for my $key (keys %$info) {
if(index($key, 'ds[') == 0) {
if(index($key, '.type') != -1) {
push(@ds, substr($key, 3, index($key, ']') - 3));
}
}
}
if(scalar(@ds) / 5 != scalar(@APACHE_LIST)) {
logger("Detected size mismatch between \@APACHE_LIST (" . scalar(@APACHE_LIST) . ") and $APACHE_RRD (" . scalar(@ds) / 5 . "). Resizing it accordingly. All historic data will be lost. Backup file created.");
rename($APACHE_RRD, "$APACHE_RRD.bak");
}
}
if(!(-e $APACHE_RRD)) {
logger("Creating '$APACHE_RRD' file.");
for($n = 0; $n < scalar(@APACHE_LIST); $n++) {
push(@tmp, "DS:apache" . $n . "_acc:GAUGE:120:0:U");
push(@tmp, "DS:apache" . $n . "_kb:GAUGE:120:0:U");
push(@tmp, "DS:apache" . $n . "_cpu:GAUGE:120:0:U");
push(@tmp, "DS:apache" . $n . "_busy:GAUGE:120:0:U");
push(@tmp, "DS:apache" . $n . "_idle:GAUGE:120:0:U");
}
eval {
RRDs::create($APACHE_RRD,
"--step=60",
@tmp,
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $APACHE_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
# Since 2.5.0 first 5 values belong to the first Apache being monitored.
# These values need to be renamed.
RRDs::tune($APACHE_RRD,
"--data-source-rename=apache_acc:apache0_acc",
"--data-source-rename=apache_kb:apache0_kb",
"--data-source-rename=apache_cpu:apache0_cpu",
"--data-source-rename=apache_busy:apache0_busy",
"--data-source-rename=apache_idle:apache0_idle",
);
our %apache_hist = ();
push(@graphs, "apache_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub apache_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my $acc;
my $kb;
my $cpu;
my $busy;
my $idle;
my $str;
my $rrdata = "N";
my $n = 0;
foreach(@APACHE_LIST) {
my $URL = $_ . "/server-status?auto";
my $ua = LWP::UserAgent->new(timeout => 30);
my $response = $ua->request(HTTP::Request->new('GET', $URL));
foreach(split('\n', $response->content)) {
if(/^Total Accesses:\s+(\d+)$/) {
$str = $n . "acc";
$acc = $1 - $apache_hist{$str};
$acc = 0 unless $acc != $1;
$acc /= 60;
$apache_hist{$str} = $1;
next;
}
if(/^Total kBytes:\s+(\d+)$/) {
$str = $n . "kb";
$kb = $1 - $apache_hist{$str};
$kb = 0 unless $kb != $1;
$apache_hist{$str} = $1;
next;
}
if(/^CPULoad:\s+(\d*\.\d+)$/) {
$cpu = abs($1);
next;
}
if(/^BusyWorkers:\s+(\d+)/ || /^BusyServers:\s+(\d+)/) {
$busy = int($1);
next;
}
if(/^IdleWorkers:\s+(\d+)/ || /^IdleServers:\s+(\d+)/) {
$idle = int($1);
last;
}
}
$rrdata .= ":$acc:$kb:$cpu:$busy:$idle";
$n++;
}
RRDs::update($APACHE_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $APACHE_RRD: $err") if $err;
}
# NGINX graph
# ----------------------------------------------------------------------------
sub nginx_init {
my $myself = (caller(0))[3];
if(!(-e $NGINX_RRD)) {
logger("Creating '$NGINX_RRD' file.");
eval {
RRDs::create($NGINX_RRD,
"--step=60",
"DS:nginx_requests:GAUGE:120:0:U",
"DS:nginx_total:GAUGE:120:0:U",
"DS:nginx_reading:GAUGE:120:0:U",
"DS:nginx_writing:GAUGE:120:0:U",
"DS:nginx_waiting:GAUGE:120:0:U",
"DS:nginx_bytes_in:GAUGE:120:0:U",
"DS:nginx_bytes_out:GAUGE:120:0:U",
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $NGINX_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
if(!defined($NGINX_PORT)) {
logger("$myself: ERROR: undefined NGINX_PORT option.");
return 0;
}
if($os eq "Linux") {
system("iptables -N NGINX_IN 2>/dev/null");
system("iptables -I INPUT -p tcp --dport $NGINX_PORT -j NGINX_IN -c 0 0");
system("iptables -N NGINX_OUT 2>/dev/null");
system("iptables -I OUTPUT -p tcp --sport $NGINX_PORT -j NGINX_OUT -c 0 0");
}
if($os eq "FreeBSD" || $os eq "OpenBSD") {
system("ipfw delete $NGINX_RULE 2>/dev/null");
system("ipfw -q add $NGINX_RULE count tcp from me $NGINX_PORT to any");
system("ipfw -q add $NGINX_RULE count tcp from any to me $NGINX_PORT");
}
# Since 2.4.0 these two values need to be converted to GAUGE.
RRDs::tune($NGINX_RRD,
"--data-source-type=nginx_bytes_in:GAUGE",
"--data-source-type=nginx_bytes_out:GAUGE",
);
our %nginx_hist = ();
push(@graphs, "nginx_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub nginx_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my $reqs;
my $tot;
my $reads;
my $writes;
my $waits;
my $in;
my $out;
my $val;
my $URL = "http://127.0.0.1:" . $NGINX_PORT . "/nginx_status";
my $ua = LWP::UserAgent->new(timeout => 30);
my $response = $ua->request(HTTP::Request->new('GET', $URL));
my $rrdata = "N";
foreach(split('\n', $response->content)) {
if(/^Active connections:\s+(\d+)\s*/) {
$tot = $1;
next;
}
if(/^\s+(\d+)\s+(\d+)\s+(\d+)\s*/) {
$reqs = $3 - $nginx_hist{'requests'};
$reqs = 0 unless $reqs != $3;
$nginx_hist{'requests'} = $3;
}
if(/^Reading:\s+(\d+).*Writing:\s+(\d+).*Waiting:\s+(\d+)\s*/) {
$reads = $1;
$writes = $2;
$waits = $3;
}
}
if($os eq "Linux") {
open(IN, "iptables -nxvL INPUT |");
while(<IN>) {
if(/ NGINX_IN /) {
(undef, $val) = split(' ', $_);
chomp($val);
$in = $val - $nginx_hist{'in'};
$in = 0 unless $in != $val;
$nginx_hist{'in'} = $val;
$in /= 60;
last;
}
}
close(IN);
open(IN, "iptables -nxvL OUTPUT |");
while(<IN>) {
if(/ NGINX_OUT /) {
(undef, $val) = split(' ', $_);
chomp($val);
$out = $val - $nginx_hist{'out'};
$out = 0 unless $out != $val;
$nginx_hist{'out'} = $val;
$out /= 60;
last;
}
}
close(IN);
}
if($os eq "FreeBSD" || $os eq "OpenBSD") {
open(IN, "ipfw show $NGINX_RULE 2>/dev/null |");
while(<IN>) {
if(/ from any to me dst-port $NGINX_PORT$/) {
(undef, undef, $val) = split(' ', $_);
chomp($val);
$in = $val - $nginx_hist{'in'};
$in = 0 unless $in != $val;
$nginx_hist{'in'} = $val;
$in /= 60;
}
if(/ from me $NGINX_PORT to any$/) {
(undef, undef, $val) = split(' ', $_);
chomp($val);
$out = $val - $nginx_hist{'out'};
$out = 0 unless $out != $val;
$nginx_hist{'out'} = $val;
$out /= 60;
}
}
close(IN);
}
$rrdata .= ":$reqs:$tot:$reads:$writes:$waits:$in:$out";
RRDs::update($NGINX_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $NGINX_RRD: $err") if $err;
}
# LIGHTTPD graph
# ----------------------------------------------------------------------------
sub lighttpd_init {
my $myself = (caller(0))[3];
my $info;
my @ds;
my @tmp;
my $n;
if(!defined(@LIGHTTPD_LIST)) {
logger("$myself: ERROR: missing or not defined LIGHTTPD_LIST option.");
return 0;
}
if(-e $LIGHTTPD_RRD) {
$info = RRDs::info($LIGHTTPD_RRD);
for my $key (keys %$info) {
if(index($key, 'ds[') == 0) {
if(index($key, '.type') != -1) {
push(@ds, substr($key, 3, index($key, ']') - 3));
}
}
}
if(scalar(@ds) / 9 != scalar(@LIGHTTPD_LIST)) {
logger("Detected size mismatch between \@LIGHTTPD_LIST (" . scalar(@LIGHTTPD_LIST) . ") and $LIGHTTPD_RRD (" . scalar(@ds) / 9 . "). Resizing it accordingly. All historic data will be lost. Backup file created.");
rename($LIGHTTPD_RRD, "$LIGHTTPD_RRD.bak");
}
}
if(!(-e $LIGHTTPD_RRD)) {
logger("Creating '$LIGHTTPD_RRD' file.");
for($n = 0; $n < scalar(@LIGHTTPD_LIST); $n++) {
push(@tmp, "DS:lighttpd" . $n . "_acc:GAUGE:120:0:U");
push(@tmp, "DS:lighttpd" . $n . "_kb:GAUGE:120:0:U");
push(@tmp, "DS:lighttpd" . $n . "_busy:GAUGE:120:0:U");
push(@tmp, "DS:lighttpd" . $n . "_idle:GAUGE:120:0:U");
push(@tmp, "DS:lighttpd" . $n . "_val01:GAUGE:120:0:U");
push(@tmp, "DS:lighttpd" . $n . "_val02:GAUGE:120:0:U");
push(@tmp, "DS:lighttpd" . $n . "_val03:GAUGE:120:0:U");
push(@tmp, "DS:lighttpd" . $n . "_val04:GAUGE:120:0:U");
push(@tmp, "DS:lighttpd" . $n . "_val05:GAUGE:120:0:U");
}
eval {
RRDs::create($LIGHTTPD_RRD,
"--step=60",
@tmp,
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $LIGHTTPD_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
our %lighttpd_hist = ();
push(@graphs, "lighttpd_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub lighttpd_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my $acc;
my $kb;
my $busy;
my $idle;
my $str;
my $rrdata = "N";
my $n = 0;
foreach(@LIGHTTPD_LIST) {
my $URL = $_ . "/server-status?auto";
my $ua = LWP::UserAgent->new(timeout => 30);
my $response = $ua->request(HTTP::Request->new('GET', $URL));
foreach(split('\n', $response->content)) {
if(/^Total Accesses:\s+(\d+)$/) {
$str = $n . "acc";
$acc = $1 - $lighttpd_hist{$str};
$acc = 0 unless $acc != $1;
$acc /= 60;
$lighttpd_hist{$str} = $1;
next;
}
if(/^Total kBytes:\s+(\d+)$/) {
$str = $n . "kb";
$kb = $1 - $lighttpd_hist{$str};
$kb = 0 unless $kb != $1;
$lighttpd_hist{$str} = $1;
next;
}
if(/^BusyServers:\s+(\d+)/) {
$busy = int($1);
next;
}
if(/^IdleServers:\s+(\d+)/) {
$idle = int($1);
last;
}
}
$rrdata .= ":$acc:$kb:$busy:$idle:0:0:0:0:0";
$n++;
}
RRDs::update($LIGHTTPD_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $LIGHTTPD_RRD: $err") if $err;
}
# MYSQL graph
# ----------------------------------------------------------------------------
sub mysql_init {
my $myself = (caller(0))[3];
my $info;
my @ds;
my @tmp;
my $num;
my $n;
$MYSQL_CONN_TYPE = $MYSQL_CONN_TYPE || "Host";
if(lc($MYSQL_CONN_TYPE) eq "host") {
$num = scalar(@MYSQL_HOST_LIST);
if(!@MYSQL_HOST_LIST) {
logger("$myself: ERROR: invalid value or not defined MYSQL_HOST_LIST option.");
return 0;
}
if(!@MYSQL_PORT_LIST) {
logger("$myself: ERROR: invalid value or not defined MYSQL_PORT_LIST option.");
return 0;
}
if(!@MYSQL_USER_LIST) {
logger("$myself: ERROR: invalid value or not defined MYSQL_USER_LIST option.");
return 0;
}
if(!@MYSQL_PASS_LIST) {
logger("$myself: ERROR: invalid value or not defined MYSQL_PASS_LIST option.");
return 0;
}
} elsif(lc($MYSQL_CONN_TYPE) eq "socket") {
$num = scalar(@MYSQL_SOCK_LIST);
if(!@MYSQL_SOCK_LIST) {
logger("$myself: ERROR: invalid value or not defined MYSQL_SOCK_LIST option.");
return 0;
}
} else {
logger("$myself: ERROR: invalid value in MYSQL_CONN_TYPE option.");
return 0;
}
if(-e $MYSQL_RRD) {
$info = RRDs::info($MYSQL_RRD);
for my $key (keys %$info) {
if(index($key, 'ds[') == 0) {
if(index($key, '.type') != -1) {
push(@ds, substr($key, 3, index($key, ']') - 3));
}
}
}
if(lc($MYSQL_CONN_TYPE) eq "host") {
if(scalar(@ds) / 38 != scalar(@MYSQL_HOST_LIST)) {
logger("Detected size mismatch between \@MYSQL_HOST_LIST (" . scalar(@MYSQL_HOST_LIST) . ") and $MYSQL_RRD (" . scalar(@ds) / 38 . "). Resizing it accordingly. All historic data will be lost. Backup file created.");
rename($MYSQL_RRD, "$MYSQL_RRD.bak");
}
}
if(lc($MYSQL_CONN_TYPE) eq "socket") {
if(scalar(@ds) / 38 != scalar(@MYSQL_SOCK_LIST)) {
logger("Detected size mismatch between \@MYSQL_SOCK_LIST (" . scalar(@MYSQL_SOCK_LIST) . ") and $MYSQL_RRD (" . scalar(@ds) / 38 . "). Resizing it accordingly. All historic data will be lost. Backup file created.");
rename($MYSQL_RRD, "$MYSQL_RRD.bak");
}
}
}
if(!(-e $MYSQL_RRD)) {
logger("Creating '$MYSQL_RRD' file.");
for($n = 0; $n < $num; $n++) {
push(@tmp, "DS:mysql" . $n . "_queries:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_sq:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_tchr:GAUGE:120:0:100");
push(@tmp, "DS:mysql" . $n . "_qcu:GAUGE:120:0:100");
push(@tmp, "DS:mysql" . $n . "_ot:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_conns_u:GAUGE:120:0:100");
push(@tmp, "DS:mysql" . $n . "_conns:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_tlw:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_kbu:GAUGE:120:0:100");
push(@tmp, "DS:mysql" . $n . "_innbu:GAUGE:120:0:100");
push(@tmp, "DS:mysql" . $n . "_csel:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_ccom:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_cdel:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_cins:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_cinss:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_cupd:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_crep:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_creps:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_crol:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_acli:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_acon:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_brecv:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_bsent:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_val01:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_val02:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_val03:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_val04:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_val05:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_val06:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_val07:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_val08:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_val09:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_val10:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_val11:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_val12:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_val13:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_val14:GAUGE:120:0:U");
push(@tmp, "DS:mysql" . $n . "_val15:GAUGE:120:0:U");
}
eval {
RRDs::create($MYSQL_RRD,
"--step=60",
@tmp,
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $MYSQL_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
# Since 2.5.0 first NN values belong to the first MySQL being monitored.
# These values need to be renamed.
RRDs::tune($MYSQL_RRD,
"--data-source-rename=mysql_queries:mysql0_queries",
"--data-source-rename=mysql_slow_queries:mysql0_sq",
"--data-source-rename=mysql_tcache_hit_r:mysql0_tchr",
"--data-source-rename=mysql_qcache_usage:mysql0_qcu",
"--data-source-rename=mysql_opened_tbl:mysql0_ot",
"--data-source-rename=mysql_conns_u:mysql0_conns_u",
"--data-source-rename=mysql_conns:mysql0_conns",
"--data-source-rename=mysql_tlocks_w:mysql0_tlw",
"--data-source-rename=mysql_key_buf_u:mysql0_kbu",
"--data-source-rename=mysql_innodb_buf_u:mysql0_innbu",
"--data-source-rename=mysql_com_select:mysql0_csel",
"--data-source-rename=mysql_com_commit:mysql0_ccom",
"--data-source-rename=mysql_com_delete:mysql0_cdel",
"--data-source-rename=mysql_com_insert:mysql0_cins",
"--data-source-rename=mysql_com_insert_s:mysql0_cinss",
"--data-source-rename=mysql_com_update:mysql0_cupd",
"--data-source-rename=mysql_com_replace:mysql0_crep",
"--data-source-rename=mysql_com_replace_s:mysql0_creps",
"--data-source-rename=mysql_com_rollback:mysql0_crol",
"--data-source-rename=mysql_aborted_cli:mysql0_acli",
"--data-source-rename=mysql_aborted_conn:mysql0_acon",
"--data-source-rename=mysql_bytes_recv:mysql0_brecv",
"--data-source-rename=mysql_bytes_sent:mysql0_bsent",
"--data-source-rename=mysql_val01:mysql0_val01",
"--data-source-rename=mysql_val02:mysql0_val02",
"--data-source-rename=mysql_val03:mysql0_val03",
"--data-source-rename=mysql_val04:mysql0_val04",
"--data-source-rename=mysql_val05:mysql0_val05",
"--data-source-rename=mysql_val06:mysql0_val06",
"--data-source-rename=mysql_val07:mysql0_val07",
"--data-source-rename=mysql_val08:mysql0_val08",
"--data-source-rename=mysql_val09:mysql0_val09",
"--data-source-rename=mysql_val10:mysql0_val10",
"--data-source-rename=mysql_val11:mysql0_val11",
"--data-source-rename=mysql_val12:mysql0_val12",
"--data-source-rename=mysql_val13:mysql0_val13",
"--data-source-rename=mysql_val14:mysql0_val14",
"--data-source-rename=mysql_val15:mysql0_val15",
);
our %mysql_hist = ();
push(@graphs, "mysql_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub mysql_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my $num;
my $str;
my $n = 0;
my $rrdata = "N";
my $print_error = 0;
if($opt_d eq "all" || $debug) {
$print_error = 1;
}
$MYSQL_CONN_TYPE = $MYSQL_CONN_TYPE || "Host";
if(lc($MYSQL_CONN_TYPE) eq "host") {
$num = scalar(@MYSQL_HOST_LIST);
}
if(lc($MYSQL_CONN_TYPE) eq "socket") {
$num = scalar(@MYSQL_SOCK_LIST);
}
for($n = 0; $n < $num; $n++) {
my $host = $MYSQL_HOST_LIST[$n];
my $port = $MYSQL_PORT_LIST[$n];
my $user = $MYSQL_USER_LIST[$n];
my $pass = $MYSQL_PASS_LIST[$n];
my $sock = $MYSQL_SOCK_LIST[$n];
my $dbh;
if(lc($MYSQL_CONN_TYPE) eq "host") {
unless ($host && $port && $user && $pass) {
logger("$myself: ERROR: undefined configuration.");
next;
}
$dbh = DBI->connect(
"DBI:mysql:host=$host;port=$port",
$user,
$pass,
{ PrintError => $print_error, }
) or logger("$myself: Cannot connect to MySQL '$host:$port'.") and next;
}
if(lc($MYSQL_CONN_TYPE) eq "socket") {
unless ($sock) {
logger("$myself: ERROR: undefined configuration.");
next;
}
$dbh = DBI->connect(
"DBI:mysql:mysql_socket=$sock",
{ PrintError => $print_error, }
) or logger("$myself: Cannot connect to MySQL '$sock'.") and next;
}
# SHOW STATUS
my $aborted_clients = 0;
my $aborted_connects = 0;
my $connections = 0;
my $connections_real = 0;
my $innodb_buffer_pool_pages_free = 0;
my $innodb_buffer_pool_pages_total = 0;
my $key_blocks_used = 0;
my $key_blocks_unused = 0;
my $max_used_connections = 0;
my $qcache_free_memory = 0;
my $qcache_hits = 0;
my $qcache_inserts = 0;
my $queries = 0;
my $opened_tables = 0;
my $slow_queries = 0;
my $table_locks_waited = 0;
my $threads_created = 0;
my $sql = "show status";
my $sth = $dbh->prepare($sql);
$sth->execute;
while((my $name, $value) = $sth->fetchrow_array) {
if($name eq "Aborted_clients") {
$str = $n . "aborted_clients";
$aborted_clients = $value - $mysql_hist{$str};
$aborted_clients = 0 unless $aborted_clients != $value;
$aborted_clients /= 60;
$mysql_hist{$str} = $value;
}
if($name eq "Aborted_connects") {
$str = $n . "aborted_connects";
$aborted_connects = $value - $mysql_hist{$str};
$aborted_connects = 0 unless $aborted_connects != $value;
$aborted_connects /= 60;
$mysql_hist{$str} = $value;
}
if($name eq "Connections") {
$str = $n . "connections";
$connections_real = int($value);
$connections = $value - $mysql_hist{$str};
$connections = 0 unless $connections != $value;
$connections /= 60;
$mysql_hist{$str} = $value;
}
if($name eq "Innodb_buffer_pool_pages_free") {
$innodb_buffer_pool_pages_free = int($value);
}
if($name eq "Innodb_buffer_pool_pages_total") {
$innodb_buffer_pool_pages_total = int($value);
}
if($name eq "Key_blocks_unused") {
$key_blocks_unused = int($value);
}
if($name eq "Key_blocks_used") {
$key_blocks_used = int($value);
}
if($name eq "Max_used_connections") {
$max_used_connections = int($value);
}
if($name eq "Opened_tables") {
$str = $n . "opened_tables";
$opened_tables = $value - $mysql_hist{$str};
$opened_tables = 0 unless $opened_tables != $value;
$opened_tables /= 60;
$mysql_hist{$str} = $value;
}
if($name eq "Qcache_free_memory") {
$qcache_free_memory = int($value);
}
if($name eq "Qcache_hits") {
$qcache_hits = int($value);
}
if($name eq "Qcache_inserts") {
$qcache_inserts = int($value);
}
if($name eq "Queries") {
$str = $n . "queries";
$queries = $value - $mysql_hist{$str};
$queries = 0 unless $queries != $value;
$queries /= 60;
$mysql_hist{$str} = $value;
}
if($name eq "Slow_queries") {
$str = $n . "slow_queries";
$slow_queries = $value - $mysql_hist{$str};
$slow_queries = 0 unless $slow_queries != $value;
$slow_queries /= 60;
$mysql_hist{$str} = $value;
}
if($name eq "Table_locks_waited") {
$str = $n . "table_locks_waited";
$table_locks_waited = $value - $mysql_hist{$str};
$table_locks_waited = 0 unless $table_locks_waited != $value;
$table_locks_waited /= 60;
$mysql_hist{$str} = $value;
}
if($name eq "Threads_created") {
$threads_created = int($value);
}
}
$sth->finish;
# SHOW GLOBAL STATUS
my $bytes_received = 0;
my $bytes_sent = 0;
my $com_commit = 0;
my $com_delete = 0;
my $com_insert = 0;
my $com_insert_s = 0;
my $com_replace = 0;
my $com_replace_s = 0;
my $com_rollback = 0;
my $com_select = 0;
my $com_update = 0;
my $sql = "show global status";
my $sth = $dbh->prepare($sql);
$sth->execute;
while((my $name, $value) = $sth->fetchrow_array) {
if($name eq "Bytes_received") {
$str = $n . "bytes_received";
$bytes_received = $value - $mysql_hist{$str};
$bytes_received = 0 unless $bytes_received != $value;
$bytes_received /= 60;
$mysql_hist{$str} = $value;
}
if($name eq "Bytes_sent") {
$str = $n . "bytes_sent";
$bytes_sent = $value - $mysql_hist{$str};
$bytes_sent = 0 unless $bytes_sent != $value;
$bytes_sent /= 60;
$mysql_hist{$str} = $value;
}
if($name eq "Com_commit") {
$str = $n . "com_commit";
$com_commit = $value - $mysql_hist{$str};
$com_commit = 0 unless $com_commit != $value;
$com_commit /= 60;
$mysql_hist{$str} = $value;
}
if($name eq "Com_delete") {
$str = $n . "com_delete";
$com_delete = $value - $mysql_hist{$str};
$com_delete = 0 unless $com_delete != $value;
$com_delete /= 60;
$mysql_hist{$str} = $value;
}
if($name eq "Com_insert") {
$str = $n . "com_insert";
$com_insert = $value - $mysql_hist{$str};
$com_insert = 0 unless $com_insert != $value;
$com_insert /= 60;
$mysql_hist{$str} = $value;
}
if($name eq "Com_insert_select") {
$str = $n . "com_insert_s";
$com_insert_s = $value - $mysql_hist{$str};
$com_insert_s = 0 unless $com_insert_s != $value;
$com_insert_s /= 60;
$mysql_hist{$str} = $value;
}
if($name eq "Com_replace") {
$str = $n . "com_replace";
$com_replace = $value - $mysql_hist{$str};
$com_replace = 0 unless $com_replace != $value;
$com_replace /= 60;
$mysql_hist{$str} = $value;
}
if($name eq "Com_replace_select") {
$str = $n . "com_replace_s";
$com_replace_s = $value - $mysql_hist{$str};
$com_replace_s = 0 unless $com_replace_s != $value;
$com_replace_s /= 60;
$mysql_hist{$str} = $value;
}
if($name eq "Com_rollback") {
$str = $n . "com_rollback";
$com_rollback = $value - $mysql_hist{$str};
$com_rollback = 0 unless $com_rollback != $value;
$com_rollback /= 60;
$mysql_hist{$str} = $value;
}
if($name eq "Com_select") {
$str = $n . "com_select";
$com_select = $value - $mysql_hist{$str};
$com_select = 0 unless $com_select != $value;
$com_select /= 60;
$mysql_hist{$str} = $value;
}
if($name eq "Com_update") {
$str = $n . "com_update";
$com_update = $value - $mysql_hist{$str};
$com_update = 0 unless $com_update != $value;
$com_update /= 60;
$mysql_hist{$str} = $value;
}
}
$sth->finish;
# SHOW VARIABLES
my $query_cache_size = 0;
my $max_connections = 0;
my $sql = "show variables";
my $sth = $dbh->prepare($sql);
$sth->execute;
while((my $name, $value) = $sth->fetchrow_array) {
if($name eq "max_connections") {
$max_connections = int($value);
}
if($name eq "query_cache_size") {
$query_cache_size = int($value);
}
}
$sth->finish;
$dbh->disconnect;
my $tcache_hit_rate = 0;
my $qcache_usage = 0;
my $connections_usage = 0;
my $key_buffer_usage = 0;
my $innodb_buffer_pool_usage = 0;
$tcache_hit_rate = (1 - ($threads_created / $connections_real)) * 100
unless !$connections_real;
$qcache_usage = (1 - ($qcache_free_memory / $query_cache_size)) * 100
unless !$query_cache_size;
$connections_usage = ($max_used_connections / $max_connections) * 100
unless !$max_connections;
$key_buffer_usage = ($key_blocks_used / ($key_blocks_used + $key_blocks_unused)) * 100
unless !($key_blocks_used + $key_blocks_unused);
$innodb_buffer_pool_usage = (1 - ($innodb_buffer_pool_pages_free / $innodb_buffer_pool_pages_total)) * 100
unless !$innodb_buffer_pool_pages_total;
$connections_usage = $connections_usage > 100 ? 100 : $connections_usage;
$rrdata .= ":$queries:$slow_queries:$tcache_hit_rate:$qcache_usage:$opened_tables:$connections_usage:$connections:$table_locks_waited:$key_buffer_usage:$innodb_buffer_pool_usage:$com_select:$com_commit:$com_delete:$com_insert:$com_insert_s:$com_update:$com_replace:$com_replace_s:$com_rollback:$aborted_clients:$aborted_connects:$bytes_received:$bytes_sent:0:0:0:0:0:0:0:0:0:0:0:0:0:0:0";
}
RRDs::update($MYSQL_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $MYSQL_RRD: $err") if $err;
}
# SQUID graph
# ----------------------------------------------------------------------------
sub squid_init {
my $myself = (caller(0))[3];
if(!(-e $SQUID_RRD)) {
logger("Creating '$SQUID_RRD' file.");
eval {
RRDs::create($SQUID_RRD,
"--step=60",
"DS:squid_g1_1:GAUGE:120:0:U",
"DS:squid_g1_2:GAUGE:120:0:U",
"DS:squid_g1_3:GAUGE:120:0:U",
"DS:squid_g1_4:GAUGE:120:0:U",
"DS:squid_g1_5:GAUGE:120:0:U",
"DS:squid_g1_6:GAUGE:120:0:U",
"DS:squid_g1_7:GAUGE:120:0:U",
"DS:squid_g1_8:GAUGE:120:0:U",
"DS:squid_g1_9:GAUGE:120:0:U",
"DS:squid_g2_1:GAUGE:120:0:U",
"DS:squid_g2_2:GAUGE:120:0:U",
"DS:squid_g2_3:GAUGE:120:0:U",
"DS:squid_g2_4:GAUGE:120:0:U",
"DS:squid_g2_5:GAUGE:120:0:U",
"DS:squid_g2_6:GAUGE:120:0:U",
"DS:squid_g2_7:GAUGE:120:0:U",
"DS:squid_g2_8:GAUGE:120:0:U",
"DS:squid_g2_9:GAUGE:120:0:U",
"DS:squid_rq_1:GAUGE:120:0:U",
"DS:squid_rq_2:GAUGE:120:0:U",
"DS:squid_rq_3:GAUGE:120:0:U",
"DS:squid_rq_4:GAUGE:120:0:U",
"DS:squid_rq_5:GAUGE:120:0:U",
"DS:squid_rq_6:GAUGE:120:0:U",
"DS:squid_rq_7:GAUGE:120:0:U",
"DS:squid_rq_8:GAUGE:120:0:U",
"DS:squid_rq_9:GAUGE:120:0:U",
"DS:squid_m_1:GAUGE:120:0:U",
"DS:squid_m_2:GAUGE:120:0:U",
"DS:squid_m_3:GAUGE:120:0:U",
"DS:squid_m_4:GAUGE:120:0:U",
"DS:squid_m_5:GAUGE:120:0:U",
"DS:squid_ic_1:GAUGE:120:0:U",
"DS:squid_ic_2:GAUGE:120:0:U",
"DS:squid_ic_3:GAUGE:120:0:U",
"DS:squid_ic_4:GAUGE:120:0:U",
"DS:squid_ic_5:GAUGE:120:0:U",
"DS:squid_io_1:GAUGE:120:0:U",
"DS:squid_io_2:GAUGE:120:0:U",
"DS:squid_io_3:GAUGE:120:0:U",
"DS:squid_io_4:GAUGE:120:0:U",
"DS:squid_io_5:GAUGE:120:0:U",
"DS:squid_s_1:GAUGE:120:0:U",
"DS:squid_s_2:GAUGE:120:0:U",
"DS:squid_s_3:GAUGE:120:0:U",
"DS:squid_s_4:GAUGE:120:0:U",
"DS:squid_s_5:GAUGE:120:0:U",
"DS:squid_tc_1:GAUGE:120:0:U",
"DS:squid_tc_2:GAUGE:120:0:U",
"DS:squid_tc_3:GAUGE:120:0:U",
"DS:squid_ts_1:GAUGE:120:0:U",
"DS:squid_ts_2:GAUGE:120:0:U",
"DS:squid_ts_3:GAUGE:120:0:U",
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $SQUID_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
our %squid_hist = ();
push(@graphs, "squid_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub squid_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my %g12 = ();
my $seek_pos;
my $logsize;
my @data;
my $all;
my $value;
my $g_result;
my $g_status;
my $rq_client_http_req;
my $rq_client_http_hit;
my $rq_server_http_req;
my $rq_server_ftp_req;
my $rq_server_other_req;
my $rq_aborted_req;
my $rq_swap_files_cleaned;
my $rq_unlink_requests;
my $tc_client_http_in;
my $tc_client_http_out;
my $ts_server_all_in;
my $ts_server_all_out;
my $m_alloc;
my $m_inuse;
my $ic_requests;
my $ic_hits;
my $ic_misses;
my $io_http;
my $io_ftp;
my $io_gopher;
my $io_wais;
my $s_entries;
my $s_maximum;
my $s_current;
my $n;
my $rrdata = "N";
$seek_pos = $squid_hist{'seek_pos'};
$seek_pos = defined($seek_pos) ? int($seek_pos) : 0;
open(IN, $SQUID_LOG);
if(!seek(IN, 0, 2)) {
logger("Couldn't seek to the end ($SQUID_LOG): $!");
return;
}
$logsize = tell(IN);
if($logsize < $seek_pos) {
$seek_pos = 0;
}
if(!seek(IN, $seek_pos, 0)) {
logger("Couldn't seek to $seek_pos ($SQUID_LOG): $!");
return;
}
if(defined($squid_hist{'seek_pos'})) { # avoid initial spike
while(<IN>) {
(undef, undef, undef, $value) = split(' ', $_);
($g_result, $g_status) = split('/', $value);
$g12{$g_result}++;
$g12{$g_status}++;
}
}
close(IN);
foreach(@SQUID_GRAPH_1) {
$rrdata .= ":";
$rrdata .= defined($g12{$_}) ? int($g12{$_}) : 0;
}
foreach(@SQUID_GRAPH_2) {
$rrdata .= ":";
$rrdata .= defined($g12{$_}) ? int($g12{$_}) : 0;
}
$squid_hist{'seek_pos'} = $logsize;
open(IN, "$SQUID_CMD mgr:counters |");
while(<IN>) {
if(/^client_http\.requests = (\d+)$/) {
$rq_client_http_req = $1 - $squid_hist{'rq_client_http_req'};
$rq_client_http_req = 0 unless $rq_client_http_req != $1;
$rq_client_http_req /= 60;
$squid_hist{'rq_client_http_req'} = $1;
next;
}
if(/^client_http\.hits = (\d+)$/) {
$rq_client_http_hit = $1 - $squid_hist{'rq_client_http_hit'};
$rq_client_http_hit = 0 unless $rq_client_http_hit != $1;
$rq_client_http_hit /= 60;
$squid_hist{'rq_client_http_hit'} = $1;
next;
}
if(/^client_http\.kbytes_in = (\d+)$/) {
$tc_client_http_in = $1 - $squid_hist{'tc_client_http_in'};
$tc_client_http_in = 0 unless $tc_client_http_in != $1;
$tc_client_http_in *= 1024;
$tc_client_http_in /= 60;
$squid_hist{'tc_client_http_in'} = $1;
next;
}
if(/^client_http\.kbytes_out = (\d+)$/) {
$tc_client_http_out = $1 - $squid_hist{'tc_client_http_out'};
$tc_client_http_out = 0 unless $tc_client_http_out != $1;
$tc_client_http_out *= 1024;
$tc_client_http_out /= 60;
$squid_hist{'tc_client_http_out'} = $1;
next;
}
if(/^server\.all\.kbytes_in = (\d+)$/) {
$ts_server_all_in = $1 - $squid_hist{'ts_server_all_in'};
$ts_server_all_in = 0 unless $ts_server_all_in != $1;
$ts_server_all_in *= 1024;
$ts_server_all_in /= 60;
$squid_hist{'ts_server_all_in'} = $1;
next;
}
if(/^server\.all\.kbytes_out = (\d+)$/) {
$ts_server_all_out = $1 - $squid_hist{'ts_server_all_out'};
$ts_server_all_out = 0 unless $ts_server_all_out != $1;
$ts_server_all_out *= 1024;
$ts_server_all_out /= 60;
$squid_hist{'ts_server_all_out'} = $1;
next;
}
if(/^server\.http\.requests = (\d+)$/) {
$rq_server_http_req = $1 - $squid_hist{'rq_server_http_req'};
$rq_server_http_req = 0 unless $rq_server_http_req != $1;
$rq_server_http_req /= 60;
$squid_hist{'rq_server_http_req'} = $1;
next;
}
if(/^server\.ftp\.requests = (\d+)$/) {
$rq_server_ftp_req = $1 - $squid_hist{'rq_server_ftp_req'};
$rq_server_ftp_req = 0 unless $rq_server_ftp_req != $1;
$rq_server_ftp_req /= 60;
$squid_hist{'rq_server_ftp_req'} = $1;
next;
}
if(/^server\.other\.requests = (\d+)$/) {
$rq_server_other_req = $1 - $squid_hist{'rq_server_other_req'};
$rq_server_other_req = 0 unless $rq_server_other_req != $1;
$rq_server_other_req /= 60;
$squid_hist{'rq_server_other_req'} = $1;
next;
}
if(/^unlink\.requests = (\d+)$/) {
$rq_unlink_requests = $1 - $squid_hist{'rq_unlink_requests'};
$rq_unlink_requests = 0 unless $rq_unlink_requests != $1;
$rq_unlink_requests /= 60;
$squid_hist{'rq_unlink_requests'} = $1;
next;
}
if(/^swap\.files_cleaned = (\d+)$/) {
$rq_swap_files_cleaned = $1 - $squid_hist{'rq_swap_files_cleaned'};
$rq_swap_files_cleaned = 0 unless $rq_swap_files_cleaned != $1;
$rq_swap_files_cleaned /= 60;
$squid_hist{'rq_swap_files_cleaned'} = $1;
next;
}
if(/^aborted_requests = (\d+)$/) {
$rq_aborted_req = $1 - $squid_hist{'rq_aborted_req'};
$rq_aborted_req = 0 unless $rq_aborted_req != $1;
$rq_aborted_req /= 60;
$squid_hist{'rq_aborted_req'} = $1;
last;
}
}
close(IN);
$rrdata .= ":$rq_client_http_req:$rq_client_http_hit:$rq_server_http_req:$rq_server_ftp_req:$rq_server_other_req:$rq_aborted_req:$rq_swap_files_cleaned:$rq_unlink_requests:0";
open(IN, "$SQUID_CMD mgr:mem |");
while(<IN>) {
if(/^Total /) {
(undef, undef, $m_alloc, undef, undef, undef, undef, $m_inuse) = split(' ', $_);
chomp($m_alloc);
chomp($m_inuse);
$m_alloc;
$m_inuse;
last;
}
}
close(IN);
$rrdata .= ":$m_alloc:$m_inuse:0:0:0";
open(IN, "$SQUID_CMD mgr:ipcache |");
while(<IN>) {
if(/^IPcache Requests:\s+(\d+)$/) {
$ic_requests = $1 - $squid_hist{'ic_requests'};
$ic_requests = 0 unless $ic_requests != $1;
$ic_requests /= 60;
$squid_hist{'ic_requests'} = $1;
next;
}
if(/^IPcache Hits:\s+(\d+)$/) {
$ic_hits = $1 - $squid_hist{'ic_hits'};
$ic_hits = 0 unless $ic_hits != $1;
$ic_hits /= 60;
$squid_hist{'ic_hits'} = $1;
next;
}
if(/^IPcache Misses:\s+(\d+)$/) {
$ic_misses = $1 - $squid_hist{'ic_misses'};
$ic_misses = 0 unless $ic_misses != $1;
$ic_misses /= 60;
$squid_hist{'ic_misses'} = $1;
last;
}
}
close(IN);
$rrdata .= ":$ic_requests:$ic_hits:$ic_misses:0:0";
open(IN, "$SQUID_CMD mgr:io |");
@data = <IN>;
close(IN);
$all = join('', @data);
$all =~ s/\n/ /g;
($value) = ($all =~ m/ HTTP I\/O number of reads.*?(\d+)/g);
chomp($value);
$io_http = $value - $squid_hist{'io_http'};
$io_http = 0 unless $io_http != $value;
$io_http /= 60;
$squid_hist{'io_http'} = $value;
($value) = ($all =~ m/ FTP I\/O number of reads.*?(\d+)/g);
chomp($value);
$io_ftp = $value - $squid_hist{'io_ftp'};
$io_ftp = 0 unless $io_ftp != $value;
$io_ftp /= 60;
$squid_hist{'io_ftp'} = $value;
($value) = ($all =~ m/ Gopher I\/O number of reads.*?(\d+)/g);
chomp($value);
$io_gopher = $value - $squid_hist{'io_gopher'};
$io_gopher = 0 unless $io_gopher != $value;
$io_gopher /= 60;
$squid_hist{'io_gopher'} = $value;
($value) = ($all =~ m/ WAIS I\/O number of reads.*?(\d+)/g);
chomp($value);
$io_wais = $value - $squid_hist{'io_wais'};
$io_wais = 0 unless $io_wais != $value;
$io_wais /= 60;
$squid_hist{'io_wais'} = $value;
$rrdata .= ":$io_http:$io_ftp:$io_gopher:$io_wais:0";
open(IN, "$SQUID_CMD mgr:storedir |");
while(<IN>) {
if(/^Store Entries\s+:\s+(\d+)$/) {
$s_entries = $1;
next;
}
if(/^Maximum Swap Size\s+:\s+(\d+)/) {
$s_maximum = $1;
next;
}
if(/^Current Store Swap Size\s*:\s+(\d+)/) {
$s_current = $1;
last;
}
}
close(IN);
$rrdata .= ":$s_entries:$s_maximum:$s_current:0:0";
$rrdata .= ":$tc_client_http_in:$tc_client_http_out:0";
$rrdata .= ":$ts_server_all_in:$ts_server_all_out:0";
RRDs::update($SQUID_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $SQUID_RRD: $err") if $err;
}
# NFSS graph
# ----------------------------------------------------------------------------
sub nfss_init {
my $myself = (caller(0))[3];
if(!(-e $NFSS_RRD)) {
logger("Creating '$NFSS_RRD' file.");
eval {
RRDs::create($NFSS_RRD,
"--step=60",
"DS:nfss_0:COUNTER:120:0:U",
"DS:nfss_1:COUNTER:120:0:U",
"DS:nfss_2:COUNTER:120:0:U",
"DS:nfss_3:COUNTER:120:0:U",
"DS:nfss_4:COUNTER:120:0:U",
"DS:nfss_5:COUNTER:120:0:U",
"DS:nfss_6:COUNTER:120:0:U",
"DS:nfss_7:COUNTER:120:0:U",
"DS:nfss_8:COUNTER:120:0:U",
"DS:nfss_9:COUNTER:120:0:U",
"DS:nfss_10:COUNTER:120:0:U",
"DS:nfss_11:COUNTER:120:0:U",
"DS:nfss_12:COUNTER:120:0:U",
"DS:nfss_13:COUNTER:120:0:U",
"DS:nfss_14:COUNTER:120:0:U",
"DS:nfss_15:COUNTER:120:0:U",
"DS:nfss_16:COUNTER:120:0:U",
"DS:nfss_17:COUNTER:120:0:U",
"DS:nfss_18:COUNTER:120:0:U",
"DS:nfss_19:COUNTER:120:0:U",
"DS:nfss_20:COUNTER:120:0:U",
"DS:nfss_21:COUNTER:120:0:U",
"DS:nfss_22:COUNTER:120:0:U",
"DS:nfss_23:COUNTER:120:0:U",
"DS:nfss_24:COUNTER:120:0:U",
"DS:nfss_25:COUNTER:120:0:U",
"DS:nfss_26:COUNTER:120:0:U",
"DS:nfss_27:COUNTER:120:0:U",
"DS:nfss_28:COUNTER:120:0:U",
"DS:nfss_29:COUNTER:120:0:U",
"DS:nfss_30:COUNTER:120:0:U",
"DS:nfss_31:COUNTER:120:0:U",
"DS:nfss_32:COUNTER:120:0:U",
"DS:nfss_33:COUNTER:120:0:U",
"DS:nfss_34:COUNTER:120:0:U",
"DS:nfss_35:COUNTER:120:0:U",
"DS:nfss_36:COUNTER:120:0:U",
"DS:nfss_37:COUNTER:120:0:U",
"DS:nfss_38:COUNTER:120:0:U",
"DS:nfss_39:COUNTER:120:0:U",
"DS:nfss_40:COUNTER:120:0:U",
"DS:nfss_41:COUNTER:120:0:U",
"DS:nfss_42:COUNTER:120:0:U",
"DS:nfss_43:COUNTER:120:0:U",
"DS:nfss_44:COUNTER:120:0:U",
"DS:nfss_45:COUNTER:120:0:U",
"DS:nfss_46:COUNTER:120:0:U",
"DS:nfss_47:COUNTER:120:0:U",
"DS:nfss_48:COUNTER:120:0:U",
"DS:nfss_49:COUNTER:120:0:U",
"DS:nfss_rc_1:COUNTER:120:0:U",
"DS:nfss_rc_2:COUNTER:120:0:U",
"DS:nfss_rc_3:COUNTER:120:0:U",
"DS:nfss_rc_4:COUNTER:120:0:U",
"DS:nfss_rc_5:COUNTER:120:0:U",
"DS:nfss_fh_1:COUNTER:120:0:U",
"DS:nfss_fh_2:COUNTER:120:0:U",
"DS:nfss_fh_3:COUNTER:120:0:U",
"DS:nfss_fh_4:COUNTER:120:0:U",
"DS:nfss_fh_5:COUNTER:120:0:U",
"DS:nfss_io_1:COUNTER:120:0:U",
"DS:nfss_io_2:COUNTER:120:0:U",
"DS:nfss_io_3:COUNTER:120:0:U",
"DS:nfss_th_0:COUNTER:120:0:U",
"DS:nfss_th_1:COUNTER:120:0:U",
"DS:nfss_th_2:COUNTER:120:0:U",
"DS:nfss_th_3:COUNTER:120:0:U",
"DS:nfss_th_4:COUNTER:120:0:U",
"DS:nfss_th_5:COUNTER:120:0:U",
"DS:nfss_th_6:COUNTER:120:0:U",
"DS:nfss_th_7:COUNTER:120:0:U",
"DS:nfss_th_8:COUNTER:120:0:U",
"DS:nfss_th_9:COUNTER:120:0:U",
"DS:nfss_th_10:COUNTER:120:0:U",
"DS:nfss_net_1:COUNTER:120:0:U",
"DS:nfss_net_2:COUNTER:120:0:U",
"DS:nfss_net_3:COUNTER:120:0:U",
"DS:nfss_net_4:COUNTER:120:0:U",
"DS:nfss_net_5:COUNTER:120:0:U",
"DS:nfss_rpc_1:COUNTER:120:0:U",
"DS:nfss_rpc_2:COUNTER:120:0:U",
"DS:nfss_rpc_3:COUNTER:120:0:U",
"DS:nfss_rpc_4:COUNTER:120:0:U",
"DS:nfss_rpc_5:COUNTER:120:0:U",
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $NFSS_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
push(@graphs, "nfss_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub nfss_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my @rc;
my @fh;
my @io;
my @th;
my @net;
my @rpc;
my @nfss;
my $n;
my $rrdata = "N";
if($os eq "Linux") {
open(IN, "/proc/net/rpc/nfsd");
while(<IN>) {
if(/^rc\s+(\d+)\s+(\d+)\s+(\d+)$/) {
@rc = ($1, $2, $3);
}
if(/^fh\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)$/) {
@fh = ($1, $2, $3, $4, $5);
}
if(/^io\s+(\d+)\s+(\d+)$/) {
@io = ($1, $2);
}
if(/^th /) {
my @tmp = split(' ', $_);
(undef, undef, @th) = @tmp;
}
if(/^net\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)$/) {
@net = ($1, $2, $3, $4);
}
if(/^rpc\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)$/) {
@rpc = ($1, $2, $3, $4, $5);
}
if(/^proc$NFSS_VERSION /) {
my @tmp = split(' ', $_);
(undef, undef, @nfss) = @tmp;
}
}
close(IN);
} elsif($os eq "FreeBSD" || $os eq "OpenBSD") {
# this part must be finished!!!! XXX
}
for($n = 0; $n < 50; $n++) {
if(!defined($nfss[$n])) {
$nfss[$n] = 0;
}
$rrdata .= ":" . $nfss[$n];
}
$rrdata .= ":$rc[0]:$rc[1]:$rc[2]:0:0";
$rrdata .= ":$fh[0]:$fh[1]:$fh[2]:$fh[3]:$fh[4]";
$rrdata .= ":$io[0]:$io[1]:0";
for($n = 0; $n < 11; $n++) {
$rrdata .= ":" . int($th[$n]);
}
$rrdata .= ":$net[0]:$net[1]:$net[2]:$net[3]:0";
$rrdata .= ":$rpc[0]:$rpc[1]:$rpc[2]:$rpc[3]:$rpc[4]";
RRDs::update($NFSS_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $NFSS_RRD: $err") if $err;
}
# NFSC graph
# ----------------------------------------------------------------------------
sub nfsc_init {
my $myself = (caller(0))[3];
if(!(-e $NFSC_RRD)) {
logger("Creating '$NFSC_RRD' file.");
eval {
RRDs::create($NFSC_RRD,
"--step=60",
"DS:nfsc_0:COUNTER:120:0:U",
"DS:nfsc_1:COUNTER:120:0:U",
"DS:nfsc_2:COUNTER:120:0:U",
"DS:nfsc_3:COUNTER:120:0:U",
"DS:nfsc_4:COUNTER:120:0:U",
"DS:nfsc_5:COUNTER:120:0:U",
"DS:nfsc_6:COUNTER:120:0:U",
"DS:nfsc_7:COUNTER:120:0:U",
"DS:nfsc_8:COUNTER:120:0:U",
"DS:nfsc_9:COUNTER:120:0:U",
"DS:nfsc_10:COUNTER:120:0:U",
"DS:nfsc_11:COUNTER:120:0:U",
"DS:nfsc_12:COUNTER:120:0:U",
"DS:nfsc_13:COUNTER:120:0:U",
"DS:nfsc_14:COUNTER:120:0:U",
"DS:nfsc_15:COUNTER:120:0:U",
"DS:nfsc_16:COUNTER:120:0:U",
"DS:nfsc_17:COUNTER:120:0:U",
"DS:nfsc_18:COUNTER:120:0:U",
"DS:nfsc_19:COUNTER:120:0:U",
"DS:nfsc_20:COUNTER:120:0:U",
"DS:nfsc_21:COUNTER:120:0:U",
"DS:nfsc_22:COUNTER:120:0:U",
"DS:nfsc_23:COUNTER:120:0:U",
"DS:nfsc_24:COUNTER:120:0:U",
"DS:nfsc_25:COUNTER:120:0:U",
"DS:nfsc_26:COUNTER:120:0:U",
"DS:nfsc_27:COUNTER:120:0:U",
"DS:nfsc_28:COUNTER:120:0:U",
"DS:nfsc_29:COUNTER:120:0:U",
"DS:nfsc_30:COUNTER:120:0:U",
"DS:nfsc_31:COUNTER:120:0:U",
"DS:nfsc_32:COUNTER:120:0:U",
"DS:nfsc_33:COUNTER:120:0:U",
"DS:nfsc_34:COUNTER:120:0:U",
"DS:nfsc_35:COUNTER:120:0:U",
"DS:nfsc_36:COUNTER:120:0:U",
"DS:nfsc_37:COUNTER:120:0:U",
"DS:nfsc_38:COUNTER:120:0:U",
"DS:nfsc_39:COUNTER:120:0:U",
"DS:nfsc_40:COUNTER:120:0:U",
"DS:nfsc_41:COUNTER:120:0:U",
"DS:nfsc_42:COUNTER:120:0:U",
"DS:nfsc_43:COUNTER:120:0:U",
"DS:nfsc_44:COUNTER:120:0:U",
"DS:nfsc_45:COUNTER:120:0:U",
"DS:nfsc_46:COUNTER:120:0:U",
"DS:nfsc_47:COUNTER:120:0:U",
"DS:nfsc_48:COUNTER:120:0:U",
"DS:nfsc_49:COUNTER:120:0:U",
"DS:nfsc_rpc_1:COUNTER:120:0:U",
"DS:nfsc_rpc_2:COUNTER:120:0:U",
"DS:nfsc_rpc_3:COUNTER:120:0:U",
"DS:nfsc_rpc_4:COUNTER:120:0:U",
"DS:nfsc_rpc_5:COUNTER:120:0:U",
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $NFSC_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
push(@graphs, "nfsc_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub nfsc_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my @rpc;
my @nfsc;
my $n;
my $rrdata = "N";
if($os eq "Linux") {
open(IN, "/proc/net/rpc/nfs");
while(<IN>) {
if(/^rpc\s+(\d+)\s+(\d+)\s+(\d+)$/) {
@rpc = ($1, $2, $3);
}
if(/^proc$NFSC_VERSION /) {
my @tmp = split(' ', $_);
(undef, undef, @nfsc) = @tmp;
}
}
close(IN);
} elsif($os eq "FreeBSD" || $os eq "OpenBSD") {
# this part must be finished!!!! XXX
}
for($n = 0; $n < 50; $n++) {
if(!defined($nfsc[$n])) {
$nfsc[$n] = 0;
}
$rrdata .= ":" . $nfsc[$n];
}
$rrdata .= ":$rpc[0]:$rpc[1]:$rpc[2]:0:0";
RRDs::update($NFSC_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $NFSC_RRD: $err") if $err;
}
# BIND graph
# ----------------------------------------------------------------------------
sub bind_init {
my $myself = (caller(0))[3];
my $info;
my @ds;
my @tmp;
my $n;
if(-e $BIND_RRD) {
$info = RRDs::info($BIND_RRD);
for my $key (keys %$info) {
if(index($key, 'ds[') == 0) {
if(index($key, '.type') != -1) {
push(@ds, substr($key, 3, index($key, ']') - 3));
}
}
}
if(scalar(@ds) / 135 != scalar(@BIND_URL_LIST)) {
logger("Detected size mismatch between \@BIND_URL_LIST (" . scalar(@BIND_URL_LIST) . ") and $BIND_RRD (" . scalar(@ds) / 135 . "). Resizing it accordingly. All historic data will be lost. Backup file created.");
rename($BIND_RRD, "$BIND_RRD.bak");
}
}
if(!(-e $BIND_RRD)) {
logger("Creating '$BIND_RRD' file.");
for($n = 0; $n < scalar(@BIND_URL_LIST); $n++) {
push(@tmp, "DS:bind" . $n . "_totalinq:GAUGE:120:U:U");
push(@tmp, "DS:bind" . $n . "_inq01:GAUGE:120:U:U");
push(@tmp, "DS:bind" . $n . "_inq02:GAUGE:120:U:U");
push(@tmp, "DS:bind" . $n . "_inq03:GAUGE:120:U:U");
push(@tmp, "DS:bind" . $n . "_inq04:GAUGE:120:U:U");
push(@tmp, "DS:bind" . $n . "_inq05:GAUGE:120:U:U");
push(@tmp, "DS:bind" . $n . "_inq06:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_inq07:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_inq08:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_inq09:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_inq10:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_inq11:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_inq12:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_inq13:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_inq14:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_inq15:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_inq16:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_inq17:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_inq18:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_inq19:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_inq20:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ouq01:GAUGE:120:U:U");
push(@tmp, "DS:bind" . $n . "_ouq02:GAUGE:120:U:U");
push(@tmp, "DS:bind" . $n . "_ouq03:GAUGE:120:U:U");
push(@tmp, "DS:bind" . $n . "_ouq04:GAUGE:120:U:U");
push(@tmp, "DS:bind" . $n . "_ouq05:GAUGE:120:U:U");
push(@tmp, "DS:bind" . $n . "_ouq06:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ouq07:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ouq08:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ouq09:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ouq10:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ouq11:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ouq12:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ouq13:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ouq14:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ouq15:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ouq16:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ouq17:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ouq18:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ouq19:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ouq20:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ss01:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ss02:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ss03:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ss04:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ss05:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ss06:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ss07:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ss08:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ss09:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ss10:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ss11:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ss12:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ss13:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ss14:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ss15:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ss16:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ss17:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ss18:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ss19:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_ss20:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_rs01:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_rs02:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_rs03:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_rs04:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_rs05:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_rs06:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_rs07:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_rs08:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_rs09:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_rs10:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_rs11:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_rs12:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_rs13:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_rs14:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_rs15:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_rs16:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_rs17:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_rs18:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_rs19:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_rs20:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_crr01:GAUGE:120:U:U");
push(@tmp, "DS:bind" . $n . "_crr02:GAUGE:120:U:U");
push(@tmp, "DS:bind" . $n . "_crr03:GAUGE:120:U:U");
push(@tmp, "DS:bind" . $n . "_crr04:GAUGE:120:U:U");
push(@tmp, "DS:bind" . $n . "_crr05:GAUGE:120:U:U");
push(@tmp, "DS:bind" . $n . "_crr06:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_crr07:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_crr08:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_crr09:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_crr10:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_crr11:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_crr12:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_crr13:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_crr14:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_crr15:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_crr16:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_crr17:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_crr18:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_crr19:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_crr20:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_sio01:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_sio02:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_sio03:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_sio04:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_sio05:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_sio06:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_sio07:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_sio08:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_sio09:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_sio10:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_sio11:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_sio12:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_sio13:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_sio14:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_sio15:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_sio16:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_sio17:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_sio18:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_sio19:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_sio20:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_mem_totaluse:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_mem_inuse:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_mem_blksize:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_mem_ctxtsize:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_mem_lost:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_mem_val01:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_mem_val02:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_mem_val03:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_tsk_workthrds:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_tsk_defquantm:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_tsk_tasksrun:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_tsk_val01:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_tsk_val02:GAUGE:120:0:U");
push(@tmp, "DS:bind" . $n . "_tsk_val03:GAUGE:120:0:U");
}
eval {
RRDs::create($BIND_RRD,
"--step=60",
@tmp,
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $BIND_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
our %bind_hist = ();
push(@graphs, "bind_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub bind_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my $totalinq;
my %inq = ();
my %ouq = ();
my %ss = ();
my %rs = ();
my %crr = ();
my $str;
my $n;
my $rrdata = "N";
my $e = 0;
foreach(@BIND_URL_LIST) {
my $ua = LWP::UserAgent->new(timeout => 30);
my $response = $ua->request(HTTP::Request->new('GET', $_));
my $data = XMLin($response->content);
my $value;
$value = $data->{bind}->{statistics}->{server}->{requests}->{opcode}->{counter};
$str = $e . "totalinq";
$totalinq = $value - $bind_hist{$str};
$totalinq = 0 unless $totalinq != $value;
$totalinq /= 60;
$bind_hist{$str} = $value;
$value = $data->{bind}->{statistics}->{server}->{'queries-in'}->{rdtype};
foreach(keys %{$value}) {
$str = $e . "inq_$_";
$inq{$str} = $value->{$_}->{counter} - $bind_hist{$str};
$inq{$str} = 0 unless $inq{$str} != $value->{$_}->{counter};
$inq{$str} /= 60;
$bind_hist{$str} = $value->{$_}->{counter};
}
my $views_default = $data->{bind}->{statistics}->{views}->{view}->{_default};
$value = $views_default->{rdtype};
foreach(keys %{$value}) {
$str = $e . "ouq_$_";
$ouq{$str} = $value->{$_}->{counter} - $bind_hist{$str};
$ouq{$str} = 0 unless $ouq{$str} != $value->{$_}->{counter};
$ouq{$str} /= 60;
$bind_hist{$str} = $value->{$_}->{counter};
}
$value = $data->{bind}->{statistics}->{server}->{nsstat};
foreach(keys %{$value}) {
$str = $e . "ss_$_";
$ss{$str} = $value->{$_}->{counter} - $bind_hist{$str};
$ss{$str} = 0 unless $ss{$str} != $value->{$_}->{counter};
$ss{$str} /= 60;
$bind_hist{$str} = $value->{$_}->{counter};
}
$value = $views_default->{resstat};
foreach(keys %{$value}) {
$str = $e . "rs_$_";
$rs{$str} = $value->{$_}->{counter} - $bind_hist{$str};
$rs{$str} = 0 unless $rs{$str} != $value->{$_}->{counter};
$rs{$str} /= 60;
$bind_hist{$str} = $value->{$_}->{counter};
}
$value = $views_default->{cache}->{rrset};
foreach(keys %{$value}) {
$str = $e . "crr_$_";
$crr{$str} = $value->{$_}->{counter};
}
# Socket I/O Statistics
# $value = $data->{bind}->{statistics}->{server}->{sockstat};
# foreach(keys %{$value}) {
# $str = $e . "sio_$_";
# $sio{$str} = $value->{$_}->{counter} - $bind_hist{$str};
# $sio{$str} = 0 unless $sio{$str} != $value->{$_}->{counter};
# $sio{$str} /= 60;
# $bind_hist{$str} = $value->{$_}->{counter};
# }
$rrdata .= ":$totalinq";
my $i = @BIND_IN_QUERIES_LIST[$e];
for($n = 0; $n < 20; $n++) {
my $j = @$i[$n];
$str = $e . "inq_$j";
$rrdata .= ":";
$rrdata .= defined($inq{$str}) ? $inq{$str} : 0;
}
$i = @BIND_OUT_QUERIES_LIST[$e];
for($n = 0; $n < 20; $n++) {
my $j = @$i[$n];
$str = $e . "ouq_$j";
$rrdata .= ":";
$rrdata .= defined($ouq{$str}) ? $ouq{$str} : 0;
}
$i = @BIND_SERVER_STATS_LIST[$e];
for($n = 0; $n < 20; $n++) {
my $j = @$i[$n];
$str = $e . "ss_$j";
$rrdata .= ":";
$rrdata .= defined($ss{$str}) ? $ss{$str} : 0;
}
$i = @BIND_RESOLVER_STATS_LIST[$e];
for($n = 0; $n < 20; $n++) {
my $j = @$i[$n];
$str = $e . "rs_$j";
$rrdata .= ":";
$rrdata .= defined($rs{$str}) ? $rs{$str} : 0;
}
$i = @BIND_CACHE_RRSETS_LIST[$e];
for($n = 0; $n < 20; $n++) {
my $j = @$i[$n];
$str = $e . "crr_$j";
$rrdata .= ":";
$rrdata .= defined($crr{$str}) ? $crr{$str} : 0;
}
# $i = @BIND_SOCKET_IO_STATS_LIST[$e];
for($n = 0; $n < 20; $n++) {
my $j = @$i[$n];
$str = $e . "sio_$j";
$rrdata .= ":";
$rrdata .= defined($sio{$str}) ? $sio{$str} : 0;
}
$value = $data->{bind}->{statistics}->{memory};
$rrdata .= ":" . $value->{summary}->{TotalUse};
$rrdata .= ":" . $value->{summary}->{InUse};
$rrdata .= ":" . $value->{summary}->{BlockSize};
$rrdata .= ":" . $value->{summary}->{ContextSize};
$rrdata .= ":" . $value->{summary}->{Lost};
$rrdata .= ":0:0:0";
$value = $data->{bind}->{statistics}->{taskmgr};
$rrdata .= ":" . $value->{'thread-model'}->{'worker-threads'};
$rrdata .= ":" . $value->{'thread-model'}->{'default-quantum'};
$rrdata .= ":" . $value->{'thread-model'}->{'tasks-running'};
$rrdata .= ":0:0:0";
$e++;
}
RRDs::update($BIND_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $BIND_RRD: $err") if $err;
}
# NTP graph
# ----------------------------------------------------------------------------
sub ntp_init {
my $myself = (caller(0))[3];
my $info;
my @ds;
my @tmp;
my $n;
if(-e $NTP_RRD) {
$info = RRDs::info($NTP_RRD);
for my $key (keys %$info) {
if(index($key, 'ds[') == 0) {
if(index($key, '.type') != -1) {
push(@ds, substr($key, 3, index($key, ']') - 3));
}
}
}
if(scalar(@ds) / 14 != scalar(@NTP_HOST_LIST)) {
logger("Detected size mismatch between \@NTP_HOST_LIST (" . scalar(@NTP_HOST_LIST) . ") and $NTP_RRD (" . scalar(@ds) / 14 . "). Resizing it accordingly. All historic data will be lost. Backup file created.");
rename($NTP_RRD, "$NTP_RRD.bak");
}
}
if(!(-e $NTP_RRD)) {
logger("Creating '$NTP_RRD' file.");
for($n = 0; $n < scalar(@NTP_HOST_LIST); $n++) {
push(@tmp, "DS:ntp" . $n . "_del:GAUGE:120:U:U");
push(@tmp, "DS:ntp" . $n . "_off:GAUGE:120:U:U");
push(@tmp, "DS:ntp" . $n . "_jit:GAUGE:120:U:U");
push(@tmp, "DS:ntp" . $n . "_str:GAUGE:120:0:U");
push(@tmp, "DS:ntp" . $n . "_c01:GAUGE:120:0:U");
push(@tmp, "DS:ntp" . $n . "_c02:GAUGE:120:0:U");
push(@tmp, "DS:ntp" . $n . "_c03:GAUGE:120:0:U");
push(@tmp, "DS:ntp" . $n . "_c04:GAUGE:120:0:U");
push(@tmp, "DS:ntp" . $n . "_c05:GAUGE:120:0:U");
push(@tmp, "DS:ntp" . $n . "_c06:GAUGE:120:0:U");
push(@tmp, "DS:ntp" . $n . "_c07:GAUGE:120:0:U");
push(@tmp, "DS:ntp" . $n . "_c08:GAUGE:120:0:U");
push(@tmp, "DS:ntp" . $n . "_c09:GAUGE:120:0:U");
push(@tmp, "DS:ntp" . $n . "_c10:GAUGE:120:0:U");
}
eval {
RRDs::create($NTP_RRD,
"--step=60",
@tmp,
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $NTP_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
push(@graphs, "ntp_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub ntp_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my @data;
my $del;
my $off;
my $jit;
my $str;
my $cod;
my $n;
my $rrdata = "N";
my $e = 0;
foreach(@NTP_HOST_LIST) {
open(IN, "ntpq -pn $_ |");
@data = <IN>;
close(IN);
$cod = $str = $del = $off = $jit = 0;
foreach(@data) {
if(/^\*/) {
(undef, $cod, $str, undef, undef, undef, undef, $del, $off, $jit) = split(' ', $_);
$cod =~ s/\.//g;
chomp($jit);
last;
}
}
$del = 0 unless defined($del);
$off = 0 unless defined($off);
$jit = 0 unless defined($jit);
$str = 0 unless defined($str);
$del /= 1000;
$off /= 1000;
$jit /= 1000;
$rrdata .= ":$del:$off:$jit:$str";
foreach my $i (@NTP_CODE_LIST[$e]) {
for($n = 0; $n < 10; $n++) {
if($cod eq @$i[$n]) {
$rrdata .= ":1";
} else {
$rrdata .= ":0";
}
}
}
$e++;
}
RRDs::update($NTP_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $NTP_RRD: $err") if $err;
}
# FAIL2BAN graph
# ----------------------------------------------------------------------------
sub fail2ban_init {
my $myself = (caller(0))[3];
my $info;
my @ds;
my @tmp;
my $n;
if(-e $FAIL2BAN_RRD) {
$info = RRDs::info($FAIL2BAN_RRD);
for my $key (keys %$info) {
if(index($key, 'ds[') == 0) {
if(index($key, '.type') != -1) {
push(@ds, substr($key, 3, index($key, ']') - 3));
}
}
}
if(scalar(@ds) / 9 != scalar(@FAIL2BAN_LIST)) {
logger("Detected size mismatch between \@FAIL2BAN_LIST (" . scalar(@FAIL2BAN_LIST) . ") and $FAIL2BAN_RRD (" . scalar(@ds) / 9 . "). Resizing it accordingly. All historic data will be lost. Backup file created.");
rename($FAIL2BAN_RRD, "$FAIL2BAN_RRD.bak");
}
}
if(!(-e $FAIL2BAN_RRD)) {
logger("Creating '$FAIL2BAN_RRD' file.");
for($n = 0; $n < scalar(@FAIL2BAN_LIST); $n++) {
push(@tmp, "DS:fail2ban" . $n . "_j1:GAUGE:120:0:U");
push(@tmp, "DS:fail2ban" . $n . "_j2:GAUGE:120:0:U");
push(@tmp, "DS:fail2ban" . $n . "_j3:GAUGE:120:0:U");
push(@tmp, "DS:fail2ban" . $n . "_j4:GAUGE:120:0:U");
push(@tmp, "DS:fail2ban" . $n . "_j5:GAUGE:120:0:U");
push(@tmp, "DS:fail2ban" . $n . "_j6:GAUGE:120:0:U");
push(@tmp, "DS:fail2ban" . $n . "_j7:GAUGE:120:0:U");
push(@tmp, "DS:fail2ban" . $n . "_j8:GAUGE:120:0:U");
push(@tmp, "DS:fail2ban" . $n . "_j9:GAUGE:120:0:U");
}
eval {
RRDs::create($FAIL2BAN_RRD,
"--step=60",
@tmp,
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $FAIL2BAN_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
our $fail2ban_hist = 0;
push(@graphs, "fail2ban_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub fail2ban_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my $seek_pos;
my $logsize;
my @jails;
my $n;
my $str;
my $rrdata = "N";
if(! -r $FAIL2BAN_LOG) {
logger("Couldn't find file $FAIL2BAN_LOG: $!");
return;
}
$seek_pos = $fail2ban_hist;
$seek_pos = defined($seek_pos) ? int($seek_pos) : 0;
open(IN, $FAIL2BAN_LOG);
if(!seek(IN, 0, 2)) {
logger("Couldn't seek to the end ($FAIL2BAN_LOG): $!");
return;
}
$logsize = tell(IN);
if($logsize < $seek_pos) {
$seek_pos = 0;
}
if(!seek(IN, $seek_pos, 0)) {
logger("Couldn't seek to $seek_pos ($FAIL2BAN_LOG): $!");
return;
}
if($fail2ban_hist > 0) { # avoid initial spike
$date = strftime("%Y-%m-%d", localtime);
while(<IN>) {
if(/^$date/) {
my $e = 0;
while($e < scalar(@FAIL2BAN_LIST)) {
foreach my $i (@FAIL2BAN_LIST[$e]) {
my $e2 = 0;
foreach my $j (@$i) {
($str = $j) =~ s/\[/\\[/;
$str =~ s/\]/\\]/;
$jails[$e][$e2] = 0 unless defined $jails[$e][$e2];
if(/ $str Ban /) {
$jails[$e][$e2]++;
}
$e2++;
}
}
$e++;
}
}
}
}
close(IN);
my $e = 0;
while($e < scalar(@FAIL2BAN_LIST)) {
for($n = 0; $n < 9; $n++) {
$jails[$e][$n] = 0 unless defined $jails[$e][$n];
$rrdata .= ":" . $jails[$e][$n];
}
$e++;
}
$fail2ban_hist = $logsize;
RRDs::update($FAIL2BAN_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $FAIL2BAN_RRD: $err") if $err;
}
# ICECAST graph
# ----------------------------------------------------------------------------
sub icecast_init {
my $myself = (caller(0))[3];
my $info;
my @ds;
my @tmp;
my $n;
if(-e $ICECAST_RRD) {
$info = RRDs::info($ICECAST_RRD);
for my $key (keys %$info) {
if(index($key, 'ds[') == 0) {
if(index($key, '.type') != -1) {
push(@ds, substr($key, 3, index($key, ']') - 3));
}
}
}
if(scalar(@ds) / 36 != scalar(@ICECAST_URL_LIST)) {
logger("Detected size mismatch between \@ICECAST_URL_LIST (" . scalar(@ICECAST_URL_LIST) . ") and $ICECAST_RRD (" . scalar(@ds) / 36 . "). Resizing it accordingly. All historic data will be lost. Backup file created.");
rename($ICECAST_RRD, "$ICECAST_RRD.bak");
}
}
if(!(-e $ICECAST_RRD)) {
logger("Creating '$ICECAST_RRD' file.");
for($n = 0; $n < scalar(@ICECAST_URL_LIST); $n++) {
push(@tmp, "DS:icecast" . $n . "_mp0_ls:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp0_br:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp0_v0:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp0_v1:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp1_ls:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp1_br:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp1_v0:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp1_v1:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp2_ls:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp2_br:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp2_v0:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp2_v1:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp3_ls:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp3_br:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp3_v0:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp3_v1:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp4_ls:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp4_br:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp4_v0:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp4_v1:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp5_ls:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp5_br:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp5_v0:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp5_v1:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp6_ls:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp6_br:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp6_v0:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp6_v1:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp7_ls:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp7_br:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp7_v0:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp7_v1:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp8_ls:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp8_br:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp8_v0:GAUGE:120:0:U");
push(@tmp, "DS:icecast" . $n . "_mp8_v1:GAUGE:120:0:U");
}
eval {
RRDs::create($ICECAST_RRD,
"--step=60",
@tmp,
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $ICECAST_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
push(@graphs, "icecast_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub icecast_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my @ls;
my @br;
my $n;
my $rrdata = "N";
my $e = 0;
foreach(@ICECAST_URL_LIST) {
my $ua = LWP::UserAgent->new(timeout => 30);
my $response = $ua->request(HTTP::Request->new('GET', $_));
my $data = $response->content;
$data =~ s/\n//g;
undef(@ls);
undef(@br);
foreach my $i (@ICECAST_MP_LIST[$e]) {
foreach(@$i) {
my $m = "Mount Point " . $_;
my ($b) = ($data =~ m/$m.*?<tr><td>Bitrate:<\/td><td class=\"streamdata\">(\d*?)<\/td><\/tr>/g);
my ($l) = ($data =~ m/$m.*?<tr><td>Current Listeners:<\/td><td class=\"streamdata\">(\d*?)<\/td>/g);
$b = 0 unless defined($b);
$l = 0 unless defined($l);
push(@ls, $l);
push(@br, $b);
}
for($n = 0; $n < 9; $n++) {
$ls[$n] = 0 unless defined($ls[$n]);
$br[$n] = 0 unless defined($br[$n]);
$rrdata .= ":" . $ls[$n];
$rrdata .= ":" . $br[$n];
$rrdata .= ":" . "0";
$rrdata .= ":" . "0";
}
}
$e++;
}
RRDs::update($ICECAST_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $ICECAST_RRD: $err") if $err;
}
# INT graph
# ----------------------------------------------------------------------------
sub int_init {
my $myself = (caller(0))[3];
if(!(-e $INT_RRD)) {
logger("Creating '$INT_RRD' file.");
eval {
RRDs::create($INT_RRD,
"--step=60",
"DS:int_0:COUNTER:120:0:U",
"DS:int_1:COUNTER:120:0:U",
"DS:int_2:COUNTER:120:0:U",
"DS:int_3:COUNTER:120:0:U",
"DS:int_4:COUNTER:120:0:U",
"DS:int_5:COUNTER:120:0:U",
"DS:int_6:COUNTER:120:0:U",
"DS:int_7:COUNTER:120:0:U",
"DS:int_8:COUNTER:120:0:U",
"DS:int_9:COUNTER:120:0:U",
"DS:int_10:COUNTER:120:0:U",
"DS:int_11:COUNTER:120:0:U",
"DS:int_12:COUNTER:120:0:U",
"DS:int_13:COUNTER:120:0:U",
"DS:int_14:COUNTER:120:0:U",
"DS:int_15:COUNTER:120:0:U",
"DS:int_16:COUNTER:120:0:U",
"DS:int_17:COUNTER:120:0:U",
"DS:int_18:COUNTER:120:0:U",
"DS:int_19:COUNTER:120:0:U",
"DS:int_20:COUNTER:120:0:U",
"DS:int_21:COUNTER:120:0:U",
"DS:int_22:COUNTER:120:0:U",
"DS:int_23:COUNTER:120:0:U",
"DS:int_24:COUNTER:120:0:U",
"DS:int_25:COUNTER:120:0:U",
"DS:int_26:COUNTER:120:0:U",
"DS:int_27:COUNTER:120:0:U",
"DS:int_28:COUNTER:120:0:U",
"DS:int_29:COUNTER:120:0:U",
"DS:int_30:COUNTER:120:0:U",
"DS:int_31:COUNTER:120:0:U",
"DS:int_32:COUNTER:120:0:U",
"DS:int_33:COUNTER:120:0:U",
"DS:int_34:COUNTER:120:0:U",
"DS:int_35:COUNTER:120:0:U",
"DS:int_36:COUNTER:120:0:U",
"DS:int_37:COUNTER:120:0:U",
"DS:int_38:COUNTER:120:0:U",
"DS:int_39:COUNTER:120:0:U",
"DS:int_40:COUNTER:120:0:U",
"DS:int_41:COUNTER:120:0:U",
"DS:int_42:COUNTER:120:0:U",
"DS:int_43:COUNTER:120:0:U",
"DS:int_44:COUNTER:120:0:U",
"DS:int_45:COUNTER:120:0:U",
"DS:int_46:COUNTER:120:0:U",
"DS:int_47:COUNTER:120:0:U",
"DS:int_48:COUNTER:120:0:U",
"DS:int_49:COUNTER:120:0:U",
"DS:int_50:COUNTER:120:0:U",
"DS:int_51:COUNTER:120:0:U",
"DS:int_52:COUNTER:120:0:U",
"DS:int_53:COUNTER:120:0:U",
"DS:int_54:COUNTER:120:0:U",
"DS:int_55:COUNTER:120:0:U",
"DS:int_56:COUNTER:120:0:U",
"DS:int_57:COUNTER:120:0:U",
"DS:int_58:COUNTER:120:0:U",
"DS:int_59:COUNTER:120:0:U",
"DS:int_60:COUNTER:120:0:U",
"DS:int_61:COUNTER:120:0:U",
"DS:int_62:COUNTER:120:0:U",
"DS:int_63:COUNTER:120:0:U",
"DS:int_64:COUNTER:120:0:U",
"DS:int_65:COUNTER:120:0:U",
"DS:int_66:COUNTER:120:0:U",
"DS:int_67:COUNTER:120:0:U",
"DS:int_68:COUNTER:120:0:U",
"DS:int_69:COUNTER:120:0:U",
"DS:int_70:COUNTER:120:0:U",
"DS:int_71:COUNTER:120:0:U",
"DS:int_72:COUNTER:120:0:U",
"DS:int_73:COUNTER:120:0:U",
"DS:int_74:COUNTER:120:0:U",
"DS:int_75:COUNTER:120:0:U",
"DS:int_76:COUNTER:120:0:U",
"DS:int_77:COUNTER:120:0:U",
"DS:int_78:COUNTER:120:0:U",
"DS:int_79:COUNTER:120:0:U",
"DS:int_80:COUNTER:120:0:U",
"DS:int_81:COUNTER:120:0:U",
"DS:int_82:COUNTER:120:0:U",
"DS:int_83:COUNTER:120:0:U",
"DS:int_84:COUNTER:120:0:U",
"DS:int_85:COUNTER:120:0:U",
"DS:int_86:COUNTER:120:0:U",
"DS:int_87:COUNTER:120:0:U",
"DS:int_88:COUNTER:120:0:U",
"DS:int_89:COUNTER:120:0:U",
"DS:int_90:COUNTER:120:0:U",
"DS:int_91:COUNTER:120:0:U",
"DS:int_92:COUNTER:120:0:U",
"DS:int_93:COUNTER:120:0:U",
"DS:int_94:COUNTER:120:0:U",
"DS:int_95:COUNTER:120:0:U",
"DS:int_96:COUNTER:120:0:U",
"DS:int_97:COUNTER:120:0:U",
"DS:int_98:COUNTER:120:0:U",
"DS:int_99:COUNTER:120:0:U",
"DS:int_100:COUNTER:120:0:U",
"DS:int_101:COUNTER:120:0:U",
"DS:int_102:COUNTER:120:0:U",
"DS:int_103:COUNTER:120:0:U",
"DS:int_104:COUNTER:120:0:U",
"DS:int_105:COUNTER:120:0:U",
"DS:int_106:COUNTER:120:0:U",
"DS:int_107:COUNTER:120:0:U",
"DS:int_108:COUNTER:120:0:U",
"DS:int_109:COUNTER:120:0:U",
"DS:int_110:COUNTER:120:0:U",
"DS:int_111:COUNTER:120:0:U",
"DS:int_112:COUNTER:120:0:U",
"DS:int_113:COUNTER:120:0:U",
"DS:int_114:COUNTER:120:0:U",
"DS:int_115:COUNTER:120:0:U",
"DS:int_116:COUNTER:120:0:U",
"DS:int_117:COUNTER:120:0:U",
"DS:int_118:COUNTER:120:0:U",
"DS:int_119:COUNTER:120:0:U",
"DS:int_120:COUNTER:120:0:U",
"DS:int_121:COUNTER:120:0:U",
"DS:int_122:COUNTER:120:0:U",
"DS:int_123:COUNTER:120:0:U",
"DS:int_124:COUNTER:120:0:U",
"DS:int_125:COUNTER:120:0:U",
"DS:int_126:COUNTER:120:0:U",
"DS:int_127:COUNTER:120:0:U",
"DS:int_128:COUNTER:120:0:U",
"DS:int_129:COUNTER:120:0:U",
"DS:int_130:COUNTER:120:0:U",
"DS:int_131:COUNTER:120:0:U",
"DS:int_132:COUNTER:120:0:U",
"DS:int_133:COUNTER:120:0:U",
"DS:int_134:COUNTER:120:0:U",
"DS:int_135:COUNTER:120:0:U",
"DS:int_136:COUNTER:120:0:U",
"DS:int_137:COUNTER:120:0:U",
"DS:int_138:COUNTER:120:0:U",
"DS:int_139:COUNTER:120:0:U",
"DS:int_140:COUNTER:120:0:U",
"DS:int_141:COUNTER:120:0:U",
"DS:int_142:COUNTER:120:0:U",
"DS:int_143:COUNTER:120:0:U",
"DS:int_144:COUNTER:120:0:U",
"DS:int_145:COUNTER:120:0:U",
"DS:int_146:COUNTER:120:0:U",
"DS:int_147:COUNTER:120:0:U",
"DS:int_148:COUNTER:120:0:U",
"DS:int_149:COUNTER:120:0:U",
"DS:int_150:COUNTER:120:0:U",
"DS:int_151:COUNTER:120:0:U",
"DS:int_152:COUNTER:120:0:U",
"DS:int_153:COUNTER:120:0:U",
"DS:int_154:COUNTER:120:0:U",
"DS:int_155:COUNTER:120:0:U",
"DS:int_156:COUNTER:120:0:U",
"DS:int_157:COUNTER:120:0:U",
"DS:int_158:COUNTER:120:0:U",
"DS:int_159:COUNTER:120:0:U",
"DS:int_160:COUNTER:120:0:U",
"DS:int_161:COUNTER:120:0:U",
"DS:int_162:COUNTER:120:0:U",
"DS:int_163:COUNTER:120:0:U",
"DS:int_164:COUNTER:120:0:U",
"DS:int_165:COUNTER:120:0:U",
"DS:int_166:COUNTER:120:0:U",
"DS:int_167:COUNTER:120:0:U",
"DS:int_168:COUNTER:120:0:U",
"DS:int_169:COUNTER:120:0:U",
"DS:int_170:COUNTER:120:0:U",
"DS:int_171:COUNTER:120:0:U",
"DS:int_172:COUNTER:120:0:U",
"DS:int_173:COUNTER:120:0:U",
"DS:int_174:COUNTER:120:0:U",
"DS:int_175:COUNTER:120:0:U",
"DS:int_176:COUNTER:120:0:U",
"DS:int_177:COUNTER:120:0:U",
"DS:int_178:COUNTER:120:0:U",
"DS:int_179:COUNTER:120:0:U",
"DS:int_180:COUNTER:120:0:U",
"DS:int_181:COUNTER:120:0:U",
"DS:int_182:COUNTER:120:0:U",
"DS:int_183:COUNTER:120:0:U",
"DS:int_184:COUNTER:120:0:U",
"DS:int_185:COUNTER:120:0:U",
"DS:int_186:COUNTER:120:0:U",
"DS:int_187:COUNTER:120:0:U",
"DS:int_188:COUNTER:120:0:U",
"DS:int_189:COUNTER:120:0:U",
"DS:int_190:COUNTER:120:0:U",
"DS:int_191:COUNTER:120:0:U",
"DS:int_192:COUNTER:120:0:U",
"DS:int_193:COUNTER:120:0:U",
"DS:int_194:COUNTER:120:0:U",
"DS:int_195:COUNTER:120:0:U",
"DS:int_196:COUNTER:120:0:U",
"DS:int_197:COUNTER:120:0:U",
"DS:int_198:COUNTER:120:0:U",
"DS:int_199:COUNTER:120:0:U",
"DS:int_200:COUNTER:120:0:U",
"DS:int_201:COUNTER:120:0:U",
"DS:int_202:COUNTER:120:0:U",
"DS:int_203:COUNTER:120:0:U",
"DS:int_204:COUNTER:120:0:U",
"DS:int_205:COUNTER:120:0:U",
"DS:int_206:COUNTER:120:0:U",
"DS:int_207:COUNTER:120:0:U",
"DS:int_208:COUNTER:120:0:U",
"DS:int_209:COUNTER:120:0:U",
"DS:int_210:COUNTER:120:0:U",
"DS:int_211:COUNTER:120:0:U",
"DS:int_212:COUNTER:120:0:U",
"DS:int_213:COUNTER:120:0:U",
"DS:int_214:COUNTER:120:0:U",
"DS:int_215:COUNTER:120:0:U",
"DS:int_216:COUNTER:120:0:U",
"DS:int_217:COUNTER:120:0:U",
"DS:int_218:COUNTER:120:0:U",
"DS:int_219:COUNTER:120:0:U",
"DS:int_220:COUNTER:120:0:U",
"DS:int_221:COUNTER:120:0:U",
"DS:int_222:COUNTER:120:0:U",
"DS:int_223:COUNTER:120:0:U",
"DS:int_224:COUNTER:120:0:U",
"DS:int_225:COUNTER:120:0:U",
"DS:int_226:COUNTER:120:0:U",
"DS:int_227:COUNTER:120:0:U",
"DS:int_228:COUNTER:120:0:U",
"DS:int_229:COUNTER:120:0:U",
"DS:int_230:COUNTER:120:0:U",
"DS:int_231:COUNTER:120:0:U",
"DS:int_232:COUNTER:120:0:U",
"DS:int_233:COUNTER:120:0:U",
"DS:int_234:COUNTER:120:0:U",
"DS:int_235:COUNTER:120:0:U",
"DS:int_236:COUNTER:120:0:U",
"DS:int_237:COUNTER:120:0:U",
"DS:int_238:COUNTER:120:0:U",
"DS:int_239:COUNTER:120:0:U",
"DS:int_240:COUNTER:120:0:U",
"DS:int_241:COUNTER:120:0:U",
"DS:int_242:COUNTER:120:0:U",
"DS:int_243:COUNTER:120:0:U",
"DS:int_244:COUNTER:120:0:U",
"DS:int_245:COUNTER:120:0:U",
"DS:int_246:COUNTER:120:0:U",
"DS:int_247:COUNTER:120:0:U",
"DS:int_248:COUNTER:120:0:U",
"DS:int_249:COUNTER:120:0:U",
"DS:int_250:COUNTER:120:0:U",
"DS:int_251:COUNTER:120:0:U",
"DS:int_252:COUNTER:120:0:U",
"DS:int_253:COUNTER:120:0:U",
"DS:int_254:COUNTER:120:0:U",
"DS:int_255:COUNTER:120:0:U",
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $INT_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
push(@graphs, "int_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub int_update {
my $myself = (caller(0))[3];
my ($debug) = @_;
my @int;
my $n;
my $maxints;
my $rrdata = "N";
if($os eq "Linux") {
open(IN, "/proc/stat");
while(<IN>) {
if(/^intr/) {
my @tmp = split(' ', $_);
(undef, undef, @int) = @tmp;
last;
}
}
close(IN);
} elsif($os eq "FreeBSD" || $os eq "OpenBSD") {
open(IN, "vmstat -i |");
my @allfields;
my $num;
my $name;
my $ticks;
$maxints = 0;
while(<IN>) {
if(/^\D{3}\d+/) {
@allfields = split(' ', $_);
$num = $allfields[0];
$name = $allfields[1];
$ticks = $allfields[$#allfields - 1];
chomp($ticks);
if($name eq "timer") {
$num = 0;
} else {
$num =~ s/^\D{3}//;
$num =~ s/://;
}
$int[$num] += $ticks;
$maxints = $maxints < $num ? $num : $maxints;
}
}
close(IN);
for($n = 0; $n < $maxints; $n++) {
$int[$n] = !$int[$n] ? 0 : $int[$n];
}
}
for($n = 0; $n < scalar(@int); $n++) {
if(($n % 256) != $n) {
$int[$n % 256] += $int[$n];
}
}
for($n = 0; $n < 256; $n++) {
if(!defined($int[$n])) {
$int[$n] = 0;
}
$rrdata .= ":" . $int[$n];
}
RRDs::update($INT_RRD, $rrdata);
if($opt_d eq "all" || $debug) {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $INT_RRD: $err") if $err;
}
# PC graph
# ----------------------------------------------------------------------------
sub pc_init {
my $myself = (caller(0))[3];
my $info;
my @ds;
my @tmp;
my @data;
my $n;
my $p;
if(!grep {$_ eq $os} ("Linux")) {
logger("$myself is not supported yet by your operating system ($os).");
return;
}
if(-e $PC_RRD) {
$info = RRDs::info($PC_RRD);
for my $key (keys %$info) {
if(index($key, 'ds[') == 0) {
if(index($key, '.type') != -1) {
push(@ds, substr($key, 3, index($key, ']') - 3));
}
}
}
if(scalar(@ds) / 2 != $PC_MAX) {
logger("Detected size mismatch between \$PC_MAX ($PC_MAX) and $PC_RRD (" . scalar(@ds) / 2 . "). Resizing it accordingly. All historic data will be lost. Backup file created.");
rename($PC_RRD, "$PC_RRD.bak");
}
}
if(!(-e $PC_RRD)) {
logger("Creating '$PC_RRD' file.");
for($n = 0; $n < $PC_MAX; $n++) {
push(@tmp, "DS:pc" . $n . "_in:GAUGE:120:0:U");
push(@tmp, "DS:pc" . $n . "_out:GAUGE:120:0:U");
}
eval {
RRDs::create($PC_RRD,
"--step=60",
@tmp,
"RRA:AVERAGE:0.5:1:1440",
"RRA:AVERAGE:0.5:30:336",
"RRA:AVERAGE:0.5:60:744",
"RRA:AVERAGE:0.5:1440:365",
"RRA:MIN:0.5:1:1440",
"RRA:MIN:0.5:30:336",
"RRA:MIN:0.5:60:744",
"RRA:MIN:0.5:1440:365",
"RRA:MAX:0.5:1:1440",
"RRA:MAX:0.5:30:336",
"RRA:MAX:0.5:60:744",
"RRA:MAX:0.5:1440:365",
"RRA:LAST:0.5:1:1440",
"RRA:LAST:0.5:30:336",
"RRA:LAST:0.5:60:744",
"RRA:LAST:0.5:1440:365",
);
};
my $err = RRDs::error;
if($@ || $err) {
logger("$@") unless !$@;
if($err) {
logger("ERROR: while creating $PC_RRD: $err");
if($err eq "RRDs::error") {
logger("... is the RRDtool Perl package installed?");
}
}
return;
}
}
if($os eq "Linux") {
if(!$NET_GATEWAY) {
logger("You must assign a valid ethernet interface in \$NET_GATEWAY");
return;
}
# remove the changed PC or those that no longer exist (daily)
open(IN, "iptables -nxvL FORWARD | grep _daily |");
@data = <IN>;
close(IN);
my $rule;
my $num;
my $exist;
foreach my $d (@data) {
$exist = 0;
(undef, undef, $name) = split(' ', $d);
$name =~ s/_daily//;
for($n = 0; $n < $PC_MAX; $n++) {
if($name eq $PC_LIST[$n]) {
$exist = 1;
last;
}
}
if(!$exist) {
logger("removing unused iptables rule: pc=$name");
$rule = system("iptables -nxvL FORWARD --line-numbers | grep -w $name" . "_daily 2>/dev/null");
$rule = split(' ', $rule);
system("iptables -D FORWARD $rule");
system("iptables -F $name" . "_daily");
system("iptables -X $name" . "_daily");
}
}
# set the iptables rules for each defined PC
my $ip;
for($n = 0; $n < $PC_MAX; $n++) {
if($PC_LIST[$n]) {
if(!($ip = $PC_IP[$n])) {
if(!(gethostbyname($PC_LIST[$n]))) {
logger("DNS problem with: ", $PC_LIST[$n]);
}
$ip = inet_ntoa((gethostbyname($PC_LIST[$n]))[4]);
$ip = $ip . "/32";
}
undef(@data);
open(IN, "iptables -nxvL $PC_LIST[$n]_daily 2>/dev/null |");
@data = <IN>;
close(IN);
if(!scalar(@data)) {
system("iptables -N $PC_LIST[$n]_daily");
system("iptables -I FORWARD -j $PC_LIST[$n]_daily");
system("iptables -A $PC_LIST[$n]_daily -s $ip -d 0/0 -o $NET_GATEWAY");
system("iptables -A $PC_LIST[$n]_daily -s 0/0 -d $ip -i $NET_GATEWAY");
}
}
}
}
# Since 2.5.2 PC_LAN values need to be converted to GAUGE.
for($n = 0; $n < $PC_MAX; $n++) {
RRDs::tune($PC_RRD,
"--data-source-type=pc" . $n . "_in:GAUGE",
"--data-source-type=pc" . $n . "_out:GAUGE",
);
}
our @pc_hist_in = ();
our @pc_hist_out = ();
push(@graphs, "pc_update");
if($opt_d eq "all" || $debug) {
logger("$myself: Ok");
}
}
sub pc_update {
my $myself = (caller(0))[3];
my @in;
my @out;
my $n;
my $ip;
my $rrdata = "N";
for($n = 0; $n < $PC_MAX; $n++) {
if($PC_LIST[$n]) {
if(!($ip = $PC_IP[$n])) {
if(!(gethostbyname($PC_LIST[$n]))) {
logger("DNS problem with: ", $PC_LIST[$n]);
}
$ip = inet_ntoa((gethostbyname($PC_LIST[$n]))[4]);
}
$ip =~ s/\/\d+//;
open(IN, "iptables -nxvL $PC_LIST[$n]_daily |");
$in[$n] = 0 unless $in[$n];
$out[$n] = 0 unless $out[$n];
while(<IN>) {
my (undef, $bytes, undef, undef, undef, undef, $source) = split(' ', $_);
if($source =~ /0.0.0.0/) {
$in[$n] = $bytes - $pc_hist_in[$n];
$in[$n] = 0 unless $in[$n] != $bytes;
$pc_hist_in[$n] = $bytes;
$in[$n] /= 60;
}
if($source eq $ip) {
$out[$n] = $bytes - $pc_hist_out[$n];
$out[$n] = 0 unless $out[$n] != $bytes;
$pc_hist_out[$n] = $bytes;
$out[$n] /= 60;
}
}
close(IN);
}
}
for($n = 0; $n < $PC_MAX; $n++) {
$rrdata .= ":$in[$n]:$out[$n]";
}
RRDs::update($PC_RRD, $rrdata);
if($opt_d eq "all") {
logger("$myself: $rrdata");
}
my $err = RRDs::error;
logger("ERROR: while updating $PC_RRD: $err") if $err;
}
sub get_counters {
my $in;
my $out;
my $n;
my $ip;
my $day = (localtime(time - 60))[3];
for($n = 0; $n < $PC_MAX; $n++) {
if($PC_LIST[$n]) {
if(!($ip = $PC_IP[$n])) {
if(!(gethostbyname($PC_LIST[$n]))) {
logger("DNS problem with: ", $PC_LIST[$n]);
}
$ip = inet_ntoa((gethostbyname($PC_LIST[$n]))[4]);
}
$ip =~ s/\/\d+//;
open(IN, "iptables -nxvL $PC_LIST[$n]_daily |");
while(<IN>) {
my (undef, $bytes, undef, undef, undef, undef, $source) = split(' ', $_);
if($source eq $ip) {
$out = $bytes;
}
if($source =~ /0.0.0.0/) {
$in = $bytes;
}
}
close(IN);
if(! -w $USAGE_DIR) {
logger("WARNING: directory '" . $USAGE_DIR ."' doesn't exists or is not writable.");
last;
} else {
open(OUT, ">> " . $USAGE_DIR . $PC_LIST[$n]);
print(OUT "$day $in $out\n");
close(OUT);
logger("Saved the daily traffic counter for '$PC_LIST[$n]'.") unless !$opt_d;
}
system("iptables -Z $PC_LIST[$n]_daily >/dev/null 2>/dev/null");
}
}
}
sub send_reports {
my $myself = (caller(0))[3];
my $n;
my $to;
if(! -x $REPORT_DIR . "send_reports") {
logger("$myself: unable to find the script '" . $REPORT_DIR . "send_reports" . "'.");
return;
}
logger("Sending monthly traffic reports.");
for($n = 0; $n < $PC_MAX; $n++) {
if($PC_LIST[$n]) {
$to = $PC_REPORT_MAIL[$n];
$to = $PC_DEFAULT_MAIL unless $PC_REPORT_MAIL[$n];
logger("$myself: $PC_LIST[$n] -> $to [$PC_REPORT_LANG]");
system("cd $REPORT_DIR ; " . $REPORT_DIR . "send_reports -h $PC_LIST[$n] -c $opt_c &");
}
}
}
# Main
# ----------------------------------------------------------------------------
getopts("d:vc:p:") || usage();
if($opt_v) {
print("Monitorix version " . VERSION . " (" . RELDATE . ")\n");
print("by Jordi Sanfeliu <jordi\@fibranet.cat>\n");
print("http://www.monitorix.org/\n\n");
exit(0);
}
if(!$opt_c) {
usage();
exit(1);
}
$opt_c = abs_path($opt_c) unless $^V lt 5.6.2;
if(!stat($opt_c)) {
die("can't open file $opt_c.\n");
}
# get the current OS and kernel branch and check its support
my $release;
($os, undef, $release) = uname();
my ($major, $minor) = split('\.', $release);
$kernel_branch = $major . "." . $minor;
if(!grep {$_ eq $os} @suppsys) {
die("FATAL: your operating system ($os) is not supported.\n");
}
# check configuration file syntax (and load it)
if(grep {$_ eq $os} ("FreeBSD", "OpenBSD", "NetBSD")) {
$SIG{'CHLD'} = 'DEFAULT';
}
if(system("perl -wc $opt_c >/dev/null 2>&1")) {
die("FATAL: configuration file '$opt_c' had compilation errors.\n");
}
require $opt_c;
$0 = sprintf("%s %s%s%s%s",
$^V lt 5.6.2 ? monitorix : abs_path($0),
$opt_c ? "-c $opt_c" : "",
$opt_p ? " -p $opt_p" : "",
$opt_d ? " -d $opt_d" : "",
$opt_v ? " -v" : "");
daemonize();
logger("Starting Monitorix version " . VERSION . " (pid $$).");
if($opt_p) {
$opt_p = abs_path($opt_p);
open(PIDFILE, "> $opt_p")
|| die("could not open $opt_p for writing");
print(PIDFILE "$$");
close(PIDFILE);
}
# change to safety directory
unless(chdir("/tmp")) {
logger("can't chdir to /tmp: $!");
unless(chdir("/lost+found")) {
die("Can't chdir to /lost+found: $!");
}
}
if($opt_d) {
if($opt_d ne "none" && $opt_d ne "all") {
@graphs_debug = split(',', $opt_d);
foreach my $t (@graphs_debug) {
if(!grep {$_ eq $t} (@GRAPH_NAME)) {
die("Invalid debug key '$t'");
}
}
}
logger("Entering in debug mode.");
logger("Changed process name to '$0'.");
}
# save the path of the configuration file
open(OUT, "> $BASE_DIR/cgi-bin/monitorix.conf.path");
print(OUT "$opt_c\n");
close(OUT);
# initialize all enabled graphs
logger("Initializing graphs.") unless !$opt_d;
flush_accounting_rules();
my $func;
foreach my $g (@GRAPH_NAME) {
if($GRAPH_ENABLE{$g} eq "Y") {
$func = $g . "_init";
eval {&$func();};
if($@) {
logger("WARNING: unexpected errors in function $func()");
}
}
}
if($PC_LAN eq "Y") {
pc_init();
}
if(!scalar(@graphs)) {
logger("nothing to do, exiting.");
exit(0);
}
# create 'index.html' file
logger("Generating the 'index.html' file.") unless !$opt_d;
create_index();
logger("Ok, done.") unless !$opt_d;
alarm(1);
while(1) {
sleep(1);
}