Merge branch 'sh/collectd-4.5' into sh/collectd-4.6
[collectd.git] / src / target_notification.c
1 /**
2  * collectd - src/target_notification.c
3  * Copyright (C) 2008  Florian Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Florian Forster <octo at verplant.org>
20  **/
21
22 /*
23  * First tell the compiler to stick to the C99 and POSIX standards as close as
24  * possible.
25  */
26 #ifndef __STRICT_ANSI__ /* {{{ */
27 # define __STRICT_ANSI__
28 #endif
29
30 #ifndef _ISOC99_SOURCE
31 # define _ISOC99_SOURCE
32 #endif
33
34 #ifdef _POSIX_C_SOURCE
35 # undef _POSIX_C_SOURCE
36 #endif
37 #define _POSIX_C_SOURCE 200112L
38
39 #if 0
40 /* Single UNIX needed for strdup. */
41 #ifdef _XOPEN_SOURCE
42 # undef _XOPEN_SOURCE
43 #endif
44 #define _XOPEN_SOURCE 500
45 #endif
46
47 #ifndef _REENTRANT
48 # define _REENTRANT
49 #endif
50
51 #ifndef _THREAD_SAFE
52 # define _THREAD_SAFE
53 #endif
54
55 #ifdef _GNU_SOURCE
56 # undef _GNU_SOURCE
57 #endif
58 /* }}} */
59
60 #include "collectd.h"
61 #include "common.h"
62 #include "filter_chain.h"
63 #include "utils_cache.h"
64 #include "utils_subst.h"
65
66 struct tn_data_s
67 {
68   int severity;
69   char *message;
70 };
71 typedef struct tn_data_s tn_data_t;
72
73 static int tn_config_add_severity (tn_data_t *data, /* {{{ */
74     const oconfig_item_t *ci)
75 {
76   if ((ci->values_num != 1)
77       || (ci->values[0].type != OCONFIG_TYPE_STRING))
78   {
79     ERROR ("Target `notification': The `%s' option requires exactly one string "
80         "argument.", ci->key);
81     return (-1);
82   }
83
84   if ((strcasecmp ("FAILURE", ci->values[0].value.string) == 0)
85       || (strcasecmp ("CRITICAL", ci->values[0].value.string) == 0))
86     data->severity = NOTIF_FAILURE;
87   else if ((strcasecmp ("WARNING", ci->values[0].value.string) == 0)
88       || (strcasecmp ("WARN", ci->values[0].value.string) == 0))
89     data->severity = NOTIF_WARNING;
90   else if (strcasecmp ("OKAY", ci->values[0].value.string) == 0)
91     data->severity = NOTIF_OKAY;
92   else
93   {
94     WARNING ("Target `notification': Unknown severity `%s'. "
95         "Will use `FAILURE' instead.",
96         ci->values[0].value.string);
97     data->severity = NOTIF_FAILURE;
98   }
99
100   return (0);
101 } /* }}} int tn_config_add_severity */
102
103 static int tn_config_add_string (char **dest, /* {{{ */
104     const oconfig_item_t *ci)
105 {
106   char *temp;
107
108   if (dest == NULL)
109     return (-EINVAL);
110
111   if ((ci->values_num != 1)
112       || (ci->values[0].type != OCONFIG_TYPE_STRING))
113   {
114     ERROR ("Target `notification': The `%s' option requires exactly one string "
115         "argument.", ci->key);
116     return (-1);
117   }
118
119   if (ci->values[0].value.string[0] == 0)
120   {
121     ERROR ("Target `notification': The `%s' option does not accept empty strings.",
122         ci->key);
123     return (-1);
124   }
125
126   temp = sstrdup (ci->values[0].value.string);
127   if (temp == NULL)
128   {
129     ERROR ("tn_config_add_string: sstrdup failed.");
130     return (-1);
131   }
132
133   free (*dest);
134   *dest = temp;
135
136   return (0);
137 } /* }}} int tn_config_add_string */
138
139 static int tn_destroy (void **user_data) /* {{{ */
140 {
141   tn_data_t *data;
142
143   if (user_data == NULL)
144     return (-EINVAL);
145
146   data = *user_data;
147   if (data == NULL)
148     return (0);
149
150   sfree (data->message);
151   sfree (data);
152
153   return (0);
154 } /* }}} int tn_destroy */
155
156 static int tn_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
157 {
158   tn_data_t *data;
159   int status;
160   int i;
161
162   data = (tn_data_t *) malloc (sizeof (*data));
163   if (data == NULL)
164   {
165     ERROR ("tn_create: malloc failed.");
166     return (-ENOMEM);
167   }
168   memset (data, 0, sizeof (*data));
169
170   data->message = NULL;
171   data->severity = 0;
172
173   status = 0;
174   for (i = 0; i < ci->children_num; i++)
175   {
176     oconfig_item_t *child = ci->children + i;
177
178     if (strcasecmp ("Message", child->key) == 0)
179       status = tn_config_add_string (&data->message, child);
180     else if (strcasecmp ("Severity", child->key) == 0)
181       status = tn_config_add_severity (data, child);
182     else
183     {
184       ERROR ("Target `notification': The `%s' configuration option is not understood "
185           "and will be ignored.", child->key);
186       status = 0;
187     }
188
189     if (status != 0)
190       break;
191   }
192
193   /* Additional sanity-checking */
194   while (status == 0)
195   {
196     if ((data->severity != NOTIF_FAILURE)
197         && (data->severity != NOTIF_WARNING)
198         && (data->severity != NOTIF_OKAY))
199     {
200       DEBUG ("Target `notification': Setting "
201           "the default severity `WARNING'.");
202       data->severity = NOTIF_WARNING;
203     }
204
205     if (data->message == NULL)
206     {
207       ERROR ("Target `notification': No `Message' option has been specified. "
208           "Without it, the `Notification' target is useless.");
209       status = -1;
210     }
211
212     break;
213   }
214
215   if (status != 0)
216   {
217     tn_destroy ((void *) data);
218     return (status);
219   }
220
221   *user_data = data;
222   return (0);
223 } /* }}} int tn_create */
224
225 static int tn_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */
226     notification_meta_t __attribute__((unused)) **meta, void **user_data)
227 {
228   tn_data_t *data;
229   notification_t n;
230   char temp[NOTIF_MAX_MSG_LEN];
231
232   gauge_t *rates;
233   int rates_failed;
234
235   int i;
236
237   if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
238     return (-EINVAL);
239
240   data = *user_data;
241   if (data == NULL)
242   {
243     ERROR ("Target `notification': Invoke: `data' is NULL.");
244     return (-EINVAL);
245   }
246
247   /* Initialize the structure. */
248   memset (&n, 0, sizeof (n));
249   n.severity = data->severity;
250   n.time = time (NULL);
251   sstrncpy (n.message, data->message, sizeof (n.message));
252   sstrncpy (n.host, vl->host, sizeof (n.host));
253   sstrncpy (n.plugin, vl->plugin, sizeof (n.plugin));
254   sstrncpy (n.plugin_instance, vl->plugin_instance,
255       sizeof (n.plugin_instance));
256   sstrncpy (n.type, vl->type, sizeof (n.type));
257   sstrncpy (n.type_instance, vl->type_instance,
258       sizeof (n.type_instance));
259   n.meta = NULL;
260
261 #define REPLACE_FIELD(t,v) \
262   if (subst_string (temp, sizeof (temp), n.message, t, v) != NULL) \
263     sstrncpy (n.message, temp, sizeof (n.message));
264   REPLACE_FIELD ("%{host}", n.host);
265   REPLACE_FIELD ("%{plugin}", n.plugin);
266   REPLACE_FIELD ("%{plugin_instance}", n.plugin_instance);
267   REPLACE_FIELD ("%{type}", n.type);
268   REPLACE_FIELD ("%{type_instance}", n.type_instance);
269
270   rates_failed = 0;
271   rates = NULL;
272   for (i = 0; i < ds->ds_num; i++)
273   {
274     char template[DATA_MAX_NAME_LEN];
275     char value_str[DATA_MAX_NAME_LEN];
276
277     ssnprintf (template, sizeof (template), "%%{ds:%s}", ds->ds[i].name);
278
279     if (ds->ds[i].type != DS_TYPE_GAUGE)
280     {
281       if ((rates == NULL) && (rates_failed == 0))
282       {
283         rates = uc_get_rate (ds, vl);
284         if (rates == NULL)
285           rates_failed = 1;
286       }
287     }
288
289     /* If this is a gauge value, use the current value. */
290     if (ds->ds[i].type == DS_TYPE_GAUGE)
291       ssnprintf (value_str, sizeof (value_str),
292           "%g", (double) vl->values[i].gauge);
293     /* If it's a counter, try to use the current rate. This may fail, if the
294      * value has been renamed. */
295     else if (rates != NULL)
296       ssnprintf (value_str, sizeof (value_str),
297           "%g", (double) rates[i]);
298     /* Since we don't know any better, use the string `unknown'. */
299     else
300       sstrncpy (value_str, "unknown", sizeof (value_str));
301
302     REPLACE_FIELD (template, value_str);
303   }
304   sfree (rates);
305
306   plugin_dispatch_notification (&n);
307
308   return (FC_TARGET_CONTINUE);
309 } /* }}} int tn_invoke */
310
311 void module_register (void)
312 {
313         target_proc_t tproc;
314
315         memset (&tproc, 0, sizeof (tproc));
316         tproc.create  = tn_create;
317         tproc.destroy = tn_destroy;
318         tproc.invoke  = tn_invoke;
319         fc_register_target ("notification", tproc);
320 } /* module_register */
321
322 /* vim: set sw=2 sts=2 tw=78 et fdm=marker : */
323