| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "health.h" |
| 4 | #include "health_internals.h" |
| 5 | |
| 6 | struct health_plugin_globals health_globals = { |
| 7 | .initialization = { |
| 8 | .spinlock = SPINLOCK_INITIALIZER, |
| 9 | .done = false, |
| 10 | }, |
| 11 | .config = { |
| 12 | .enabled = true, |
| 13 | .stock_enabled = true, |
| 14 | .use_summary_for_notifications = true, |
| 15 | |
| 16 | .health_log_entries_max = HEALTH_LOG_ENTRIES_DEFAULT, |
| 17 | .health_log_retention_s = HEALTH_LOG_RETENTION_DEFAULT, |
| 18 | |
| 19 | .default_warn_repeat_every = 0, |
| 20 | .default_crit_repeat_every = 0, |
| 21 | |
| 22 | .run_at_least_every_seconds = 10, |
| 23 | .postpone_alarms_during_hibernation_for_seconds = 60, |
| 24 | .notification_execution_timeout_seconds = 120, |
| 25 | }, |
| 26 | .prototypes = { |
| 27 | .dict = NULL, |
| 28 | .registering = false, |
| 29 | } |
| 30 | }; |
| 31 | |
| 32 | bool health_plugin_enabled(void) { |
| 33 | return health_globals.config.enabled; |
| 34 | } |
| 35 | |
| 36 | void health_plugin_disable(void) { |
| 37 | health_globals.config.enabled = false; |
| 38 | } |
| 39 | |
| 40 | |
| 41 | void health_load_config_defaults(void) { |
| 42 | static bool done = false; |
| 43 | if(done) return; |
| 44 | done = true; |
| 45 | |
| 46 | char filename[FILENAME_MAX + 1]; |
| 47 | |
| 48 | health_globals.config.enabled = |
| 49 | inicfg_get_boolean(&netdata_config, CONFIG_SECTION_HEALTH, |
| 50 | "enabled", |
| 51 | health_globals.config.enabled); |
| 52 | |
| 53 | health_globals.config.stock_enabled = |
| 54 | inicfg_get_boolean(&netdata_config, CONFIG_SECTION_HEALTH, |
| 55 | "enable stock health configuration", |
| 56 | health_globals.config.stock_enabled); |
| 57 | |
| 58 | health_globals.config.use_summary_for_notifications = |
| 59 | inicfg_get_boolean(&netdata_config, CONFIG_SECTION_HEALTH, |
| 60 | "use summary for notifications", |
| 61 | health_globals.config.use_summary_for_notifications); |
| 62 | |
| 63 | health_globals.config.default_warn_repeat_every = |
| 64 | inicfg_get_duration_seconds(&netdata_config, CONFIG_SECTION_HEALTH, "default repeat warning", 0); |
| 65 | |
| 66 | health_globals.config.default_crit_repeat_every = |
| 67 | inicfg_get_duration_seconds(&netdata_config, CONFIG_SECTION_HEALTH, "default repeat critical", 0); |
| 68 | |
| 69 | health_globals.config.health_log_entries_max = |
| 70 | inicfg_get_number(&netdata_config, CONFIG_SECTION_HEALTH, "in memory max health log entries", |
| 71 | health_globals.config.health_log_entries_max); |
| 72 | |
| 73 | health_globals.config.health_log_retention_s = |
| 74 | inicfg_get_duration_seconds(&netdata_config, CONFIG_SECTION_HEALTH, "health log retention", HEALTH_LOG_RETENTION_DEFAULT); |
| 75 | |
| 76 | snprintfz(filename, FILENAME_MAX, "%s/alarm-notify.sh", netdata_configured_primary_plugins_dir); |
| 77 | health_globals.config.default_exec = |
| 78 | string_strdupz(inicfg_get_filename(&netdata_config, CONFIG_SECTION_HEALTH, "script to execute on alarm", filename)); |
| 79 | |
| 80 | health_globals.config.enabled_alerts = |
| 81 | simple_pattern_create(inicfg_get(&netdata_config, CONFIG_SECTION_HEALTH, "enabled alarms", "*"), |
| 82 | NULL, SIMPLE_PATTERN_EXACT, true); |
| 83 | |
| 84 | health_globals.config.run_at_least_every_seconds = |
| 85 | (int)inicfg_get_duration_seconds(&netdata_config, CONFIG_SECTION_HEALTH, "run at least every", |
| 86 | health_globals.config.run_at_least_every_seconds); |
| 87 | |
| 88 | health_globals.config.postpone_alarms_during_hibernation_for_seconds = |
| 89 | inicfg_get_duration_seconds(&netdata_config, CONFIG_SECTION_HEALTH, |
| 90 | "postpone alarms during hibernation for", |
| 91 | health_globals.config.postpone_alarms_during_hibernation_for_seconds); |
| 92 | |
| 93 | time_t notification_execution_timeout = |
| 94 | inicfg_get_duration_seconds(&netdata_config, CONFIG_SECTION_HEALTH, |
| 95 | "notification execution timeout", |
| 96 | health_globals.config.notification_execution_timeout_seconds); |
| 97 | // clamp to [0, INT32_MAX] before narrowing to int32: the upper clamp prevents a huge |
| 98 | // value from overflowing into a negative, the lower clamp normalizes negatives to 0. |
| 99 | // 0 means "wait forever". |
| 100 | if(notification_execution_timeout < 0) notification_execution_timeout = 0; |
| 101 | if(notification_execution_timeout > INT32_MAX) notification_execution_timeout = INT32_MAX; |
| 102 | health_globals.config.notification_execution_timeout_seconds = (int32_t)notification_execution_timeout; |
| 103 | |
| 104 | health_globals.config.default_recipient = |
| 105 | string_strdupz("root"); |
| 106 | |
| 107 | // ------------------------------------------------------------------------ |
| 108 | // verify after loading |
| 109 | |
| 110 | if(health_globals.config.run_at_least_every_seconds < 1) |
| 111 | health_globals.config.run_at_least_every_seconds = 1; |
| 112 | |
| 113 | if(health_globals.config.health_log_entries_max < HEALTH_LOG_ENTRIES_MIN) { |
| 114 | nd_log(NDLS_DAEMON, NDLP_WARNING, |
| 115 | "Health configuration has invalid max log entries %u, using minimum of %u", |
| 116 | health_globals.config.health_log_entries_max, |
| 117 | HEALTH_LOG_ENTRIES_MIN); |
| 118 | |
| 119 | health_globals.config.health_log_entries_max = HEALTH_LOG_ENTRIES_MIN; |
| 120 | inicfg_set_number(&netdata_config, CONFIG_SECTION_HEALTH, "in memory max health log entries", |
| 121 | (long)health_globals.config.health_log_entries_max); |
| 122 | } |
| 123 | else if(health_globals.config.health_log_entries_max > HEALTH_LOG_ENTRIES_MAX) { |
| 124 | nd_log(NDLS_DAEMON, NDLP_WARNING, |
| 125 | "Health configuration has invalid max log entries %u, using maximum of %u", |
| 126 | health_globals.config.health_log_entries_max, |
| 127 | HEALTH_LOG_ENTRIES_MAX); |
| 128 | |
| 129 | health_globals.config.health_log_entries_max = HEALTH_LOG_ENTRIES_MAX; |
| 130 | inicfg_set_number(&netdata_config, CONFIG_SECTION_HEALTH, "in memory max health log entries", |
| 131 | (long)health_globals.config.health_log_entries_max); |
| 132 | } |
| 133 | |
| 134 | if (health_globals.config.health_log_retention_s < HEALTH_LOG_MINIMUM_HISTORY) { |
| 135 | nd_log(NDLS_DAEMON, NDLP_WARNING, |
| 136 | "Health configuration has invalid health log retention %u. Using minimum %d", |
| 137 | health_globals.config.health_log_retention_s, HEALTH_LOG_MINIMUM_HISTORY); |
| 138 | |
| 139 | health_globals.config.health_log_retention_s = HEALTH_LOG_MINIMUM_HISTORY; |
| 140 | inicfg_set_duration_seconds(&netdata_config, CONFIG_SECTION_HEALTH, "health log retention", health_globals.config.health_log_retention_s); |
| 141 | } |
| 142 | |
| 143 | nd_log(NDLS_DAEMON, NDLP_DEBUG, |
| 144 | "Health log history is set to %u seconds (%u days)", |
| 145 | health_globals.config.health_log_retention_s, health_globals.config.health_log_retention_s / 86400); |
| 146 | } |
| 147 | |
| 148 | inline const char *health_user_config_dir(void) { |
| 149 | char buffer[FILENAME_MAX + 1]; |
| 150 | snprintfz(buffer, FILENAME_MAX, "%s/health.d", netdata_configured_user_config_dir); |
| 151 | return inicfg_get_path(&netdata_config, CONFIG_SECTION_DIRECTORIES, "health config", buffer); |
| 152 | } |
| 153 | |
| 154 | inline const char *health_stock_config_dir(void) { |
| 155 | char buffer[FILENAME_MAX + 1]; |
| 156 | snprintfz(buffer, FILENAME_MAX, "%s/health.d", netdata_configured_stock_config_dir); |
| 157 | return inicfg_get_path(&netdata_config, CONFIG_SECTION_DIRECTORIES, "stock health config", buffer); |
| 158 | } |
| 159 | |
| 160 | void health_plugin_init(void) { |
| 161 | spinlock_lock(&health_globals.initialization.spinlock); |
| 162 | |
| 163 | if(health_globals.initialization.done) |
| 164 | goto cleanup; |
| 165 | |
| 166 | health_globals.initialization.done = true; |
| 167 | |
| 168 | health_alarm_entry_aral_init(); |
| 169 | health_init_prototypes(); |
| 170 | health_load_config_defaults(); |
| 171 | |
| 172 | if(!health_plugin_enabled()) |
| 173 | goto cleanup; |
| 174 | |
| 175 | health_reload_prototypes(); |
| 176 | health_silencers_init(); |
| 177 | |
| 178 | cleanup: |
| 179 | spinlock_unlock(&health_globals.initialization.spinlock); |
| 180 | } |
| 181 | |
| 182 | void health_plugin_destroy(void) { |
| 183 | if(!health_globals.initialization.done) |
| 184 | return; |
| 185 | |
| 186 | spinlock_lock(&health_globals.initialization.spinlock); |
| 187 | |
| 188 | // Clean up health prototypes dictionary |
| 189 | if(health_globals.prototypes.dict) { |
| 190 | dictionary_destroy(health_globals.prototypes.dict); |
| 191 | health_globals.prototypes.dict = NULL; |
| 192 | } |
| 193 | |
| 194 | // Free allocated strings |
| 195 | string_freez(health_globals.config.default_exec); |
| 196 | string_freez(health_globals.config.default_recipient); |
| 197 | string_freez(health_globals.config.silencers_filename); |
| 198 | |
| 199 | // Free the enabled_alerts pattern |
| 200 | simple_pattern_free(health_globals.config.enabled_alerts); |
| 201 | |
| 202 | // Reset pointers to NULL |
| 203 | health_globals.config.default_exec = NULL; |
| 204 | health_globals.config.default_recipient = NULL; |
| 205 | health_globals.config.silencers_filename = NULL; |
| 206 | health_globals.config.enabled_alerts = NULL; |
| 207 | |
| 208 | alert_variable_lookup_cleanup(); |
| 209 | |
| 210 | health_globals.initialization.done = false; |
| 211 | |
| 212 | spinlock_unlock(&health_globals.initialization.spinlock); |
| 213 | } |
| 214 | |
| 215 | void health_plugin_reload(void) { |
| 216 | health_reload_prototypes(); |
| 217 | health_apply_prototypes_to_all_hosts(); |
| 218 | } |