a002131cb69c0ea8ad05cc3e8eb08229fca3fc0e
[collectd.git] / src / utils_threshold.c
1 /**
2  * collectd - src/utils_threshold.c
3  * Copyright (C) 2007-2010  Florian octo Forster
4  * Copyright (C) 2008-2009  Sebastian Harl
5  * Copyright (C) 2009,2010  Andrés J. Díaz
6  *
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by the
10  * Free Software Foundation; only version 2 of the License is applicable.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
20  *
21  * Author:
22  *   Florian octo Forster <octo at verplant.org>
23  *   Sebastian Harl <sh at tokkee.org>
24  *   Andrés J. Díaz <ajdiaz at connectical.com>
25  **/
26
27 #include "collectd.h"
28 #include "common.h"
29 #include "plugin.h"
30 #include "utils_avltree.h"
31 #include "utils_cache.h"
32 #include "utils_threshold.h"
33 #include "utils_subst.h"
34
35 #include <assert.h>
36 #include <pthread.h>
37
38 /*
39  * Private data structures
40  * {{{ */
41 #define UT_FLAG_INVERT  0x01
42 #define UT_FLAG_PERSIST 0x02
43 #define UT_FLAG_PERCENTAGE 0x04
44 #define UT_FLAG_INTERESTING 0x08
45 /* }}} */
46
47 /*
48  * Private (static) variables
49  * {{{ */
50 static c_avl_tree_t   *threshold_tree = NULL;
51 static pthread_mutex_t threshold_lock = PTHREAD_MUTEX_INITIALIZER;
52 /* }}} */
53
54 /*
55  * Threshold management
56  * ====================
57  * The following functions add, delete, search, etc. configured thresholds to
58  * the underlying AVL trees.
59  * {{{ */
60 static threshold_t *threshold_get (const char *hostname,
61     const char *plugin, const char *plugin_instance,
62     const char *type, const char *type_instance)
63 {
64   char name[6 * DATA_MAX_NAME_LEN];
65   threshold_t *th = NULL;
66
67   format_name (name, sizeof (name),
68       (hostname == NULL) ? "" : hostname,
69       (plugin == NULL) ? "" : plugin, plugin_instance,
70       (type == NULL) ? "" : type, type_instance);
71   name[sizeof (name) - 1] = '\0';
72
73   if (c_avl_get (threshold_tree, name, (void *) &th) == 0)
74     return (th);
75   else
76     return (NULL);
77 } /* threshold_t *threshold_get */
78
79 static int ut_threshold_add (const threshold_t *th)
80 {
81   char name[6 * DATA_MAX_NAME_LEN];
82   char *name_copy;
83   threshold_t *th_copy;
84   threshold_t *th_ptr;
85   int status = 0;
86
87   if (format_name (name, sizeof (name), th->host,
88         th->plugin, th->plugin_instance,
89         th->type, th->type_instance) != 0)
90   {
91     ERROR ("ut_threshold_add: format_name failed.");
92     return (-1);
93   }
94
95   name_copy = strdup (name);
96   if (name_copy == NULL)
97   {
98     ERROR ("ut_threshold_add: strdup failed.");
99     return (-1);
100   }
101
102   th_copy = (threshold_t *) malloc (sizeof (threshold_t));
103   if (th_copy == NULL)
104   {
105     sfree (name_copy);
106     ERROR ("ut_threshold_add: malloc failed.");
107     return (-1);
108   }
109   memcpy (th_copy, th, sizeof (threshold_t));
110   th_ptr = NULL;
111
112   DEBUG ("ut_threshold_add: Adding entry `%s'", name);
113
114   pthread_mutex_lock (&threshold_lock);
115
116   th_ptr = threshold_get (th->host, th->plugin, th->plugin_instance,
117       th->type, th->type_instance);
118
119   while ((th_ptr != NULL) && (th_ptr->next != NULL))
120     th_ptr = th_ptr->next;
121
122   if (th_ptr == NULL) /* no such threshold yet */
123   {
124     status = c_avl_insert (threshold_tree, name_copy, th_copy);
125   }
126   else /* th_ptr points to the last threshold in the list */
127   {
128     th_ptr->next = th_copy;
129     /* name_copy isn't needed */
130     sfree (name_copy);
131   }
132
133   pthread_mutex_unlock (&threshold_lock);
134
135   if (status != 0)
136   {
137     ERROR ("ut_threshold_add: c_avl_insert (%s) failed.", name);
138     sfree (name_copy);
139     sfree (th_copy);
140   }
141
142   return (status);
143 } /* int ut_threshold_add */
144 /*
145  * End of the threshold management functions
146  * }}} */
147
148 /*
149  * Configuration
150  * =============
151  * The following approximately two hundred functions are used to handle the
152  * configuration and fill the threshold list.
153  * {{{ */
154 static int ut_config_type_datasource (threshold_t *th, oconfig_item_t *ci)
155 {
156   if ((ci->values_num != 1)
157       || (ci->values[0].type != OCONFIG_TYPE_STRING))
158   {
159     WARNING ("threshold values: The `DataSource' option needs exactly one "
160         "string argument.");
161     return (-1);
162   }
163
164   sstrncpy (th->data_source, ci->values[0].value.string,
165       sizeof (th->data_source));
166
167   return (0);
168 } /* int ut_config_type_datasource */
169
170 static int ut_config_type_instance (threshold_t *th, oconfig_item_t *ci)
171 {
172   if ((ci->values_num != 1)
173       || (ci->values[0].type != OCONFIG_TYPE_STRING))
174   {
175     WARNING ("threshold values: The `Instance' option needs exactly one "
176         "string argument.");
177     return (-1);
178   }
179
180   sstrncpy (th->type_instance, ci->values[0].value.string,
181       sizeof (th->type_instance));
182
183   return (0);
184 } /* int ut_config_type_instance */
185
186 static int ut_config_type_max (threshold_t *th, oconfig_item_t *ci)
187 {
188   if ((ci->values_num != 1)
189       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
190   {
191     WARNING ("threshold values: The `%s' option needs exactly one "
192         "number argument.", ci->key);
193     return (-1);
194   }
195
196   if (strcasecmp (ci->key, "WarningMax") == 0)
197     th->warning_max = ci->values[0].value.number;
198   else
199     th->failure_max = ci->values[0].value.number;
200
201   return (0);
202 } /* int ut_config_type_max */
203
204 static int ut_config_type_min (threshold_t *th, oconfig_item_t *ci)
205 {
206   if ((ci->values_num != 1)
207       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
208   {
209     WARNING ("threshold values: The `%s' option needs exactly one "
210         "number argument.", ci->key);
211     return (-1);
212   }
213
214   if (strcasecmp (ci->key, "WarningMin") == 0)
215     th->warning_min = ci->values[0].value.number;
216   else
217     th->failure_min = ci->values[0].value.number;
218
219   return (0);
220 } /* int ut_config_type_min */
221
222 static int ut_config_type_hits (threshold_t *th, oconfig_item_t *ci)
223 {
224   if ((ci->values_num != 1)
225       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
226   {
227     WARNING ("threshold values: The `%s' option needs exactly one "
228       "number argument.", ci->key);
229     return (-1);
230   }
231
232   th->hits = ci->values[0].value.number;
233
234   return (0);
235 } /* int ut_config_type_hits */
236
237 static int ut_config_type_hysteresis (threshold_t *th, oconfig_item_t *ci)
238 {
239   if ((ci->values_num != 1)
240       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
241   {
242     WARNING ("threshold values: The `%s' option needs exactly one "
243       "number argument.", ci->key);
244     return (-1);
245   }
246
247   th->hysteresis = ci->values[0].value.number;
248
249   return (0);
250 } /* int ut_config_type_hysteresis */
251
252 static int ut_config_type_message (threshold_t *th, oconfig_item_t *ci)
253 {
254
255   if ((ci->values_num != 1)
256       || (ci->values[0].type != OCONFIG_TYPE_STRING))
257   {
258     WARNING ("threshold values: The `%s' option needs exactly one "
259       "string argument.", ci->key);
260     return (-1);
261   }
262
263   if (ci->values[0].value.string[0] == 0)
264   {
265     WARNING ("threshold values: The `%s' option does not accept empty strings.",
266         ci->key);
267     return (-1);
268   }
269
270   th->message = strdup (ci->values[0].value.string);
271   if (th->message == NULL)
272   {
273     ERROR ("ut_config_type_message: sstrdup failed.");
274     return (-1);
275   }
276
277   return (0);
278 } /* int ut_config_type_message */
279
280 static int ut_config_type_missingmessage (threshold_t *th, oconfig_item_t *ci)
281 {
282
283   if ((ci->values_num != 1)
284       || (ci->values[0].type != OCONFIG_TYPE_STRING))
285   {
286     WARNING ("threshold values: The `%s' option needs exactly one "
287       "string argument.", ci->key);
288     return (-1);
289   }
290
291   if (ci->values[0].value.string[0] == 0)
292   {
293     WARNING ("threshold values: The `%s' option does not accept empty strings.",
294         ci->key);
295     return (-1);
296   }
297
298   th->missing_message = strdup (ci->values[0].value.string);
299   if (th->missing_message == NULL)
300   {
301     ERROR ("ut_config_type_missingmessage: sstrdup failed.");
302     return (-1);
303   }
304
305   return (0);
306 } /* int ut_config_type_missingmessage */
307
308 static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci)
309 {
310   int i;
311   threshold_t th;
312   int status = 0;
313
314   if ((ci->values_num != 1)
315       || (ci->values[0].type != OCONFIG_TYPE_STRING))
316   {
317     WARNING ("threshold values: The `Type' block needs exactly one string "
318         "argument.");
319     return (-1);
320   }
321
322   if (ci->children_num < 1)
323   {
324     WARNING ("threshold values: The `Type' block needs at least one option.");
325     return (-1);
326   }
327
328   memcpy (&th, th_orig, sizeof (th));
329   sstrncpy (th.type, ci->values[0].value.string, sizeof (th.type));
330
331   th.warning_min = NAN;
332   th.warning_max = NAN;
333   th.failure_min = NAN;
334   th.failure_max = NAN;
335   th.hits = 0;
336   th.hysteresis = 0;
337   th.message = NULL;
338   th.flags = UT_FLAG_INTERESTING; /* interesting by default */
339
340   for (i = 0; i < ci->children_num; i++)
341   {
342     oconfig_item_t *option = ci->children + i;
343     status = 0;
344
345     if (strcasecmp ("Instance", option->key) == 0)
346       status = ut_config_type_instance (&th, option);
347     else if (strcasecmp ("DataSource", option->key) == 0)
348       status = ut_config_type_datasource (&th, option);
349     else if ((strcasecmp ("WarningMax", option->key) == 0)
350         || (strcasecmp ("FailureMax", option->key) == 0))
351       status = ut_config_type_max (&th, option);
352     else if ((strcasecmp ("WarningMin", option->key) == 0)
353         || (strcasecmp ("FailureMin", option->key) == 0))
354       status = ut_config_type_min (&th, option);
355     else if (strcasecmp ("Interesting", option->key) == 0)
356       status = cf_util_get_flag (option, &th.flags, UT_FLAG_INTERESTING);
357     else if (strcasecmp ("Invert", option->key) == 0)
358       status = cf_util_get_flag (option, &th.flags, UT_FLAG_INVERT);
359     else if (strcasecmp ("Persist", option->key) == 0)
360       status = cf_util_get_flag (option, &th.flags, UT_FLAG_PERSIST);
361     else if (strcasecmp ("Percentage", option->key) == 0)
362       status = cf_util_get_flag (option, &th.flags, UT_FLAG_PERCENTAGE);
363     else if (strcasecmp ("Hits", option->key) == 0)
364       status = ut_config_type_hits (&th, option);
365     else if (strcasecmp ("Hysteresis", option->key) == 0)
366       status = ut_config_type_hysteresis (&th, option);
367     else if (strcasecmp ("Message", option->key) == 0)
368       status = ut_config_type_message (&th, option);
369     else if (strcasecmp ("MissingMessage", option->key) == 0)
370       status = ut_config_type_missingmessage (&th, option);
371     else
372     {
373       WARNING ("threshold values: Option `%s' not allowed inside a `Type' "
374           "block.", option->key);
375       status = -1;
376     }
377
378     if (status != 0)
379       break;
380   }
381
382   if (status == 0)
383   {
384     status = ut_threshold_add (&th);
385   }
386
387   return (status);
388 } /* int ut_config_type */
389
390 static int ut_config_plugin_instance (threshold_t *th, oconfig_item_t *ci)
391 {
392   if ((ci->values_num != 1)
393       || (ci->values[0].type != OCONFIG_TYPE_STRING))
394   {
395     WARNING ("threshold values: The `Instance' option needs exactly one "
396         "string argument.");
397     return (-1);
398   }
399
400   sstrncpy (th->plugin_instance, ci->values[0].value.string,
401       sizeof (th->plugin_instance));
402
403   return (0);
404 } /* int ut_config_plugin_instance */
405
406 static int ut_config_plugin (const threshold_t *th_orig, oconfig_item_t *ci)
407 {
408   int i;
409   threshold_t th;
410   int status = 0;
411
412   if ((ci->values_num != 1)
413       || (ci->values[0].type != OCONFIG_TYPE_STRING))
414   {
415     WARNING ("threshold values: The `Plugin' block needs exactly one string "
416         "argument.");
417     return (-1);
418   }
419
420   if (ci->children_num < 1)
421   {
422     WARNING ("threshold values: The `Plugin' block needs at least one nested "
423         "block.");
424     return (-1);
425   }
426
427   memcpy (&th, th_orig, sizeof (th));
428   sstrncpy (th.plugin, ci->values[0].value.string, sizeof (th.plugin));
429
430   for (i = 0; i < ci->children_num; i++)
431   {
432     oconfig_item_t *option = ci->children + i;
433     status = 0;
434
435     if (strcasecmp ("Type", option->key) == 0)
436       status = ut_config_type (&th, option);
437     else if (strcasecmp ("Instance", option->key) == 0)
438       status = ut_config_plugin_instance (&th, option);
439     else
440     {
441       WARNING ("threshold values: Option `%s' not allowed inside a `Plugin' "
442           "block.", option->key);
443       status = -1;
444     }
445
446     if (status != 0)
447       break;
448   }
449
450   return (status);
451 } /* int ut_config_plugin */
452
453 static int ut_config_host (const threshold_t *th_orig, oconfig_item_t *ci)
454 {
455   int i;
456   threshold_t th;
457   int status = 0;
458
459   if ((ci->values_num != 1)
460       || (ci->values[0].type != OCONFIG_TYPE_STRING))
461   {
462     WARNING ("threshold values: The `Host' block needs exactly one string "
463         "argument.");
464     return (-1);
465   }
466
467   if (ci->children_num < 1)
468   {
469     WARNING ("threshold values: The `Host' block needs at least one nested "
470         "block.");
471     return (-1);
472   }
473
474   memcpy (&th, th_orig, sizeof (th));
475   sstrncpy (th.host, ci->values[0].value.string, sizeof (th.host));
476
477   for (i = 0; i < ci->children_num; i++)
478   {
479     oconfig_item_t *option = ci->children + i;
480     status = 0;
481
482     if (strcasecmp ("Type", option->key) == 0)
483       status = ut_config_type (&th, option);
484     else if (strcasecmp ("Plugin", option->key) == 0)
485       status = ut_config_plugin (&th, option);
486     else
487     {
488       WARNING ("threshold values: Option `%s' not allowed inside a `Host' "
489           "block.", option->key);
490       status = -1;
491     }
492
493     if (status != 0)
494       break;
495   }
496
497   return (status);
498 } /* int ut_config_host */
499
500 int ut_config (const oconfig_item_t *ci)
501 {
502   int i;
503   int status = 0;
504
505   threshold_t th;
506
507   if (ci->values_num != 0)
508   {
509     ERROR ("threshold values: The `Threshold' block may not have any "
510         "arguments.");
511     return (-1);
512   }
513
514   if (threshold_tree == NULL)
515   {
516     threshold_tree = c_avl_create ((void *) strcmp);
517     if (threshold_tree == NULL)
518     {
519       ERROR ("ut_config: c_avl_create failed.");
520       return (-1);
521     }
522   }
523
524   memset (&th, '\0', sizeof (th));
525   th.warning_min = NAN;
526   th.warning_max = NAN;
527   th.failure_min = NAN;
528   th.failure_max = NAN;
529
530   th.hits = 0;
531   th.hysteresis = 0;
532   th.flags = UT_FLAG_INTERESTING; /* interesting by default */
533     
534   for (i = 0; i < ci->children_num; i++)
535   {
536     oconfig_item_t *option = ci->children + i;
537     status = 0;
538
539     if (strcasecmp ("Type", option->key) == 0)
540       status = ut_config_type (&th, option);
541     else if (strcasecmp ("Plugin", option->key) == 0)
542       status = ut_config_plugin (&th, option);
543     else if (strcasecmp ("Host", option->key) == 0)
544       status = ut_config_host (&th, option);
545     else
546     {
547       WARNING ("threshold values: Option `%s' not allowed here.", option->key);
548       status = -1;
549     }
550
551     if (status != 0)
552       break;
553   }
554
555   return (status);
556 } /* int um_config */
557 /*
558  * End of the functions used to configure threshold values.
559  */
560 /* }}} */
561
562 static threshold_t *threshold_search (const value_list_t *vl)
563 { /* {{{ */
564   threshold_t *th;
565
566   if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
567           vl->type, vl->type_instance)) != NULL)
568     return (th);
569   else if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
570           vl->type, NULL)) != NULL)
571     return (th);
572   else if ((th = threshold_get (vl->host, vl->plugin, NULL,
573           vl->type, vl->type_instance)) != NULL)
574     return (th);
575   else if ((th = threshold_get (vl->host, vl->plugin, NULL,
576           vl->type, NULL)) != NULL)
577     return (th);
578   else if ((th = threshold_get (vl->host, "", NULL,
579           vl->type, vl->type_instance)) != NULL)
580     return (th);
581   else if ((th = threshold_get (vl->host, "", NULL,
582           vl->type, NULL)) != NULL)
583     return (th);
584   else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
585           vl->type, vl->type_instance)) != NULL)
586     return (th);
587   else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
588           vl->type, NULL)) != NULL)
589     return (th);
590   else if ((th = threshold_get ("", vl->plugin, NULL,
591           vl->type, vl->type_instance)) != NULL)
592     return (th);
593   else if ((th = threshold_get ("", vl->plugin, NULL,
594           vl->type, NULL)) != NULL)
595     return (th);
596   else if ((th = threshold_get ("", "", NULL,
597           vl->type, vl->type_instance)) != NULL)
598     return (th);
599   else if ((th = threshold_get ("", "", NULL,
600           vl->type, NULL)) != NULL)
601     return (th);
602
603   return (NULL);
604 } /* }}} threshold_t *threshold_search */
605
606 /* char *ut_build_message
607  *
608  * Return a custom formated message for dataset, values and previously created
609  * notification (which must include time and other fields), if th is present,
610  * templates for threshold will be interpreted, if th is NULL these will be
611  * skipped.
612  */
613 int ut_build_message(char *out, size_t bufsize, const char *fmt,
614     const data_set_t *ds, int ds_index, const value_list_t *vl, const gauge_t *values,
615     const notification_t *n, const threshold_t *th)
616 { /* {{{ */
617
618   /* TODO: We could provide here a way to use meta information on thresholds
619    * directly in the future. */
620   char msg[NOTIF_MAX_MSG_LEN];
621   gauge_t *rates;
622   int rates_failed;
623   int i;
624
625   sstrncpy (msg, fmt, sizeof (msg));
626
627 #define REPLACE_FIELD(t,v) do {                                              \
628   char temp[sizeof (msg)];                                                   \
629   if (subst_string (temp, sizeof (temp), msg, (t), (v)) != NULL)             \
630     sstrncpy (msg, temp, sizeof (msg));                                      \
631 } while (0)
632
633 #define REPLACE_FIELD_DBL(t,v) do {                                          \
634   char value_str[NOTIF_MAX_MSG_LEN];                                         \
635   ssnprintf (value_str, sizeof (value_str), "%g", (v));                      \
636   REPLACE_FIELD(t, value_str);                                               \
637 } while (0)
638
639 #define REPLACE_FIELD_INT(t,v) do {                                          \
640   char value_str[NOTIF_MAX_MSG_LEN];                                         \
641   ssnprintf (value_str, sizeof (value_str), "%i", (v));                      \
642   REPLACE_FIELD(t, value_str);                                               \
643 } while (0)
644
645   REPLACE_FIELD ("%{host}", n->host);
646   REPLACE_FIELD ("%{plugin}", n->plugin);
647   REPLACE_FIELD ("%{plugin_instance}", n->plugin_instance);
648   REPLACE_FIELD ("%{type}", n->type);
649   REPLACE_FIELD ("%{type_instance}", n->type_instance);
650   REPLACE_FIELD ("%{data_source}", ds->ds[ds_index].name);
651
652   /* This is the offending value, its equivalent to %{ds:value}, if
653    * value is the data_source name. */
654   REPLACE_FIELD_DBL ("%{value}", values[ds_index]);
655
656   /* Now replace all %{ds:<template>} like target_notification does */
657   rates_failed = 0;
658   rates = NULL;
659   for (i = 0; i < ds->ds_num; i++)
660   {
661     char template[DATA_MAX_NAME_LEN];
662     char value_str[DATA_MAX_NAME_LEN];
663
664     ssnprintf (template, sizeof (template), "%%{ds:%s}", ds->ds[i].name);
665
666     if (ds->ds[i].type != DS_TYPE_GAUGE)
667     {
668       if ((rates == NULL) && (rates_failed == 0))
669       {
670         rates = uc_get_rate (ds, vl);
671         if (rates == NULL)
672           rates_failed = 1;
673       }
674     }
675
676     /* If this is a gauge value, use the current value. */
677     if (ds->ds[i].type == DS_TYPE_GAUGE)
678       ssnprintf (value_str, sizeof (value_str),
679           "%g", (double) vl->values[i].gauge);
680     /* If it's a counter, try to use the current rate. This may fail, if the
681      * value has been renamed. */
682     else if (rates != NULL)
683       ssnprintf (value_str, sizeof (value_str),
684           "%g", (double) rates[i]);
685     /* Since we don't know any better, use the string `unknown'. */
686     else
687       sstrncpy (value_str, "unknown", sizeof (value_str));
688
689     REPLACE_FIELD (template, value_str);
690   }
691   sfree (rates);
692
693   if (th != NULL)
694   {
695     if ( !isnan(th->warning_min)) {
696       REPLACE_FIELD_DBL ("%{warning_min}", th->warning_min);
697     }
698     if ( !isnan(th->warning_max)) {
699       REPLACE_FIELD_DBL ("%{warning_max}", th->warning_max);
700     }
701     if ( !isnan(th->failure_min)) {
702       REPLACE_FIELD_DBL ("%{failure_min}", th->failure_min);
703     }
704     if ( !isnan(th->failure_max)) {
705       REPLACE_FIELD_DBL ("%{failure_max}", th->failure_max);
706     }
707
708     REPLACE_FIELD_DBL ("%{hysteresis}", th->hysteresis);
709     REPLACE_FIELD_INT ("%{hits}", th->hits);
710   }
711
712   return ssnprintf (out, bufsize, "%s", msg);
713 } /* }}} int ut_build_message */
714
715 /*
716  * int ut_report_state
717  *
718  * Checks if the `state' differs from the old state and creates a notification
719  * if appropriate.
720  * Does not fail.
721  */
722 static int ut_report_state (const data_set_t *ds,
723     const value_list_t *vl,
724     const threshold_t *th,
725     const gauge_t *values,
726     int ds_index,
727     int state)
728 { /* {{{ */
729   int state_old;
730   notification_t n;
731
732   char *buf;
733   size_t bufsize;
734
735   int status;
736
737   /* Check if hits matched */
738   if (th->hits != 0)
739   {
740     int hits = uc_get_hits(ds,vl);
741     /* The STATE_OKAY always reset hits, or if hits reaise the limit */
742     if ((state == STATE_OKAY) || (hits > th->hits))
743     {
744         DEBUG("ut_report_state: reset uc_get_hits = 0");
745         uc_set_hits (ds, vl, 0); /* reset hit counter and notify */
746     }
747     else
748     {
749       DEBUG("ut_report_state: th->hits = %d, uc_get_hits = %d",th->hits,uc_get_hits(ds,vl));
750       (void) uc_inc_hits (ds, vl, 1); /* increase hit counter */
751       return (0);
752     }
753   } /* end check hits */
754
755   state_old = uc_get_state (ds, vl);
756
757   /* If the state didn't change, only report if `persistent' is specified and
758    * the state is not `okay'. */
759   if (state == state_old)
760   {
761     if ((th->flags & UT_FLAG_PERSIST) == 0)
762       return (0);
763     else if (state == STATE_OKAY)
764       return (0);
765   }
766
767   if (state != state_old)
768     uc_set_state (ds, vl, state);
769
770   NOTIFICATION_INIT_VL (&n, vl, ds);
771
772   buf = n.message;
773   bufsize = sizeof (n.message);
774
775   if (state == STATE_OKAY)
776     n.severity = NOTIF_OKAY;
777   else if (state == STATE_WARNING)
778     n.severity = NOTIF_WARNING;
779   else
780     n.severity = NOTIF_FAILURE;
781
782   n.time = vl->time;
783
784   /* Format custom message if present */
785   if (th->message != NULL)
786   {
787     status = ut_build_message (buf, bufsize, th->message,
788         ds, ds_index, vl, values,
789         &n, th);
790     buf += status;
791     bufsize -= status;
792   }
793   else /* No custom message. Using default message for threshold */
794   {
795     status = ssnprintf (buf, bufsize, "Host %s, plugin %s",
796         vl->host, vl->plugin);
797     buf += status;
798     bufsize -= status;
799
800     if (vl->plugin_instance[0] != '\0')
801     {
802       status = ssnprintf (buf, bufsize, " (instance %s)",
803           vl->plugin_instance);
804       buf += status;
805       bufsize -= status;
806     }
807
808     status = ssnprintf (buf, bufsize, " type %s", vl->type);
809     buf += status;
810     bufsize -= status;
811
812     if (vl->type_instance[0] != '\0')
813     {
814       status = ssnprintf (buf, bufsize, " (instance %s)",
815           vl->type_instance);
816       buf += status;
817       bufsize -= status;
818     }
819
820     /* Build okay notification message */
821     if (state == STATE_OKAY)
822     {
823       status = ssnprintf (buf, bufsize, ": All data sources are within range again.");
824       buf += status;
825       bufsize -= status;
826     }
827     else /* build non-okay notification message */
828     {
829       double min;
830       double max;
831
832       min = (state == STATE_ERROR) ? th->failure_min : th->warning_min;
833       max = (state == STATE_ERROR) ? th->failure_max : th->warning_max;
834
835       if (th->flags & UT_FLAG_INVERT)
836       {
837         if (!isnan (min) && !isnan (max))
838         {
839           status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
840               "%f. That is within the %s region of %f%s and %f%s.",
841               ds->ds[ds_index].name, values[ds_index],
842               (state == STATE_ERROR) ? "failure" : "warning",
843               min, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "",
844               max, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
845         }
846         else
847         {
848           status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
849               "%f. That is %s the %s threshold of %f%s.",
850               ds->ds[ds_index].name, values[ds_index],
851               isnan (min) ? "below" : "above",
852               (state == STATE_ERROR) ? "failure" : "warning",
853               isnan (min) ? max : min,
854               ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
855         }
856       }
857       else /* is not inverted */
858       {
859         status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
860             "%f. That is %s the %s threshold of %f%s.",
861             ds->ds[ds_index].name, values[ds_index],
862             (values[ds_index] < min) ? "below" : "above",
863             (state == STATE_ERROR) ? "failure" : "warning",
864             (values[ds_index] < min) ? min : max,
865             ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
866       }
867       buf += status;
868       bufsize -= status;
869     }
870   }
871
872   /* adds meta to notification */
873   plugin_notification_meta_add_string (&n, "DataSource",
874       ds->ds[ds_index].name);
875   plugin_notification_meta_add_double (&n, "CurrentValue", values[ds_index]);
876   plugin_notification_meta_add_double (&n, "WarningMin", th->warning_min);
877   plugin_notification_meta_add_double (&n, "WarningMax", th->warning_max);
878   plugin_notification_meta_add_double (&n, "FailureMin", th->failure_min);
879   plugin_notification_meta_add_double (&n, "FailureMax", th->failure_max);
880
881   plugin_dispatch_notification (&n);
882
883   plugin_notification_meta_free (n.meta);
884   return (0);
885 } /* }}} int ut_report_state */
886
887 /*
888  * int ut_check_one_data_source
889  *
890  * Checks one data source against the given threshold configuration. If the
891  * `DataSource' option is set in the threshold, and the name does NOT match,
892  * `okay' is returned. If the threshold does match, its failure and warning
893  * min and max values are checked and `failure' or `warning' is returned if
894  * appropriate.
895  * Does not fail.
896  */
897 static int ut_check_one_data_source (const data_set_t *ds,
898     const value_list_t __attribute__((unused)) *vl,
899     const threshold_t *th,
900     const gauge_t *values,
901     int ds_index)
902 { /* {{{ */
903   const char *ds_name;
904   int is_warning = 0;
905   int is_failure = 0;
906   int prev_state = STATE_OKAY;
907
908   /* check if this threshold applies to this data source */
909   if (ds != NULL)
910   {
911     ds_name = ds->ds[ds_index].name;
912     if ((th->data_source[0] != 0)
913         && (strcmp (ds_name, th->data_source) != 0))
914       return (STATE_OKAY);
915   }
916
917   if ((th->flags & UT_FLAG_INVERT) != 0)
918   {
919     is_warning--;
920     is_failure--;
921   }
922
923   /* XXX: This is an experimental code, not optimized, not fast, not reliable,
924    * and probably, do not work as you expect. Enjoy! :D */
925   if ( (th->hysteresis > 0) && ((prev_state = uc_get_state(ds,vl)) != STATE_OKAY) )
926   {
927     switch(prev_state)
928     {
929       case STATE_ERROR:
930         if ( (!isnan (th->failure_min) && ((th->failure_min + th->hysteresis) < values[ds_index])) ||
931              (!isnan (th->failure_max) && ((th->failure_max - th->hysteresis) > values[ds_index])) )
932           return (STATE_OKAY);
933         else
934           is_failure++;
935       case STATE_WARNING:
936         if ( (!isnan (th->warning_min) && ((th->warning_min + th->hysteresis) < values[ds_index])) ||
937              (!isnan (th->warning_max) && ((th->warning_max - th->hysteresis) > values[ds_index])) )
938           return (STATE_OKAY);
939         else
940           is_warning++;
941      }
942   }
943   else { /* no hysteresis */
944     if ((!isnan (th->failure_min) && (th->failure_min > values[ds_index]))
945         || (!isnan (th->failure_max) && (th->failure_max < values[ds_index])))
946       is_failure++;
947
948     if ((!isnan (th->warning_min) && (th->warning_min > values[ds_index]))
949         || (!isnan (th->warning_max) && (th->warning_max < values[ds_index])))
950       is_warning++;
951  }
952
953   if (is_failure != 0)
954     return (STATE_ERROR);
955
956   if (is_warning != 0)
957     return (STATE_WARNING);
958
959   return (STATE_OKAY);
960 } /* }}} int ut_check_one_data_source */
961
962 /*
963  * int ut_check_one_threshold
964  *
965  * Checks all data sources of a value list against the given threshold, using
966  * the ut_check_one_data_source function above. Returns the worst status,
967  * which is `okay' if nothing has failed.
968  * Returns less than zero if the data set doesn't have any data sources.
969  */
970 static int ut_check_one_threshold (const data_set_t *ds,
971     const value_list_t *vl,
972     const threshold_t *th,
973     const gauge_t *values,
974     int *ret_ds_index)
975 { /* {{{ */
976   int ret = -1;
977   int ds_index = -1;
978   int i;
979   gauge_t values_copy[ds->ds_num];
980
981   memcpy (values_copy, values, sizeof (values_copy));
982
983   if ((th->flags & UT_FLAG_PERCENTAGE) != 0)
984   {
985     int num = 0;
986     gauge_t sum=0.0;
987
988     if (ds->ds_num == 1)
989     {
990       WARNING ("ut_check_one_threshold: The %s type has only one data "
991           "source, but you have configured to check this as a percentage. "
992           "That doesn't make much sense, because the percentage will always "
993           "be 100%%!", ds->type);
994     }
995
996     /* Prepare `sum' and `num'. */
997     for (i = 0; i < ds->ds_num; i++)
998       if (!isnan (values[i]))
999       {
1000         num++;
1001         sum += values[i];
1002       }
1003
1004     if ((num == 0) /* All data sources are undefined. */
1005         || (sum == 0.0)) /* Sum is zero, cannot calculate percentage. */
1006     {
1007       for (i = 0; i < ds->ds_num; i++)
1008         values_copy[i] = NAN;
1009     }
1010     else /* We can actually calculate the percentage. */
1011     {
1012       for (i = 0; i < ds->ds_num; i++)
1013         values_copy[i] = 100.0 * values[i] / sum;
1014     }
1015   } /* if (UT_FLAG_PERCENTAGE) */
1016
1017   for (i = 0; i < ds->ds_num; i++)
1018   {
1019     int status;
1020
1021     status = ut_check_one_data_source (ds, vl, th, values_copy, i);
1022     if (ret < status)
1023     {
1024       ret = status;
1025       ds_index = i;
1026     }
1027   } /* for (ds->ds_num) */
1028
1029   if (ret_ds_index != NULL)
1030     *ret_ds_index = ds_index;
1031
1032   return (ret);
1033 } /* }}} int ut_check_one_threshold */
1034
1035 /*
1036  * int ut_check_threshold (PUBLIC)
1037  *
1038  * Gets a list of matching thresholds and searches for the worst status by one
1039  * of the thresholds. Then reports that status using the ut_report_state
1040  * function above. 
1041  * Returns zero on success and if no threshold has been configured. Returns
1042  * less than zero on failure.
1043  */
1044 int ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
1045 { /* {{{ */
1046   threshold_t *th;
1047   gauge_t *values;
1048   int status;
1049
1050   int worst_state = -1;
1051   threshold_t *worst_th = NULL;
1052   int worst_ds_index = -1;
1053
1054   if (threshold_tree == NULL)
1055     return (0);
1056
1057   /* Is this lock really necessary? So far, thresholds are only inserted at
1058    * startup. -octo */
1059   pthread_mutex_lock (&threshold_lock);
1060   th = threshold_search (vl);
1061   pthread_mutex_unlock (&threshold_lock);
1062   if (th == NULL)
1063     return (0);
1064
1065   DEBUG ("ut_check_threshold: Found matching threshold(s)");
1066
1067   values = uc_get_rate (ds, vl);
1068   if (values == NULL)
1069     return (0);
1070
1071   while (th != NULL)
1072   {
1073     int ds_index = -1;
1074
1075     status = ut_check_one_threshold (ds, vl, th, values, &ds_index);
1076     if (status < 0)
1077     {
1078       ERROR ("ut_check_threshold: ut_check_one_threshold failed.");
1079       sfree (values);
1080       return (-1);
1081     }
1082
1083     if (worst_state < status)
1084     {
1085       worst_state = status;
1086       worst_th = th;
1087       worst_ds_index = ds_index;
1088     }
1089
1090     th = th->next;
1091   } /* while (th) */
1092
1093   status = ut_report_state (ds, vl, worst_th, values,
1094       worst_ds_index, worst_state);
1095   if (status != 0)
1096   {
1097     ERROR ("ut_check_threshold: ut_report_state failed.");
1098     sfree (values);
1099     return (-1);
1100   }
1101
1102   sfree (values);
1103
1104   return (0);
1105 } /* }}} int ut_check_threshold */
1106
1107 /*
1108  * int ut_check_interesting (PUBLIC)
1109  *
1110  * Given an identification returns
1111  * 0: No threshold is defined.
1112  * 1: A threshold has been found. The flag `persist' is off.
1113  * 2: A threshold has been found. The flag `persist' is on.
1114  *    (That is, it is expected that many notifications are sent until the
1115  *    problem disappears.)
1116  */
1117 int ut_check_interesting (const char *name)
1118 { /* {{{ */
1119   char *name_copy = NULL;
1120   char *host = NULL;
1121   char *plugin = NULL;
1122   char *plugin_instance = NULL;
1123   char *type = NULL;
1124   char *type_instance = NULL;
1125   int status;
1126   data_set_t ds;
1127   value_list_t vl;
1128   threshold_t *th;
1129
1130   /* If there is no tree nothing is interesting. */
1131   if (threshold_tree == NULL)
1132     return (0);
1133
1134   name_copy = strdup (name);
1135   if (name_copy == NULL)
1136   {
1137     ERROR ("ut_check_interesting: strdup failed.");
1138     return (-1);
1139   }
1140
1141   status = parse_identifier (name_copy, &host,
1142       &plugin, &plugin_instance, &type, &type_instance);
1143   if (status != 0)
1144   {
1145     ERROR ("ut_check_interesting: parse_identifier failed.");
1146     sfree (name_copy);
1147     return (-1);
1148   }
1149
1150   memset (&ds, '\0', sizeof (ds));
1151   memset (&vl, '\0', sizeof (vl));
1152
1153   sstrncpy (vl.host, host, sizeof (vl.host));
1154   sstrncpy (vl.plugin, plugin, sizeof (vl.plugin));
1155   if (plugin_instance != NULL)
1156     sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
1157   sstrncpy (ds.type, type, sizeof (ds.type));
1158   sstrncpy (vl.type, type, sizeof (vl.type));
1159   if (type_instance != NULL)
1160     sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
1161
1162   sfree (name_copy);
1163   host = plugin = plugin_instance = type = type_instance = NULL;
1164
1165   th = threshold_search (&vl);
1166   if (th == NULL)
1167     return (0);
1168
1169   if ((th->flags & UT_FLAG_INTERESTING) == 0)
1170     return (0);
1171
1172   if ((th->flags & UT_FLAG_PERSIST) == 0)
1173     return (1);
1174   return (2);
1175 } /* }}} int ut_check_interesting */
1176
1177 int ut_search_threshold (const value_list_t *vl, /* {{{ */
1178     threshold_t *ret_threshold)
1179 {
1180   threshold_t *t;
1181
1182   if (vl == NULL)
1183     return (EINVAL);
1184
1185   t = threshold_search (vl);
1186   if (t == NULL)
1187     return (ENOENT);
1188
1189   memcpy (ret_threshold, t, sizeof (*ret_threshold));
1190   ret_threshold->next = NULL;
1191
1192   return (0);
1193 } /* }}} int ut_search_threshold */
1194
1195 /* vim: set sw=2 ts=8 sts=2 tw=78 et fdm=marker : */