Logo Search packages:      
Sourcecode: nagios3 version File versions  Download package

events.c

/*****************************************************************************
 *
 * EVENTS.C - Timed event functions for Nagios
 *
 * Copyright (c) 1999-2008 Ethan Galstad (nagios@nagios.org)
 * Last Modified: 05-19-2008
 *
 * License:
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *****************************************************************************/

#include "../include/config.h"
#include "../include/common.h"
#include "../include/downtime.h"
#include "../include/comments.h"
#include "../include/statusdata.h"
#include "../include/nagios.h"
#include "../include/broker.h"
#include "../include/sretention.h"


extern char *config_file;

extern int      test_scheduling;

extern time_t   program_start;
extern time_t   event_start;
extern time_t   last_command_check;

extern int      sigshutdown;
extern int      sigrestart;

extern double   sleep_time;
extern int      interval_length;
extern int      service_inter_check_delay_method;
extern int      host_inter_check_delay_method;
extern int      service_interleave_factor_method;
extern int      max_host_check_spread;
extern int      max_service_check_spread;

extern int      command_check_interval;
extern int      check_reaper_interval;
extern int      service_freshness_check_interval;
extern int      host_freshness_check_interval;
extern int      auto_rescheduling_interval;
extern int      auto_rescheduling_window;

extern int      check_external_commands;
extern int      check_orphaned_services;
extern int      check_orphaned_hosts;
extern int      check_service_freshness;
extern int      check_host_freshness;
extern int      auto_reschedule_checks;

extern int      retain_state_information;
extern int      retention_update_interval;

extern int      max_parallel_service_checks;
extern int      currently_running_service_checks;

extern int      aggregate_status_updates;
extern int      status_update_interval;

extern int      log_rotation_method;

extern int      service_check_timeout;

extern int      execute_service_checks;
extern int      execute_host_checks;

extern int      child_processes_fork_twice;

extern int      time_change_threshold;

timed_event *event_list_low=NULL;
timed_event *event_list_low_tail=NULL;
timed_event *event_list_high=NULL;
timed_event *event_list_high_tail=NULL;

extern host     *host_list;
extern service  *service_list;

sched_info scheduling_info;



/******************************************************************/
/************ EVENT SCHEDULING/HANDLING FUNCTIONS *****************/
/******************************************************************/

