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