#!/usr/bin/perl
#
# mon - schedules service tests and triggers alerts upon failures
#
# Jim Trocki, trockij@arctic.org
#
# $Id: mon,v 1.22.2.2 2007/06/06 11:46:19 trockij Exp $
#
# Copyright (C) 1998 Jim Trocki
#
#    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; either version 2 of the License, or
#    (at your option) any later version.
#
#    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
#
#
use strict;

my $RCSID='$Id: mon,v 1.22.2.2 2007/06/06 11:46:19 trockij Exp $';
my $AUTHOR='trockij@arctic.org';
my $RELEASE='$Name: mon-1-2-0-release $';

#
# NetBSD rc.d script compatibility
#
$0= "mon" . " " . join(" ", @ARGV) if $^O eq "netbsd";

#
# modules in the perl distribution
#
use Getopt::Long qw(:config no_ignore_case);
use Text::ParseWords;
use POSIX;
use Fcntl;
use Socket;
use Sys::Hostname;
use Sys::Syslog qw(:DEFAULT);
use FileHandle;

use Data::Dumper;

#
# CPAN modules
#
use Time::HiRes qw(gettimeofday tv_interval usleep);
use Time::Period;

sub auth;
sub call_alert;
sub check_auth;
sub clear_timers;
sub client_accept;
sub client_close;
sub client_command;
sub client_dopending;
sub client_write_opstatus;
sub collect_output;
sub daemon;
sub debug;
sub debug_dir;
sub dep_ok;
sub dep_summary;
sub depend;
sub dhmstos;
sub die_die;
sub disen_host;
sub disen_service;
sub disen_watch;
sub do_alert;
sub do_startup_alerts;
sub err_startup;
sub esc_str;
sub gen_scriptdir_hash;
sub handle_io;
sub handle_trap;
sub handle_trap_timeout;
sub host_exists;
sub host_singleton_group;
sub inRange;
sub init_cf_globals;
sub init_globals;
sub load_auth;
sub load_state;
sub normalize_paths;
sub mysystem;
sub init_dtlog;
sub pam_conv_func;
sub proc_cleanup;
sub process_event;
sub randomize_startdelay;
sub read_cf;
sub readhistoricfile;
sub reload;
sub remove_proc;
sub reset_server;
sub run_monitor;
sub save_state;
sub set_last_test;
sub set_op_status;
sub reset_timer;
sub setup_server;
sub sock_write;
sub syslog_die;
sub un_esc_str;
sub usage;
sub write_dtlog;

#
# globals
#
my %opt;		# cmdline arguments
my %CF;			# configuration directives
my $PWD;		# current working directory
my $HOSTNAME;		# system hostname
my $STOPPED;		# 1 = scheduler stopped, 0 = not stopped
my $STOPPED_TIME;	# time(2) scheduler was stopped, if stopped
my $SLEEPINT;		# don't touch
my %watch_disabled;	# watches disabled, indexed by watch
my %watch;		# main configuration file data structure
my %alias;		# aliases
my %groups;		# hostgroups, indexed by group
my %views;		# view lists, indexed by name
my %view_users;         # view preferences, per user

#
# I/O routine globals
#
my %clients;		# fds of connected clients
my $numclients;		# count of connected clients
my %running;		# procs which are forked and running,
			# indexed by group/service
my $iovec;		# used for select loop
my %runningpid;		# procs which are forked and running,
			# indexed by PID
my $procs;		# number of outstanding procs
my %fhandles;		# input file handles of children
my %ibufs;		# buffer structure to hold data from children
my ($fdset_rbits, $fdset_ebits);

#
# history globals
#
my @last_alerts;	# alert history, in memory
my @last_failures;	# failure history, in memory

#
# misc. globals
#
my $i;			# loop iteration counter, used for debugging only
my $lasttm;		# the last time(2) the mon loop started
my $pid_file_owner;	# set when creating pid file
my $tm;			# used in main loop

#
# authentication structure globals
#
my %AUTHCMDS;
my %NOAUTHCMDS;
my %AUTHTRAPS;

#
# PAM authentication globals (must not be lexically scoped)
#
use vars qw ( $PAM_username $PAM_password ) ;


#
# opstatus globals
#
my (%OPSTAT, %FAILURE, %SUCCESS, %WARNING);	# operational statuses
my ($TRAP_COLDSTART, $TRAP_WARMSTART,		# trap types
	$TRAP_LINKDOWN, $TRAP_LINKUP,
	$TRAP_AUTHFAIL, $TRAP_EGPNEIGHBORLOSS,
	$TRAP_ENTERPRISE, $TRAP_HEARTBEAT);

my ($STAT_FAIL, $STAT_OK, $STAT_COLDSTART,	# _op_status values
	$STAT_WARMSTART, $STAT_LINKDOWN,
	$STAT_UNKNOWN, $STAT_TIMEOUT,
	$STAT_UNTESTED, $STAT_DEPEND, $STAT_WARN);

my ($FL_MONITOR, $FL_UPALERT,			# alert type flags
	$FL_TRAP, $FL_TRAPTIMEOUT,
	$FL_STARTUPALERT, $FL_TEST, $FL_REDISTRIBUTE,
        $FL_ACKALERT, $FL_DISABLEALERT);

my $TRAP_PDU;
my (%ALERTHASH, %MONITORHASH);			# hash of pathnames for
						# alerts/monitors
my $PROT_VERSION;
my $START_TIME;					# time(2) server started
my $TRAP_PRO_VERSION;				# trap protocol version
my $DEP_EVAL_SANDBOX;				# perl environment for
						# dep evals

#
# argument parsing
#
my $getopt_result = GetOptions(\%opt,
			       qw/
				  A|authfile=s
				  B|cfbasedir=s
				  D|statedir=s
				  L|logdir=s
				  M|m4:s
				  O|syslogfacility=s
				  P|pidfile=s
				  S|stopped
				  a|alertdir=s
				  b|basedir=s
				  c|configfile=s
				  d|debug+
				  f|fork
				  h|help
				  i|sleep=i
				  k|maxkeep=i
				  l|loadstate:s
				  m|maxprocs=i
				  p|port=i
				  r|randstart=s
				  s|scriptdir=s
				  t|trapport=i
				  v|version
				  /);

if (!$getopt_result) {
  usage();
  exit;
}

#
# these two things can be taken care of without
# initializing things further
#
if ($opt{"v"}) {
    print "$RCSID\n$RELEASE\n";
    exit;
}

if ($opt{"h"}) {
    usage();
    exit;
}

if ($opt{"d"})
{
    eval 'require Data::Dumper;';

    if ($@ ne "")
    {
    	die "error: $@\n";
    }
}

if ($^O eq "linux" || $^O =~ /^(open|free|net)bsd$/ || $^O eq "aix")
{
    Sys::Syslog::setlogsock ('unix');
}

elsif ($^O eq "solaris")
{
    Sys::Syslog::setlogsock ('stream');
}

openlog ("mon", "cons,pid", $CF{"SYSLOG_FACILITY"});

#
# definitions
#
die "basedir $opt{b} does not exist\n" if ($opt{"b"} && ! -d $opt{"b"});

init_globals();
init_cf_globals();

syslog_die ("config file $CF{CF} does not exist") if (! -f $CF{"CF"});

#
# read config file
#
if ((my $err = read_cf ($CF{"CF"}, 1)) ne "") {
    syslog_die ("$err");
}

closelog;

openlog ("mon", "cons,pid", $CF{"SYSLOG_FACILITY"});

#
# cmdline args override config file
#
$CF{"ALERTDIR"}  = $opt{"a"} if ($opt{"a"});
$CF{"BASEDIR"}   = $opt{"b"} if ($opt{"b"});
$CF{"AUTHFILE"}  = $opt{"A"} if ($opt{"A"});
$CF{"LOGDIR"}    = $opt{"L"} if ($opt{"L"});
$CF{"STATEDIR"}  = $opt{"D"} if ($opt{"D"});
$CF{"SCRIPTDIR"} = $opt{"s"} if ($opt{"s"});

