1 /**
2  * collectd - src/target_notification.c
3  * Copyright (C) 2008       Florian Forster
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Florian Forster <octo at collectd.org>
25  **/
26 
27 #include "collectd.h"
28 
29 #include "filter_chain.h"
30 #include "utils/common/common.h"
31 #include "utils_cache.h"
32 #include "utils_subst.h"
33 
34 struct tn_data_s {
35   int severity;
36   char *message;
37 };
38 typedef struct tn_data_s tn_data_t;
39 
tn_config_add_severity(tn_data_t * data,const oconfig_item_t * ci)40 static int tn_config_add_severity(tn_data_t *data, /* {{{ */
41                                   const oconfig_item_t *ci) {
42   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
43     ERROR("Target `notification': The `%s' option requires exactly one string "
44           "argument.",
45           ci->key);
46     return -1;
47   }
48 
49   if ((strcasecmp("FAILURE", ci->values[0].value.string) == 0) ||
50       (strcasecmp("CRITICAL", ci->values[0].value.string) == 0))
51     data->severity = NOTIF_FAILURE;
52   else if ((strcasecmp("WARNING", ci->values[0].value.string) == 0) ||
53            (strcasecmp("WARN", ci->values[0].value.string) == 0))
54     data->severity = NOTIF_WARNING;
55   else if (strcasecmp("OKAY", ci->values[0].value.string) == 0)
56     data->severity = NOTIF_OKAY;
57   else {
58     WARNING("Target `notification': Unknown severity `%s'. "
59             "Will use `FAILURE' instead.",
60             ci->values[0].value.string);
61     data->severity = NOTIF_FAILURE;
62   }
63 
64   return 0;
65 } /* }}} int tn_config_add_severity */
66 
tn_config_add_string(char ** dest,const oconfig_item_t * ci)67 static int tn_config_add_string(char **dest, /* {{{ */
68                                 const oconfig_item_t *ci) {
69   char *temp;
70 
71   if (dest == NULL)
72     return -EINVAL;
73 
74   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
75     ERROR("Target `notification': The `%s' option requires exactly one string "
76           "argument.",
77           ci->key);
78     return -1;
79   }
80 
81   if (ci->values[0].value.string[0] == 0) {
82     ERROR(
83         "Target `notification': The `%s' option does not accept empty strings.",
84         ci->key);
85     return -1;
86   }
87 
88   temp = sstrdup(ci->values[0].value.string);
89   if (temp == NULL) {
90     ERROR("tn_config_add_string: sstrdup failed.");
91     return -1;
92   }
93 
94   free(*dest);
95   *dest = temp;
96 
97   return 0;
98 } /* }}} int tn_config_add_string */
99 
tn_destroy(void ** user_data)100 static int tn_destroy(void **user_data) /* {{{ */
101 {
102   tn_data_t *data;
103 
104   if (user_data == NULL)
105     return -EINVAL;
106 
107   data = *user_data;
108   if (data == NULL)
109     return 0;
110 
111   sfree(data->message);
112   sfree(data);
113 
114   return 0;
115 } /* }}} int tn_destroy */
116 
tn_create(const oconfig_item_t * ci,void ** user_data)117 static int tn_create(const oconfig_item_t *ci, void **user_data) /* {{{ */
118 {
119   tn_data_t *data;
120   int status;
121 
122   data = calloc(1, sizeof(*data));
123   if (data == NULL) {
124     ERROR("tn_create: calloc failed.");
125     return -ENOMEM;
126   }
127 
128   data->message = NULL;
129   data->severity = 0;
130 
131   status = 0;
132   for (int i = 0; i < ci->children_num; i++) {
133     oconfig_item_t *child = ci->children + i;
134 
135     if (strcasecmp("Message", child->key) == 0)
136       status = tn_config_add_string(&data->message, child);
137     else if (strcasecmp("Severity", child->key) == 0)
138       status = tn_config_add_severity(data, child);
139     else {
140       ERROR("Target `notification': The `%s' configuration option is not "
141             "understood "
142             "and will be ignored.",
143             child->key);
144       status = 0;
145     }
146 
147     if (status != 0)
148       break;
149   }
150 
151   /* Additional sanity-checking */
152   while (status == 0) {
153     if ((data->severity != NOTIF_FAILURE) &&
154         (data->severity != NOTIF_WARNING) && (data->severity != NOTIF_OKAY)) {
155       DEBUG("Target `notification': Setting "
156             "the default severity `WARNING'.");
157       data->severity = NOTIF_WARNING;
158     }
159 
160     if (data->message == NULL) {
161       ERROR("Target `notification': No `Message' option has been specified. "
162             "Without it, the `Notification' target is useless.");
163       status = -1;
164     }
165 
166     break;
167   }
168 
169   if (status != 0) {
170     tn_destroy((void *)&data);
171     return status;
172   }
173 
174   *user_data = data;
175   return 0;
176 } /* }}} int tn_create */
177 
tn_invoke(const data_set_t * ds,value_list_t * vl,notification_meta_t ** meta,void ** user_data)178 static int tn_invoke(const data_set_t *ds, value_list_t *vl, /* {{{ */
179                      notification_meta_t __attribute__((unused)) * *meta,
180                      void **user_data) {
181   tn_data_t *data;
182   notification_t n = {0};
183   char temp[NOTIF_MAX_MSG_LEN];
184 
185   gauge_t *rates;
186   int rates_failed;
187 
188   if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
189     return -EINVAL;
190 
191   data = *user_data;
192   if (data == NULL) {
193     ERROR("Target `notification': Invoke: `data' is NULL.");
194     return -EINVAL;
195   }
196 
197   /* Initialize the structure. */
198   n.severity = data->severity;
199   n.time = cdtime();
200   sstrncpy(n.message, data->message, sizeof(n.message));
201   sstrncpy(n.host, vl->host, sizeof(n.host));
202   sstrncpy(n.plugin, vl->plugin, sizeof(n.plugin));
203   sstrncpy(n.plugin_instance, vl->plugin_instance, sizeof(n.plugin_instance));
204   sstrncpy(n.type, vl->type, sizeof(n.type));
205   sstrncpy(n.type_instance, vl->type_instance, sizeof(n.type_instance));
206   n.meta = NULL;
207 
208 #define REPLACE_FIELD(t, v)                                                    \
209   if (subst_string(temp, sizeof(temp), n.message, t, v) != NULL)               \
210     sstrncpy(n.message, temp, sizeof(n.message));
211   REPLACE_FIELD("%{host}", n.host);
212   REPLACE_FIELD("%{plugin}", n.plugin);
213   REPLACE_FIELD("%{plugin_instance}", n.plugin_instance);
214   REPLACE_FIELD("%{type}", n.type);
215   REPLACE_FIELD("%{type_instance}", n.type_instance);
216 
217   rates_failed = 0;
218   rates = NULL;
219 
220   for (size_t i = 0; i < ds->ds_num; i++) {
221     char template[DATA_MAX_NAME_LEN];
222     char value_str[DATA_MAX_NAME_LEN];
223 
224     const char *format = "%%{ds:%.*s}";
225     snprintf(template, sizeof(template), format,
226              DATA_MAX_NAME_LEN - strlen(format), ds->ds[i].name);
227 
228     if (ds->ds[i].type != DS_TYPE_GAUGE) {
229       if ((rates == NULL) && (rates_failed == 0)) {
230         rates = uc_get_rate(ds, vl);
231         if (rates == NULL)
232           rates_failed = 1;
233       }
234     }
235 
236     /* If this is a gauge value, use the current value. */
237     if (ds->ds[i].type == DS_TYPE_GAUGE)
238       snprintf(value_str, sizeof(value_str), GAUGE_FORMAT,
239                (double)vl->values[i].gauge);
240     /* If it's a counter, try to use the current rate. This may fail, if the
241      * value has been renamed. */
242     else if (rates != NULL)
243       snprintf(value_str, sizeof(value_str), GAUGE_FORMAT, (double)rates[i]);
244     /* Since we don't know any better, use the string `unknown'. */
245     else
246       sstrncpy(value_str, "unknown", sizeof(value_str));
247 
248     REPLACE_FIELD(template, value_str);
249   }
250   sfree(rates);
251 
252   plugin_dispatch_notification(&n);
253 
254   return FC_TARGET_CONTINUE;
255 } /* }}} int tn_invoke */
256 
module_register(void)257 void module_register(void) {
258   target_proc_t tproc = {0};
259 
260   tproc.create = tn_create;
261   tproc.destroy = tn_destroy;
262   tproc.invoke = tn_invoke;
263   fc_register_target("notification", tproc);
264 } /* module_register */
265