/***************************************************************************** * * NOTIFICATIONS.C - Service and host notification functions for NetSaint * * Copyright (c) 1999-2001 Ethan Galstad (netsaint@netsaint.org) * Last Modified: 02-28-2001 * * License: * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * *****************************************************************************/ #include "../common/config.h" #include "../common/common.h" #include "../common/objects.h" #include "../common/statusdata.h" #include "netsaint.h" extern notification *notification_list; extern contact *contact_list; extern serviceescalation *serviceescalation_list; extern hostgroupescalation *hostgroupescalation_list; extern int program_mode; extern int interval_length; extern int log_notifications; extern int notification_timeout; extern char *macro_host_state; extern char *macro_service_state; extern char *macro_output; extern char *macro_notification_type; extern char *macro_notification_number; extern char *generic_summary; /******************************************************************/ /***************** SERVICE NOTIFICATION FUNCTIONS *****************/ /******************************************************************/ /* notify contacts about a service problem or recovery */ int service_notification(service *svc, char *ack_data){ host *temp_host; notification *temp_notification; time_t current_time; #ifdef DEBUG0 printf("service_notification() start\n"); #endif /* get the current time */ time(¤t_time); #ifdef DEBUG4 printf("\nSERVICE NOTIFICATION ATTEMPT: Service '%s' on host '%s'\n",svc->description,svc->host_name); printf("\tCurrent time: %s",ctime(¤t_time)); #endif /* find the host this service is associated with */ temp_host=find_host(svc->host_name,NULL); /* if we couldn't find the host, return an error */ if(temp_host==NULL){ #ifdef DEBUG4 printf("\tCouldn't find the host associated with this service, so we won't send a notification!\n"); #endif return ERROR; } /* check the viability of sending out a service notification */ if(check_service_notification_viability(svc,ack_data)==ERROR){ #ifdef DEBUG4 printf("\tSending out a notification for this service is not viable at this time.\n"); #endif return OK; } /* if this is just a normal notification and NOT a problem acknowledgement... */ if(ack_data==NULL){ /* increase the current notification number */ svc->current_notification_number++; #ifdef DEBUG4 printf("\tCurrent notification number: %d\n",svc->current_notification_number); #endif /* calculate the next acceptable re-notification time */ svc->next_notification=get_next_service_notification_time(svc,current_time); #ifdef DEBUG4 printf("\tCurrent Time: %s",ctime(¤t_time)); printf("\tNext acceptable notification time: %s",ctime(&svc->next_notification)); #endif } /* grab the macro variables */ clear_volatile_macros(); grab_host_macros(temp_host); grab_service_macros(svc); /* if this is an aknowledgement, modify the $OUTPUT$ macro to have it contain the comment that was entered by the user */ if(ack_data!=NULL){ if(macro_output!=NULL) free(macro_output); macro_output=(char *)malloc(strlen(ack_data)+1); if(macro_output!=NULL) strcpy(macro_output,ack_data); } /* set the notification type macro */ if(macro_notification_type!=NULL) free(macro_notification_type); macro_notification_type=(char *)malloc(MAX_NOTIFICATIONTYPE_LENGTH); if(macro_notification_type!=NULL){ if(ack_data!=NULL) strcpy(macro_notification_type,"ACKNOWLEDGEMENT"); else if(svc->current_state==STATE_OK) strcpy(macro_notification_type,"RECOVERY"); else strcpy(macro_notification_type,"PROBLEM"); } /* set the notification number macro */ if(macro_notification_number!=NULL) free(macro_notification_number); macro_notification_number=(char *)malloc(MAX_NOTIFICATIONNUMBER_LENGTH); if(macro_notification_number!=NULL){ snprintf(macro_notification_number,MAX_NOTIFICATIONNUMBER_LENGTH-1,"%d",svc->current_notification_number); macro_notification_number[MAX_NOTIFICATIONNUMBER_LENGTH-1]='\x0'; } /* create the contact notification list for this service */ create_notification_list_from_service(svc); /* notify each contact (duplicates have been removed) */ for(temp_notification=notification_list;temp_notification!=NULL;temp_notification=temp_notification->next){ /* grab the macro variables for this contact */ grab_contact_macros(temp_notification->contact); /* notify this contact */ notify_contact_of_service(temp_notification->contact,svc,ack_data); } /* free memory allocated to the notification list */ free_notification_list(); /* update the last notification time for this service if this was NOT an acknowledgement (this is needed for rescheduling later notifications) */ if(ack_data==NULL) svc->last_notification=current_time; /* update the status log with the service information */ update_service_status(svc,FALSE); #ifdef DEBUG0 printf("service_notification() end\n"); #endif return OK; } /* checks the viability of sending out a service alert (top level filters) */ int check_service_notification_viability(service *svc, char *ack_data){ host *temp_host; time_t current_time; time_t timeperiod_start; #ifdef DEBUG0 printf("check_service_notification_viability() start\n"); #endif /* get current time */ time(¤t_time); /* are we in active mode? */ if(program_mode!=ACTIVE_MODE){ #ifdef DEBUG4 printf("\tWe are in STANDBY mode, so service notifications (problems and acknowledments) will not be sent out!\n"); #endif return ERROR; } /* see if the service can have notifications sent out at this time */ if(check_time_against_period(current_time,svc->notification_period)==ERROR){ #ifdef DEBUG4 printf("\tThis service shouldn't have notifications sent out at this time!\n"); #endif /* calculate the next acceptable notification time, once the next valid time range arrives... */ if(ack_data==NULL){ get_next_valid_time(current_time,&timeperiod_start,svc->notification_period); /* looks like there are no valid notification times defined, so schedule the next one far into the future (one year)... */ if(timeperiod_start==(time_t)0) svc->next_notification=(time_t)(current_time+(60*60*24*365)); /* else use the next valid notification time */ else svc->next_notification=timeperiod_start; } return ERROR; } /* are notifications temporarily disabled for this service? */ if(svc->notifications_enabled==FALSE){ #ifdef DEBUG4 printf("\tNotifications are temporarily disabled for this service, so we won't send one out!\n"); #endif return ERROR; } /****************************************/ /*** SPECIAL CASE FOR ACKNOWLEGEMENTS ***/ /****************************************/ /* acknowledgements only have to pass three general filters, although they have another test of their own... */ if(ack_data!=NULL){ /* don't send an acknowledgement if there isn't a problem... */ if(svc->current_state==STATE_OK){ #ifdef DEBUG4 printf("\tThe service is currently OK, so we won't send an acknowledgement!\n"); #endif return ERROR; } /* acknowledgement viability test passed, so the notification can be sent out */ return OK; } /****************************************/ /*** NORMAL NOTIFICATIONS ***************/ /****************************************/ /* has this problem already been acknowledged? */ if(svc->problem_has_been_acknowledged==TRUE){ #ifdef DEBUG4 printf("\tThis service problem has already been acknowledged, so we won't send a notification out!\n"); #endif return ERROR; } /* do not send a recovery notification if we shouldn't (this flag was set by the check logic) */ if(svc->current_state==STATE_OK && svc->no_recovery_notification==TRUE){ #ifdef DEBUG4 printf("\tRecovery notifications should not be sent out for this service!\n"); #endif return ERROR; } /* don't send notifications if the host was down or unreachable at last service check */ if(svc->host_problem_at_last_check==TRUE){ #ifdef DEBUG4 printf("\tThe host associated with this service had problems at last check, so notifications won't be sent out!\n"); #endif return ERROR; } /* don't send notifications if the there were failed notification dependencies at the last check */ if(svc->dependency_failure_at_last_check==TRUE){ #ifdef DEBUG4 printf("\tNotification dependencies failed for this service, so notifications won't be sent out!\n"); #endif return ERROR; } /* find the host this service is associated with */ temp_host=find_host(svc->host_name,NULL); /* if we couldn't find the host, return an error */ if(temp_host==NULL){ #ifdef DEBUG4 printf("\tCouldn't find the host associated with this service, so we won't send a notification!\n"); #endif return ERROR; } /* if the host is down or unreachable, don't notify contacts about service failures */ if(temp_host->status!=HOST_UP){ #ifdef DEBUG4 printf("\tThe host is either down or unreachable, so we won't notify contacts about this service!\n"); #endif return ERROR; } /* see if we should notify about problems with this service */ if(svc->current_state==STATE_UNKNOWN && svc->notify_on_unknown==FALSE){ #ifdef DEBUG4 printf("\tWe shouldn't notify about UNKNOWN states for this service!\n"); #endif return ERROR; } if(svc->current_state==STATE_WARNING && svc->notify_on_warning==FALSE){ #ifdef DEBUG4 printf("\tWe shouldn't notify about WARNING states for this service!\n"); #endif return ERROR; } if(svc->current_state==STATE_CRITICAL && svc->notify_on_critical==FALSE){ #ifdef DEBUG4 printf("\tWe shouldn't notify about CRITICAL states for this service!\n"); #endif return ERROR; } if(svc->current_state==STATE_OK){ if(svc->notify_on_recovery==FALSE){ #ifdef DEBUG4 printf("\tWe shouldn't notify about RECOVERY states for this service!\n"); #endif return ERROR; } if(!((svc->has_been_unknown==TRUE && svc->notify_on_unknown==TRUE) || (svc->has_been_warning==TRUE && svc->notify_on_warning==TRUE) || (svc->has_been_critical==TRUE && svc->notify_on_critical==TRUE))){ #ifdef DEBUG4 printf("\tWe shouldn't notify about this recovery\n"); #endif return ERROR; } } /* dont notify contacts about this service problem again if the notification interval is set to 0 */ if(svc->current_state!=STATE_OK && svc->no_more_notifications==TRUE){ #ifdef DEBUG4 printf("\tWe shouldn't re-notify contacts about this service problem!\n"); #endif return ERROR; } /* don't notify if we haven't waited long enough since the last time (and the service is not marked as being volatile) */ if((current_time < svc->next_notification) && svc->is_volatile==FALSE){ #ifdef DEBUG4 printf("\tWe haven't waited long enough to re-notify contacts about this service!\n"); printf("\tNext valid notification time: %s",ctime(&svc->next_notification)); #endif return ERROR; } /* if this service is currently in a scheduled downtime period, don't send the notification */ if(svc->scheduled_downtime_depth>0){ #ifdef DEBUG4 printf("\tThis service is currently in a scheduled downtime, so we won't send notifications!\n"); #endif return ERROR; } /* if this host is currently in a scheduled downtime period, don't send the notification */ if(temp_host->scheduled_downtime_depth>0){ #ifdef DEBUG4 printf("\tThe host this service is associated with is currently in a scheduled downtime, so we won't send notifications!\n"); #endif return ERROR; } /* if this service is currently flapping, don't send the notification */ if(svc->is_flapping==TRUE){ #ifdef DEBUG4 printf("\tThis service is currently flapping, so we won't send notifications!\n"); #endif return ERROR; } #ifdef DEBUG0 printf("check_service_notification_viability() end\n"); #endif return OK; } /* check viability of sending out a service notification to a specific contact (contact-specific filters) */ int check_contact_service_notification_viability(contact *cntct,service *svc){ #ifdef DEBUG0 printf("check_contact_service_notification_viability() start\n"); #endif /* see if we should notify about problems with this service */ if(svc->current_state==STATE_UNKNOWN && cntct->notify_on_service_unknown==FALSE){ #ifdef DEBUG4 printf("\tWe shouldn't notify this contact about UNKNOWN service states!\n"); #endif return ERROR; } if(svc->current_state==STATE_WARNING && cntct->notify_on_service_warning==FALSE){ #ifdef DEBUG4 printf("\tWe shouldn't notify this contact about WARNING service states!\n"); #endif return ERROR; } if(svc->current_state==STATE_CRITICAL && cntct->notify_on_service_critical==FALSE){ #ifdef DEBUG4 printf("\tWe shouldn't notify this contact about CRITICAL service states!\n"); #endif return ERROR; } if(svc->current_state==STATE_OK){ if(cntct->notify_on_service_recovery==FALSE){ #ifdef DEBUG4 printf("\tWe shouldn't notify this contact about RECOVERY service states!\n"); #endif return ERROR; } if(!((svc->has_been_unknown==TRUE && cntct->notify_on_service_unknown==TRUE) || (svc->has_been_warning==TRUE && cntct->notify_on_service_warning==TRUE) || (svc->has_been_critical==TRUE && cntct->notify_on_service_critical==TRUE))){ #ifdef DEBUG4 printf("\tWe shouldn't notify about this recovery\n"); #endif return ERROR; } } #ifdef DEBUG0 printf("check_contact_service_notification_viability() end\n"); #endif return OK; } /* notify a specific contact about a service problem or recovery */ int notify_contact_of_service(contact *cntct,service *svc,char *ack_data){ commandsmember *temp_commandsmember; char temp_command_line[MAX_INPUT_BUFFER]; char raw_command_line[MAX_INPUT_BUFFER]; char command_line[MAX_INPUT_BUFFER]; char temp_buffer[MAX_INPUT_BUFFER]; int early_timeout=FALSE; #ifdef DEBUG0 printf("notify_contact_of_service() start\n"); #endif #ifdef DEBUG4 printf("\tNotify user %s\n",cntct->name); #endif /* see if the contact can be notified at this time */ if(check_time_against_period(time(NULL),cntct->service_notification_period)==ERROR){ #ifdef DEBUG4 printf("\tThis contact shouldn't be notified at this time!\n"); #endif return OK; } /* check viability of notifying this user */ if(ack_data==NULL && check_contact_service_notification_viability(cntct,svc)==ERROR) return OK; /* process all the notification commands this user has */ for(temp_commandsmember=cntct->service_notification_commands;temp_commandsmember!=NULL;temp_commandsmember=temp_commandsmember->next){ /* get the raw command line */ strncpy(temp_command_line,temp_commandsmember->command,sizeof(temp_command_line)-1); temp_command_line[sizeof(temp_command_line)-1]='\x0'; get_raw_command_line(temp_command_line,&raw_command_line[0],(int)sizeof(raw_command_line)); /* run the notification command */ if(strcmp(raw_command_line,"")){ #ifdef DEBUG4 printf("\tRaw Command: %s\n",raw_command_line); #endif /* replace macros in the command line */ process_macros(raw_command_line,&command_line[0],(int)sizeof(command_line)); #ifdef DEBUG4 printf("\tProcessed Command: %s\n",command_line); #endif /* log the notification to program log file */ if(log_notifications==TRUE){ if(ack_data==NULL) snprintf(temp_buffer,sizeof(temp_buffer),"SERVICE NOTIFICATION: %s;%s;%s;%s;%s;%s\n",cntct->name,svc->host_name,svc->description,macro_service_state,temp_command_line,macro_output); else snprintf(temp_buffer,sizeof(temp_buffer),"SERVICE NOTIFICATION: %s;%s;%s;ACKNOWLEDGEMENT (%s);%s;%s\n",cntct->name,svc->host_name,svc->description,macro_service_state,temp_command_line,macro_output); temp_buffer[sizeof(temp_buffer)-1]='\x0'; write_to_logs_and_console(temp_buffer,NSLOG_SERVICE_NOTIFICATION,FALSE); } /* run the command */ my_system(command_line,notification_timeout,&early_timeout,NULL,0); /* check to see if the notification command timed out */ if(early_timeout==TRUE){ snprintf(temp_buffer,sizeof(temp_buffer),"Warning: Contact '%s' service notification command '%s' timed out after %d seconds\n",cntct->name,temp_commandsmember->command,DEFAULT_NOTIFICATION_TIMEOUT); temp_buffer[sizeof(temp_buffer)-1]='\x0'; write_to_logs_and_console(temp_buffer,NSLOG_SERVICE_NOTIFICATION | NSLOG_RUNTIME_WARNING,TRUE); } } } #ifdef DEBUG0 printf("notify_contact_of_service() end\n"); #endif return OK; } /* checks to see if a service escalation entry is a match for the current service notification */ int is_valid_escalation_for_service_notification(service *svc,serviceescalation *se){ int notification_number; #ifdef DEBUG0 printf("is_valid_escalation_for_service_notification() start\n"); #endif /* if this is a recovery, really we check for who got notified about a previous problem */ if(svc->current_state==STATE_OK) notification_number=svc->current_notification_number-1; else notification_number=svc->current_notification_number; /* this entry if it is not for this service */ if(strcmp(svc->host_name,se->host_name) || strcmp(svc->description,se->description)) return FALSE; /* skip this escalation if it happens later */ if(se->first_notification > notification_number) return FALSE; /* skip this escalation if it has already passed */ if(se->last_notification!=0 && se->last_notification < notification_number) return FALSE; #ifdef DEBUG0 printf("is_valid_escalation_for_service_notification() end\n"); #endif return TRUE; } /* checks to see whether a service notification should be escalation */ int should_service_notification_be_escalated(service *svc){ serviceescalation *temp_se; #ifdef DEBUG0 printf("should_service_notification_be_escalated() start\n"); #endif /* search the service escalation list */ for(temp_se=serviceescalation_list;temp_se!=NULL;temp_se=temp_se->next){ /* we found a matching entry, so escalate this notification! */ if(is_valid_escalation_for_service_notification(svc,temp_se)==TRUE){ #ifdef DEBUG4 printf("\tService notification WILL BE escalated\n"); #endif return TRUE; } } #ifdef DEBUG4 printf("\tService notification will NOT be escalated\n"); #endif #ifdef DEBUG0 printf("should_service_notification_be_escalated() end\n"); #endif return FALSE; } /* given a service, create a list of contacts to be notified, removing duplicates */ int create_notification_list_from_service(service *svc){ serviceescalation *temp_se; contactgroupsmember *temp_group; contactgroup *temp_contactgroup; contact *temp_contact; #ifdef DEBUG0 printf("create_notification_list_from_service() end\n"); #endif /* should this notification be escalated? */ if(should_service_notification_be_escalated(svc)==TRUE){ /* search all the escalation entries for valid matches */ for(temp_se=serviceescalation_list;temp_se!=NULL;temp_se=temp_se->next){ /* skip this entry if it isn't appropriate */ if(is_valid_escalation_for_service_notification(svc,temp_se)==FALSE) continue; /* find each contact group in this escalation entry */ for(temp_group=temp_se->contact_groups;temp_group!=NULL;temp_group=temp_group->next){ temp_contactgroup=find_contactgroup(temp_group->group_name,NULL); if(temp_contactgroup==NULL) continue; /* check all contacts */ for(temp_contact=contact_list;temp_contact!=NULL;temp_contact=temp_contact->next){ if(is_contact_member_of_contactgroup(temp_contactgroup,temp_contact)==TRUE) add_notification(temp_contact); } } } } /* no escalation is necessary - just continue normally... */ else{ /* find all contacts for this service */ for(temp_contact=contact_list;temp_contact!=NULL;temp_contact=temp_contact->next){ if(is_contact_for_service(svc,temp_contact)==TRUE) add_notification(temp_contact); } } #ifdef DEBUG0 printf("create_notification_list_from_service() end\n"); #endif return OK; } /******************************************************************/ /******************* HOST NOTIFICATION FUNCTIONS ******************/ /******************************************************************/ /* notify all contacts for a host that the entire host is down or up */ int host_notification(host *hst,int state, char *ack_data){ notification *temp_notification; time_t current_time; #ifdef DEBUG0 printf("host_notification() start\n"); #endif /* get the current time */ time(¤t_time); #ifdef DEBUG4 printf("\nHOST NOTIFICATION ATTEMPT: Host '%s'\n",hst->name); printf("\tCurrent time: %s",ctime(¤t_time)); #endif /* check viability of sending out a host notification */ if(check_host_notification_viability(hst,state,ack_data)==ERROR){ #ifdef DEBUG4 printf("\tSending out a notification for this host is not viable at this time.\n"); #endif return OK; } /* if this is just a normal notification and NOT an acknowledgement... */ if(ack_data==NULL){ /* increment the current notification number */ hst->current_notification_number++; #ifdef DEBUG4 printf("\tCurrent notification number: %d\n",hst->current_notification_number); #endif /* calculate the next acceptable re-notification time */ hst->next_host_notification=get_next_host_notification_time(hst,state,current_time); #ifdef DEBUG4 printf("\tCurrent Time: %s",ctime(¤t_time)); printf("\tNext acceptable notification time: %s",ctime(&hst->next_host_notification)); #endif } /* grab the macro variables */ clear_volatile_macros(); grab_host_macros(hst); /* make sure the host state macro is correct */ if(macro_host_state!=NULL) free(macro_host_state); macro_host_state=(char *)malloc(MAX_STATE_LENGTH); if(macro_host_state!=NULL){ if(state==HOST_DOWN) strcpy(macro_host_state,"DOWN"); else if(state==HOST_UNREACHABLE) strcpy(macro_host_state,"UNREACHABLE"); else strcpy(macro_host_state,"UP"); } /* if this is an aknowledgement, modify the $OUTPUT$ macro to contain the comment that the user entered */ if(ack_data!=NULL){ if(macro_output!=NULL) free(macro_output); macro_output=(char *)malloc(strlen(ack_data)+1); if(macro_output!=NULL) strcpy(macro_output,ack_data); } /* set the notification type macro */ if(macro_notification_type!=NULL) free(macro_notification_type); macro_notification_type=(char *)malloc(MAX_NOTIFICATIONTYPE_LENGTH); if(macro_notification_type!=NULL){ if(ack_data!=NULL) strcpy(macro_notification_type,"ACKNOWLEDGEMENT"); else if(state==HOST_UP) strcpy(macro_notification_type,"RECOVERY"); else strcpy(macro_notification_type,"PROBLEM"); } /* set the notification number macro */ if(macro_notification_number!=NULL) free(macro_notification_number); macro_notification_number=(char *)malloc(MAX_NOTIFICATIONNUMBER_LENGTH); if(macro_notification_number!=NULL){ snprintf(macro_notification_number,MAX_NOTIFICATIONNUMBER_LENGTH-1,"%d",hst->current_notification_number); macro_notification_number[MAX_NOTIFICATIONNUMBER_LENGTH-1]='\x0'; } /* create the contact notification list for this host */ create_notification_list_from_host(hst,state); /* notify each contact (duplicates have been removed) */ for(temp_notification=notification_list;temp_notification!=NULL;temp_notification=temp_notification->next){ /* grab the macro variables for this contact */ grab_contact_macros(temp_notification->contact); /* notify this contact */ notify_contact_of_host(temp_notification->contact,hst,state,ack_data); } /* free memory allocated to the notification list */ free_notification_list(); /* update the last notification time for this host if this was NOT an acknowledgement (this is needed for scheduling the next problem notification) */ if(ack_data==NULL) hst->last_host_notification=current_time; /* update the status log with the host info */ update_host_status(hst,FALSE); #ifdef DEBUG0 printf("host_notification() end\n"); #endif return OK; } /* checks viability of sending a host notification */ int check_host_notification_viability(host *hst,int state,char *ack_data){ int state_change=FALSE; time_t current_time; time_t timeperiod_start; #ifdef DEBUG0 printf("check_host_notification_viability() start\n"); #endif /* get current time */ time(¤t_time); /* are we in active mode? */ if(program_mode!=ACTIVE_MODE){ #ifdef DEBUG4 printf("\tWe are in STANDBY mode, so host notifications (problems or acknowledgements) will not be sent out!\n"); #endif return ERROR; } /* see if the host can have notifications sent out at this time */ if(check_time_against_period(current_time,hst->notification_period)==ERROR){ #ifdef DEBUG4 printf("\tThis host shouldn't have notifications sent out at this time!\n"); #endif /* if this is NOT an acknowledgement, calculate the next acceptable notification time, once the next valid time range arrives... */ if(ack_data==NULL){ get_next_valid_time(current_time,&timeperiod_start,hst->notification_period); /* it looks like there is no notification time defined, so schedule next one far into the future (one year)... */ if(timeperiod_start==(time_t)0) hst->next_host_notification=(time_t)(current_time+(60*60*24*365)); /* else use the next valid notification time */ else hst->next_host_notification=timeperiod_start; } return ERROR; } /* are notifications temporarily disabled for this host? */ if(hst->notifications_enabled==FALSE){ #ifdef DEBUG4 printf("\tNotifications are temporarily disabled for this host, so we won't send one out!\n"); #endif return ERROR; } /****************************************/ /*** SPECIAL CASE FOR ACKNOWLEGEMENTS ***/ /****************************************/ /* acknowledgements only have to pass three general filters, although they have another test of their own... */ if(ack_data!=NULL){ /* don't send an acknowledgement if there isn't a problem... */ if(state==HOST_UP){ #ifdef DEBUG4 printf("\tThe host is currently UP, so we won't send an acknowledgement!\n"); #endif return ERROR; } /* acknowledgement viability test passed, so the notification can be sent out */ return OK; } /****************************************/ /*** NORMAL NOTIFICATIONS ***************/ /****************************************/ /* has this problem already been acknowledged? */ if(hst->problem_has_been_acknowledged==TRUE){ #ifdef DEBUG4 printf("\tThis host problem has already been acknowledged, so we won't send a notification out!\n"); #endif return ERROR; } /* get the time */ time(¤t_time); /* has the host state changed? */ if(hst->status!=state) state_change=TRUE; else state_change=FALSE; /* see if we should notify about problems with this host */ if((state==HOST_UNREACHABLE) && hst->notify_on_unreachable==FALSE){ #ifdef DEBUG4 printf("\tWe shouldn't notify about UNREACHABLE status for this host!\n"); #endif return ERROR; } if(state==HOST_DOWN && hst->notify_on_down==FALSE){ #ifdef DEBUG4 printf("\tWe shouldn't notify about DOWN states for this host!\n"); #endif return ERROR; } if(state==HOST_UP){ if(hst->notify_on_recovery==FALSE){ #ifdef DEBUG4 printf("\tWe shouldn't notify about RECOVERY states for this host!\n"); #endif return ERROR; } if(!((hst->has_been_down==TRUE && hst->notify_on_down==TRUE) || (hst->has_been_unreachable==TRUE && hst->notify_on_unreachable==TRUE))){ #ifdef DEBUG4 printf("\tWe shouldn't notify about this recovery\n"); #endif return ERROR; } } /* if this host is currently in a scheduled downtime period, don't send the notification */ if(hst->scheduled_downtime_depth>0){ #ifdef DEBUG4 printf("\tThis host is currently in a scheduled downtime, so we won't send notifications!\n"); #endif return ERROR; } /* if this host is currently flapping, don't send the notification */ if(hst->is_flapping==TRUE){ #ifdef DEBUG4 printf("\tThis host is currently flapping, so we won't send notifications!\n"); #endif return ERROR; } /* if we haven't notified the contacts about the host state change, do so */ if(state_change==TRUE){ #ifdef DEBUG4 printf("\tHOST STATE CHANGE!\n"); #endif } /* check if we shouldn't renotify contacts about the host problem */ else if(state!=HOST_UP && hst->no_more_notifications==TRUE){ #ifdef DEBUG4 printf("\tWe shouldn't re-notify contacts about this host problem!!\n"); #endif return ERROR; } /* else if its time to re-notify the contacts about the host... */ else if(state!=HOST_UP && current_time >= hst->next_host_notification){ #ifdef DEBUG4 printf("\tTime to re-notify the contacts about the host problem!\n"); #endif } /* else we shouldn't do the notification, so exit */ else{ #ifdef DEBUG4 printf("\tIts not yet time to re-notify the contacts about this host problem...\n"); printf("\tNext acceptable notification time: %s",ctime(&hst->next_host_notification)); #endif return ERROR; } #ifdef DEBUG0 printf("check_host_notification_viability() end\n"); #endif return OK; } /* checks the viability of notifying a specific contact about a host */ int check_contact_host_notification_viability(contact *cntct, host *hst, int state){ #ifdef DEBUG0 printf("check_contact_host_notification_viability() start\n"); #endif /* see if we should notify about problems with this host */ if((state==HOST_DOWN) && cntct->notify_on_host_down==FALSE){ #ifdef DEBUG4 printf("\tWe shouldn't notify this contact about DOWN states!\n"); #endif return ERROR; } if(state==HOST_UNREACHABLE && cntct->notify_on_host_unreachable==FALSE){ #ifdef DEBUG4 printf("\tWe shouldn't notify this contact about UNREACHABLE states!\n"); #endif return ERROR; } if(state==HOST_UP){ if(cntct->notify_on_host_recovery==FALSE){ #ifdef DEBUG4 printf("\tWe shouldn't notify this contact about RECOVERY states!\n"); #endif return ERROR; } if(!((hst->has_been_down==TRUE && cntct->notify_on_host_down==TRUE) || (hst->has_been_unreachable==TRUE && cntct->notify_on_host_unreachable==TRUE))){ #ifdef DEBUG4 printf("\tWe shouldn't notify about this recovery\n"); #endif return ERROR; } } #ifdef DEBUG0 printf("check_contact_host_notification_viability() end\n"); #endif return OK; } /* notify a specific contact that an entire host is down or up */ int notify_contact_of_host(contact *cntct,host *hst,int state, char *ack_data){ commandsmember *temp_commandsmember; char temp_command_line[MAX_INPUT_BUFFER]; char temp_buffer[MAX_INPUT_BUFFER]; char raw_command_line[MAX_INPUT_BUFFER]; char command_line[MAX_INPUT_BUFFER]; int early_timeout; #ifdef DEBUG0 printf("notify_contact_of_host() start\n"); #endif #ifdef DEBUG4 printf("\tNotify user %s\n",cntct->name); #endif /* see if the contact can be notified at this time */ if(check_time_against_period(time(NULL),cntct->host_notification_period)==ERROR){ #ifdef DEBUG4 printf("\tThis contact shouldn't be notified at this time!\n"); #endif return OK; } /* check viability of notifying this user about the host */ if(ack_data==NULL && check_contact_host_notification_viability(cntct,hst,state)==ERROR) return OK; /* process all the notification commands this user has */ for(temp_commandsmember=cntct->host_notification_commands;temp_commandsmember!=NULL;temp_commandsmember=temp_commandsmember->next){ /* get the raw command line */ strncpy(temp_command_line,temp_commandsmember->command,sizeof(temp_command_line)-1); temp_command_line[sizeof(temp_command_line)-1]='\x0'; get_raw_command_line(temp_command_line,&raw_command_line[0],(int)sizeof(raw_command_line)); /* run the notification command */ if(strcmp(raw_command_line,"")){ #ifdef DEBUG4 printf("\tRaw Command: %s\n",raw_command_line); #endif /* replace macros in the command line */ process_macros(raw_command_line,&command_line[0],(int)sizeof(command_line)); #ifdef DEBUG4 printf("\tProcessed Command: %s\n",command_line); #endif /* log the notification to program log file */ if(log_notifications==TRUE){ if(ack_data==NULL) snprintf(temp_buffer,sizeof(temp_buffer),"HOST NOTIFICATION: %s;%s;%s;%s;%s\n",cntct->name,hst->name,macro_host_state,temp_command_line,macro_output); else snprintf(temp_buffer,sizeof(temp_buffer),"HOST NOTIFICATION: %s;%s;ACKNOWLEDGEMENT (%s);%s;%s\n",cntct->name,hst->name,macro_host_state,temp_command_line,macro_output); temp_buffer[sizeof(temp_buffer)-1]='\x0'; write_to_logs_and_console(temp_buffer,NSLOG_HOST_NOTIFICATION,FALSE); } /* run the command */ my_system(command_line,notification_timeout,&early_timeout,NULL,0); /* check to see if the notification timed out */ if(early_timeout==TRUE){ snprintf(temp_buffer,sizeof(temp_buffer),"Warning: Contact '%s' host notification command '%s' timed out after %d seconds\n",cntct->name,temp_commandsmember->command,DEFAULT_NOTIFICATION_TIMEOUT); temp_buffer[sizeof(temp_buffer)-1]='\x0'; write_to_logs_and_console(temp_buffer,NSLOG_HOST_NOTIFICATION | NSLOG_RUNTIME_WARNING,TRUE); } } } #ifdef DEBUG0 printf("notify_contact_of_host() end\n"); #endif return OK; } /* checks to see if a hostgroup escalation entry is a match for the current host notification */ int is_valid_escalation_for_host_notification(host *hst,int state,hostgroupescalation *hge){ hostgroup *temp_hostgroup; int notification_number; #ifdef DEBUG0 printf("is_valid_escalation_for_host_notification() start\n"); #endif /* if this is a recovery, really we check for who got notified about a previous problem */ if(state==HOST_UP) notification_number=hst->current_notification_number-1; else notification_number=hst->current_notification_number; /* find the hostgroup this escalation entry is associated with */ temp_hostgroup=find_hostgroup(hge->group_name,NULL); if(temp_hostgroup==NULL) return FALSE; /* see if the host is a member of this hostgroup */ if(is_host_member_of_hostgroup(temp_hostgroup,hst)==FALSE) return FALSE; /* skip this escalation if it happens later */ if(hge->first_notification > notification_number) return FALSE; /* skip this escalation if it has already passed */ if(hge->last_notification!=0 && hge->last_notification < notification_number) return FALSE; #ifdef DEBUG0 printf("is_valid_escalation_for_host_notification() end\n"); #endif return TRUE; } /* checks to see whether a host notification should be escalation */ int should_host_notification_be_escalated(host *hst,int state){ hostgroupescalation *temp_hge; #ifdef DEBUG0 printf("should_host_notification_be_escalated() start\n"); #endif /* search the hostgroup escalation list */ for(temp_hge=hostgroupescalation_list;temp_hge!=NULL;temp_hge=temp_hge->next){ /* we found a matching entry, so escalate this notification! */ if(is_valid_escalation_for_host_notification(hst,state,temp_hge)==TRUE) return TRUE; } #ifdef DEBUG0 printf("should_host_notification_be_escalated() end\n"); #endif return FALSE; } /* given a host, create a list of contacts to be notified, removing duplicates */ int create_notification_list_from_host(host *hst,int state){ hostgroupescalation *temp_hge; contactgroupsmember *temp_group; contactgroup *temp_contactgroup; contact *temp_contact; #ifdef DEBUG0 printf("create_notification_list_from_host() start\n"); #endif /* see if this notification should be escalated */ if(should_host_notification_be_escalated(hst,state)==TRUE){ /* check all the hostgroup escalation entries */ for(temp_hge=hostgroupescalation_list;temp_hge!=NULL;temp_hge=temp_hge->next){ /* see if this escalation if valid for this notification */ if(is_valid_escalation_for_host_notification(hst,state,temp_hge)==FALSE) continue; /* find each contact group in this escalation entry */ for(temp_group=temp_hge->contact_groups;temp_group!=NULL;temp_group=temp_group->next){ temp_contactgroup=find_contactgroup(temp_group->group_name,NULL); if(temp_contactgroup==NULL) continue; /* check all contacts */ for(temp_contact=contact_list;temp_contact!=NULL;temp_contact=temp_contact->next){ if(is_contact_member_of_contactgroup(temp_contactgroup,temp_contact)==TRUE) add_notification(temp_contact); } } } } /* else we shouldn't escalate the notification, so continue as normal... */ else{ /* get all contacts for this host */ for(temp_contact=contact_list;temp_contact!=NULL;temp_contact=temp_contact->next){ if(is_contact_for_host(hst,temp_contact)==TRUE) add_notification(temp_contact); } } #ifdef DEBUG0 printf("create_notification_list_from_host() end\n"); #endif return OK; } /******************************************************************/ /***************** NOTIFICATION TIMING FUNCTIONS ******************/ /******************************************************************/ /* calculates next acceptable re-notification time for a service */ time_t get_next_service_notification_time(service *svc, time_t offset){ time_t next_notification; int interval_to_use; serviceescalation *temp_se; int have_escalated_interval=FALSE; #ifdef DEBUG0 printf("get_next_service_notification_time() start\n"); #endif #ifdef DEBUG4 printf("\tCalculating next valid notification time...\n"); #endif /* default notification interval */ interval_to_use=svc->notification_interval; #ifdef DEBUG4 printf("\t\tDefault interval: %d\n",interval_to_use); #endif /* search all the escalation entries for valid matches for this service (at its current notification number) */ for(temp_se=serviceescalation_list;temp_se!=NULL;temp_se=temp_se->next){ /* skip this entry if it isn't appropriate */ if(is_valid_escalation_for_service_notification(svc,temp_se)==FALSE) continue; #ifdef DEBUG4 printf("\t\tFound a valid escalation w/ interval of %d\n",temp_se->notification_interval); #endif /* if we haven't used a notification interval from an escalation yet, use this one */ if(have_escalated_interval==FALSE){ have_escalated_interval=TRUE; interval_to_use=temp_se->notification_interval; } /* else use the shortest of all valid escalation intervals */ else if(temp_se->notification_intervalnotification_interval; #ifdef DEBUG4 printf("\t\tNew interval: %d\n",interval_to_use); #endif } /* if notification interval is 0, we shouldn't send any more problem notifications */ if(interval_to_use==0) svc->no_more_notifications=TRUE; #ifdef DEBUG4 printf("\tInterval used for calculating next valid notification time: %d\n",interval_to_use); #endif /* calculate next notification time */ next_notification=offset+(interval_to_use*interval_length); #ifdef DEBUG0 printf("get_next_service_notification_time() end\n"); #endif return next_notification; } /* calculates next acceptable re-notification time for a host */ time_t get_next_host_notification_time(host *hst, int state, time_t offset){ time_t next_notification; int interval_to_use; hostgroupescalation *temp_hge; int have_escalated_interval=FALSE; #ifdef DEBUG0 printf("get_next_host_notification_time() start\n"); #endif /* default notification interval */ interval_to_use=hst->notification_interval; /* check all the hostgroup escalation entries for valid matches for this host (at its current notification number) */ for(temp_hge=hostgroupescalation_list;temp_hge!=NULL;temp_hge=temp_hge->next){ /* skip this entry if it itsn't appropriate */ if(is_valid_escalation_for_host_notification(hst,state,temp_hge)==FALSE) continue; /* if we haven't used a notification interval from an escalation yet, use this one */ if(have_escalated_interval==FALSE){ have_escalated_interval=TRUE; interval_to_use=temp_hge->notification_interval; } /* else use the shortest of all valid escalation intervals */ else if(temp_hge->notification_intervalnotification_interval; } /* if interval is 0, no more notifications should be sent */ if(interval_to_use==0) hst->no_more_notifications=TRUE; else hst->no_more_notifications=FALSE; /* calculate next notification time */ next_notification=offset+(interval_to_use*interval_length); #ifdef DEBUG0 printf("get_next_host_notification_time() end\n"); #endif return next_notification; } /******************************************************************/ /***************** NOTIFICATION OBJECT FUNCTIONS ******************/ /******************************************************************/ /* given a contact name, find the notification entry for them for the list in memory */ notification * find_notification(char *contact_name){ notification *temp_notification; #ifdef DEBUG0 printf("find_notification() start\n"); #endif if(contact_name==NULL) return NULL; temp_notification=notification_list; while(temp_notification!=NULL){ if(!strcmp(contact_name,temp_notification->contact->name)) return temp_notification; temp_notification=temp_notification->next; } #ifdef DEBUG0 printf("find_notification() end\n"); #endif /* we couldn't find the contact in the notification list */ return NULL; } /* add a new notification to the list in memory */ int add_notification(contact *cntct){ notification *new_notification; notification *temp_notification; #ifdef DEBUG0 printf("add_notification() start\n"); #endif #ifdef DEBUG1 printf("\tAdd contact '%s'\n",cntct->name); #endif /* don't add anything if this contact is already on the notification list */ temp_notification=find_notification(cntct->name); if(temp_notification!=NULL) return OK; /* allocate memory for a new contact in the notification list */ new_notification=malloc(sizeof(notification)); if(new_notification==NULL) return ERROR; /* fill in the contact info */ new_notification->contact=cntct; /* add new notification to head of list */ new_notification->next=notification_list; notification_list=new_notification; #ifdef DEBUG0 printf("add_notification() end\n"); #endif return OK; }