/* initialize the event timing loop before we start monitoring */
void init_timing_loop(void){
      host *temp_host=NULL;
      service *temp_service=NULL;
      time_t current_time=0L;
      unsigned long interval_to_use=0L;
      int total_interleave_blocks=0;
      int current_interleave_block=1;
      int interleave_block_index=0;
      int mult_factor=0;
      int is_valid_time=0;
      time_t next_valid_time=0L;
      int schedule_check=0;
      double max_inter_check_delay=0.0;
      struct timeval tv[9];
      double runtime[9];


      log_debug_info(DEBUGL_FUNCTIONS,0,"init_timing_loop() start\n");

      /* get the time right now */
      time(&current_time);


      /******** GET BASIC HOST/SERVICE INFO  ********/

      scheduling_info.total_services=0;
      scheduling_info.total_scheduled_services=0;
      scheduling_info.total_hosts=0;
      scheduling_info.total_scheduled_hosts=0;
      scheduling_info.average_services_per_host=0.0;
      scheduling_info.average_scheduled_services_per_host=0.0;
      scheduling_info.average_service_execution_time=0.0;
      scheduling_info.service_check_interval_total=0;
      scheduling_info.average_service_inter_check_delay=0.0;
      scheduling_info.host_check_interval_total=0;
      scheduling_info.average_host_inter_check_delay=0.0;
            
      if(test_scheduling==TRUE)
            gettimeofday(&tv[0],NULL);

      /* get info on service checks to be scheduled */
      for(temp_service=service_list;temp_service!=NULL;temp_service=temp_service->next){

            schedule_check=TRUE;

            /* service has no check interval */
            if(temp_service->check_interval==0)
                  schedule_check=FALSE;

            /* active checks are disabled */
            if(temp_service->checks_enabled==FALSE)
                  schedule_check=FALSE;

            /* are there any valid times this service can be checked? */
            is_valid_time=check_time_against_period(current_time,temp_service->check_period_ptr);
            if(is_valid_time==ERROR){
                  get_next_valid_time(current_time,&next_valid_time,temp_service->check_period_ptr);
                  if(current_time==next_valid_time)
                        schedule_check=FALSE;
                    }

            if(schedule_check==TRUE){

                  scheduling_info.total_scheduled_services++;

                  /* used later in inter-check delay calculations */
                  scheduling_info.service_check_interval_total+=temp_service->check_interval;

                  /* calculate rolling average execution time (available from retained state information) */
                  scheduling_info.average_service_execution_time=(double)(((scheduling_info.average_service_execution_time*(scheduling_info.total_scheduled_services-1)) + temp_service->execution_time)/(double)scheduling_info.total_scheduled_services);
                    }
            else{
                  temp_service->should_be_scheduled=FALSE;

                  log_debug_info(DEBUGL_EVENTS,1,"Service '%s' on host '%s' should not be scheduled.\n",temp_service->description,temp_service->host_name);
                    }

            scheduling_info.total_services++;
              }

      if(test_scheduling==TRUE)
            gettimeofday(&tv[1],NULL);

      /* get info on host checks to be scheduled */
      for(temp_host=host_list;temp_host!=NULL;temp_host=temp_host->next){

            schedule_check=TRUE;

            /* host has no check interval */
            if(temp_host->check_interval==0)
                  schedule_check=FALSE;

            /* active checks are disabled */
            if(temp_host->checks_enabled==FALSE)
                  schedule_check=FALSE;

            /* are there any valid times this host can be checked? */
            is_valid_time=check_time_against_period(current_time,temp_host->check_period_ptr);
            if(is_valid_time==ERROR){
                  get_next_valid_time(current_time,&next_valid_time,temp_host->check_period_ptr);
                  if(current_time==next_valid_time)
                        schedule_check=FALSE;
                    }

            if(schedule_check==TRUE){

                  scheduling_info.total_scheduled_hosts++;

                  /* this is used later in inter-check delay calculations */
                  scheduling_info.host_check_interval_total+=temp_host->check_interval;
                    }
            else{
                  temp_host->should_be_scheduled=FALSE;

                  log_debug_info(DEBUGL_EVENTS,1,"Host '%s' should not be scheduled.\n",temp_host->name);
                    }

            scheduling_info.total_hosts++;
              }

      if(test_scheduling==TRUE)
            gettimeofday(&tv[2],NULL);

      scheduling_info.average_services_per_host=(double)((double)scheduling_info.total_services/(double)scheduling_info.total_hosts);
      scheduling_info.average_scheduled_services_per_host=(double)((double)scheduling_info.total_scheduled_services/(double)scheduling_info.total_hosts);

      /* adjust the check interval total to correspond to the interval length */
      scheduling_info.service_check_interval_total=(scheduling_info.service_check_interval_total*interval_length);

      /* calculate the average check interval for services */
      scheduling_info.average_service_check_interval=(double)((double)scheduling_info.service_check_interval_total/(double)scheduling_info.total_scheduled_services);


      /******** DETERMINE SERVICE SCHEDULING PARAMS  ********/

      log_debug_info(DEBUGL_EVENTS,2,"Determining service scheduling parameters...");

      /* default max service check spread (in minutes) */
      scheduling_info.max_service_check_spread=max_service_check_spread;

      /* how should we determine the service inter-check delay to use? */
      switch(service_inter_check_delay_method){

      case ICD_NONE:

            /* don't spread checks out - useful for testing parallelization code */
            scheduling_info.service_inter_check_delay=0.0;
            break;

      case ICD_DUMB:

            /* be dumb and just schedule checks 1 second apart */
            scheduling_info.service_inter_check_delay=1.0;
            break;
            
      case ICD_USER:

            /* the user specified a delay, so don't try to calculate one */
            break;

      case ICD_SMART:
      default:

            /* be smart and calculate the best delay to use to minimize local load... */
            if(scheduling_info.total_scheduled_services>0 && scheduling_info.service_check_interval_total>0){

                  /* calculate the average inter check delay (in seconds) needed to evenly space the service checks out */
                  scheduling_info.average_service_inter_check_delay=(double)(scheduling_info.average_service_check_interval/(double)scheduling_info.total_scheduled_services);

                  /* set the global inter check delay value */
                  scheduling_info.service_inter_check_delay=scheduling_info.average_service_inter_check_delay;

                  /* calculate max inter check delay and see if we should use that instead */
                  max_inter_check_delay=(double)((scheduling_info.max_service_check_spread*60.0)/(double)scheduling_info.total_scheduled_services);
                  if(scheduling_info.service_inter_check_delay>max_inter_check_delay)
                        scheduling_info.service_inter_check_delay=max_inter_check_delay;
                    }
            else
                  scheduling_info.service_inter_check_delay=0.0;

            log_debug_info(DEBUGL_EVENTS,1,"Total scheduled service checks:  %d\n",scheduling_info.total_scheduled_services);
            log_debug_info(DEBUGL_EVENTS,1,"Average service check interval:  %0.2f sec\n",scheduling_info.average_service_check_interval);
            log_debug_info(DEBUGL_EVENTS,1,"Service inter-check delay:       %0.2f sec\n",scheduling_info.service_inter_check_delay);
              }

      /* how should we determine the service interleave factor? */
      switch(service_interleave_factor_method){

      case ILF_USER:

            /* the user supplied a value, so don't do any calculation */
            break;

      case ILF_SMART:
      default:

            /* protect against a divide by zero problem - shouldn't happen, but just in case... */
            if(scheduling_info.total_hosts==0)
                  scheduling_info.total_hosts=1;

            scheduling_info.service_interleave_factor=(int)(ceil(scheduling_info.average_scheduled_services_per_host));

            log_debug_info(DEBUGL_EVENTS,1,"Total scheduled service checks: %d\n",scheduling_info.total_scheduled_services);
            log_debug_info(DEBUGL_EVENTS,1,"Total hosts:                    %d\n",scheduling_info.total_hosts);
            log_debug_info(DEBUGL_EVENTS,1,"Service Interleave factor:      %d\n",scheduling_info.service_interleave_factor);
              }

      /* calculate number of service interleave blocks */
      if(scheduling_info.service_interleave_factor==0)
            total_interleave_blocks=scheduling_info.total_scheduled_services;
      else
            total_interleave_blocks=(int)ceil((double)scheduling_info.total_scheduled_services/(double)scheduling_info.service_interleave_factor);

      scheduling_info.first_service_check=(time_t)0L;
      scheduling_info.last_service_check=(time_t)0L;

      log_debug_info(DEBUGL_EVENTS,1,"Total scheduled services: %d\n",scheduling_info.total_scheduled_services);
      log_debug_info(DEBUGL_EVENTS,1,"Service Interleave factor: %d\n",scheduling_info.service_interleave_factor);
      log_debug_info(DEBUGL_EVENTS,1,"Total service interleave blocks: %d\n",total_interleave_blocks);
      log_debug_info(DEBUGL_EVENTS,1,"Service inter-check delay: %2.1f\n",scheduling_info.service_inter_check_delay);
      

      if(test_scheduling==TRUE)
            gettimeofday(&tv[3],NULL);

      /******** SCHEDULE SERVICE CHECKS  ********/

      log_debug_info(DEBUGL_EVENTS,2,"Scheduling service checks...");

      /* determine check times for service checks (with interleaving to minimize remote load) */
      current_interleave_block=0;
      for(temp_service=service_list;temp_service!=NULL && scheduling_info.service_interleave_factor>0;){

            log_debug_info(DEBUGL_EVENTS,2,"Current Interleave Block: %d\n",current_interleave_block);

            for(interleave_block_index=0;interleave_block_index<scheduling_info.service_interleave_factor && temp_service!=NULL;temp_service=temp_service->next){

                  log_debug_info(DEBUGL_EVENTS,2,"Service '%s' on host '%s'\n",temp_service->description,temp_service->host_name);
                  /* skip this service if it shouldn't be scheduled */
                  if(temp_service->should_be_scheduled==FALSE){
                        log_debug_info(DEBUGL_EVENTS,2,"Service check should not be scheduled.\n");
                        continue;
                        }

                  /* skip services that are already scheduled for the future (from retention data), but reschedule ones that were supposed to happen while we weren't running... */
                  if(temp_service->next_check>current_time){
                        log_debug_info(DEBUGL_EVENTS,2,"Service is already scheduled to be checked in the future: %s\n",ctime(&temp_service->next_check));
                        continue;
                        }

                  /* interleave block index should only be increased when we find a schedulable service */
                  /* moved from for() loop 11/05/05 EG */
                  interleave_block_index++;

                  mult_factor=current_interleave_block+(interleave_block_index*total_interleave_blocks);

                  log_debug_info(DEBUGL_EVENTS,2,"CIB: %d, IBI: %d, TIB: %d, SIF: %d\n",current_interleave_block,interleave_block_index,total_interleave_blocks,scheduling_info.service_interleave_factor);
                  log_debug_info(DEBUGL_EVENTS,2,"Mult factor: %d\n",mult_factor);

                  /* set the preferred next check time for the service */
                  temp_service->next_check=(time_t)(current_time+(mult_factor*scheduling_info.service_inter_check_delay));

                  log_debug_info(DEBUGL_EVENTS,2,"Preferred Check Time: %lu --> %s",(unsigned long)temp_service->next_check,ctime(&temp_service->next_check));


                  /* make sure the service can actually be scheduled when we want */
                  is_valid_time=check_time_against_period(temp_service->next_check,temp_service->check_period_ptr);
                  if(is_valid_time==ERROR){
                        get_next_valid_time(temp_service->next_check,&next_valid_time,temp_service->check_period_ptr);
                        temp_service->next_check=next_valid_time;
                          }

                  log_debug_info(DEBUGL_EVENTS,2,"Actual Check Time: %lu --> %s",(unsigned long)temp_service->next_check,ctime(&temp_service->next_check));

                  if(scheduling_info.first_service_check==(time_t)0 || (temp_service->next_check<scheduling_info.first_service_check))
                        scheduling_info.first_service_check=temp_service->next_check;
                  if(temp_service->next_check > scheduling_info.last_service_check)
                        scheduling_info.last_service_check=temp_service->next_check;
                    }

            current_interleave_block++;
              }

      if(test_scheduling==TRUE)
            gettimeofday(&tv[4],NULL);

      /* add scheduled service checks to event queue */
      for(temp_service=service_list;temp_service!=NULL;temp_service=temp_service->next){

            /* skip most services that shouldn't be scheduled */
            if(temp_service->should_be_scheduled==FALSE){

                  /* passive checks are an exception if a forced check was scheduled before Nagios was restarted */
                  if(!(temp_service->checks_enabled==FALSE && temp_service->next_check!=(time_t)0L && (temp_service->check_options & CHECK_OPTION_FORCE_EXECUTION)))
                        continue;
                  }

            /* create a new service check event */
            schedule_new_event(EVENT_SERVICE_CHECK,FALSE,temp_service->next_check,FALSE,0,NULL,TRUE,(void *)temp_service,NULL,temp_service->check_options);
              }


      if(test_scheduling==TRUE)
            gettimeofday(&tv[5],NULL);

      /******** DETERMINE HOST SCHEDULING PARAMS  ********/

      log_debug_info(DEBUGL_EVENTS,2,"Determining host scheduling parameters...");

      scheduling_info.first_host_check=(time_t)0L;
      scheduling_info.last_host_check=(time_t)0L;

      /* default max host check spread (in minutes) */
      scheduling_info.max_host_check_spread=max_host_check_spread;

      /* how should we determine the host inter-check delay to use? */
      switch(host_inter_check_delay_method){

      case ICD_NONE:

            /* don't spread checks out */
            scheduling_info.host_inter_check_delay=0.0;
            break;

      case ICD_DUMB:

            /* be dumb and just schedule checks 1 second apart */
            scheduling_info.host_inter_check_delay=1.0;
            break;
            
      case ICD_USER:

            /* the user specified a delay, so don't try to calculate one */
            break;

      case ICD_SMART:
      default:

            /* be smart and calculate the best delay to use to minimize local load... */
            if(scheduling_info.total_scheduled_hosts>0 && scheduling_info.host_check_interval_total>0){

                  /* adjust the check interval total to correspond to the interval length */
                  scheduling_info.host_check_interval_total=(scheduling_info.host_check_interval_total*interval_length);

                  /* calculate the average check interval for hosts */
                  scheduling_info.average_host_check_interval=(double)((double)scheduling_info.host_check_interval_total/(double)scheduling_info.total_scheduled_hosts);

                  /* calculate the average inter check delay (in seconds) needed to evenly space the host checks out */
                  scheduling_info.average_host_inter_check_delay=(double)(scheduling_info.average_host_check_interval/(double)scheduling_info.total_scheduled_hosts);

                  /* set the global inter check delay value */
                  scheduling_info.host_inter_check_delay=scheduling_info.average_host_inter_check_delay;

                  /* calculate max inter check delay and see if we should use that instead */
                  max_inter_check_delay=(double)((scheduling_info.max_host_check_spread*60.0)/(double)scheduling_info.total_scheduled_hosts);
                  if(scheduling_info.host_inter_check_delay>max_inter_check_delay)
                        scheduling_info.host_inter_check_delay=max_inter_check_delay;
                    }
            else
                  scheduling_info.host_inter_check_delay=0.0;

            log_debug_info(DEBUGL_EVENTS,2,"Total scheduled host checks:  %d\n",scheduling_info.total_scheduled_hosts);
            log_debug_info(DEBUGL_EVENTS,2,"Host check interval total:    %lu\n",scheduling_info.host_check_interval_total);
            log_debug_info(DEBUGL_EVENTS,2,"Average host check interval:  %0.2f sec\n",scheduling_info.average_host_check_interval);
            log_debug_info(DEBUGL_EVENTS,2,"Host inter-check delay:       %0.2f sec\n",scheduling_info.host_inter_check_delay);
              }

      if(test_scheduling==TRUE)
            gettimeofday(&tv[6],NULL);


      /******** SCHEDULE HOST CHECKS  ********/

      log_debug_info(DEBUGL_EVENTS,2,"Scheduling host checks...");

      /* determine check times for host checks */
      mult_factor=0;
      for(temp_host=host_list;temp_host!=NULL;temp_host=temp_host->next){

            log_debug_info(DEBUGL_EVENTS,2,"Host '%s'\n",temp_host->name);

            /* skip hosts that shouldn't be scheduled */
            if(temp_host->should_be_scheduled==FALSE){
                  log_debug_info(DEBUGL_EVENTS,2,"Host check should not be scheduled.\n");
                  continue;
                  }

            /* skip hosts that are already scheduled for the future (from retention data), but reschedule ones that were supposed to be checked before we started */
            if(temp_host->next_check>current_time){
                  log_debug_info(DEBUGL_EVENTS,2,"Host is already scheduled to be checked in the future: %s\n",ctime(&temp_host->next_check));
                  continue;
                  }

            /* calculate preferred host check time */
            temp_host->next_check=(time_t)(current_time+(mult_factor*scheduling_info.host_inter_check_delay));

            log_debug_info(DEBUGL_EVENTS,2,"Preferred Check Time: %lu --> %s",(unsigned long)temp_host->next_check,ctime(&temp_host->next_check));

            /* make sure the host can actually be scheduled at this time */
            is_valid_time=check_time_against_period(temp_host->next_check,temp_host->check_period_ptr);
            if(is_valid_time==ERROR){
                  get_next_valid_time(temp_host->next_check,&next_valid_time,temp_host->check_period_ptr);
                  temp_host->next_check=next_valid_time;
                    }

            log_debug_info(DEBUGL_EVENTS,2,"Actual Check Time: %lu --> %s",(unsigned long)temp_host->next_check,ctime(&temp_host->next_check));

            if(scheduling_info.first_host_check==(time_t)0 || (temp_host->next_check<scheduling_info.first_host_check))
                  scheduling_info.first_host_check=temp_host->next_check;
            if(temp_host->next_check > scheduling_info.last_host_check)
                  scheduling_info.last_host_check=temp_host->next_check;

            mult_factor++;
              }
      
      if(test_scheduling==TRUE)
            gettimeofday(&tv[7],NULL);

      /* add scheduled host checks to event queue */
      for(temp_host=host_list;temp_host!=NULL;temp_host=temp_host->next){

            /* skip most hosts that shouldn't be scheduled */
            if(temp_host->should_be_scheduled==FALSE){

                  /* passive checks are an exception if a forced check was scheduled before Nagios was restarted */
                  if(!(temp_host->checks_enabled==FALSE && temp_host->next_check!=(time_t)0L && (temp_host->check_options & CHECK_OPTION_FORCE_EXECUTION)))
                        continue;
                  }

            /* schedule a new host check event */
            schedule_new_event(EVENT_HOST_CHECK,FALSE,temp_host->next_check,FALSE,0,NULL,TRUE,(void *)temp_host,NULL,temp_host->check_options);
              }

      if(test_scheduling==TRUE)
            gettimeofday(&tv[8],NULL);


      /******** SCHEDULE MISC EVENTS ********/

      /* add a host and service check rescheduling event */
      if(auto_reschedule_checks==TRUE)
            schedule_new_event(EVENT_RESCHEDULE_CHECKS,TRUE,current_time+auto_rescheduling_interval,TRUE,auto_rescheduling_interval,NULL,TRUE,NULL,NULL,0);

      /* add a check result reaper event */
      schedule_new_event(EVENT_CHECK_REAPER,TRUE,current_time+check_reaper_interval,TRUE,check_reaper_interval,NULL,TRUE,NULL,NULL,0);

      /* add an orphaned check event */
      if(check_orphaned_services==TRUE || check_orphaned_hosts==TRUE)
            schedule_new_event(EVENT_ORPHAN_CHECK,TRUE,current_time+DEFAULT_ORPHAN_CHECK_INTERVAL,TRUE,DEFAULT_ORPHAN_CHECK_INTERVAL,NULL,TRUE,NULL,NULL,0);

      /* add a service result "freshness" check event */
      if(check_service_freshness==TRUE)
            schedule_new_event(EVENT_SFRESHNESS_CHECK,TRUE,current_time+service_freshness_check_interval,TRUE,service_freshness_check_interval,NULL,TRUE,NULL,NULL,0);

      /* add a host result "freshness" check event */
      if(check_host_freshness==TRUE)
            schedule_new_event(EVENT_HFRESHNESS_CHECK,TRUE,current_time+host_freshness_check_interval,TRUE,host_freshness_check_interval,NULL,TRUE,NULL,NULL,0);

      /* add a status save event */
      if(aggregate_status_updates==TRUE)
            schedule_new_event(EVENT_STATUS_SAVE,TRUE,current_time+status_update_interval,TRUE,status_update_interval,NULL,TRUE,NULL,NULL,0);

      /* add an external command check event if needed */
      if(check_external_commands==TRUE){
            if(command_check_interval==-1)
                  interval_to_use=(unsigned long)60;
            else
                  interval_to_use=(unsigned long)command_check_interval;
            schedule_new_event(EVENT_COMMAND_CHECK,TRUE,current_time+interval_to_use,TRUE,interval_to_use,NULL,TRUE,NULL,NULL,0);
              }

      /* add a log rotation event if necessary */
      if(log_rotation_method!=LOG_ROTATION_NONE)
            schedule_new_event(EVENT_LOG_ROTATION,TRUE,get_next_log_rotation_time(),TRUE,0,get_next_log_rotation_time,TRUE,NULL,NULL,0);

      /* add a retention data save event if needed */
      if(retain_state_information==TRUE && retention_update_interval>0)
            schedule_new_event(EVENT_RETENTION_SAVE,TRUE,current_time+(retention_update_interval*60),TRUE,(retention_update_interval*60),NULL,TRUE,NULL,NULL,0);

      if(test_scheduling==TRUE){

            runtime[0]=(double)((double)(tv[1].tv_sec-tv[0].tv_sec)+(double)((tv[1].tv_usec-tv[0].tv_usec)/1000.0)/1000.0);
            runtime[1]=(double)((double)(tv[2].tv_sec-tv[1].tv_sec)+(double)((tv[2].tv_usec-tv[1].tv_usec)/1000.0)/1000.0);
            runtime[2]=(double)((double)(tv[3].tv_sec-tv[2].tv_sec)+(double)((tv[3].tv_usec-tv[2].tv_usec)/1000.0)/1000.0);
            runtime[3]=(double)((double)(tv[4].tv_sec-tv[3].tv_sec)+(double)((tv[4].tv_usec-tv[3].tv_usec)/1000.0)/1000.0);
            runtime[4]=(double)((double)(tv[5].tv_sec-tv[4].tv_sec)+(double)((tv[5].tv_usec-tv[4].tv_usec)/1000.0)/1000.0);
            runtime[5]=(double)((double)(tv[6].tv_sec-tv[5].tv_sec)+(double)((tv[6].tv_usec-tv[5].tv_usec)/1000.0)/1000.0);
            runtime[6]=(double)((double)(tv[7].tv_sec-tv[6].tv_sec)+(double)((tv[7].tv_usec-tv[6].tv_usec)/1000.0)/1000.0);
            runtime[7]=(double)((double)(tv[8].tv_sec-tv[7].tv_sec)+(double)((tv[8].tv_usec-tv[7].tv_usec)/1000.0)/1000.0);

            runtime[8]=(double)((double)(tv[8].tv_sec-tv[0].tv_sec)+(double)((tv[8].tv_usec-tv[0].tv_usec)/1000.0)/1000.0);

            printf("EVENT SCHEDULING TIMES\n");
            printf("-------------------------------------\n");
            printf("Get service info:        %.6lf sec\n",runtime[0]);
            printf("Get host info info:      %.6lf sec\n",runtime[1]);
            printf("Get service params:      %.6lf sec\n",runtime[2]);
            printf("Schedule service times:  %.6lf sec\n",runtime[3]);
            printf("Schedule service events: %.6lf sec\n",runtime[4]);
            printf("Get host params:         %.6lf sec\n",runtime[5]);
            printf("Schedule host times:     %.6lf sec\n",runtime[6]);
            printf("Schedule host events:    %.6lf sec\n",runtime[7]);
            printf("                         ============\n");
            printf("TOTAL:                   %.6lf sec\n",runtime[8]);
            printf("\n\n");
            }

      log_debug_info(DEBUGL_FUNCTIONS,0,"init_timing_loop() end\n");

      return;
        }



