#!/usr/bin/perl
# -*- perl  -*-
# Copyright (C) 2004 Jimmy Olsen
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; version 2 dated June,
# 1991.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
#
# $Log$
# Revision 1.24.2.4  2005/02/19 16:06:14  ilmari
# Place the munin-limits lock file in rundir, not dbdir.
#
# Revision 1.24.2.3  2005/02/19 16:04:03  ilmari
# Fix the file vs. pipe check in munin-limits
#
# Revision 1.24.2.2  2005/02/16 20:03:31  jimmyo
# yet another rewrite of munin-limits open call (SF#1115434).
#
# Revision 1.24.2.1  2005/01/25 20:07:36  jimmyo
# Make munin-limits work properly with perl 5.6 (SF#1109039).
#
# Revision 1.24  2005/01/05 15:40:35  jimmyo
# Main: "contacts" can now be set to "none".
#
# Revision 1.23  2005/01/05 12:12:30  jimmyo
# Main: Added limit message option "strtrunc"
#
# Revision 1.22  2004/12/08 08:47:44  jimmyo
# Fix bug where munin-limits didn't warn properly in all situations.
#
# Revision 1.21  2004/11/26 08:48:50  ilmari
# Allow floating point values in warning/critical limits.
#
# Revision 1.20  2004/11/24 12:01:07  jimmyo
# Bugfix in munin-limits (it didn't work properly).
#
# Revision 1.19  2004/11/19 21:51:05  jimmyo
# Cosmetics on the nagios warnings.
#
# Revision 1.18  2004/11/19 21:32:39  jimmyo
# Added a --force option to munin-limits, to force sending absolutely all messages.
#
# Revision 1.17  2004/11/19 21:05:54  jimmyo
# Removed the munin-nagios program, as it is no longer needed.
#
# Revision 1.16  2004/11/19 20:51:24  jimmyo
# New notification system finished (I think).
#
# Revision 1.15  2004/11/19 18:38:02  jimmyo
# Worked a bit more on the notification system.
#
# Revision 1.14  2004/11/18 15:31:37  jimmyo
# Worked a bit more on the notification system.
#
# Revision 1.13  2004/11/18 00:22:02  jimmyo
# Midway implementation of new notification scheme.
#
# Revision 1.12  2004/11/16 20:00:44  jimmyo
# License cleanups.
#
# Revision 1.11  2004/11/13 21:22:59  jimmyo
# Changed some of the critical/warning output..
#
# Revision 1.10  2004/11/13 19:14:08  jimmyo
# Changed the default warning/critical output a bit.
#
# Revision 1.9  2004/11/12 23:18:52  jimmyo
# Added new options notify_enable and notify_text, to allow more finely tuned notifications (both what to notify and what text to send.)
#
# Revision 1.8  2004/09/24 16:31:07  jimmyo
# Bugfixes.
#
# Revision 1.7  2004/09/08 15:25:33  ilmari
# Use /usr/bin/perl in all perl shebang lines.
#
# Revision 1.6  2004/06/08 15:30:34  jimmyo
# The server programs now open the log file at an earlier point.
#
# Revision 1.5  2004/05/20 22:30:08  jimmyo
# * Munin-limits added to distro.
# * Breached limis now show up in overview and node view.
#
# Revision 1.4  2004/01/29 17:40:10  jimmyo
# Fixed pod typos patched by Lupe Christoph (SF#884092)
#
# Revision 1.3  2004/01/29 17:34:06  jimmyo
# Updated copyright information
#
# Revision 1.2  2004/01/15 15:20:01  jimmyo
# Making things workable after name change. Upping for test verwion.
#
# Revision 1.1  2004/01/02 18:50:01  jimmyo
# Renamed occurrances of lrrd -> munin
#
# Revision 1.1.1.1  2004/01/02 15:18:08  jimmyo
# Import of LRRD CVS tree after renaming to Munin
#
# Revision 1.4  2003/11/07 20:46:12  jimmyo
# Only require Config::General if using old config format.
#
# Revision 1.3  2003/11/07 17:43:16  jimmyo
# Cleanups and log entries
#

use strict;

use Munin;
use POSIX qw(strftime);
use Getopt::Long;
use Text::Balanced qw (extract_multiple extract_delimited extract_quotelike extract_bracketed);

