diff --git a/Changelog b/Changelog index ae07c8b36..b51d45850 100644 --- a/Changelog +++ b/Changelog @@ -5,6 +5,7 @@ Nagios Core 4 Change Log 4.5.7 - 2024-11-19 ------------------ * Expand the custom variable macros to an empty string, if the custom variable does not exist (Andre Klärner) +* Fix notification logic to not send a notification if a host and services parents are down (#873) (Dylan Anderson) * Update Exfoliation theme (Dylan Anderson) 4.5.6 - 2024-10-08 diff --git a/base/checks.c b/base/checks.c index aa2be2f68..e5fff720d 100644 --- a/base/checks.c +++ b/base/checks.c @@ -1064,8 +1064,8 @@ static inline void host_propagate_checks_to_immediate_parents(host * hst, int pa log_debug_info(DEBUGL_CHECKS, 1, "Propagating checks to parent host(s)...\n"); for(temp_hostsmember = hst->parent_hosts; temp_hostsmember != NULL; temp_hostsmember = temp_hostsmember->next) { parent_host = temp_hostsmember->host_ptr; - if ((parent_host_up == TRUE && parent_host->current_state == HOST_UP) - || ((parent_host_up == FALSE && parent_host->current_state != HOST_UP))) { + if ((parent_host_up == TRUE && parent_host->current_state != HOST_UP) + || ((parent_host_up == FALSE && parent_host->current_state == HOST_UP))) { log_debug_info(DEBUGL_CHECKS, 1, "Check of parent host '%s' queued.\n", parent_host->name); schedule_host_check(parent_host, current_time, CHECK_OPTION_DEPENDENCY_CHECK); @@ -1208,6 +1208,8 @@ int handle_async_service_check_result(service *svc, check_result *cr) host * hst = NULL; + int notification_type = NOTIFICATION_NORMAL; + log_debug_info(DEBUGL_FUNCTIONS, 0, "handle_async_service_check_result()\n"); if (svc == NULL) { @@ -1590,10 +1592,16 @@ int handle_async_service_check_result(service *svc, check_result *cr) handle_event = TRUE; } + // Recovery notification was not sent + if(svc->notified_on != 0 && svc->current_state == STATE_OK && svc->state_type == HARD_STATE) { + notification_type = NOTIFICATION_RECOVERY; + send_notification = TRUE; + } + if (send_notification == TRUE) { /* send notification */ - if (service_notification(svc, NOTIFICATION_NORMAL, NULL, NULL, NOTIFICATION_OPTION_NONE) == OK) { + if (service_notification(svc, notification_type, NULL, NULL, NOTIFICATION_OPTION_NONE) == OK) { /* log state due to notification event when stalking_options N is set */ if (should_stalk_notifications(svc)) { @@ -1602,13 +1610,6 @@ int handle_async_service_check_result(service *svc, check_result *cr) } } - /* the service recovered, so reset the current notification number and state flags (after the recovery notification has gone out) */ - /* We don't want to reset notifications if the service is currently flapping because we want recovery notifications */ - if(svc->current_state == STATE_OK && svc->state_type == HARD_STATE && hard_state_change == TRUE && svc->is_flapping == FALSE) { - svc->current_notification_number = 0; - svc->notified_on = 0; - } - if (obsess_over_services == TRUE) { obsessive_compulsive_service_check_processor(svc); } @@ -1634,7 +1635,6 @@ int handle_async_service_check_result(service *svc, check_result *cr) /* Reset attempts */ if (hard_state_change == TRUE) { - svc->current_notification_number = 0; svc->host_problem_at_last_check = FALSE; } @@ -2255,6 +2255,8 @@ int handle_async_host_check_result(host *hst, check_result *cr) char * old_plugin_output = NULL; + int notification_type = NOTIFICATION_NORMAL; + log_debug_info(DEBUGL_FUNCTIONS, 0, "handle_async_host_check_result()\n"); if (is_valid_check_result_data(hst, cr) == FALSE) { @@ -2482,10 +2484,16 @@ int handle_async_host_check_result(host *hst, check_result *cr) hst->current_attempt = 1; } + // Recovery notification was not sent + if(hst->notified_on != 0 && hst->current_state == HOST_UP && hst->state_type == HARD_STATE) { + notification_type = NOTIFICATION_RECOVERY; + send_notification = TRUE; + } + if (send_notification == TRUE) { /* send notifications */ - if (host_notification(hst, NOTIFICATION_NORMAL, NULL, NULL, NOTIFICATION_OPTION_NONE) == OK) { + if (host_notification(hst, notification_type, NULL, NULL, NOTIFICATION_OPTION_NONE) == OK) { /* log state due to notification event when stalking_options N is set */ if (should_stalk_notifications(hst)) { @@ -2494,13 +2502,6 @@ int handle_async_host_check_result(host *hst, check_result *cr) } } - /* the host recovered, so reset the current notification number and state flags (after the recovery notification has gone out) */ - /* We don't want to reset notifications if the host is currently flapping because we want recovery notifications */ - if(hst->current_state == HOST_UP && hst->state_type == HARD_STATE && hard_state_change == TRUE && hst->is_flapping == FALSE) { - hst->current_notification_number = 0; - hst->notified_on = 0; - } - if (obsess_over_hosts == TRUE) { obsessive_compulsive_host_check_processor(hst); } diff --git a/base/commands.c b/base/commands.c index 09287a025..4b9a53833 100644 --- a/base/commands.c +++ b/base/commands.c @@ -5390,10 +5390,7 @@ void clear_host_flapping_state(host *hst) { /* should we send a recovery notification? */ if (hst->current_state == HOST_UP && hst->check_flapping_recovery_notification == TRUE) { - host_notification(hst, NOTIFICATION_NORMAL, NULL, NULL, NOTIFICATION_OPTION_NONE); - /* Similar to what happens when a host recovers in handle_async_host_check_result */ - hst->current_notification_number = 0; - hst->notified_on = 0; + host_notification(hst, NOTIFICATION_RECOVERY, NULL, NULL, NOTIFICATION_OPTION_NONE); } } @@ -5450,10 +5447,7 @@ void clear_service_flapping_state(service *svc) { /* should we send a recovery notification? */ if (svc->current_state == STATE_OK && svc->check_flapping_recovery_notification == TRUE) { - service_notification(svc, NOTIFICATION_NORMAL, NULL, NULL, NOTIFICATION_OPTION_NONE); - /* Similar to what happens when a service recovers in handle_async_service_check_result */ - svc->current_notification_number = 0; - svc->notified_on = 0; + service_notification(svc, NOTIFICATION_RECOVERY, NULL, NULL, NOTIFICATION_OPTION_NONE); } } diff --git a/base/flapping.c b/base/flapping.c index ed13d463b..ce3677a69 100644 --- a/base/flapping.c +++ b/base/flapping.c @@ -375,10 +375,7 @@ void clear_service_flap(service *svc, double percent_change, double high_thresho /* should we send a recovery notification? */ if(svc->check_flapping_recovery_notification == TRUE && svc->current_state == STATE_OK) { - service_notification(svc, NOTIFICATION_NORMAL, NULL, NULL, NOTIFICATION_OPTION_NONE); - /* Similar to what happens when a service recovers in handle_async_service_check_result */ - svc->current_notification_number = 0; - svc->notified_on = 0; + service_notification(svc, NOTIFICATION_RECOVERY, NULL, NULL, NOTIFICATION_OPTION_NONE); } } @@ -470,10 +467,7 @@ void clear_host_flap(host *hst, double percent_change, double high_threshold, do /* should we send a recovery notification? */ if(hst->check_flapping_recovery_notification == TRUE && hst->current_state == HOST_UP) { - host_notification(hst, NOTIFICATION_NORMAL, NULL, NULL, NOTIFICATION_OPTION_NONE); - /* Similar to what happens when a host recovers in handle_async_host_check_result */ - hst->current_notification_number = 0; - hst->notified_on = 0; + host_notification(hst, NOTIFICATION_RECOVERY, NULL, NULL, NOTIFICATION_OPTION_NONE); } } @@ -663,10 +657,7 @@ void handle_host_flap_detection_disabled(host *hst) { /* should we send a recovery notification? */ if(hst->check_flapping_recovery_notification == TRUE && hst->current_state == HOST_UP) { - host_notification(hst, NOTIFICATION_NORMAL, NULL, NULL, NOTIFICATION_OPTION_NONE); - /* Similar to what happens when a host recovers in handle_async_host_check_result */ - hst->current_notification_number = 0; - hst->notified_on = 0; + host_notification(hst, NOTIFICATION_RECOVERY, NULL, NULL, NOTIFICATION_OPTION_NONE); } /* clear the recovery notification flag */ @@ -781,10 +772,7 @@ void handle_service_flap_detection_disabled(service *svc) { /* should we send a recovery notification? */ if(svc->check_flapping_recovery_notification == TRUE && svc->current_state == STATE_OK) { - service_notification(svc, NOTIFICATION_NORMAL, NULL, NULL, NOTIFICATION_OPTION_NONE); - /* Similar to what happens when a service recovers in handle_async_service_check_result */ - svc->current_notification_number = 0; - svc->notified_on = 0; + service_notification(svc, NOTIFICATION_RECOVERY, NULL, NULL, NOTIFICATION_OPTION_NONE); } /* clear the recovery notification flag */ diff --git a/base/notifications.c b/base/notifications.c index 0d218b479..4e8786ea5 100644 --- a/base/notifications.c +++ b/base/notifications.c @@ -98,14 +98,19 @@ int service_notification(service *svc, int type, char *not_author, char *not_dat /* check the viability of sending out a service notification */ if(check_service_notification_viability(svc, type, options) == ERROR) { log_debug_info(DEBUGL_NOTIFICATIONS, 0, "Notification viability test failed. No notification will be sent out.\n"); + // Recovery notification wasn't sent because they don't want it. Still have to modify values + if(type == NOTIFICATION_RECOVERY && ! flag_isset(svc->notification_options, OPT_RECOVERY)) { + svc->notified_on = 0; + svc->current_notification_number = 0; + } /* Set next_notification time if we're in a downtime and notification_interval = zero, otherwise if the service doesn't recover after downtime ends, it will never send the notification */ - if (svc->notification_interval == 0.0) { - if (temp_host->scheduled_downtime_depth > 0 || svc->scheduled_downtime_depth > 0) - svc->next_notification = current_time; - } + if (svc->notification_interval == 0.0) { + if (temp_host->scheduled_downtime_depth > 0 || svc->scheduled_downtime_depth > 0) + svc->next_notification = current_time; + } return ERROR; } @@ -212,7 +217,7 @@ int service_notification(service *svc, int type, char *not_author, char *not_dat mac.x[MACRO_NOTIFICATIONTYPE] = "DOWNTIMECANCELLED"; else if(type == NOTIFICATION_CUSTOM) mac.x[MACRO_NOTIFICATIONTYPE] = "CUSTOM"; - else if(svc->current_state == STATE_OK) + else if(type == NOTIFICATION_RECOVERY) mac.x[MACRO_NOTIFICATIONTYPE] = "RECOVERY"; else mac.x[MACRO_NOTIFICATIONTYPE] = "PROBLEM"; @@ -276,7 +281,7 @@ int service_notification(service *svc, int type, char *not_author, char *not_dat clear_servicegroup_macros_r(&mac); clear_datetime_macros_r(&mac); - if(type == NOTIFICATION_NORMAL) { + if(type == NOTIFICATION_NORMAL || type == NOTIFICATION_RECOVERY) { /* adjust last/next notification time and notification flags if we notified someone */ if(contacts_notified > 0) { @@ -289,8 +294,14 @@ int service_notification(service *svc, int type, char *not_author, char *not_dat /* update the last notification time for this service (this is needed for rescheduling later notifications) */ svc->last_notification = current_time; - /* update notifications flags */ - add_notified_on(svc, svc->current_state); + /* update notifications flags, make sure to consider recovery */ + if (type == NOTIFICATION_NORMAL) { + add_notified_on(svc, svc->current_state); + } + else { + svc->notified_on = 0; + svc->current_notification_number = 0; + } } /* we didn't end up notifying anyone */ @@ -603,6 +614,20 @@ int check_service_notification_viability(service *svc, int type, int options) { return ERROR; } + /* If any of the parents are down, don't notify */ + if (temp_host->parent_hosts != NULL) { + hostsmember *temp_hostsmember = NULL; + host *parent_host = NULL; + + for(temp_hostsmember = temp_host->parent_hosts; temp_hostsmember != NULL; temp_hostsmember = temp_hostsmember->next) { + parent_host = temp_hostsmember->host_ptr; + if (parent_host->current_state != HOST_UP) { + log_debug_info(DEBUGL_NOTIFICATIONS, 1, "At least one parent (%s) is down, so we won't notify about this service.\n", parent_host->name); + 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) { log_debug_info(DEBUGL_NOTIFICATIONS, 1, "We haven't waited long enough to re-notify contacts about this service.\n"); @@ -1065,6 +1090,11 @@ int host_notification(host *hst, int type, char *not_author, char *not_data, int /* check viability of sending out a host notification */ if(check_host_notification_viability(hst, type, options) == ERROR) { + // Recovery notification wasn't sent because they don't want it. Still have to modify values + if(type == NOTIFICATION_RECOVERY && ! flag_isset(hst->notification_options, OPT_RECOVERY)) { + hst->notified_on = 0; + hst->current_notification_number = 0; + } log_debug_info(DEBUGL_NOTIFICATIONS, 0, "Notification viability test failed. No notification will be sent out.\n"); return ERROR; } @@ -1169,7 +1199,7 @@ int host_notification(host *hst, int type, char *not_author, char *not_data, int mac.x[MACRO_NOTIFICATIONTYPE] = "DOWNTIMECANCELLED"; else if(type == NOTIFICATION_CUSTOM) mac.x[MACRO_NOTIFICATIONTYPE] = "CUSTOM"; - else if(hst->current_state == HOST_UP) + else if(type == NOTIFICATION_RECOVERY) mac.x[MACRO_NOTIFICATIONTYPE] = "RECOVERY"; else mac.x[MACRO_NOTIFICATIONTYPE] = "PROBLEM"; @@ -1233,7 +1263,7 @@ int host_notification(host *hst, int type, char *not_author, char *not_data, int clear_hostgroup_macros_r(&mac); clear_datetime_macros_r(&mac); - if(type == NOTIFICATION_NORMAL) { + if(type == NOTIFICATION_NORMAL || type == NOTIFICATION_RECOVERY) { /* adjust last/next notification time and notification flags if we notified someone */ if(contacts_notified > 0) { @@ -1245,7 +1275,13 @@ int host_notification(host *hst, int type, char *not_author, char *not_data, int hst->last_notification = current_time; /* update notifications flags */ - add_notified_on(hst, hst->current_state); + if(type == NOTIFICATION_NORMAL) { + add_notified_on(hst, hst->current_state); + } + else { + hst->notified_on = 0; + hst->current_notification_number = 0; + } log_debug_info(DEBUGL_NOTIFICATIONS, 0, "%d contacts were notified. Next possible notification time: %s", contacts_notified, ctime(&hst->next_notification)); } @@ -1449,21 +1485,13 @@ int check_host_notification_viability(host *hst, int type, int options) { } /* see if we should notify about problems with this host */ - if((hst->notification_options & (1 << hst->current_state)) == FALSE) { + if(should_notify(hst) == FALSE) { log_debug_info(DEBUGL_NOTIFICATIONS, 1, "We shouldn't notify about %s status for this host.\n", host_state_name(hst->current_state)); return ERROR; } - if(hst->current_state == HOST_UP) { - - if((hst->notification_options & OPT_RECOVERY) == FALSE) { - log_debug_info(DEBUGL_NOTIFICATIONS, 1, "We shouldn't notify about RECOVERY states for this host.\n"); - return ERROR; - } - if(!hst->notified_on) { - log_debug_info(DEBUGL_NOTIFICATIONS, 1, "We shouldn't notify about this recovery.\n"); - return ERROR; - } - + if(hst->current_state == HOST_UP && hst->notified_on == 0) { + log_debug_info(DEBUGL_NOTIFICATIONS, 1, "We shouldn't notify about this recovery.\n"); + return ERROR; } /* see if enough time has elapsed for first notification (Mathias Sundman) */ @@ -1510,6 +1538,20 @@ int check_host_notification_viability(host *hst, int type, int options) { return ERROR; } + /* If any of the parents are down, don't notify */ + if (hst->parent_hosts != NULL) { + hostsmember *temp_hostsmember = NULL; + host *parent_host = NULL; + + for(temp_hostsmember = hst->parent_hosts; temp_hostsmember != NULL; temp_hostsmember = temp_hostsmember->next) { + parent_host = temp_hostsmember->host_ptr; + if (parent_host->current_state != HOST_UP) { + log_debug_info(DEBUGL_NOTIFICATIONS, 1, "At least one parent (%s) is down, so we won't notify about this host.\n", parent_host->name); + return ERROR; + } + } + } + return OK; } diff --git a/include/nagios.h b/include/nagios.h index ac5ca793c..63504d0c4 100644 --- a/include/nagios.h +++ b/include/nagios.h @@ -326,6 +326,7 @@ extern struct load_control loadctl; #define NOTIFICATION_DOWNTIMEEND 6 #define NOTIFICATION_DOWNTIMECANCELLED 7 #define NOTIFICATION_CUSTOM 8 +#define NOTIFICATION_RECOVERY 9 diff --git a/t-tap/.gitignore b/t-tap/.gitignore index 5e2d4abd4..004481582 100644 --- a/t-tap/.gitignore +++ b/t-tap/.gitignore @@ -8,4 +8,5 @@ test_xsddefault test_commands test_downtime test_strtoul +test_notifications *.dSYM diff --git a/t-tap/Makefile.in b/t-tap/Makefile.in index 1b3544fbb..5194552cd 100644 --- a/t-tap/Makefile.in +++ b/t-tap/Makefile.in @@ -24,6 +24,7 @@ TESTS += test_downtime TESTS += test_nagios_config TESTS += test_timeperiods TESTS += test_macros +TESTS += test_notifications XSD_OBJS = $(BLD_CGI)/statusdata-cgi.o $(BLD_CGI)/xstatusdata-cgi.o XSD_OBJS += $(BLD_CGI)/objects-cgi.o $(BLD_CGI)/xobjects-cgi.o @@ -104,6 +105,9 @@ test_timeperiods: test_timeperiods.o $(TP_OBJS) $(TAPOBJ) test_macros: test_macros.o $(TP_OBJS) $(TAPOBJ) $(CC) $(CFLAGS) -o $@ $^ $(BLD_BASE)/checks.o $(BROKER_LDFLAGS) $(LDFLAGS) $(MATHLIBS) $(SOCKETLIBS) $(LIBS) +test_notifications: test_notifications.o $(BLD_BASE)/notifications.o $(BLD_BASE)/checks.o $(BLD_BASE)/utils.o $(BLD_COMMON)/shared.o $(BLD_BASE)/objects-base.o $(TAPOBJ) + $(CC) $(CFLAGS) -o $@ $^ $(LIBS) $(MATHLIBS) + test_xsddefault: test_xsddefault.o $(XSD_OBJS) $(TAPOBJ) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) diff --git a/t-tap/stub_broker.c b/t-tap/stub_broker.c index eed664648..6cd9649c5 100644 --- a/t-tap/stub_broker.c +++ b/t-tap/stub_broker.c @@ -42,3 +42,12 @@ void broker_timed_event(int type, int flags, int attr, timed_event *event, struc void broker_flapping_data(int type, int flags, int attr, int flapping_type, void *data, double percent_change, double high_threshold, double low_threshold, struct timeval *timestamp) { } + +int broker_contact_notification_data(int type, int flags, int attr, int notification_type, int reason_type, struct timeval start_time, struct timeval end_time, void *data, contact *cntct, char *ack_author, char *ack_data, int escalated, struct timeval *timestamp) +{ return OK; } + +int broker_contact_notification_method_data(int type, int flags, int attr, int notification_type, int reason_type, struct timeval start_time, struct timeval end_time, void *data, contact *cntct, char *cmd, char *ack_author, char *ack_data, int escalated, struct timeval *timestamp) +{ return OK; } + +int broker_notification_data(int type, int flags, int attr, int notification_type, int reason_type, struct timeval start_time, struct timeval end_time, void *data, char *ack_author, char *ack_data, int escalated, int contacts_notified, struct timeval *timestamp) +{ return OK; } \ No newline at end of file diff --git a/t-tap/stub_macros.c b/t-tap/stub_macros.c index 0ba86737a..3a11c7a5e 100644 --- a/t-tap/stub_macros.c +++ b/t-tap/stub_macros.c @@ -29,3 +29,27 @@ int clear_argv_macros_r(nagios_macros *mac) int set_all_macro_environment_vars_r(nagios_macros *mac, int set) { return OK; } + +int clear_datetime_macros_r(nagios_macros *mac) +{ return OK; } + +int clear_hostgroup_macros_r(nagios_macros *mac) +{ return OK; } + +int clear_contactgroup_macros_r(nagios_macros *mac) +{ return OK; } + +int clear_contact_macros_r(nagios_macros *mac) +{ return OK; } + +int clear_summary_macros_r(nagios_macros *mac) +{ return OK; } + +int grab_contact_macros_r(nagios_macros *mac, contact *cntct) +{ return OK; } + +int clear_service_macros_r(nagios_macros *mac) +{ return OK; } + +int clear_servicegroup_macros_r(nagios_macros *mac) +{ return OK; } \ No newline at end of file diff --git a/t-tap/stub_workers.c b/t-tap/stub_workers.c index 6848108af..784e4d2f4 100644 --- a/t-tap/stub_workers.c +++ b/t-tap/stub_workers.c @@ -9,3 +9,6 @@ void wproc_reap(int jobs, int msecs) int get_desired_workers(int desired_workers) { return 4; } + +int wproc_notify(char *cname, char *hname, char *sdesc, char *cmd, nagios_macros *mac) +{ return OK; } \ No newline at end of file diff --git a/t-tap/test_notifications.c b/t-tap/test_notifications.c new file mode 100644 index 000000000..41c6104d1 --- /dev/null +++ b/t-tap/test_notifications.c @@ -0,0 +1,549 @@ +/***************************************************************************** +* +* 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 3 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, see . +* +* +*****************************************************************************/ + +#define TEST_NOTIFICATIONS_C +#define NSCORE 1 + +#include "config.h" + +#include +#include + +#include "comments.h" +#include "common.h" +#include "statusdata.h" +#include "downtime.h" +#include "macros.h" +#include "nagios.h" +#include "broker.h" +#include "perfdata.h" +#include "../lib/lnag-utils.h" + +#include "tap.h" + +#include "stub_sehandlers.c" +#include "stub_comments.c" +#include "stub_perfdata.c" +#include "stub_downtime.c" +#include "stub_logging.c" +#include "stub_broker.c" +#include "stub_macros.c" +#include "stub_workers.c" +#include "stub_events.c" +#include "stub_statusdata.c" +#include "stub_flapping.c" +#include "stub_nebmods.c" +#include "stub_netutils.c" +#include "stub_commands.c" +#include "stub_xodtemplate.c" +#include "stub_iobroker.c" + +#define HOST_NAME "hst1" +#define HOST_ADDRESS "127.0.0.1" +#define HOST_COMMAND "hst1_command" +#define SERVICE_NAME "svc1" + +#define NO_SERVICE 0,0,NULL + +service * svc1 = NULL; +host * hst1 = NULL; +host * parent1 = NULL; +host * parent2 = NULL; + +int c = 0; + + +void free_host(host ** hst) +{ + if ((*hst) != NULL) { + + my_free((*hst)->name); + my_free((*hst)->address); + my_free((*hst)->plugin_output); + my_free((*hst)->long_plugin_output); + my_free((*hst)->perf_data); + my_free((*hst)->check_command); + + if ((*hst)->parent_hosts != NULL) { + my_free((*hst)->parent_hosts->next); + my_free((*hst)->parent_hosts); + } + + my_free((*hst)); + } +} + +void free_parent1() +{ + free_host(&parent1); +} + +void free_parent2() +{ + free_host(&parent2); +} + +void free_hst1() +{ + free_host(&hst1); +} + +void free_svc1() +{ + if (svc1 != NULL) { + my_free(svc1->host_name); + my_free(svc1->description); + my_free(svc1->plugin_output); + my_free(svc1->long_plugin_output); + my_free(svc1->perf_data); + my_free(svc1->check_command); + my_free(svc1); + } +} + +void free_all() +{ + free_parent1(); + free_parent2(); + free_hst1(); + free_svc1(); +} + +void adjust_host_output(char * output) +{ + my_free(hst1->plugin_output); + hst1->plugin_output = strdup(output); +} + +void adjust_service_output(char * output) +{ + my_free(svc1->plugin_output); + svc1->plugin_output = strdup(output); +} + +void create_objects(int host_state, int host_state_type, char * host_output, int service_state, int service_state_type, char * service_output) +{ + free_hst1(); + free_svc1(); + + hst1 = (host *) calloc(1, sizeof(host)); + + if (service_output != NULL) { + svc1 = (service *) calloc(1, sizeof(service)); + } + + hst1->name = strdup(HOST_NAME); + hst1->address = strdup(HOST_ADDRESS); + hst1->check_command = strdup(HOST_COMMAND); + hst1->retry_interval = 1; + hst1->check_interval = 5; + hst1->current_attempt = 1; + hst1->max_attempts = 4; + hst1->check_options = CHECK_OPTION_NONE; + hst1->has_been_checked = TRUE; + hst1->last_state_change = (time_t) 0L; + hst1->last_hard_state_change = (time_t) 0L; + hst1->last_time_up = (time_t) 0L; + hst1->last_time_down = (time_t) 0L; + hst1->last_time_unreachable = (time_t) 0L; + hst1->last_notification = (time_t) 0L; + hst1->next_notification = (time_t) 0L; + hst1->last_check = (time_t) 0L; + hst1->next_check = (time_t) 0L; + hst1->should_be_scheduled = TRUE; + hst1->last_hard_state = STATE_UP; + hst1->notification_options = OPT_ALL; + hst1->notifications_enabled = TRUE; + hst1->event_handler_enabled = TRUE; + hst1->accept_passive_checks = TRUE; + hst1->initial_state = STATE_UP; + hst1->accept_passive_checks = TRUE; + + if (service_output != NULL) { + svc1->host_name = strdup(HOST_NAME); + svc1->description = strdup(SERVICE_NAME); + svc1->host_ptr = hst1; + svc1->retry_interval = 1; + svc1->check_interval = 5; + svc1->current_attempt = 1; + svc1->max_attempts = 4; + svc1->check_options = CHECK_OPTION_NONE; + svc1->has_been_checked = TRUE; + svc1->last_state_change = (time_t) 0L; + svc1->last_hard_state_change = (time_t) 0L; + svc1->last_time_ok = (time_t) 0L; + svc1->last_time_warning = (time_t) 0L; + svc1->last_time_unknown = (time_t) 0L; + svc1->last_time_critical = (time_t) 0L; + svc1->last_notification = (time_t) 0L; + svc1->next_notification = (time_t) 0L; + svc1->last_check = (time_t) 0L; + svc1->next_check = (time_t) 0L; + svc1->host_problem_at_last_check = FALSE; + svc1->should_be_scheduled = TRUE; + svc1->last_hard_state = STATE_OK; + svc1->notification_options = OPT_ALL; + svc1->notifications_enabled = TRUE; + svc1->event_handler_enabled = TRUE; + svc1->accept_passive_checks = TRUE; + svc1->initial_state = STATE_OK; + svc1->accept_passive_checks = TRUE; + } + + hst1->current_state = host_state; + hst1->state_type = host_state_type; + adjust_host_output(host_output); + + if (service_output != NULL) { + svc1->current_state = service_state; + svc1->state_type = service_state_type; + adjust_service_output(service_output); + } +} + +void setup_parent(host ** parent, const char * host_name) +{ + (*parent) = (host *) calloc(1, sizeof(host)); + + (*parent)->name = strdup(host_name); + (*parent)->address = strdup(HOST_ADDRESS); + (*parent)->check_command = strdup(HOST_COMMAND); + (*parent)->retry_interval = 1; + (*parent)->check_interval = 5; + (*parent)->current_attempt = 1; + (*parent)->max_attempts = 4; + (*parent)->check_options = CHECK_OPTION_NONE; + (*parent)->has_been_checked = TRUE; + (*parent)->current_state = STATE_DOWN; + +} + +void setup_parents() +{ + hostsmember * tmp_hsts = NULL; + tmp_hsts = (hostsmember *) calloc(1, sizeof(hostsmember)); + + setup_parent(&parent1, "Parent 1"); + setup_parent(&parent2, "Parent 2"); + + tmp_hsts->host_ptr = parent1; + tmp_hsts->next = (hostsmember *) calloc(1, sizeof(hostsmember)); + tmp_hsts->next->host_ptr = parent2; + + hst1->parent_hosts = tmp_hsts; +} + +void run_service_tests() { + + int result = OK; + + // debug_level = DEBUGL_NOTIFICATIONS; + // debug_verbosity = 2; + + enable_notifications = FALSE; + + // Forced notifications always pass + create_objects(STATE_UP, HARD_STATE, "host up", STATE_OK, HARD_STATE, "service ok"); + result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_FORCED); + ok(result == OK, "Notification Forced - Service should notify"); + + // No notifications when notifications are disabled + create_objects(STATE_UP, HARD_STATE, "host up", STATE_CRITICAL, HARD_STATE, "service critical"); + result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Notifications disabled - Service should NOT notify"); + + enable_notifications = TRUE; + + // All parents are bad. Reminder that host parents are different than service parents. + create_objects(STATE_DOWN, HARD_STATE, "host down", STATE_CRITICAL, HARD_STATE, "service critical"); + setup_parents(); + result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "All parents are down - Service should NOT notify"); + + // Not all parents are bad + create_objects(STATE_UP, HARD_STATE, "host up", STATE_CRITICAL, HARD_STATE, "service critical"); + setup_parents(); + result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Not all parents are down - Service should NOT notify"); + + free_all(); + + // Timeperiod checking + + // Notifications disabled for service + create_objects(STATE_UP, HARD_STATE, "host up", STATE_CRITICAL, HARD_STATE, "service critical"); + svc1->notifications_enabled = FALSE; + result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Service notifications disabled - Service should NOT notify"); + + // Custom notifications + create_objects(STATE_UP, HARD_STATE, "host up", STATE_OK, HARD_STATE, "service ok"); + result = check_service_notification_viability(svc1, NOTIFICATION_CUSTOM, NOTIFICATION_OPTION_NONE); + ok(result == OK, "Custom notification - Service should notify"); + + // Custom Notification in downtime + create_objects(STATE_UP, HARD_STATE, "host up", STATE_OK, HARD_STATE, "service ok"); + svc1->scheduled_downtime_depth = 1; + result = check_service_notification_viability(svc1, NOTIFICATION_CUSTOM, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Custom notification in downtime - Service should NOT notify"); + + // Acknowledgement + create_objects(STATE_UP, HARD_STATE, "host up", STATE_CRITICAL, HARD_STATE, "service critical"); + result = check_service_notification_viability(svc1, NOTIFICATION_ACKNOWLEDGEMENT, NOTIFICATION_OPTION_NONE); + ok(result == OK, "Acknowledgement - Service should notify"); + + // Acknowledgement while ok + create_objects(STATE_UP, HARD_STATE, "host up", STATE_OK, HARD_STATE, "service ok"); + result = check_service_notification_viability(svc1, NOTIFICATION_ACKNOWLEDGEMENT, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Acknowledgement while OK - Service should NOT notify"); + + // Service Flapping start + create_objects(STATE_UP, HARD_STATE, "host up", STATE_OK, HARD_STATE, "service ok"); + result = check_service_notification_viability(svc1, NOTIFICATION_FLAPPINGSTART, NOTIFICATION_OPTION_NONE); + ok(result == OK, "Flapping started - Service should notify"); + + // Service Flapping start while in downtime + create_objects(STATE_UP, HARD_STATE, "host up", STATE_OK, HARD_STATE, "service ok"); + svc1->scheduled_downtime_depth = 1; + result = check_service_notification_viability(svc1, NOTIFICATION_FLAPPINGSTART, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Flapping started in downtime - Service should NOT notify"); + + // Service downtime start + create_objects(STATE_UP, HARD_STATE, "host up", STATE_OK, HARD_STATE, "service ok"); + result = check_service_notification_viability(svc1, NOTIFICATION_DOWNTIMESTART, NOTIFICATION_OPTION_NONE); + ok(result == OK, "Downtime started - Service should notify"); + + // Service downtime start while in downtime (Sounds wrong) + create_objects(STATE_UP, HARD_STATE, "host up", STATE_OK, HARD_STATE, "service ok"); + svc1->scheduled_downtime_depth = 1; + result = check_service_notification_viability(svc1, NOTIFICATION_DOWNTIMESTART, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Downtime started in downtime - Service should NOT notify"); + + // Soft states don't get notifications + create_objects(STATE_UP, HARD_STATE, "host up", STATE_CRITICAL, SOFT_STATE, "service critical"); + result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Host: up,hard - Service: critical,soft - Service should NOT notify"); + + // Problem has already been acknowledged + create_objects(STATE_UP, HARD_STATE, "host up", STATE_CRITICAL, HARD_STATE, "service critical"); + svc1->problem_has_been_acknowledged = TRUE; + result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Service problem acknowledged - Service should NOT notify"); + + // Host and Service Dependencies + + // Notification options for the service don't match + create_objects(STATE_UP, HARD_STATE, "host up", STATE_CRITICAL, HARD_STATE, "service critical"); + svc1->notification_options = OPT_NOTHING; + result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Service options don't have critical - Service should NOT notify"); + + // Recovery notification + create_objects(STATE_UP, HARD_STATE, "host up", STATE_OK, HARD_STATE, "service ok"); + svc1->notified_on = 1; + result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == OK, "Recovery notification - Service should notify"); + + // Recovery notification without matching error notification + create_objects(STATE_UP, HARD_STATE, "host up", STATE_OK, HARD_STATE, "service ok"); + svc1->notified_on = 0; + result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Recovery notification without matching error - Service should NOT notify"); + + // Enough time between last notification + + // Service is flapping + create_objects(STATE_UP, HARD_STATE, "host up", STATE_CRITICAL, HARD_STATE, "service critical"); + svc1->is_flapping = TRUE; + result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Service is flapping - Service should NOT notify"); + + // Service in downtime + create_objects(STATE_UP, HARD_STATE, "host up", STATE_CRITICAL, HARD_STATE, "service critical"); + svc1->scheduled_downtime_depth = 1; + result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Service is in downtime - Service should NOT notify"); + + // Parent host in downtime + create_objects(STATE_UP, HARD_STATE, "host up", STATE_CRITICAL, HARD_STATE, "service critical"); + hst1->scheduled_downtime_depth = 1; + result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Parent host is in downtime - Service should NOT notify"); + + // Host is down, service shouldn't get notification + create_objects(STATE_DOWN, HARD_STATE, "host down", STATE_CRITICAL, HARD_STATE, "service critical"); + result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Host: down,hard - Service: critical,hard - Service should NOT notify"); + + // When you should be typically notified + create_objects(STATE_UP, HARD_STATE, "host up", STATE_CRITICAL, HARD_STATE, "service critical"); + result = check_service_notification_viability(svc1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == OK, "Host: up,hard - Service: critical,hard - Service should notify"); + + // Some other thing with not notifying again too soon + + free_all(); +} + +void run_host_tests() { + int result = OK; + + // debug_level = DEBUGL_NOTIFICATIONS; + // debug_verbosity = 2; + + enable_notifications = FALSE; + + // Forced notifications always pass + create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE); + result = check_host_notification_viability(hst1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_FORCED); + ok(result == OK, "Notification Forced - Host should notify"); + + // No notifications when notifications are disabled + create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE); + result = check_host_notification_viability(hst1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Notifications disabled - Host should NOT notify"); + + enable_notifications = TRUE; + + // Timeperiod checking + + // Notifications disabled for host + create_objects(STATE_DOWN, HARD_STATE, "host down", NO_SERVICE); + hst1->notifications_enabled = FALSE; + result = check_host_notification_viability(hst1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Service notifications disabled - Host should NOT notify"); + + // Custom notifications + create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE); + result = check_host_notification_viability(hst1, NOTIFICATION_CUSTOM, NOTIFICATION_OPTION_NONE); + ok(result == OK, "Custom notification - Host should notify"); + + // Custom Notification in downtime + create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE); + hst1->scheduled_downtime_depth = 1; + result = check_host_notification_viability(hst1, NOTIFICATION_CUSTOM, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Custom notification in downtime - Host should NOT notify"); + + // Acknowledgement + create_objects(STATE_DOWN, HARD_STATE, "host down", NO_SERVICE); + result = check_host_notification_viability(hst1, NOTIFICATION_ACKNOWLEDGEMENT, NOTIFICATION_OPTION_NONE); + ok(result == OK, "Acknowledgement - Host should notify"); + + // Acknowledgement while ok + create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE); + result = check_host_notification_viability(hst1, NOTIFICATION_ACKNOWLEDGEMENT, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Acknowledgement while OK - Host should NOT notify"); + + // Host Flapping start + create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE); + result = check_host_notification_viability(hst1, NOTIFICATION_FLAPPINGSTART, NOTIFICATION_OPTION_NONE); + ok(result == OK, "Flapping started - Host should notify"); + + // Host Flapping start while in downtime + create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE); + hst1->scheduled_downtime_depth = 1; + result = check_host_notification_viability(hst1, NOTIFICATION_FLAPPINGSTART, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Flapping started in downtime - Host should NOT notify"); + + // Host downtime start + create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE); + result = check_host_notification_viability(hst1, NOTIFICATION_DOWNTIMESTART, NOTIFICATION_OPTION_NONE); + ok(result == OK, "Downtime started - Host should notify"); + + // Host downtime start while in downtime (Sounds wrong) + create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE); + hst1->scheduled_downtime_depth = 1; + result = check_host_notification_viability(hst1, NOTIFICATION_DOWNTIMESTART, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Downtime started in downtime - Host should NOT notify"); + + // Soft states don't get notifications + create_objects(STATE_DOWN, SOFT_STATE, "host down", NO_SERVICE); + result = check_host_notification_viability(hst1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Host: down,soft - Host should NOT notify"); + + // Problem has already been acknowledged + create_objects(STATE_DOWN, HARD_STATE, "host down", NO_SERVICE); + hst1->problem_has_been_acknowledged = TRUE; + result = check_host_notification_viability(hst1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Host problem acknowledged - Host should NOT notify"); + + // Host and Service Dependencies + + // Notification options for the host don't match + create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE); + hst1->notification_options = OPT_NOTHING; + result = check_host_notification_viability(hst1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Host options don't have critical - Host should NOT notify"); + + // Recovery notification + create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE); + hst1->notified_on = 1; + result = check_host_notification_viability(hst1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == OK, "Recovery notification - Host should notify"); + + // Recovery notification without matching error notification + create_objects(STATE_UP, HARD_STATE, "host up", NO_SERVICE); + hst1->notified_on = 0; + result = check_host_notification_viability(hst1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Recovery notification without matching error - Host should NOT notify"); + + // Enough time between last notification + + // Host is flapping + create_objects(STATE_DOWN, HARD_STATE, "host down", NO_SERVICE); + hst1->is_flapping = TRUE; + result = check_host_notification_viability(hst1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Host is flapping - Host should NOT notify"); + + // Host in downtime + create_objects(STATE_DOWN, HARD_STATE, "host down", NO_SERVICE); + hst1->scheduled_downtime_depth = 1; + result = check_host_notification_viability(hst1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == ERROR, "Host is in downtime - Host should NOT notify"); + + // When you should be typically notified + create_objects(STATE_DOWN, HARD_STATE, "host down", NO_SERVICE); + result = check_host_notification_viability(hst1, NOTIFICATION_NORMAL, NOTIFICATION_OPTION_NONE); + ok(result == OK, "Host: down,hard - Host should notify"); + + // Some other thing with not notifying again too soon + + free_all(); + +} + +int main(int argc, char **argv) +{ + char cwd[1024]; + if (getcwd(cwd, sizeof(cwd)) != NULL) { + printf("\n\n********** cwd: %s\n\n", cwd); + } else { + printf("\n\n********** cwd error!\n\n"); + } + + time_t now = 0L; + + plan_tests(42); + + time(&now); + + run_service_tests(); + run_host_tests(); + + return exit_status(); +} \ No newline at end of file