Merge pull request #1843 from xinity/mysql-galera-stats
[collectd.git] / src / target_notification.c
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 "common.h"
30 #include "filter_chain.h"
31 #include "utils_cache.h"
32 #include "utils_subst.h"
33
34 struct tn_data_s
35 {
36   int severity;
37   char *message;
38 };
39 typedef struct tn_data_s tn_data_t;
40
41 static int tn_config_add_severity (tn_data_t *data, /* {{{ */
42     const oconfig_item_t *ci)
43 {
44   if ((ci->values_num != 1)
45       || (ci->values[0].type != OCONFIG_TYPE_STRING))
46   {
47     ERROR ("Target `notification': The `%s' option requires exactly one string "
48         "argument.", ci->key);
49     return (-1);
50   }
51
52   if ((strcasecmp ("FAILURE", ci->values[0].value.string) == 0)
53       || (strcasecmp ("CRITICAL", ci->values[0].value.string) == 0))
54     data->severity = NOTIF_FAILURE;
55   else if ((strcasecmp ("WARNING", ci->values[0].value.string) == 0)
56       || (strcasecmp ("WARN", ci->values[0].value.string) == 0))
57     data->severity = NOTIF_WARNING;
58   else if (strcasecmp ("OKAY", ci->values[0].value.string) == 0)
59     data->severity = NOTIF_OKAY;
60   else
61   {
62     WARNING ("Target `notification': Unknown severity `%s'. "
63         "Will use `FAILURE' instead.",
64         ci->values[0].value.string);
65     data->severity = NOTIF_FAILURE;
66   }
67
68   return (0);
69 } /* }}} int tn_config_add_severity */
70
71 static int tn_config_add_string (char **dest, /* {{{ */
72     const oconfig_item_t *ci)
73 {
74   char *temp;
75
76   if (dest == NULL)
77     return (-EINVAL);
78
79   if ((ci->values_num != 1)
80       || (ci->values[0].type != OCONFIG_TYPE_STRING))
81   {
82     ERROR ("Target `notification': The `%s' option requires exactly one string "
83         "argument.", ci->key);
84     return (-1);
85   }
86
87   if (ci->values[0].value.string[0] == 0)
88   {
89     ERROR ("Target `notification': The `%s' option does not accept empty strings.",
90         ci->key);
91     return (-1);
92   }
93
94   temp = sstrdup (ci->values[0].value.string);
95   if (temp == NULL)
96   {
97     ERROR ("tn_config_add_string: sstrdup failed.");
98     return (-1);
99   }
100
101   free (*dest);
102   *dest = temp;
103
104   return (0);
105 } /* }}} int tn_config_add_string */
106
107 static int tn_destroy (void **user_data) /* {{{ */
108 {
109   tn_data_t *data;
110
111   if (user_data == NULL)
112     return (-EINVAL);
113
114   data = *user_data;
115   if (data == NULL)
116     return (0);
117
118   sfree (data->message);
119   sfree (data);
120
121   return (0);
122 } /* }}} int tn_destroy */
123
124 static int tn_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
125 {
126   tn_data_t *data;
127   int status;
128
129   data = calloc (1, sizeof (*data));
130   if (data == NULL)
131   {
132     ERROR ("tn_create: calloc failed.");
133     return (-ENOMEM);
134   }
135
136   data->message = NULL;
137   data->severity = 0;
138
139   status = 0;
140   for (int i = 0; i < ci->children_num; i++)
141   {
142     oconfig_item_t *child = ci->children + i;
143
144     if (strcasecmp ("Message", child->key) == 0)
145       status = tn_config_add_string (&data->message, child);
146     else if (strcasecmp ("Severity", child->key) == 0)
147       status = tn_config_add_severity (data, child);
148     else
149     {
150       ERROR ("Target `notification': The `%s' configuration option is not understood "
151           "and will be ignored.", child->key);
152       status = 0;
153     }
154
155     if (status != 0)
156       break;
157   }
158
159   /* Additional sanity-checking */
160   while (status == 0)
161   {
162     if ((data->severity != NOTIF_FAILURE)
163         && (data->severity != NOTIF_WARNING)
164         && (data->severity != NOTIF_OKAY))
165     {
166       DEBUG ("Target `notification': Setting "
167           "the default severity `WARNING'.");
168       data->severity = NOTIF_WARNING;
169     }
170
171     if (data->message == NULL)
172     {
173       ERROR ("Target `notification': No `Message' option has been specified. "
174           "Without it, the `Notification' target is useless.");
175       status = -1;
176     }
177
178     break;
179   }
180
181   if (status != 0)
182   {
183     tn_destroy ((void *) &data);
184     return (status);
185   }
186
187   *user_data = data;
188   return (0);
189 } /* }}} int tn_create */
190
191 static int tn_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */
192     notification_meta_t __attribute__((unused)) **meta, void **user_data)
193 {
194   tn_data_t *data;
195   notification_t n = { 0 };
196   char temp[NOTIF_MAX_MSG_LEN];
197
198   gauge_t *rates;
199   int rates_failed;
200
201   if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
202     return (-EINVAL);
203
204   data = *user_data;
205   if (data == NULL)
206   {
207     ERROR ("Target `notification': Invoke: `data' is NULL.");
208     return (-EINVAL);
209   }
210
211   /* Initialize the structure. */
212   n.severity = data->severity;
213   n.time = cdtime ();
214   sstrncpy (n.message, data->message, sizeof (n.message));
215   sstrncpy (n.host, vl->host, sizeof (n.host));
216   sstrncpy (n.plugin, vl->plugin, sizeof (n.plugin));
217   sstrncpy (n.plugin_instance, vl->plugin_instance,
218       sizeof (n.plugin_instance));
219   sstrncpy (n.type, vl->type, sizeof (n.type));
220   sstrncpy (n.type_instance, vl->type_instance,
221       sizeof (n.type_instance));
222   n.meta = NULL;
223
224 #define REPLACE_FIELD(t,v) \
225   if (subst_string (temp, sizeof (temp), n.message, t, v) != NULL) \
226     sstrncpy (n.message, temp, sizeof (n.message));
227   REPLACE_FIELD ("%{host}", n.host);
228   REPLACE_FIELD ("%{plugin}", n.plugin);
229   REPLACE_FIELD ("%{plugin_instance}", n.plugin_instance);
230   REPLACE_FIELD ("%{type}", n.type);
231   REPLACE_FIELD ("%{type_instance}", n.type_instance);
232
233   rates_failed = 0;
234   rates = NULL;
235
236   for (size_t i = 0; i < ds->ds_num; i++)
237   {
238     char template[DATA_MAX_NAME_LEN];
239     char value_str[DATA_MAX_NAME_LEN];
240
241     ssnprintf (template, sizeof (template), "%%{ds:%s}", ds->ds[i].name);
242
243     if (ds->ds[i].type != DS_TYPE_GAUGE)
244     {
245       if ((rates == NULL) && (rates_failed == 0))
246       {
247         rates = uc_get_rate (ds, vl);
248         if (rates == NULL)
249           rates_failed = 1;
250       }
251     }
252
253     /* If this is a gauge value, use the current value. */
254     if (ds->ds[i].type == DS_TYPE_GAUGE)
255       ssnprintf (value_str, sizeof (value_str),
256           GAUGE_FORMAT, (double) vl->values[i].gauge);
257     /* If it's a counter, try to use the current rate. This may fail, if the
258      * value has been renamed. */
259     else if (rates != NULL)
260       ssnprintf (value_str, sizeof (value_str),
261           GAUGE_FORMAT, (double) rates[i]);
262     /* Since we don't know any better, use the string `unknown'. */
263     else
264       sstrncpy (value_str, "unknown", sizeof (value_str));
265
266     REPLACE_FIELD (template, value_str);
267   }
268   sfree (rates);
269
270   plugin_dispatch_notification (&n);
271
272   return (FC_TARGET_CONTINUE);
273 } /* }}} int tn_invoke */
274
275 void module_register (void)
276 {
277         target_proc_t tproc = { 0 };
278
279         tproc.create  = tn_create;
280         tproc.destroy = tn_destroy;
281         tproc.invoke  = tn_invoke;
282         fc_register_target ("notification", tproc);
283 } /* module_register */
284
285 /* vim: set sw=2 sts=2 tw=78 et fdm=marker : */
286