/***************************************************************************** * * DOWNTIME.C - Scheduled downtime functions for Nagios * * Copyright (c) 2000-2008 Ethan Galstad (egalstad@nagios.org) * Last Modified: 02-17-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/comments.h" #include "../include/downtime.h" #include "../include/objects.h" #include "../include/statusdata.h" /***** IMPLEMENTATION-SPECIFIC INCLUDES *****/ #ifdef USE_XDDDEFAULT #include "../xdata/xdddefault.h" #endif #ifdef NSCORE #include "../include/nagios.h" #include "../include/broker.h" #endif #ifdef NSCGI #include "../include/cgiutils.h" #endif scheduled_downtime *scheduled_downtime_list = NULL; int defer_downtime_sorting = 0; #ifdef NSCORE extern timed_event *event_list_high; extern timed_event *event_list_high_tail; pthread_mutex_t nagios_downtime_lock = PTHREAD_MUTEX_INITIALIZER; #endif #ifdef NSCORE /******************************************************************/ /**************** INITIALIZATION/CLEANUP FUNCTIONS ****************/ /******************************************************************/ /* initializes scheduled downtime data */ int initialize_downtime_data(char *config_file) { int result = OK; log_debug_info(DEBUGL_FUNCTIONS, 0, "initialize_downtime_data()\n"); /**** IMPLEMENTATION-SPECIFIC CALLS ****/ #ifdef USE_XDDDEFAULT result = xdddefault_initialize_downtime_data(config_file); #endif return result; } /* cleans up scheduled downtime data */ int cleanup_downtime_data(char *config_file) { int result = OK; log_debug_info(DEBUGL_FUNCTIONS, 0, "cleanup_downtime_data()\n"); /**** IMPLEMENTATION-SPECIFIC CALLS ****/ #ifdef USE_XDDDEFAULT result = xdddefault_cleanup_downtime_data(config_file); #endif /* free memory allocated to downtime data */ free_downtime_data(); return result; } /******************************************************************/ /********************** SCHEDULING FUNCTIONS **********************/ /******************************************************************/ /* schedules a host or service downtime */ int schedule_downtime(int type, char *host_name, char *service_description, time_t entry_time, char *author, char *comment_data, time_t start_time, time_t end_time, int fixed, unsigned long triggered_by, unsigned long duration, unsigned long *new_downtime_id) { unsigned long downtime_id = 0L; log_debug_info(DEBUGL_FUNCTIONS, 0, "schedule_downtime()\n"); /* don't add old or invalid downtimes */ if(start_time >= end_time || end_time <= time(NULL)) { log_debug_info(DEBUGL_DOWNTIME, 1, "Invalid start (%lu) or end (%lu) times\n", start_time, end_time); return ERROR; } /* add a new downtime entry */ add_new_downtime(type, host_name, service_description, entry_time, author, comment_data, start_time, end_time, fixed, triggered_by, duration, &downtime_id, FALSE, FALSE); /* register the scheduled downtime */ register_downtime(type, downtime_id); /* return downtime id */ if(new_downtime_id != NULL) *new_downtime_id = downtime_id; return OK; } /* unschedules a host or service downtime */ int unschedule_downtime(int type, unsigned long downtime_id) { scheduled_downtime *temp_downtime = NULL; scheduled_downtime *next_downtime = NULL; host *hst = NULL; service *svc = NULL; timed_event *temp_event = NULL; #ifdef USE_EVENT_BROKER int attr = 0; #endif log_debug_info(DEBUGL_FUNCTIONS, 0, "unschedule_downtime()\n"); /* find the downtime entry in the list in memory */ if((temp_downtime = find_downtime(type, downtime_id)) == NULL) return ERROR; /* find the host or service associated with this downtime */ if(temp_downtime->type == HOST_DOWNTIME) { if((hst = find_host(temp_downtime->host_name)) == NULL) return ERROR; } else { if((svc = find_service(temp_downtime->host_name, temp_downtime->service_description)) == NULL) return ERROR; } /* decrement pending flex downtime if necessary ... */ if(temp_downtime->fixed == FALSE && temp_downtime->incremented_pending_downtime == TRUE) { if(temp_downtime->type == HOST_DOWNTIME) hst->pending_flex_downtime--; else svc->pending_flex_downtime--; } /* decrement the downtime depth variable and update status data if necessary */ if(temp_downtime->is_in_effect == TRUE) { #ifdef USE_EVENT_BROKER /* send data to event broker */ attr = NEBATTR_DOWNTIME_STOP_CANCELLED; broker_downtime_data(NEBTYPE_DOWNTIME_STOP, NEBFLAG_NONE, attr, temp_downtime->type, temp_downtime->host_name, temp_downtime->service_description, temp_downtime->entry_time, temp_downtime->author, temp_downtime->comment, temp_downtime->start_time, temp_downtime->end_time, temp_downtime->fixed, temp_downtime->triggered_by, temp_downtime->duration, temp_downtime->downtime_id, NULL); #endif if(temp_downtime->type == HOST_DOWNTIME) { hst->scheduled_downtime_depth--; update_host_status(hst, FALSE); /* log a notice - this is parsed by the history CGI */ if(hst->scheduled_downtime_depth == 0) { logit(NSLOG_INFO_MESSAGE, FALSE, "HOST DOWNTIME ALERT: %s;CANCELLED; Scheduled downtime for host has been cancelled.\n", hst->name); /* send a notification */ host_notification(hst, NOTIFICATION_DOWNTIMECANCELLED, NULL, NULL, NOTIFICATION_OPTION_NONE); } } else { svc->scheduled_downtime_depth--; update_service_status(svc, FALSE); /* log a notice - this is parsed by the history CGI */ if(svc->scheduled_downtime_depth == 0) { logit(NSLOG_INFO_MESSAGE, FALSE, "SERVICE DOWNTIME ALERT: %s;%s;CANCELLED; Scheduled downtime for service has been cancelled.\n", svc->host_name, svc->description); /* send a notification */ service_notification(svc, NOTIFICATION_DOWNTIMECANCELLED, NULL, NULL, NOTIFICATION_OPTION_NONE); } } } /* remove scheduled entry from event queue */ for(temp_event = event_list_high; temp_event != NULL; temp_event = temp_event->next) { if(temp_event->event_type != EVENT_SCHEDULED_DOWNTIME) continue; if(((unsigned long)temp_event->event_data) == downtime_id) break; } if(temp_event != NULL) { remove_event(temp_event, &event_list_high, &event_list_high_tail); my_free(temp_event->event_data); my_free(temp_event); } /* delete downtime entry */ if(temp_downtime->type == HOST_DOWNTIME) delete_host_downtime(downtime_id); else delete_service_downtime(downtime_id); /* unschedule all downtime entries that were triggered by this one */ while(1) { for(temp_downtime = scheduled_downtime_list; temp_downtime != NULL; temp_downtime = next_downtime) { next_downtime = temp_downtime->next; if(temp_downtime->triggered_by == downtime_id) { unschedule_downtime(ANY_DOWNTIME, temp_downtime->downtime_id); break; } } if(temp_downtime == NULL) break; } return OK; } /* registers scheduled downtime (schedules it, adds comments, etc.) */ int register_downtime(int type, unsigned long downtime_id) { char *temp_buffer = NULL; char start_time_string[MAX_DATETIME_LENGTH] = ""; char flex_start_string[MAX_DATETIME_LENGTH] = ""; char end_time_string[MAX_DATETIME_LENGTH] = ""; scheduled_downtime *temp_downtime = NULL; host *hst = NULL; service *svc = NULL; char *type_string = NULL; int hours = 0; int minutes = 0; int seconds = 0; unsigned long *new_downtime_id = NULL; int was_in_effect = FALSE; log_debug_info(DEBUGL_FUNCTIONS, 0, "register_downtime( %d, %lu)\n", type, downtime_id); /* find the downtime entry in memory */ temp_downtime = find_downtime(type, downtime_id); if(temp_downtime == NULL) { log_debug_info(DEBUGL_DOWNTIME, 0, "Cannot find downtime ID: %lu\n", downtime_id); return ERROR; } /* find the host or service associated with this downtime */ if(temp_downtime->type == HOST_DOWNTIME) { if((hst = find_host(temp_downtime->host_name)) == NULL) { log_debug_info(DEBUGL_DOWNTIME, 1, "Cannot find host (%s) for downtime ID: %lu\n", temp_downtime->host_name, downtime_id); return ERROR; } } else { if((svc = find_service(temp_downtime->host_name, temp_downtime->service_description)) == NULL) { log_debug_info(DEBUGL_DOWNTIME, 1, "Cannot find service (%s) for host (%s) for downtime ID: %lu\n", temp_downtime->service_description, temp_downtime->host_name, downtime_id); return ERROR; } } /* create the comment */ get_datetime_string(&(temp_downtime->start_time), start_time_string, MAX_DATETIME_LENGTH, SHORT_DATE_TIME); get_datetime_string(&(temp_downtime->flex_downtime_start), flex_start_string, MAX_DATETIME_LENGTH, SHORT_DATE_TIME); get_datetime_string(&(temp_downtime->end_time), end_time_string, MAX_DATETIME_LENGTH, SHORT_DATE_TIME); hours = temp_downtime->duration / 3600; minutes = ((temp_downtime->duration - (hours * 3600)) / 60); seconds = temp_downtime->duration - (hours * 3600) - (minutes * 60); if(temp_downtime->type == HOST_DOWNTIME) type_string = "host"; else type_string = "service"; if(temp_downtime->fixed == TRUE) asprintf(&temp_buffer, "This %s has been scheduled for fixed downtime from %s to %s. Notifications for the %s will not be sent out during that time period.", type_string, start_time_string, end_time_string, type_string); else asprintf(&temp_buffer, "This %s has been scheduled for flexible downtime starting between %s and %s and lasting for a period of %d hours and %d minutes. Notifications for the %s will not be sent out during that time period.", type_string, start_time_string, end_time_string, hours, minutes, type_string); log_debug_info(DEBUGL_DOWNTIME, 0, "Scheduled Downtime Details:\n"); if(temp_downtime->type == HOST_DOWNTIME) { log_debug_info(DEBUGL_DOWNTIME, 0, " Type: Host Downtime\n"); log_debug_info(DEBUGL_DOWNTIME, 0, " Host: %s\n", hst->name); } else { log_debug_info(DEBUGL_DOWNTIME, 0, " Type: Service Downtime\n"); log_debug_info(DEBUGL_DOWNTIME, 0, " Host: %s\n", svc->host_name); log_debug_info(DEBUGL_DOWNTIME, 0, " Service: %s\n", svc->description); } log_debug_info(DEBUGL_DOWNTIME, 0, " Fixed/Flex: %s\n", (temp_downtime->fixed == TRUE) ? "Fixed" : "Flexible"); log_debug_info(DEBUGL_DOWNTIME, 0, " Start: %s\n", start_time_string); if(temp_downtime->flex_downtime_start) { log_debug_info(DEBUGL_DOWNTIME, 0, " Flex Start: %s\n", flex_start_string); } log_debug_info(DEBUGL_DOWNTIME, 0, " End: %s\n", end_time_string); log_debug_info(DEBUGL_DOWNTIME, 0, " Duration: %dh %dm %ds\n", hours, minutes, seconds); log_debug_info(DEBUGL_DOWNTIME, 0, " Downtime ID: %lu\n", temp_downtime->downtime_id); log_debug_info(DEBUGL_DOWNTIME, 0, " Trigger ID: %lu\n", temp_downtime->triggered_by); /* add a non-persistent comment to the host or service regarding the scheduled outage */ if(temp_downtime->type == SERVICE_DOWNTIME) add_new_comment(SERVICE_COMMENT, DOWNTIME_COMMENT, svc->host_name, svc->description, time(NULL), ( NULL == temp_downtime->author ? "(Nagios Process)" : temp_downtime->author), temp_buffer, 0, COMMENTSOURCE_INTERNAL, FALSE, (time_t)0, &(temp_downtime->comment_id)); else add_new_comment(HOST_COMMENT, DOWNTIME_COMMENT, hst->name, NULL, time(NULL), ( NULL == temp_downtime->author ? "(Nagios Process)" : temp_downtime->author), temp_buffer, 0, COMMENTSOURCE_INTERNAL, FALSE, (time_t)0, &(temp_downtime->comment_id)); my_free(temp_buffer); /*** SCHEDULE DOWNTIME - FLEXIBLE (NON-FIXED) DOWNTIME IS HANDLED AT A LATER POINT ***/ /* only non-triggered downtime is scheduled... */ if((temp_downtime->triggered_by == 0) && ((TRUE == temp_downtime->fixed) || ((FALSE == temp_downtime->fixed) && (TRUE == temp_downtime->is_in_effect)))) { /* If this is a fixed downtime, schedule the event to start it. If this is a flexible downtime, normally we wait for one of the check_pending_flex_*_downtime() functions to start it, but if the downtime is already in effect, this means that we are restarting Nagios and the downtime was in effect when we last shutdown Nagios, so we should restart the flexible downtime now. This should work even if the downtime has ended because the handle_scheduled_dowtime() function will immediately schedule another downtime event which will end the downtime. */ if((new_downtime_id = (unsigned long *)malloc(sizeof(unsigned long *)))) { *new_downtime_id = downtime_id; /*temp_downtime->start_event = schedule_new_event(EVENT_SCHEDULED_DOWNTIME, TRUE, temp_downtime->start_time, FALSE, 0, NULL, FALSE, (void *)new_downtime_id, NULL, 0); */ schedule_new_event(EVENT_SCHEDULED_DOWNTIME, TRUE, temp_downtime->start_time, FALSE, 0, NULL, FALSE, (void *)new_downtime_id, NULL, 0); /* Turn off is_in_effect flag so handle_scheduled_downtime() will handle it correctly */ was_in_effect = temp_downtime->is_in_effect; temp_downtime->is_in_effect = FALSE; } } /* If the downtime is triggered and was in effect, mark it as not in effect so it gets scheduled correctly */ if((temp_downtime->triggered_by != 0) && (TRUE == temp_downtime->is_in_effect)) { was_in_effect = temp_downtime->is_in_effect; temp_downtime->is_in_effect = FALSE; } if((FALSE == temp_downtime->fixed) && (FALSE == was_in_effect)) { /* increment pending flex downtime counter */ if(temp_downtime->type == HOST_DOWNTIME) hst->pending_flex_downtime++; else svc->pending_flex_downtime++; temp_downtime->incremented_pending_downtime = TRUE; /* Since a flex downtime may never start, schedule an expiring event in case the event is never triggered. The expire event will NOT cancel a downtime event that is in effect */ log_debug_info(DEBUGL_DOWNTIME, 1, "Scheduling downtime expire event in case flexible downtime is never triggered\n"); /*temp_downtime->stop_event = schedule_new_event(EVENT_EXPIRE_DOWNTIME, TRUE, (temp_downtime->end_time + 1), FALSE, 0, NULL, FALSE, NULL, NULL, 0);*/ schedule_new_event(EVENT_EXPIRE_DOWNTIME, TRUE, (temp_downtime->end_time + 1), FALSE, 0, NULL, FALSE, NULL, NULL, 0); } #ifdef PROBABLY_NOT_NEEDED /*** FLEXIBLE DOWNTIME SANITY CHECK - ADDED 02/17/2008 ****/ /* if host/service is in a non-OK/UP state right now, see if we should start flexible time immediately */ /* this is new logic added in 3.0rc3 */ if(temp_downtime->fixed == FALSE) { if(temp_downtime->type == HOST_DOWNTIME) check_pending_flex_host_downtime(hst); else check_pending_flex_service_downtime(svc); } #endif return OK; } /* handles scheduled downtime (id passed from timed event queue) */ int handle_scheduled_downtime_by_id(unsigned long downtime_id) { scheduled_downtime *temp_downtime = NULL; log_debug_info(DEBUGL_FUNCTIONS, 0, "handle_scheduled_downtime_by_id()\n"); /* find the downtime entry */ if((temp_downtime = find_downtime(ANY_DOWNTIME, downtime_id)) == NULL) { log_debug_info(DEBUGL_DOWNTIME, 1, "Unable to find downtime id: %lu\n", downtime_id); return ERROR; } /* handle the downtime */ return handle_scheduled_downtime(temp_downtime); } /* handles scheduled host or service downtime */ int handle_scheduled_downtime(scheduled_downtime *temp_downtime) { scheduled_downtime *this_downtime = NULL; host *hst = NULL; service *svc = NULL; time_t event_time = 0L; //time_t current_time = 0L; unsigned long *new_downtime_id = NULL; #ifdef USE_EVENT_BROKER int attr = 0; #endif log_debug_info(DEBUGL_FUNCTIONS, 0, "handle_scheduled_downtime()\n"); if(temp_downtime == NULL) return ERROR; /* find the host or service associated with this downtime */ if(temp_downtime->type == HOST_DOWNTIME) { if((hst = find_host(temp_downtime->host_name)) == NULL) { log_debug_info(DEBUGL_DOWNTIME, 1, "Unable to find host (%s) for downtime\n", temp_downtime->host_name); return ERROR; } } else { if((svc = find_service(temp_downtime->host_name, temp_downtime->service_description)) == NULL) { log_debug_info(DEBUGL_DOWNTIME, 1, "Unable to find service (%s) host (%s) for downtime\n", temp_downtime->service_description, temp_downtime->host_name); return ERROR; } } /* have we come to the end of the scheduled downtime? */ if(temp_downtime->is_in_effect == TRUE) { #ifdef USE_EVENT_BROKER /* send data to event broker */ attr = NEBATTR_DOWNTIME_STOP_NORMAL; broker_downtime_data(NEBTYPE_DOWNTIME_STOP, NEBFLAG_NONE, attr, temp_downtime->type, temp_downtime->host_name, temp_downtime->service_description, temp_downtime->entry_time, temp_downtime->author, temp_downtime->comment, temp_downtime->start_time, temp_downtime->end_time, temp_downtime->fixed, temp_downtime->triggered_by, temp_downtime->duration, temp_downtime->downtime_id, NULL); #endif /* decrement the downtime depth variable */ if(temp_downtime->type == HOST_DOWNTIME) hst->scheduled_downtime_depth--; else svc->scheduled_downtime_depth--; if(temp_downtime->type == HOST_DOWNTIME && hst->scheduled_downtime_depth == 0) { log_debug_info(DEBUGL_DOWNTIME, 0, "Host '%s' has exited from a period of scheduled downtime (id=%lu).\n", hst->name, temp_downtime->downtime_id); /* log a notice - this one is parsed by the history CGI */ logit(NSLOG_INFO_MESSAGE, FALSE, "HOST DOWNTIME ALERT: %s;STOPPED; Host has exited from a period of scheduled downtime", hst->name); /* send a notification */ host_notification(hst, NOTIFICATION_DOWNTIMEEND, temp_downtime->author, temp_downtime->comment, NOTIFICATION_OPTION_NONE); } else if(temp_downtime->type == SERVICE_DOWNTIME && svc->scheduled_downtime_depth == 0) { log_debug_info(DEBUGL_DOWNTIME, 0, "Service '%s' on host '%s' has exited from a period of scheduled downtime (id=%lu).\n", svc->description, svc->host_name, temp_downtime->downtime_id); /* log a notice - this one is parsed by the history CGI */ logit(NSLOG_INFO_MESSAGE, FALSE, "SERVICE DOWNTIME ALERT: %s;%s;STOPPED; Service has exited from a period of scheduled downtime", svc->host_name, svc->description); /* send a notification */ service_notification(svc, NOTIFICATION_DOWNTIMEEND, temp_downtime->author, temp_downtime->comment, NOTIFICATION_OPTION_NONE); } /* update the status data */ if(temp_downtime->type == HOST_DOWNTIME) update_host_status(hst, FALSE); else update_service_status(svc, FALSE); /* decrement pending flex downtime if necessary */ if(temp_downtime->fixed == FALSE && temp_downtime->incremented_pending_downtime == TRUE) { if(temp_downtime->type == HOST_DOWNTIME) { if(hst->pending_flex_downtime > 0) hst->pending_flex_downtime--; } else { if(svc->pending_flex_downtime > 0) svc->pending_flex_downtime--; } } /* handle (stop) downtime that is triggered by this one */ while(1) { /* list contents might change by recursive calls, so we use this inefficient method to prevent segfaults */ for(this_downtime = scheduled_downtime_list; this_downtime != NULL; this_downtime = this_downtime->next) { if(this_downtime->triggered_by == temp_downtime->downtime_id) { handle_scheduled_downtime(this_downtime); break; } } if(this_downtime == NULL) break; } /* delete downtime entry */ if(temp_downtime->type == HOST_DOWNTIME) delete_host_downtime(temp_downtime->downtime_id); else delete_service_downtime(temp_downtime->downtime_id); } /* else we are just starting the scheduled downtime */ else { #ifdef USE_EVENT_BROKER /* send data to event broker */ broker_downtime_data(NEBTYPE_DOWNTIME_START, NEBFLAG_NONE, NEBATTR_NONE, temp_downtime->type, temp_downtime->host_name, temp_downtime->service_description, temp_downtime->entry_time, temp_downtime->author, temp_downtime->comment, temp_downtime->start_time, temp_downtime->end_time, temp_downtime->fixed, temp_downtime->triggered_by, temp_downtime->duration, temp_downtime->downtime_id, NULL); #endif if(temp_downtime->type == HOST_DOWNTIME && hst->scheduled_downtime_depth == 0) { log_debug_info(DEBUGL_DOWNTIME, 0, "Host '%s' has entered a period of scheduled downtime (id=%lu).\n", hst->name, temp_downtime->downtime_id); /* log a notice - this one is parsed by the history CGI */ logit(NSLOG_INFO_MESSAGE, FALSE, "HOST DOWNTIME ALERT: %s;STARTED; Host has entered a period of scheduled downtime", hst->name); /* send a notification */ if(FALSE == temp_downtime->start_notification_sent) { host_notification(hst, NOTIFICATION_DOWNTIMESTART, temp_downtime->author, temp_downtime->comment, NOTIFICATION_OPTION_NONE); temp_downtime->start_notification_sent = TRUE; } } else if(temp_downtime->type == SERVICE_DOWNTIME && svc->scheduled_downtime_depth == 0) { log_debug_info(DEBUGL_DOWNTIME, 0, "Service '%s' on host '%s' has entered a period of scheduled downtime (id=%lu).\n", svc->description, svc->host_name, temp_downtime->downtime_id); /* log a notice - this one is parsed by the history CGI */ logit(NSLOG_INFO_MESSAGE, FALSE, "SERVICE DOWNTIME ALERT: %s;%s;STARTED; Service has entered a period of scheduled downtime", svc->host_name, svc->description); /* send a notification */ if(FALSE == temp_downtime->start_notification_sent) { service_notification(svc, NOTIFICATION_DOWNTIMESTART, temp_downtime->author, temp_downtime->comment, NOTIFICATION_OPTION_NONE); temp_downtime->start_notification_sent = TRUE; } } /* increment the downtime depth variable */ if(temp_downtime->type == HOST_DOWNTIME) hst->scheduled_downtime_depth++; else svc->scheduled_downtime_depth++; /* set the in effect flag */ temp_downtime->is_in_effect = TRUE; /* update the status data */ if(temp_downtime->type == HOST_DOWNTIME) update_host_status(hst, FALSE); else update_service_status(svc, FALSE); /* schedule an event to end the downtime */ if(temp_downtime->fixed == FALSE) { event_time = (time_t)((unsigned long)temp_downtime->flex_downtime_start + temp_downtime->duration); } else { event_time = temp_downtime->end_time; } if((new_downtime_id = (unsigned long *)malloc(sizeof(unsigned long *)))) { *new_downtime_id = temp_downtime->downtime_id; schedule_new_event(EVENT_SCHEDULED_DOWNTIME, TRUE, event_time, FALSE, 0, NULL, FALSE, (void *)new_downtime_id, NULL, 0); } /* handle (start) downtime that is triggered by this one */ for(this_downtime = scheduled_downtime_list; this_downtime != NULL; this_downtime = this_downtime->next) { if(this_downtime->triggered_by == temp_downtime->downtime_id) handle_scheduled_downtime(this_downtime); } } return OK; } /* checks for flexible (non-fixed) host downtime that should start now */ int check_pending_flex_host_downtime(host *hst) { scheduled_downtime *temp_downtime = NULL; time_t current_time = 0L; unsigned long * new_downtime_id = NULL; log_debug_info(DEBUGL_FUNCTIONS, 0, "check_pending_flex_host_downtime()\n"); if(hst == NULL) return ERROR; time(¤t_time); /* if host is currently up, nothing to do */ if(hst->current_state == HOST_UP) return OK; /* check all downtime entries */ for(temp_downtime = scheduled_downtime_list; temp_downtime != NULL; temp_downtime = temp_downtime->next) { if(temp_downtime->type != HOST_DOWNTIME) continue; if(temp_downtime->fixed == TRUE) continue; if(temp_downtime->is_in_effect == TRUE) continue; /* triggered downtime entries should be ignored here */ if(temp_downtime->triggered_by != 0) continue; /* this entry matches our host! */ if(find_host(temp_downtime->host_name) == hst) { /* if the time boundaries are okay, start this scheduled downtime */ if(temp_downtime->start_time <= current_time && current_time <= temp_downtime->end_time) { log_debug_info(DEBUGL_DOWNTIME, 0, "Flexible downtime (id=%lu) for host '%s' starting now...\n", temp_downtime->downtime_id, hst->name); temp_downtime->flex_downtime_start = current_time; if((new_downtime_id = (unsigned long *)malloc(sizeof(unsigned long *)))) { *new_downtime_id = temp_downtime->downtime_id; /*temp_downtime->start_event = schedule_new_event(EVENT_SCHEDULED_DOWNTIME, TRUE, temp_downtime->flex_downtime_start, FALSE, 0, NULL, FALSE, (void *)new_downtime_id, NULL, 0);*/ schedule_new_event(EVENT_SCHEDULED_DOWNTIME, TRUE, temp_downtime->flex_downtime_start, FALSE, 0, NULL, FALSE, (void *)new_downtime_id, NULL, 0); } } } } return OK; } /* checks for flexible (non-fixed) service downtime that should start now */ int check_pending_flex_service_downtime(service *svc) { scheduled_downtime *temp_downtime = NULL; time_t current_time = 0L; unsigned long * new_downtime_id = NULL; log_debug_info(DEBUGL_FUNCTIONS, 0, "check_pending_flex_service_downtime()\n"); if(svc == NULL) return ERROR; time(¤t_time); /* if service is currently ok, nothing to do */ if(svc->current_state == STATE_OK) return OK; /* check all downtime entries */ for(temp_downtime = scheduled_downtime_list; temp_downtime != NULL; temp_downtime = temp_downtime->next) { if(temp_downtime->type != SERVICE_DOWNTIME) continue; if(temp_downtime->fixed == TRUE) continue; if(temp_downtime->is_in_effect == TRUE) continue; /* triggered downtime entries should be ignored here */ if(temp_downtime->triggered_by != 0) continue; /* this entry matches our service! */ if(find_service(temp_downtime->host_name, temp_downtime->service_description) == svc) { /* if the time boundaries are okay, start this scheduled downtime */ if(temp_downtime->start_time <= current_time && current_time <= temp_downtime->end_time) { log_debug_info(DEBUGL_DOWNTIME, 0, "Flexible downtime (id=%lu) for service '%s' on host '%s' starting now...\n", temp_downtime->downtime_id, svc->description, svc->host_name); temp_downtime->flex_downtime_start = current_time; if((new_downtime_id = (unsigned long *)malloc(sizeof(unsigned long *)))) { *new_downtime_id = temp_downtime->downtime_id; schedule_new_event(EVENT_SCHEDULED_DOWNTIME, TRUE, temp_downtime->flex_downtime_start, FALSE, 0, NULL, FALSE, (void *)new_downtime_id, NULL, 0); } } } } return OK; } /* checks for (and removes) expired downtime entries */ int check_for_expired_downtime(void) { scheduled_downtime *temp_downtime = NULL; scheduled_downtime *next_downtime = NULL; time_t current_time = 0L; log_debug_info(DEBUGL_FUNCTIONS, 0, "check_for_expired_downtime()\n"); time(¤t_time); /* check all downtime entries... */ for(temp_downtime = scheduled_downtime_list; temp_downtime != NULL; temp_downtime = next_downtime) { next_downtime = temp_downtime->next; /* this entry should be removed */ if(temp_downtime->is_in_effect == FALSE && temp_downtime->end_time < current_time) { log_debug_info(DEBUGL_DOWNTIME, 0, "Expiring %s downtime (id=%lu)...\n", (temp_downtime->type == HOST_DOWNTIME) ? "host" : "service", temp_downtime->downtime_id); /* delete the downtime entry */ if(temp_downtime->type == HOST_DOWNTIME) delete_host_downtime(temp_downtime->downtime_id); else delete_service_downtime(temp_downtime->downtime_id); } } return OK; } /******************************************************************/ /************************* SAVE FUNCTIONS *************************/ /******************************************************************/ /* save a host or service downtime */ int add_new_downtime(int type, char *host_name, char *service_description, time_t entry_time, char *author, char *comment_data, time_t start_time, time_t end_time, int fixed, unsigned long triggered_by, unsigned long duration, unsigned long *downtime_id, int is_in_effect, int start_notification_sent) { int result = OK; if(type == HOST_DOWNTIME) result = add_new_host_downtime(host_name, entry_time, author, comment_data, start_time, end_time, fixed, triggered_by, duration, downtime_id, is_in_effect, start_notification_sent); else result = add_new_service_downtime(host_name, service_description, entry_time, author, comment_data, start_time, end_time, fixed, triggered_by, duration, downtime_id, is_in_effect, start_notification_sent); return result; } /* saves a host downtime entry */ int add_new_host_downtime(char *host_name, time_t entry_time, char *author, char *comment_data, time_t start_time, time_t end_time, int fixed, unsigned long triggered_by, unsigned long duration, unsigned long *downtime_id, int is_in_effect, int start_notification_sent) { int result = OK; unsigned long new_downtime_id = 0L; if(host_name == NULL) return ERROR; /**** IMPLEMENTATION-SPECIFIC CALLS ****/ #ifdef USE_XDDDEFAULT result = xdddefault_add_new_host_downtime(host_name, entry_time, author, comment_data, start_time, end_time, fixed, triggered_by, duration, &new_downtime_id, is_in_effect, start_notification_sent); #endif /* save downtime id */ if(downtime_id != NULL) *downtime_id = new_downtime_id; #ifdef USE_EVENT_BROKER /* send data to event broker */ broker_downtime_data(NEBTYPE_DOWNTIME_ADD, NEBFLAG_NONE, NEBATTR_NONE, HOST_DOWNTIME, host_name, NULL, entry_time, author, comment_data, start_time, end_time, fixed, triggered_by, duration, new_downtime_id, NULL); #endif return result; } /* saves a service downtime entry */ int add_new_service_downtime(char *host_name, char *service_description, time_t entry_time, char *author, char *comment_data, time_t start_time, time_t end_time, int fixed, unsigned long triggered_by, unsigned long duration, unsigned long *downtime_id, int is_in_effect, int start_notification_sent) { int result = OK; unsigned long new_downtime_id = 0L; if(host_name == NULL || service_description == NULL) return ERROR; /**** IMPLEMENTATION-SPECIFIC CALLS ****/ #ifdef USE_XDDDEFAULT result = xdddefault_add_new_service_downtime(host_name, service_description, entry_time, author, comment_data, start_time, end_time, fixed, triggered_by, duration, &new_downtime_id, is_in_effect, start_notification_sent); #endif /* save downtime id */ if(downtime_id != NULL) *downtime_id = new_downtime_id; #ifdef USE_EVENT_BROKER /* send data to event broker */ broker_downtime_data(NEBTYPE_DOWNTIME_ADD, NEBFLAG_NONE, NEBATTR_NONE, SERVICE_DOWNTIME, host_name, service_description, entry_time, author, comment_data, start_time, end_time, fixed, triggered_by, duration, new_downtime_id, NULL); #endif return result; } /******************************************************************/ /*********************** DELETION FUNCTIONS ***********************/ /******************************************************************/ /* deletes a scheduled host or service downtime entry from the list in memory */ int delete_downtime(int type, unsigned long downtime_id) { int result = OK; scheduled_downtime *this_downtime = NULL; scheduled_downtime *last_downtime = NULL; scheduled_downtime *next_downtime = NULL; #ifdef NSCORE pthread_mutex_lock(&nagios_downtime_lock); #endif /* find the downtime we should remove */ for(this_downtime = scheduled_downtime_list, last_downtime = scheduled_downtime_list; this_downtime != NULL; this_downtime = next_downtime) { next_downtime = this_downtime->next; /* we found the downtime we should delete */ if(this_downtime->downtime_id == downtime_id && this_downtime->type == type) break; last_downtime = this_downtime; } /* remove the downtime from the list in memory */ if(this_downtime != NULL) { /* first remove the comment associated with this downtime */ if(this_downtime->type == HOST_DOWNTIME) delete_host_comment(this_downtime->comment_id); else delete_service_comment(this_downtime->comment_id); #ifdef USE_EVENT_BROKER /* send data to event broker */ broker_downtime_data(NEBTYPE_DOWNTIME_DELETE, NEBFLAG_NONE, NEBATTR_NONE, type, this_downtime->host_name, this_downtime->service_description, this_downtime->entry_time, this_downtime->author, this_downtime->comment, this_downtime->start_time, this_downtime->end_time, this_downtime->fixed, this_downtime->triggered_by, this_downtime->duration, downtime_id, NULL); #endif if(scheduled_downtime_list == this_downtime) scheduled_downtime_list = this_downtime->next; else last_downtime->next = next_downtime; /* free memory */ my_free(this_downtime->host_name); my_free(this_downtime->service_description); my_free(this_downtime->author); my_free(this_downtime->comment); my_free(this_downtime); result = OK; } else result = ERROR; #ifdef NSCORE pthread_mutex_unlock(&nagios_downtime_lock); #endif return result; } /* deletes a scheduled host downtime entry */ int delete_host_downtime(unsigned long downtime_id) { int result = OK; /* delete the downtime from memory */ delete_downtime(HOST_DOWNTIME, downtime_id); /**** IMPLEMENTATION-SPECIFIC CALLS ****/ #ifdef USE_XDDDEFAULT result = xdddefault_delete_host_downtime(downtime_id); #endif return result; } /* deletes a scheduled service downtime entry */ int delete_service_downtime(unsigned long downtime_id) { int result = OK; /* delete the downtime from memory */ delete_downtime(SERVICE_DOWNTIME, downtime_id); /**** IMPLEMENTATION-SPECIFIC CALLS ****/ #ifdef USE_XDDDEFAULT result = xdddefault_delete_service_downtime(downtime_id); #endif return result; } /* Deletes all host and service downtimes on a host by hostname, optionally filtered by service description, start time and comment. All char* must be set or NULL - "" will silently fail to match Returns number deleted */ int delete_downtime_by_hostname_service_description_start_time_comment(char *hostname, char *service_description, time_t start_time, char *comment) { scheduled_downtime *temp_downtime; scheduled_downtime *next_downtime; int deleted = 0; /* Do not allow deletion of everything - must have at least 1 filter on */ if(hostname == NULL && service_description == NULL && start_time == 0 && comment == NULL) return deleted; for(temp_downtime = scheduled_downtime_list; temp_downtime != NULL; temp_downtime = next_downtime) { next_downtime = temp_downtime->next; if(start_time != 0 && temp_downtime->start_time != start_time) { continue; } if(comment != NULL && strcmp(temp_downtime->comment, comment) != 0) continue; if(temp_downtime->type == HOST_DOWNTIME) { /* If service is specified, then do not delete the host downtime */ if(service_description != NULL) continue; if(hostname != NULL && strcmp(temp_downtime->host_name, hostname) != 0) continue; } else if(temp_downtime->type == SERVICE_DOWNTIME) { if(hostname != NULL && strcmp(temp_downtime->host_name, hostname) != 0) continue; if(service_description != NULL && strcmp(temp_downtime->service_description, service_description) != 0) continue; } unschedule_downtime(temp_downtime->type, temp_downtime->downtime_id); deleted++; } return deleted; } #endif /******************************************************************/ /******************** ADDITION FUNCTIONS **************************/ /******************************************************************/ /* adds a host downtime entry to the list in memory */ int add_host_downtime(char *host_name, time_t entry_time, char *author, char *comment_data, time_t start_time, time_t flex_downtime_start, time_t end_time, int fixed, unsigned long triggered_by, unsigned long duration, unsigned long downtime_id, int is_in_effect, int start_notification_sent) { int result = OK; result = add_downtime(HOST_DOWNTIME, host_name, NULL, entry_time, author, comment_data, start_time, flex_downtime_start, end_time, fixed, triggered_by, duration, downtime_id, is_in_effect, start_notification_sent); return result; } /* adds a service downtime entry to the list in memory */ int add_service_downtime(char *host_name, char *svc_description, time_t entry_time, char *author, char *comment_data, time_t start_time, time_t flex_downtime_start, time_t end_time, int fixed, unsigned long triggered_by, unsigned long duration, unsigned long downtime_id, int is_in_effect, int start_notification_sent) { int result = OK; result = add_downtime(SERVICE_DOWNTIME, host_name, svc_description, entry_time, author, comment_data, start_time, flex_downtime_start, end_time, fixed, triggered_by, duration, downtime_id, is_in_effect, start_notification_sent); return result; } /* adds a host or service downtime entry to the list in memory */ int add_downtime(int downtime_type, char *host_name, char *svc_description, time_t entry_time, char *author, char *comment_data, time_t start_time, time_t flex_downtime_start, time_t end_time, int fixed, unsigned long triggered_by, unsigned long duration, unsigned long downtime_id, int is_in_effect, int start_notification_sent) { scheduled_downtime *new_downtime = NULL; scheduled_downtime *last_downtime = NULL; scheduled_downtime *temp_downtime = NULL; int result = OK; log_debug_info(DEBUGL_FUNCTIONS, 0, "add_downtime()\n"); /* don't add triggered downtimes that don't have a valid parent */ if(triggered_by > 0 && find_downtime(ANY_DOWNTIME, triggered_by) == NULL) { log_debug_info(DEBUGL_DOWNTIME, 1, "Downtime is triggered, but has no valid parent\n"); return ERROR; } /* we don't have enough info */ if(host_name == NULL || (downtime_type == SERVICE_DOWNTIME && svc_description == NULL)) { log_debug_info(DEBUGL_DOWNTIME, 1, "Host name (%s) or service description (%s) is null\n", ((NULL == host_name) ? "null" : host_name), ((NULL == svc_description) ? "null" : svc_description)); return ERROR; } /* allocate memory for the downtime */ if((new_downtime = (scheduled_downtime *)calloc(1, sizeof(scheduled_downtime))) == NULL) return ERROR; /* duplicate vars */ if((new_downtime->host_name = (char *)strdup(host_name)) == NULL) { log_debug_info(DEBUGL_DOWNTIME, 1, "Unable to allocate memory for new downtime's host name\n"); result = ERROR; } if(downtime_type == SERVICE_DOWNTIME) { if((new_downtime->service_description = (char *)strdup(svc_description)) == NULL) { log_debug_info(DEBUGL_DOWNTIME, 1, "Unable to allocate memory for new downtime's service description\n"); result = ERROR; } } if(author) { if((new_downtime->author = (char *)strdup(author)) == NULL) { log_debug_info(DEBUGL_DOWNTIME, 1, "Unable to allocate memory for new downtime's author\n"); result = ERROR; } } if(comment_data) { if((new_downtime->comment = (char *)strdup(comment_data)) == NULL) result = ERROR; } /* handle errors */ if(result == ERROR) { my_free(new_downtime->comment); my_free(new_downtime->author); my_free(new_downtime->service_description); my_free(new_downtime->host_name); my_free(new_downtime); return ERROR; } new_downtime->type = downtime_type; new_downtime->entry_time = entry_time; new_downtime->start_time = start_time; new_downtime->flex_downtime_start = flex_downtime_start; new_downtime->end_time = end_time; new_downtime->fixed = (fixed > 0) ? TRUE : FALSE; new_downtime->triggered_by = triggered_by; new_downtime->duration = duration; new_downtime->downtime_id = downtime_id; new_downtime->is_in_effect = is_in_effect; new_downtime->start_notification_sent = start_notification_sent; if(defer_downtime_sorting) { new_downtime->next = scheduled_downtime_list; scheduled_downtime_list = new_downtime; } else { /* * add new downtime to downtime list, sorted by start time, * but lock the lists first so broker modules fiddling * with them at the same time doesn't crash out. */ #ifdef NSCORE pthread_mutex_lock(&nagios_downtime_lock); #endif last_downtime = scheduled_downtime_list; for(temp_downtime = scheduled_downtime_list; temp_downtime != NULL; temp_downtime = temp_downtime->next) { if(new_downtime->start_time < temp_downtime->start_time) { new_downtime->next = temp_downtime; if(temp_downtime == scheduled_downtime_list) scheduled_downtime_list = new_downtime; else last_downtime->next = new_downtime; break; } else last_downtime = temp_downtime; } if(scheduled_downtime_list == NULL) { new_downtime->next = NULL; scheduled_downtime_list = new_downtime; } else if(temp_downtime == NULL) { new_downtime->next = NULL; last_downtime->next = new_downtime; } #ifdef NSCORE pthread_mutex_unlock(&nagios_downtime_lock); #endif } #ifdef NSCORE #ifdef USE_EVENT_BROKER /* send data to event broker */ broker_downtime_data(NEBTYPE_DOWNTIME_LOAD, NEBFLAG_NONE, NEBATTR_NONE, downtime_type, host_name, svc_description, entry_time, author, comment_data, start_time, end_time, fixed, triggered_by, duration, downtime_id, NULL); #endif #endif return OK; } static int downtime_compar(const void *p1, const void *p2) { scheduled_downtime *d1 = *(scheduled_downtime **)p1; scheduled_downtime *d2 = *(scheduled_downtime **)p2; /* If the start times of two downtimes are equal and one is triggered but but the other is not, the triggered downtime should be later in the list than the untriggered one. This is so they are written to the retention.dat and status.dat in the correct order. Previously the triggered downtime always appeared before its triggering downtime in those files. When the downtimes were read from those files, either on a core restart or by the CGIs, the triggered downtime would be discarded because the triggering downtime did not yet exist. The most common case for this is when a downtime is created and the option is selected to create triggered downtimes on all child objects. This change in the sort order does NOT resolve the case where a manually created, triggered downtime is created with a start time earlier than the triggering downtime. This would need to be resolved by comparing the triggered_by value with the downtime ID regardless of the start time. However, this should be a relatively rare case and only caused by intentional scheduling by a human. This change was not implemented because it would cause the downtime list to be out of time order and the implications of this were not well understood. */ if(d1->start_time == d2->start_time) { if(( d1->triggered_by == 0 && d2->triggered_by != 0) || ( d1->triggered_by != 0 && d2->triggered_by == 0)) { return d1->triggered_by == 0 ? -1 : 1; } } return (d1->start_time < d2->start_time) ? -1 : (d1->start_time - d2->start_time); } int sort_downtime(void) { scheduled_downtime **array, *temp_downtime; unsigned long i = 0, unsorted_downtimes = 0; log_debug_info(DEBUGL_FUNCTIONS, 0, "sort_downtime()\n"); if(!defer_downtime_sorting) return OK; defer_downtime_sorting = 0; temp_downtime = scheduled_downtime_list; while(temp_downtime != NULL) { temp_downtime = temp_downtime->next; unsorted_downtimes++; } if(!unsorted_downtimes) return OK; if(!(array = malloc(sizeof(*array) * unsorted_downtimes))) return ERROR; while(scheduled_downtime_list) { array[i++] = scheduled_downtime_list; scheduled_downtime_list = scheduled_downtime_list->next; } qsort((void *)array, i, sizeof(*array), downtime_compar); scheduled_downtime_list = temp_downtime = array[0]; for(i = 1; i < unsorted_downtimes; i++) { temp_downtime->next = array[i]; temp_downtime = temp_downtime->next; } temp_downtime->next = NULL; my_free(array); return OK; } /******************************************************************/ /************************ SEARCH FUNCTIONS ************************/ /******************************************************************/ /* finds a specific downtime entry */ scheduled_downtime *find_downtime(int type, unsigned long downtime_id) { scheduled_downtime *temp_downtime = NULL; log_debug_info(DEBUGL_FUNCTIONS, 0, "find_downtime()\n"); for(temp_downtime = scheduled_downtime_list; temp_downtime != NULL; temp_downtime = temp_downtime->next) { log_debug_info(DEBUGL_DOWNTIME, 2, "find_downtime() looking at type %d, id: %lu\n", type, downtime_id); if(type != ANY_DOWNTIME && temp_downtime->type != type) continue; if(temp_downtime->downtime_id == downtime_id) return temp_downtime; } return NULL; } /* finds a specific host downtime entry */ scheduled_downtime *find_host_downtime(unsigned long downtime_id) { return find_downtime(HOST_DOWNTIME, downtime_id); } /* finds a specific service downtime entry */ scheduled_downtime *find_service_downtime(unsigned long downtime_id) { return find_downtime(SERVICE_DOWNTIME, downtime_id); } /******************************************************************/ /********************* CLEANUP FUNCTIONS **************************/ /******************************************************************/ /* frees memory allocated for the scheduled downtime data */ void free_downtime_data(void) { scheduled_downtime *this_downtime = NULL; scheduled_downtime *next_downtime = NULL; /* free memory for the scheduled_downtime list */ for(this_downtime = scheduled_downtime_list; this_downtime != NULL; this_downtime = next_downtime) { next_downtime = this_downtime->next; my_free(this_downtime->host_name); my_free(this_downtime->service_description); my_free(this_downtime->author); my_free(this_downtime->comment); my_free(this_downtime); } /* reset list pointer */ scheduled_downtime_list = NULL; return; }