/* displays service check scheduling information */
void display_scheduling_info(void){
      float minimum_concurrent_checks1=0.0;
      float minimum_concurrent_checks2=0.0;
      float minimum_concurrent_checks=0.0;
      float max_reaper_interval=0.0;
      int suggestions=0;

      printf("Projected scheduling information for host and service checks\n");
      printf("is listed below.  This information assumes that you are going\n");
      printf("to start running Nagios with your current config files.\n\n");

      printf("HOST SCHEDULING INFORMATION\n");
      printf("---------------------------\n");
      printf("Total hosts:                     %d\n",scheduling_info.total_hosts);
      printf("Total scheduled hosts:           %d\n",scheduling_info.total_scheduled_hosts);

      printf("Host inter-check delay method:   ");
      if(host_inter_check_delay_method==ICD_NONE)
            printf("NONE\n");
      else if(host_inter_check_delay_method==ICD_DUMB)
            printf("DUMB\n");
      else if(host_inter_check_delay_method==ICD_SMART){
            printf("SMART\n");
            printf("Average host check interval:     %.2f sec\n",scheduling_info.average_host_check_interval);
              }
      else
            printf("USER-SUPPLIED VALUE\n");
      printf("Host inter-check delay:          %.2f sec\n",scheduling_info.host_inter_check_delay);
      printf("Max host check spread:           %d min\n",scheduling_info.max_host_check_spread);
      printf("First scheduled check:           %s",(scheduling_info.total_scheduled_hosts==0)?"N/A\n":ctime(&scheduling_info.first_host_check));
      printf("Last scheduled check:            %s",(scheduling_info.total_scheduled_hosts==0)?"N/A\n":ctime(&scheduling_info.last_host_check));
      printf("\n\n");

      printf("SERVICE SCHEDULING INFORMATION\n");
      printf("-------------------------------\n");
      printf("Total services:                     %d\n",scheduling_info.total_services);
      printf("Total scheduled services:           %d\n",scheduling_info.total_scheduled_services);

      printf("Service inter-check delay method:   ");
      if(service_inter_check_delay_method==ICD_NONE)
            printf("NONE\n");
      else if(service_inter_check_delay_method==ICD_DUMB)
            printf("DUMB\n");
      else if(service_inter_check_delay_method==ICD_SMART){
            printf("SMART\n");
            printf("Average service check interval:     %.2f sec\n",scheduling_info.average_service_check_interval);
              }
      else
            printf("USER-SUPPLIED VALUE\n");
      printf("Inter-check delay:                  %.2f sec\n",scheduling_info.service_inter_check_delay);

      printf("Interleave factor method:           %s\n",(service_interleave_factor_method==ILF_USER)?"USER-SUPPLIED VALUE":"SMART");
      if(service_interleave_factor_method==ILF_SMART)
            printf("Average services per host:          %.2f\n",scheduling_info.average_services_per_host);
      printf("Service interleave factor:          %d\n",scheduling_info.service_interleave_factor);

      printf("Max service check spread:           %d min\n",scheduling_info.max_service_check_spread);
      printf("First scheduled check:              %s",ctime(&scheduling_info.first_service_check));
      printf("Last scheduled check:               %s",ctime(&scheduling_info.last_service_check));
      printf("\n\n");

      printf("CHECK PROCESSING INFORMATION\n");
      printf("----------------------------\n");
      printf("Check result reaper interval:       %d sec\n",check_reaper_interval);
      printf("Max concurrent service checks:      ");
      if(max_parallel_service_checks==0)
            printf("Unlimited\n");
      else
            printf("%d\n",max_parallel_service_checks);
      printf("\n\n");

      printf("PERFORMANCE SUGGESTIONS\n");
      printf("-----------------------\n");


      /***** MAX REAPER INTERVAL RECOMMENDATION *****/

      /* assume a 100% (2x) check burst for check reaper */
      /* assume we want a max of 2k files in the result queue at any given time */
      max_reaper_interval=floor(2000*scheduling_info.service_inter_check_delay);
      if(max_reaper_interval<2.0)
            max_reaper_interval=2.0;
      if(max_reaper_interval>30.0)
            max_reaper_interval=30.0;
      if((int)max_reaper_interval<check_reaper_interval){
            printf("* Value for 'check_result_reaper_frequency' should be <= %d seconds\n",(int)max_reaper_interval);
            suggestions++;
              }
      if(check_reaper_interval<2){
            printf("* Value for 'check_result_reaper_frequency' should be >= 2 seconds\n");
            suggestions++;
              }

      /***** MINIMUM CONCURRENT CHECKS RECOMMENDATION *****/

      /* first method (old) - assume a 100% (2x) service check burst for max concurrent checks */
      if(scheduling_info.service_inter_check_delay==0.0)
            minimum_concurrent_checks1=ceil(check_reaper_interval*2.0);
      minimum_concurrent_checks1=ceil((check_reaper_interval*2.0)/scheduling_info.service_inter_check_delay);

      /* second method (new) - assume a 25% (1.25x) service check burst for max concurrent checks */
      minimum_concurrent_checks2=ceil((((double)scheduling_info.total_scheduled_services)/scheduling_info.average_service_check_interval) * 1.25 * check_reaper_interval * scheduling_info.average_service_execution_time);

      /* use max of computed values */
      if(minimum_concurrent_checks1>minimum_concurrent_checks2)
            minimum_concurrent_checks=minimum_concurrent_checks1;
      else
            minimum_concurrent_checks=minimum_concurrent_checks2;

      /* compare with configured value */
      if(((int)minimum_concurrent_checks > max_parallel_service_checks) && max_parallel_service_checks!=0){
            printf("* Value for 'max_concurrent_checks' option should be >= %d\n",(int)minimum_concurrent_checks);
            suggestions++;
              }

      if(suggestions==0)
            printf("I have no suggestions - things look okay.\n");

      printf("\n");

      return;
        }


