#!/usr/bin/perl -w

use strict;

use Munin::Plugin;

my @dircomponents = split('/',$0);
my $me = pop(@dircomponents);

my @suggest=qw(temp clock volt);

=head1 NAME

nvidia_ - Munin wildcard plugin to monitor nvidia graphics cards with
the help of the nvclock command.

Run as nvidia_temp, nvidia_clock or nvidia_volt

=cut

=head1 APPLICABLE SYSTEMS

Any system with a nvclock command.  Currently this is available on
Linux, FreeBSD and Windows.

=head1 CONFIGURATION

Usually none is needed.  This shows the default configuration:

  [nvidia_*]
     env.nvclock = /usr/bin/nvclock

=cut

my $nvclock = $ENV{nvclock} || '/usr/bin/nvclock';

=head1 INTERPRETATION

The plugin invoked under the different aliases listed above will run
the nvclock command and show the different measurements as found in
the output of the program.

=head1 VERSION


Tested with nvclock version 0.8b3a on a nVidia Geforce 8600GT.

=head1 BUGS

None known

=head1 FEATURES

Requires Munin::Plugin as supplied in Munin 1.3.3 and 1.2.5.  If the
plugin complains about "Use of uninitialized value in string eq at
/usr/lib/perl/5.8/lib.pm line 29." and so on please make this plugin
configuration:

  [*]
	env.MUNIN_LIBDIR /usr/share/munin

using the correct LIBDIR for your installation of course (the other
standard value is /opt/munin/lib).


=head1 AUTHOR

  Copyright (C) 2008-2009 Nicolai Langfeldt, Linpro AS

=head1 LICENSE

GPLv2

=head1 MAGIC MARKERS

  #%# family=auto
  #%# capabilities=autoconf suggest

=cut

delete $ENV{'DISPLAY'};

sub collect_and_print ($$) {

  my ($todo,$function)=@_;

  my $cardinfo;

  my $config = $todo eq 'config';

  my $NVC;

  open($NVC,"$nvclock -i |") or die "Could not run $nvclock: $!\n";

  my @nvclock;

  @nvclock = <$NVC>;
  close($NVC) or die "Could not close command $nvclock: $!\n";

  my $section;

  foreach (@nvclock) {

    if (/^-- (.*) --/) {
      $section=$1;

      # Remove the "info" or "information" part
      $section =~ s/\s+info.*//;

      next;
    }

    my ($key,$value);
    ( $key,$value ) = split(':\s+',$_,2);

    next if ! defined($value);

    chomp $value;

    # Preprocessing special cases
    ($key eq 'Clock'  ) && do { $key = "$section $key"; };
    ($key eq 'Card'   ) && do { $cardinfo = $value; };
    ($key eq 'Version') && do { $cardinfo .= " Version $value"; };
    ($key =~ /^Signon/) && do { $cardinfo .= " $value"; };
    ($key eq 'Amount' ) &&
      ($section eq 'Memory') && do { $cardinfo .= ", with $value"; };
    ($key =~ 'Type'   ) &&
      ($section eq 'Memory') && do { $cardinfo .= " $value memory"; };

    # Yes, the appearance of the $cardinfo related things will be
    # fairly order sensitive, tell me if it breaks.

    my $label = clean_fieldname($key);

    # Processing each case of the plugin

    ($key =~ /voltage/i) && ($function eq 'volt') && do {
      if ($config) {
	print "$label.label $key\n";
	print "$label.type GAUGE\n";
      } else {
	my ($volt) = $value =~ m/\s*(\d+\.\d+)V/;
	$volt = 'U' unless defined $volt;
	print "$label.value $volt\n";
      }
      next;
    };

    ($key =~ /clock/i) && ($function eq 'clock') && do {
      if ($config) {
	print "$label.label $key\n";
	print "$label.type GAUGE\n";
      } else {
	my ($hz,$scale) = $value =~ m/\s*(\d+\.\d+)\s*([Mk]?)Hz/;
	if (defined($hz)) {
	  $scale = '' unless defined($scale);
	  $hz=$hz*1000*1000 if $scale eq 'M';
	  $hz=$hz*1000 if $scale eq 'k';
	} else {
	  $hz='U';
	}
	print "$label.value $hz\n";
      }
      next;
    };

    ($key =~ /temp/i) && ($function eq 'temp') && do {
      if ($config) {
	print "$label.label $key\n";
	print "$label.type GAUGE\n";
      } else {
	my ($temp) = $value =~ m/\s*(\d+(\.\d+)?)C/;
	$temp='U' unless defined($temp);
	print "$label.value $temp\n";
      }
      next;
    }
  }

  if ($config) {
    if ($function eq 'volt') {
      print "graph_title Voltages of NVidia graphics card\n";
      print "graph_vlabel Voltage\n";
    } elsif ($function eq 'clock') {
      print "graph_title Clock speeds of NVidia graphics card\n";
      print "graph_vlabel Hz\n";
    } elsif ($function eq 'temp') {
      print "graph_title Temperatures of NVidia graphics card\n";
      print "graph_vlabel Degrees Celsius\n";
    } else {
      die "$me: Unknown function!\n";
    }
    print "graph_category sensors\n";
    print "graph_info Graphics card is a $cardinfo\n";
  }
}


# Main

my ($function) = ( $me =~ m/.*_(.*)/ );

if (defined($ARGV[0])) {
  if ($ARGV[0] eq 'autoconf') {
    if (-x $nvclock) {
      print "yes\n";
      exit 0;
    } else {
      print "no (no nvclock executable at $nvclock, please configure)\n";
      exit 0;
    }
  } elsif ($ARGV[0] eq 'suggest') {
    print join("\n",@suggest),"\n";
    exit 0;
  } elsif ($ARGV[0] eq 'config') {
    collect_and_print('config',$function);
    exit 0;
  } elsif ($ARGV[0] eq '') {
    collect_and_print('fetch',$function);
    exit 0;
  } else {
    die "$me: Unknown argument: $ARGV[0]\n";
  }
} else {
  collect_and_print('fetch',$function);
  exit 0;
}

die "$me: Running over the edge of the plugin.  Why?"