$CF{"PIDFILE"}   = $opt{"P"} if defined($opt{"P"});	# allow empty pidfile
$CF{"MAX_KEEP"}  = $opt{"k"} if ($opt{"k"});
$CF{"MAXPROCS"}  = $opt{"m"} if ($opt{"m"});
$CF{"SERVPORT"}  = $opt{"p"} if ($opt{"p"});
$CF{"TRAPPORT"}  = $opt{"t"} if ($opt{"t"});

$SLEEPINT  = $opt{"i"} if ($opt{"i"});

if ($opt{"r"}) {
    syslog_die ("bad randstart value") if (!defined (dhmstos ($opt{"r"})));
    $CF{"RANDSTART"} = dhmstos($opt{"r"});
}

if ($opt{"S"}) {
    $STOPPED = 1;
    $STOPPED_TIME = time;
}


#
# do some path cleanups and
# build lookup tables for alerts and monitors
#
normalize_paths();
gen_scriptdir_hash();

if ($opt{"d"}) {
    debug_dir();
}

#
# load the auth control, bind, and listen
#
load_auth (1);
load_view_users(1);

#
# init client interface
#   %clients is an I/O structure, indexed by the fd of the client
#   $numclients is the number of clients currently connected
#   $iovec is fd_set for clients and traps
#
%clients = ();
$numclients = 0;
$iovec = '';
setup_server();

#
# fork and become a daemon
#
init_dtlog() if ($CF{"DTLOGGING"});
daemon() if ($opt{"f"});
if ($CF{"PIDFILE"} ne '' && open PID, ">$CF{PIDFILE}") {
    $pid_file_owner = $$;
    print PID "$pid_file_owner\n";
    close PID;
}
set_last_test ();

#
# randomize startup checks if asked to
#
randomize_startdelay() if ($CF{"RANDSTART"});

@last_alerts = ();
@last_failures = ();
readhistoricfile ();

$procs = 0;
$i=0;
$lasttm=time;
$fdset_rbits = $fdset_ebits = '';
%watch_disabled = ();

$SIG{HUP} = \&reset_server;
$SIG{INT} = \&handle_sigterm;		# for interactive debugging
$SIG{TERM} = \&handle_sigterm;
$SIG{PIPE} = 'IGNORE';

#
# load previously saved state
#
if (exists $opt{"l"}) {
    if ($opt{"l"}) {
	# If -l was given an argument (all, disabled, opstatus, etc...)
	# pass that to load_state
	load_state($opt{"l"});
    }else{
	# Otherwise default to old behavior of just loading disabled hosts/services/groups
	load_state("disabled");
    }
}



syslog ('info', "mon server started");

#
# startup alerts
#
do_startup_alerts();

#
# main monitoring loop
#
for (;;) {
debug (1, "$i" . ($STOPPED ? " (stopped)" : "") . "\n");
    $i++;
    $tm = time;

    #
    # step through the watch groups, decrementing and
    # handing expired timers
    #
    if (!$STOPPED) {
	if (defined $CF{"EXCLUDE_PERIOD"}
	    && $CF{"EXCLUDE_PERIOD"} ne "" &&
	    inPeriod (time, $CF{"EXCLUDE_PERIOD"})) {
	    debug (1, "not running monitors because of global exclude_period\n");
	} else {
	    foreach my $group (keys %watch) {
		foreach my $service (keys %{$watch{$group}}) {

		    my $sref = \%{$watch{$group}->{$service}};

		    my $t = $tm - $lasttm;
		    $t = 1 if ($t <= 0);

		    #
		    # trap timer
		    #
		    if ($sref->{"traptimeout"}) {
			$sref->{"_trap_timer"} -= $t;
			
			if ($sref->{"_trap_timer"} <= 0 && 
			    $tm - $sref->{"_last_trap"} > $sref->{"traptimeout"}) 
			  {
			      $sref->{"_trap_timer"} = $sref->{"traptimeout"};
			      handle_trap_timeout ($group, $service);
			  }
		    }

		    #
		    # trap duration timer
		    #
		    if (defined ($sref->{"_trap_duration_timer"})) {
			$sref->{"_trap_duration_timer"} -= $t;
			
			if ($sref->{"_trap_duration_timer"} <= 0) {
			    set_op_status ($group, $service, $STAT_OK);
			    undef $sref->{"_trap_duration_timer"};
			}
		    }

		    #
		    # polling monitor timer
		    #
		    if ($sref->{"interval"} && $sref->{"_timer"} <= 0 &&
			!$running{"$group/$service"})
		      {
			  if (!$CF{"MAXPROCS"} || $procs < $CF{"MAXPROCS"})
			    {
				if (defined $sref->{"exclude_period"} 
				    && $sref->{"exclude_period"} ne "" &&
				    inPeriod (time, $sref->{"exclude_period"}))
				  {
				      debug (1, "not running $group,$service because of exclude_period\n");
				  }

				elsif (($sref->{"dep_behavior"} eq "m" &&
					defined $sref->{"depend"} && $sref->{"depend"} ne "")
				       || (defined $sref->{"monitordepend"} && $sref->{"monitordepend"} ne "")) 
				  {
				      if (dep_ok ($sref, 'm'))
					{
					    run_monitor ($group, $service);
					}

				      else
					{
					    debug (1, "not running $group,$service because of depend\n");
					}
				  }

				else
				  {
				      run_monitor ($group, $service);
				  }
			    }

			  else
			    {
				syslog ('info', "throttled at $procs processes");
			    }
		      }

		    else
		      {
			  $sref->{"_timer"} -= $t;
			  if ($sref->{"_timer"} < 0)
			    {
				$sref->{"_timer"} = 0;
			    }
		      }
		}
	    }
	}
    }

    $lasttm = time;

    #
    # collect any output from subprocs
    #
    collect_output;

    #
    # clean up after exited processes, and trigger alerts
    #
    proc_cleanup;

    #
    # handle client, server, and trap I/O
    # this routine sleeps for $SLEEPINT if no I/O is ready
    #
    handle_io;
}

die "not reached";

END {
    unlink $CF{"PIDFILE"} if $$ == $pid_file_owner && $CF{"PIDFILE"} ne '';
}


##############################################################################

#
# startup alerts
#
sub do_startup_alerts {
    foreach my $group (keys %watch) {
    	foreach my $service (keys %{$watch{$group}}) {
	    do_alert ($group, $service, "", 0, $FL_STARTUPALERT);
	}
    }
}