/* schedule a new timed event */
int schedule_new_event(int event_type, int high_priority, time_t run_time, int recurring, unsigned long event_interval, void *timing_func, int compensate_for_time_change, void *event_data, void *event_args, int event_options){
      timed_event **event_list=NULL;
      timed_event **event_list_tail=NULL;
      timed_event *new_event=NULL;

      log_debug_info(DEBUGL_FUNCTIONS,0,"schedule_new_event()\n");

      if(high_priority==TRUE){
            event_list=&event_list_high;
            event_list_tail=&event_list_high_tail;
            }
      else{
            event_list=&event_list_low;
            event_list_tail=&event_list_low_tail;
            }

      new_event=(timed_event *)malloc(sizeof(timed_event));
      if(new_event!=NULL){
            new_event->event_type=event_type;
            new_event->event_data=event_data;
            new_event->event_args=event_args;
            new_event->event_options=event_options;
            new_event->run_time=run_time;
            new_event->recurring=recurring;
            new_event->event_interval=event_interval;
            new_event->timing_func=timing_func;
            new_event->compensate_for_time_change=compensate_for_time_change;
              }
      else
            return ERROR;

      /* add the event to the event list */
      add_event(new_event,event_list,event_list_tail);

      return OK;
        }


/* reschedule an event in order of execution time */
void reschedule_event(timed_event *event, timed_event **event_list, timed_event **event_list_tail){
      time_t current_time=0L;
      time_t (*timingfunc)(void);

      log_debug_info(DEBUGL_FUNCTIONS,0,"reschedule_event()\n");

      /* reschedule recurring events... */
      if(event->recurring==TRUE){

            /* use custom timing function */
            if(event->timing_func!=NULL){
                  timingfunc=event->timing_func;
                  event->run_time=(*timingfunc)();
                    }

            /* normal recurring events */
            else{
                  event->run_time=event->run_time+event->event_interval;
                  time(&current_time);
                  if(event->run_time<current_time)
                        event->run_time=current_time;
                    }
              }

      /* add the event to the event list */
      add_event(event,event_list,event_list_tail);

      return;
        }