my $DEBUG=0;
my $conffile = "/etc/munin/munin.conf";
my $do_usage = 0;
my @limit_hosts = ();
my @limit_services = ();
my @limit_contacts = ();
my $force_root = 0;
my %notes = ();
my $stdout = 0;
my $force = 0;
my %default_text = ( "default" => '${var:group} :: ${var:host} :: ${var:graph_title}${if:cfields \n\tCRITICALs:${loop<,>:cfields  ${var:label} is ${var:value} (outside range [${var:crange}])${if:extinfo : ${var:extinfo}}}.}${if:wfields \n\tWARNINGs:${loop<,>:wfields  ${var:label} is ${var:value} (outside range [${var:wrange}])${if:extinfo : ${var:extinfo}}}.}${if:ufields \n\tUNKNOWNs:${loop<,>:ufields  ${var:label} is ${var:value}${if:extinfo : ${var:extinfo}}}.}${if:fofields \n\tOKs:${loop<,>:fofields  ${var:label} is ${var:value}${if:extinfo : ${var:extinfo}}}.}\n',
    		     "nagios"  => '${var:host}\t${var:graph_title}\t${var:worstid}\t${strtrunc:350 ${if:cfields CRITICALs:${loop<,>:cfields  ${var:label} is ${var:value} (outside range [${var:crange}])${if:extinfo : ${var:extinfo}}}.}${if:wfields WARNINGs:${loop<,>:wfields  ${var:label} is ${var:value} (outside range [${var:wrange}])${if:extinfo : ${var:extinfo}}}.}${if:ufields UNKNOWNs:${loop<,>:ufields  ${var:label} is ${var:value}${if:extinfo : ${var:extinfo}}}.}${if:fofields OKs:${loop<,>:fofields  ${var:label} is ${var:value}${if:extinfo : ${var:extinfo}}}.}}',
    		     "old-nagios"  => '${var:host}\t${var:plugin}\t${var:worstid}\t${strtrunc:350 ${var:graph_title}:${if:cfields CRITICALs:${loop<,>:cfields  ${var:label} is ${var:value} (outside range [${var:crange}])${if:extinfo : ${var:extinfo}}}.}${if:wfields WARNINGs:${loop<,>:wfields  ${var:label} is ${var:value} (outside range [${var:wrange}])${if:extinfo : ${var:extinfo}}}.}${if:ufields UNKNOWNs:${loop<,>:ufields  ${var:label} is ${var:value}${if:extinfo : ${var:extinfo}}}.}${if:fofields OKs:${loop<,>:fofields  ${var:label} is ${var:value}${if:extinfo : ${var:extinfo}}}.}}'
		    );

my $log = new IO::Handle;

# Get options
$do_usage=1  unless 
GetOptions ( "force-root!"  => \$force_root,
	     "host=s"       => \@limit_hosts,
	     "service=s"    => \@limit_services,
	     "contact=s"    => \@limit_contacts,
	     "config=s"     => \$conffile,
	     "debug!"       => \$DEBUG,
	     "stdout!"      => \$stdout,
	     "force!"       => \$force,
	     "help"         => \$do_usage );

if ($do_usage)
{
    print "Usage: $0 [options]

Options:
    --[no]force-root    Force running, even as root. [--noforce-root]
    --help		View this message.
    --debug		View debug messages.
    --stdout		Log to stdout as well as the log file.
    --force		Send messages even if they shouldn't normally be sent.
    --service <service>	Limit notified services to <service>. Multiple 
    			--service options may be supplied.
    --host <host>	Limit notified hosts to <host>. Multiple --host 
    			options may be supplied.
    --contact <contact>	Limit notified contacts to <contact>. Multiple 
    			--contact options may be supplied.
    --config <file>	Use <file> as configuration file. 
    			[/etc/munin/munin.conf]

";
    exit 0;
}

if ($> == 0 and !$force_root)
{
    print "You are running this program as root, which is neither smart nor necessary.
If you really want to run it as root, use the --force-root option. Else, run
it as the user \"munin\". Aborting.\n\n";
    exit (1);
}

my $config = &munin_config ($conffile);
my $oldnotes = &munin_readconfig ($config->{'dbdir'}."/limits", 1, 1);
my $modified=0;