#
# handle alert event, throttling the alert call if necessary
#
sub do_alert {
    my ($group, $service, $output, $retval, $flags) = @_;
    my (@groupargs, $last_alert, $alert);
    my ($sref, $range, @alerts);

debug (1, "do_alert flags=$flags\n");

    $sref = \%{$watch{$group}->{$service}};

    my $tmnow = time;

    if ($STOPPED) {
      syslog ("notice", "ignoring alert for $group,$service because the mon scheduler is stopped");
      return;
    }

    #
    # if redistribute it set, call it now
    #
    if ($sref->{"redistribute"} ne '') 
    {
        my ($fac, $args);
        ($fac, $args) = split (/\s+/, $sref->{"redistribute"}, 2);
        call_alert (
                    group       => $group,
                    service     => $service,
                    output      => $output,
                    retval      => $retval,
                    flags       => $flags | $FL_REDISTRIBUTE,

                    alert       => $fac,
                    args        => $args,
                   )
    }

    #
    # if the alarm is disabled, ignore it
    #
    if ((exists $watch_disabled{$group} && $watch_disabled{$group} == 1) 
	|| (defined $sref->{"disable"} && $sref->{"disable"} == 1))
    {
	syslog ("notice", "ignoring alert for $group,$service");
	return;
    }

    #
    # dependency check
    #
    if (!($flags & $FL_STARTUPALERT) &&
	!($flags & $FL_UPALERT) &&
	((defined $sref->{"depend"} && $sref->{"dep_behavior"} eq "a")
	 || (defined $sref->{"alertdepend"})))
    {
	if (!$sref->{"_depend_status"})
	{
	    debug (1, "alert for $group,$service supressed because of dep fail\n");
	    return;
	}
    }

    my ($summary) = split("\n", $output);
    $summary = "(NO SUMMARY)" if (!defined $summary || $summary =~ /^\s*$/m);
    my ($prevsumm) = split("\n", $sref->{"_failure_output"}) if (defined $sref->{"_failure_output"});
    $prevsumm = "(NO SUMMARY)" if (!defined $prevsumm || $prevsumm =~ /^\s*$/m);
    

    my $strippedsummary = $summary;
    $strippedsummary =~ s/\s//mg;
    my $strippedprevious = $prevsumm;
    $strippedprevious =~ s/\s//mg;
    # If the summary changed, un-acknowledge the service if 'unack_summary' is set
    if ($sref->{'_ack'} != 0 
	&& $sref->{'unack_summary'} == 1 
	&& $strippedsummary ne $strippedprevious
	&& !($flags & ($FL_UPALERT|$FL_ACKALERT|$FL_DISABLEALERT))) {
	print STDERR "Unacking $group/$service:\nSummary: X".$strippedsummary."X\nPrevious: X".$strippedprevious."X\n";
	$sref->{"_ack"} = 0;
	$sref->{"_ack_comment"} = "";
        $sref->{"_consec_failures"}=1;
        foreach my $period (keys %{$sref->{"periods"}})
          {
            $sref->{"periods"}->{$period}->{"_last_alert"} = 0;
#            $sref->{"periods"}->{$period}->{"_alert_sent"} = 0;
            $sref->{"periods"}->{$period}->{"_1stfailtime"} = 0;
            $sref->{"periods"}->{$period}->{"_failcount"} = 0;
          }
    }

    #
    # no alerts for ack'd failures, except for upalerts or summary changes
    # when observe_summary is set
    #
    if ($sref->{"_ack"} != 0 && !($flags & ($FL_UPALERT|$FL_ACKALERT|$FL_DISABLEALERT)))
    {
	syslog ("debug", "no alert for $group.$service" .
		" because of ack'd failure");
	return;
    }

    #
    # check each time period for pending alerts
    #
    foreach my $periodlabel (keys %{$sref->{"periods"}})
    {
	#
	# only send alerts that are in the proper period
	#
    	next if (!inPeriod ($tmnow, $sref->{"periods"}->{$periodlabel}->{"period"}));

    	my $pref = \%{$sref->{"periods"}->{$periodlabel}};

	#
	# skip upalerts/ackalerts not paired with down alerts
	# disable by setting "no_comp_alerts" in period section
	#
	if (!$pref->{"no_comp_alerts"} && ($flags & ($FL_UPALERT | $FL_ACKALERT)) && !$pref->{"_alert_sent"})
	{
	    syslog ('debug', "$group/$service/$periodlabel: Suppressing upalert since no down alert was sent.") if ($flags & $FL_UPALERT);
	    syslog ('debug', "$group/$service/$periodlabel: Suppressing ackalert since no down alert was sent.") if ($flags & $FL_ACKALERT);
	    next;
	}

        #
        # skip looping upalerts when "no_comp-alerts" set.
        #
        if ($pref->{"no_comp_alerts"} && ($flags & $FL_UPALERT) && ($pref->{"_no_comp_alerts_upalert_sent"}>0))
        {   
            next;
        }

	#
	# do this if we're not handling an upalert, startupalert, ackalert, or disablealert
	#
	if (!($flags & $FL_UPALERT) && !($flags & $FL_STARTUPALERT)  && !($flags & $FL_DISABLEALERT) && !($flags & $FL_ACKALERT))
	{
  	    #
	    # alert only when exit code matches
	    #

	    if (exists $pref->{"alertexitrange"}) {
		next if (!inRange($retval, $pref->{"alertexitrange"}));
	    }

	    #
	    # alert only numalerts
	    #
	    if ($pref->{"numalerts"} &&
	    	     $pref->{"_alert_sent"} >= $pref->{"numalerts"})
	    {
                syslog ('debug', "$group/$service/$periodlabel: Suppressing alert since numalerts is met.");
	    	next;
	    }

	    #
	    # only alert once every "alertevery" seconds, unless
	    # output from monitor is different or if strict alertevery
	    #
	    # strict and _ignore_summary are basically the same though
	    # strict short-circuits and overrides other settings and exists
	    # for compatibility with pre-1.1 configs
	    #
	    if	($pref->{"alertevery"} != 0 &&                                                                 # if alertevery is set and
		 ($tmnow - $pref->{"_last_alert"} < $pref->{"alertevery"}) &&                                  # we're within the time period and one of these:
		 (($pref->{"_alertevery_strict"}) ||                                                           # [ strict is set or
		  ($pref->{"_observe_detail"} && $sref->{"_failure_output"} eq $output) ||                     # observing detail and output hasn't changed or
		  (!$pref->{"_observe_detail"} && (!$pref->{"_ignore_summary"}) && ($prevsumm eq $summary)) || # not observing detail
		    											       # and not ignoring summary and summ hasn't changed or
		  ($pref->{"_ignore_summary"})))	                                                       # we're ignoring summary changes ]
	    {
                syslog ('debug', "$group/$service/$periodlabel: Suppressing alert for now due to alertevery.");
		next;
	    }

	    #
	    # alertafter NUM
	    #
	    if (defined $pref->{"alertafter_consec"} && ($sref->{"_consec_failures"} < $pref->{"alertafter_consec"}))
	    {
                syslog ('debug', "$group/$service/$periodlabel: Suppressing alert for now due to alertafter consecutive failures.");
	    	next;
	    }

	    #
	    # alertafter timeval
	    #
	    elsif ( (!defined ($pref->{"alertafter"})) && (defined ($pref->{"alertafterival"})) )
	    {
	    	$pref->{'_1stfailtime'} = $tmnow if $pref->{'_1stfailtime'} == 0;
		if ($tmnow - $pref->{'_1stfailtime'} <= $pref->{'alertafterival'})
		{
                    syslog ('debug', "$group/$service/$periodlabel: Suppressing alert for now due to alertafter numval.");
		    next;
		}
	    }

	    #
	    # alertafter NUM timeval
	    #
	    elsif (defined ($pref->{"alertafter"}))
	    {
		$pref->{"_failcount"}++;

		if ($tmnow - $pref->{'_1stfailtime'} <= $pref->{'alertafterival'} &&
		    $pref->{"_failcount"} < $pref->{"alertafter"})
		{
                    syslog ('debug', "$group/$service/$periodlabel: Suppressing alert for now due to alertafter num timeval.");
		    next;
		}

		#
		# start a new time interval
		#
		if ($tmnow - $pref->{'_1stfailtime'} > $pref->{'alertafterival'})
		{
		    $pref->{"_failcount"} = 1;
		}

		if ($pref->{"_failcount"} == 1)
		{
		    $pref->{"_1stfailtime"} = $tmnow;
		}

		if ($pref->{"_failcount"} < $pref->{"alertafter"})
		{
                    syslog ('debug', "$group/$service/$periodlabel: Suppressing alert for now due to alertafter num timeval.");
		    next;
		}
	    }
	}

	#
	# at this point, no alerts are blocked,
	# so send the alerts
	#

	#
	# trigger multiple alerts in this period
	#
	if ($flags & $FL_UPALERT)
	{
	    @alerts = @{$pref->{"upalerts"}};
	}
	elsif ($flags & $FL_STARTUPALERT)
	{
	    @alerts = @{$pref->{"startupalerts"}};
	}
	elsif ($flags & $FL_DISABLEALERT)
	{
	    @alerts = @{$pref->{"disablealerts"}};
	}
	elsif ($flags & $FL_ACKALERT)
	{
	    @alerts = @{$pref->{"ackalerts"}};
	}
	else
	{
	    @alerts = @{$pref->{"alerts"}};
	}

	my $called = 0;

	for (my $i=0;$i<@alerts;$i++)
	{
	    my ($range, $fac, $args);

	    if ($alerts[$i] =~ /^exit\s*=\s*((\d+|\d+-\d+))\s/i)
	    {
		$range=$1;
		next if (!inRange($retval, $range));
		($fac, $args) = (split (/\s+/, $alerts[$i], 3))[1,2];
	    }
	    else
	    {
		($fac, $args) = split (/\s+/, $alerts[$i], 2);
	    }

	    $called++ if (call_alert (
		    group	=> $group,
		    service	=> $service,
		    output	=> $output,
		    retval	=> $retval,
		    flags	=> $flags,

		    pref	=> $pref,
		    alert	=> $fac,
		    args	=> $args,
		)
	    );
	}

	#
	# reset _alert_sent if up alert was sent from a trap
	#
        if ($called)
        {
            if( (($FL_TRAP | $flags) && ($FL_UPALERT & $flags)) ) {
	        $pref->{"_alert_sent"} = 0;
                $pref->{"_last_alert"} = 0;
            }
            else {
                $pref->{"_alert_sent"}++;

                #
                # reset _no_comp_alerts_upalert_sent counter - when service will be
                # back up, upalert will be sent.
                #
                if ($pref->{"no_comp_alerts"}) {
                    $pref->{"_no_comp_alerts_upalert_sent"} = 0;
                }
            }

	    if ($pref->{"no_comp_alerts"} && ($flags & $FL_UPALERT)) {
		$pref->{"_no_comp_alerts_upalert_sent"}++;
	    }
        }
    }
}