/* add an event to list ordered by execution time */
void add_event(timed_event *event, timed_event **event_list, timed_event **event_list_tail){
      timed_event *temp_event=NULL;
      timed_event *first_event=NULL;

      log_debug_info(DEBUGL_FUNCTIONS,0,"add_event()\n");

      event->next=NULL;
      event->prev=NULL;

      first_event=*event_list;

      /* add the event to the head of the list if there are no other events */
      if(*event_list==NULL){
            *event_list=event;
            *event_list_tail=event;
            }

        /* add event to head of the list if it should be executed first */
      else if(event->run_time < first_event->run_time){
            event->prev=NULL;
            (*event_list)->prev=event;
              event->next=*event_list;
            *event_list=event;
              }

      /* else place the event according to next execution time */
      else{

            /* start from the end of the list, as new events are likely to be executed in the future, rather than now... */
            for(temp_event=*event_list_tail;temp_event!=NULL;temp_event=temp_event->prev){
                  if(event->run_time >= temp_event->run_time){
                        event->next=temp_event->next;
                        event->prev=temp_event;
                        temp_event->next=event;
                        if(event->next==NULL)
                              *event_list_tail=event;
                        else
                              event->next->prev=event;
                        break;
                          }
                  else if(temp_event->prev==NULL){
                        temp_event->prev=event;
                        event->next=temp_event;
                        *event_list=event;
                        break;
                          }
                  }
              }

#ifdef USE_EVENT_BROKER
      /* send event data to broker */
      broker_timed_event(NEBTYPE_TIMEDEVENT_ADD,NEBFLAG_NONE,NEBATTR_NONE,event,NULL);
#endif

      return;
        }



/* remove an event from the queue */
void remove_event(timed_event *event, timed_event **event_list, timed_event **event_list_tail){
      timed_event *temp_event=NULL;

      log_debug_info(DEBUGL_FUNCTIONS,0,"remove_event()\n");

#ifdef USE_EVENT_BROKER
      /* send event data to broker */
      broker_timed_event(NEBTYPE_TIMEDEVENT_REMOVE,NEBFLAG_NONE,NEBATTR_NONE,event,NULL);
#endif

      if(*event_list==NULL)
            return;

      if(*event_list==event){
            event->prev=NULL;
            *event_list=event->next;
            if(*event_list==NULL)
                  *event_list_tail=NULL;
            }

      else{

            for(temp_event=*event_list;temp_event!=NULL;temp_event=temp_event->next){
                  if(temp_event->next==event){
                        temp_event->next=temp_event->next->next;
                        if(temp_event->next==NULL)
                              *event_list_tail=temp_event;
                        else
                              temp_event->next->prev=temp_event;
                        event->next=NULL;
                        event->prev=NULL;
                        break;
                          }
                    }
              }


      return;
        }