logger("Starting munin-limits, checking lock");
munin_runlock("$config->{rundir}/munin-limits.lock");
logger("Created lock: $config->{rundir}/munin-limits.lock");

if (!defined $config->{'contact'}->{'nagios'}->{'command'} and
	defined $config->{'nsca'})
{
    $config->{'contact'}->{'old-nagios'}->{'command'} = "$config->{nsca} $config->{nsca_server} -c $config->{nsca_config} -to 60";
    $config->{'contact'}->{'old-nagios'}->{'always_send'} = "critical warning";
}
if (!defined $config->{'contact'}->{'nagios'}->{'always_send'})
{
    $config->{'contact'}->{'nagios'}->{'always_send'} = "critical warning";
}

for my $domain ( keys %{$config->{domain}}) {
    logger ("processing domain: $domain");
    process_domain($domain);
}
&munin_writeconfig ("$config->{dbdir}/limits", \%notes);
logger("munin-limits finished.");


sub process_domain {
    my ($domain) = @_;
    for my $node ( keys %{$config->{domain}->{$domain}->{node}}) {
    	if (@limit_hosts and !grep (/^$node$/, @limit_hosts))
	{
		logger ("skipping node: $node");
		next;
	}
	logger ("processing node: $node");
	process_node($domain,$node ,$config->{domain}->{$domain}->{node}->{$node} );
    }
}

sub process_node {
  my ($domain,$name,$node) = @_;
  for my $client (keys %{$node->{client}}) {
      logger ("processing service: $client") if $DEBUG;
      process_service($domain,$name,$client,$node->{client}->{$client});
  }
}