#
# walk through the watch list and reset the time
# the service was last called
#
sub set_last_test {
    my ($i, $k, $t);
    $t = time;
    foreach $k (keys %watch)
    {
    	foreach my $service (keys %{$watch{$k}})
	{
	    $watch{$k}->{$service}->{"_timer"} = $watch{$k}->{$service}->{"interval"};
	}
    }

}


#
# parse configuration file
#
# build the following data structures:
#
# %group
#       each element of %group is an array of hostnames
#       group records are terminated by a blank line in the
#       configuration file
# %watch{"group"}->{"service"}->{"variable"} = value
# %alias
#
sub read_cf {
    my ($CF, $commit) = @_;
    my ($var, $watchgroup, $ingroup, $curgroup, $inwatch,
	$args, $hosts, %disabled, $h, $i,
	$inalias, $curalias, $inview, $curview);
    my ($sref, $pref);
    my ($service, $period);
    my ($authtype, @authtypes);
    my $line_num = 0;

    #
    # parse configuration file
    #
    if (exists($opt{"M"}) || $CF =~ /\.m4$/)
    {
        my $m4 = "m4";
	$m4 = $opt{"M"} if (defined($opt{"M"}));
	return "could not open m4 pipe of cf file: $CF: $!"
	    if (!open (CFG, "$m4 $CF |"));
    }

    else
    {
	return "could not open cf file: $CF: $!"
	    if (!open (CFG, $CF));
    }

    #
    # buffers to hold the new un-committed config
    #
    my %new_alias = ();
    my %new_views = ();
    my %new_CF = %CF;
    my %new_groups;
    my %new_watch;

    my %is_watch;

    my $servnum = 0;

    my $DEP_BEHAVIOR = "a";
    my $DEP_MEMORY = 0;
    my $UNACK_SUMMARY = 0;

    my $incomplete_line = 0;
    my $linepart = "";
    my $l = "";
    my $acc_line = "";

    for (;;)
    {
	#
	# read in a logical "line", which may span actual lines
	#
	do
	{
	    $line_num++;
	    last if (!defined ($linepart = <CFG>));
	    next if $linepart =~ /^\s*#/;

	    #
	    # accumulate multi-line lines (ones which are \-escaped)
	    #
	    if ($incomplete_line) { $linepart =~ s/^\s*//; }

	    if ($linepart =~ /^(.*)\\\s*$/)
	    {
		$incomplete_line = 1;
		$acc_line .= $1;
		chomp $acc_line;
		next;
	    }

	    else
	    {
		$acc_line .= $linepart;
	    }

	    $l = $acc_line;
	    $acc_line = "";

	    chomp $l;
	    $l =~ s/^\s*//;
	    $l =~ s/\s*$//;

	    $incomplete_line = 0;
	    $linepart = "";
	};

	#
	# global variables which can be overriden by the command line
	#
	if (!$inwatch && $l =~ /^(\w+) \s* = \s* (.*) \s*$/ix)
	{
	    if ($1 eq "alertdir") {
		$new_CF{"ALERTDIR"} = $2;

	    } elsif ($1 eq "basedir") {
		$new_CF{"BASEDIR"} = $2;
		$new_CF{"BASEDIR"} = "$PWD/$new_CF{BASEDIR}" if ($new_CF{"BASEDIR"} !~ m{^/});
		$new_CF{"BASEDIR"} =~ s{/$}{};

	    } elsif ($1 eq "cfbasedir") {
		$new_CF{"CFBASEDIR"} = $2;
		$new_CF{"CFBASEDIR"} = "$PWD/$new_CF{CFBASEDIR}" if ($new_CF{"CFBASEDIR"} !~ m{^/});
		$new_CF{"CFBASEDIR"} =~ s{/$}{};

	    } elsif ($1 eq "mondir") {
		$new_CF{"SCRIPTDIR"} = $2;

	    } elsif ($1 eq "logdir") {
		$new_CF{"LOGDIR"} = $2;

	    } elsif ($1 eq "histlength") {
		$new_CF{"MAX_KEEP"} = $2;

	    } elsif ($1 eq "serverport") {
		$new_CF{"SERVPORT"} = $2;

	    } elsif ($1 eq "trapport") {
		$new_CF{"TRAPPORT"} = $2;

	    } elsif ($1 eq "serverbind") {
	    	$new_CF{"SERVERBIND"} = $2;

	    } elsif ($1 eq "clientallow") {
		$new_CF{"CLIENTALLOW"}= $2;

	    } elsif ($1 eq "trapbind") {
	    	$new_CF{"TRAPBIND"} = $2;

	    } elsif ($1 eq "pidfile") {
		$new_CF{"PIDFILE"} = $2;

	    } elsif ($1 eq "randstart") {
		$new_CF{"RANDSTART"} = dhmstos($2);
		if (!defined ($new_CF{"RANDSTART"})) {
		    close (CFG);
		    return "cf error: bad value '$2' for randstart option (syntax: randstart = timeval), line $line_num";
		}

	    } elsif ($1 eq "maxprocs") {
		$new_CF{"MAXPROCS"} = $2;

	    } elsif ($1 eq "statedir") {
		$new_CF{"STATEDIR"} = $2;

	    } elsif ($1 eq "authfile") {
		$new_CF{"AUTHFILE"} = $2;
                if (! -r $new_CF{"AUTHFILE"}) {
                    close (CFG);
                    return "cf error: authfile '$2' does not exist or is not readable, line $line_num";
                }

	    } elsif ($1 eq "authtype") {
		$new_CF{"AUTHTYPE"} = $2;
		@authtypes = split(' ' , $new_CF{"AUTHTYPE"}) ;
		foreach $authtype (@authtypes) {
		    if ($authtype eq "pam") {
			eval 'use Authen::PAM qw(:constants);' ;
			if ($@ ne "") {
			    close (CFG);
			    return "cf error: could not use PAM authentication: $@";
			}
		    }
		}

	    } elsif ($1 eq "pamservice") {
		$new_CF{"PAMSERVICE"} = $2;

	    } elsif ($1 eq "userfile") {
		$new_CF{"USERFILE"} = $2;
                if (! -r $new_CF{"USERFILE"}) {
                    close (CFG);
                    return "cf error: userfile '$2' does not exist or is not readable, line $line_num";
                }

	    } elsif ($1 eq "historicfile") {
	    	$new_CF{"HISTORICFILE"} = $2;

	    } elsif ($1 eq "historictime") {
	    	$new_CF{"HISTORICTIME"} = dhmstos($2);
		if (!defined $new_CF{"HISTORICTIME"}) {
		    close (CFG);
		    return "cf error: bad value '$2' for historictime command (syntax: historictime = timeval), line $line_num";
		}

	    } elsif ($1 eq "cltimeout") {
		$new_CF{"CLIENT_TIMEOUT"} = dhmstos($2);
		if (!defined ($new_CF{"CLIENT_TIMEOUT"})) {
		    close (CFG);
		    return "cf error: bad value '$2' for cltimeout command (syntax: cltimeout = secs), line $line_num";
		}

	    } elsif ($1 eq "monerrfile") {
	    	$new_CF{"MONERRFILE"} = $2;

	    } elsif ($1 eq "dtlogfile") {
		$new_CF{"DTLOGFILE"} = $2;

	    } elsif ($1 eq "dtlogging") {
		$new_CF{"DTLOGGING"} = 0;
		if ($2 == 1 || $2 eq "yes" || $2 eq "true") {
		    $new_CF{"DTLOGGING"} = 1;
		}

	    } elsif ($1 eq "dep_recur_limit") {
	    	$new_CF{"DEP_RECUR_LIMIT"} = $2;

	    } elsif ($1 eq "dep_behavior") {
		if ($2 ne "m" && $2 ne "a" && $2 ne "hm") {
		    close (CFG);
		    return "cf error: unknown dependency behavior '$2', line $line_num";
		}
		$DEP_BEHAVIOR = $2;

	    } elsif ($1 eq "dep_memory") {
		my $memory = dhmstos($2);
		if (!defined $memory) {
		    close (CFG);
		    return "cf error: bad value '$2' for dep_memory option (syntax: dep_memory = timeval), line $line_num";
		}
		$DEP_MEMORY = $memory;

	    } elsif ($1 eq "unack_summary") {
		if (defined $2) {
		    if ($2 =~ /y(es)?/i) {
			$UNACK_SUMMARY = 1;
		    } elsif ($2 =~ /n(o)?/i) {
			$UNACK_SUMMARY = 0;
		    } elsif ($2 eq "0" || $2 eq "1") {
			$UNACK_SUMMARY = $2;
		    } else {
			return "cf error: invalid unack_summary value '$2' (syntax: unack_summary [0|1|y|yes|n|no])";
		    }
		} else {
		    $UNACK_SUMMARY = 1;
		}

	    } elsif ($1 eq "syslog_facility") {
	    	$new_CF{"SYSLOG_FACILITY"} = $2;

	    } elsif ($1 eq "startupalerts_on_reset") {
		if ($2 =~ /^1|yes|true|on$/i) {
		    $new_CF{"STARTUPALERTS_ON_RESET"} = 1;
		} else {
		    $new_CF{"STARTUPALERTS_ON_RESET"} = 0;
		}

	    } elsif ($1 eq "monremote") {
		$new_CF{"MONREMOTE"} = $2;
		
	    } elsif ($1 eq "exclude_period") {
		if (inPeriod (time, $2) == -1)
		  {
		      close (CFG);
		      return "cf error: malformed exclude_period '$2' (the specified time period is not valid as per Time::Period::inPeriod), line $line_num";
		  }
		$new_CF{"EXCLUDE_PERIOD"} = $2;
	    } else {
		close (CFG);
		return "cf error: unknown variable '$1', line $line_num";
	    }

	    next;
	}

	#
	# end of record
	#
	if ($l eq "")
	{
	    $ingroup    = 0;
	    $inalias	= 0;
	    $inwatch    = 0;
	    $period	= 0;
	    $inview     = 0;

	    $curgroup   = "";
	    $curalias	= "";
	    $watchgroup = "";

	    $servnum	= 0;
	    next;
	}

	#
	# hostgroup record
	#
	if ($l =~ /^hostgroup\s+([a-zA-Z0-9_.-]+)\s*(.*)/)
	{
	    $curgroup = $1;

	    $ingroup = 1;
	    $inview = 0;
	    $inalias = 0;
	    $inwatch = 0;
	    $period  = 0;


	    $hosts = $2;
	    %disabled = ();

	    foreach $h (grep (/^\*/, @{$groups{$curgroup}}))
	    {
		# We have to make $i = $h because $h is actually
		# a pointer to %groups and will modify it.
		$i = $h;
		$i =~ s/^\*//;
		$disabled{$i} = 1;
	    }

	    @{$new_groups{$curgroup}} = split(/\s+/, $hosts);

	    #
	    # keep hosts which were previously disabled
	    #
	    for ($i=0;$i<@{$new_groups{$curgroup}};$i++)
	    {
		$new_groups{$curgroup}[$i] = "*$new_groups{$curgroup}[$i]"
		    if ($disabled{$new_groups{$curgroup}[$i]});
	    }

	    next;
	}

	if ($ingroup)
	{
	    push (@{$new_groups{$curgroup}}, split(/\s+/, $l));

	    for ($i=0;$i<@{$new_groups{$curgroup}};$i++)
	    {
		$new_groups{$curgroup}[$i] = "*$new_groups{$curgroup}[$i]"
		    if ($disabled{$new_groups{$curgroup}[$i]});
	    }

	    next;
	}

	#
	# alias record
	#
	if ($l =~ /^alias\s+([a-zA-Z0-9_.-]+)\s*$/)
	{
	    $inalias = 1;
	    $inview = 0;
	    $ingroup = 0;
	    $inwatch = 0;
	    $period  = 0;

	    $curalias = $1;
	    next;
	}

	if ($inalias)
	{
	    if ($l =~ /\A(.*)\Z/)
	    {
		push (@{$new_alias{$curalias}}, $1);
		next;
	    }
	}

	#
	# view record
	#
	if ($l =~ /^view\s+([a-zA-Z0-9_.-]+)\s+(.*)$/)
	{
	    $inview = 1;
	    $inalias = 0;
	    $ingroup = 0;
	    $inwatch = 0;
	    $period  = 0;

	    $curview = $1;
            $new_views{$curview}={};

	    foreach (split(/\s+/, $2)) {
		$new_views{$curview}->{$_} = 1;
	    };
	    next;
	}
	
	if ($inview)
	{
	    foreach (split(/\s+/, $l)) {
		$new_views{$curview}->{$_} = 1;
	    };
	    next;
	}

	#
	# watch record
	#
	if ($l =~ /^watch\s+([a-zA-Z0-9_.-]+)\s*/)
	{
	    $watchgroup = $1;
	    $inwatch = 1;
	    $inview = 0;
	    $inalias = 0;
	    $ingroup = 0;
	    $period  = 0;

	    if (!defined ($new_groups{$watchgroup}))
	    {
		#
		# This hostgroup doesn't exist yet, we'll create it and warn
		#
	    	@{$new_groups{$watchgroup}} = ($watchgroup);
		print STDERR "Warning: watch group $watchgroup defined with no corresponding hostgroup.\n";
	    }
	    if ($new_watch{$watchgroup})
	    {
		close (CFG);
		return "cf error: watch '$watchgroup' already defined, line $line_num";
	    }

	    $curgroup   = "";
	    $service = "";

	    next;
	}

	if ($inwatch)
	{
	    #
	    # env variables
	    #
	    if ($l =~ /^([A-Z_][A-Z0-9_]*)=(.*)/)
	    {
		if ($service eq "") {
		    close (CFG);
		    return "cf error: environment variable defined without a service, line $line_num";
		}
		$new_watch{$watchgroup}->{$service}->{"ENV"}->{$1} = $2;

		next;
	    }

	    #
	    # non-env variables
	    #
	    else
	    {
		$l =~ /^(\w+)\s*(.*)$/;
		$var = $1;
		$args = $2;
	    }

	    #
	    # service entry
	    #
	    if ($var eq "service")
	    {
		$service = $args;

		if ($service !~ /^[a-zA-Z0-9_.-]+$/) {
		    close (CFG);
		    return "cf error: invalid service tag '$args', line $line_num";
		}

		elsif (exists $new_watch{$watchgroup}->{$service})
		{
		    close (CFG);
		    return "cf error: service $service already defined for watch group $watchgroup, line $line_num";
		}

		$period = 0;
		$sref = \%{$new_watch{$watchgroup}->{$service}};
		$sref->{"service"} = $args;
		$sref->{"interval"} = undef;
		$sref->{"randskew"} = 0;
                $sref->{"redistribute"} = "";
		$sref->{"dep_behavior"} = $DEP_BEHAVIOR;
		$sref->{"dep_memory"} = $DEP_MEMORY;
		$sref->{"exclude_period"} = "";
		$sref->{"exclude_hosts"} = {};
		$sref->{"_op_status"} = $STAT_UNTESTED;
		$sref->{"_last_op_status"} = $STAT_UNTESTED;
		$sref->{"_ack"} = 0;
		$sref->{"_ack_comment"} = '';
		$sref->{"unack_summary"} = $UNACK_SUMMARY;
		$sref->{"_consec_failures"} = 0;
		$sref->{"_failure_count"} = 0 if (!defined($sref->{"_failure_count"}));
		$sref->{"_start_of_monitor"} = time if (!defined($sref->{"_start_of_monitor"}));
		$sref->{"_alert_count"} = 0 if (!defined($sref->{"_alert_count"}));
		$sref->{"_last_failure"} = 0 if (!defined($sref->{"_last_failure"}));
		$sref->{"_last_success"} = 0 if (!defined($sref->{"_last_success"}));
		$sref->{"_last_trap"} = 0 if (!defined($sref->{"_last_trap"}));
		$sref->{"_last_traphost"} = '' if (!defined($sref->{"_last_traphost"}));
		$sref->{"_exitval"} = "undef" if (!defined($sref->{"_exitval"}));
		$sref->{"_last_check"} = undef;
		#
		# -1 for _monitor_duration means no monitor has been run yet
		# so there is no duration data available
		#
		$sref->{"_monitor_duration"} = -1;
		$sref->{"_monitor_running"} = 0;
		$sref->{"_depend_status"} = undef;
		$sref->{"failure_interval"} = undef;
		$sref->{"_old_interval"} = undef;
		next;
	    }

	    if ($service eq "")
	    {
		close (CFG);
		return "cf error: need to specify service in watch record, line $line_num";
	    }


	    #
	    # period definition
	    #
	    # for each service there can be one or more alert periods
	    # this is stored as an array of hashes named
	    #     %{$watch{$watchgroup}->{$service}->{"periods"}}
	    # each index for this hash is a unique tag for the period as
	    # defined by the user or named after the period (such as
	    # "wd {Mon-Fri} hr {7am-11pm}")
	    #
	    # the value of the hash is an array containing the list of alert commands
	    # and arguments, so
	    #
	    # @alerts = @{$watch{$watchgroup}->{$service}->{"periods"}->{"TAG"}}
	    #
	    if ($var eq "period")
	    {
		$period = 1;

		my $periodstr;

		if ($args =~ /^([a-z_]\w*) \s* : \s* (.*)$/ix)
		{
		    $periodstr = $1;
		    $args = $2;
		}

		else
		{
		    $periodstr = $args;
		}

		if (exists $sref->{"periods"}->{$periodstr})
		{
		    close (CFG);
		    return "cf error: period '$periodstr' already defined for watch group $watchgroup service $service, line $line_num";
		}

		$pref = \%{$sref->{"periods"}->{$periodstr}};

		if (inPeriod (time, $args) == -1)
		{
		    close (CFG);
		    return "cf error: malformed period '$args' (the specified time period is not valid as per Time::Period::inPeriod), line $line_num";
		}

		$pref->{"period"} = $args;
		$pref->{"alertevery"} = 0;
		$pref->{"numalerts"} = 0;
		$pref->{"_alert_sent"} = 0;
		$pref->{"no_comp_alerts"} = 0;
		$pref->{"_no_comp_alerts_upalert_sent"} = 0;
		@{$pref->{"alerts"}} = ();
		@{$pref->{"upalerts"}} = ();
		@{$pref->{"ackalerts"}} = ();
		@{$pref->{"disablealerts"}} = ();
		@{$pref->{"startupalerts"}} = ();
		next;
	    }

	    #
	    # period variables
	    #
	    if ($period)
	    {
		if ($var eq "alert")
		{
		    push @{$pref->{"alerts"}}, $args;
		}
		
		elsif ($var eq "ackalert")
		{
		    push @{$pref->{"ackalerts"}}, $args;
		}
		
		elsif ($var eq "disablealert")
		{
		    push @{$pref->{"disablealerts"}}, $args;
		}
		
		elsif ($var eq "upalert")
		{
		    $sref->{"_upalert"} = 1;
		    push @{$pref->{"upalerts"}}, $args;
		}

		elsif ($var eq "startupalert")
		{
		    push @{$pref->{"startupalerts"}}, $args;
		}

		elsif ($var eq "alertevery")
		{
		    $pref->{"_observe_detail"} = 0;
		    $pref->{"_alertevery_strict"} = 0;
		    $pref->{"_ignore_summary"} = 0;

		    if ($args =~ /(\S+) \s+ observe_detail \s*$/ix)
		    {
			$pref->{"_observe_detail"} = 1;
			$args = $1;
		    }

		    elsif ($args =~ /(\S+) \s+ ignore_summary \s*$/ix)
		    {
			$pref->{"_ignore_summary"} = 1;
			$args = $1;
		    }

		    #
		    # for backawards-compatibility with <= 0.38.21
		    #
		    elsif ($args =~ /(\S+) \s+ summary/ix)
		    {
			$args = $1;
		    }

		    #
		    # strict
		    #
		    elsif ($args =~ /(\S+) \s+ strict \s*$/ix)
		    {
			$pref->{"_alertevery_strict"} = 1;
		    	$args = $1;
		    }

		    if (!($args = dhmstos ($args))) {
			close (CFG);
			return "cf error: invalid time interval '$args' (syntax: alertevery {positive number}{smhd} [ strict | observe_detail | ignore_summary ]), line $line_num";
		    }

		    $pref->{"alertevery"} = $args;
		    next;
		}

		elsif ($var eq "alertafter")
		{
		    my ($p1, $p2);

		    #
		    # alertafter NUM
		    #
		    if ($args =~ /^(\d+)$/)
		    {
			$p1 = $1;
			$pref->{"alertafter_consec"} = $p1;
		    }

		    #
		    # alertafter timeval
		    #
		    elsif ($args =~ /^(\d+[hms])$/)
		    {
			$p1 = $1;
			if (!($p1 = dhmstos ($p1)))
			{
			    close (CFG);
			    return "cf error: invalid time interval '$args' (syntax: alertafter = [{positive integer}] [{positive number}{smhd}]), line $line_num";
			}

			$pref->{"alertafterival"} = $p1;
			$pref->{"_1stfailtime"} = 0;
		    }

		    #
		    # alertafter NUM timeval
		    #
		    elsif ($args =~ /(\d+)\s+(\d+[hms])$/)
		    {
			($p1, $p2) = ($1, $2);
			if (($p1 - 1) * $sref->{"interval"} >= dhmstos($p2))
			{
			    close (CFG);
			    return "cf error:  interval & alertafter not sensible. No alerts can be generated with those parameters, line $line_num";
			}
			$pref->{"alertafter"} = $p1;
			$pref->{"alertafterival"} = dhmstos ($p2);

			$pref->{"_1stfailtime"} = 0;
			$pref->{"_failcount"} = 0;
		    }

		    else
		    {
			close (CFG);
			return "cf error: invalid interval specification '$args', line $line_num";
		    }
		}

		elsif ($var eq "upalertafter")
		{
		    if (!($args = dhmstos ($args))) {
			close (CFG);
			return "cf error: invalid upalertafter specification '$args' (syntax: upalertafter = {positive number}{smhd}), line $line_num";
		    }

		    $pref->{"upalertafter"} = $args;
		}

		elsif ($var eq "numalerts")
		{
		    if ($args !~ /^\d+$/) {
			close (CFG);
			return "cf error: -numeric arg '$args' (syntax: numalerts = {positive integer}, line $line_num";
		    }
		    $pref->{"numalerts"} = $args;
		    next;
		}

		elsif ($var eq "no_comp_alerts")
		{
		    $pref->{"no_comp_alerts"} = 1;
		    next;
		}

		elsif ($var eq "alerts_dont_count")
		{
		    $pref->{"alerts_dont_count"} = 1;
		    next;
		}

		elsif ($var eq 'alertexitrange') {
		  if ($args !~ /^\s*(\d+|\d+-\d+)\s*$/) {
		    close (CFG);
		    return "cf error: invalid exit code range '$args', line $line_num";
		  }
		  $pref->{"alertexitrange"} = $args;
		}

		else
		{
		    close (CFG);
		    return "cf error: unknown syntax [$l], line $line_num";
		}
		
	    }

	    #
	    # non-period variables
	    #
	    elsif (!$period)
	    {
		if ($var eq "interval")
		{
		    if (!($args = dhmstos ($args))) {
			close (CFG);
			return "cf error: invalid time interval '$args' (syntax: interval = {positive number}{smhd}), line $line_num";
		    }
		}

		elsif ($var eq "failure_interval")
		{
		    if (!($args = dhmstos ($args))) {
			close (CFG);
			return "cf error: invalid interval '$args' (syntax: failure_interval = {positive number}{smhd}), line $line_num";
		    }
		}

		elsif ($var eq "monitor")
		{
		    # valid
		}

                elsif ($var eq "redistribute")
                {
                    # valid
                }

		elsif ($var eq "allow_empty_group")
		{
		    # valid
		}

		elsif ($var eq "description")
		{
		    # valid
		}

		elsif ($var eq "unack_summary")
		{
		    if (defined $args) {
			if ($args =~ /y(es)?/i) {
			    $args = 1;
			} elsif ($args =~ /n(o)?/i) {
			    $args = 0;
			}
			if ($args eq "0" || $args eq "1") {
			    $sref->{"unack_summary"} = $args;
			} else {
			    return "cf error: invalid unack_summary value '$args' (syntax: unack_summary [0|1|y|yes|n|no])";
			}
		    } else {
			$sref->{"unack_summary"} = 1;
		    }
		    next;
		}

		elsif ($var eq "traptimeout")
		{
		    if (!($args = dhmstos ($args))) {
			close (CFG);
			return "cf error: invalid traptimeout interval '$args' (syntax: traptimeout = {positive number}{smhd}), line $line_num";
		    }
		    $sref->{"_trap_timer"} = $args;
		}

		elsif ($var eq "trapduration")
		{
		    if (!($args = dhmstos ($args))) {
			close (CFG);
			return "cf error: invalid trapduration interval '$args' (syntax: trapduration = {positive number}{smhd}), line $line_num";
		    }
		}

		elsif ($var eq "randskew")
		{
		    if (!($args = dhmstos ($args))) {
			close (CFG);
			return "cf error: invalid randskew time interval '$args' (syntax: randskew = {positive number}{smhd}), line $line_num";
		    }
		}

		elsif ($var eq "dep_behavior")
		{
		    if ($args ne "m" && $args ne "a" && $args ne "hm")
		    {
			close (CFG);
			return "cf error: unknown dependency behavior '$args' (syntax: dep_behavior = {m|a}), line $line_num";
		    }
		}
 
		elsif ($var eq "dep_memory")
		{
		    my $timeval = dhmstos($args);
		    if (!$timeval) {
  		        close (CFG);
			return "cf error: bad value '$args' for dep_memory option (syntax: dep_memory = timeval), line $line_num";
		    }
		    $args = $timeval;
		}

		elsif ($var eq "depend")
		{
		    $args =~ s/SELF:/$watchgroup:/g;
		}

		elsif ($var eq "alertdepend")
		{
		    $args =~ s/SELF:/$watchgroup:/g;
		}

		elsif ($var eq "monitordepend")
		{
		    $args =~ s/SELF:/$watchgroup:/g;
		}

		elsif ($var eq "hostdepend")
		{
		    $args =~ s/SELF:/$watchgroup:/g;
		}

		elsif ($var eq "exclude_hosts")
		{
		    my $ex = {};
		    foreach my $h (split (/\s+/, $args))
		    {
			$ex->{$h} = 1;
		    }
		    $args = $ex;
		}

		elsif ($var eq "exclude_period")
		{
		    if (inPeriod (time, $args) == -1)
		    {
			close (CFG);
			return "cf error: malformed exclude_period '$args' (the specified time period is not valid as per Time::Period::inPeriod), line $line_num";
		    }
		}

		else
		{
		    close (CFG);
		    return "cf error: unknown syntax [$l], line $line_num";
		}

		$sref->{$var} = $args;
	    }

	    else
	    {
		close (CFG);
		return "cf error: unknown syntax outside of period section [$l], line $line_num";
	    }
	}

	next;
    }

    close (CFG) || return "Could not open pipe to m4 (check that m4 is properly installed and in your PATH): $!";

    #
    # Go through each defined hostgroup and check that there is a 
    #  watch associated with that hostgroup record.
    #
    # hostgroups without associated watches are not a violation of 
    #  mon config syntax, but it's usually not what you want.
    #
    for (keys(%new_watch)) { $is_watch{$_} = 1 };
    foreach $watchgroup ( keys (%new_groups) ) {
	print STDERR "Warning: hostgroup $watchgroup has no watch assigned to it!\n" unless $is_watch{$watchgroup};
    }

    #
    # no errors, commit new config if $commit was specified
    #
    return "" unless $commit;
    %views = %new_views;
    %alias = %new_alias;
    %groups = %new_groups;
    %watch = %new_watch;
    %CF = %new_CF;

    "";
}


#
# convert a string like "20m" into seconds
#
sub dhmstos {
    my ($str) = @_;
    my ($s);

    $str = lc ($str);

    if ($str =~ /^\s*(\d+(?:\.\d+)?)([dhms])\s*$/i) {
	if ($2 eq "m") {
	    $s = $1 * 60;
	} elsif ($2 eq "h") {
	    $s = $1 * 60 * 60;
	} elsif ($2 eq "d") {
	    $s = $1 * 60 * 60 * 24;
	} else {
	    $s = $1;
	}
    } else {
    	return undef;
    }
    $s;
}


#
# reset the state of the server on SIGHUP, and reread config
# file.
#
sub reset_server {
    my ($keepstate) = @_;

    #
    # reap children that may be running
    #
    foreach my $pid (keys %runningpid) {
	my ($group, $service) = split (/\//, $runningpid{$pid});
    	kill 15, $pid;
	waitpid ($pid, 0);
	syslog ('info', "reset killed child $pid, exit status $?");
	remove_proc ($pid);
    }

    $procs = 0;
    save_state ("all") if ($keepstate);
    syslog ('info', "resetting, and re-reading configuration $CF{CF}");

    if ((my $err = read_cf ($CF{"CF"}, 1)) ne "") {
    	syslog ('err', "error reading config file: $err");
	return undef;
    }

    normalize_paths;
    gen_scriptdir_hash;
    $lasttm=time; # the last time(2) the loop started
    $fdset_rbits = $fdset_ebits = '';
    set_last_test ();
    randomize_startdelay() if ($CF{"RANDSTART"});
    load_state ("all") if ($keepstate);
    if ($CF{"DTLOGGING"}) {
	init_dtlog();
    }

    readhistoricfile;

    if ($CF{"STARTUPALERTS_ON_RESET"}) {
    	do_startup_alerts;
    }

    return 1;
}


sub init_dtlog {
    my $t = time;

    return if (!$CF{"DTLOGGING"});

    if (!open (DTLOG, ">>$CF{DTLOGFILE}")) {
       syslog ('err', "could not append to $CF{DTLOGFILE}: $!");
       $CF{"DTLOGGING"} = 0;
    } else {
       $CF{"DTLOGGING"} = 1;
       print DTLOG <<EOF;
#
# downtime log start $t
# time back up, group, service, first failure, downtime, interval, summary
#
EOF
    	close (DTLOG);
    }
}


#
# remove a process from our state
#
sub remove_proc {
    my ($pid) = @_;

    return if (!defined $runningpid{$pid});

    vec ($fdset_rbits, fileno($fhandles{$runningpid{$pid}}), 1) = 0;
    close ($fhandles{$runningpid{$pid}});
    delete $fhandles{$runningpid{$pid}};
    delete $running{$runningpid{$pid}};
    delete $runningpid{$pid};
    $procs--;
}


#
# exit on SIGTERM
#
sub handle_sigterm {
    syslog ("info", "caught TERM signal, exiting");
    exit (1);
}


#
# set O_NONBLOCK and FD_CLOEXEC on the given filehandle
#
sub configure_filehandle {
    my ($fh) = @_;
    my ($fl);

    $fl = '';
    $fl = fcntl ($fh, F_GETFL, $fl)          || return;
    $fl |= O_NONBLOCK;
    fcntl ($fh, F_SETFL, $fl)          || return;

    $fl = fcntl ($fh, F_GETFD, 0)      || return;
    $fl |= FD_CLOEXEC;
    fcntl ($fh, F_SETFD, $fl)          || return;

    return 1;
}


#
# setup server
#
sub setup_server {
    my ($tcpproto, $udpproto, $fl);

    if (!defined ($tcpproto = getprotobyname ('tcp')))
    {
    	die_die ("err", "could not get protocol for tcp");
    }

    if (!defined ($udpproto = getprotobyname ('udp')))
    {
    	die_die ("err", "could not get protocol for tcp");
    }

    #
    # client server, such as moncmd
    #
    my $bindaddr;
    if (defined $CF{"SERVERBIND"})
    {
	if (!($bindaddr = gethostbyname ($CF{"SERVERBIND"})))
	{
	    die_die ("err", "error returned by gethostbyname for serverbind: $?");
	}
    }

    else
    {
    	$bindaddr = INADDR_ANY;
    }

    socket (SERVER, PF_INET, SOCK_STREAM, $tcpproto) ||
    	die_die ("err", "could not create TCP socket: $!");

    setsockopt (SERVER, SOL_SOCKET, SO_REUSEADDR, pack ("l", 1)) ||
    	die_die ("err", "could not setsockopt: $!");

    bind (SERVER, sockaddr_in ($CF{"SERVPORT"}, $bindaddr)) ||
    	die_die ("err", "could not bind TCP server port $CF{'SERVPORT'}: $!");

    listen (SERVER, SOMAXCONN);

    configure_filehandle (*SERVER) ||
    	die_die ("err", "could not configure TCP server port: $!");

    #
    # remote monitor traps
    #
    if (defined $CF{"TRAPBIND"})
    {
	if (!($bindaddr = gethostbyname ($CF{"TRAPBIND"})))
	{
	    die_die ("err", "error returned by gethostbyname for trapbind: $?");
	}
    }

    else
    {
    	$bindaddr = INADDR_ANY;
    }

    socket (TRAPSERVER, PF_INET, SOCK_DGRAM, $udpproto) ||
    	die_die ("err", "could not create UDP socket: $!");
    bind (TRAPSERVER, sockaddr_in ($CF{"TRAPPORT"}, $bindaddr)) ||
    	die_die ("err", "could not bind UDP server port: $!");
    configure_filehandle (*TRAPSERVER) ||
    	die_die ("err", "could not configure UDP trap port: $!");
}


#
# set up a client connection if necessary
#
sub client_accept {
    my ($rin, $rout, $n, $sock, $port, $addr, $fl);

    my $CLIENT = new FileHandle;

    if (!defined ($sock = accept ($CLIENT, SERVER))) {
    	syslog ('err', "accept returned error: $!");
	return;
    }

debug(1, "accepted client $CLIENT\n");
    my $fno = fileno ($CLIENT);

    #
    # set socket to nonblocking
    #
    if (!configure_filehandle ($CLIENT)) {
    	syslog ("err", "could not configure for client: $!");
	close ($CLIENT);
	return;
    }

    ($port, $addr) = unpack_sockaddr_in ($sock);
    my $clientip = inet_ntoa($addr);

    syslog ('info', "client connection from $clientip:$port");

    my @clientregex = split(' ', $CF{"CLIENTALLOW"});
    my $ipok= 0;

    foreach my $ippattern (@clientregex)
    {
	#
	# change all periods, except those preceded by [ or \, into \.
	#
	$ippattern=~ s/([^[\\])\./$1\\./g;

	if ($clientip =~ /^${ippattern}$/)
	{
	    $ipok= 1;
	    last;
	}
    }

    if (! $ipok)
    {
	syslog('notice', "closing unwanted client: $clientip");
	close($CLIENT);
	return;
    }

    select ($CLIENT);
    $|=1;
    select (STDOUT);

    $clients{$fno}->{"host"} = inet_ntoa($addr);
    $clients{$fno}->{"fhandle"} = $CLIENT;
    $clients{$fno}->{"user"} = undef;		# username if authenticated
    $clients{$fno}->{"timeout"} = $CF{"CLIENT_TIMEOUT"};
    $clients{$fno}->{"last_read"} = time;		# last time data was read
    $clients{$fno}->{"buf"} = '';
    $numclients++;
}


#
# do all pending client commands
#
sub client_dopending {
    my ($cl, $cmd, $l);

    foreach $cl (keys %clients) {
    	if ($clients{$cl}->{"buf"} =~ /^([^\r\n]*)[\r\n]+/s) {
	    $cmd = $1;
	    $l = length ($cmd);
	    $clients{$cl}->{"buf"} =~ s/^[^\r\n]*[\r\n]+//s;
	    client_command ($cl, $cmd);
	}
    }
}


#
# close a client connection
#
sub client_close {
    my ($cl, $reason) = @_;

    syslog ('info', "closing client $cl: $reason") if (defined $reason);
    die if !defined ($clients{$cl}->{"fhandle"});
    close ($clients{$cl}->{"fhandle"});
    delete $clients{$cl};
    vec ($iovec, $cl, 1) = 0;
    $numclients--;
}


#
# Handle a connection from a client
#
sub client_command {
    my ($cl, $l) = @_;
    my ($cmd, $args, $group, $service, $s, $sname, $stchanged);
    my ($var, $value, $msg, @l, $sock, $port, $addr, $sref, $auth, $fh);
    my ($user, $pass, @argsList, $comment);
    my ($authtype, @authtypes);
    my $is_auth = 0;    #flag for multiple auth types

    syslog ('info', "client command \"$l\"")
	if ($l !~ /^\s*login/i);

    $fh = $clients{$cl}->{"fhandle"};

    if ($l !~ /^(dump|login|disable|enable|quit|list|set|get|setview|getview|
		    stop|start|loadstate|savestate|reset|clear|checkauth|
		    reload|term|test|servertime|ack|version|protid)(\s+(.*))?$/ix) {
	sock_write ($fh, "520 invalid command\n");
	return;
    }
    ($cmd, $args) = ("\L$1", $3);

    $stchanged = 0;

    print STDERR "client command $cmd\nclient args $args\n";
    #
    # quit command
    #
    if ($cmd eq "quit") {
	sock_write ($fh, "220 quitting\n");
	client_close                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   