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 **/
27 #include "collectd.h"
28 #include "common.h"
29 #include "filter_chain.h"
30 #include "utils_cache.h"
31 #include "utils_subst.h"
33 struct tn_data_s
34 {
35 int severity;
36 char *message;
37 };
38 typedef struct tn_data_s tn_data_t;
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 }
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 }
67 return (0);
68 } /* }}} int tn_config_add_severity */
70 static int tn_config_add_string (char **dest, /* {{{ */
71 const oconfig_item_t *ci)
72 {
73 char *temp;
75 if (dest == NULL)
76 return (-EINVAL);
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 }
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 }
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 }
100 free (*dest);
101 *dest = temp;
103 return (0);
104 } /* }}} int tn_config_add_string */
106 static int tn_destroy (void **user_data) /* {{{ */
107 {
108 tn_data_t *data;
110 if (user_data == NULL)
111 return (-EINVAL);
113 data = *user_data;
114 if (data == NULL)
115 return (0);
117 sfree (data->message);
118 sfree (data);
120 return (0);
121 } /* }}} int tn_destroy */
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;
129 data = malloc (sizeof (*data));
130 if (data == NULL)
131 {
132 ERROR ("tn_create: malloc failed.");
133 return (-ENOMEM);
134 }
135 memset (data, 0, sizeof (*data));
137 data->message = NULL;
138 data->severity = 0;
140 status = 0;
141 for (i = 0; i < ci->children_num; i++)
142 {
143 oconfig_item_t *child = ci->children + i;
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 }
156 if (status != 0)
157 break;
158 }
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 }
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 }
179 break;
180 }
182 if (status != 0)
183 {
184 tn_destroy ((void *) &data);
185 return (status);
186 }
188 *user_data = data;
189 return (0);
190 } /* }}} int tn_create */
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];
199 gauge_t *rates;
200 int rates_failed;
202 size_t i;
204 if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
205 return (-EINVAL);
207 data = *user_data;
208 if (data == NULL)
209 {
210 ERROR ("Target `notification': Invoke: `data' is NULL.");
211 return (-EINVAL);
212 }
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;
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);
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];
244 ssnprintf (template, sizeof (template), "%%{ds:%s}", ds->ds[i].name);
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 }
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 GAUGE_FORMAT, (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 GAUGE_FORMAT, (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));
269 REPLACE_FIELD (template, value_str);
270 }
271 sfree (rates);
273 plugin_dispatch_notification (&n);
275 return (FC_TARGET_CONTINUE);
276 } /* }}} int tn_invoke */
278 void module_register (void)
279 {
280 target_proc_t tproc;
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 */
289 /* vim: set sw=2 sts=2 tw=78 et fdm=marker : */