#!/usr/bin/perl

use strict;
use warnings;

=head1 NAME

  hwmon - Multigraph plugin to monitor Linux hwmon drivers

=head1 APPLICABLE SYSTEMS

Any Linux system with a modern kernel (2.6.something), with an
accessible C</sys> filesystem and available drivers for hardware
monitoring.

Different kernels expose more or fewer probes through the C</sys>
hwmon interfaces, but at least the recent 3.6 kernels expose the
following:

 - hardware sensors (also accessible via lm_sensors);
 - CPU core probes (Intel and AMD at least);
 - CPU power probes (AMD);
 - ACPI thermal zones (most x86/AMD64 CPUs);
 - video card temperatuers (open-source drivers).

The specifications, and this plugin, include support for voltage
input, fan speed, temperature, current, power and humidity.

Please note that on server-grade hardware, sensors are usually
implemented through IPMI, which the kernel does not expose to
userspace directly. For those systems, please refer to the C<freeipmi>
plugin.

=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

Copyright (c) 2012-2013 Diego Elio Pettenò <flameeyes@flameeyes.eu>

=head1 LICENSE

GPLv2

=head1 MAGIC MARKERS

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

=cut

use Munin::Plugin::Framework;
use Munin::Plugin;
use File::Basename;

my $plugin = Munin::Plugin::Framework->new;

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

$plugin->{autoconf} = "no (no hwmon device found)" if ( scalar(@devdirs) == 0 );

$plugin->add_graphs
  (
   hwmon_in =>
   {
    title => "Voltages",
    vlabel => "Volt",
    category => "sensors",
    args => "--base 1000 --logarithmic",
    denominator => 1000, # milliVolt -> Volt
    fields => {}
   },
   hwmon_fan =>
   {
    title => "Fans speed",
    vlabel => "RPM",
    category => "sensors",
    args => "--base 1000 -l 0",
    denominator => 1,
    fields => {}
   },
   hwmon_temp =>
   {
    title => "Temperatures",
    vlabel => "Degrees Celsius",
    category => "sensors",
    args => "--base 1000 -l 0",
    denominator => 1000, # milliCelsius -> Celsius
    fields => {}
   },
   hwmon_curr =>
   {
    title => "Currents",
    vlabel => "A",
    category => "sensors",
    args => "--base 1000 -l 0",
    denominator => 1000, # milliAmperes -> Amperes
    fields => {}
   },
   hwmon_power =>
   {
    title => "Power",
    vlabel => "W",
    category => "sensors",
    args => "--base 1000 -l 0",
    denominator => 1000000, # microWatts -> Watts
    fields => {}
   },
   hwmon_humidity =>
   {
    title => "Humidity",
    vlabel => "%",
    category => "sensors",
    args => "--base 1000 -l 0 -u 100",
    denominator => 1,
    fields => {}
   },
  );


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) = readarray("$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 $denominator = $plugin->{graphs}->{"hwmon_" . $type}->{denominator};

    my ($label) = readarray($path . "_label") || (($devname || "") . " $name");

    my ($lwarn) = readarray($path . "_min");
    $lwarn /= $denominator if $lwarn;
    $lwarn = '' unless $lwarn;

    my ($hwarn) = readarray($path . "_max");
    $hwarn /= $denominator if $hwarn;
    $hwarn = '' unless $hwarn;

    my ($lcrit) = readarray($path . "_lcrit");
    $lcrit /= $denominator if $lcrit;
    $lcrit = '' unless $lcrit;

    my ($hcrit) = readarray($path . "_crit");
    $hcrit /= $denominator if $hcrit;
    $hcrit = '' unless $hcrit;

    my ($val) = readarray($path . "_input");
    $val /= $denominator if $val;

    $plugin->{graphs}->{"hwmon_" . $type}->{fields}->{$graphid} =
      {
       label => $label,
       warning  => ($lwarn || $hwarn ) ? "$lwarn:$hwarn" : undef,
       critical => ($lcrit || $hcrit ) ? "$lcrit:$hcrit" : undef,
       value => $val,
      };
  }
}

$plugin->run;
