#!/usr/bin/perl -w
# -*- cperl -*-
=head1 NAME

  hwmon - Multigraph-plugin to monitor Linux hwmon drivers

=head1 CONFIGURATION

There is no environment configuration for this plugin on the node
side. If you need to ignore some values, do so from the master
directly.

The data is being received directly by the kernel-configured
parameters. Some of these parameters are initialized by lm_sensors
depending on your driver and distribution, so while the plugin does
not require you to have the package installed, it's still suggested.

=head1 AUTHOR

Diego Elio Pettenò <flameeyes@flameeyes.eu>.

=head1 LICENSE

GPLv2

=head1 MAGIC MARKERS

 #%# family=manual
 #%# capabilities=autoconf

=cut

use strict;
use Munin::Plugin;
use File::Basename;

sub readpath {
  my $path = shift;
  open READ, $path or return '';
  chomp(my $read = readline(READ));
  close READ;

  return $read;
}

# we'll opent he hwmon class, then find all the devices registered
# there.
my $classdir = '/sys/class/hwmon';
my @devdirs = <$classdir/hwmon*>;

if ( defined $ARGV[0] and $ARGV[0] eq 'autoconf' ) {
  if ( scalar(@devdirs) > 0 ) {
    print "yes\n";
  } else {
    print "no (no hwmon device found)\n";
  }

  exit 0;
}

my %sensors = (
	     in => {
		    inputs => [],
		    title => "Voltages",
		    vlabel => "Volt",
		    graph_args => "--base 1000 --logarithmic",
		    denominator => 1000 # milliVolt -> Volt
		   },
	     fan => {
		     inputs => [],
		     title => "Fans speed",
		     vlabel => "RPM",
		     graph_args => "--base 1000 -l 0",
		     denominator => 1
		    },
	     temp => {
		      inputs => [],
		      title => "Temperatures",
		      vlabel => "Degrees Celsius",
		      graph_args => "--base 1000 -l 0",
		      denominator => 1000 # milliCelsius -> Celsius
		     },
	     curr => {
		      inputs => [],
		      title => "Currents",
		      vlabel => "A",
		      graph_args => "--base 1000 -l 0",
		      denominator => 1000 # milliAmperes -> Amperes
		     },
	     power => {
		       inputs => [],
		       title => "Power",
		       vlabel => "W",
		       graph_args => "--base 1000 -l 0",
		       denominator => 1000000 # microWatts -> Watts
		      },
	     humidity => {
			  inputs => [],
			  title => "Humidity",
			  vlabel => "%",
			  graph_args => "--base 1000 -l 0 -u 100",
			  denominator => 1
			 }
	    );

foreach my $devdir (@devdirs) {
  my $devname = basename($devdir);

  # we have to find where the actual data is. Unfortunately some
  # drivers use /sys/class/hwmon/hwmon* directly while most use
  # /sys/class/hwmon/hwmon*/device.
  if (-e "$devdir/device/name") {
    $devdir = "$devdir/device";
  } elsif (! -e "$devdir/name") {
    next;
  }

  my $devlabel = readpath "$devdir/name";

  foreach my $input (<$devdir/*_input>) {
    basename($input) =~ /^(([a-z]+)[0-9]+)_input$/;
    my $name = $1;
    my $type = $2;
    my $graphid = clean_fieldname("$devname $name");
    my $path = "$devdir/$name";
    my $label = "$devlabel $name";

    push(@{$sensors{$type}->{inputs}}, { path => $path, label => $label, graphid => $graphid });
  }
}

if ( defined $ARGV[0] and $ARGV[0] eq 'config' ) {
  foreach my $type (keys %sensors) {
    # don't print anything if no value is found
    next if scalar(@{$sensors{$type}->{inputs}}) == 0;

    print <<END;

multigraph hwmon_$type
graph_title $sensors{$type}->{title}
graph_vlabel $sensors{$type}->{vlabel}
graph_args $sensors{$type}->{graph_args}
graph_category sensors
END

    foreach my $sensor (@{$sensors{$type}->{inputs}}) {
      my $label = readpath("$sensor->{path}_label");
      $label = $sensor->{label} if $label eq '';

      print "$sensor->{graphid}.label $label\n";

      my $lwarn = readpath("$sensor->{path}_min");
      $lwarn = $lwarn/$sensors{$type}->{denominator}
	unless $lwarn eq '';

      my $hwarn = readpath("$sensor->{path}_max");
      $hwarn = $hwarn/$sensors{$type}->{denominator}
	unless $hwarn eq '';

      print "$sensor->{graphid}.warning $lwarn:$hwarn\n"
	unless $lwarn eq '' and $hwarn eq '';

      my $lcrit = readpath("$sensor->{path}_lcrit");
      $lcrit = $lcrit/$sensors{$type}->{denominator}
	unless $lcrit eq '';

      my $hcrit = readpath("$sensor->{path}_crit");
      $hcrit = $hcrit/$sensors{$type}->{denominator}
	unless $hcrit eq '';

      print "$sensor->{graphid}.critical $lcrit:$hcrit\n"
	unless $lcrit eq '' and $hcrit eq '';
    }
  }

  unless ( ($ENV{MUNIN_CAP_DIRTYCONFIG} || 0) == 1 ) {
    exit 0;
  }
}

foreach my $type (keys %sensors) {
  # don't print anything if no value is found
  next if scalar(@{$sensors{$type}->{inputs}}) == 0;

  print "multigraph hwmon_$type\n";

  foreach my $sensor (@{$sensors{$type}->{inputs}}) {
    my $val = readpath("$sensor->{path}_input") / $sensors{$type}->{denominator};
    print "$sensor->{graphid}.value $val\n";
  }
}

# vim:syntax=perl
