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 **/
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
30 #ifndef _ISOC99_SOURCE
31 # define _ISOC99_SOURCE
32 #endif
34 #ifdef _POSIX_C_SOURCE
35 # undef _POSIX_C_SOURCE
36 #endif
37 #define _POSIX_C_SOURCE 200112L
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
47 #ifndef _REENTRANT
48 # define _REENTRANT
49 #endif
51 #ifndef _THREAD_SAFE
52 # define _THREAD_SAFE
53 #endif
55 #ifdef _GNU_SOURCE
56 # undef _GNU_SOURCE
57 #endif
58 /* }}} */
60 #include "collectd.h"
61 #include "common.h"
62 #include "filter_chain.h"
63 #include "utils_cache.h"
64 #include "utils_subst.h"
66 struct tn_data_s
67 {
68 int severity;
69 char *message;
70 };
71 typedef struct tn_data_s tn_data_t;
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 }
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 }
100 return (0);
101 } /* }}} int tn_config_add_severity */
103 static int tn_config_add_string (char **dest, /* {{{ */
104 const oconfig_item_t *ci)
105 {
106 char *temp;
108 if (dest == NULL)
109 return (-EINVAL);
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 }
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 }
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 }
133 free (*dest);
134 *dest = temp;
136 return (0);
137 } /* }}} int tn_config_add_string */
139 static int tn_destroy (void **user_data) /* {{{ */
140 {
141 tn_data_t *data;
143 if (user_data == NULL)
144 return (-EINVAL);
146 data = *user_data;
147 if (data == NULL)
148 return (0);
150 sfree (data->message);
151 sfree (data);
153 return (0);
154 } /* }}} int tn_destroy */
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;
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));
170 data->message = NULL;
171 data->severity = 0;
173 status = 0;
174 for (i = 0; i < ci->children_num; i++)
175 {
176 oconfig_item_t *child = ci->children + i;
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 }
189 if (status != 0)
190 break;
191 }
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 }
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 }
212 break;
213 }
215 if (status != 0)
216 {
217 tn_destroy ((void *) data);
218 return (status);
219 }
221 *user_data = data;
222 return (0);
223 } /* }}} int tn_create */
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];
232 gauge_t *rates;
233 int rates_failed;
235 int i;
237 if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
238 return (-EINVAL);
240 data = *user_data;
241 if (data == NULL)
242 {
243 ERROR ("Target `notification': Invoke: `data' is NULL.");
244 return (-EINVAL);
245 }
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;
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);
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];
277 ssnprintf (template, sizeof (template), "%%{ds:%s}", ds->ds[i].name);
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 }
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));
302 REPLACE_FIELD (template, value_str);
303 }
304 sfree (rates);
306 plugin_dispatch_notification (&n);
308 return (FC_TARGET_CONTINUE);
309 } /* }}} int tn_invoke */
311 void module_register (void)
312 {
313 target_proc_t tproc;
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 */
322 /* vim: set sw=2 sts=2 tw=78 et fdm=marker : */