diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..a01ee289
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.*.swp
diff --git a/images/README.md b/images/README.md
index fedf12ba..9216f9b0 100644
--- a/images/README.md
+++ b/images/README.md
@@ -1,2 +1,4 @@
Please **don't** put screenshots of your plugins here.
Put them right next to your plugins.
+
+The `t-shirt/` subdir contains a t-shirt vector gfx.
diff --git a/images/t-shirts/README.md b/images/t-shirts/README.md
new file mode 100644
index 00000000..ae8aa3a4
--- /dev/null
+++ b/images/t-shirts/README.md
@@ -0,0 +1,7 @@
+# T-Shirt subproject
+
+Some t-shirts
+
+
+
+
diff --git a/images/t-shirts/logo-horizontal-tshirt-black-modified.svg b/images/t-shirts/logo-horizontal-tshirt-black-modified.svg
new file mode 100644
index 00000000..f0e0c683
--- /dev/null
+++ b/images/t-shirts/logo-horizontal-tshirt-black-modified.svg
@@ -0,0 +1,75 @@
+
+
+
+
diff --git a/images/t-shirts/logo-horizontal-tshirt-black.svg b/images/t-shirts/logo-horizontal-tshirt-black.svg
new file mode 100644
index 00000000..3ce5c0e6
--- /dev/null
+++ b/images/t-shirts/logo-horizontal-tshirt-black.svg
@@ -0,0 +1,172 @@
+
+
+
+
diff --git a/images/t-shirts/logo-shirt-black.svg b/images/t-shirts/logo-shirt-black.svg
new file mode 100644
index 00000000..6b2d8289
--- /dev/null
+++ b/images/t-shirts/logo-shirt-black.svg
@@ -0,0 +1,57 @@
+
+
+
+
diff --git a/plugins/asterisk/asterisk b/plugins/asterisk/asterisk
new file mode 100755
index 00000000..7a3c13ce
--- /dev/null
+++ b/plugins/asterisk/asterisk
@@ -0,0 +1,378 @@
+#!/usr/bin/perl -w
+# -*- cperl -*-
+
+=head1 NAME
+
+asterisk - Multigraph-capable plugin to monitor Asterisk
+
+=head1 NOTES
+
+This plugin will produce multiple graphs showing:
+
+ - total number of active channels (replaces asterisk_channels),
+ together with breakdown of specific channel types (replaces
+ asterisk_channelstypes);
+
+ - the number of messages in all voicemail boxes (replaces
+ asterisk_voicemail);
+
+ - the number of active MeetMe conferences and users connected to them
+ (replace asterisk_meetme and asterisk_meetmeusers, respectively);
+
+ - the number of active channels for a given codec, for both SIP and
+ IAX2 channels (replaces asterisk_sipchannels and asterisk_codecs).
+
+=head1 CONFIGURATION
+
+The following configuration parameters are used by this plugin
+
+ [asterisk]
+ env.host - hostname to connect to
+ env.port - port number to connect to
+ env.username - username used for authentication
+ env.secret - secret used for authentication
+ env.channels - The channel types to look for
+ env.codecsx - List of codec IDs (hexadecimal values)
+ env.codecs - List of codecs names, matching codecsx order
+
+The "username" and "secret" parameters are mandatory, and have no
+defaults.
+
+=head2 DEFAULT CONFIGURATION
+
+ [asterisk]
+ env.host 127.0.0.1
+ env.port 5038
+ env.channels Zap IAX2 SIP
+ env.codecsx 0x2 0x4 0x8
+ env.codecs gsm ulaw alaw
+
+=head2 WILDCARD CONFIGURATION
+
+It's possible to use the plugin in a virtual-node capacity, in which
+case the host configuration will default to the hostname following the
+underscore:
+
+ [asterisk_someserver]
+ env.host someserver
+ env.port 5038
+
+=head1 AUTHOR
+
+Copyright (C) 2005-2006 Rodolphe Quiedeville
+Copyright (C) 2012 Diego Elio Pettenò
+
+=head1 LICENSE
+
+GPLv2
+
+=head1 MAGIC MARKERS
+
+ #%# family=auto
+ #%# capabilities=autoconf
+
+=cut
+
+use strict;
+use Munin::Plugin;
+use IO::Socket;
+
+sub asterisk_command {
+ my ($socket, $command) = @_;
+ my $line, my $reply;
+
+ $socket->print("Action: command\nCommand: $command\n\n");
+
+ # Response: (Error|Follows|???)
+ $line = $socket->getline;
+ if ($line ne "Response: Follows\r\n") {
+ while ( $line = $socket->getline and $line ne "\r\n" ) {}
+ return undef;
+ }
+
+ # Privilege: Command
+ $line = $socket->getline;
+
+ # Until we get the --END COMMAND-- marker, it's the command's output.
+ while ( $line = $socket->getline and $line ne "--END COMMAND--\r\n" ) {
+ $reply .= $line;
+ }
+
+ # And then wait for the empty line that says we're done
+ while ( $line = $socket->getline and $line ne "\r\n" ) {}
+
+ return $reply;
+}
+
+$0 =~ /asterisk(?:_(.+))$/;
+my $hostname = $1;
+
+my $peeraddr = $ENV{'host'} || $hostname || '127.0.0.1';
+my $peerport = $ENV{'port'} || '5038';
+
+my $username = $ENV{'username'};
+my $secret = $ENV{'secret'};
+
+my @CHANNELS = exists $ENV{'channels'} ? split ' ',$ENV{'channels'} : qw(Zap IAX2 SIP);
+my @CODECS = exists $ENV{'codecs'} ? split ' ',$ENV{'codecs'} : qw(gsm ulaw alaw);
+my @CODECSX = exists $ENV{'codecsx'} ? split ' ',$ENV{'codecsx'} : qw(0x2 0x4 0x8);
+
+my $line, my $error;
+my $socket = new IO::Socket::INET(PeerAddr => $peeraddr,
+ PeerPort => $peerport,
+ Proto => 'tcp')
+ or $error = "Could not create socket: $!";
+
+if ( $socket ) {
+ # This will consume the "Asterisk Call Manager" welcome line.
+ $socket->getline;
+
+ $socket->print("Action: login\nUsername: $username\nSecret: $secret\nEvents: off\n\n");
+ my $response_status = $socket->getline;
+
+ if ( $response_status ne "Response: Success\r\n" ) {
+ my $response_message = $socket->getline;
+ $response_message =~ s/Message: (.*)\r\n/$1/;
+ $error = "Asterisk authentication error: " . $response_message;
+ }
+
+ while ( $line = $socket->getline and $line ne "\r\n" ) {}
+}
+
+if ( $ARGV[0] and $ARGV[0] eq 'autoconf' ) {
+ if ( $error ) {
+ print "no ($error)\n";
+ } else {
+ print "yes\n";
+ }
+
+ exit 0;
+} elsif ( $ARGV[0] and $ARGV[0] eq 'config' ) {
+ print "host_name $hostname" if $hostname;
+
+ print <close();
+
+my $active_channels = 'U';
+$active_channels = $1 if $channels_response =~ /\n([0-9]+) active channels/;
+
+print < $HOME/run/$(basename $0).txt
+
+# Get users list
+touch $HOME/run/$(basename $0).users
+awk '{ print $2 }' $HOME/run/$(basename $0).txt | cat $HOME/run/$(basename $0).users - | sort -ru > $HOME/run/$(basename $0).users.tmp
+mv $HOME/run/$(basename $0).users.tmp $HOME/run/$(basename $0).users
+
+# Emit config
+if [ "$1" = "config" ]
+then
+ awk ' { print $1 ".label " $1 "\n" $1 ".draw AREASTACK" }' $HOME/run/$(basename $0).users
+fi
+
+# Emit values
+for i in $( cat $HOME/run/$(basename $0).users )
+do
+ TMPLINE=$(awk -v i=$i '($2 == i) { print }' $HOME/run/$(basename $0).txt)
+ if [ -z "$TMPLINE" ]
+ then
+ echo "$i.value 0"
+ else
+ echo "$TMPLINE" | awk ' { print $2 ".value " $1 }'
+
+ fi
+done
diff --git a/plugins/gpu/nvidia_gpu_ b/plugins/gpu/nvidia_gpu_
index a43f5989..453c50fa 100755
--- a/plugins/gpu/nvidia_gpu_
+++ b/plugins/gpu/nvidia_gpu_
@@ -180,7 +180,7 @@ case $name in
totalMemGpu=`echo "$totalMemGpus" | sed -n $(( $nGpusCounter + 1 ))p`
usedMemGpu=`echo "$usedMemGpus" | sed -n $(( $nGpusCounter + 1 ))p`
percentMemUsed=$(( $usedMemGpu * 100 / $totalMemGpu ))
- valueGpus="${valueGpus}${percentMemUsed}\n"
+ valueGpus="${valueGpus}${percentMemUsed}"
: $(( nGpusCounter = $nGpusCounter + 1 ))
done
;;
diff --git a/plugins/http/http_request_time b/plugins/http/http_request_time
index 509db0d8..8ecd774d 100755
--- a/plugins/http/http_request_time
+++ b/plugins/http/http_request_time
@@ -10,7 +10,12 @@
=head1 CONFIGURATION
[http_request_time]
- env.url http://127.0.0.1/1 http://127.0.0.1/2 http://127.0.0.1/3
+ env.url1 http://127.0.0.1/1
+ env.url2 http://127.0.0.1/2
+ env.url3 http://www.example.com
+ env.url3_name some_munin_internal_name
+ env.url3_label Some random page on our website
+ env.url3_proxy http://firewall:3128
=head1 MAGIC MARKERS
@@ -41,7 +46,6 @@ sub clean {
return $surl;
};
-
if (! eval "require LWP::UserAgent;")
{
$ret = "LWP::UserAgent not found";
@@ -50,12 +54,19 @@ if (! eval "require LWP::UserAgent;")
}
}
-my $URL = $ENV{'url'}?$ENV{'url'}:"http://127.0.0.1/";
my %URLS;
-foreach $_ (split(/ /,$URL)){
- $URLS{$_}={
- url=>$_,
- surl=>clean($_),
+
+for (my $i = 1; $ENV{"url$i"}; $i++)
+{
+ my $url = $ENV{"url$i"};
+ my $proxy = $ENV{"url${i}_proxy"};
+ my $name = $ENV{"url${i}_name"} || clean($url);
+ my $label = $ENV{"url${i}_label"} || $url;
+
+ $URLS{$name}={
+ url=>$url,
+ proxy=>$proxy,
+ label=>$label,
time=>'U'
};
}
@@ -94,50 +105,67 @@ if ( defined $ARGV[0] and $ARGV[0] eq "config" )
print "graph_category other\n";
my @go;
- foreach my $url (values %URLS) {
- print "$$url{'surl'}.label $$url{'url'}\n";
- print "$$url{'surl'}.info The response time of a single request\n";
- print "$$url{'surl'}.min 0\n";
- print "$$url{'surl'}.draw LINE1\n";
- push(@go,$$url{'surl'});
+ foreach my $name (keys %URLS) {
+ my $url = $URLS{$name};
+ print "$name.label $$url{'label'}\n";
+ print "$name.info The response time of a single request\n";
+ print "$name.min 0\n";
+ print "$name.draw LINE1\n";
+ push(@go, $name);
}
# multigraphs
- foreach my $url (values %URLS) {
- print "\nmultigraph http_request_time.$$url{'surl'}\n";
+ foreach my $name (keys %URLS) {
+ my $url = $URLS{$name};
+ print "\nmultigraph http_request_time.$name\n";
print "graph_title $$url{'url'}\n";
print "graph_args --base 1000\n";
print "graph_vlabel response time in ms\n";
print "graph_category other\n";
- print "$$url{'surl'}.label $$url{'url'}\n";
- print "$$url{'surl'}.info The response time of a single request\n";
- print "$$url{'surl'}.min 0\n";
- print "$$url{'surl'}.draw LINE1\n";
+ print "$name.label $$url{'label'}\n";
+ print "$name.info The response time of a single request\n";
+ print "$name.min 0\n";
+ print "$name.draw LINE1\n";
}
exit 0;
}
my $ua = LWP::UserAgent->new(timeout => 15);
+foreach my $name (keys %URLS) {
+ my $url = $URLS{$name};
-foreach my $url (values %URLS) {
- my $t1=[gettimeofday];
+ if ($url->{proxy}) {
+ $ua->proxy(['http', 'ftp'], $url->{proxy});
+ }
+ else {
+ $ua->proxy(['http', 'ftp'], undef);
+ }
+
+ # warm up
my $response = $ua->request(HTTP::Request->new('GET',$$url{'url'}));
+
+ # timed run
+ my $t1=[gettimeofday];
+ $response = $ua->request(HTTP::Request->new('GET',$$url{'url'}));
my $t2=[gettimeofday];
+
if ($response->is_success) {
$$url{'time'}=sprintf("%d",tv_interval($t1,$t2)*1000);
};
};
print("multigraph http_request_time\n");
-foreach my $url (values %URLS) {
- print("$$url{'surl'}.value $$url{'time'}\n");
-}
-foreach my $url (values %URLS) {
- print("\nmultigraph http_request_time.$$url{'surl'}\n");
- print("$$url{'surl'}.value $$url{'time'}\n");
+foreach my $name (keys %URLS) {
+ my $url = $URLS{$name};
+ print("$name.value $$url{'time'}\n");
}
+foreach my $name (keys %URLS) {
+ my $url = $URLS{$name};
+ print("\nmultigraph http_request_time.$name\n");
+ print("$name.value $$url{'time'}\n");
+}
# vim:syntax=perl
diff --git a/plugins/mail/qmailconn b/plugins/mail/qmailconn
index 65413ef2..eb4fbf9e 100755
--- a/plugins/mail/qmailconn
+++ b/plugins/mail/qmailconn
@@ -2,7 +2,11 @@
#
# Plugin to show amount of smtp-connections per hour
#
-# Contributed by Håkon Nessjøen
+# Contributed by Håkon Nessjøen
+#
+# Modified by Massimiliano Cianelli
+# 2013-02-02 - Added support for Greylist and simscan Virus\Spam detect
+# Added LOGPATH env var
#
# Magic markers - optional - used by installation scripts and
# munin-config:
@@ -10,6 +14,9 @@
#%# family=manual
#%# capabilities=autoconf
+# get: env.logpath
+LOGPATH=${logpath:-/var/log/qmail/qmail-smtpd/}
+
if [ "$1" = "autoconf" ]; then
echo yes
exit 0
@@ -17,26 +24,41 @@ fi
if [ "$1" = "config" ]; then
- echo 'graph_title Qmail SMTP connections'
- echo 'graph_args --base 1000 -l 0 '
- echo 'graph_vlabel connections/hour'
- echo 'graph_category Mail'
- echo 'graph_order rbl accepted total'
- echo 'rbl.label RBL rejected connections'
- echo 'rbl.min 0'
- echo 'rbl.draw AREA'
- echo 'accepted.label Accepted connections'
- echo 'accepted.min 0'
- echo 'accepted.draw STACK'
- echo 'total.label Total connections'
- echo 'total.min 0'
- echo 'total.draw LINE1'
+ echo 'graph_title Qmail SMTP connections'
+ echo 'graph_args --base 1000 -l 0 '
+ echo 'graph_vlabel connections/hour'
+ echo 'graph_category mail'
+ echo 'graph_order rbl greylisted accepted simscan_spam simscan_virus total'
+ echo 'rbl.label RBL rejected connections'
+ echo 'rbl.min 0'
+ echo 'rbl.draw AREA'
+ echo 'greylisted.label Greylisted connections'
+ echo 'greylisted.min 0'
+ echo 'greylisted.draw STACK'
+ echo 'accepted.label Accepted connections'
+ echo 'accepted.min 0'
+ echo 'accepted.draw STACK'
+ echo 'simscan_spam.label Rejected SPAM'
+ echo 'simscan_spam.min 0'
+ echo 'simscan_spam.draw STACK'
+ echo 'simscan_virus.label Rejected VIRUS'
+ echo 'simscan_virus.min 0'
+ echo 'simscan_virus.draw STACK'
+ echo 'total.label Total connections'
+ echo 'total.min 0'
+ echo 'total.draw LINE1'
exit 0
fi
-rbl=`cat /var/log/qmail/smtpd/@* /var/log/qmail/smtpd/current | grep -c rblsmtp`
-accepted=`cat /var/log/qmail/smtpd/@* /var/log/qmail/smtpd/current | grep -c 'tcpserver: ok'`
+rbl=`cat $LOGPATH/@* $LOGPATH/current | grep -c rblsmtp`
+accepted=`cat $LOGPATH/@* $LOGPATH/current | grep -c 'tcpserver: ok'`
+greylisted=`cat $LOGPATH/@* $LOGPATH/current | grep -ce "jgreylist\[[[:digit:]]\+\]: .\+\: GREY"`
+simscan_spam=`cat $LOGPATH/@* $LOGPATH/current | grep -ce "simscan:\[[[:digit:]]\+\]:SPAM"`
+simscan_virus=`cat $LOGPATH/@* $LOGPATH/current | grep -ce "simscan:\[[[:digit:]]\+\]:VIRUS"`
echo -n "rbl.value " && ( echo $rbl || echo U )
echo -n "accepted.value " && ( echo $accepted || echo U )
-echo "total.value $[$rbl + $accepted]"
+echo -n "greylisted.value " && ( echo $greylisted || echo U )
+echo -n "simscan_spam.value " && ( echo $simscan_spam || echo U )
+echo -n "simscan_virus.value " && ( echo $simscan_virus || echo U )
+echo "total.value $(expr $rbl + $accepted + $greylisted)"
diff --git a/plugins/mysql/mysql_size_ondisk b/plugins/mysql/mysql_size_ondisk
new file mode 100755
index 00000000..c18b9a38
--- /dev/null
+++ b/plugins/mysql/mysql_size_ondisk
@@ -0,0 +1,80 @@
+#!/bin/bash
+#
+INFO=\
+"mysql_size_ondisk - Munin plugin that reports the size of the files and
+ directories in /var/lib/mysql, biggest to smallest.
+ To correctly count InnoDB tables you should have innodb_file_per_table enabled
+ in MySQL (good practise anyway), otherwise all InnoDB tables will be counted into
+ ibdataX.
+ This plugin must be run as the user mysql, to do that append the following
+ to your /etc/munin/plugins/plugin-conf.d/munin-node:
+
+ [mysql_size_ondisk]
+ user mysql
+
+ This plugin gives you similar information as mysql_size_all. A difference
+ is that mysql_size_ondisk is much faster (0.4 seconds vs 14 seconds, on a server
+ with 170 databases, 26 GB total). Also note that mysql_size_all gives you the net
+ data size, mysql_size_ondisk gives you the gross storage space used, which may be
+ much more than your actual data."
+#
+# License: GPLv2 or later
+#
+# v1.0, 27.01.2012 Jakob Unterwurzacher
+
+#%# family=auto
+#%# capabilities=autoconf
+
+set -eu
+
+DIR=/var/lib/mysql
+
+function clean {
+ # First character must not be a number
+ a=${1/#[0-9]/_}
+ # Other characters must be alphanumeric
+ b=${a//[^a-zA-Z0-9]/_}
+ echo $b
+}
+
+if [ "${1:-}" = "autoconf" ]
+then
+ if du -sb $DIR &> /dev/null
+ then
+ echo "yes"
+ exit 0
+ else
+ echo "no"
+ exit 1
+ fi
+elif [ "${1:-}" = "config" ]
+then
+ echo "graph_title MySQL on-disk database size"
+ echo "graph_category mysql"
+ # graph_info cannot have newlines - replace by
which will be rendered to newlines in
+ # the web interface.
+ echo "graph_info ${INFO//$'\n'/
}"
+ echo "graph_args --base 1024 --lower-limit 0"
+ echo "graph_vlabel Bytes"
+ cd $DIR
+ du -sb * | sort -nr | while read s i
+ do
+ i=`clean $i`
+ echo "$i.label $i"
+ echo "$i.type GAUGE"
+ echo "$i.draw AREASTACK"
+ done
+ exit 0
+elif [ "${1:-}" = "" ]
+then
+ cd $DIR
+ du -sb * | sort -nr | while read s i
+ do
+ i=`clean $i`
+ echo "$i.value $s"
+ done
+ exit 0
+else
+ echo "Unknown argument"
+ exit 1
+fi
diff --git a/plugins/network/if1sec_ b/plugins/network/if1sec_
new file mode 100755
index 00000000..6e5ad6d4
--- /dev/null
+++ b/plugins/network/if1sec_
@@ -0,0 +1,59 @@
+#! /bin/sh
+
+pluginfull="$0" # full name of plugin
+plugin="${0##*/}" # name of plugin
+pidfile="$MUNIN_PLUGSTATE/munin.$plugin.pid"
+cache="$MUNIN_PLUGSTATE/munin.$plugin.value"
+
+IFACE="${0##*/if1sec_}" # interface
+
+if [ ! -r "/sys/class/net/$IFACE/statistics/tx_bytes" ]
+then
+ echo "# Unknown Interface : $IFACE"
+ exit 1
+fi
+
+if [ "$1" = "acquire" ]
+then
+ (
+ while sleep 1
+ do
+ echo $(
+ date +%s
+ cat /sys/class/net/$IFACE/statistics/tx_bytes
+ cat /sys/class/net/$IFACE/statistics/rx_bytes
+ )
+ done | awk "{
+ print \"${IFACE}_tx.value \" \$1 \":\" \$2;
+ print \"${IFACE}_rx.value \" \$1 \":\" \$3;
+ system(\"\");
+ }" >> $cache
+ ) &
+ echo $! > $pidfile
+ exit 0
+fi
+
+
+if [ "$1" = "config" ]
+then
+ cat < ${cache}
+
+exit 0
diff --git a/plugins/network/netatalk b/plugins/network/netatalk
old mode 100644
new mode 100755
diff --git a/plugins/network/ping/ping b/plugins/network/ping/ping
index 010d39ad..858fb6d3 100755
--- a/plugins/network/ping/ping
+++ b/plugins/network/ping/ping
@@ -164,6 +164,12 @@ for (my $host_i = 0; $host_i < @host_addrs; ++$host_i) {
my $h_norm_name = clean_fieldname($h_cur_name);
if ($config_mode) {
print "$h_norm_name.min 0\n$h_norm_name.label $h_cur_name\n$h_norm_name.draw LINE2\n";
+ my ( $warning, $critical ) =
+ get_thresholds($h_norm_name);
+ printf "%s.warning %s\n", $h_norm_name, $warning
+ if $warning;
+ printf "%s.critical %s\n", $h_norm_name, $critical
+ if $critical;
} else {
my $pid = $fork ? fork() : -1;
if ($pid <= 0) {
diff --git a/plugins/network/qos_ b/plugins/network/qos_
index 6382cfe1..3b9bc6fa 100755
--- a/plugins/network/qos_
+++ b/plugins/network/qos_
@@ -16,6 +16,8 @@
# tc - Override default program
# ignore_queue - Queue with handle not be plotted
# label_name - Queue with handle as defined label
+# update_rate - Custom update_rate, to be used with async
+# graph_data_size - Custom graph_data_size, to be used with async & custom update_rate
#
# Magic markers:
#%# family=manual
@@ -100,6 +102,8 @@ if ( exists $ARGV[0] and $ARGV[0] eq 'config' ) {
print "graph_vlabel bits per \${graph_period}\n";
print "graph_category network\n";
print "graph_info This graph shows the QoS queue of the $IFACE network interface.\n";
+ print "update_rate $ENV{update_rate}\n" if $ENV{update_rate};
+ print "graph_data_size $ENV{graph_data_size}\n" if $ENV{graph_data_size};
print "graph_order ";
foreach my $key (sort by_handle keys %queues) {
$haschild = 0;
diff --git a/plugins/network/traffic b/plugins/network/traffic
index 5df88dc6..a3f8f09e 100755
--- a/plugins/network/traffic
+++ b/plugins/network/traffic
@@ -22,6 +22,8 @@ If trouble reading files, use:
=item 2012.09.20: Initial version by Arturo Borrero Gonzalez
+=item 2013.01.12: Added percentage graphing by Michiel Holtkamp
+
=back
=head1 LICENSE
@@ -39,6 +41,7 @@ GPLv2
if [ "$1" == "config" ]
then
cat <<'EOF'
+multigraph traffic
graph_title Throughput by IP protocol
graph_vlabel bits per ${graph_period}
graph_category network
@@ -55,6 +58,32 @@ total.label Total bps
total.min 0
total.type DERIVE
total.draw LINE1
+EOF
+
+ # Adapted from http://munin-monitoring.org/wiki/PercentGraphHowto
+ cat <<'EOF'
+multigraph traffic_percent
+graph_scale no
+graph_title Throughput of IP protocols by percentage
+graph_vlabel Percentage
+graph_order IPv4=traffic.IPv4 IPv6=traffic.IPv6 total=traffic.total IPv4_percent=traffic.total IPv6_percent=traffic.total total_percent=traffic.total
+graph_category network
+graph_args --upper-limit 100 -l 0 -r
+IPv4.label no
+IPv6.label no
+total.label no
+total_percent.label no
+IPv4.graph no
+IPv6.graph no
+total.graph no
+total_percent.graph no
+total_percent.cdef total,0.0000001,+
+IPv4_percent.label IPv4
+IPv4_percent.cdef IPv4,total_percent,/,100,*
+IPv4_percent.draw AREASTACK
+IPv6_percent.label IPv6
+IPv6_percent.cdef IPv6,total_percent,/,100,*
+IPv6_percent.draw AREASTACK
EOF
exit 0
fi
diff --git a/plugins/network/traffic_ipt b/plugins/network/traffic_ipt
new file mode 100755
index 00000000..d03b45b2
--- /dev/null
+++ b/plugins/network/traffic_ipt
@@ -0,0 +1,128 @@
+#!/bin/bash
+# -*- bash -*-
+
+: << =cut
+
+=head1 NAME
+
+traffic - Plugin to monitor the traffic (throughput) by IP protocols.
+
+=head1 CONFIGURATION
+
+To make this plugin work, you need to add rules to your firewall.
+They are empty rules, we only use them to count traffic, not do anything
+with them. To make this plugin work correctly, these rules have to
+in the beginning of the chain(s), or else traffic that matches rules
+above will not be counted (you can use this to your advantage of course).
+
+The rules can be added with:
+iptables -I INPUT
+iptables -I OUTPUT
+ip6tables -I INPUT
+ip6tables -I OUTPUT
+
+If trouble reading output, use:
+
+ [traffic_ipt]
+ user root
+
+=head1 AUTHORS
+
+=over
+
+=item 2012.09.20: Initial version by Arturo Borrero Gonzalez
+
+=item 2013.01.12: Added percentage graphing by Michiel Holtkamp
+
+=item 2013.02.03: Converted to use iptables/ip6tables by Michiel Holtkamp
+
+=back
+
+=head1 LICENSE
+
+GPLv2
+
+=head1 MAGIC MARKERS
+
+ #%# family=auto
+ #%# capabilities=autoconf
+
+=cut
+
+
+if [ "$1" == "config" ]
+then
+ cat <<'EOF'
+multigraph traffic_ipt
+graph_title Throughput by IP protocol
+graph_vlabel bits per ${graph_period}
+graph_category network
+graph_args --base 1000 --upper-limit 100 -l 0
+IPv4.label IPv4 bps
+IPv4.min 0
+IPv4.type DERIVE
+IPv4.draw AREA
+IPv6.label IPv6 bps
+IPv6.min 0
+IPv6.type DERIVE
+IPv6.draw STACK
+total.label Total bps
+total.min 0
+total.type DERIVE
+total.draw LINE1
+EOF
+
+ # Adapted from http://munin-monitoring.org/wiki/PercentGraphHowto
+ cat <<'EOF'
+multigraph traffic_ipt_percent
+graph_scale no
+graph_title Throughput of IP protocols by percentage
+graph_vlabel Percentage
+graph_order IPv4=traffic_ipt.IPv4 IPv6=traffic_ipt.IPv6 total=traffic_ipt.total IPv4_percent=traffic_ipt.total IPv6_percent=traffic_ipt.total total_percent=traffic_ipt.total
+graph_category network
+graph_args --upper-limit 100 -l 0 -r
+IPv4.label no
+IPv6.label no
+total.label no
+total_percent.label no
+IPv4.graph no
+IPv6.graph no
+total.graph no
+total_percent.graph no
+total_percent.cdef total,0.0000001,+
+IPv4_percent.label IPv4
+IPv4_percent.cdef IPv4,total_percent,/,100,*
+IPv4_percent.draw AREASTACK
+IPv6_percent.label IPv6
+IPv6_percent.cdef IPv6,total_percent,/,100,*
+IPv6_percent.draw AREASTACK
+EOF
+ exit 0
+fi
+
+
+ipv4=0
+ipv6=0
+
+IPv4_bytes=$(iptables -L -n -v -x | egrep '^\W+[0-9]+\W+[0-9]+\W+all\W+--\W+\*\W+\*\W+0.0.0.0/0\W+0.0.0.0/0\W+$' | while read pkts bytes rest; do echo $bytes; done)
+if [ -z "$IPv4_bytes" ];
+then
+ echo "W: Unable to read rule from iptables, please add rules" >&2
+else
+ ipv4=$(echo $IPv4_bytes | sed -e 's/ / + /' | bc -l)
+fi
+
+IPv6_bytes=$(ip6tables -L -n -v -x | egrep '^\W+[0-9]+\W+[0-9]+\W+all\W+\*\W+\*\W+::/0\W+::/0\W+$' | while read pkts bytes rest; do echo $bytes; done)
+if [ -z "$IPv6_bytes" ];
+then
+ echo "W: Unable to read rule from ip6tables, please add rules" >&2
+else
+ ipv6=$(echo $IPv6_bytes | sed -e 's/ / + /' | bc -l)
+fi
+
+echo "IPv4.value $ipv4"
+echo "IPv6.value $ipv6"
+echo "total.value $( echo $ipv4 + $ipv6 | bc )"
+
+exit 0
+
diff --git a/plugins/other/bitcoind_ b/plugins/other/bitcoind_
index bf75a589..a62cb7a2 100755
--- a/plugins/other/bitcoind_
+++ b/plugins/other/bitcoind_
@@ -56,6 +56,7 @@ def main():
'transactions': ("Transactions", "Transactions",
('confirmed', 'waiting')),
'block_age': ("Last Block Age", "Seconds"),
+ 'difficulty': ("Difficulty", ""),
}
labels = request_labels[request_var]
if len(labels) < 3:
diff --git a/plugins/qpid/qpid_bytedepth b/plugins/qpid/qpid_bytedepth
new file mode 100755
index 00000000..43959fe8
--- /dev/null
+++ b/plugins/qpid/qpid_bytedepth
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+#
+# Plugin to monitor Apache Qpid
+# - graphs the number of outstanding bytes on queue(s) specified in config
+#
+# Parameters understood:
+#
+# queues (required) - space separated list of queues to display (regex allowed)
+#
+# Made by Jimmy Jones (jimmyjones2 AT gmx DOT co DOT uk)
+#
+# Licence: GPLv2
+#
+
+import re
+import sys
+import os
+from qmf.console import Session
+
+if not "queues" in os.environ:
+ print >> sys.stderr, "Missing env.queues in config"
+ sys.exit(-1)
+
+output_queue = []
+sess = Session()
+broker = sess.addBroker()
+queues = sess.getObjects(_class="queue", _package="org.apache.qpid.broker")
+for q in queues:
+ for match in os.environ["queues"].split(" "):
+ if re.match(match, q.name):
+ output_queue.append(re.sub('[^a-zA-Z0-9_]', '_', q.name))
+
+if len(sys.argv) > 1 and sys.argv[1] == "config":
+ print "graph_category Qpid";
+ print "graph_title Queue byte depth";
+ print "graph_vlabel bytes"
+ for queue in output_queue:
+ print "%s.label %s" % (queue, queue)
+ print "%s.min 0" % queue
+ print "%s.type GAUGE" % queue
+else:
+ for q in queues:
+ qname = re.sub('[^a-zA-Z0-9_]', '_', q.name)
+ if qname in output_queue:
+ print "%s.value %u" % (qname, q.byteDepth)
+
+sess.delBroker(broker)
+
diff --git a/plugins/qpid/qpid_discardsring b/plugins/qpid/qpid_discardsring
new file mode 100755
index 00000000..62d85d1b
--- /dev/null
+++ b/plugins/qpid/qpid_discardsring
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+#
+# Plugin to monitor Apache Qpid
+# - graphs the number of messages discarded from queue(s) specified in config
+#
+# Parameters understood:
+#
+# queues (required) - space separated list of queues to display (regex allowed)
+#
+# Made by Jimmy Jones (jimmyjones2 AT gmx DOT co DOT uk)
+#
+# Licence: GPLv2
+#
+
+import re
+import sys
+import os
+from qmf.console import Session
+
+if not "queues" in os.environ:
+ print >> sys.stderr, "Missing env.queues in config"
+ sys.exit(-1)
+
+output_queue = []
+sess = Session()
+broker = sess.addBroker()
+queues = sess.getObjects(_class="queue", _package="org.apache.qpid.broker")
+for q in queues:
+ for match in os.environ["queues"].split(" "):
+ if re.match(match, q.name):
+ output_queue.append(re.sub('[^a-zA-Z0-9_]', '_', q.name))
+
+if len(sys.argv) > 1 and sys.argv[1] == "config":
+ print "graph_category Qpid";
+ print "graph_title Ring queue discard rate";
+ print "graph_vlabel messages/second";
+ for queue in output_queue:
+ print "%s.label %s" % (queue, queue)
+ print "%s.min 0" % queue
+ print "%s.type COUNTER" % queue
+else:
+ for q in queues:
+ qname = re.sub('[^a-zA-Z0-9_]', '_', q.name)
+ if qname in output_queue:
+ print "%s.value %u" % (qname, q.discardsRing)
+
+sess.delBroker(broker)
+
diff --git a/plugins/qpid/qpid_enqueuebytes b/plugins/qpid/qpid_enqueuebytes
new file mode 100755
index 00000000..8afc420f
--- /dev/null
+++ b/plugins/qpid/qpid_enqueuebytes
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+#
+# Plugin to monitor Apache Qpid
+# - graph ingest rate (bytes/sec) of queue(s) specified in config
+#
+# Parameters understood:
+#
+# queues (required) - space separated list of queues to display (regex allowed)
+#
+# Made by Jimmy Jones (jimmyjones2 AT gmx DOT co DOT uk)
+#
+# Licence: GPLv2
+#
+
+import re
+import sys
+import os
+from qmf.console import Session
+
+if not "queues" in os.environ:
+ print >> sys.stderr, "Missing env.queues in config"
+ sys.exit(-1)
+
+output_queue = []
+sess = Session()
+broker = sess.addBroker()
+queues = sess.getObjects(_class="queue", _package="org.apache.qpid.broker")
+for q in queues:
+ for match in os.environ["queues"].split(" "):
+ if re.match(match, q.name):
+ output_queue.append(re.sub('[^a-zA-Z0-9_]', '_', q.name))
+
+if len(sys.argv) > 1 and sys.argv[1] == "config":
+ print "graph_category Qpid";
+ print "graph_title Enqueue data rate";
+ print "graph_vlabel bytes/second"
+ for queue in output_queue:
+ print "%s.label %s" % (queue, queue)
+ print "%s.min 0" % queue
+ print "%s.type COUNTER" % queue
+else:
+ for q in queues:
+ qname = re.sub('[^a-zA-Z0-9_]', '_', q.name)
+ if qname in output_queue:
+ print "%s.value %u" % (qname, q.byteTotalEnqueues)
+
+sess.delBroker(broker)
+
diff --git a/plugins/qpid/qpid_enqueuecount b/plugins/qpid/qpid_enqueuecount
new file mode 100755
index 00000000..45420d89
--- /dev/null
+++ b/plugins/qpid/qpid_enqueuecount
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+#
+# Plugin to monitor Apache Qpid
+# - graphs ingest rate (messages/sec) of queue(s) specified in config
+#
+# Parameters understood:
+#
+# queues (required) - space separated list of queues to display (regex allowed)
+#
+# Made by Jimmy Jones (jimmyjones2 AT gmx DOT co DOT uk)
+#
+# Licence: GPLv2
+#
+
+import re
+import sys
+import os
+from qmf.console import Session
+
+if not "queues" in os.environ:
+ print >> sys.stderr, "Missing env.queues in config"
+ sys.exit(-1)
+
+output_queue = []
+sess = Session()
+broker = sess.addBroker()
+queues = sess.getObjects(_class="queue", _package="org.apache.qpid.broker")
+for q in queues:
+ for match in os.environ["queues"].split(" "):
+ if re.match(match, q.name):
+ output_queue.append(re.sub('[^a-zA-Z0-9_]', '_', q.name))
+
+if len(sys.argv) > 1 and sys.argv[1] == "config":
+ print "graph_category Qpid";
+ print "graph_title Enqueue message rate";
+ print "graph_vlabel messages/second"
+ for queue in output_queue:
+ print "%s.label %s" % (queue, queue)
+ print "%s.min 0" % queue
+ print "%s.type COUNTER" % queue
+else:
+ for q in queues:
+ qname = re.sub('[^a-zA-Z0-9_]', '_', q.name)
+ if qname in output_queue:
+ print "%s.value %u" % (qname, q.msgTotalEnqueues)
+
+sess.delBroker(broker)
+
diff --git a/plugins/qpid/qpid_msgdepth b/plugins/qpid/qpid_msgdepth
new file mode 100755
index 00000000..33143d00
--- /dev/null
+++ b/plugins/qpid/qpid_msgdepth
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+#
+# Plugin to monitor Apache Qpid
+# - graphs the number of outstanding messages on queue(s) specified in config
+#
+# Parameters understood:
+#
+# queues (required) - space separated list of queues to display (regex allowed)
+#
+# Made by Jimmy Jones (jimmyjones2 AT gmx DOT co DOT uk)
+#
+# Licence: GPLv2
+#
+
+import re
+import sys
+import os
+from qmf.console import Session
+
+if not "queues" in os.environ:
+ print >> sys.stderr, "Missing env.queues in config"
+ sys.exit(-1)
+
+output_queue = []
+sess = Session()
+broker = sess.addBroker()
+queues = sess.getObjects(_class="queue", _package="org.apache.qpid.broker")
+for q in queues:
+ for match in os.environ["queues"].split(" "):
+ if re.match(match, q.name):
+ output_queue.append(re.sub('[^a-zA-Z0-9_]', '_', q.name))
+
+if len(sys.argv) > 1 and sys.argv[1] == "config":
+ print "graph_category Qpid";
+ print "graph_title Queue message depth";
+ print "graph_vlabel messages"
+ for queue in output_queue:
+ print "%s.label %s" % (queue, queue)
+ print "%s.min 0" % queue
+ print "%s.type GAUGE" % queue
+else:
+ for q in queues:
+ qname = re.sub('[^a-zA-Z0-9_]', '_', q.name)
+ if qname in output_queue:
+ print "%s.value %u" % (qname, q.msgDepth)
+
+sess.delBroker(broker)
+
diff --git a/plugins/sensors/esxi__sensors b/plugins/sensors/esxi__sensors
index d33b507f..87556193 100755
--- a/plugins/sensors/esxi__sensors
+++ b/plugins/sensors/esxi__sensors
@@ -27,6 +27,11 @@ file:
env.user monitor
env.pass passw0rd
+To create an account just for monitoring the ESXi, add a user to the host,
+make it a member of the 'root' group, and give it the 'No access' role.
+That will allow the account to connect via WBEM, but not via any of the
+management tools.
+
In the event that not all sensor types are desired, the plugin can be limited
to a subset of the types available:
diff --git a/plugins/sensors/freeipmi b/plugins/sensors/freeipmi
index 0d34fb06..feb64189 100755
--- a/plugins/sensors/freeipmi
+++ b/plugins/sensors/freeipmi
@@ -17,10 +17,29 @@ When used for the local host, the plugin should be linked as
non-wildcard plugin, i.e., 'freeipmi', whereas when used to monitor a
foreign host it should be, e.g., 'freeipmi_192.168.0.253'.
+=head1 DEPENDENCIES
+
+The plugin requires FreeIPMI 1.1.5 or later to fetch the information.
+Limits set on thresholds are available when using FreeIPMI 1.2.0 or
+later.
+
=head1 AUTHOR
Diego Elio Pettenò .
+With help and suggestions of:
+
+Bart ten Brinke
+
+=head1 LICENSE
+
+GPLv2
+
+=head1 MAGIC MARKERS
+
+ #%# family=auto
+ #%# capabilities=autoconf
+
=head1 LICENSE
GPLv2
@@ -42,12 +61,15 @@ my $IPMISENSORS = $ENV{'ipmisensors'} || 'ipmi-sensors';
$0 =~ /freeipmi(?:_(.+))$/;
my $hostname = $1;
-$IPMISENSORS .= " --quiet-cache --comma-separated-output --no-header-output --ignore-not-available-sensors --sensor-types=Temperature,Fan,Current,Voltage --output-sensor-thresholds";
+my $help_output = `$IPMISENSORS --help`;
+
+$IPMISENSORS .= " --output-sensor-thresholds" if $help_output =~ /--output-sensor-thresholds/;
+$IPMISENSORS .= " --quiet-cache --comma-separated-output --no-header-output --ignore-not-available-sensors --sensor-types=Temperature,Fan,Current,Voltage";
$IPMISENSORS .= " --hostname=$hostname" if defined($hostname);
$IPMISENSORS .= " --username=$ENV{IPMI_USERNAME}" if defined($ENV{IPMI_USERNAME});
$IPMISENSORS .= " --password=$ENV{IPMI_PASSWORD}" if defined($ENV{IPMI_PASSWORD});
-my $output=`$IPMISENSORS --output-sensor-thresholds 2>/dev/null`;
+my $output=`$IPMISENSORS 2>/dev/null`;
my $retval=$?;
if ( defined $ARGV[0] and $ARGV[0] eq 'autoconf' ) {
@@ -109,11 +131,11 @@ foreach my $line (@data) {
value => $dataline[3],
label => $dataline[1]
);
- $sensor{lwarn} = $dataline[7] ne "N/A" ? $dataline[7] : '';
- $sensor{hwarn} = $dataline[8] ne "N/A" ? $dataline[8] : '';
+ $sensor{lwarn} = (defined($dataline[7]) and $dataline[7] ne "N/A") ? $dataline[7] : '';
+ $sensor{hwarn} = (defined($dataline[8]) and $dataline[8] ne "N/A") ? $dataline[8] : '';
- $sensor{lcrit} = $dataline[6] ne "N/A" ? $dataline[6] : '';
- $sensor{hcrit} = $dataline[9] ne "N/A" ? $dataline[9] : '';
+ $sensor{lcrit} = (defined($dataline[6]) and $dataline[6] ne "N/A") ? $dataline[6] : '';
+ $sensor{hcrit} = (defined($dataline[9]) and $dataline[9] ne "N/A") ? $dataline[9] : '';
my $type;
if ( $dataline[2] eq "Temperature" ) {
@@ -158,7 +180,7 @@ END
}
}
- unless ( $ENV{MUNIN_CAP_DIRTYCONFIG} == 1 ) {
+ unless ( ($ENV{MUNIN_CAP_DIRTYCONFIG} || 0) == 1 ) {
exit 0;
}
}
diff --git a/plugins/sensors/hwmon b/plugins/sensors/hwmon
index 5e755436..66e44801 100755
--- a/plugins/sensors/hwmon
+++ b/plugins/sensors/hwmon
@@ -173,7 +173,7 @@ END
}
}
- unless ( $ENV{MUNIN_CAP_DIRTYCONFIG} == 1 ) {
+ unless ( ($ENV{MUNIN_CAP_DIRTYCONFIG} || 0) == 1 ) {
exit 0;
}
}
diff --git a/plugins/sickbeard/sickbeard_episodes b/plugins/sickbeard/sickbeard_episodes
new file mode 100755
index 00000000..2a6eb7d5
--- /dev/null
+++ b/plugins/sickbeard/sickbeard_episodes
@@ -0,0 +1,69 @@
+#!/usr/bin/perl
+#
+# Munin plugins for Sick-Beard
+#
+# Copyright (C) 2012 - Blauwbek
+# Copyright (C) 2012 - Thiago
+#
+# Sick-Beard : http://sickbeard.com/
+#
+# 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 3 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, see .
+#
+# Requires: JSON::Any
+# LWP::UserAgent
+#
+# Configuration example
+# [sickbeard*]
+# env.host http://host:port/
+# env.api apikey
+#
+
+use strict;
+use JSON::Any;
+use LWP::UserAgent;
+
+#defines
+my $HOST = exists $ENV{'host'} ? $ENV{'host'} : "http://localhost:8081/";
+my $API = exists $ENV{'api'} ? $ENV{'api'} : "";
+my $URL = $HOST."/api/".$API."/?cmd=shows.stats";
+my $sURL = sprintf $URL;
+
+#config output
+if(defined $ARGV[0] && $ARGV[0] eq 'config')
+{
+ print <new;
+my $req = $get->get($sURL);
+my $json = JSON::Any->jsonToObj($req->content());
+
+if ($json->{result} eq 'success') {
+ print "down.value $json->{data}->{ep_downloaded}\n";
+ print "total.value $json->{data}->{ep_total}\n";
+ exit 0;
+} else {
+ print "$json->{message}\n";
+ exit 1;
+}
+
diff --git a/plugins/sickbeard/sickbeard_shows b/plugins/sickbeard/sickbeard_shows
new file mode 100755
index 00000000..a81c6bd8
--- /dev/null
+++ b/plugins/sickbeard/sickbeard_shows
@@ -0,0 +1,69 @@
+#!/usr/bin/perl
+#
+# Munin plugins for Sick-Beard
+#
+# Copyright (C) 2012 - Blauwbek
+# Copyright (C) 2012 - Thiago
+#
+# Sick-Beard : http://sickbeard.com/
+#
+# 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 3 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, see .
+#
+# Requires: JSON::Any
+# LWP::UserAgent
+#
+# Configuration example
+# [sickbeard*]
+# env.host http://host:port/
+# env.api apikey
+#
+
+use strict;
+use JSON::Any;
+use LWP::UserAgent;
+
+#defines
+my $HOST = exists $ENV{'host'} ? $ENV{'host'} : "http://localhost:8081/";
+my $API = exists $ENV{'api'} ? $ENV{'api'} : "";
+my $URL = $HOST."/api/".$API."/?cmd=shows.stats";
+my $sURL = sprintf $URL;
+
+#config output
+if(defined $ARGV[0] && $ARGV[0] eq 'config')
+{
+ print <new;
+my $req = $get->get($sURL);
+my $json = JSON::Any->jsonToObj($req->content());
+
+if ($json->{result} eq 'success') {
+ print "active.value $json->{data}->{shows_active}\n";
+ print "total.value $json->{data}->{shows_total}\n";
+ exit 0;
+} else {
+ print "$json->{message}\n";
+ exit 1;
+}
+
diff --git a/plugins/system/cpu_by_process b/plugins/system/cpu_by_process
new file mode 100755
index 00000000..504ff309
--- /dev/null
+++ b/plugins/system/cpu_by_process
@@ -0,0 +1,106 @@
+#!/usr/bin/perl
+#
+# Copyright 2012 Chris Wilson
+# Copyright 2006 Holger Levsen
+#
+# This plugin monitors ALL processes on a system. No exceptions. It can
+# produce very big graphs! But if you want to know where your CPU time
+# is going without knowing what to monitor in advance, this can help;
+# or in addition to one of the more specific CPU plugins to monitor
+# just Apache or MySQL, for example.
+#
+# It's not obvious what the graph heights actually mean, even to me.
+# Each counter is a DERIVE (difference since the last counter reading)
+# of the CPU time usage (in seconds) accounted to each process, summed
+# by the process name, so all Apache and all MySQL processes are grouped
+# together. Processes with no CPU usage at all are ignored. Processes
+# that die may not appear on the graph, and anyway their last chunk of
+# CPU usage before they died is lost. You could modify this plugin to
+# read SAR/psacct records if you care about that.
+#
+# 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; version 2 dated June,
+# 1991.
+
+#scriptname=`basename $0`
+#vsname=`echo $scriptname | perl -ne '/^vserver_proc_VM_(.*)/ and print $1'`
+
+#if [ "$1" = "suggest" ]; then
+# ls -1 /etc/vservers
+# exit 0
+#elif [ -z "$vsname" ]; then
+# echo "Must be used with a vserver name; try '$0 suggest'" >&2
+# exit 2
+#fi
+
+use strict;
+use warnings;
+
+my $cmd = "ps -eo time,comm h";
+open PS, "$cmd|" or die "Failed to run ps command: $cmd: $!";
+
+# my $header_line = ;
+my %total_cpu_by_process;
+
+while ()
+{
+ my @fields = split;
+ my $cputime = $fields[0];
+ my $process = $fields[1];
+
+ # remove any / and everything after it from the process name,
+ # e.g. kworker/0:2 -> kworker
+ $process =~ s|/.*||;
+
+ # remove any . at the end of the name (why does this appear?)
+ # $process =~ s|\.$||;
+
+ # change any symbol that's not allowed in a munin variable name to _
+ $process =~ tr|a-zA-Z0-9|_|c;
+
+ my @times = split /:/, $cputime;
+ $cputime = (($times[0] * 60) + $times[1]) * 60 + $times[2];
+ $total_cpu_by_process{$process} += $cputime;
+}
+
+foreach my $process (keys %total_cpu_by_process)
+{
+ # remove all processes with 0 cpu time
+ if (not $total_cpu_by_process{$process})
+ {
+ delete $total_cpu_by_process{$process};
+ }
+}
+
+close(PS);
+
+if (@ARGV and $ARGV[1] eq "config")
+{
+ print < $pidfile
- mpstat -P ALL $interval |
- awk -v cpus=$cpus '$2>=0&&$2<10 {print $2, systime(), (100-$11)/cpus}' >> $cache
+ LANG=C mpstat -P ALL $interval |
+ awk -v cpus=$cpus '$2>=0&&$2<99 {print $2, systime(), (100-$11)/cpus; system("");}' >> $cache
rm -f $pidfile $cache
}
diff --git a/plugins/system/pagefaults_by_process b/plugins/system/pagefaults_by_process
new file mode 100755
index 00000000..00a6027f
--- /dev/null
+++ b/plugins/system/pagefaults_by_process
@@ -0,0 +1,105 @@
+#!/usr/bin/perl
+#
+# Copyright 2012 Chris Wilson
+# Copyright 2006 Holger Levsen
+#
+# This plugin monitors ALL processes on a system. No exceptions. It can
+# produce very big graphs! But if you want to know which processes are
+# killing your system by page faulting, without knowing what to monitor
+# in advance, this can help; or in addition to one of the more specific
+# plugins to monitor just Apache or MySQL, for example.
+#
+# Each counter is a DERIVE (difference since the last counter reading)
+# of the number of major page faults, usually 4k each, read in by a
+# process. Memory mapped files probably contribute to this. The process
+# cannot continue until the page fault is served, so this is a
+# high-priority read that usually indicates memory starvation.
+# Processes with no page faults at all are ignored. Processes
+# that die may not appear on the graph, and anyway their last chunk of
+# CPU usage before they died is lost. You could modify this plugin to
+# read SAR/psacct records if you care about that.
+#
+# 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; version 2 dated June,
+# 1991.
+
+#scriptname=`basename $0`
+#vsname=`echo $scriptname | perl -ne '/^vserver_proc_VM_(.*)/ and print $1'`
+
+#if [ "$1" = "suggest" ]; then
+# ls -1 /etc/vservers
+# exit 0
+#elif [ -z "$vsname" ]; then
+# echo "Must be used with a vserver name; try '$0 suggest'" >&2
+# exit 2
+#fi
+
+use strict;
+use warnings;
+
+my $cmd = "ps -eo maj_flt,comm h";
+open PS, "$cmd|" or die "Failed to run ps command: $cmd: $!";
+
+# my $header_line = ;
+my %total_by_process;
+
+while ()
+{
+ my @fields = split;
+ my $value = $fields[0];
+ my $process = $fields[1];
+
+ # remove any / and everything after it from the process name,
+ # e.g. kworker/0:2 -> kworker
+ $process =~ s|/.*||;
+
+ # remove any . at the end of the name (why does this appear?)
+ # $process =~ s|\.$||;
+
+ # change any symbol that's not allowed in a munin variable name to _
+ $process =~ tr|a-zA-Z0-9|_|c;
+
+ $total_by_process{$process} += $value;
+}
+
+foreach my $process (keys %total_by_process)
+{
+ # remove all processes with 0 faults
+ if (not $total_by_process{$process})
+ {
+ delete $total_by_process{$process};
+ }
+}
+
+close(PS);
+
+if (@ARGV and $ARGV[1] eq "config")
+{
+ print <;
+my %total_by_process;
+
+while ()
+{
+ my @fields = split;
+ my $value = $fields[0];
+ my $process = $fields[1];
+
+ # remove any / and everything after it from the process name,
+ # e.g. kworker/0:2 -> kworker
+ $process =~ s|/.*||;
+
+ # remove any . at the end of the name (why does this appear?)
+ # $process =~ s|\.$||;
+
+ # change any symbol that's not allowed in a munin variable name to _
+ $process =~ tr|a-zA-Z0-9|_|c;
+
+ $total_by_process{$process} += $value;
+}
+
+foreach my $process (keys %total_by_process)
+{
+ # remove all processes with 0 faults
+ if (not $total_by_process{$process})
+ {
+ delete $total_by_process{$process};
+ }
+}
+
+close(PS);
+
+if (@ARGV and $ARGV[1] == "config")
+{
+ print <"
+
+Modules: cput, cpup, mem, disk, net
Then restart munin-node and you're done.
@@ -34,10 +37,14 @@ NOTE: xentop always reports 0 for dom0's disk IOs and network traffic, but
both graphs show its entry all the same, so each domain can keep its own color
along the different graphs.
-=head2 CPU usage
+=head2 CPU time
This graph shows a percentage of the CPU time used by each domain.
+=head2 CPU usage
+
+This graph shows a percentage of the CPU percentage used by each domain.
+
=head2 Memory usage
This graph shows the amount of memory (in bytes) used by each domain.
@@ -66,6 +73,7 @@ Michael Renner for the C plugin which I borrowed some code from.
=head1 AUTHOR
Raphael HALIMI
+Modified by Krisztian IVANCSO
=head1 LICENSE
@@ -76,6 +84,8 @@ GPLv2
use strict;
use Munin::Plugin;
+my $XEN_SKIP = $ENV{xenskip} || "";
+
# autoconf - quite simple
if ($ARGV[0] eq "autoconf") {
if (`which xentop`) {
@@ -101,7 +111,7 @@ if ($ARGV[0] eq "autoconf") {
sub trim_label {
my ($type, $label) = @_; my $data_characters;
- my ($graph_width, $graph_border_width, $padding_characters, $pixels_per_character) = (400,97,10,6);
+ my ($graph_width, $graph_border_width, $padding_characters, $pixels_per_character) = (497,3,5,6);
if ($type eq 'pos') {$data_characters = 32;} elsif ($type eq 'neg') {$data_characters = 64;} else {return $label;}
my $available_characters = abs(($graph_width+$graph_border_width)/$pixels_per_character)-$padding_characters-$data_characters;
@@ -117,9 +127,9 @@ sub trim_label {
}
# Global variables
-my (%domains,$domain,$munindomain,$cpusecs,$memk,$nettxk,$netrxk,$vbdrd,$vbdwr);
+my (%domains,$domain,@domainlist,$munindomain,$cpusecs,$cpupercent,$memk,$nettxk,$netrxk,$vbdrd,$vbdwr);
-open (XENTOP,"xentop -b -f -i1 |") or die "Could not execute xentop, $!";
+open (XENTOP,"xentop -b -f -i2 |") or die "Could not execute xentop, $!";
# Now we build a hash of hashes to store information
while () {
@@ -129,7 +139,7 @@ while () {
next if /^NAME/;
# We define only what we need
- ($domain,undef,$cpusecs,undef,$memk,undef,undef,undef,undef,undef,$nettxk,$netrxk,undef,undef,$vbdrd,$vbdwr,undef,undef,undef) = split(/\s+/);
+ ($domain,undef,$cpusecs,$cpupercent,$memk,undef,undef,undef,undef,undef,$nettxk,$netrxk,undef,undef,$vbdrd,$vbdwr,undef,undef,undef) = split(/\s+/);
# We have to store the sanitised domain name in a separate variable
$domains{$domain}{'munindomain'} = clean_fieldname($domain);
@@ -137,6 +147,7 @@ while () {
# We need the remaining data only for a normal run
if ($ARGV[0] eq "") {
$domains{$domain}{'cpusecs'} = $cpusecs;
+ $domains{$domain}{'cpupercent'} = $cpupercent;
$domains{$domain}{'mem'} = $memk;
$domains{$domain}{'nettx'} = $nettxk;
$domains{$domain}{'netrx'} = $netrxk;
@@ -145,71 +156,94 @@ while () {
}
}
+@domainlist = sort(keys(%domains));
#
# config - quite simple, too
#
if ($ARGV[0] eq "config") {
- print "multigraph xen_cpu_time\n";
- print "graph_title Xen domains CPU time\n";
- print "graph_args --base 1000 -l 0\n";
- print "graph_vlabel %\n";
- print "graph_scale no\n";
- print "graph_category xen\n";
- print "graph_info This graph shows CPU time for each Xen domain.\n";
- for $domain (sort(keys(%domains))) {
- print "$domains{$domain}{'munindomain'}_cpu_time.label ".trim_label('pos',$domain)."\n";
- print "$domains{$domain}{'munindomain'}_cpu_time.type DERIVE\n";
- print "$domains{$domain}{'munindomain'}_cpu_time.cdef $domains{$domain}{'munindomain'}_cpu_time,100,*\n";
- print "$domains{$domain}{'munindomain'}_cpu_time.min 0\n";
- print "$domains{$domain}{'munindomain'}_cpu_time.draw AREASTACK\n";
+ if ($XEN_SKIP !~ /cput/) {
+ print "multigraph xen_cpu_time\n";
+ print "graph_title Xen domains CPU time\n";
+ print "graph_args --base 1000 -l 0\n";
+ print "graph_vlabel cpu seconds\n";
+ print "graph_scale no\n";
+ print "graph_category xen\n";
+ print "graph_info This graph shows CPU time for each Xen domain.\n";
+ for $domain (@domainlist) {
+ print "$domains{$domain}{'munindomain'}_cpu_time.label ".trim_label('pos',$domain)."\n";
+ print "$domains{$domain}{'munindomain'}_cpu_time.type DERIVE\n";
+ print "$domains{$domain}{'munindomain'}_cpu_time.cdef $domains{$domain}{'munindomain'}_cpu_time,100,*\n";
+ print "$domains{$domain}{'munindomain'}_cpu_time.min 0\n";
+ print "$domains{$domain}{'munindomain'}_cpu_time.draw AREASTACK\n";
+ }
}
- print "\nmultigraph xen_mem\n";
- print "graph_title Xen domains memory usage\n";
- print "graph_args --base 1024 -l 0\n";
- print "graph_vlabel bytes\n";
- print "graph_category xen\n";
- print "graph_info This graph shows memory usage for each Xen domain.\n";
- for $domain (sort(keys(%domains))) {
- print "$domains{$domain}{'munindomain'}_mem.label ".trim_label('pos',$domain)."\n";
- print "$domains{$domain}{'munindomain'}_mem.cdef $domains{$domain}{'munindomain'}_mem,1024,*\n";
- print "$domains{$domain}{'munindomain'}_mem.draw AREASTACK\n";
+ if ($XEN_SKIP !~ /cpup/) {
+ print "\nmultigraph xen_cpu\n";
+ print "graph_title Xen domains CPU utilization\n";
+ print "graph_args --base 1000 -l 0 --upper-limit 100\n";
+ print "graph_vlabel %\n";
+ print "graph_scale no\n";
+ print "graph_category xen\n";
+ print "graph_info This graph shows CPU utilization for each Xen domain.\n";
+ for $domain (@domainlist) {
+ print "$domains{$domain}{'munindomain'}_cpu.label ".trim_label('pos',$domain)."\n";
+ print "$domains{$domain}{'munindomain'}_cpu.draw AREASTACK\n"
+ }
}
- print "\nmultigraph xen_net\n";
- print "graph_title Xen domains network traffic\n";
- print "graph_args --base 1000\n";
- print "graph_vlabel bits per \${graph_period} in (-) / out (+)\n";
- print "graph_category xen\n";
- print "graph_info This graph shows network traffic for each Xen domain.\n";
- for $domain (sort(keys(%domains))) {
- print "$domains{$domain}{'munindomain'}_netrx.label none\n";
- print "$domains{$domain}{'munindomain'}_netrx.cdef $domains{$domain}{'munindomain'}_netrx,8192,*\n";
- print "$domains{$domain}{'munindomain'}_netrx.type COUNTER\n";
- print "$domains{$domain}{'munindomain'}_netrx.graph no\n";
- print "$domains{$domain}{'munindomain'}_nettx.label ".trim_label('neg',$domain)."\n";
- print "$domains{$domain}{'munindomain'}_nettx.cdef $domains{$domain}{'munindomain'}_nettx,8192,*\n";
- print "$domains{$domain}{'munindomain'}_nettx.type COUNTER\n";
- print "$domains{$domain}{'munindomain'}_nettx.draw AREASTACK\n";
- print "$domains{$domain}{'munindomain'}_nettx.negative $domains{$domain}{'munindomain'}_netrx\n";
+ if ($XEN_SKIP !~ /mem/) {
+ print "\nmultigraph xen_mem\n";
+ print "graph_title Xen domains memory usage\n";
+ print "graph_args --base 1024 -l 0\n";
+ print "graph_vlabel bytes\n";
+ print "graph_category xen\n";
+ print "graph_info This graph shows memory usage for each Xen domain.\n";
+ for $domain (@domainlist) {
+ print "$domains{$domain}{'munindomain'}_mem.label ".trim_label('pos',$domain)."\n";
+ print "$domains{$domain}{'munindomain'}_mem.cdef $domains{$domain}{'munindomain'}_mem,1024,*\n";
+ print "$domains{$domain}{'munindomain'}_mem.draw AREASTACK\n";
+ }
}
- print "\nmultigraph xen_disk\n";
- print "graph_title Xen domains disk IOs\n";
- print "graph_args --base 1000\n";
- print "graph_vlabel IOs per \${graph_period} read (-) / write (+)\n";
- print "graph_category xen\n";
- print "graph_info This graph shows disk IOs for each Xen domain.\n";
- for $domain (sort(keys(%domains))) {
- print "$domains{$domain}{'munindomain'}_vbdrd.label none\n";
- print "$domains{$domain}{'munindomain'}_vbdrd.type COUNTER\n";
- print "$domains{$domain}{'munindomain'}_vbdrd.graph no\n";
- print "$domains{$domain}{'munindomain'}_vbdwr.label ".trim_label('neg',$domain)."\n";
- print "$domains{$domain}{'munindomain'}_vbdwr.type COUNTER\n";
- print "$domains{$domain}{'munindomain'}_vbdwr.draw AREASTACK\n";
- print "$domains{$domain}{'munindomain'}_vbdwr.negative $domains{$domain}{'munindomain'}_vbdrd\n";
+ if ($XEN_SKIP !~ /net/) {
+ print "\nmultigraph xen_net\n";
+ print "graph_title Xen domains network traffic\n";
+ print "graph_args --base 1000\n";
+ print "graph_vlabel bits per \${graph_period} in (-) / out (+)\n";
+ print "graph_category xen\n";
+ print "graph_info This graph shows network traffic for each Xen domain.\n";
+ for $domain (@domainlist) {
+ print "$domains{$domain}{'munindomain'}_netrx.label none\n";
+ print "$domains{$domain}{'munindomain'}_netrx.cdef $domains{$domain}{'munindomain'}_netrx,8192,*\n";
+ print "$domains{$domain}{'munindomain'}_netrx.type COUNTER\n";
+ print "$domains{$domain}{'munindomain'}_netrx.graph no\n";
+ print "$domains{$domain}{'munindomain'}_nettx.label ".trim_label('neg',$domain)."\n";
+ print "$domains{$domain}{'munindomain'}_nettx.cdef $domains{$domain}{'munindomain'}_nettx,8192,*\n";
+ print "$domains{$domain}{'munindomain'}_nettx.type COUNTER\n";
+ print "$domains{$domain}{'munindomain'}_nettx.draw AREASTACK\n";
+ print "$domains{$domain}{'munindomain'}_nettx.negative $domains{$domain}{'munindomain'}_netrx\n";
+ }
+ }
+
+ if ($XEN_SKIP !~ /disk/) {
+ print "\nmultigraph xen_disk\n";
+ print "graph_title Xen domains disk IOs\n";
+ print "graph_args --base 1000\n";
+ print "graph_vlabel IOs per \${graph_period} read (-) / write (+)\n";
+ print "graph_category xen\n";
+ print "graph_info This graph shows disk IOs for each Xen domain.\n";
+ for $domain (@domainlist) {
+ print "$domains{$domain}{'munindomain'}_vbdrd.label none\n";
+ print "$domains{$domain}{'munindomain'}_vbdrd.type COUNTER\n";
+ print "$domains{$domain}{'munindomain'}_vbdrd.graph no\n";
+ print "$domains{$domain}{'munindomain'}_vbdwr.label ".trim_label('neg',$domain)."\n";
+ print "$domains{$domain}{'munindomain'}_vbdwr.type COUNTER\n";
+ print "$domains{$domain}{'munindomain'}_vbdwr.draw AREASTACK\n";
+ print "$domains{$domain}{'munindomain'}_vbdwr.negative $domains{$domain}{'munindomain'}_vbdrd\n";
+ }
}
exit 0;
@@ -220,24 +254,40 @@ if ($ARGV[0] eq "config") {
# Normal run
#
-print "multigraph xen_cpu_time\n";
-for $domain (sort(keys(%domains))) {
- print "$domains{$domain}{'munindomain'}_cpu_time.value $domains{$domain}{'cpusecs'}\n";
+if ($XEN_SKIP !~ /cput/) {
+ print "multigraph xen_cpu_time\n";
+ for $domain (@domainlist) {
+ print "$domains{$domain}{'munindomain'}_cpu_time.value $domains{$domain}{'cpusecs'}\n";
+ }
}
-print "\nmultigraph xen_mem\n";
-for $domain (sort(keys(%domains))) {
- print "$domains{$domain}{'munindomain'}_mem.value $domains{$domain}{'mem'}\n";
+if ($XEN_SKIP !~ /cpup/) {
+ print "\nmultigraph xen_cpu\n";
+ for $domain (@domainlist) {
+ print "$domains{$domain}{'munindomain'}_cpu.value $domains{$domain}{'cpupercent'}\n";
+ }
}
-print "\nmultigraph xen_net\n";
-for $domain (sort(keys(%domains))) {
- print "$domains{$domain}{'munindomain'}_nettx.value $domains{$domain}{'nettx'}\n";
- print "$domains{$domain}{'munindomain'}_netrx.value $domains{$domain}{'netrx'}\n";
+if ($XEN_SKIP !~ /mem/) {
+ print "\nmultigraph xen_mem\n";
+ for $domain (@domainlist) {
+ print "$domains{$domain}{'munindomain'}_mem.value $domains{$domain}{'mem'}\n";
+ }
}
-print "\nmultigraph xen_disk\n";
-for $domain (sort(keys(%domains))) {
- print "$domains{$domain}{'munindomain'}_vbdrd.value $domains{$domain}{'vbdrd'}\n";
- print "$domains{$domain}{'munindomain'}_vbdwr.value $domains{$domain}{'vbdwr'}\n";
+if ($XEN_SKIP !~ /net/) {
+ print "\nmultigraph xen_net\n";
+ for $domain (@domainlist) {
+ print "$domains{$domain}{'munindomain'}_nettx.value $domains{$domain}{'nettx'}\n";
+ print "$domains{$domain}{'munindomain'}_netrx.value $domains{$domain}{'netrx'}\n";
+ }
}
+
+if ($XEN_SKIP !~ /disk/) {
+ print "\nmultigraph xen_disk\n";
+ for $domain (@domainlist) {
+ print "$domains{$domain}{'munindomain'}_vbdrd.value $domains{$domain}{'vbdrd'}\n";
+ print "$domains{$domain}{'munindomain'}_vbdwr.value $domains{$domain}{'vbdwr'}\n";
+ }
+}
+
diff --git a/plugins/wowza/wowza-media-server b/plugins/wowza/wowza-media-server
index 81fee466..d732687d 100755
--- a/plugins/wowza/wowza-media-server
+++ b/plugins/wowza/wowza-media-server
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/python
"""
Plugin to monitor Wowza streaming servers.
@@ -83,14 +83,14 @@ tree.parse(f)
f.close()
vhosts = []
-for vh in tree.iter("VHost"):
+for vh in tree.findall("VHost"):
if vh.find("Name").text not in vhost_exclude:
applications = []
- for app in vh.iter("Application"):
+ for app in vh.findall("Application"):
if app.find("Name").text not in app_exclude:
if app.find("Status").text == "loaded":
clients = []
- for client in app.iter("Client"):
+ for client in app.findall("Client"):
if client.find("IpAddress").text not in client_exclude:
clients.append({"ClientId": client.find("ClientId").text,
"FlashVersion": client.find("FlashVersion").text,
@@ -289,7 +289,7 @@ elif plugin_name == "wowza_vhost_uptime":
elif plugin_name == "wowza_app_listeners":
for vh in vhosts:
for app in vh["Applications"]:
- print (vh["Name"].strip("/").replace(".","_").replace("-","_"),"_",app["Name"].strip("/").replace(".","_").replace("-","_"),".value ",app["TimeRunning"],sep='')
+ print (vh["Name"].strip("/").replace(".","_").replace("-","_"),"_",app["Name"].strip("/").replace(".","_").replace("-","_"),".value ",app["ConnectionsCurrent"],sep='')
elif plugin_name == "wowza_app_duration":
alldurations = {}
diff --git a/plugins/xastir/xastir b/plugins/xastir/xastir
new file mode 100755
index 00000000..c8944d6b
--- /dev/null
+++ b/plugins/xastir/xastir
@@ -0,0 +1,117 @@
+#!/bin/bash
+
+## Copyright (C) 2012 Robert Kilian
+##
+## This file is part of the Xastir plugin for Munin.
+##
+## Xastir-Munin 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 3 of the License, or (at your option) any
+## later version.
+##
+## Xastir-Munin 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 Xastir-Munin; see the file COPYING. If not,
+## see .
+##
+## Version 0.1 -- 07.26.12
+##
+
+## Be sure to correctly edit the STATION_CALL, XASTIRDIR, and LOGDIR variables
+##
+## STATION_CALL: The callsign used by Xastir (include suffix if one is in use)
+## XASTIRDIR: The directory where Xastir's data, config, etc files are found. Typically ~/.xastir
+## LOGDIR: Logs are typically stored in ~/.xastir/logs. Ensure that permissions are set appropriately to allow the munin user to read these logs
+
+# Location of active instance of Xastir
+XASTIRDIR="/home/USERNAME/.xastir"
+
+# Grab the station's callsign from Xastir config
+# this currently does not derive the station call correctly when called by the server, but works using munin-run...
+#STATION_CALL=`grep ^STATION_CALLSIGN:.* $XASTIRDIR/config/xastir.cnf | awk -F":" '{print $2}'`
+STATION_CALL=""
+
+# Location of Xastir's logs (this can be a symlink to where Xastir actually writes the logs)
+LOGDIR="/var/log/xastir/logs"
+
+
+case $1 in
+ config)
+ cat <<'EOM'
+
+graph_title Xastir Packet Stats
+graph_vlabel Packets
+graph_category ham_radio
+graph_scale no
+graph_printf %.0lf
+
+igatetonet.label IGate - RF to Net
+igatetonet.type COUNTER
+igatetonet.min 0
+igatetonet.max 500
+igatetonet.LINE1
+
+message.label Message RX
+message.type COUNTER
+message.min 0
+message.max 500
+message.draw LINE1
+
+messagetx.label Message TX
+messagetx.type COUNTER
+messagetx.min 0
+messagetx.max 500
+messagetx.draw LINE1
+
+net.label Net RX
+net.type COUNTER
+net.min 0
+net.max 500
+net.draw LINE1
+
+nettx.label Net TX
+nettx.type COUNTER
+nettx.min 0
+nettx.max 500
+nettx.draw LINE1
+
+tnc.label TNC RX
+tnc.type COUNTER
+tnc.min 0
+tnc.max 500
+tnc.draw LINE1
+
+tnctx.label TNC TX
+tnctx.type COUNTER
+tnctx.min 0
+tnctx.max 500
+tnctx.draw LINE1
+
+EOM
+ exit 0;;
+esac
+
+# Parse logs for various values
+IGATETONET=`cat $LOGDIR/igate.log | grep -e '^IGATE RF' | wc -l`
+MESSAGE=`cat $LOGDIR/message.log | grep -v '^\#' | grep -v ^$STATION_CALL | wc -l`
+MESSAGETX=`cat $LOGDIR/message.log | grep -v '^\#' | grep ^$STATION_CALL | wc -l`
+NET=`cat $LOGDIR/net.log | grep -v '^\#' | grep -v ^$STATION_CALL | wc -l`
+NETTX=`cat $LOGDIR/net.log | grep -v '^\#' | grep ^$STATION_CALL | wc -l`
+TNC=`cat $LOGDIR/tnc.log | grep -v '^\#' | grep -v ^$STATION_CALL | wc -l`
+TNCTX=`cat $LOGDIR/tnc.log | grep -v '^\#' | grep ^$STATION_CALL | wc -l`
+
+# Display values
+echo "igatetonet.value $IGATETONET"
+echo "message.value $MESSAGE"
+echo "messagetx.value $MESSAGETX"
+echo "net.value $NET"
+echo "nettx.value $NETTX"
+echo "tnc.value $TNC"
+echo "tnctx.value $TNCTX"
+
diff --git a/plugins/zfs/zfs_stats_ b/plugins/zfs/zfs_stats_
index 40a4072b..7252dfe0 100755
--- a/plugins/zfs/zfs_stats_
+++ b/plugins/zfs/zfs_stats_
@@ -109,8 +109,10 @@ else
fi
L2_ACCESSES_TOTAL=`echo "$L2_HITS+$L2_MISSES" | $BC`
-L2_HIT_RATIO_PERC=`echo "scale=2 ; (100*$L2_HITS/$L2_ACCESSES_TOTAL)" | $BC`
-L2_MISS_RATIO_PERC=`echo "scale=2 ; (100*$L2_MISSES/$L2_ACCESSES_TOTAL)" | $BC`
+if [ $L2_ACCESSES_TOTAL -gt 0 ]; then
+ L2_HIT_RATIO_PERC=`echo "scale=2 ; (100*$L2_HITS/$L2_ACCESSES_TOTAL)" | $BC`
+ L2_MISS_RATIO_PERC=`echo "scale=2 ; (100*$L2_MISSES/$L2_ACCESSES_TOTAL)" | $BC`
+fi
efficiency() {
if [ "$1" = "config" ]; then
diff --git a/plugins/zfs/zfs_usage_ b/plugins/zfs/zfs_usage_
new file mode 100755
index 00000000..d3700a6f
--- /dev/null
+++ b/plugins/zfs/zfs_usage_
@@ -0,0 +1,285 @@
+#!/usr/local/bin/perl
+# -*- perl
+
+=pod
+
+=head1 NAME
+
+zfs_usage_ - Script to monitor zfs pool usage
+
+=head1 CONFIGURATION
+
+Create one symlink per zpool for exampe zfs_usage_system
+
+=head1 BUGS
+
+=head1 AUTHOR
+
+2012, Claudius Herder
+
+=head1 MAGIC MARKERS
+
+ #%# family=auto
+ #%# capabilities=autoconf suggest
+
+=head1 LICENSE
+
+GPLv2
+
+=cut
+
+use strict;
+use warnings;
+use Munin::Plugin;
+need_multigraph();
+
+my $filesystems;
+my $zpool;
+my $zpoolexec="/sbin/zpool";
+my $zfsexec="/sbin/zfs";
+
+my $properties = {
+ available => "Read-only property that identifies the amount of disk"
+ ." space available to a file system and all its children,"
+ ." assuming no other activity in the pool. Because disk"
+ ." space is shared within a pool, available space can be"
+ ." limited by various factors including physical pool size,"
+ ." quotas, reservations, and other datasets within the"
+ ." pool.",
+
+ quota => "Limits the amount of disk space a file system and its"
+ ." descendents can consume. This property enforces a"
+ ." hard limit on the amount of disk space used, including"
+ ." all space consumed by descendents, such as file systems"
+ ." and snapshots. Setting a quota on a descendent of a file"
+ ." system that already has a quota does not override the"
+ ." ancestor's quota, but rather imposes an additional"
+ ." limit. Quotas cannot be set on volumes, as the volsize"
+ ." property acts as an implicit quota.",
+
+ referenced => "Read-only property that identifies the amount of data"
+ ." accessible by a dataset, which might or might not be"
+ ." shared with other datasets in the pool."
+ ." When a snapshot or clone is created, it initially"
+ ." references the same amount of disk space as the file"
+ ." system or snapshot it was created from, because its"
+ ." contents are identical.",
+
+ refquota => "Sets the amount of disk space that a dataset can"
+ ." consume. This property enforces a hard limit on the"
+ ." amount of space used. This hard limit does not include"
+ ." disk space used by descendents, such as snapshots and"
+ ." clones.",
+
+ refreservation => "Sets the minimum amount of disk space that is"
+ ." guaranteed to a dataset, not including descendents,"
+ ." such as snapshots and clones. When the amount of disk"
+ ." space used is below this value, the dataset is treated as if"
+ ." it were taking up the amount of space specified by"
+ ." refreservation. The refreservation reservation is"
+ ." accounted for in the parent dataset's disk space used,"
+ ." and counts against the parent dataset's quotas and"
+ ." reservations."
+ ." If refreservation is set, a snapshot is only allowed if"
+ ." enough free pool space is available outside of this"
+ ." reservation to accommodate the current number of"
+ ." referenced bytes in the dataset.",
+
+ reservation => "Sets the minimum amount of disk space guaranteed to"
+ ." a file system and its descendents. When the amount of"
+ ." disk space used is below this value, the file system is"
+ ." treated as if it were using the amount of space specified"
+ ." by its reservation. Reservations are accounted for in the"
+ ." parent file system's disk space used, and count against"
+ ." the parent file system's quotas and reservations.",
+
+ type => "Read-only property that identifies the dataset type as"
+ ." filesystem (file system or clone), volume, or"
+ ." snapshot.",
+
+ used => "Read-only property that identifies the amount of disk"
+ ." space consumed by a dataset and all its descendents.",
+
+ usedbychildren => "Read-only property that identifies the amount of disk"
+ ." space that is used by children of this dataset, which"
+ ." would be freed if all the dataset's children were"
+ ." destroyed. The property abbreviation is usedchild.",
+
+ usedbydataset => "Read-only property that identifies the amount of disk"
+ ." space that is used by a dataset itself, which would be"
+ ." freed if the dataset was destroyed, after first destroying"
+ ." any snapshots and removing any refreservation"
+ ." reservations. The property abbreviation is usedds.",
+
+ usedbyrefreservation=> "Read-only property that identifies the amount of disk"
+ ." space that is used by a refreservation set on a dataset,"
+ ." which would be freed if the refreservation was"
+ ." removed.",
+
+ usedbysnapshots => "Read-only property that identifies the amount of disk"
+ ." space that is consumed by snapshots of a dataset. In"
+ ." particular, it is the amount of disk space that would be"
+ ." freed if all of this dataset's snapshots were destroyed."
+ ." Note that this value is not simply the sum of the"
+ ." snapshots' used properties, because space can be"
+ ." shared by multiple snapshots.",
+ volsize => "For volumes, specifies the logical size of the volume.",
+};
+
+my @order = (
+"usedbydataset",
+"usedbysnapshots",
+"usedbyrefreservation",
+"usedbychildren",
+"available",
+"quota",
+"refquota",
+"referenced",
+"reservation",
+"refreservation",
+"used",
+"volsize",
+);
+
+sub do_collect {
+ my $fslist=(`$zfsexec list -H -r -o name $zpool`);
+
+ my @params = join(',',( keys %{$properties} ));
+
+ my $fsget="$zfsexec get -H -p @params";
+
+ foreach my $fsname (split(/\n/,$fslist)) {
+ foreach my $line (split(/\n/, `$fsget $fsname` )) {
+ my ($name, $key, $value, undef ) = (split(/\t/,$line));
+ ($name =~ s/\//_/g);
+ $filesystems->{$name}->{$key}=$value;
+ }
+ }
+}
+
+
+sub do_config_fs {
+ my ($fs) = @_;
+ my $fs_slash = ($fs);
+ ($fs_slash =~ s/_/\//g);
+
+ if ( $fs ne $zpool ) {
+ print "multigraph zfs_usage_$zpool.$fs\n";
+ print "graph_title ZFS usage for $filesystems->{$fs}->{type} $fs_slash\n";
+ print "graph_info This graph shows used bytes of $filesystems->{$fs}->{type} $fs_slash\n";
+ } else {
+ print "multigraph zfs_usage_$zpool\n";
+ print "graph_title ZFS usage for zpool $zpool\n";
+ print "graph_info This graph shows used bytes of zpool $zpool\n";
+ }
+ print "graph_args --base 1024 --lower-limit 0 --rigid\n";
+ print "graph_vlabel bytes \n";
+ print "graph_category zfs\n";
+ print "graph_order @order\n";
+
+ foreach my $key ( keys %{$filesystems->{$fs}}) {
+ if ( $key ne "type" ) {
+ if ( $filesystems->{$fs}->{type} eq "volume" && $key =~ /quota/ ) {
+ }
+ elsif ($filesystems->{$fs}->{type} eq "filesystem" && $key eq "volsize") {
+ }
+ else {
+ print "$key.label $key\n";
+ print "$key.min 0\n";
+ print "$key.type GAUGE\n";
+ print "$key.info $properties->{$key}\n";
+ if ( $key =~ /quota|referenced|^(ref)*reservation|^used$|volsize/ ) {
+ print "$key.draw LINE3\n";
+ }
+ else {
+ print "$key.draw AREASTACK\n";
+ }
+ }
+ }
+ }
+}
+
+
+sub do_fetch_fs {
+ my ($fs) = @_;
+
+ if ( $fs ne $zpool ) {
+ print "multigraph zfs_usage_$zpool.$fs\n";
+ } else {
+ print "multigraph zfs_usage_$zpool\n";
+ }
+
+ foreach my $key ( keys %{$filesystems->{$fs}}) {
+ if ( $key ne "type" ) {
+ if ( $filesystems->{$fs}->{type} eq "volume" && $key =~ /quota/ ) {
+ }
+ elsif ($filesystems->{$fs}->{type} eq "filesystem" && $key eq "volsize") {
+ }
+ else {
+ print "$key.value $filesystems->{$fs}->{$key}\n";
+ }
+ }
+ }
+}
+
+sub do_config {
+ foreach my $fs ( sort keys %{$filesystems}) {
+ do_config_fs($fs);
+ }
+}
+
+sub do_autoconf {
+ if (`which $zpoolexec 2>/dev/null` =~ m{^/}) {
+ print "yes\n";
+ } else {
+ print "no ($zpoolexec could not be found)\n";
+ }
+ exit 0;
+}
+
+
+sub do_suggest {
+ my $poollist=(`zpool list -H -o name`);
+ print "$poollist";
+}
+
+sub do_fetch {
+ foreach my $fs ( sort keys %{$filesystems}) {
+ do_fetch_fs($fs);
+ }
+}
+
+sub do_setpool {
+ if ( $0 =~ /zfs_usage_$/) {
+ die ("Can't run without a symlinked name\n")
+ }
+ elsif ($0 =~ /zfs_usage_(.+)*$/) {
+ $zpool = $1;
+ }
+}
+
+if ($ARGV[0]) {
+ if ($ARGV[0] eq "config") {
+ do_setpool();
+ do_collect();
+ do_config();
+ exit 0;
+ }
+ elsif ($ARGV[0] eq "autoconf") {
+ do_autoconf();
+ exit 0;
+ }
+ elsif ($ARGV[0] eq "suggest") {
+ do_suggest();
+ exit 0;
+ }
+}
+else {
+ do_setpool();
+ do_collect();
+ do_fetch();
+}
+
+exit 0;
+
+__END__
diff --git a/tools/munin-node-c/.gitignore b/tools/munin-node-c/.gitignore
new file mode 100644
index 00000000..91f33c2b
--- /dev/null
+++ b/tools/munin-node-c/.gitignore
@@ -0,0 +1,4 @@
+# output files
+/*.o
+/munin-node-c
+/plugins/*
diff --git a/tools/munin-node-c/Makefile b/tools/munin-node-c/Makefile
new file mode 100644
index 00000000..8081951c
--- /dev/null
+++ b/tools/munin-node-c/Makefile
@@ -0,0 +1,24 @@
+CC=gcc
+CFLAGS=-W -Wall -pedantic -Wextra -g -O2
+OBJS=main.o
+LINKS=
+
+%.o: %.c
+ ${CC} ${CFLAGS} -c $< -o $@
+all: munin-node-c
+
+munin-node-c: ${OBJS}
+ ${CC} ${CFLAGS} $^ -o $@
+clean:
+ rm -f munin-node-c ${OBJS} ${LINKS}
+ rm -Rf plugins
+
+plugins: plugins/.munin-plugins-busybox.installed
+
+plugins/.munin-plugins-busybox.installed:
+ mkdir -p plugins
+ cd ../munin-plugins-busybox && $(MAKE)
+ cd plugins && for i in $$(find ../../munin-plugins-busybox -type l); do ln -s $$i; done
+ touch plugins/.munin-plugins-busybox.installed
+
+.PHONY: all clean plugins
diff --git a/tools/munin-node-c/README b/tools/munin-node-c/README
new file mode 100644
index 00000000..67c1544f
--- /dev/null
+++ b/tools/munin-node-c/README
@@ -0,0 +1,28 @@
+This is a rewrite of munin node in C.
+
+Pro:
+----
+
+The purpose is multiple:
+
+ * reducing resource usage for embedded plateforms, specially when paired
+ with the C rewrite of the core plugins.
+
+ * no need for Perl
+
+ * Everything runs from inetd.
+
+Cons:
+-----
+
+ * You lose flexibility
+
+ It is compiled code, so you have to create binaries. Even one for each
+ architecture.
+
+ * Not all the features are implemented
+
+ - root uid is not supported. All plugins are run with a single user, usually nobody.
+ - no socket is opened. Everything runs from inetd.
+
+GPLv2 - (C) 2013 Steve SCHNEPP
diff --git a/tools/munin-node-c/main.c b/tools/munin-node-c/main.c
new file mode 100644
index 00000000..88a97561
--- /dev/null
+++ b/tools/munin-node-c/main.c
@@ -0,0 +1,145 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+char VERSION[] = "1.0.0";
+
+int verbose = 0;
+
+char* host = "";
+char* plugin_dir = "plugins";
+char* spoolfetch_dir = "";
+
+int main(int argc, char *argv[]) {
+
+ int optch;
+ extern int opterr;
+ int optarg_len;
+
+ char format[] = "vd:h:s:";
+
+ char line[LINE_MAX];
+
+ opterr = 1;
+
+ while ((optch = getopt(argc, argv, format)) != -1)
+ switch (optch) {
+ case 'v':
+ verbose ++;
+ break;
+ case 'd':
+ optarg_len = strlen(optarg);
+ plugin_dir = (char *) malloc(optarg_len + 1);
+ strcpy(plugin_dir, optarg);
+ break;
+ case 'h':
+ optarg_len = strlen(optarg);
+ host = (char *) malloc(optarg_len + 1);
+ strcpy(host, optarg);
+ break;
+ case 's':
+ optarg_len = strlen(optarg);
+ spoolfetch_dir = (char *) malloc(optarg_len + 1);
+ strcpy(spoolfetch_dir, optarg);
+ break;
+ }
+
+ /* get default hostname if not precised */
+ if (! strlen(host)) {
+ host = (char *) malloc(HOST_NAME_MAX + 1);
+ gethostname(host, HOST_NAME_MAX);
+ }
+
+ printf("# munin node at %s\n", host);
+ while (fgets(line, LINE_MAX, stdin) != NULL) {
+ char* cmd;
+ char* arg;
+
+ line[LINE_MAX-1] = '\0';
+
+ cmd = strtok(line, " \t\n");
+ if(cmd == NULL)
+ arg = NULL;
+ else
+ arg = strtok(NULL, " \t\n");
+
+ if (!cmd || strlen(cmd) == 0) {
+ printf("# empty cmd\n");
+ } else if (strcmp(cmd, "version") == 0) {
+ printf("munin c node version: %s\n", VERSION);
+ } else if (strcmp(cmd, "nodes") == 0) {
+ printf("%s\n", host);
+ printf(".\n");
+ } else if (strcmp(cmd, "quit") == 0) {
+ return(0);
+ } else if (strcmp(cmd, "list") == 0) {
+ DIR* dirp = opendir(plugin_dir);
+ struct dirent* dp;
+ while ((dp = readdir(dirp)) != NULL) {
+ char cmdline[LINE_MAX];
+ char* plugin_filename = dp->d_name;;
+
+ if (plugin_filename[0] == '.') {
+ /* No dotted plugin */
+ continue;
+ }
+
+ snprintf(cmdline, LINE_MAX, "%s/%s", plugin_dir, plugin_filename);
+ if (access(cmdline, X_OK) == 0) {
+ printf("%s ", plugin_filename);
+ }
+ }
+ printf("\n");
+ closedir(dirp);
+ } else if (
+ strcmp(cmd, "config") == 0 ||
+ strcmp(cmd, "fetch") == 0
+ ) {
+ char cmdline[LINE_MAX];
+ pid_t pid;
+ if(arg == NULL) {
+ printf("# no plugin given\n");
+ continue;
+ }
+ if(arg[0] == '.' || strchr(arg, '/')) {
+ printf("# invalid plugin character");
+ continue;
+ }
+ snprintf(cmdline, LINE_MAX, "%s/%s", plugin_dir, arg);
+ if (access(cmdline, X_OK) == -1) {
+ printf("# unknown plugin: %s\n", arg);
+ continue;
+ }
+ if(0 == (pid = vfork())) {
+ execl(cmdline, arg, cmd, NULL);
+ /* according to vfork(2) we must use _exit */
+ _exit(1);
+ } else if(pid < 0) {
+ printf("# fork failed\n");
+ continue;
+ } else {
+ waitpid(pid, NULL, 0);
+ }
+ printf(".\n");
+ } else if (strcmp(cmd, "cap") == 0) {
+ printf("cap ");
+ if (strlen(spoolfetch_dir)) {
+ printf("spool ");
+ }
+ printf("\n");
+ } else if (strcmp(cmd, "spoolfetch") == 0) {
+ printf("# not implem yet cmd: %s\n", cmd);
+ } else {
+ printf("# unknown cmd: %s\n", cmd);
+ }
+ }
+
+ return 0;
+}
diff --git a/tools/munin-plugins-busybox/.gitignore b/tools/munin-plugins-busybox/.gitignore
new file mode 100644
index 00000000..5761abcf
--- /dev/null
+++ b/tools/munin-plugins-busybox/.gitignore
@@ -0,0 +1 @@
+*.o
diff --git a/tools/munin-plugins-busybox/Makefile b/tools/munin-plugins-busybox/Makefile
new file mode 100644
index 00000000..74de3046
--- /dev/null
+++ b/tools/munin-plugins-busybox/Makefile
@@ -0,0 +1,18 @@
+CC=gcc
+CFLAGS=-W -Wall -pedantic -Wextra -g -O2
+OBJS=main.o common.o cpu.o entropy.o forks.o fw_packets.o interrupts.o \
+ if_err_.o load.o open_files.o open_inodes.o processes.o swap.o threads.o \
+ uptime.o
+LINKS=cpu entropy forks fw_packets interrupts if_err_eth0 load open_files \
+ open_inodes processes swap threads uptime
+
+%.o:%.c
+ ${CC} ${CFLAGS} -c $< -o $@
+all:munin-plugins-busybox
+ strip -s munin-plugins-busybox
+ for l in ${LINKS}; do test -f $$l || ln -s munin-plugins-busybox $$l; done
+munin-plugins-busybox:${OBJS}
+ ${CC} ${CFLAGS} $^ -o $@
+clean:
+ rm -f munin-plugins-busybox ${OBJS} ${LINKS}
+.PHONY:all clean
diff --git a/tools/munin-plugins-busybox/README b/tools/munin-plugins-busybox/README
new file mode 100644
index 00000000..4f8c56be
--- /dev/null
+++ b/tools/munin-plugins-busybox/README
@@ -0,0 +1,36 @@
+What is this?
+~~~~~~~~~~~~~
+This is a rewrite of commonly used munin plugins in C as a single binary.
+The purpose is reducing resource usage:
+ * disk space: the binary is smaler than the plugins together
+ * more diskspace: it has no dependencies on other programs
+ * less forks: it does not fork internally
+ * faster startup: it doesn't start perl or shell
+ * less memory: just a small C program
+ * less file accesses: one binary for many plugins
+This can be useful for machines with restricted resources like embedded
+machines.
+
+What plugins are included?
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+cpu entropy forks fw_packets interrupts load open_files open_inodes
+processes swap uptime
+
+Disadvantages?
+~~~~~~~~~~~~~~
+You lose flexibility. You can no longer just edit the plugin and if you try
+you have to be very careful not to break it. If you want to deploy this you
+have to create one binary for each architecture.
+
+How to use?
+~~~~~~~~~~~
+After compiling there will be binary munin-plugins-busybox. You can just
+replace symlinks in /etc/munin/plugins/ with symlinks to this binary.
+
+License?
+~~~~~~~~
+(C) 2008 Helmut Grohne
+At your choice GPLv2 or GPLv3 applies to *.c *.h and Makefile
+
+# vim7:spelllang=en
+# vim:textwidth=75
diff --git a/tools/munin-plugins-busybox/common.c b/tools/munin-plugins-busybox/common.c
new file mode 100644
index 00000000..03638b10
--- /dev/null
+++ b/tools/munin-plugins-busybox/common.c
@@ -0,0 +1,74 @@
+#include
+#include
+#include
+#include
+#include
+
+extern char **environ;
+
+int writeyes(void) {
+ puts("yes");
+ return 0;
+}
+
+int autoconf_check_readable(const char *path) {
+ if(0 == access(path, R_OK))
+ return writeyes();
+ else {
+ printf("no (%s is not readable, errno=%d)\n", path, errno);
+ return 0;
+ }
+}
+
+int getenvint(const char *name, int defvalue) {
+ const char *value;
+ value = getenv(name);
+ if(value == NULL)
+ return defvalue;
+ return atoi(value);
+}
+
+const char *getenv_composed(const char *name1, const char *name2) {
+ char **p;
+ size_t len1 = strlen(name1), len2 = strlen(name2);
+ for(p = environ; *p; ++p) {
+ if(0 == strncmp(*p, name1, len1) &&
+ 0 == strncmp(len1 + *p, name2, len2) &&
+ (*p)[len1 + len2] == '=')
+ return len1 + len2 + 1 + *p;
+ }
+ return NULL;
+}
+
+void print_warning(const char *name) {
+ const char *p;
+ p = getenv_composed(name, "_warning");
+ if(p == NULL)
+ p = getenv("warning");
+ if(p == NULL)
+ return;
+
+ printf("%s.warning %s\n", name, p);
+}
+
+void print_critical(const char *name) {
+ const char *p;
+ p = getenv_composed(name, "_critical");
+ if(p == NULL)
+ p = getenv("critical");
+ if(p == NULL)
+ return;
+
+ printf("%s.critial %s\n", name, p);
+}
+
+void print_warncrit(const char *name) {
+ print_warning(name);
+ print_critical(name);
+}
+
+int fail(const char *message) {
+ fputs(message, stderr);
+ fputc('\n', stderr);
+ return 1;
+}
diff --git a/tools/munin-plugins-busybox/common.h b/tools/munin-plugins-busybox/common.h
new file mode 100644
index 00000000..c35cac85
--- /dev/null
+++ b/tools/munin-plugins-busybox/common.h
@@ -0,0 +1,15 @@
+#ifndef COMMON_H
+#define COMMON_H
+
+#define PROC_STAT "/proc/stat"
+
+int writeyes(void);
+int autoconf_check_readable(const char *);
+int getenvint(const char *, int);
+const char *getenv_composed(const char *, const char *);
+void print_warning(const char *);
+void print_critical(const char *);
+void print_warncrit(const char *);
+int fail(const char *);
+
+#endif
diff --git a/tools/munin-plugins-busybox/cpu.c b/tools/munin-plugins-busybox/cpu.c
new file mode 100644
index 00000000..0cfa1d45
--- /dev/null
+++ b/tools/munin-plugins-busybox/cpu.c
@@ -0,0 +1,175 @@
+#include
+#include
+#include
+#include
+#include
+#include "common.h"
+
+#define SYSWARNING 30
+#define SYSCRITICAL 50
+#define USRWARNING 80
+
+/* TODO: port support for env.foo_warning and env.foo_critical from mainline plugin */
+
+int cpu(int argc, char **argv) {
+ FILE *f;
+ char buff[256], *s;
+ int ncpu=0, extinfo=0, scaleto100=0, hz;
+ if(argc > 1) {
+ if(!strcmp(argv[1], "config")) {
+ s = getenv("scaleto100");
+ if(s && !strcmp(s, "yes"))
+ scaleto100=1;
+
+ if(!(f=fopen(PROC_STAT, "r")))
+ return fail("cannot open " PROC_STAT);
+ while(fgets(buff, 256, f)) {
+ if(!strncmp(buff, "cpu", 3)) {
+ if(isdigit(buff[3]))
+ ncpu++;
+ if(buff[3] == ' ') {
+ s = strtok(buff+4, " \t");
+ for(extinfo=0;strtok(NULL, " \t");extinfo++)
+ ;
+ }
+ }
+ }
+ fclose(f);
+
+ if(ncpu < 1 || extinfo < 4)
+ return fail("cannot parse " PROC_STAT);
+
+ puts("graph_title CPU usage");
+ if(extinfo >= 7)
+ puts("graph_order system user nice idle iowait irq softirq");
+ else
+ puts("graph_order system user nice idle");
+ if(scaleto100)
+ puts("graph_args --base 1000 -r --lower-limit 0 --upper-limit 100");
+ else
+ printf("graph_args --base 1000 -r --lower-limit 0 --upper-limit %d\n", 100 * ncpu);
+ puts("graph_vlabel %\n"
+ "graph_scale no\n"
+ "graph_info This graph shows how CPU time is spent.\n"
+ "graph_category system\n"
+ "graph_period second\n"
+ "system.label system\n"
+ "system.draw AREA");
+ printf("system.max %d\n", 100 * ncpu);
+ puts("system.min 0\n"
+ "system.type DERIVE");
+ printf("system.warning %d\n", SYSWARNING * ncpu);
+ printf("system.critical %d\n", SYSCRITICAL * ncpu);
+ puts("system.info CPU time spent by the kernel in system activities\n"
+ "user.label user\n"
+ "user.draw STACK\n"
+ "user.min 0");
+ printf("user.max %d\n", 100 * ncpu);
+ printf("user.warning %d\n", USRWARNING * ncpu);
+ puts("user.type DERIVE\n"
+ "user.info CPU time spent by normal programs and daemons\n"
+ "nice.label nice\n"
+ "nice.draw STACK\n"
+ "nice.min 0");
+ printf("nice.max %d\n", 100 * ncpu);
+ puts("nice.type DERIVE\n"
+ "nice.info CPU time spent by nice(1)d programs\n"
+ "idle.label idle\n"
+ "idle.draw STACK\n"
+ "idle.min 0");
+ printf("idle.max %d\n", 100 * ncpu);
+ puts("idle.type DERIVE\n"
+ "idle.info Idle CPU time");
+ if(scaleto100)
+ printf("system.cdef system,%d,/\n"
+ "user.cdef user,%d,/\n"
+ "nice.cdef nice,%d,/\n"
+ "idle.cdef idle,%d,/\n", ncpu, ncpu, ncpu, ncpu);
+ if(extinfo >= 7) {
+ puts("iowait.label iowait\n"
+ "iowait.draw STACK\n"
+ "iowait.min 0");
+ printf("iowait.max %d\n", 100 * ncpu);
+ puts("iowait.type DERIVE\n"
+ "iowait.info CPU time spent waiting for I/O operations to finish\n"
+ "irq.label irq\n"
+ "irq.draw STACK\n"
+ "irq.min 0");
+ printf("irq.max %d\n", 100 * ncpu);
+ puts("irq.type DERIVE\n"
+ "irq.info CPU time spent handling interrupts\n"
+ "softirq.label softirq\n"
+ "softirq.draw STACK\n"
+ "softirq.min 0");
+ printf("softirq.max %d\n", 100 * ncpu);
+ puts("softirq.type DERIVE\n"
+ "softirq.info CPU time spent handling \"batched\" interrupts");
+ if(scaleto100)
+ printf("iowait.cdef iowait,%d,/\n"
+ "irq.cdef irq,%d,/\n"
+ "softirq.cdef softirq,%d,/\n", ncpu, ncpu, ncpu);
+ }
+ if(extinfo >= 8) {
+ puts("steal.label steal\n"
+ "steal.draw STACK\n"
+ "steal.min 0");
+ printf("steal.max %d\n", 100 * ncpu);
+ puts("steal.type DERIVE\n"
+ "steal.info The time that a virtual CPU had runnable tasks, but the virtual CPU itself was not running");
+ if(scaleto100)
+ printf("steal.cdef steal,%d,/\n", ncpu);
+ }
+ if(extinfo >= 9) {
+ puts("guest.label guest\n"
+ "guest.draw STACK\n"
+ "guest.min 0");
+ printf("guest.max %d\n", 100 * ncpu);
+ puts("guest.type DERIVE\n"
+ "guest.info The time spent running a virtual CPU for guest operating systems under the control of the Linux kernel.");
+ if(scaleto100)
+ printf("guest.cdef guest,%d,/\n", ncpu);
+ }
+ return 0;
+ }
+ if(!strcmp(argv[1], "autoconf"))
+ return autoconf_check_readable(PROC_STAT);
+ }
+ if(!(f=fopen(PROC_STAT, "r")))
+ return fail("cannot open " PROC_STAT);
+ while(fgets(buff, 256, f)) {
+ hz = getenvint("HZ", 100);
+ if(!strncmp(buff, "cpu ", 4)) {
+ fclose(f);
+ if(!(s = strtok(buff+4, " \t")))
+ break;
+ printf("user.value %ld\n", atol(s) * 100 / hz);
+ if(!(s = strtok(NULL, " \t")))
+ break;
+ printf("nice.value %ld\n", atol(s) * 100 / hz);
+ if(!(s = strtok(NULL, " \t")))
+ break;
+ printf("system.value %ld\n", atol(s) * 100 / hz);
+ if(!(s = strtok(NULL, " \t")))
+ break;
+ printf("idle.value %ld\n", atol(s) * 100 / hz);
+ if(!(s = strtok(NULL, " \t")))
+ return 0;
+ printf("iowait.value %ld\n", atol(s) * 100 / hz);
+ if(!(s = strtok(NULL, " \t")))
+ return 0;
+ printf("irq.value %ld\n", atol(s) * 100 / hz);
+ if(!(s = strtok(NULL, " \t")))
+ return 0;
+ printf("softirq.value %ld\n", atol(s) * 100 / hz);
+ if(!(s = strtok(NULL, " \t")))
+ return 0;
+ printf("steal.value %ld\n", atol(s) * 100 / hz);
+ if(!(s = strtok(NULL, " \t")))
+ return 0;
+ printf("guest.value %ld\n", atol(s) * 100 / hz);
+ return 0;
+ }
+ }
+ fclose(f);
+ return fail("no cpu line found in " PROC_STAT);
+}
diff --git a/tools/munin-plugins-busybox/entropy.c b/tools/munin-plugins-busybox/entropy.c
new file mode 100644
index 00000000..2f7d998f
--- /dev/null
+++ b/tools/munin-plugins-busybox/entropy.c
@@ -0,0 +1,36 @@
+#include
+#include
+#include
+#include "common.h"
+
+#define ENTROPY_AVAIL "/proc/sys/kernel/random/entropy_avail"
+
+int entropy(int argc, char **argv) {
+ FILE *f;
+ int entropy;
+ if(argc > 1) {
+ if(!strcmp(argv[1], "config")) {
+ puts("graph_title Available entropy\n"
+ "graph_args --base 1000 -l 0\n"
+ "graph_vlabel entropy (bytes)\n"
+ "graph_scale no\n"
+ "graph_category system\n"
+ "graph_info This graph shows the amount of entropy available in the system.\n"
+ "entropy.label entropy\n"
+ "entropy.info The number of random bytes available. This is typically used by cryptographic applications.");
+ print_warncrit("entropy");
+ return 0;
+ }
+ if(!strcmp(argv[1], "autoconf"))
+ return autoconf_check_readable(ENTROPY_AVAIL);
+ }
+ if(!(f=fopen(ENTROPY_AVAIL, "r")))
+ return fail("cannot open " ENTROPY_AVAIL);
+ if(1 != fscanf(f, "%d", &entropy)) {
+ fclose(f);
+ return fail("cannot read from " ENTROPY_AVAIL);
+ }
+ fclose(f);
+ printf("entropy.value %d\n", entropy);
+ return 0;
+}
diff --git a/tools/munin-plugins-busybox/forks.c b/tools/munin-plugins-busybox/forks.c
new file mode 100644
index 00000000..fbbc5e59
--- /dev/null
+++ b/tools/munin-plugins-busybox/forks.c
@@ -0,0 +1,38 @@
+#include
+#include
+#include
+#include "common.h"
+
+int forks(int argc, char **argv) {
+ FILE *f;
+ char buff[256];
+ if(argc > 1) {
+ if(!strcmp(argv[1], "config")) {
+ puts("graph_title Fork rate\n"
+ "graph_args --base 1000 -l 0 \n"
+ "graph_vlabel forks / ${graph_period}\n"
+ "graph_category processes\n"
+ "graph_info This graph shows the forking rate (new processes started).\n"
+ "forks.label forks\n"
+ "forks.type DERIVE\n"
+ "forks.min 0\n"
+ "forks.max 100000\n"
+ "forks.info The number of forks per second.");
+ print_warncrit("forks");
+ return 0;
+ }
+ if(!strcmp(argv[1], "autoconf"))
+ return autoconf_check_readable(PROC_STAT);
+ }
+ if(!(f=fopen(PROC_STAT, "r")))
+ return fail("cannot open " PROC_STAT);
+ while(fgets(buff, 256, f)) {
+ if(!strncmp(buff, "processes ", 10)) {
+ fclose(f);
+ printf("forks.value %s", buff+10);
+ return 0;
+ }
+ }
+ fclose(f);
+ return fail("no processes line found in " PROC_STAT);
+}
diff --git a/tools/munin-plugins-busybox/fw_packets.c b/tools/munin-plugins-busybox/fw_packets.c
new file mode 100644
index 00000000..1bb9f0f0
--- /dev/null
+++ b/tools/munin-plugins-busybox/fw_packets.c
@@ -0,0 +1,55 @@
+#include
+#include
+#include
+#include
+#include "common.h"
+
+#define PROC_NET_SNMP "/proc/net/snmp"
+
+int fw_packets(int argc, char **argv) {
+ FILE *f;
+ char buff[1024], *s;
+ if(argc > 1) {
+ if(!strcmp(argv[1], "config")) {
+ puts("graph_title Firewall Throughput\n"
+ "graph_args --base 1000 -l 0\n"
+ "graph_vlabel Packets/${graph_period}\n"
+ "graph_category network\n"
+ "received.label Received\n"
+ "received.draw AREA\n"
+ "received.type DERIVE\n"
+ "received.min 0\n"
+ "forwarded.label Forwarded\n"
+ "forwarded.draw LINE2\n"
+ "forwarded.type DERIVE\n"
+ "forwarded.min 0");
+ return 0;
+ }
+ if(!strcmp(argv[1], "autoconf"))
+ return autoconf_check_readable(PROC_NET_SNMP);
+ }
+ if(!(f=fopen(PROC_NET_SNMP, "r")))
+ return fail("cannot open " PROC_NET_SNMP);
+ while(fgets(buff, 1024, f)) {
+ if(!strncmp(buff, "Ip: ", 4) && isdigit(buff[4])) {
+ fclose(f);
+ if(!(s = strtok(buff+4, " \t")))
+ break;
+ if(!(s = strtok(NULL, " \t")))
+ break;
+ if(!(s = strtok(NULL, " \t")))
+ break;
+ printf("received.value %s\n", s);
+ if(!(s = strtok(NULL, " \t")))
+ break;
+ if(!(s = strtok(NULL, " \t")))
+ break;
+ if(!(s = strtok(NULL, " \t")))
+ break;
+ printf("forwarded.value %s\n", s);
+ return 0;
+ }
+ }
+ fclose(f);
+ return fail("no ip line found in " PROC_NET_SNMP);
+}
diff --git a/tools/munin-plugins-busybox/if_err_.c b/tools/munin-plugins-busybox/if_err_.c
new file mode 100644
index 00000000..8858a4a5
--- /dev/null
+++ b/tools/munin-plugins-busybox/if_err_.c
@@ -0,0 +1,118 @@
+#include
+#include
+#include
+#include
+#include
+#include "common.h"
+
+#define PROC_NET_DEV "/proc/net/dev"
+
+int if_err_(int argc, char **argv) {
+ char *interface;
+ size_t interface_len;
+ FILE *f;
+ char buff[256], *s;
+ int i;
+
+ interface = basename(argv[0]);
+ if(strncmp(interface, "if_err_", 7) != 0)
+ return fail("if_err_ invoked with invalid basename");
+ interface += 7;
+ interface_len = strlen(interface);
+
+ if(argc > 1) {
+ if(!strcmp(argv[1], "autoconf"))
+ return autoconf_check_readable(PROC_NET_DEV);
+ if(!strcmp(argv[1], "suggest")) {
+ if(NULL == (f = fopen(PROC_NET_DEV, "r")))
+ return 1;
+ while(fgets(buff, 256, f)) {
+ for(s=buff;*s == ' ';++s)
+ ;
+ i = 0;
+ if(!strncmp(s, "lo:", 3))
+ continue;
+ if(!strncmp(s, "sit", 3)) {
+ for(i=3; isdigit(s[i]); ++i)
+ ;
+ if(s[i] == ':')
+ continue;
+ }
+ while(s[i] != ':' && s[i] != '\0')
+ ++i;
+ if(s[i] != ':')
+ continue; /* a header line */
+ s[i] = '\0';
+ puts(s);
+ }
+ fclose(f);
+ return 0;
+ }
+ if(!strcmp(argv[1], "config")) {
+ puts("graph_order rcvd trans");
+ printf("graph_title %s errors\n", interface);
+ puts("graph_args --base 1000\n"
+ "graph_vlabel packets in (-) / out (+) per "
+ "${graph_period}\n"
+ "graph_category network");
+ printf("graph_info This graph shows the amount of "
+ "errors on the %s network interface.\n",
+ interface);
+ puts("rcvd.label packets\n"
+ "rcvd.type COUNTER\n"
+ "rcvd.graph no\n"
+ "rcvd.warning 1\n"
+ "trans.label packets\n"
+ "trans.type COUNTER\n"
+ "trans.negative rcvd\n"
+ "trans.warning 1");
+ print_warncrit("rcvd");
+ print_warncrit("trans");
+ return 0;
+ }
+ }
+ if(NULL == (f = fopen(PROC_NET_DEV, "r")))
+ return 1;
+ while(fgets(buff, 256, f)) {
+ for(s=buff;*s == ' ';++s)
+ ;
+ if(0 != strncmp(s, interface, interface_len))
+ continue;
+ s += interface_len;
+ if(*s != ':')
+ continue;
+ ++s;
+
+ while(*s == ' ')
+ ++s;
+
+ for(i=1;i<3;++i) {
+ while(isdigit(*s))
+ ++s;
+ while(isspace(*s))
+ ++s;
+ }
+ for(i=0;isdigit(s[i]);++i)
+ ;
+ printf("rcvd.value ");
+ fwrite(s, 1, i, stdout);
+ putchar('\n');
+ s += i;
+ while(isspace(*s))
+ ++s;
+
+ for(i=4;i<11;++i) {
+ while(isdigit(*s))
+ ++s;
+ while(isspace(*s))
+ ++s;
+ }
+ for(i=0;isdigit(s[i]);++i)
+ ;
+ printf("trans.value ");
+ fwrite(s, 1, i, stdout);
+ putchar('\n');
+ }
+ fclose(f);
+ return 0;
+}
diff --git a/tools/munin-plugins-busybox/interrupts.c b/tools/munin-plugins-busybox/interrupts.c
new file mode 100644
index 00000000..7fcc6094
--- /dev/null
+++ b/tools/munin-plugins-busybox/interrupts.c
@@ -0,0 +1,46 @@
+#include
+#include
+#include
+#include "common.h"
+
+int interrupts(int argc, char **argv) {
+ FILE *f;
+ char buff[256];
+ if(argc > 1) {
+ if(!strcmp(argv[1], "config")) {
+ puts("graph_title Interrupts and context switches\n"
+ "graph_args --base 1000 -l 0\n"
+ "graph_vlabel interrupts & ctx switches / ${graph_period}\n"
+ "graph_category system\n"
+ "graph_info This graph shows the number of interrupts and context switches on the system. These are typically high on a busy system.\n"
+ "intr.info Interrupts are events that alter sequence of instructions executed by a processor. They can come from either hardware (exceptions, NMI, IRQ) or software.");
+ puts("ctx.info A context switch occurs when a multitasking operatings system suspends the currently running process, and starts executing another.\n"
+ "intr.label interrupts\n"
+ "ctx.label context switches\n"
+ "intr.type DERIVE\n"
+ "ctx.type DERIVE\n"
+ "intr.max 100000\n"
+ "ctx.max 100000\n"
+ "intr.min 0\n"
+ "ctx.min 0");
+ print_warncrit("intr");
+ print_warncrit("ctx");
+ return 0;
+ }
+ if(!strcmp(argv[1], "autoconf"))
+ return autoconf_check_readable(PROC_STAT);
+ }
+ if(!(f=fopen(PROC_STAT, "r")))
+ return fail("cannot open " PROC_STAT);
+ while(fgets(buff, 256, f)) {
+ if(!strncmp(buff, "intr ", 5)) {
+ buff[5 + strcspn(buff + 5, " \t\n")] = '\0';
+ printf("intr.value %s\n", buff+5);
+ } else if(!strncmp(buff, "ctxt ", 5)) {
+ buff[5 + strcspn(buff + 5, " \t\n")] = '\0';
+ printf("ctx.value %s\n", buff+5);
+ }
+ }
+ fclose(f);
+ return 0;
+}
diff --git a/tools/munin-plugins-busybox/load.c b/tools/munin-plugins-busybox/load.c
new file mode 100644
index 00000000..3b608ca2
--- /dev/null
+++ b/tools/munin-plugins-busybox/load.c
@@ -0,0 +1,36 @@
+#include
+#include
+#include
+#include "common.h"
+
+#define PROC_LOADAVG "/proc/loadavg"
+
+int load(int argc, char **argv) {
+ FILE *f;
+ float val;
+ if(argc > 1) {
+ if(!strcmp(argv[1], "config")) {
+ puts("graph_title Load average\n"
+ "graph_args --base 1000 -l 0\n"
+ "graph_vlabel load\n"
+ "graph_scale no\n"
+ "graph_category system\n"
+ "load.label load");
+ print_warncrit("load");
+ puts("graph_info The load average of the machine describes how many processes are in the run-queue (scheduled to run \"immediately\").\n"
+ "load.info 5 minute load average");
+ return 0;
+ }
+ if(!strcmp(argv[1], "autoconf"))
+ return writeyes();
+ }
+ if(!(f=fopen(PROC_LOADAVG, "r")))
+ return fail("cannot open " PROC_LOADAVG);
+ if(1 != fscanf(f, "%*f %f", &val)) {
+ fclose(f);
+ return fail("cannot read from " PROC_LOADAVG);
+ }
+ fclose(f);
+ printf("load.value %.2f\n", val);
+ return 0;
+}
diff --git a/tools/munin-plugins-busybox/main.c b/tools/munin-plugins-busybox/main.c
new file mode 100644
index 00000000..205731b2
--- /dev/null
+++ b/tools/munin-plugins-busybox/main.c
@@ -0,0 +1,86 @@
+#include
+#include
+#include
+#include "common.h"
+
+int cpu(int argc, char **argv);
+int entropy(int argc, char **argv);
+int forks(int argc, char **argv);
+int fw_packets(int argc, char **argv);
+int if_err_(int argc, char **argv);
+int interrupts(int argc, char **argv);
+int load(int argc, char **argv);
+int open_files(int argc, char **argv);
+int open_inodes(int argc, char **argv);
+int processes(int argc, char **argv);
+int swap(int argc, char **argv);
+int threads(int argc, char **argv);
+int uptime(int argc, char **argv);
+
+int busybox(int argc, char **argv) {
+ if(argc < 2)
+ return fail("missing parameter");
+ if(0 != strcmp(argv[1], "listplugins"))
+ return fail("unknown parameter");
+ puts("cpu\nentropy\nforks\nfw_packets\ninterrupts\nload\n"
+ "open_files\nopen_inodes\nswap\nthreads\nuptime");
+ return 0;
+}
+
+int main(int argc, char **argv) {
+ char *progname;
+ progname = basename(argv[0]);
+ switch(*progname) {
+ case 'c':
+ if(!strcmp(progname, "cpu"))
+ return cpu(argc, argv);
+ break;
+ case 'e':
+ if(!strcmp(progname, "entropy"))
+ return entropy(argc, argv);
+ break;
+ case 'f':
+ if(!strcmp(progname, "forks"))
+ return forks(argc, argv);
+ if(!strcmp(progname, "fw_packets"))
+ return fw_packets(argc, argv);
+ break;
+ case 'i':
+ if(!strcmp(progname, "interrupts"))
+ return interrupts(argc, argv);
+ if(!strncmp(progname, "if_err_", 6))
+ return if_err_(argc, argv);
+ break;
+ case 'l':
+ if(!strcmp(progname, "load"))
+ return load(argc, argv);
+ break;
+ case 'm':
+ if(!strcmp(progname, "munin-plugins-busybox"))
+ return busybox(argc, argv);
+ break;
+ case 'o':
+ if(!strcmp(progname, "open_files"))
+ return open_files(argc, argv);
+ if(!strcmp(progname, "open_inodes"))
+ return open_inodes(argc, argv);
+ break;
+ case 'p':
+ if(!strcmp(progname, "processes"))
+ return processes(argc, argv);
+ break;
+ case 's':
+ if(!strcmp(progname, "swap"))
+ return swap(argc, argv);
+ break;
+ case 't':
+ if(!strcmp(progname, "threads"))
+ return threads(argc, argv);
+ break;
+ case 'u':
+ if(!strcmp(progname, "uptime"))
+ return uptime(argc, argv);
+ break;
+ }
+ return fail("unknown basename");
+}
diff --git a/tools/munin-plugins-busybox/open_files.c b/tools/munin-plugins-busybox/open_files.c
new file mode 100644
index 00000000..f83a9f6c
--- /dev/null
+++ b/tools/munin-plugins-busybox/open_files.c
@@ -0,0 +1,49 @@
+#include
+#include
+#include
+#include "common.h"
+
+#define FS_FILE_NR "/proc/sys/fs/file-nr"
+
+/* TODO: support env.warning and friends after the upstream plugin is fixed */
+
+int open_files(int argc, char **argv) {
+ FILE *f;
+ int alloc, freeh, avail;
+ if(argc > 1) {
+ if(!strcmp(argv[1], "config")) {
+ if(!(f=fopen(FS_FILE_NR, "r")))
+ return fail("cannot open " FS_FILE_NR);
+ if(1 != fscanf(f, "%*d %*d %d", &avail)) {
+ fclose(f);
+ return fail("cannot read from " FS_FILE_NR);
+ }
+ fclose(f);
+ puts("graph_title File table usage\n"
+ "graph_args --base 1000 -l 0\n"
+ "graph_vlabel number of open files\n"
+ "graph_category system\n"
+ "graph_info This graph monitors the Linux open files table.\n"
+ "used.label open files\n"
+ "used.info The number of currently open files.\n"
+ "max.label max open files\n"
+ "max.info The maximum supported number of open "
+ "files. Tune by modifying " FS_FILE_NR
+ ".");
+ printf("used.warning %d\nused.critical %d\n",
+ (int)(avail*0.92), (int)(avail*0.98));
+ return 0;
+ }
+ if(!strcmp(argv[1], "autoconf"))
+ return autoconf_check_readable(FS_FILE_NR);
+ }
+ if(!(f=fopen(FS_FILE_NR, "r")))
+ return fail("cannot open " FS_FILE_NR);
+ if(3 != fscanf(f, "%d %d %d", &alloc, &freeh, &avail)) {
+ fclose(f);
+ return fail("cannot read from " FS_FILE_NR);
+ }
+ fclose(f);
+ printf("used.value %d\nmax.value %d\n", alloc-freeh, avail);
+ return 0;
+}
diff --git a/tools/munin-plugins-busybox/open_inodes.c b/tools/munin-plugins-busybox/open_inodes.c
new file mode 100644
index 00000000..1c33e4de
--- /dev/null
+++ b/tools/munin-plugins-busybox/open_inodes.c
@@ -0,0 +1,38 @@
+#include
+#include
+#include
+#include "common.h"
+
+#define FS_INODE_NR "/proc/sys/fs/inode-nr"
+
+int open_inodes(int argc, char **argv) {
+ FILE *f;
+ int nr, freen;
+ if(argc > 1) {
+ if(!strcmp(argv[1], "config")) {
+ puts("graph_title Inode table usage\n"
+ "graph_args --base 1000 -l 0\n"
+ "graph_vlabel number of open inodes\n"
+ "graph_category system\n"
+ "graph_info This graph monitors the Linux open inode table.\n"
+ "used.label open inodes\n"
+ "used.info The number of currently open inodes.\n"
+ "max.label inode table size\n"
+ "max.info The size of the system inode table. This is dynamically adjusted by the kernel.");
+ print_warncrit("used");
+ print_warncrit("max");
+ return 0;
+ }
+ if(!strcmp(argv[1], "autoconf"))
+ return autoconf_check_readable(FS_INODE_NR);
+ }
+ if(!(f=fopen(FS_INODE_NR, "r")))
+ return fail("cannot open " FS_INODE_NR);
+ if(2 != fscanf(f, "%d %d", &nr, &freen)) {
+ fclose(f);
+ return fail("cannot read from " FS_INODE_NR);
+ }
+ fclose(f);
+ printf("used.value %d\nmax.value %d\n", nr-freen, nr);
+ return 0;
+}
diff --git a/tools/munin-plugins-busybox/processes.c b/tools/munin-plugins-busybox/processes.c
new file mode 100644
index 00000000..8eb0b72e
--- /dev/null
+++ b/tools/munin-plugins-busybox/processes.c
@@ -0,0 +1,42 @@
+#include
+#include
+#include
+#include
+#include
+#include "common.h"
+
+/* TODO: The upstream plugin does way more nowawdays. */
+
+int processes(int argc, char **argv) {
+ DIR *d;
+ struct dirent *e;
+ char *s;
+ int n=0;
+ if(argc > 1) {
+ if(!strcmp(argv[1], "config")) {
+ puts("graph_title Number of Processes\n"
+ "graph_args --base 1000 -l 0 \n"
+ "graph_vlabel number of processes\n"
+ "graph_category processes\n"
+ "graph_info This graph shows the number of processes in the system.\n"
+ "processes.label processes\n"
+ "processes.draw LINE2\n"
+ "processes.info The current number of processes.");
+ return 0;
+ }
+ if(!strcmp(argv[1], "autoconf"))
+ return writeyes();
+ }
+ if(!(d = opendir("/proc")))
+ return fail("cannot open /proc");
+ while((e = readdir(d))) {
+ for(s=e->d_name;*s;++s)
+ if(!isdigit(*s))
+ break;
+ if(!*s)
+ ++n;
+ }
+ closedir(d);
+ printf("processes.value %d\n", n);
+ return 0;
+}
diff --git a/tools/munin-plugins-busybox/swap.c b/tools/munin-plugins-busybox/swap.c
new file mode 100644
index 00000000..2c46fd49
--- /dev/null
+++ b/tools/munin-plugins-busybox/swap.c
@@ -0,0 +1,66 @@
+#include
+#include
+#include
+#include "common.h"
+
+int swap(int argc, char **argv) {
+ FILE *f;
+ char buff[256];
+ int in, out;
+ if(argc > 1) {
+ if(!strcmp(argv[1], "config")) {
+ puts("graph_title Swap in/out\n"
+ "graph_args -l 0 --base 1000\n"
+ "graph_vlabel pages per ${graph_period} in (-) / out (+)\n"
+ "graph_category system\n"
+ "swap_in.label swap\n"
+ "swap_in.type DERIVE\n"
+ "swap_in.max 100000\n"
+ "swap_in.min 0\n"
+ "swap_in.graph no\n"
+ "swap_out.label swap\n"
+ "swap_out.type DERIVE\n"
+ "swap_out.max 100000\n"
+ "swap_out.min 0\n"
+ "swap_out.negative swap_in");
+ print_warncrit("swap_in");
+ print_warncrit("swap_out");
+ return 0;
+ }
+ if(!strcmp(argv[1], "autoconf"))
+ return autoconf_check_readable(PROC_STAT);
+ }
+ if(!access("/proc/vmstat", F_OK)) {
+ in=out=0;
+ if(!(f=fopen("/proc/vmstat", "r")))
+ return fail("cannot open /proc/vmstat");
+ while(fgets(buff, 256, f)) {
+ if(!in && !strncmp(buff, "pswpin ", 7)) {
+ ++in;
+ printf("swap_in.value %s", buff+7);
+ }
+ else if(!out && !strncmp(buff, "pswpout ", 8)) {
+ ++out;
+ printf("swap_out.value %s", buff+8);
+ }
+ }
+ fclose(f);
+ if(!(in*out))
+ return fail("no usable data on /proc/vmstat");
+ return 0;
+ } else {
+ if(!(f=fopen(PROC_STAT, "r")))
+ return fail("cannot open " PROC_STAT);
+ while(fgets(buff, 256, f)) {
+ if(!strncmp(buff, "swap ", 5)) {
+ fclose(f);
+ if(2 != sscanf(buff+5, "%d %d", &in, &out))
+ return fail("bad data on " PROC_STAT);
+ printf("swap_in.value %d\nswap_out.value %d\n", in, out);
+ return 0;
+ }
+ }
+ fclose(f);
+ return fail("no swap line found in " PROC_STAT);
+ }
+}
diff --git a/tools/munin-plugins-busybox/threads.c b/tools/munin-plugins-busybox/threads.c
new file mode 100644
index 00000000..c3389e60
--- /dev/null
+++ b/tools/munin-plugins-busybox/threads.c
@@ -0,0 +1,70 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include "common.h"
+
+int threads(int argc, char **argv) {
+ FILE *f;
+ char buff[256];
+ const char *s;
+ int i, sum;
+ DIR *d;
+ struct dirent *e;
+
+ if(argc > 1) {
+ if(!strcmp(argv[1], "autoconf")) {
+ i = getpid();
+ sprintf(buff, "/proc/%d/status", i);
+ if(NULL == (f = fopen(buff, "r")))
+ return fail("failed to open /proc/$$/status");
+ while(fgets(buff, 256, f))
+ if(!strncmp(buff, "Threads:", 8)) {
+ fclose(f);
+ return writeyes();
+ }
+ fclose(f);
+ puts("no");
+ return 0;
+ }
+ if(!strcmp(argv[1], "config")) {
+ puts("graph_title Number of threads\n"
+ "graph_vlabel number of threads\n"
+ "graph_category processes\n"
+ "graph_info This graph shows the number of threads.\n"
+ "threads.label threads\n"
+ "threads.info The current number of threads.");
+ return 0;
+ }
+ }
+ if(NULL == (d = opendir("/proc")))
+ return fail("cannot open /proc");
+ sum = 0;
+ while((e = readdir(d))) {
+ for(s=e->d_name;*s;++s)
+ if(!isdigit(*s))
+ break;
+ if(*s) /* non-digit found */
+ continue;
+ snprintf(buff, 256, "/proc/%s/status", e->d_name);
+ if(!(f = fopen(buff, "r")))
+ continue; /* process has vanished */
+ while(fgets(buff, 256, f)) {
+ if(strncmp(buff, "Threads:", 8))
+ continue;
+ if(1 != sscanf(buff+8, "%d", &i)) {
+ fclose(f);
+ closedir(d);
+ return fail("failed to parse "
+ "/proc/somepid/status");
+ }
+ sum += i;
+ }
+ fclose(f);
+ }
+ closedir(d);
+ printf("threads.value %d\n", sum);
+ return 0;
+}
diff --git a/tools/munin-plugins-busybox/uptime.c b/tools/munin-plugins-busybox/uptime.c
new file mode 100644
index 00000000..6270acb6
--- /dev/null
+++ b/tools/munin-plugins-busybox/uptime.c
@@ -0,0 +1,30 @@
+#include
+#include
+#include "common.h"
+
+int uptime(int argc, char **argv) {
+ FILE *f;
+ float uptime;
+ if(argc > 1) {
+ if(!strcmp(argv[1], "config")) {
+ puts("graph_title Uptime\n"
+ "graph_args --base 1000 -l 0 \n"
+ "graph_vlabel uptime in days\n"
+ "uptime.label uptime\n"
+ "uptime.draw AREA");
+ print_warncrit("uptime");
+ return 0;
+ }
+ if(!strcmp(argv[1], "autoconf"))
+ return writeyes();
+ }
+ if(!(f=fopen("/proc/uptime", "r")))
+ return fail("cannot open /proc/uptime");
+ if(1 != fscanf(f, "%f", &uptime)) {
+ fclose(f);
+ return fail("cannot read from /proc/uptime");
+ }
+ fclose(f);
+ printf("uptime.value %.2f\n", uptime/86400);
+ return 0;
+}