#!/usr/bin/env perl # # Monitorix - A lightweight system monitoring tool. # # Copyright (C) 2005-2012 by Jordi Sanfeliu # # 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 < $TITLE


v@{[VERSION]}

EOF if(!$FAVICON) { logger("WARNING: the option \$FAVICON is missing in your configuration file. Do you really have the latest version?"); } print(OUT " \n"); print(OUT " \n"); print(OUT <
 Hostname   Graph 
\n"); print(OUT " \n"); print(OUT " \n"); print(OUT " \n"); print(OUT "

Daily  Weekly  Monthly  Yearly 

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() { 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() { 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() { 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() { 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() { if(/^\{ (\d+\.\d+) (\d+\.\d+) (\d+\.\d+) \}$/) { $load1 = $1; $load5 = $2; $load15 = $3; } } close(IN); open(IN, "sysctl vm.vmtotal |"); while() { 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() { if(/^(\d+\.\d+) (\d+\.\d+) (\d+\.\d+)$/) { $load1 = $1; $load5 = $2; $load15 = $3; } } close(IN); open(IN, "top -b |"); while() { 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() { 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() { if(/^(\d+)\s+(\d+)\s+/) { $dentry = ($1 * 100) / ($1 + $2); } } close(IN); open(IN, "/proc/sys/fs/file-nr"); while() { if(/^(\d+)\s+\d+\s+(\d+)$/) { $file = ($1 * 100) / $2; } } close(IN); open(IN, "/proc/sys/fs/inode-nr"); while() { 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() { 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() { 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() { 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() { 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(' ', ); 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 = ; 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 = ; 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 = ; 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 = ; 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 = ; 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 = ; 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() { 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 = ; 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() { 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 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() { if(/^Swap:\s+\d+\s+(\d+)\s+(\d+)$/) { $used = $1; $free = $2; } } close(IN); } elsif($os eq "FreeBSD") { open(IN, "swapinfo -k |"); while() { 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() { 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() { 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() { 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() { 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() { ($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() { 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() { 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() { 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() { # 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() { 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() { 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() { if(/^$date/ && /SEND/) { $fax++; } } close(IN); } if(-r $CUPS_LOG) { $date = strftime("%d/%b/%Y", localtime); open(IN, "$CUPS_LOG"); while() { if(/\[$date:/) { $cups++; } } close(IN); } if(-r $FAIL2BAN_LOG) { $date = strftime("%Y-%m-%d", localtime); open(IN, $FAIL2BAN_LOG); while() { if(/^$date/ && / fail2ban/ && / WARNING / && / Ban /) { $f2b++; } } close(IN); } if(-r $MAIL_LOG) { $date = strftime("%b %e", localtime); open(IN, "$MAIL_LOG"); while() { 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() { 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() { if(/^$date/ && /spamd: identified spam/) { $spam++; } } close(IN); } if(-r $CLAMAV_LOG) { $date = strftime("%a %b %e", localtime); open(IN, $CLAMAV_LOG); while() { 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() { 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() { 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 = ; 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() { 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() { 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() { 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() { 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() { 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() { 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() { 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() { 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() { 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() { if(/^----------/) { $smb++; next; } if($smb) { $smb++ unless !$_; } } close(IN); $smb--; open(IN, "macusers 2>/dev/null |"); @data = ; 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() { 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() { 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() { 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() { (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() { 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() { 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() { 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 = ; 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() { 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() { 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); } $NFSS_VERSION = "4ops" if $NFSS_VERSION eq "4"; 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() { 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 = ; 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() { 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.*?Bitrate:<\/td>(\d*?)<\/td><\/tr>/g); my ($l) = ($data =~ m/$m.*?Current Listeners:<\/td>(\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() { 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() { 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 = ; 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 = ; 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() { 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() { 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 \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) { pause(); }