From 44e66720361cc32a483c4f4c1721a2e574775f1e Mon Sep 17 00:00:00 2001 From: "Adrien \"ze\" Urban" Date: Thu, 13 Sep 2012 21:02:01 +0200 Subject: [PATCH 1/4] cpu_linux_multi: detailed cpu usage per core (multigraph, supersampling) --- plugins/system/cpu_linux_multi | 387 +++++++++++++++++++++++++++++++++ 1 file changed, 387 insertions(+) create mode 100755 plugins/system/cpu_linux_multi diff --git a/plugins/system/cpu_linux_multi b/plugins/system/cpu_linux_multi new file mode 100755 index 00000000..4f2f10b4 --- /dev/null +++ b/plugins/system/cpu_linux_multi @@ -0,0 +1,387 @@ +#! /usr/bin/perl +######################################################################## +# # +# WARNING WARNING WARNING WARNING WARNING WARNING # +# # +# This plugin does not work properly with multiple master # +# # +######################################################################## +# +# +# multigraph, supersampling, extended cpu informations +# +# require: mpstat (to actually collect the data) +# require linux /proc +# (sorry, quick and dirty retrieve the number of cpu from /proc/cpuinfo) +# +# +# ENV (default): +# MUNIN_PLUGSTATE - pid and cache files gets there +# +# ENV (user defined): +# MUNIN_UPDATERATE - rate at which to update (default: 1s) +# MUNIN_CACHEFLUSH_RATE - flush data every N batch (default: 1) +# MUNIN_MPSTAT - binary to use as mpstat +# +# increase cache flush rate if you have i/o performance issues +# warning: increasing flushrate too much might cause partial write, and loss +# of data. 0 to disable flush +# +# +# Parent graph: cpu usage per core/thread +# child graph(1): detailed cpu usage overall +# child graph(n): detailed cpu usage per thread +# +# Known bugs: +# +# Multi-Master +# If there are many masters, the data is only sent once. Each master will +# only have part of the data. +# +# Everlasting +# The daemon is launched on first config/fetch. A touch of the pidfile is +# done on every following config/fetch. The daemon should check if the +# pidfile is recent (configurable) enough, and stop itself if not. +# +# Graph Order +# There is currently (2.0.6) noway to order childgraphs. +# +# RRD file +# The master currently (2.0.6) generate rrd file for aggregate values, and +# complains that no data is provided for them (but the graph still works +# fine) + +#%# family=auto +#%# capabilities=autoconf + +use strict; +use warnings; + +my $plugin = $0; +$plugin =~ s/.*\///; + +# order to display +my $fields_order = [ + 'sys', + 'usr', + 'nice', + 'idle', + 'iowait', + 'irq', + 'soft', + 'steal', + 'guest', +]; +# order is the order given by mpstat +my $fields_info = [ + { + name => 'usr', + label => 'usr', + info => "%s time spent in normal programs and daemons", + }, { + name => 'nice', + label => 'nice', + info => "%s time spent in nice(1)d programs and daemons", + }, { + name => 'sys', + label => 'sys', + info => "%s time spent in kernel system activity", + }, { + name => 'iowait', + label => 'iowait', + info => "%s time spent waiting for blocking I/O operations", + }, { + name => 'irq', + label => 'irq', + info => "%s time spent handling interrupts", + }, { + name => 'soft', + label => 'soft', + info => "%s time spent handling software interrupts", + }, { + name => 'steal', + label => 'steal', + info => "%s time spent elsewhere (stolen from us)", + }, { + name => 'guest', + label => 'guest', + info => "%s time spent in a guest operating system", + }, { + name => 'idle', + label => 'idle', + info => "%s time spent idling (waiting to get something to do)", + } +]; + +# mpstat sampling interval +my $update_rate = 1; +if (defined $ENV{MUNIN_UPDATERATE}) { + if ($ENV{MUNIN_UPDATERATE} =~ /^[1-9][0-9]*$/) { + $update_rate = int($ENV{MUNIN_UPDATERATE}); + } else { + print STDERR "Invalid update_rate: $ENV{MUNIN_UPDATERATE}"; + } +} + +my $flush_interval = 1; +if (defined $ENV{MUNIN_CACHEFLUSH_RATE}) { + if ($ENV{MUNIN_CACHEFLUSH_RATE} =~ /^[0-9]+$/) { + $update_rate = int($ENV{MUNIN_CACHEFLUSH_RATE}); + } else { + print STDERR "Invalid flush rate: $ENV{MUNIN_CACHEFLUSH_RATE}"; + } +} + +my $mpstat = "mpstat"; +if (defined $ENV{MUNIN_MPSTAT}) { + if (-f $ENV{MUNIN_MPSTAT}) { + print STDERR "MUNIN_STAT: file not found: $ENV{MUNIN_MPSTAT}"; + } else { + $mpstat = defined $ENV{MUNIN_MPSTAT}; + } +} + +sub pidfile() { "$ENV{MUNIN_PLUGSTATE}/munin.$plugin.pid" } +sub cachefile() { "$ENV{MUNIN_PLUGSTATE}/munin.$plugin.cache" } + +sub graph_section() { "system:cpu" }; +sub graph_name() { "cpu_extended_multi_1s" }; +sub graph_title() { "CPU usage" }; +sub graph_title_all() { "Overall CPU usage" }; +sub graph_title_n($) { "CPU#" . shift . " usage" }; +sub acquire_name() { "<$plugin> collecting information" } + +my $cpu_count_cache = undef; +sub cpu_count() { + # XXX: is there any way to do that cleanly ? + if (not defined $cpu_count_cache) { + $cpu_count_cache = `grep -c ^processor /proc/cpuinfo`; + chomp $cpu_count_cache; + } + return $cpu_count_cache; +} + +sub is_running() { + if (-f pidfile()) { + my $pid = undef; + if (open FILE, "<", pidfile()) { + $pid = ; + close FILE; + chomp $pid; + } + if ($pid) { + # does not exist ? kill it + if (kill 0, $pid) { + return 1; + } + } + unlink(pidfile()); + } + return 0; +} + + +# FIXME: should also trap kill sigint and sigterm +# FIXME: check pidfile got touched recently +sub acquire() { + $0 = acquire_name(); + $ARGV = [ '' ]; + $0 = "<$plugin> collecting information"; + open PIDFILE, '>', pidfile() or die "open: @{[ pidfile() ]}: $!\n"; + print PIDFILE $$, "\n"; + close PIDFILE; + open CACHE, ">>", cachefile() or die "open: @{[ cachefile() ]}: $!\n"; + open MPSTAT, "-|", "$mpstat -P ALL $update_rate" or + die "open mpstat|: $!\n"; + my $flush_count = 0; + while () { + chomp; + my @field = split(); + if (!($field[1] =~ /^(all|[0-9]+)$/)) { + next; + } + $field[0] = $field[1]; + $field[1] = time(); + print CACHE join(" ", @field), "\n"; + if ($flush_interval) { + if ($flush_interval == ++$flush_count) { + CACHE->flush(); + $flush_count = 0; + } + } + } + unlink(pidfile()); + unlink(cachefile()); +} + +sub run_daemon() { + if (is_running()) { + my $atime; + my $mtime; + $atime = $mtime = time; + utime $atime, $mtime, pidfile(); + } else { + if (0 == fork()) { + close(STDIN); + close(STDOUT); + close(STDERR); + open STDIN, "<", "/dev/null"; + open STDOUT, ">", "/dev/null"; + open STDERR, ">", "/dev/null"; + acquire(); + exit(0); + } + } +} + + +sub run_autoconf() { + # in case we have specified args, check the file before that + my $file = $mpstat; + $file =~ s/ .*//; + my $path = `which "$file"`; + if ($path) { + print "yes\n"; + } else { + print "no\n"; + } +} + +sub show_config($$$) { + my $i = shift; + my $name = shift; + my $title = shift; + my $graph_order = "graph_order"; + for my $field (@$fields_order) { + $graph_order .= " $field"; + } + print <{name} eq $fields_order->[0]) { + $style = "AREA"; + } + print <{name}.label $field->{label} +$field->{name}.draw $style +$field->{name}.info @{[ sprintf($field->{info}, $name) ]} +$field->{name}.min 0 +$field->{name}.cdef $field->{name} +EOF + } +} + +sub run_config() { + run_daemon(); + my $cpus = cpu_count(); + my $graph_order = "graph_order"; + my $sub_order = "order cpuall"; + for (my $i = 0; $i < $cpus; ++$i) { + $graph_order .= " use$i=@{[ graph_name() ]}.cpu$i.idle"; + $sub_order .= " cpu$i"; + } +# none of those seems to have any effect +#domain_$sub_order +#node_$sub_order +#graph_$sub_order +#service_$sub_order +#category_$sub_order +#group_$sub_order + + print <{name}.value $line->[1]:$line->[$n] +EOF + ++$n; + } +} +sub run_fetch() { + run_daemon(); + if (open CACHE, "+<", cachefile()) { + my $cpus = {}; + while () { + chomp; + my $field = []; + @$field = split(/ /); + if (not defined $cpus->{$field->[0]}) { + $cpus->{$field->[0]} = []; + } + push @{$cpus->{$field->[0]}}, $field; + } + # finished reading ? trucate it right away + truncate CACHE, 0; + close CACHE; + foreach my $cpu (keys %$cpus) { + print <{$cpu}}) { + fetch_showline($line); + } + } + } +} + +my $cmd = 'fetch'; +if (defined $ARGV[0]) { + $cmd = $ARGV[0]; +} +if ('fetch' eq $cmd) { + run_fetch(); +} elsif ('config' eq $cmd) { + run_config(); +} elsif ('autoconf' eq $cmd) { + run_autoconf(); +} elsif ('daemon' eq $cmd) { + run_daemon(); +} else { + print STDERR < Date: Thu, 13 Sep 2012 21:24:05 +0200 Subject: [PATCH 2/4] cpu_linux_multi: license license, and a few more comments --- plugins/system/cpu_linux_multi | 79 ++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 23 deletions(-) diff --git a/plugins/system/cpu_linux_multi b/plugins/system/cpu_linux_multi index 4f2f10b4..92032f24 100755 --- a/plugins/system/cpu_linux_multi +++ b/plugins/system/cpu_linux_multi @@ -1,5 +1,32 @@ #! /usr/bin/perl ######################################################################## +# Copyright (c) 2012, Adrien Urban +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the +# distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +######################################################################## # # # WARNING WARNING WARNING WARNING WARNING WARNING # # # @@ -7,12 +34,11 @@ # # ######################################################################## # -# # multigraph, supersampling, extended cpu informations # # require: mpstat (to actually collect the data) # require linux /proc -# (sorry, quick and dirty retrieve the number of cpu from /proc/cpuinfo) +# (sorry, quick/dirty retrieve the number of cpu from /proc/cpuinfo) # # # ENV (default): @@ -24,8 +50,8 @@ # MUNIN_MPSTAT - binary to use as mpstat # # increase cache flush rate if you have i/o performance issues -# warning: increasing flushrate too much might cause partial write, and loss -# of data. 0 to disable flush +# warning: increasing flushrate too much might cause partial write, and +# loss of data. 0 to disable flush # # # Parent graph: cpu usage per core/thread @@ -50,6 +76,7 @@ # The master currently (2.0.6) generate rrd file for aggregate values, and # complains that no data is provided for them (but the graph still works # fine) +# #%# family=auto #%# capabilities=autoconf @@ -60,17 +87,18 @@ use warnings; my $plugin = $0; $plugin =~ s/.*\///; +# quick failsafe +if (!defined $ENV{MUNIN_PLUGSTATE}) { + die "This plugin should be run via munin. Try munin-run $plugin\n"); +} + +######################################################################## +# If you want to change something, it's probably doable here +# + # order to display my $fields_order = [ - 'sys', - 'usr', - 'nice', - 'idle', - 'iowait', - 'irq', - 'soft', - 'steal', - 'guest', + 'sys', 'usr', 'nice', 'idle', 'iowait', 'irq', 'soft', 'steal', 'guest', ]; # order is the order given by mpstat my $fields_info = [ @@ -113,6 +141,21 @@ my $fields_info = [ } ]; +sub pidfile() { "$ENV{MUNIN_PLUGSTATE}/munin.$plugin.pid" } +sub cachefile() { "$ENV{MUNIN_PLUGSTATE}/munin.$plugin.cache" } + +sub graph_section() { "system:cpu" }; +sub graph_name() { "cpu_extended_multi_1s" }; +sub graph_title() { "CPU usage" }; +sub graph_title_all() { "Overall CPU usage" }; +sub graph_title_n($) { "CPU#" . shift . " usage" }; +sub acquire_name() { "<$plugin> collecting information" } + +######################################################################## +# if you need to change something after that line, It should probably be +# changed to be configurable above it. +# + # mpstat sampling interval my $update_rate = 1; if (defined $ENV{MUNIN_UPDATERATE}) { @@ -141,16 +184,6 @@ if (defined $ENV{MUNIN_MPSTAT}) { } } -sub pidfile() { "$ENV{MUNIN_PLUGSTATE}/munin.$plugin.pid" } -sub cachefile() { "$ENV{MUNIN_PLUGSTATE}/munin.$plugin.cache" } - -sub graph_section() { "system:cpu" }; -sub graph_name() { "cpu_extended_multi_1s" }; -sub graph_title() { "CPU usage" }; -sub graph_title_all() { "Overall CPU usage" }; -sub graph_title_n($) { "CPU#" . shift . " usage" }; -sub acquire_name() { "<$plugin> collecting information" } - my $cpu_count_cache = undef; sub cpu_count() { # XXX: is there any way to do that cleanly ? From 5d4c3e8ec1c8cc7b60d8cc30060590c2bd099ae8 Mon Sep 17 00:00:00 2001 From: "Adrien \"ze\" Urban" Date: Fri, 14 Sep 2012 17:56:12 +0200 Subject: [PATCH 3/4] cpu_linux_multi: fix a typo preventing it to run --- plugins/system/cpu_linux_multi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/system/cpu_linux_multi b/plugins/system/cpu_linux_multi index 92032f24..63fe9591 100755 --- a/plugins/system/cpu_linux_multi +++ b/plugins/system/cpu_linux_multi @@ -89,7 +89,7 @@ $plugin =~ s/.*\///; # quick failsafe if (!defined $ENV{MUNIN_PLUGSTATE}) { - die "This plugin should be run via munin. Try munin-run $plugin\n"); + die "This plugin should be run via munin. Try munin-run $plugin\n"; } ######################################################################## From cdf6eeb3df44cdf595812cc5e633812f39e41954 Mon Sep 17 00:00:00 2001 From: "Adrien \"ze\" Urban" Date: Fri, 14 Sep 2012 19:06:15 +0200 Subject: [PATCH 4/4] interfaces_linux_multi: interfaces, supersampling, multigraph First release 3 graphs per interfaces : - bps - pkt/s - errors graph types can be disabled/enabled interfaces can be included/excluded --- plugins/network/interfaces_linux_multi | 617 +++++++++++++++++++++++++ 1 file changed, 617 insertions(+) create mode 100755 plugins/network/interfaces_linux_multi diff --git a/plugins/network/interfaces_linux_multi b/plugins/network/interfaces_linux_multi new file mode 100755 index 00000000..eff953aa --- /dev/null +++ b/plugins/network/interfaces_linux_multi @@ -0,0 +1,617 @@ +#! /usr/bin/perl +######################################################################## +# Copyright (c) 2012, Adrien Urban +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the +# distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +######################################################################## +# # +# WARNING WARNING WARNING WARNING WARNING WARNING # +# # +# This plugin does not work properly with multiple master # +# # +######################################################################## +# +# multigraph, supersampling, detailed interfaces statistics +# +# require: ifconfig +# linux only for now. Would need to implement way to gather the same +# data for other ifconfig usage and output. +# require: Time::HiRes +# +# ENV (default): +# MUNIN_PLUGSTATE - pid and cache files gets there +# +# ENV (user defined): +# MUNIN_UPDATERATE - rate at which to update (default: 1s) +# MUNIN_CACHEFLUSH_RATE - flush data every N batch (default: 1) +# MUNIN_IFCONFIG - path for ifconfig (default /sbin/ifconfig) +# +# MUNIN_IF_INCLUDE - list of interfaces to graph (all by default) +# MUNIN_IF_EXCLUDE - exclude all of those interfaces (none by default) +# MUNIN_GRAPH_BYTES - do graph bytes per seconds (default: yes) +# MUNIN_GRAPH_PACKETS - do graph packets per seconds (default: yes) +# MUNIN_GRAPH_ERRORS - do graph errors (default: yes) +# +# Parent graphs: none +# child graphs: per interface - bytes, packets, errors +# interfaces/XXX/{bw,pkt,err} +# +# Known bugs: +# +# Multi-Master +# If there are many masters, the data is only sent once. Each master will +# only have part of the data. +# +# Everlasting +# The daemon is launched on first config/fetch. A touch of the pidfile is +# done on every following config/fetch. The daemon should check if the +# pidfile is recent (configurable) enough, and stop itself if not. +# +# Graph Order +# There is currently (2.0.6) noway to order childgraphs. +# +# RRD file +# The master currently (2.0.6) generate rrd file for aggregate values, and +# complains that no data is provided for them (but the graph still works +# fine) +# + +#%# family=auto +#%# capabilities=autoconf + +use strict; +use warnings; +use Time::HiRes; + +my $plugin = $0; +$plugin =~ s/.*\///; + +# quick failsafe +if (!defined $ENV{MUNIN_PLUGSTATE}) { + die "This plugin should be run via munin. Try munin-run $plugin\n"; +} + +######################################################################## +# If you want to change something, it's probably doable here +# + +sub pidfile() { "$ENV{MUNIN_PLUGSTATE}/munin.$plugin.pid" } +sub cachefile() { "$ENV{MUNIN_PLUGSTATE}/munin.$plugin.cache" } + +sub graph_section() { "system:network" } +sub graph_name() { "interfaces" } +#sub graph_title() { "interfaces" } +#sub graph_title_all() { "Overall CPU usage" } +#sub graph_title_n($) { "CPU#" . shift . " usage" } +sub acquire_name() { "<$plugin> collecting information" } + +# Default update rate. Can be changed by configuration. +my $update_rate = 1; +# default flush interval. Can be changed by configuration. +my $flush_interval = 1; +# default ifconfig command. Can be changed by configuration +my $ifconfig = '/sbin/ifconfig'; + +######################################################################## +# if you need to change something after that line, It should probably be +# changed to be configurable above it. +# + +if (defined $ENV{MUNIN_UPDATERATE}) { + if ($ENV{MUNIN_UPDATERATE} =~ /^[1-9][0-9]*$/) { + $update_rate = int($ENV{MUNIN_UPDATERATE}); + } else { + print STDERR "Invalid update_rate: $ENV{MUNIN_UPDATERATE}"; + } +} + +if (defined $ENV{MUNIN_CACHEFLUSH_RATE}) { + if ($ENV{MUNIN_CACHEFLUSH_RATE} =~ /^[0-9]+$/) { + $update_rate = int($ENV{MUNIN_CACHEFLUSH_RATE}); + } else { + print STDERR "Invalid flush rate: $ENV{MUNIN_CACHEFLUSH_RATE}"; + } +} + +if (defined $ENV{MUNIN_IFCONFIG}) { + if (-f $ENV{MUNIN_IFCONFIG}) { + print STDERR "MUNIN_IFCONFIG: file not found: $ENV{MUNIN_IFCONFIG}"; + } else { + $ifconfig = defined $ENV{MUNIN_IFCONFIG}; + } +} + +my $include_list = undef; +if (defined $ENV{MUNIN_IF_INCLUDE}) { + $include_list = [ split(/[[:space:]]+/, $ENV{MUNIN_IF_INCLUDE}) ]; + if (0 == scalar (@$include_list)) { + $include_list = undef; + } elsif ('' eq $include_list->[0]) { + shift @$include_list; + } +} +my $exclude_list = undef; +if (defined $ENV{MUNIN_IF_INCLUDE}) { + $exclude_list = [ split(/[[:space:]]+/, $ENV{MUNIN_IF_INCLUDE}) ]; + if (0 == scalar (@$exclude_list)) { + $exclude_list = undef; + } elsif ('' eq $exclude_list->[0]) { + shift @$exclude_list; + } +} + +sub configbool($) { + my $str = shift; + if ($str =~ /^(y(es)?|1|t(rue)?)$/i) { + return 1; + } + if ($str =~ /^(no?|0|f(alse)?)$/i) { + return 0; + } + print STDERR "$str: unrecognized bool\n"; + return 1; +} + +my $should_graph = { + 'bytes' => 1, + 'packets' => 1, + 'errors' => 1, +}; +if (defined $ENV{MUNIN_GRAPH_BYTES}) { + $should_graph->{'bytes'} = configbool($ENV{MUNIN_GRAPH_BYTES}); +} +if (defined $ENV{MUNIN_GRAPH_PACKETS}) { + $should_graph->{'packets'} = configbool($ENV{MUNIN_GRAPH_PACKETS}); +} +if (defined $ENV{MUNIN_GRAPH_ERRORS}) { + $should_graph->{'errors'} = configbool($ENV{MUNIN_GRAPH_ERRORS}); +} +unless ($should_graph->{bytes} or $should_graph->{packets} + or $should_graph->{errors}) { + die "Nothing to graph!"; +} + +######################################################################## +# Base functions, specific to what we really try to do here. +# +sub included_interface($) +{ + my $if = shift; + if (defined $exclude_list) { + foreach my $ifl (@$exclude_list) { + return 0 if ($if =~ /^($ifl)$/); + } + } + if (defined $include_list) { + foreach my $ifl (@$include_list) { + return 1 if ($if =~ /^($ifl)$/); + } + return 0; + } + return 1; +} +sub if_to_name($) +{ + my $if = shift; + $if =~ s/[^A-Za-z0-9]/_/g; + return $if; +} + +sub get_data() +{ + open IFCONFIG, "-|", $ifconfig or + die "open: $ifconfig|: $!\n"; + my $data = {}; + my $current_if = undef; + while () { + if (/^([^[:space:]]+)/) { + $current_if = $1; + if (!included_interface($current_if)) { + $current_if = undef; + next + } + $data->{$current_if} = {}; + next; # nothing else on that line + } + next if (!defined $current_if); + if (/RX packets:([0-9]+) errors:([0-9]+) dropped:([0-9]+) overruns:([0-9]+) frame:([0-9]+)/) { + $data->{$current_if}{'rx_pkt'} = $1; + $data->{$current_if}{'rx_err'} = $2; + $data->{$current_if}{'rx_drp'} = $3; + $data->{$current_if}{'rx_ovr'} = $4; + $data->{$current_if}{'rx_frm'} = $5; + next; + } + if (/TX packets:([0-9]+) errors:([0-9]+) dropped:([0-9]+) overruns:([0-9]+) carrier:([0-9]+)/) { + $data->{$current_if}{'tx_pkt'} = $1; + $data->{$current_if}{'tx_err'} = $2; + $data->{$current_if}{'tx_drp'} = $3; + $data->{$current_if}{'tx_ovr'} = $4; + $data->{$current_if}{'tx_car'} = $5; + next; + } + if (/RX bytes:([0-9]+) \([^)]*\) TX bytes:([0-9]+) /) { + $data->{$current_if}{'rx_byt'} = $1; + $data->{$current_if}{'tx_byt'} = $2; + } + } + close IFCONFIG; + return $data; +} + +# values names, from a data line +sub get_data_names($) +{ + my $line = shift; + my $name = $line->[0]; + my $count = scalar(@$line) - 2; # 2: name, and timestamp + if ($name =~ /\.(bps|pkt)$/ and 2 == $count) { + return [ 'rx', 'tx' ]; + } + if ($name =~ /\.err$/ and 8 == $count) { + return [ 'rxerr', 'txerr', 'rxdrp', 'txdrp', + 'rxovr', 'txovr', 'rxfrm', 'txcar', ]; + } + # no idea what it is ? corrupted data + return undef; +} + +sub collect_info_once($$) +{ + my $fh = shift; + my $now = shift; + my $data = get_data(); + foreach my $if (keys %$data) { + my $name = if_to_name($if); + my $d = $data->{$if}; + if ($should_graph->{'bytes'}) { + print $fh <{'rx_byt'} $d->{'tx_byt'} +EOF +#$name.byt $now rx $d->{'rx_byt'} +#$name.byt $now tx $d->{'tx_byt'} + } + if ($should_graph->{'packets'}) { + print $fh <{'rx_pkt'} $d->{'tx_pkt'} +EOF +#$name.pkt $now rx $d->{'rx_pkt'} +#$name.pkt $now tx $d->{'tx_pkt'} + } + if ($should_graph->{'errors'}) { + print $fh <{'rx_err'} $d->{'tx_err'} $d->{'rx_drp'} $d->{'tx_drp'} $d->{'rx_ovr'} $d->{'tx_ovr'} $d->{'rx_frm'} $d->{'tx_car'} +EOF +#$name.err $now rxerr $d->{'rx_err'} +#$name.err $now txerr $d->{'tx_err'} +#$name.err $now rxdrp $d->{'rx_drp'} +#$name.err $now txdrp $d->{'tx_drp'} +#$name.err $now rxovr $d->{'rx_ovr'} +#$name.err $now txovr $d->{'tx_ovr'} +#$name.err $now rxfrm $d->{'rx_frm'} +#$name.err $now txcar $d->{'tx_car'} + } + } +} +sub show_config() +{ + my $data = get_data(); + my $graph_order = "graph_order"; + foreach my $if (sort keys %$data) { + my $name = if_to_name($if); + $graph_order .= " ${name}_bps=${name}.bps.tx"; + } + print <{'bytes'}) { + print <{'packets'}) { + print <{'errors'}) { + print <; + close FILE; + chomp $pid; + } + if ($pid) { + # does not exist ? kill it + if (kill 0, $pid) { + return 1; + } + } + unlink(pidfile()); + } + return 0; +} + + +# FIXME: should also trap kill sigint and sigterm +# FIXME: check pidfile got touched recently +sub collect_loop() { + $0 = acquire_name(); + $0 = "<$plugin> collecting information"; + # write our pid + open PIDFILE, '>', pidfile() or die "open: @{[ pidfile() ]}: $!\n"; + print PIDFILE $$, "\n"; + close PIDFILE; + # open cache + my $fh_cache; + open $fh_cache, ">>", cachefile() or + die "open: @{[ cachefile() ]}: $!\n"; + my @tick = Time::HiRes::gettimeofday(); + my $flush_count = 0; + while (1) { + collect_info_once($fh_cache, $tick[0]); + if ($flush_interval) { + if ($flush_interval == ++$flush_count) { + $fh_cache->flush(); + $flush_count = 0; + } + } + my @now = Time::HiRes::gettimeofday(); + # when should the next tick be ? + $tick[0] += $update_rate; + # how long until next tick ? + my $diff = ($tick[0] - $now[0]) * 1000000 + + $tick[1] - $now[1]; + if ($diff <= 0) { + # next tick already passed ? damn! + @tick = @now; + } else { + # sleep what remains + Time::HiRes::usleep($diff); + } + } + unlink(pidfile()); + unlink(cachefile()); +} + +# launch daemon if not running +# notify the daemon we still need it (touch its pid) +sub daemon_alive() { + if (check_running()) { + my $atime; + my $mtime; + $atime = $mtime = time; + utime $atime, $mtime, pidfile(); + } else { + if (0 == fork()) { + close(STDIN); + close(STDOUT); + close(STDERR); + open STDIN, "<", "/dev/null"; + open STDOUT, ">", "/dev/null"; + open STDERR, ">", "/dev/null"; + collect_loop(); + exit(0); + } + } +} + + +sub run_autoconf() { + if (check_req()) { + print "yes\n"; + } else { + print "no\n"; + } +} + +sub run_config() { + daemon_alive(); + show_config(); +} + +sub fetch_showline($) { + my $line = shift; + my $names = get_data_names($line); + # don't display anything if we don't like what it is + return unless (defined $names); + my $graph = shift @$line; + my $time = shift @$line; + foreach my $value (@$line) { + my $name = shift @$names; + print <) { + chomp; + my $field = []; + @$field = split(/ /); + if (not defined $data->{$field->[0]}) { + $data->{$field->[0]} = []; + } + push @{$data->{$field->[0]}}, $field; + } + # finished reading ? trucate it right away + truncate CACHE, 0; + close CACHE; + foreach my $graph (keys %$data) { + print <{$graph}}) { + fetch_showline($line); + } + } + } +} + +my $cmd = 'fetch'; +if (defined $ARGV[0]) { + $cmd = $ARGV[0]; +} +if ('fetch' eq $cmd) { + run_fetch(); +} elsif ('config' eq $cmd) { + run_config(); +} elsif ('autoconf' eq $cmd) { + run_autoconf(); +} elsif ('daemon' eq $cmd) { + run_daemon(); +} else { + print STDERR <