/* this is the main event handler loop */
int event_execution_loop(void){
      timed_event *temp_event=NULL;
      timed_event sleep_event;
      time_t last_time=0L;
      time_t current_time=0L;
      time_t last_status_update=0L;
      int run_event=TRUE;
      host *temp_host=NULL;
      service *temp_service=NULL;
      struct timespec delay;
      pid_t wait_result;


      log_debug_info(DEBUGL_FUNCTIONS,0,"event_execution_loop() start\n");

      time(&last_time);

      /* initialize fake "sleep" event */
      sleep_event.event_type=EVENT_SLEEP;
      sleep_event.run_time=last_time;
      sleep_event.recurring=FALSE;
      sleep_event.event_interval=0L;
      sleep_event.compensate_for_time_change=FALSE;
      sleep_event.timing_func=NULL;
      sleep_event.event_data=NULL;
      sleep_event.event_args=NULL;
      sleep_event.event_options=0;
      sleep_event.next=NULL;
      sleep_event.prev=NULL;

      while(1){

            /* see if we should exit or restart (a signal was encountered) */
            if(sigshutdown==TRUE || sigrestart==TRUE)
                  break;

            /* if we don't have any events to handle, exit */
            if(event_list_high==NULL && event_list_low==NULL){
                  log_debug_info(DEBUGL_EVENTS,0,"There aren't any events that need to be handled! Exiting...\n");
                  break;
                      }

            /* get the current time */
            time(&current_time);

            /* hey, wait a second...  we traveled back in time! */
            if(current_time<last_time)
                  compensate_for_system_time_change((unsigned long)last_time,(unsigned long)current_time);

            /* else if the time advanced over the specified threshold, try and compensate... */
            else if((current_time-last_time)>=time_change_threshold)
                  compensate_for_system_time_change((unsigned long)last_time,(unsigned long)current_time);

            /* keep track of the last time */
            last_time=current_time;

            log_debug_info(DEBUGL_EVENTS,1,"** Event Check Loop\n");
            if(event_list_high!=NULL)
                  log_debug_info(DEBUGL_EVENTS,1,"Next High Priority Event Time: %s",ctime(&event_list_high->run_time));
            else
                  log_debug_info(DEBUGL_EVENTS,1,"No high priority events are scheduled...\n");
            if(event_list_low!=NULL)
                  log_debug_info(DEBUGL_EVENTS,1,"Next Low Priority Event Time:  %s",ctime(&event_list_low->run_time));
            else
                  log_debug_info(DEBUGL_EVENTS,1,"No low priority events are scheduled...\n");
            log_debug_info(DEBUGL_EVENTS,1,"Current/Max Service Checks: %d/%d\n",currently_running_service_checks,max_parallel_service_checks);

            /* get rid of terminated child processes (zombies) */
            if(child_processes_fork_twice==FALSE){
                  while((wait_result=waitpid(-1,NULL,WNOHANG))>0);
                  }

            /* handle high priority events */
            if(event_list_high!=NULL && (current_time>=event_list_high->run_time)){

                  /* remove the first event from the timing loop */
                  temp_event=event_list_high;
                  event_list_high=event_list_high->next;
                  event_list_high->prev=NULL;

                  /* handle the event */
                  handle_timed_event(temp_event);

                  /* reschedule the event if necessary */
                  if(temp_event->recurring==TRUE)
                        reschedule_event(temp_event,&event_list_high,&event_list_high_tail);

                  /* else free memory associated with the event */
                  else
                        my_free(temp_event);
                    }

            /* handle low priority events */
            else if(event_list_low!=NULL && (current_time>=event_list_low->run_time)){

                  /* default action is to execute the event */
                  run_event=TRUE;

                  /* run a few checks before executing a service check... */
                  if(event_list_low->event_type==EVENT_SERVICE_CHECK){

                        temp_service=(service *)event_list_low->event_data;

                        /* don't run a service check if we're already maxed out on the number of parallel service checks...  */
                        if(max_parallel_service_checks!=0 && (currently_running_service_checks >= max_parallel_service_checks)){

                              log_debug_info(DEBUGL_EVENTS|DEBUGL_CHECKS,0,"**WARNING** Max concurrent service checks (%d) has been reached!  Delaying further service checks until previous checks are complete...\n",max_parallel_service_checks);

                              logit(NSLOG_RUNTIME_WARNING,TRUE,"\tMax concurrent service checks (%d) has been reached.  Delaying further checks until previous checks are complete...\n",max_parallel_service_checks);
                              run_event=FALSE;
                              }

                        /* don't run a service check if active checks are disabled */
                        if(execute_service_checks==FALSE){

                              log_debug_info(DEBUGL_EVENTS|DEBUGL_CHECKS,1,"We're not executing service checks right now, so we'll skip this event.\n");

                              run_event=FALSE;
                              }

                        /* forced checks override normal check logic */
                        if((temp_service->check_options & CHECK_OPTION_FORCE_EXECUTION))
                              run_event=TRUE;

                        /* reschedule the check if we can't run it now */
                        if(run_event==FALSE){

                              /* remove the service check from the event queue and reschedule it for a later time */
                              /* 12/20/05 since event was not executed, it needs to be remove()'ed to maintain sync with event broker modules */
                              temp_event=event_list_low;
                              remove_event(temp_event,&event_list_low,&event_list_low_tail);
                              /*
                              event_list_low=event_list_low->next;
                              */
                              if(temp_service->state_type==SOFT_STATE && temp_service->current_state!=STATE_OK)
                                    temp_service->next_check=(time_t)(temp_service->next_check+(temp_service->retry_interval*interval_length));
                              else
                                    temp_service->next_check=(time_t)(temp_service->next_check+(temp_service->check_interval*interval_length));
                              temp_event->run_time=temp_service->next_check;
                              reschedule_event(temp_event,&event_list_low,&event_list_low_tail);
                              update_service_status(temp_service,FALSE);

                              run_event=FALSE;
                                }

                          }

                  /* run a few checks before executing a host check... */
                  if(event_list_low->event_type==EVENT_HOST_CHECK){

                        temp_host=(host *)event_list_low->event_data;

                        /* don't run a host check if active checks are disabled */
                        if(execute_host_checks==FALSE){

                              log_debug_info(DEBUGL_EVENTS|DEBUGL_CHECKS,1,"We're not executing host checks right now, so we'll skip this event.\n");

                              run_event=FALSE;
                              }

                        /* forced checks override normal check logic */
                        if((temp_host->check_options & CHECK_OPTION_FORCE_EXECUTION))
                              run_event=TRUE;

                        /* reschedule the host check if we can't run it right now */
                        if(run_event==FALSE){

                              /* remove the host check from the event queue and reschedule it for a later time */
                              /* 12/20/05 since event was not executed, it needs to be remove()'ed to maintain sync with event broker modules */
                              temp_event=event_list_low;
                              remove_event(temp_event,&event_list_low,&event_list_low_tail);
                              /*
                              event_list_low=event_list_low->next;
                              */
                              if(temp_host->state_type==SOFT_STATE && temp_host->current_state!=STATE_OK)
                                    temp_host->next_check=(time_t)(temp_host->next_check+(temp_host->retry_interval*interval_length));
                              else
                                    temp_host->next_check=(time_t)(temp_host->next_check+(temp_host->check_interval*interval_length));

                              temp_event->run_time=temp_host->next_check;
                              reschedule_event(temp_event,&event_list_low,&event_list_low_tail);
                              update_host_status(temp_host,FALSE);

                              run_event=FALSE;
                                }
                          }

                  /* run the event */
                  if(run_event==TRUE){

                        /* remove the first event from the timing loop */
                        temp_event=event_list_low;
                        event_list_low=event_list_low->next;
                        /* we may have just removed the only item from the list */
                        if(event_list_low!=NULL)
                              event_list_low->prev=NULL;

                        log_debug_info(DEBUGL_EVENTS,1,"Running event...\n");

#                       /* handle the event */
                        handle_timed_event(temp_event);

                        /* reschedule the event if necessary */
                        if(temp_event->recurring==TRUE)
                              reschedule_event(temp_event,&event_list_low,&event_list_low_tail);

                        /* else free memory associated with the event */
                        else
                              my_free(temp_event);
                          }

                  /* wait a while so we don't hog the CPU... */
                  else{

                        log_debug_info(DEBUGL_EVENTS,2,"Did not execute scheduled event.  Idling for a bit...\n");

#ifdef USE_NANOSLEEP
                        delay.tv_sec=(time_t)sleep_time;
                        delay.tv_nsec=(long)((sleep_time-(double)delay.tv_sec)*1000000000);
                        nanosleep(&delay,NULL);
#else
                        delay.tv_sec=(time_t)sleep_time;
                        if(delay.tv_sec==0L)
                              delay.tv_sec=1;
                        delay.tv_nsec=0L;
                        sleep((unsigned int)delay.tv_sec);
#endif
                          }
                    }

            /* we don't have anything to do at this moment in time... */
            else if((event_list_high==NULL || (current_time<event_list_high->run_time)) && (event_list_low==NULL || (current_time<event_list_low->run_time))){

                  log_debug_info(DEBUGL_EVENTS,2,"No events to execute at the moment.  Idling for a bit...\n");

                  /* check for external commands if we're supposed to check as often as possible */
                  if(command_check_interval==-1)
                        check_for_external_commands();

                  /* set time to sleep so we don't hog the CPU... */
#ifdef USE_NANOSLEEP
                  delay.tv_sec=(time_t)sleep_time;
                  delay.tv_nsec=(long)((sleep_time-(double)delay.tv_sec)*1000000000);
#else
                  delay.tv_sec=(time_t)sleep_time;
                  if(delay.tv_sec==0L)
                        delay.tv_sec=1;
                  delay.tv_nsec=0L;
#endif

#ifdef USE_EVENT_BROKER
                  /* populate fake "sleep" event */
                  sleep_event.run_time=current_time;
                  sleep_event.event_data=(void *)&delay;

                  /* send event data to broker */
                  broker_timed_event(NEBTYPE_TIMEDEVENT_SLEEP,NEBFLAG_NONE,NEBATTR_NONE,&sleep_event,NULL);
#endif

                  /* wait a while so we don't hog the CPU... */
#ifdef USE_NANOSLEEP
                  nanosleep(&delay,NULL);
#else
                  sleep((unsigned int)delay.tv_sec);
#endif
                    }

            /* update status information occassionally - NagVis watches the NDOUtils DB to see if Nagios is alive */
            if((unsigned long)(current_time-last_status_update)>5){
                  last_status_update=current_time;
                  update_program_status(FALSE);
                  }
              }

      log_debug_info(DEBUGL_FUNCTIONS,0,"event_execution_loop() end\n");

      return OK;
      }



