rrdtool plugin: Make sure "cache_timeout + random_variation" dosn't get negative.
[collectd.git] / src / utils_threshold.c
1 /**
2  * collectd - src/utils_threshold.c
3  * Copyright (C) 2007-2009  Florian octo Forster
4  * Copyright (C) 2008-2009  Sebastian Harl
5  * Copyright (C) 2009       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
34 #include <assert.h>
35 #include <pthread.h>
36
37 /*
38  * Private data structures
39  * {{{ */
40 #define UT_FLAG_INVERT  0x01
41 #define UT_FLAG_PERSIST 0x02
42 #define UT_FLAG_PERCENTAGE 0x04
43 #define UT_FLAG_INTERESTING 0x08
44 /* }}} */
45
46 /*
47  * Private (static) variables
48  * {{{ */
49 static c_avl_tree_t   *threshold_tree = NULL;
50 static pthread_mutex_t threshold_lock = PTHREAD_MUTEX_INITIALIZER;
51 /* }}} */
52
53 /*
54  * Threshold management
55  * ====================
56  * The following functions add, delete, search, etc. configured thresholds to
57  * the underlying AVL trees.
58  * {{{ */
59 static threshold_t *threshold_get (const char *hostname,
60     const char *plugin, const char *plugin_instance,
61     const char *type, const char *type_instance)
62 {
63   char name[6 * DATA_MAX_NAME_LEN];
64   threshold_t *th = NULL;
65
66   format_name (name, sizeof (name),
67       (hostname == NULL) ? "" : hostname,
68       (plugin == NULL) ? "" : plugin, plugin_instance,
69       (type == NULL) ? "" : type, type_instance);
70   name[sizeof (name) - 1] = '\0';
71
72   if (c_avl_get (threshold_tree, name, (void *) &th) == 0)
73     return (th);
74   else
75     return (NULL);
76 } /* threshold_t *threshold_get */
77
78 static int ut_threshold_add (const threshold_t *th)
79 {
80   char name[6 * DATA_MAX_NAME_LEN];
81   char *name_copy;
82   threshold_t *th_copy;
83   threshold_t *th_ptr;
84   int status = 0;
85
86   if (format_name (name, sizeof (name), th->host,
87         th->plugin, th->plugin_instance,
88         th->type, th->type_instance) != 0)
89   {
90     ERROR ("ut_threshold_add: format_name failed.");
91     return (-1);
92   }
93
94   name_copy = strdup (name);
95   if (name_copy == NULL)
96   {
97     ERROR ("ut_threshold_add: strdup failed.");
98     return (-1);
99   }
100
101   th_copy = (threshold_t *) malloc (sizeof (threshold_t));
102   if (th_copy == NULL)
103   {
104     sfree (name_copy);
105     ERROR ("ut_threshold_add: malloc failed.");
106     return (-1);
107   }
108   memcpy (th_copy, th, sizeof (threshold_t));
109   th_ptr = NULL;
110
111   DEBUG ("ut_threshold_add: Adding entry `%s'", name);
112
113   pthread_mutex_lock (&threshold_lock);
114
115   th_ptr = threshold_get (th->host, th->plugin, th->plugin_instance,
116       th->type, th->type_instance);
117
118   while ((th_ptr != NULL) && (th_ptr->next != NULL))
119     th_ptr = th_ptr->next;
120
121   if (th_ptr == NULL) /* no such threshold yet */
122   {
123     status = c_avl_insert (threshold_tree, name_copy, th_copy);
124   }
125   else /* th_ptr points to the last threshold in the list */
126   {
127     th_ptr->next = th_copy;
128     /* name_copy isn't needed */
129     sfree (name_copy);
130   }
131
132   pthread_mutex_unlock (&threshold_lock);
133
134   if (status != 0)
135   {
136     ERROR ("ut_threshold_add: c_avl_insert (%s) failed.", name);
137     sfree (name_copy);
138     sfree (th_copy);
139   }
140
141   return (status);
142 } /* int ut_threshold_add */
143 /*
144  * End of the threshold management functions
145  * }}} */
146
147 /*
148  * Configuration
149  * =============
150  * The following approximately two hundred functions are used to handle the
151  * configuration and fill the threshold list.
152  * {{{ */
153 static int ut_config_type_datasource (threshold_t *th, oconfig_item_t *ci)
154 {
155   if ((ci->values_num != 1)
156       || (ci->values[0].type != OCONFIG_TYPE_STRING))
157   {
158     WARNING ("threshold values: The `DataSource' option needs exactly one "
159         "string argument.");
160     return (-1);
161   }
162
163   sstrncpy (th->data_source, ci->values[0].value.string,
164       sizeof (th->data_source));
165
166   return (0);
167 } /* int ut_config_type_datasource */
168
169 static int ut_config_type_instance (threshold_t *th, oconfig_item_t *ci)
170 {
171   if ((ci->values_num != 1)
172       || (ci->values[0].type != OCONFIG_TYPE_STRING))
173   {
174     WARNING ("threshold values: The `Instance' option needs exactly one "
175         "string argument.");
176     return (-1);
177   }
178
179   sstrncpy (th->type_instance, ci->values[0].value.string,
180       sizeof (th->type_instance));
181
182   return (0);
183 } /* int ut_config_type_instance */
184
185 static int ut_config_type_max (threshold_t *th, oconfig_item_t *ci)
186 {
187   if ((ci->values_num != 1)
188       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
189   {
190     WARNING ("threshold values: The `%s' option needs exactly one "
191         "number argument.", ci->key);
192     return (-1);
193   }
194
195   if (strcasecmp (ci->key, "WarningMax") == 0)
196     th->warning_max = ci->values[0].value.number;
197   else
198     th->failure_max = ci->values[0].value.number;
199
200   return (0);
201 } /* int ut_config_type_max */
202
203 static int ut_config_type_min (threshold_t *th, oconfig_item_t *ci)
204 {
205   if ((ci->values_num != 1)
206       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
207   {
208     WARNING ("threshold values: The `%s' option needs exactly one "
209         "number argument.", ci->key);
210     return (-1);
211   }
212
213   if (strcasecmp (ci->key, "WarningMin") == 0)
214     th->warning_min = ci->values[0].value.number;
215   else
216     th->failure_min = ci->values[0].value.number;
217
218   return (0);
219 } /* int ut_config_type_min */
220
221 static int ut_config_type_hits (threshold_t *th, oconfig_item_t *ci)
222 {
223   if ((ci->values_num != 1)
224       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
225   {
226     WARNING ("threshold values: The `%s' option needs exactly one "
227       "number argument.", ci->key);
228     return (-1);
229   }
230
231   th->hits = ci->values[0].value.number;
232
233   return (0);
234 } /* int ut_config_type_hits */
235
236 static int ut_config_type_hysteresis (threshold_t *th, oconfig_item_t *ci)
237 {
238   if ((ci->values_num != 1)
239       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
240   {
241     WARNING ("threshold values: The `%s' option needs exactly one "
242       "number argument.", ci->key);
243     return (-1);
244   }
245
246   th->hysteresis = ci->values[0].value.number;
247
248   return (0);
249 } /* int ut_config_type_hysteresis */
250
251 static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci)
252 {
253   int i;
254   threshold_t th;
255   int status = 0;
256
257   if ((ci->values_num != 1)
258       || (ci->values[0].type != OCONFIG_TYPE_STRING))
259   {
260     WARNING ("threshold values: The `Type' block needs exactly one string "
261         "argument.");
262     return (-1);
263   }
264
265   if (ci->children_num < 1)
266   {
267     WARNING ("threshold values: The `Type' block needs at least one option.");
268     return (-1);
269   }
270
271   memcpy (&th, th_orig, sizeof (th));
272   sstrncpy (th.type, ci->values[0].value.string, sizeof (th.type));
273
274   th.warning_min = NAN;
275   th.warning_max = NAN;
276   th.failure_min = NAN;
277   th.failure_max = NAN;
278   th.hits = 0;
279   th.hysteresis = 0;
280   th.flags = UT_FLAG_INTERESTING; /* interesting by default */
281
282   for (i = 0; i < ci->children_num; i++)
283   {
284     oconfig_item_t *option = ci->children + i;
285     status = 0;
286
287     if (strcasecmp ("Instance", option->key) == 0)
288       status = ut_config_type_instance (&th, option);
289     else if (strcasecmp ("DataSource", option->key) == 0)
290       status = ut_config_type_datasource (&th, option);
291     else if ((strcasecmp ("WarningMax", option->key) == 0)
292         || (strcasecmp ("FailureMax", option->key) == 0))
293       status = ut_config_type_max (&th, option);
294     else if ((strcasecmp ("WarningMin", option->key) == 0)
295         || (strcasecmp ("FailureMin", option->key) == 0))
296       status = ut_config_type_min (&th, option);
297     else if (strcasecmp ("Interesting", option->key) == 0)
298       status = cf_util_get_flag (option, &th.flags, UT_FLAG_INTERESTING);
299     else if (strcasecmp ("Invert", option->key) == 0)
300       status = cf_util_get_flag (option, &th.flags, UT_FLAG_INVERT);
301     else if (strcasecmp ("Persist", option->key) == 0)
302       status = cf_util_get_flag (option, &th.flags, UT_FLAG_PERSIST);
303     else if (strcasecmp ("Percentage", option->key) == 0)
304       status = cf_util_get_flag (option, &th.flags, UT_FLAG_PERCENTAGE);
305     else if (strcasecmp ("Hits", option->key) == 0)
306       status = ut_config_type_hits (&th, option);
307     else if (strcasecmp ("Hysteresis", option->key) == 0)
308       status = ut_config_type_hysteresis (&th, option);
309     else
310     {
311       WARNING ("threshold values: Option `%s' not allowed inside a `Type' "
312           "block.", option->key);
313       status = -1;
314     }
315
316     if (status != 0)
317       break;
318   }
319
320   if (status == 0)
321   {
322     status = ut_threshold_add (&th);
323   }
324
325   return (status);
326 } /* int ut_config_type */
327
328 static int ut_config_plugin_instance (threshold_t *th, oconfig_item_t *ci)
329 {
330   if ((ci->values_num != 1)
331       || (ci->values[0].type != OCONFIG_TYPE_STRING))
332   {
333     WARNING ("threshold values: The `Instance' option needs exactly one "
334         "string argument.");
335     return (-1);
336   }
337
338   sstrncpy (th->plugin_instance, ci->values[0].value.string,
339       sizeof (th->plugin_instance));
340
341   return (0);
342 } /* int ut_config_plugin_instance */
343
344 static int ut_config_plugin (const threshold_t *th_orig, oconfig_item_t *ci)
345 {
346   int i;
347   threshold_t th;
348   int status = 0;
349
350   if ((ci->values_num != 1)
351       || (ci->values[0].type != OCONFIG_TYPE_STRING))
352   {
353     WARNING ("threshold values: The `Plugin' block needs exactly one string "
354         "argument.");
355     return (-1);
356   }
357
358   if (ci->children_num < 1)
359   {
360     WARNING ("threshold values: The `Plugin' block needs at least one nested "
361         "block.");
362     return (-1);
363   }
364
365   memcpy (&th, th_orig, sizeof (th));
366   sstrncpy (th.plugin, ci->values[0].value.string, sizeof (th.plugin));
367
368   for (i = 0; i < ci->children_num; i++)
369   {
370     oconfig_item_t *option = ci->children + i;
371     status = 0;
372
373     if (strcasecmp ("Type", option->key) == 0)
374       status = ut_config_type (&th, option);
375     else if (strcasecmp ("Instance", option->key) == 0)
376       status = ut_config_plugin_instance (&th, option);
377     else
378     {
379       WARNING ("threshold values: Option `%s' not allowed inside a `Plugin' "
380           "block.", option->key);
381       status = -1;
382     }
383
384     if (status != 0)
385       break;
386   }
387
388   return (status);
389 } /* int ut_config_plugin */
390
391 static int ut_config_host (const threshold_t *th_orig, oconfig_item_t *ci)
392 {
393   int i;
394   threshold_t th;
395   int status = 0;
396
397   if ((ci->values_num != 1)
398       || (ci->values[0].type != OCONFIG_TYPE_STRING))
399   {
400     WARNING ("threshold values: The `Host' block needs exactly one string "
401         "argument.");
402     return (-1);
403   }
404
405   if (ci->children_num < 1)
406   {
407     WARNING ("threshold values: The `Host' block needs at least one nested "
408         "block.");
409     return (-1);
410   }
411
412   memcpy (&th, th_orig, sizeof (th));
413   sstrncpy (th.host, ci->values[0].value.string, sizeof (th.host));
414
415   for (i = 0; i < ci->children_num; i++)
416   {
417     oconfig_item_t *option = ci->children + i;
418     status = 0;
419
420     if (strcasecmp ("Type", option->key) == 0)
421       status = ut_config_type (&th, option);
422     else if (strcasecmp ("Plugin", option->key) == 0)
423       status = ut_config_plugin (&th, option);
424     else
425     {
426       WARNING ("threshold values: Option `%s' not allowed inside a `Host' "
427           "block.", option->key);
428       status = -1;
429     }
430
431     if (status != 0)
432       break;
433   }
434
435   return (status);
436 } /* int ut_config_host */
437
438 int ut_config (const oconfig_item_t *ci)
439 {
440   int i;
441   int status = 0;
442
443   threshold_t th;
444
445   if (ci->values_num != 0)
446   {
447     ERROR ("threshold values: The `Threshold' block may not have any "
448         "arguments.");
449     return (-1);
450   }
451
452   if (threshold_tree == NULL)
453   {
454     threshold_tree = c_avl_create ((void *) strcmp);
455     if (threshold_tree == NULL)
456     {
457       ERROR ("ut_config: c_avl_create failed.");
458       return (-1);
459     }
460   }
461
462   memset (&th, '\0', sizeof (th));
463   th.warning_min = NAN;
464   th.warning_max = NAN;
465   th.failure_min = NAN;
466   th.failure_max = NAN;
467
468   th.hits = 0;
469   th.hysteresis = 0;
470   th.flags = UT_FLAG_INTERESTING; /* interesting by default */
471     
472   for (i = 0; i < ci->children_num; i++)
473   {
474     oconfig_item_t *option = ci->children + i;
475     status = 0;
476
477     if (strcasecmp ("Type", option->key) == 0)
478       status = ut_config_type (&th, option);
479     else if (strcasecmp ("Plugin", option->key) == 0)
480       status = ut_config_plugin (&th, option);
481     else if (strcasecmp ("Host", option->key) == 0)
482       status = ut_config_host (&th, option);
483     else
484     {
485       WARNING ("threshold values: Option `%s' not allowed here.", option->key);
486       status = -1;
487     }
488
489     if (status != 0)
490       break;
491   }
492
493   return (status);
494 } /* int um_config */
495 /*
496  * End of the functions used to configure threshold values.
497  */
498 /* }}} */
499
500 static threshold_t *threshold_search (const value_list_t *vl)
501 {
502   threshold_t *th;
503
504   if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
505           vl->type, vl->type_instance)) != NULL)
506     return (th);
507   else if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
508           vl->type, NULL)) != NULL)
509     return (th);
510   else if ((th = threshold_get (vl->host, vl->plugin, NULL,
511           vl->type, vl->type_instance)) != NULL)
512     return (th);
513   else if ((th = threshold_get (vl->host, vl->plugin, NULL,
514           vl->type, NULL)) != NULL)
515     return (th);
516   else if ((th = threshold_get (vl->host, "", NULL,
517           vl->type, vl->type_instance)) != NULL)
518     return (th);
519   else if ((th = threshold_get (vl->host, "", NULL,
520           vl->type, NULL)) != NULL)
521     return (th);
522   else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
523           vl->type, vl->type_instance)) != NULL)
524     return (th);
525   else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
526           vl->type, NULL)) != NULL)
527     return (th);
528   else if ((th = threshold_get ("", vl->plugin, NULL,
529           vl->type, vl->type_instance)) != NULL)
530     return (th);
531   else if ((th = threshold_get ("", vl->plugin, NULL,
532           vl->type, NULL)) != NULL)
533     return (th);
534   else if ((th = threshold_get ("", "", NULL,
535           vl->type, vl->type_instance)) != NULL)
536     return (th);
537   else if ((th = threshold_get ("", "", NULL,
538           vl->type, NULL)) != NULL)
539     return (th);
540
541   return (NULL);
542 } /* threshold_t *threshold_search */
543
544 /*
545  * int ut_report_state
546  *
547  * Checks if the `state' differs from the old state and creates a notification
548  * if appropriate.
549  * Does not fail.
550  */
551 static int ut_report_state (const data_set_t *ds,
552     const value_list_t *vl,
553     const threshold_t *th,
554     const gauge_t *values,
555     int ds_index,
556     int state)
557 { /* {{{ */
558   int state_old;
559   notification_t n;
560
561   char *buf;
562   size_t bufsize;
563
564   int status;
565
566   /* Check if hits matched */
567   if ( (th->hits != 0) )
568   {
569     int hits = uc_get_hits(ds,vl);
570     /* The STATE_OKAY always reset hits, or if hits reaise the limit */
571     if ( (state == STATE_OKAY) || (hits > th->hits) )
572     {
573         DEBUG("ut_report_state: reset uc_get_hits = 0");
574         uc_set_hits(ds,vl,0); /* reset hit counter and notify */
575     } else {
576       DEBUG("ut_report_state: th->hits = %d, uc_get_hits = %d",th->hits,uc_get_hits(ds,vl));
577       (void) uc_inc_hits(ds,vl,1); /* increase hit counter */
578       return (0);
579     }
580   } /* end check hits */
581
582   state_old = uc_get_state (ds, vl);
583
584   /* If the state didn't change, only report if `persistent' is specified and
585    * the state is not `okay'. */
586   if (state == state_old)
587   {
588     if ((th->flags & UT_FLAG_PERSIST) == 0)
589       return (0);
590     else if (state == STATE_OKAY)
591       return (0);
592   }
593
594   if (state != state_old)
595     uc_set_state (ds, vl, state);
596
597   NOTIFICATION_INIT_VL (&n, vl, ds);
598
599   buf = n.message;
600   bufsize = sizeof (n.message);
601
602   if (state == STATE_OKAY)
603     n.severity = NOTIF_OKAY;
604   else if (state == STATE_WARNING)
605     n.severity = NOTIF_WARNING;
606   else
607     n.severity = NOTIF_FAILURE;
608
609   n.time = vl->time;
610
611   status = ssnprintf (buf, bufsize, "Host %s, plugin %s",
612       vl->host, vl->plugin);
613   buf += status;
614   bufsize -= status;
615
616   if (vl->plugin_instance[0] != '\0')
617   {
618     status = ssnprintf (buf, bufsize, " (instance %s)",
619         vl->plugin_instance);
620     buf += status;
621     bufsize -= status;
622   }
623
624   status = ssnprintf (buf, bufsize, " type %s", vl->type);
625   buf += status;
626   bufsize -= status;
627
628   if (vl->type_instance[0] != '\0')
629   {
630     status = ssnprintf (buf, bufsize, " (instance %s)",
631         vl->type_instance);
632     buf += status;
633     bufsize -= status;
634   }
635
636   plugin_notification_meta_add_string (&n, "DataSource",
637       ds->ds[ds_index].name);
638   plugin_notification_meta_add_double (&n, "CurrentValue", values[ds_index]);
639   plugin_notification_meta_add_double (&n, "WarningMin", th->warning_min);
640   plugin_notification_meta_add_double (&n, "WarningMax", th->warning_max);
641   plugin_notification_meta_add_double (&n, "FailureMin", th->failure_min);
642   plugin_notification_meta_add_double (&n, "FailureMax", th->failure_max);
643
644   /* Send an okay notification */
645   if (state == STATE_OKAY)
646   {
647     status = ssnprintf (buf, bufsize, ": All data sources are within range again.");
648     buf += status;
649     bufsize -= status;
650   }
651   else
652   {
653     double min;
654     double max;
655
656     min = (state == STATE_ERROR) ? th->failure_min : th->warning_min;
657     max = (state == STATE_ERROR) ? th->failure_max : th->warning_max;
658
659     if (th->flags & UT_FLAG_INVERT)
660     {
661       if (!isnan (min) && !isnan (max))
662       {
663         status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
664             "%f. That is within the %s region of %f%s and %f%s.",
665             ds->ds[ds_index].name, values[ds_index],
666             (state == STATE_ERROR) ? "failure" : "warning",
667             min, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "",
668             max, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
669       }
670       else
671       {
672         status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
673             "%f. That is %s the %s threshold of %f%s.",
674             ds->ds[ds_index].name, values[ds_index],
675             isnan (min) ? "below" : "above",
676             (state == STATE_ERROR) ? "failure" : "warning",
677             isnan (min) ? max : min,
678             ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
679       }
680     }
681     else /* is not inverted */
682     {
683       status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
684           "%f. That is %s the %s threshold of %f%s.",
685           ds->ds[ds_index].name, values[ds_index],
686           (values[ds_index] < min) ? "below" : "above",
687           (state == STATE_ERROR) ? "failure" : "warning",
688           (values[ds_index] < min) ? min : max,
689           ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
690     }
691     buf += status;
692     bufsize -= status;
693   }
694
695   plugin_dispatch_notification (&n);
696
697   plugin_notification_meta_free (n.meta);
698   return (0);
699 } /* }}} int ut_report_state */
700
701 /*
702  * int ut_check_one_data_source
703  *
704  * Checks one data source against the given threshold configuration. If the
705  * `DataSource' option is set in the threshold, and the name does NOT match,
706  * `okay' is returned. If the threshold does match, its failure and warning
707  * min and max values are checked and `failure' or `warning' is returned if
708  * appropriate.
709  * Does not fail.
710  */
711 static int ut_check_one_data_source (const data_set_t *ds,
712     const value_list_t __attribute__((unused)) *vl,
713     const threshold_t *th,
714     const gauge_t *values,
715     int ds_index)
716 { /* {{{ */
717   const char *ds_name;
718   int is_warning = 0;
719   int is_failure = 0;
720   int prev_state = STATE_OKAY;
721
722   /* check if this threshold applies to this data source */
723   if (ds != NULL)
724   {
725     ds_name = ds->ds[ds_index].name;
726     if ((th->data_source[0] != 0)
727         && (strcmp (ds_name, th->data_source) != 0))
728       return (STATE_OKAY);
729   }
730
731   if ((th->flags & UT_FLAG_INVERT) != 0)
732   {
733     is_warning--;
734     is_failure--;
735   }
736
737   /* XXX: This is an experimental code, not optimized, not fast, not reliable,
738    * and probably, do not work as you expect. Enjoy! :D */
739   if ( (th->hysteresis > 0) && ((prev_state = uc_get_state(ds,vl)) != STATE_OKAY) )
740   {
741     switch(prev_state)
742     {
743       case STATE_ERROR:
744         if ( (!isnan (th->failure_min) && ((th->failure_min + th->hysteresis) < values[ds_index])) ||
745              (!isnan (th->failure_max) && ((th->failure_max - th->hysteresis) > values[ds_index])) )
746           return (STATE_OKAY);
747         else
748           is_failure++;
749       case STATE_WARNING:
750         if ( (!isnan (th->warning_min) && ((th->warning_min + th->hysteresis) < values[ds_index])) ||
751              (!isnan (th->warning_max) && ((th->warning_max - th->hysteresis) > values[ds_index])) )
752           return (STATE_OKAY);
753         else
754           is_warning++;
755      }
756   }
757   else { /* no hysteresis */
758     if ((!isnan (th->failure_min) && (th->failure_min > values[ds_index]))
759         || (!isnan (th->failure_max) && (th->failure_max < values[ds_index])))
760       is_failure++;
761
762     if ((!isnan (th->warning_min) && (th->warning_min > values[ds_index]))
763         || (!isnan (th->warning_max) && (th->warning_max < values[ds_index])))
764       is_warning++;
765  }
766
767   if (is_failure != 0)
768     return (STATE_ERROR);
769
770   if (is_warning != 0)
771     return (STATE_WARNING);
772
773   return (STATE_OKAY);
774 } /* }}} int ut_check_one_data_source */
775
776 /*
777  * int ut_check_one_threshold
778  *
779  * Checks all data sources of a value list against the given threshold, using
780  * the ut_check_one_data_source function above. Returns the worst status,
781  * which is `okay' if nothing has failed.
782  * Returns less than zero if the data set doesn't have any data sources.
783  */
784 static int ut_check_one_threshold (const data_set_t *ds,
785     const value_list_t *vl,
786     const threshold_t *th,
787     const gauge_t *values,
788     int *ret_ds_index)
789 { /* {{{ */
790   int ret = -1;
791   int ds_index = -1;
792   int i;
793   gauge_t values_copy[ds->ds_num];
794
795   memcpy (values_copy, values, sizeof (values_copy));
796
797   if ((th->flags & UT_FLAG_PERCENTAGE) != 0)
798   {
799     int num = 0;
800     gauge_t sum=0.0;
801
802     if (ds->ds_num == 1)
803     {
804       WARNING ("ut_check_one_threshold: The %s type has only one data "
805           "source, but you have configured to check this as a percentage. "
806           "That doesn't make much sense, because the percentage will always "
807           "be 100%%!", ds->type);
808     }
809
810     /* Prepare `sum' and `num'. */
811     for (i = 0; i < ds->ds_num; i++)
812       if (!isnan (values[i]))
813       {
814         num++;
815         sum += values[i];
816       }
817
818     if ((num == 0) /* All data sources are undefined. */
819         || (sum == 0.0)) /* Sum is zero, cannot calculate percentage. */
820     {
821       for (i = 0; i < ds->ds_num; i++)
822         values_copy[i] = NAN;
823     }
824     else /* We can actually calculate the percentage. */
825     {
826       for (i = 0; i < ds->ds_num; i++)
827         values_copy[i] = 100.0 * values[i] / sum;
828     }
829   } /* if (UT_FLAG_PERCENTAGE) */
830
831   for (i = 0; i < ds->ds_num; i++)
832   {
833     int status;
834
835     status = ut_check_one_data_source (ds, vl, th, values_copy, i);
836     if (ret < status)
837     {
838       ret = status;
839       ds_index = i;
840     }
841   } /* for (ds->ds_num) */
842
843   if (ret_ds_index != NULL)
844     *ret_ds_index = ds_index;
845
846   return (ret);
847 } /* }}} int ut_check_one_threshold */
848
849 /*
850  * int ut_check_threshold (PUBLIC)
851  *
852  * Gets a list of matching thresholds and searches for the worst status by one
853  * of the thresholds. Then reports that status using the ut_report_state
854  * function above. 
855  * Returns zero on success and if no threshold has been configured. Returns
856  * less than zero on failure.
857  */
858 int ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
859 { /* {{{ */
860   threshold_t *th;
861   gauge_t *values;
862   int status;
863
864   int worst_state = -1;
865   threshold_t *worst_th = NULL;
866   int worst_ds_index = -1;
867
868   if (threshold_tree == NULL)
869     return (0);
870
871   /* Is this lock really necessary? So far, thresholds are only inserted at
872    * startup. -octo */
873   pthread_mutex_lock (&threshold_lock);
874   th = threshold_search (vl);
875   pthread_mutex_unlock (&threshold_lock);
876   if (th == NULL)
877     return (0);
878
879   DEBUG ("ut_check_threshold: Found matching threshold(s)");
880
881   values = uc_get_rate (ds, vl);
882   if (values == NULL)
883     return (0);
884
885   while (th != NULL)
886   {
887     int ds_index = -1;
888
889     status = ut_check_one_threshold (ds, vl, th, values, &ds_index);
890     if (status < 0)
891     {
892       ERROR ("ut_check_threshold: ut_check_one_threshold failed.");
893       sfree (values);
894       return (-1);
895     }
896
897     if (worst_state < status)
898     {
899       worst_state = status;
900       worst_th = th;
901       worst_ds_index = ds_index;
902     }
903
904     th = th->next;
905   } /* while (th) */
906
907   status = ut_report_state (ds, vl, worst_th, values,
908       worst_ds_index, worst_state);
909   if (status != 0)
910   {
911     ERROR ("ut_check_threshold: ut_report_state failed.");
912     sfree (values);
913     return (-1);
914   }
915
916   sfree (values);
917
918   return (0);
919 } /* }}} int ut_check_threshold */
920
921 /*
922  * int ut_check_interesting (PUBLIC)
923  *
924  * Given an identification returns
925  * 0: No threshold is defined.
926  * 1: A threshold has been found. The flag `persist' is off.
927  * 2: A threshold has been found. The flag `persist' is on.
928  *    (That is, it is expected that many notifications are sent until the
929  *    problem disappears.)
930  */
931 int ut_check_interesting (const char *name)
932 { /* {{{ */
933   char *name_copy = NULL;
934   char *host = NULL;
935   char *plugin = NULL;
936   char *plugin_instance = NULL;
937   char *type = NULL;
938   char *type_instance = NULL;
939   int status;
940   data_set_t ds;
941   value_list_t vl;
942   threshold_t *th;
943
944   /* If there is no tree nothing is interesting. */
945   if (threshold_tree == NULL)
946     return (0);
947
948   name_copy = strdup (name);
949   if (name_copy == NULL)
950   {
951     ERROR ("ut_check_interesting: strdup failed.");
952     return (-1);
953   }
954
955   status = parse_identifier (name_copy, &host,
956       &plugin, &plugin_instance, &type, &type_instance);
957   if (status != 0)
958   {
959     ERROR ("ut_check_interesting: parse_identifier failed.");
960     sfree (name_copy);
961     return (-1);
962   }
963
964   memset (&ds, '\0', sizeof (ds));
965   memset (&vl, '\0', sizeof (vl));
966
967   sstrncpy (vl.host, host, sizeof (vl.host));
968   sstrncpy (vl.plugin, plugin, sizeof (vl.plugin));
969   if (plugin_instance != NULL)
970     sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
971   sstrncpy (ds.type, type, sizeof (ds.type));
972   sstrncpy (vl.type, type, sizeof (vl.type));
973   if (type_instance != NULL)
974     sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
975
976   sfree (name_copy);
977   host = plugin = plugin_instance = type = type_instance = NULL;
978
979   th = threshold_search (&vl);
980   if (th == NULL)
981     return (0);
982
983   if ((th->flags & UT_FLAG_INTERESTING) == 0)
984     return (0);
985
986   if ((th->flags & UT_FLAG_PERSIST) == 0)
987     return (1);
988   return (2);
989 } /* }}} int ut_check_interesting */
990
991 int ut_search_threshold (const value_list_t *vl, /* {{{ */
992     threshold_t *ret_threshold)
993 {
994   threshold_t *t;
995
996   if (vl == NULL)
997     return (EINVAL);
998
999   t = threshold_search (vl);
1000   if (t == NULL)
1001     return (ENOENT);
1002
1003   memcpy (ret_threshold, t, sizeof (*ret_threshold));
1004   ret_threshold->next = NULL;
1005
1006   return (0);
1007 } /* }}} int ut_search_threshold */
1008
1009 /* vim: set sw=2 ts=8 sts=2 tw=78 et fdm=marker : */