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 #include "collectd.h"
23 #include "common.h"
24 #include "filter_chain.h"
25 #include "utils_cache.h"
26 #include "utils_subst.h"
28 struct tn_data_s
29 {
30 int severity;
31 char *message;
32 };
33 typedef struct tn_data_s tn_data_t;
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 }
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 }
62 return (0);
63 } /* }}} int tn_config_add_severity */
65 static int tn_config_add_string (char **dest, /* {{{ */
66 const oconfig_item_t *ci)
67 {
68 char *temp;
70 if (dest == NULL)
71 return (-EINVAL);
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 }
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 }
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 }
95 free (*dest);
96 *dest = temp;
98 return (0);
99 } /* }}} int tn_config_add_string */
101 static int tn_destroy (void **user_data) /* {{{ */
102 {
103 tn_data_t *data;
105 if (user_data == NULL)
106 return (-EINVAL);
108 data = *user_data;
109 if (data == NULL)
110 return (0);
112 sfree (data->message);
113 sfree (data);
115 return (0);
116 } /* }}} int tn_destroy */
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;
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));
132 data->message = NULL;
133 data->severity = 0;
135 status = 0;
136 for (i = 0; i < ci->children_num; i++)
137 {
138 oconfig_item_t *child = ci->children + i;
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 }
151 if (status != 0)
152 break;
153 }
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 }
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 }
174 break;
175 }
177 if (status != 0)
178 {
179 tn_destroy ((void *) data);
180 return (status);
181 }
183 *user_data = data;
184 return (0);
185 } /* }}} int tn_create */
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];
194 gauge_t *rates;
195 int rates_failed;
197 int i;
199 if ((ds == NULL) || (vl == NULL) || (user_data == NULL))
200 return (-EINVAL);
202 data = *user_data;
203 if (data == NULL)
204 {
205 ERROR ("Target `notification': Invoke: `data' is NULL.");
206 return (-EINVAL);
207 }
209 /* Initialize the structure. */
210 memset (&n, 0, sizeof (n));
211 n.severity = data->severity;
212 n.time = time (NULL);
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;
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);
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];
239 ssnprintf (template, sizeof (template), "%%{ds:%s}", ds->ds[i].name);
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 }
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));
264 REPLACE_FIELD (template, value_str);
265 }
266 sfree (rates);
268 plugin_dispatch_notification (&n);
270 return (FC_TARGET_CONTINUE);
271 } /* }}} int tn_invoke */
273 void module_register (void)
274 {
275 target_proc_t tproc;
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 */
284 /* vim: set sw=2 sts=2 tw=78 et fdm=marker : */