#!/usr/bin/perl -w
#
# Plugin for monitoring boinc processes
#
# Parameters:
#
#	password	-- The password for RPC authentication
#			   (default: boinc_cmd will look for a file
#			   'gui_rpc_auth.cfg' and use the password in it)
#       host 		-- the host to connect to (default: localhost)
#	port 		-- optional (default: 31416)
#
# This plugin can monitor boinc processes running on local/remote machines.
# You can see the progress on various projects.
#
# Author: Petr Ruzicka <petr.ruzicka@gmail.com>
# GPL v3
#
#%# family=auto
#%# capabilities=autoconf

use IO::Socket;
use Digest::MD5 qw(md5_hex);


if ($ENV{'password_path'}) {
  $password = $ENV{'password_path'};
} else {
    $password = $ENV{'password'} || `cat /var/lib/boinc/gui_rpc_auth.cfg 2>/dev/null`;
  }

my $host = $ENV{'host'} || '127.0.0.1';
my $port = $ENV{'port'} || '31416';


sub autoconf {
  my $client = new IO::Socket::INET (
  PeerAddr => $host,
  PeerPort => $port,
  Proto => 'tcp' );

  if ($client) {
    print $client "<boinc_gui_rpc_request><auth1/></boinc_gui_rpc_request>\003";

    {
      local $/ = "\003";
      $reply = <$client>;
    }

    $reply =~ /<nonce>(.*)<\/nonce>/;
    $hash = md5_hex($1, $password);

    print $client "<boinc_gui_rpc_request><auth2><nonce_hash>$hash</nonce_hash></auth2></boinc_gui_rpc_request>\003";

    {
      local $/ = "\003";
      $reply = <$client>;
    }

    if ($reply =~ /<authorized\/>/) {
      print "yes\n";
      exit 0;
    }
  }
  print "no\n";
  exit 0;
}

sub config {
  my $client = IO::Socket::INET->new ( PeerAddr => $host,
    				       PeerPort => $port,
				       Proto => 'tcp' )
  or die "Can't bind : $@\n";

  print $client "<boinc_gui_rpc_request><auth1/></boinc_gui_rpc_request>\003";

  {
    local $/ = "\003";
    $reply = <$client>;
  }

  $reply =~ /<nonce>(.*)<\/nonce>/;
  my $hash = md5_hex($1, $password);

  print $client "<boinc_gui_rpc_request><auth2><nonce_hash>$hash</nonce_hash></auth2></boinc_gui_rpc_request>\003";

  {
    local $/ = "\003";
    $reply = <$client>;
  }

  if ($reply !~ /<authorized\/>/) {
    die "Wrong password: $_";
  }

  print $client "<boinc_gui_rpc_request><get_state></boinc_gui_rpc_request>";

  while (chomp($reply = <$client>) && ($reply ne "</boinc_gui_rpc_reply>")) {
    if ($reply =~ /<domain_name>(.*)<\/domain_name>/) {
      print "graph_title BOINC task progress [$1]\n";
      print "graph_category htc\n";
      print "graph_args -l 0\n";
      print "graph_vlabel %\n";
    }
    if ($reply =~ /<project_name>(.*)<\/project_name>/) {
      my $boinc_munin_name=$1;
      $boinc_munin_name =~ /(\w+).*/;
      print "$1.label $boinc_munin_name\n";
    }
  }
  close ($client);
}

sub report {
  my $client = IO::Socket::INET->new ( PeerAddr => $host,
    				       PeerPort => $port,
				       Proto => 'tcp' )
  or die "Can't bind : $@\n";

  print $client "<boinc_gui_rpc_request><auth1/></boinc_gui_rpc_request>\003";

  {
    local $/ = "\003";
    $reply = <$client>;
  }

  $reply =~ /<nonce>(.*)<\/nonce>/;
  my $hash = md5_hex($1, $password);

  print $client "<boinc_gui_rpc_request><auth2><nonce_hash>$hash</nonce_hash></auth2></boinc_gui_rpc_request>\003";

  {
    local $/ = "\003";
    $reply = <$client>;
  }

  if ($reply !~ /<authorized\/>/) {
    die "Wrong password: $_";
  }

  print $client "<boinc_gui_rpc_request><get_state></boinc_gui_rpc_request>";

  while (chomp($reply = <$client>) && ($reply ne "</boinc_gui_rpc_reply>")) {
    if ($reply =~ /<project_name>(\w+).*<\/project_name>/) {
      $project = $1;
      $fraction_done=0;
      while (chomp($reply = <$client>) && ($reply ne "<project>") && ($reply ne "</client_state>")) {
        if ($reply =~ /\s*<active_task_state>1<\/active_task_state>/) {
	  while (chomp($reply = <$client>) && ($reply ne "</active_task>")) {
            if ($reply =~ /<fraction_done>(.*)<\/fraction_done>/) {
	      $fraction_done+=int($1*100 + .5 * ($1*100 <=> 0));
	    }
	  }
	}
      }
      print "$project.value $fraction_done\n";
    }
  }
  close ($client);
}

if (defined $ARGV[0]) {
  my $arg = $ARGV[0];
  my %funcs = ( config => \&config,
		autoconf => \&autoconf,
		report => \&report
	      );

  if (exists $funcs{$arg}) {
    $funcs{$arg}->();
  }
  else {
    $funcs{"report"}->();
  }
} else {
    report();
  }
