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