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