sub process_service {
    my $critical= undef;
    my ($domain, $name,$clientname,$client) = @_;
    return unless $client;
    for my $service (keys %$client) {
	if ($service =~ /(^.*)\.label/) {
	    my $key = $1;
	    next unless ((exists $client->{"$key.warning"}) || ($client->{"$key.critical"}));
	    logger ("processing field: $key") if $DEBUG;
	    if (@limit_services and !grep (/^$service$/, @limit_services))
	    {
		next;
	    }
	    my $critical;
	    my $warning;
	    ($warning, $critical) = get_limits ($client, $domain, $name, $clientname, $key);

	    my $filename = "$config->{dbdir}/$domain/$name-$clientname-$key-".
	    lc substr (($client->{"$key.type"}||"GAUGE"),0,1) . ".rrd";
	    my $value = sprintf "%.2f",&munin_fetch("$filename");

	    # Some fields that are nice to have in the plugin output
	    $client->{$key.".value"} = $value;
	    $client->{'fields'} = join (' ', map { $_ =~ s/\.label$//; $_} grep (/\.label/, keys %$client));
	    $client->{'plugin'} = $clientname;
	    $client->{'graph_title'} = $client->{'notify_alias'} if defined $client->{'notify_alias'};
	    $client->{'host'} = $config->{'domain'}->{$domain}->{'node'}->{$name}->{'notify_alias'} || $name;
	    $client->{'group'} = $config->{'domain'}->{$domain}->{'notify_alias'} || $domain;
	    $client->{'worst'} = "OK";
	    $client->{'worstid'} = 0 unless defined $client->{'worstid'};
	    $client->{$key.".crange"} = (defined $critical->[0]?$critical->[0]:"").":".(defined $critical->[1]?$critical->[1]:"");
	    $client->{$key.".wrange"} = (defined $warning->[0]?$warning->[0]:"").":".(defined $warning->[1]?$warning->[1]:"");

	    logger ("value: $domain -> $name -> $clientname -> $key : $value") if $DEBUG;
	    if ((defined ($critical->[0]) and $value < $critical->[0]) or
	    (defined ($critical->[1]) and $value > $critical->[1])) {
		$critical->[0] ||= "";
		$critical->[1] ||= "";
		$client->{'worst'} = "CRITICAL";
		$client->{'worstid'} = 2;
		$notes{$domain}{$name}{$clientname}{"$key.state"} = "critical";
		$notes{$domain}{$name}{$clientname}{"$key.critical"} = 
		(defined $client->{"$key.extinfo"}?
		"$value (not in $critical->[0]:$critical->[1]): ".
		$client->{"$key.extinfo"}:
		"Value is $value. Critical range ($critical->[0]:$critical->[1]) exceeded");
		if (!defined ($oldnotes->{'domain'}->{$domain}->{'node'}->{$name}->{'client'}->{$clientname}->{"$key.state"}) or 
			$oldnotes->{'domain'}->{$domain}->{'node'}->{$name}->{'client'}->{$clientname}->{"$key.state"} ne "critical")
		{
		    $client->{'state_changed'} = 1;
		}
	    }
	    elsif ((defined ($warning->[0]) and $value < $warning->[0]) or 
		(defined ($warning->[1]) and $value > $warning->[1])) 
	    {
		$warning->[0] ||= "";
		$warning->[1] ||= "";
		$client->{'worst'} = "WARNING" if $client->{"worst"} eq "OK";
		$client->{'worstid'} = 1 if $client->{"worstid"} == 0;
		$notes{$domain}{$name}{$clientname}{"$key.state"} = "warning";
		$notes{$domain}{$name}{$clientname}{"$key.warning"} = 
		(defined $client->{"$key.extinfo"}?
		"$value (not in $warning->[0]:$warning->[1]): ".
		$client->{"$key.extinfo"}:
		"Value is $value. Warning range ($warning->[0]:$warning->[1]) exceeded");
		if (!defined ($oldnotes->{'domain'}->{$domain}->{'node'}->{$name}->{'client'}->{$clientname}->{"$key.state"}) or 
			$oldnotes->{'domain'}->{$domain}->{'node'}->{$name}->{'client'}->{$clientname}->{"$key.state"} ne "warning")
		{
		    $client->{'state_changed'} = 1;
		}
	    } 
	    elsif (defined ($oldnotes->{'domain'}->{$domain}->{'node'}->{$name}->{'client'}->{$clientname}->{"$key.state"}) or
		    $force)
	    {
		$notes{$domain}{$name}{$clientname}{"$key.ok"} = "OK";
		$client->{'state_changed'} = 1;
	    }
	}
    }
    generate_service_message ($domain, $name, $clientname, $client);
}

sub get_limits
{
    my $client = shift;
    my $domain = shift;
    my $name   = shift;
    my $clientname = shift;
    my $key    = shift;
    my @critical = (undef, undef);
    my @warning  = (undef, undef);
    if (defined $client->{"$key.critical"} and 
	$client->{"$key.critical"} =~ /^\s*([-+\d.]*):([-+\d.]*)\s*$/)
    {
	$critical[0] = $1 if length $1;
	$critical[1] = $2 if length $2;
	logger ("processing critical: $domain -> $name -> $clientname -> $key -> $critical[0] : $critical[1]") if $DEBUG;
    }
    elsif (defined $client->{"$key.critical"} and
	$client->{"$key.critical"} =~ /^\s*([-+\d.]+)\s*$/)
    {
	$critical[1] = $1 if defined $1;
	logger ("processing critical: $domain -> $name -> $clientname -> $key -> $critical[0] : $critical[1]") if $DEBUG;
    }
    elsif (defined $client->{"$key.critical"})
    {
	@critical = (0, 0);
	logger ("processing critical: $domain -> $name -> $clientname -> $key -> $critical[0] : $critical[1]") if $DEBUG;
    }
    if (defined $client->{"$key.warning"} and 
	$client->{"$key.warning"} =~ /^\s*([-+\d.]*):([-+\d.]*)\s*$/)
    {
	$warning[0] = $1 if length $1;
	$warning[1] = $2 if length $2;
	logger ("processing warning: $domain -> $name -> $clientname -> $key -> $warning[0] : $warning[1]") if $DEBUG;
    }
    elsif (defined $client->{"$key.warning"} and
	$client->{"$key.warning"} =~ /^\s*([-+\d.]+)\s*$/)
    {
	$warning[1] = $1 if defined $1;
	logger ("processing warning: $domain -> $name -> $clientname -> $key -> $warning[0] : $warning[1]") if $DEBUG;
    }
    elsif (defined $client->{"$key.warning"})
    {
	@warning = (0, 0);
	logger ("processing warning: $domain -> $name -> $clientname -> $key -> $warning[0] : $warning[1]") if $DEBUG;
    }
    return (\@warning, \@critical);
}

sub generate_service_message {
    my $critical= undef;
    my ($domain, $name,$clientname,$client) = @_;
    return unless $client;
    my $worst = "";
    my %stats = ('critical' => [], 'warning' => [], 'unknown' => [], 'foks' => [], 'ok' => []);

    foreach my $key (keys %{$notes{$domain}{$name}{$clientname}})
    {
	if ($key =~ /^([^\.]+)\.critical$/)
	{
	    $worst = "critical";
	    push @{$stats{'critical'}}, $1;
	}
    	elsif ($key =~ /^([^\.]+)\.warning$/)
	{
	    $worst = "warning" if $worst ne "critical";
	    push @{$stats{'warning'}}, $1;
	}
    	elsif ($key =~ /^([^\.]+)\.unknown$/)
	{
	    $worst = "unknown" unless $worst;
	    push @{$stats{'unknown'}}, $1;
	}
    	elsif ($key =~ /^([^\.]+)\.ok$/)
	{
	    $worst = "ok" unless $worst;
	    push @{$stats{'oks'}}, $1;
	    push @{$stats{'foks'}}, $1;
	}
	else
	{
	    push @{$stats{'oks'}}, $1;
	}
    }
    $client->{'cfields'}  = join " ", @{$stats{'critical'}};
    $client->{'wfields'}  = join " ", @{$stats{'warning'}};
    $client->{'ufields'}  = join " ", @{$stats{'unknown'}};
    $client->{'fofields'} = join " ", @{$stats{'foks'}};
    $client->{'ofields'}  = join " ", @{$stats{'ok'}};
    $client->{'numcfields'}  = scalar @{$stats{'critical'}};
    $client->{'numwfields'}  = scalar @{$stats{'warning'}};
    $client->{'numufields'}  = scalar @{$stats{'unknown'}};
    $client->{'numfofields'} = scalar @{$stats{'foks'}};
    $client->{'numofields'}  = scalar @{$stats{'ok'}};

    if ($worst)
    {
	foreach my $c (split (/\s+/, munin_get ($config, "contacts", join (' ', keys %{$config->{'contact'}}), $domain, $name, $clientname)))
	{
	    next if $c eq "none";
	    next unless defined $config->{'contact'}->{$c}->{'command'};
	    if (@limit_contacts and !grep (/^$c$/, @limit_contacts))
	    {
		next;
	    }
	    my $obsess = 0;
	    if (defined ($config->{'contact'}->{$c}->{'always_send'}))
	    {
		$obsess = grep {scalar(@{$stats{$_}})} (split (/\s+/, lc $config->{'contact'}->{$c}->{'always_send'}));
	    }
	    if (!$client->{'state_changed'} and !$obsess)
	    {
		next;
	    }
	    my $precmd = $config->{'contact'}->{$c}->{'command'};
	    my $pretxt = ($config->{'contact'}->{$c}->{'text'} || $config->{'contact'}->{'default'}->{'text'} || $default_text{$c} || $default_text{'default'});
	    my $txt = message_expand ($pretxt, $client, "");
	    my $cmd = message_expand ($precmd, $client, "");
	    $txt =~ s/\\n/\n/g;
	    $txt =~ s/\\t/\t/g;

	    # In some cases we want to reopen the command
	    if ($config->{'contact'}->{$c}->{'max_messages'} and defined ($config->{'contact'}->{$c}->{'num_messages'}) and
		    $config->{'contact'}->{$c}->{'num_messages'} >= $config->{'contact'}->{$c}->{'max_messages'})
	    {
		close ($config->{'contact'}->{$c}->{'pipe'});
		$config->{'contact'}->{$c}->{'pipe'} = undef;
	    }
	    elsif (defined ($config->{'contact'}->{$c}->{'pipe_command'}) and 
		    $config->{'contact'}->{$c}->{'pipe_command'} ne $cmd)
	    {
		close ($config->{'contact'}->{$c}->{'pipe'});
		$config->{'contact'}->{$c}->{'pipe'} = undef;
	    }
	
	    my $pipe;
	    if (!defined $config->{'contact'}->{$c}->{'pipe'})
	    {
		my @cmd = extract_multiple (
			message_expand ($cmd),
			[ sub { extract_delimited ($_[0], q{"'})},
			  qr/\S+/
			],
			undef, 1);
		@cmd = map { s/['"]$//; s/^['"]//; $_ } @cmd;
		$config->{'contact'}->{$c}->{'num_messages'} = 0;
		if ($cmd[0] eq "|")
		{
		    $cmd[0] = "|-";
		} 
		elsif ($cmd[0] !~ /^[|>]/)
		{
		    unshift (@cmd, "|-");
		}
		logger ("Debug: opening for writing: \"" . join('" "',@cmd) . "\".") if $DEBUG;
		if ($cmd[0] eq ">")
		{
		    if (! open ($pipe, join (' ', @cmd)))
		    {
			logger ("Fatal: Could not open " . join (' ', @cmd[1 .. $#cmd]) . " for writing: $!");
			exit 3;
		    }
		}
		else
		{
		    my $pid = open ($pipe, "|-");
		    if (!defined $pid)
		    {
			logger ("Fatal: Unable to  fork: $!");
			exit 3;
		    }
		    if (!$pid) # Child
		    {
			# Fork of stdout-to-log filter
			my $logstdout;
			my $logstderr;
			my $logpid = open ($logstdout, "|-");
			if (!defined $logpid)
			{
			    logger ("Fatal: Unable to  fork: $!");
			    exit 3;
			}
			if (!$logpid) # Child
			{
			    while (<STDIN>)
			    {
				chomp;
				logger ("Command \"$c\" stdout: $_");
			    }
			    exit 0;
			}
			close (STDOUT);
			*STDOUT = \$logstdout;
			my $logpid = open ($logstderr, "|-");
			if (!defined $logpid)
			{
			    logger ("Fatal: Unable to  fork: $!");
			    exit 3;
			}
			if (!$logpid) # Child
			{
			    while (<STDIN>)
			    {
				chomp;
				logger ("Command \"$c\" stderr: $_");
			    }
			    exit 0;
			}
			close (STDERR);
			*STDERR = \$logstderr;

			exec (@cmd[1 .. $#cmd]) or logger ("Warning: Could not run command \"" . join(' ',@cmd[1 .. $#cmd]) . "\": $!");
			exit 5;
                        # NOTREACHED
		    }
		}
		$config->{'contact'}->{$c}->{'pipe_command'} = $cmd;
		$config->{'contact'}->{$c}->{'pipe'} = $pipe;
	    } 
	    $pipe = $config->{'contact'}->{$c}->{'pipe'};
	    print $pipe $txt, "\n" if (defined $pipe);
	    $config->{'contact'}->{$c}->{'num_messages'}++;
	}
    }
}


sub message_expand {
    my $text   = shift;
    my $client = shift;
    my $prefix = shift || "";
    my @res    = ();

    
    while (length ($text))
    {   
	if ($text =~ /^([^\$]+|)(?:\$(\{.*)|)$/)
	{
	    push @res, $1;
	    $text = $2;
	}   
	my @a = extract_bracketed ($text, '{}');
	if ($a[0] =~ /^\{var:(\S+)\}$/)
	{
	    $a[0] = (defined $client->{$prefix.$1} ? $client->{$prefix.$1} : "");
	}
	elsif ($a[0] =~ /^\{loop<([^>]+)>:\s*(\S+)\s(.+)\}$/)
	{
	    my $d = $1;
	    my $f = $2;
	    my $t = $3;
	    my @res  = ();
	    if (defined $client->{$f})
	    {
		foreach my $sub (split /\s+/, $client->{$f})
		{
		    push @res, message_expand ($t, $client, $sub.".");
		}
	    } 
	    $a[0] = join ($d, @res);
	}
	elsif ($a[0] =~ /^\{loop:\s*(\S+)\s(.+)\}$/)
	{
	    my $f = $1;
	    my $t = $2;
	    my $res  = "";
	    if (defined $client->{$f})
	    {
		foreach my $sub (split /\s+/, $client->{$f})
		{
		    $res .= message_expand ($t, $client, $sub.".");
		}
	    } 
	    $a[0] = $res;
	}
	elsif ($a[0] =~ /^\{strtrunc:\s*(\S+)\s(.+)\}$/)
	{
	    my $f = "%.".$1."s";
	    my $t = $2;
	    $a[0] = sprintf ($f, message_expand ($t, $client, $prefix));
	}
	elsif ($a[0] =~ /^\{if:\s*(\!)?(\S+)\s(.+)\}$/)
	{
	    my $n = $1;
	    my $f = $2;
	    my $t = $3;
	    my $res  = "";
	    my $check = (defined $client->{$prefix.$f} and length($client->{$prefix.$f}) and $client->{$prefix.$f} ne "0");

	    $check = (!defined $client->{$prefix.$f} or !length($client->{$prefix.$f}) or $client->{$prefix.$f} eq "0")
		if $n;

	    if ($check)
	    {
		$res .= message_expand ($t, $client, $prefix);
	    } 
	    $a[0] = $res;
	}
	push @res, $a[0];
	$text = $a[1];
    }

    return join ('', @res);
}

sub logger_open {
    my $dirname = shift;

    if (!$log->opened)
    {
	  unless (open ($log, ">>$dirname/munin-limits.log"))
	  {
		  print STDERR "Warning: Could not open log file \"$dirname/munin-limits.log\" for writing: $!";
	  }
	  else
	  {
		  close (STDERR);
		  *STDERR = \$log;
		
	  }
    }
}

sub logger {
  my ($comment) = @_;
  my $now = strftime "%b %d %H:%M:%S", localtime;

  print "$now - $comment\n" if $stdout;
  if ($log->opened)
  {
	  print $log "$now - $comment\n";
  }
  else
  {
	  if (defined $config->{logdir})
	  {
		  if (open ($log, ">>$config->{logdir}/munin-graph.log"))
		  {
			  print $log "$now - $comment\n";
		  }
		  else
		  {
			  print STDERR "Warning: Could not open log file \"$config->{logdir}/munin-graph.log\" for writing: $!";
			  print STDERR "$now - $comment\n";
		  }
	  }
	  else
	  {
		  print STDERR "$now - $comment\n";
	  }
    }
}

close $log;

=head1 NAME

munin-limits - A program to check for any off-limit values

=head1 SYNOPSIS

munin-limits [options]

=head1 OPTIONS

=over 5

=item B<< --service <service> >>

Limit services to those of E<lt>serviceE<gt>. Multiple --service options may be supplied. [unset]

=item B<< --host <host> >>

Limit hosts to those of E<lt>host<gt>. Multiple --host options may be supplied. [unset]

=item B<< --contact <contact> >>

Limit contacts to those of E<lt>contact<gt>. Multiple --contact options may be supplied. [unset]

=item B<< --config <file> >>

Use E<lt>fileE<gt> as configuration file. [/etc/munin/munin.conf]

=item B<< --[no]force >>

Force sending of messages even if you normally wouldn't. [--noforce]

=item B<< --[no]force-root >>

Force running as root (stupid and unnecessary). [--noforce-root]

=item B<< --help >>

View help message.

=item B<< --[no]debug >>

If set, view debug messages. [--nodebug]

=back

=head1 DESCRIPTION

Munin-limits is a part of the package Munin, which is used in combination
with Munin's node.  Munin is a group of programs to gather data from
Munin's nodes, graph them, create html-pages, and optionally warn Nagios
about any off-limit values.

Munin-limits checks if any values are above or below the set limits, and saves these notes to a file. This file
is later used by programs like munin-nagios (to warn nagios) and munin-html (to incorporate them in the web
display).

If a service has fields with "warning" or "critical"-options (e.g. "load.warning 10"), and the munin-server
configuration file contains the necessary configuration options, munin-limits will check its value.

=head1 FILES

	/etc/munin/munin.conf
	/var/lib/munin/*
	/var/run/munin/*

=head1 VERSION

This is munin-limits version 1.2.5

=head1 AUTHORS

Audun Ytterdal and Jimmy Olsen.

=head1 BUGS

munin-limits does, as of now, not check the syntax of the configuration file.

Please report other bugs in the bug tracker at L<http://munin.sf.net/>.

=head1 COPYRIGHT

Copyright  2002-2004 Knut Haugen, Audun Ytterdal, and Jimmy Olsen / Linpro AS.

This is free software; see the source for copying conditions. There is
NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.

This program is released under the GNU General Public License

=cut

# vim: syntax=perl ts=8
