diff --git a/plugins/other/srcds_fps b/plugins/other/srcds_fps new file mode 100755 index 00000000..99db64f9 --- /dev/null +++ b/plugins/other/srcds_fps @@ -0,0 +1,148 @@ +#!/usr/bin/perl +# +# 2007-06-26 +# Written by Ghost +# +# 2008-04-16 +# Update: Wildcard version +# +# 2008-11-12 +# Update: Perl RCON system +# +# Configuration variables +# +# srcdspass - RCON password +# srcdssamples - Number of samples to take (optional, default: 5) +# srcdsfpsmax - Maximum FPS on the game server (optional, default: 1000) +# +# Magic markers - optional - used by installation scripts and +# munin-config: +# +#%# family=contrib +#%# capabilities=autoconf + +use strict; + +# Set library path correctly +use File::Basename; +if (-l $0) { + push(@INC, dirname(readlink($0))); +} +push(@INC, dirname($0)); + +# Load Rcon module or exit with failuer message +if (!eval "require Rcon") { + print "Failed to load Rcon module. "; + print "Make sure Rcon.pm is copied to Munin plugins directory.\n"; + exit 1; +} + +# Parse hostname and port from the plugin filename +my ($HOST, $PORT) = $0 =~ m/.*_([^:]+)_(\d+)$/; +if (!defined($HOST) || !defined($PORT)) { + print "Could not parse server address from filename.\n"; + exit 1; +} + +# Load config variables or use default values +my $PASS = $ENV{srcdspass} || ""; +my $SAMPLES = $ENV{srcdssamples} || 5; +my $MAX = $ENV{srcdsfpsmax} || 1000; + +# Print config or do plugin test if asked +my $arg = shift(); +if ($arg eq 'config') { + print_config(); +} elsif ($arg eq 'autoconf') { + test_service(); +} + + +# +# Main program starts here +# + +my $fps_avg = 0; +my @fps_samples; + +my $sock = Rcon::sock_connect($HOST, $PORT); +if (!$sock) { + print "Could not open socket to $HOST:$PORT.\n"; + exit 1; +} +if (!Rcon::rcon_auth($sock, $PASS)) { + print "Could not authenticate.\n"; + exit 1; +} + +for (my $i = 0; $i < $SAMPLES; $i++) { + my $reply = Rcon::rcon_command($sock, "stats"); + if (!defined($reply)) { + print "Did not receive reply from server (sample $i).\n"; + next; + } + my @reply = split(/\n/, $reply); + + foreach my $statline (@reply) { + next if ($statline !~ m/\s*[\w+\.]+\s+[\w+\.]+\s+[\w+\.]+\s+\d+\s+\d+\s+[\w+\.]+\s+\d+/); + my ($cpu, $in, $out, $uptime, $users, $fps, $players) = ($statline =~ m/^\s*([\w+\.]+)\s+([\w+\.]+)\s+([\w+\.]+)\s+(\d+)\s+(\d+)\s+([\w+\.]+)\s+(\d+)/); + + if (defined($fps)) { + push(@fps_samples, $fps); + } + } + select(undef, undef, undef, 0.2); # Wait moment before next sample +} + +# MEAN +if (@fps_samples) { + foreach (@fps_samples) { + $fps_avg += int($_); + } + $fps_avg /= ($#fps_samples+1); + $fps_avg = int($fps_avg); + print "fps.value $fps_avg\n"; +} + +# MEDIAN +#if (@fps_samples) { +# @fps_samples = sort {$a <=> $b} @fps_samples; +# my $median = int($#fps_samples / 2 + 0.5); +# $fps_avg = $fps_samples[$median]; +#} + + +sub print_config { + print("graph_title Server FPS at $HOST:$PORT\n", + "graph_args --base 1000\n", + "graph_vlabel FPS\n", + "graph_category SourceDS\n", + "graph_info The number of frames per second generated by Source game server, such as HL2, CS:S and DoD:S.\n"); + + print ("fps.label FPS\n", + "fps.min 0\n", + "fps.max $MAX\n", + "fps.type GAUGE\n"); + + exit 0; +} + + +sub test_service { + my $sock = Rcon::sock_connect($HOST, $PORT); + if (!$sock) { + print "no (could not open socket to $HOST:$PORT)\n"; + exit 1; + } + if (!Rcon::rcon_auth($sock, $PASS)) { + print "no (could not authenticate)\n"; + exit 1; + } + if (!defined(Rcon::rcon_command($sock, "stats"))) { + print "no (did not receive reply from server)\n"; + exit 1; + } + + print "yes\n"; + exit 0; +}