/* handles a timed event */
int handle_timed_event(timed_event *event){
      host *temp_host=NULL;
      service *temp_service=NULL;
      void (*userfunc)(void *);
      struct timeval tv;
      double latency=0.0;


      log_debug_info(DEBUGL_FUNCTIONS,0,"handle_timed_event() start\n");

#ifdef USE_EVENT_BROKER
      /* send event data to broker */
      broker_timed_event(NEBTYPE_TIMEDEVENT_EXECUTE,NEBFLAG_NONE,NEBATTR_NONE,event,NULL);
#endif

      log_debug_info(DEBUGL_EVENTS,0,"** Timed Event ** Type: %d, Run Time: %s",event->event_type,ctime(&event->run_time));
            
      /* how should we handle the event? */
      switch(event->event_type){

      case EVENT_SERVICE_CHECK:

            temp_service=(service *)event->event_data;

            /* get check latency */
            gettimeofday(&tv,NULL);
            latency=(double)((double)(tv.tv_sec-event->run_time)+(double)(tv.tv_usec/1000)/1000.0);

            log_debug_info(DEBUGL_EVENTS,0,"** Service Check Event ==> Host: '%s', Service: '%s', Options: %d, Latency: %f sec\n",temp_service->host_name,temp_service->description,event->event_options,latency);

            /* run the service check */
            temp_service=(service *)event->event_data;
            run_scheduled_service_check(temp_service,event->event_options,latency);
            break;

      case EVENT_HOST_CHECK:

            temp_host=(host *)event->event_data;

            /* get check latency */
            gettimeofday(&tv,NULL);
            latency=(double)((double)(tv.tv_sec-event->run_time)+(double)(tv.tv_usec/1000)/1000.0);

            log_debug_info(DEBUGL_EVENTS,0,"** Host Check Event ==> Host: '%s', Options: %d, Latency: %f sec\n",temp_host->name,event->event_options,latency);

            /* run the host check */
            temp_host=(host *)event->event_data;
            perform_scheduled_host_check(temp_host,event->event_options,latency);
            break;

      case EVENT_COMMAND_CHECK:

            log_debug_info(DEBUGL_EVENTS,0,"** External Command Check Event\n");

            /* check for external commands */
            check_for_external_commands();
            break;

      case EVENT_LOG_ROTATION:

            log_debug_info(DEBUGL_EVENTS,0,"** Log File Rotation Event\n");

            /* rotate the log file */
            rotate_log_file(event->run_time);
            break;

      case EVENT_PROGRAM_SHUTDOWN:
            
            log_debug_info(DEBUGL_EVENTS,0,"** Program Shutdown Event\n");

            /* set the shutdown flag */
            sigshutdown=TRUE;

            /* log the shutdown */
            logit(NSLOG_PROCESS_INFO,TRUE,"PROGRAM_SHUTDOWN event encountered, shutting down...\n");
            break;

      case EVENT_PROGRAM_RESTART:

            log_debug_info(DEBUGL_EVENTS,0,"** Program Restart Event\n");

            /* set the restart flag */
            sigrestart=TRUE;

            /* log the restart */
            logit(NSLOG_PROCESS_INFO,TRUE,"PROGRAM_RESTART event encountered, restarting...\n");
            break;

      case EVENT_CHECK_REAPER:

            log_debug_info(DEBUGL_EVENTS,0,"** Check Result Reaper\n");

            /* reap host and service check results */
            reap_check_results();
            break;

      case EVENT_ORPHAN_CHECK:

            log_debug_info(DEBUGL_EVENTS,0,"** Orphaned Host and Service Check Event\n");

            /* check for orphaned hosts and services */
            if(check_orphaned_hosts==TRUE)
                  check_for_orphaned_hosts();
            if(check_orphaned_services==TRUE)
                  check_for_orphaned_services();
            break;

      case EVENT_RETENTION_SAVE:

            log_debug_info(DEBUGL_EVENTS,0,"** Retention Data Save Event\n");

            /* save state retention data */
            save_state_information(TRUE);
            break;

      case EVENT_STATUS_SAVE:

            log_debug_info(DEBUGL_EVENTS,0,"** Status Data Save Event\n");

            /* save all status data (program, host, and service) */
            update_all_status_data();
            break;

      case EVENT_SCHEDULED_DOWNTIME:

            log_debug_info(DEBUGL_EVENTS,0,"** Scheduled Downtime Event\n");

            /* process scheduled downtime info */
            if(event->event_data){
                  handle_scheduled_downtime_by_id(*(unsigned long *)event->event_data);
                  free(event->event_data);
                  event->event_data=NULL;
                  }
            break;

      case EVENT_SFRESHNESS_CHECK:
            
            log_debug_info(DEBUGL_EVENTS,0,"** Service Result Freshness Check Event\n");

            /* check service result freshness */
            check_service_result_freshness();
            break;

      case EVENT_HFRESHNESS_CHECK:
            
            log_debug_info(DEBUGL_EVENTS,0,"** Host Result Freshness Check Event\n");

            /* check host result freshness */
            check_host_result_freshness();
            break;

      case EVENT_EXPIRE_DOWNTIME:

            log_debug_info(DEBUGL_EVENTS,0,"** Expire Downtime Event\n");

            /* check for expired scheduled downtime entries */
            check_for_expired_downtime();
            break;

      case EVENT_RESCHEDULE_CHECKS:

            /* adjust scheduling of host and service checks */
            log_debug_info(DEBUGL_EVENTS,0,"** Reschedule Checks Event\n");

            adjust_check_scheduling();
            break;

      case EVENT_EXPIRE_COMMENT:

            log_debug_info(DEBUGL_EVENTS,0,"** Expire Comment Event\n");

            /* check for expired comment */
            check_for_expired_comment((unsigned long)event->event_data);
            break;

      case EVENT_USER_FUNCTION:

            log_debug_info(DEBUGL_EVENTS,0,"** User Function Event\n");

            /* run a user-defined function */
            if(event->event_data!=NULL){
                  userfunc=event->event_data;
                  (*userfunc)(event->event_args);
                    }
            break;

      default:

            break;
              }

      log_debug_info(DEBUGL_FUNCTIONS,0,"handle_timed_event() end\n");

      return OK;
        }



/* adjusts scheduling of host and service checks */
void adjust_check_scheduling(void){
      timed_event *temp_event=NULL;
      service *temp_service=NULL;
      host *temp_host=NULL;
      double projected_host_check_overhead=0.1;
      double projected_service_check_overhead=0.1;
      time_t current_time=0L;
      time_t first_window_time=0L;
      time_t last_window_time=0L;
      time_t last_check_time=0L;
      time_t new_run_time=0L;
      int total_checks=0;
      int current_check=0;
      double inter_check_delay=0.0;
      double current_icd_offset=0.0;
      double total_check_exec_time=0.0;
      double last_check_exec_time=0.0;
      int adjust_scheduling=FALSE;
      double exec_time_factor=0.0;
      double current_exec_time=0.0;
      double current_exec_time_offset=0.0;
      double new_run_time_offset=0.0;


      log_debug_info(DEBUGL_FUNCTIONS,0,"adjust_check_scheduling() start\n");

      /* TODO:
         - Track host check overhead on a per-host basis
         - Figure out how to calculate service check overhead 
      */

      /* determine our adjustment window */
      time(&current_time);
      first_window_time=current_time;
      last_window_time=first_window_time+auto_rescheduling_window;

      /* get current scheduling data */
      for(temp_event=event_list_low;temp_event!=NULL;temp_event=temp_event->next){

            /* skip events outside of our current window */
            if(temp_event->run_time<=first_window_time)
                  continue;
            if(temp_event->run_time>last_window_time)
                  break;

            if(temp_event->event_type==EVENT_HOST_CHECK){

                  if((temp_host=(host *)temp_event->event_data)==NULL)
                        continue;

                  /* ignore forced checks */
                  if(temp_host->check_options & CHECK_OPTION_FORCE_EXECUTION)
                        continue;

                  /* does the last check "bump" into this one? */
                  if((unsigned long)(last_check_time+last_check_exec_time)>temp_event->run_time)
                        adjust_scheduling=TRUE;
      
                  last_check_time=temp_event->run_time;

                  /* calculate time needed to perform check */
                  /* NOTE: host check execution time is not taken into account, as scheduled host checks are run in parallel */
                  last_check_exec_time=projected_host_check_overhead;
                  total_check_exec_time+=last_check_exec_time;
                    }

            else if(temp_event->event_type==EVENT_SERVICE_CHECK){

                  if((temp_service=(service *)temp_event->event_data)==NULL)
                        continue;

                  /* ignore forced checks */
                  if(temp_service->check_options & CHECK_OPTION_FORCE_EXECUTION)
                        continue;

                  /* does the last check "bump" into this one? */
                  if((unsigned long)(last_check_time+last_check_exec_time)>temp_event->run_time)
                        adjust_scheduling=TRUE;
      
                  last_check_time=temp_event->run_time;

                  /* calculate time needed to perform check */
                  /* NOTE: service check execution time is not taken into account, as service checks are run in parallel */
                  last_check_exec_time=projected_service_check_overhead;
                  total_check_exec_time+=last_check_exec_time;
                    }

            else
                  continue;

            total_checks++;
              }


      /* nothing to do... */
      if(total_checks==0 || adjust_scheduling==FALSE){

            /*
            printf("\n\n");
            printf("NOTHING TO DO!\n");
            printf("# CHECKS:    %d\n",total_checks);
            printf("WINDOW TIME: %d\n",auto_rescheduling_window);
            printf("EXEC TIME:   %.3f\n",total_check_exec_time);
            */

            return;
              }

      if((unsigned long)total_check_exec_time>auto_rescheduling_window){
            inter_check_delay=0.0;
            exec_time_factor=(double)((double)auto_rescheduling_window/total_check_exec_time);
              }
      else{
            inter_check_delay=(double)((((double)auto_rescheduling_window)-total_check_exec_time)/(double)(total_checks*1.0));
            exec_time_factor=1.0;
              }

      /*
      printf("\n\n");
      printf("TOTAL CHECKS: %d\n",total_checks);
      printf("WINDOW TIME:  %d\n",auto_rescheduling_window);
      printf("EXEC TIME:    %.3f\n",total_check_exec_time);
      printf("ICD:          %.3f\n",inter_check_delay);
      printf("EXEC FACTOR:  %.3f\n",exec_time_factor);
      */

      /* adjust check scheduling */
      current_icd_offset=(inter_check_delay/2.0);
      for(temp_event=event_list_low;temp_event!=NULL;temp_event=temp_event->next){

            /* skip events outside of our current window */
            if(temp_event->run_time<=first_window_time)
                  continue;
            if(temp_event->run_time>last_window_time)
                  break;

            if(temp_event->event_type==EVENT_HOST_CHECK){

                  if((temp_host=(host *)temp_event->event_data)==NULL)
                        continue;

                  /* ignore forced checks */
                  if(temp_host->check_options & CHECK_OPTION_FORCE_EXECUTION)
                        continue;

                  current_exec_time=((temp_host->execution_time+projected_host_check_overhead)*exec_time_factor);
                    }

            else if(temp_event->event_type==EVENT_SERVICE_CHECK){

                  if((temp_service=(service *)temp_event->event_data)==NULL)
                        continue;

                  /* ignore forced checks */
                  if(temp_service->check_options & CHECK_OPTION_FORCE_EXECUTION)
                        continue;

                  /* NOTE: service check execution time is not taken into account, as service checks are run in parallel */
                  current_exec_time=(projected_service_check_overhead*exec_time_factor);
                    }

            else
                  continue;

            current_check++;
            new_run_time_offset=current_exec_time_offset+current_icd_offset;
            new_run_time=(time_t)(first_window_time+(unsigned long)new_run_time_offset);

            /*
            printf("  CURRENT CHECK #:      %d\n",current_check);
            printf("  CURRENT ICD OFFSET:   %.3f\n",current_icd_offset);
            printf("  CURRENT EXEC TIME:    %.3f\n",current_exec_time);
            printf("  CURRENT EXEC OFFSET:  %.3f\n",current_exec_time_offset);
            printf("  NEW RUN TIME:         %lu\n",new_run_time);
            */

            if(temp_event->event_type==EVENT_HOST_CHECK){
                  temp_event->run_time=new_run_time;
                  temp_host->next_check=new_run_time;
                  update_host_status(temp_host,FALSE);
                    }
            else{
                  temp_event->run_time=new_run_time;
                  temp_service->next_check=new_run_time;
                  update_service_status(temp_service,FALSE);
                    }

            current_icd_offset+=inter_check_delay;
            current_exec_time_offset+=current_exec_time;
              }

      /* resort event list (some events may be out of order at this point) */
      resort_event_list(&event_list_low,&event_list_low_tail);

      log_debug_info(DEBUGL_FUNCTIONS,0,"adjust_check_scheduling() end\n");

      return;
        }



