/** * collectd - src/target_notification.c * Copyright (C) 2008 Florian Forster * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. * * Authors: * Florian Forster **/ #include "collectd.h" #include "filter_chain.h" #include "utils/common/common.h" #include "utils_cache.h" #include "utils_subst.h" struct tn_data_s { int severity; char *message; }; typedef struct tn_data_s tn_data_t; static int tn_config_add_severity(tn_data_t *data, /* {{{ */ const oconfig_item_t *ci) { if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) { ERROR("Target `notification': The `%s' option requires exactly one string " "argument.", ci->key); return -1; } if ((strcasecmp("FAILURE", ci->values[0].value.string) == 0) || (strcasecmp("CRITICAL", ci->values[0].value.string) == 0)) data->severity = NOTIF_FAILURE; else if ((strcasecmp("WARNING", ci->values[0].value.string) == 0) || (strcasecmp("WARN", ci->values[0].value.string) == 0)) data->severity = NOTIF_WARNING; else if (strcasecmp("OKAY", ci->values[0].value.string) == 0) data->severity = NOTIF_OKAY; else { WARNING("Target `notification': Unknown severity `%s'. " "Will use `FAILURE' instead.", ci->values[0].value.string); data->severity = NOTIF_FAILURE; } return 0; } /* }}} int tn_config_add_severity */ static int tn_config_add_string(char **dest, /* {{{ */ const oconfig_item_t *ci) { char *temp; if (dest == NULL) return -EINVAL; if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) { ERROR("Target `notification': The `%s' option requires exactly one string " "argument.", ci->key); return -1; } if (ci->values[0].value.string[0] == 0) { ERROR( "Target `notification': The `%s' option does not accept empty strings.", ci->key); return -1; } temp = sstrdup(ci->values[0].value.string); if (temp == NULL) { ERROR("tn_config_add_string: sstrdup failed."); return -1; } free(*dest); *dest = temp; return 0; } /* }}} int tn_config_add_string */ static int tn_destroy(void **user_data) /* {{{ */ { tn_data_t *data; if (user_data == NULL) return -EINVAL; data = *user_data; if (data == NULL) return 0; sfree(data->message); sfree(data); return 0; } /* }}} int tn_destroy */ static int tn_create(const oconfig_item_t *ci, void **user_data) /* {{{ */ { tn_data_t *data; int status; data = calloc(1, sizeof(*data)); if (data == NULL) { ERROR("tn_create: calloc failed."); return -ENOMEM; } data->message = NULL; data->severity = 0; status = 0; for (int i = 0; i < ci->children_num; i++) { oconfig_item_t *child = ci->children + i; if (strcasecmp("Message", child->key) == 0) status = tn_config_add_string(&data->message, child); else if (strcasecmp("Severity", child->key) == 0) status = tn_config_add_severity(data, child); else { ERROR("Target `notification': The `%s' configuration option is not " "understood " "and will be ignored.", child->key); status = 0; } if (status != 0) break; } /* Additional sanity-checking */ while (status == 0) { if ((data->severity != NOTIF_FAILURE) && (data->severity != NOTIF_WARNING) && (data->severity != NOTIF_OKAY)) { DEBUG("Target `notification': Setting " "the default severity `WARNING'."); data->severity = NOTIF_WARNING; } if (data->message == NULL) { ERROR("Target `notification': No `Message' option has been specified. " "Without it, the `Notification' target is useless."); status = -1; } break; } if (status != 0) { tn_destroy((void *)&data); return status; } *user_data = data; return 0; } /* }}} int tn_create */ static int tn_invoke(const data_set_t *ds, value_list_t *vl, /* {{{ */ notification_meta_t __attribute__((unused)) * *meta, void **user_data) { tn_data_t *data; notification_t n = {0}; char temp[NOTIF_MAX_MSG_LEN]; gauge_t *rates; int rates_failed; if ((ds == NULL) || (vl == NULL) || (user_data == NULL)) return -EINVAL; data = *user_data; if (data == NULL) { ERROR("Target `notification': Invoke: `data' is NULL."); return -EINVAL; } /* Initialize the structure. */ n.severity = data->severity; n.time = cdtime(); sstrncpy(n.message, data->message, sizeof(n.message)); sstrncpy(n.host, vl->host, sizeof(n.host)); sstrncpy(n.plugin, vl->plugin, sizeof(n.plugin)); sstrncpy(n.plugin_instance, vl->plugin_instance, sizeof(n.plugin_instance)); sstrncpy(n.type, vl->type, sizeof(n.type)); sstrncpy(n.type_instance, vl->type_instance, sizeof(n.type_instance)); n.meta = NULL; #define REPLACE_FIELD(t, v) \ if (subst_string(temp, sizeof(temp), n.message, t, v) != NULL) \ sstrncpy(n.message, temp, sizeof(n.message)); REPLACE_FIELD("%{host}", n.host); REPLACE_FIELD("%{plugin}", n.plugin); REPLACE_FIELD("%{plugin_instance}", n.plugin_instance); REPLACE_FIELD("%{type}", n.type); REPLACE_FIELD("%{type_instance}", n.type_instance); rates_failed = 0; rates = NULL; for (size_t i = 0; i < ds->ds_num; i++) { char template[DATA_MAX_NAME_LEN]; char value_str[DATA_MAX_NAME_LEN]; const char *format = "%%{ds:%.*s}"; snprintf(template, sizeof(template), format, DATA_MAX_NAME_LEN - strlen(format), ds->ds[i].name); if (ds->ds[i].type != DS_TYPE_GAUGE) { if ((rates == NULL) && (rates_failed == 0)) { rates = uc_get_rate(ds, vl); if (rates == NULL) rates_failed = 1; } } /* If this is a gauge value, use the current value. */ if (ds->ds[i].type == DS_TYPE_GAUGE) snprintf(value_str, sizeof(value_str), GAUGE_FORMAT, (double)vl->values[i].gauge); /* If it's a counter, try to use the current rate. This may fail, if the * value has been renamed. */ else if (rates != NULL) snprintf(value_str, sizeof(value_str), GAUGE_FORMAT, (double)rates[i]); /* Since we don't know any better, use the string `unknown'. */ else sstrncpy(value_str, "unknown", sizeof(value_str)); REPLACE_FIELD(template, value_str); } sfree(rates); plugin_dispatch_notification(&n); return FC_TARGET_CONTINUE; } /* }}} int tn_invoke */ void module_register(void) { target_proc_t tproc = {0}; tproc.create = tn_create; tproc.destroy = tn_destroy; tproc.invoke = tn_invoke; fc_register_target("notification", tproc); } /* module_register */