/* attempts to compensate for a change in the system time */
void compensate_for_system_time_change(unsigned long last_time, unsigned long current_time){
      unsigned long time_difference=0L;
      timed_event *temp_event=NULL;
      service *temp_service=NULL;
      host *temp_host=NULL;
      int days=0;
      int hours=0;
      int minutes=0;
      int seconds=0;
      time_t (*timingfunc)(void);


      log_debug_info(DEBUGL_FUNCTIONS,0,"compensate_for_system_time_change() start\n");

      /* we moved back in time... */
      if(last_time>current_time){
            time_difference=last_time-current_time;
            get_time_breakdown(time_difference,&days,&hours,&minutes,&seconds);
            log_debug_info(DEBUGL_EVENTS,0,"Detected a backwards time change of %dd %dh %dm %ds.\n",days,hours,minutes,seconds);
            }

      /* we moved into the future... */
      else{
            time_difference=current_time-last_time;
            get_time_breakdown(time_difference,&days,&hours,&minutes,&seconds);
            log_debug_info(DEBUGL_EVENTS,0,"Detected a forwards time change of %dd %dh %dm %ds.\n",days,hours,minutes,seconds);
            }

      /* log the time change */
      logit(NSLOG_PROCESS_INFO | NSLOG_RUNTIME_WARNING,TRUE,"Warning: A system time change of %dd %dh %dm %ds (%s in time) has been detected.  Compensating...\n",days,hours,minutes,seconds,(last_time>current_time)?"backwards":"forwards");

      /* adjust the next run time for all high priority timed events */
      for(temp_event=event_list_high;temp_event!=NULL;temp_event=temp_event->next){

            /* skip special events that occur at specific times... */
            if(temp_event->compensate_for_time_change==FALSE)
                  continue;

            /* use custom timing function */
            if(temp_event->timing_func!=NULL){
                  timingfunc=temp_event->timing_func;
                  temp_event->run_time=(*timingfunc)();
                    }

            /* else use standard adjustment */
            else
                  adjust_timestamp_for_time_change(last_time,current_time,time_difference,&temp_event->run_time);
              }

      /* resort event list (some events may be out of order at this point) */
      resort_event_list(&event_list_high,&event_list_high_tail);

      /* adjust the next run time for all low priority timed events */
      for(temp_event=event_list_low;temp_event!=NULL;temp_event=temp_event->next){

            /* skip special events that occur at specific times... */
            if(temp_event->compensate_for_time_change==FALSE)
                  continue;

            /* use custom timing function */
            if(temp_event->timing_func!=NULL){
                  timingfunc=temp_event->timing_func;
                  temp_event->run_time=(*timingfunc)();
                    }

            /* else use standard adjustment */
            else
                  adjust_timestamp_for_time_change(last_time,current_time,time_difference,&temp_event->run_time);
              }

      /* resort event list (some events may be out of order at this point) */
      resort_event_list(&event_list_low,&event_list_low_tail);

      /* adjust service timestamps */
      for(temp_service=service_list;temp_service!=NULL;temp_service=temp_service->next){

            adjust_timestamp_for_time_change(last_time,current_time,time_difference,&temp_service->last_notification);
            adjust_timestamp_for_time_change(last_time,current_time,time_difference,&temp_service->last_check);
            adjust_timestamp_for_time_change(last_time,current_time,time_difference,&temp_service->next_check);
            adjust_timestamp_for_time_change(last_time,current_time,time_difference,&temp_service->last_state_change);
            adjust_timestamp_for_time_change(last_time,current_time,time_difference,&temp_service->last_hard_state_change);

            /* recalculate next re-notification time */
            temp_service->next_notification=get_next_service_notification_time(temp_service,temp_service->last_notification);

            /* update the status data */
            update_service_status(temp_service,FALSE);
              }

      /* adjust host timestamps */
      for(temp_host=host_list;temp_host!=NULL;temp_host=temp_host->next){

            adjust_timestamp_for_time_change(last_time,current_time,time_difference,&temp_host->last_host_notification);
            adjust_timestamp_for_time_change(last_time,current_time,time_difference,&temp_host->last_check);
            adjust_timestamp_for_time_change(last_time,current_time,time_difference,&temp_host->next_check);
            adjust_timestamp_for_time_change(last_time,current_time,time_difference,&temp_host->last_state_change);
            adjust_timestamp_for_time_change(last_time,current_time,time_difference,&temp_host->last_hard_state_change);
            adjust_timestamp_for_time_change(last_time,current_time,time_difference,&temp_host->last_state_history_update);

            /* recalculate next re-notification time */
            temp_host->next_host_notification=get_next_host_notification_time(temp_host,temp_host->last_host_notification);

            /* update the status data */
            update_host_status(temp_host,FALSE);
              }

      /* adjust program timestamps */
      adjust_timestamp_for_time_change(last_time,current_time,time_difference,&program_start);
      adjust_timestamp_for_time_change(last_time,current_time,time_difference,&event_start);
      adjust_timestamp_for_time_change(last_time,current_time,time_difference,&last_command_check);

      /* update the status data */
      update_program_status(FALSE);

      return;
        }



/* resorts an event list by event execution time - needed when compensating for system time changes */
void resort_event_list(timed_event **event_list, timed_event **event_list_tail){
      timed_event *temp_event_list=NULL;
      timed_event *temp_event=NULL;
      timed_event *next_event=NULL;

      log_debug_info(DEBUGL_FUNCTIONS,0,"resort_event_list()\n");

      /* move current event list to temp list */
      temp_event_list=*event_list;
      *event_list=NULL;

      /* move all events to the new event list */
      for(temp_event=temp_event_list;temp_event!=NULL;temp_event=next_event){
            next_event=temp_event->next;

            /* add the event to the newly created event list so it will be resorted */
            temp_event->next=NULL;
            temp_event->prev=NULL;
            add_event(temp_event,event_list,event_list_tail);
              }

      return;
        }



/* adjusts a timestamp variable in accordance with a system time change */
void adjust_timestamp_for_time_change(time_t last_time, time_t current_time, unsigned long time_difference, time_t *ts){

      log_debug_info(DEBUGL_FUNCTIONS,0,"adjust_timestamp_for_time_change()\n");

      /* we shouldn't do anything with epoch values */
      if(*ts==(time_t)0)
            return;

      /* we moved back in time... */
      if(last_time>current_time){

            /* we can't precede the UNIX epoch */
            if(time_difference>(unsigned long)*ts)
                  *ts=(time_t)0;
            else
                  *ts=(time_t)(*ts-time_difference);
              }

      /* we moved into the future... */
      else
            *ts=(time_t)(*ts+time_difference);

      return;
        }

Generated by  Doxygen 1.6.0   Back to index