Add Interesting option for thresholds
[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_interesting (threshold_t *th, oconfig_item_t *ci)
222 {
223   if ((ci->values_num != 1)
224       || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
225   {
226     WARNING ("threshold values: The `Interesting' option needs exactly one "
227    "boolean argument.");
228     return (-1);
229   }
230
231   if (ci->values[0].value.boolean)
232     th->flags |= UT_FLAG_INTERESTING;
233   else
234     th->flags &= ~UT_FLAG_INTERESTING;
235
236   return (0);
237 } /* int ut_config_type_interesting */
238
239 static int ut_config_type_invert (threshold_t *th, oconfig_item_t *ci)
240 {
241   if ((ci->values_num != 1)
242       || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
243   {
244     WARNING ("threshold values: The `Invert' option needs exactly one "
245         "boolean argument.");
246     return (-1);
247   }
248
249   if (ci->values[0].value.boolean)
250     th->flags |= UT_FLAG_INVERT;
251   else
252     th->flags &= ~UT_FLAG_INVERT;
253
254   return (0);
255 } /* int ut_config_type_invert */
256
257 static int ut_config_type_persist (threshold_t *th, oconfig_item_t *ci)
258 {
259   if ((ci->values_num != 1)
260       || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
261   {
262     WARNING ("threshold values: The `Persist' option needs exactly one "
263         "boolean argument.");
264     return (-1);
265   }
266
267   if (ci->values[0].value.boolean)
268     th->flags |= UT_FLAG_PERSIST;
269   else
270     th->flags &= ~UT_FLAG_PERSIST;
271
272   return (0);
273 } /* int ut_config_type_persist */
274
275 static int ut_config_type_percentage(threshold_t *th, oconfig_item_t *ci)
276 {
277   if ((ci->values_num != 1)
278       || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
279   {
280     WARNING ("threshold values: The `Percentage' option needs exactly one "
281         "boolean argument.");
282     return (-1);
283   }
284
285   if (ci->values[0].value.boolean)
286     th->flags |= UT_FLAG_PERCENTAGE;
287   else
288     th->flags &= ~UT_FLAG_PERCENTAGE;
289
290   return (0);
291 } /* int ut_config_type_percentage */
292
293 static int ut_config_type_hits (threshold_t *th, oconfig_item_t *ci)
294 {
295   if ((ci->values_num != 1)
296       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
297   {
298     WARNING ("threshold values: The `%s' option needs exactly one "
299       "number argument.", ci->key);
300     return (-1);
301   }
302
303   th->hits = ci->values[0].value.number;
304
305   return (0);
306 } /* int ut_config_type_hits */
307
308 static int ut_config_type_hysteresis (threshold_t *th, oconfig_item_t *ci)
309 {
310   if ((ci->values_num != 1)
311       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
312   {
313     WARNING ("threshold values: The `%s' option needs exactly one "
314       "number argument.", ci->key);
315     return (-1);
316   }
317
318   th->hysteresis = ci->values[0].value.number;
319
320   return (0);
321 } /* int ut_config_type_hysteresis */
322
323 static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci)
324 {
325   int i;
326   threshold_t th;
327   int status = 0;
328
329   if ((ci->values_num != 1)
330       || (ci->values[0].type != OCONFIG_TYPE_STRING))
331   {
332     WARNING ("threshold values: The `Type' block needs exactly one string "
333         "argument.");
334     return (-1);
335   }
336
337   if (ci->children_num < 1)
338   {
339     WARNING ("threshold values: The `Type' block needs at least one option.");
340     return (-1);
341   }
342
343   memcpy (&th, th_orig, sizeof (th));
344   sstrncpy (th.type, ci->values[0].value.string, sizeof (th.type));
345
346   th.warning_min = NAN;
347   th.warning_max = NAN;
348   th.failure_min = NAN;
349   th.failure_max = NAN;
350   th.hits = 0;
351   th.hysteresis = 0;
352   th.flags = UT_FLAG_INTERESTING; /* interesting by default */
353
354   for (i = 0; i < ci->children_num; i++)
355   {
356     oconfig_item_t *option = ci->children + i;
357     status = 0;
358
359     if (strcasecmp ("Instance", option->key) == 0)
360       status = ut_config_type_instance (&th, option);
361     else if (strcasecmp ("DataSource", option->key) == 0)
362       status = ut_config_type_datasource (&th, option);
363     else if ((strcasecmp ("WarningMax", option->key) == 0)
364         || (strcasecmp ("FailureMax", option->key) == 0))
365       status = ut_config_type_max (&th, option);
366     else if ((strcasecmp ("WarningMin", option->key) == 0)
367         || (strcasecmp ("FailureMin", option->key) == 0))
368       status = ut_config_type_min (&th, option);
369     else if (strcasecmp ("Interesting", option->key) == 0)
370       status = ut_config_type_interesting (&th, option);
371     else if (strcasecmp ("Invert", option->key) == 0)
372       status = ut_config_type_invert (&th, option);
373     else if (strcasecmp ("Persist", option->key) == 0)
374       status = ut_config_type_persist (&th, option);
375     else if (strcasecmp ("Percentage", option->key) == 0)
376       status = ut_config_type_percentage (&th, option);
377     else if (strcasecmp ("Hits", option->key) == 0)
378       status = ut_config_type_hits (&th, option);
379     else if (strcasecmp ("Hysteresis", option->key) == 0)
380       status = ut_config_type_hysteresis (&th, option);
381     else
382     {
383       WARNING ("threshold values: Option `%s' not allowed inside a `Type' "
384           "block.", option->key);
385       status = -1;
386     }
387
388     if (status != 0)
389       break;
390   }
391
392   if (status == 0)
393   {
394     status = ut_threshold_add (&th);
395   }
396
397   return (status);
398 } /* int ut_config_type */
399
400 static int ut_config_plugin_instance (threshold_t *th, oconfig_item_t *ci)
401 {
402   if ((ci->values_num != 1)
403       || (ci->values[0].type != OCONFIG_TYPE_STRING))
404   {
405     WARNING ("threshold values: The `Instance' option needs exactly one "
406         "string argument.");
407     return (-1);
408   }
409
410   sstrncpy (th->plugin_instance, ci->values[0].value.string,
411       sizeof (th->plugin_instance));
412
413   return (0);
414 } /* int ut_config_plugin_instance */
415
416 static int ut_config_plugin (const threshold_t *th_orig, oconfig_item_t *ci)
417 {
418   int i;
419   threshold_t th;
420   int status = 0;
421
422   if ((ci->values_num != 1)
423       || (ci->values[0].type != OCONFIG_TYPE_STRING))
424   {
425     WARNING ("threshold values: The `Plugin' block needs exactly one string "
426         "argument.");
427     return (-1);
428   }
429
430   if (ci->children_num < 1)
431   {
432     WARNING ("threshold values: The `Plugin' block needs at least one nested "
433         "block.");
434     return (-1);
435   }
436
437   memcpy (&th, th_orig, sizeof (th));
438   sstrncpy (th.plugin, ci->values[0].value.string, sizeof (th.plugin));
439
440   for (i = 0; i < ci->children_num; i++)
441   {
442     oconfig_item_t *option = ci->children + i;
443     status = 0;
444
445     if (strcasecmp ("Type", option->key) == 0)
446       status = ut_config_type (&th, option);
447     else if (strcasecmp ("Instance", option->key) == 0)
448       status = ut_config_plugin_instance (&th, option);
449     else
450     {
451       WARNING ("threshold values: Option `%s' not allowed inside a `Plugin' "
452           "block.", option->key);
453       status = -1;
454     }
455
456     if (status != 0)
457       break;
458   }
459
460   return (status);
461 } /* int ut_config_plugin */
462
463 static int ut_config_host (const threshold_t *th_orig, oconfig_item_t *ci)
464 {
465   int i;
466   threshold_t th;
467   int status = 0;
468
469   if ((ci->values_num != 1)
470       || (ci->values[0].type != OCONFIG_TYPE_STRING))
471   {
472     WARNING ("threshold values: The `Host' block needs exactly one string "
473         "argument.");
474     return (-1);
475   }
476
477   if (ci->children_num < 1)
478   {
479     WARNING ("threshold values: The `Host' block needs at least one nested "
480         "block.");
481     return (-1);
482   }
483
484   memcpy (&th, th_orig, sizeof (th));
485   sstrncpy (th.host, ci->values[0].value.string, sizeof (th.host));
486
487   for (i = 0; i < ci->children_num; i++)
488   {
489     oconfig_item_t *option = ci->children + i;
490     status = 0;
491
492     if (strcasecmp ("Type", option->key) == 0)
493       status = ut_config_type (&th, option);
494     else if (strcasecmp ("Plugin", option->key) == 0)
495       status = ut_config_plugin (&th, option);
496     else
497     {
498       WARNING ("threshold values: Option `%s' not allowed inside a `Host' "
499           "block.", option->key);
500       status = -1;
501     }
502
503     if (status != 0)
504       break;
505   }
506
507   return (status);
508 } /* int ut_config_host */
509
510 int ut_config (const oconfig_item_t *ci)
511 {
512   int i;
513   int status = 0;
514
515   threshold_t th;
516
517   if (ci->values_num != 0)
518   {
519     ERROR ("threshold values: The `Threshold' block may not have any "
520         "arguments.");
521     return (-1);
522   }
523
524   if (threshold_tree == NULL)
525   {
526     threshold_tree = c_avl_create ((void *) strcmp);
527     if (threshold_tree == NULL)
528     {
529       ERROR ("ut_config: c_avl_create failed.");
530       return (-1);
531     }
532   }
533
534   memset (&th, '\0', sizeof (th));
535   th.warning_min = NAN;
536   th.warning_max = NAN;
537   th.failure_min = NAN;
538   th.failure_max = NAN;
539
540   th.hits = 0;
541   th.hysteresis = 0;
542   th.flags = UT_FLAG_INTERESTING; /* interesting by default */
543     
544   for (i = 0; i < ci->children_num; i++)
545   {
546     oconfig_item_t *option = ci->children + i;
547     status = 0;
548
549     if (strcasecmp ("Type", option->key) == 0)
550       status = ut_config_type (&th, option);
551     else if (strcasecmp ("Plugin", option->key) == 0)
552       status = ut_config_plugin (&th, option);
553     else if (strcasecmp ("Host", option->key) == 0)
554       status = ut_config_host (&th, option);
555     else
556     {
557       WARNING ("threshold values: Option `%s' not allowed here.", option->key);
558       status = -1;
559     }
560
561     if (status != 0)
562       break;
563   }
564
565   return (status);
566 } /* int um_config */
567 /*
568  * End of the functions used to configure threshold values.
569  */
570 /* }}} */
571
572 static threshold_t *threshold_search (const value_list_t *vl)
573 {
574   threshold_t *th;
575
576   if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
577           vl->type, vl->type_instance)) != NULL)
578     return (th);
579   else if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
580           vl->type, NULL)) != NULL)
581     return (th);
582   else if ((th = threshold_get (vl->host, vl->plugin, NULL,
583           vl->type, vl->type_instance)) != NULL)
584     return (th);
585   else if ((th = threshold_get (vl->host, vl->plugin, NULL,
586           vl->type, NULL)) != NULL)
587     return (th);
588   else if ((th = threshold_get (vl->host, "", NULL,
589           vl->type, vl->type_instance)) != NULL)
590     return (th);
591   else if ((th = threshold_get (vl->host, "", NULL,
592           vl->type, NULL)) != NULL)
593     return (th);
594   else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
595           vl->type, vl->type_instance)) != NULL)
596     return (th);
597   else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
598           vl->type, NULL)) != NULL)
599     return (th);
600   else if ((th = threshold_get ("", vl->plugin, NULL,
601           vl->type, vl->type_instance)) != NULL)
602     return (th);
603   else if ((th = threshold_get ("", vl->plugin, NULL,
604           vl->type, NULL)) != NULL)
605     return (th);
606   else if ((th = threshold_get ("", "", NULL,
607           vl->type, vl->type_instance)) != NULL)
608     return (th);
609   else if ((th = threshold_get ("", "", NULL,
610           vl->type, NULL)) != NULL)
611     return (th);
612
613   return (NULL);
614 } /* threshold_t *threshold_search */
615
616 /*
617  * int ut_report_state
618  *
619  * Checks if the `state' differs from the old state and creates a notification
620  * if appropriate.
621  * Does not fail.
622  */
623 static int ut_report_state (const data_set_t *ds,
624     const value_list_t *vl,
625     const threshold_t *th,
626     const gauge_t *values,
627     int ds_index,
628     int state)
629 { /* {{{ */
630   int state_old;
631   notification_t n;
632
633   char *buf;
634   size_t bufsize;
635
636   int status;
637
638   /* Check if hits matched */
639   if ( (th->hits != 0) )
640   {
641     int hits = uc_get_hits(ds,vl);
642     /* The STATE_OKAY always reset hits, or if hits reaise the limit */
643     if ( (state == STATE_OKAY) || (hits > th->hits) )
644     {
645         DEBUG("ut_report_state: reset uc_get_hits = 0");
646         uc_set_hits(ds,vl,0); /* reset hit counter and notify */
647     } else {
648       DEBUG("ut_report_state: th->hits = %d, uc_get_hits = %d",th->hits,uc_get_hits(ds,vl));
649       (void) uc_inc_hits(ds,vl,1); /* increase hit counter */
650       return (0);
651     }
652   } /* end check hits */
653
654   state_old = uc_get_state (ds, vl);
655
656   /* If the state didn't change, only report if `persistent' is specified and
657    * the state is not `okay'. */
658   if (state == state_old)
659   {
660     if ((th->flags & UT_FLAG_PERSIST) == 0)
661       return (0);
662     else if (state == STATE_OKAY)
663       return (0);
664   }
665
666   if (state != state_old)
667     uc_set_state (ds, vl, state);
668
669   NOTIFICATION_INIT_VL (&n, vl, ds);
670
671   buf = n.message;
672   bufsize = sizeof (n.message);
673
674   if (state == STATE_OKAY)
675     n.severity = NOTIF_OKAY;
676   else if (state == STATE_WARNING)
677     n.severity = NOTIF_WARNING;
678   else
679     n.severity = NOTIF_FAILURE;
680
681   n.time = vl->time;
682
683   status = ssnprintf (buf, bufsize, "Host %s, plugin %s",
684       vl->host, vl->plugin);
685   buf += status;
686   bufsize -= status;
687
688   if (vl->plugin_instance[0] != '\0')
689   {
690     status = ssnprintf (buf, bufsize, " (instance %s)",
691         vl->plugin_instance);
692     buf += status;
693     bufsize -= status;
694   }
695
696   status = ssnprintf (buf, bufsize, " type %s", vl->type);
697   buf += status;
698   bufsize -= status;
699
700   if (vl->type_instance[0] != '\0')
701   {
702     status = ssnprintf (buf, bufsize, " (instance %s)",
703         vl->type_instance);
704     buf += status;
705     bufsize -= status;
706   }
707
708   plugin_notification_meta_add_string (&n, "DataSource",
709       ds->ds[ds_index].name);
710   plugin_notification_meta_add_double (&n, "CurrentValue", values[ds_index]);
711   plugin_notification_meta_add_double (&n, "WarningMin", th->warning_min);
712   plugin_notification_meta_add_double (&n, "WarningMax", th->warning_max);
713   plugin_notification_meta_add_double (&n, "FailureMin", th->failure_min);
714   plugin_notification_meta_add_double (&n, "FailureMax", th->failure_max);
715
716   /* Send an okay notification */
717   if (state == STATE_OKAY)
718   {
719     status = ssnprintf (buf, bufsize, ": All data sources are within range again.");
720     buf += status;
721     bufsize -= status;
722   }
723   else
724   {
725     double min;
726     double max;
727
728     min = (state == STATE_ERROR) ? th->failure_min : th->warning_min;
729     max = (state == STATE_ERROR) ? th->failure_max : th->warning_max;
730
731     if (th->flags & UT_FLAG_INVERT)
732     {
733       if (!isnan (min) && !isnan (max))
734       {
735         status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
736             "%f. That is within the %s region of %f%s and %f%s.",
737             ds->ds[ds_index].name, values[ds_index],
738             (state == STATE_ERROR) ? "failure" : "warning",
739             min, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "",
740             max, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
741       }
742       else
743       {
744         status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
745             "%f. That is %s the %s threshold of %f%s.",
746             ds->ds[ds_index].name, values[ds_index],
747             isnan (min) ? "below" : "above",
748             (state == STATE_ERROR) ? "failure" : "warning",
749             isnan (min) ? max : min,
750             ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
751       }
752     }
753     else /* is not inverted */
754     {
755       status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
756           "%f. That is %s the %s threshold of %f%s.",
757           ds->ds[ds_index].name, values[ds_index],
758           (values[ds_index] < min) ? "below" : "above",
759           (state == STATE_ERROR) ? "failure" : "warning",
760           (values[ds_index] < min) ? min : max,
761           ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
762     }
763     buf += status;
764     bufsize -= status;
765   }
766
767   plugin_dispatch_notification (&n);
768
769   plugin_notification_meta_free (n.meta);
770   return (0);
771 } /* }}} int ut_report_state */
772
773 /*
774  * int ut_check_one_data_source
775  *
776  * Checks one data source against the given threshold configuration. If the
777  * `DataSource' option is set in the threshold, and the name does NOT match,
778  * `okay' is returned. If the threshold does match, its failure and warning
779  * min and max values are checked and `failure' or `warning' is returned if
780  * appropriate.
781  * Does not fail.
782  */
783 static int ut_check_one_data_source (const data_set_t *ds,
784     const value_list_t __attribute__((unused)) *vl,
785     const threshold_t *th,
786     const gauge_t *values,
787     int ds_index)
788 { /* {{{ */
789   const char *ds_name;
790   int is_warning = 0;
791   int is_failure = 0;
792   int prev_state = STATE_OKAY;
793
794   /* check if this threshold applies to this data source */
795   if (ds != NULL)
796   {
797     ds_name = ds->ds[ds_index].name;
798     if ((th->data_source[0] != 0)
799         && (strcmp (ds_name, th->data_source) != 0))
800       return (STATE_OKAY);
801   }
802
803   if ((th->flags & UT_FLAG_INVERT) != 0)
804   {
805     is_warning--;
806     is_failure--;
807   }
808
809   /* XXX: This is an experimental code, not optimized, not fast, not reliable,
810    * and probably, do not work as you expect. Enjoy! :D */
811   if ( (th->hysteresis > 0) && ((prev_state = uc_get_state(ds,vl)) != STATE_OKAY) )
812   {
813     switch(prev_state)
814     {
815       case STATE_ERROR:
816         if ( (!isnan (th->failure_min) && ((th->failure_min + th->hysteresis) < values[ds_index])) ||
817              (!isnan (th->failure_max) && ((th->failure_max - th->hysteresis) > values[ds_index])) )
818           return (STATE_OKAY);
819         else
820           is_failure++;
821       case STATE_WARNING:
822         if ( (!isnan (th->warning_min) && ((th->warning_min + th->hysteresis) < values[ds_index])) ||
823              (!isnan (th->warning_max) && ((th->warning_max - th->hysteresis) > values[ds_index])) )
824           return (STATE_OKAY);
825         else
826           is_warning++;
827      }
828   }
829   else { /* no hysteresis */
830     if ((!isnan (th->failure_min) && (th->failure_min > values[ds_index]))
831         || (!isnan (th->failure_max) && (th->failure_max < values[ds_index])))
832       is_failure++;
833
834     if ((!isnan (th->warning_min) && (th->warning_min > values[ds_index]))
835         || (!isnan (th->warning_max) && (th->warning_max < values[ds_index])))
836       is_warning++;
837  }
838
839   if (is_failure != 0)
840     return (STATE_ERROR);
841
842   if (is_warning != 0)
843     return (STATE_WARNING);
844
845   return (STATE_OKAY);
846 } /* }}} int ut_check_one_data_source */
847
848 /*
849  * int ut_check_one_threshold
850  *
851  * Checks all data sources of a value list against the given threshold, using
852  * the ut_check_one_data_source function above. Returns the worst status,
853  * which is `okay' if nothing has failed.
854  * Returns less than zero if the data set doesn't have any data sources.
855  */
856 static int ut_check_one_threshold (const data_set_t *ds,
857     const value_list_t *vl,
858     const threshold_t *th,
859     const gauge_t *values,
860     int *ret_ds_index)
861 { /* {{{ */
862   int ret = -1;
863   int ds_index = -1;
864   int i;
865   gauge_t values_copy[ds->ds_num];
866
867   memcpy (values_copy, values, sizeof (values_copy));
868
869   if ((th->flags & UT_FLAG_PERCENTAGE) != 0)
870   {
871     int num = 0;
872     gauge_t sum=0.0;
873
874     if (ds->ds_num == 1)
875     {
876       WARNING ("ut_check_one_threshold: The %s type has only one data "
877           "source, but you have configured to check this as a percentage. "
878           "That doesn't make much sense, because the percentage will always "
879           "be 100%%!", ds->type);
880     }
881
882     /* Prepare `sum' and `num'. */
883     for (i = 0; i < ds->ds_num; i++)
884       if (!isnan (values[i]))
885       {
886         num++;
887         sum += values[i];
888       }
889
890     if ((num == 0) /* All data sources are undefined. */
891         || (sum == 0.0)) /* Sum is zero, cannot calculate percentage. */
892     {
893       for (i = 0; i < ds->ds_num; i++)
894         values_copy[i] = NAN;
895     }
896     else /* We can actually calculate the percentage. */
897     {
898       for (i = 0; i < ds->ds_num; i++)
899         values_copy[i] = 100.0 * values[i] / sum;
900     }
901   } /* if (UT_FLAG_PERCENTAGE) */
902
903   for (i = 0; i < ds->ds_num; i++)
904   {
905     int status;
906
907     status = ut_check_one_data_source (ds, vl, th, values_copy, i);
908     if (ret < status)
909     {
910       ret = status;
911       ds_index = i;
912     }
913   } /* for (ds->ds_num) */
914
915   if (ret_ds_index != NULL)
916     *ret_ds_index = ds_index;
917
918   return (ret);
919 } /* }}} int ut_check_one_threshold */
920
921 /*
922  * int ut_check_threshold (PUBLIC)
923  *
924  * Gets a list of matching thresholds and searches for the worst status by one
925  * of the thresholds. Then reports that status using the ut_report_state
926  * function above. 
927  * Returns zero on success and if no threshold has been configured. Returns
928  * less than zero on failure.
929  */
930 int ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
931 { /* {{{ */
932   threshold_t *th;
933   gauge_t *values;
934   int status;
935
936   int worst_state = -1;
937   threshold_t *worst_th = NULL;
938   int worst_ds_index = -1;
939
940   if (threshold_tree == NULL)
941     return (0);
942
943   /* Is this lock really necessary? So far, thresholds are only inserted at
944    * startup. -octo */
945   pthread_mutex_lock (&threshold_lock);
946   th = threshold_search (vl);
947   pthread_mutex_unlock (&threshold_lock);
948   if (th == NULL)
949     return (0);
950
951   DEBUG ("ut_check_threshold: Found matching threshold(s)");
952
953   values = uc_get_rate (ds, vl);
954   if (values == NULL)
955     return (0);
956
957   while (th != NULL)
958   {
959     int ds_index = -1;
960
961     status = ut_check_one_threshold (ds, vl, th, values, &ds_index);
962     if (status < 0)
963     {
964       ERROR ("ut_check_threshold: ut_check_one_threshold failed.");
965       sfree (values);
966       return (-1);
967     }
968
969     if (worst_state < status)
970     {
971       worst_state = status;
972       worst_th = th;
973       worst_ds_index = ds_index;
974     }
975
976     th = th->next;
977   } /* while (th) */
978
979   status = ut_report_state (ds, vl, worst_th, values,
980       worst_ds_index, worst_state);
981   if (status != 0)
982   {
983     ERROR ("ut_check_threshold: ut_report_state failed.");
984     sfree (values);
985     return (-1);
986   }
987
988   sfree (values);
989
990   return (0);
991 } /* }}} int ut_check_threshold */
992
993 /*
994  * int ut_check_interesting (PUBLIC)
995  *
996  * Given an identification returns
997  * 0: No threshold is defined.
998  * 1: A threshold has been found. The flag `persist' is off.
999  * 2: A threshold has been found. The flag `persist' is on.
1000  *    (That is, it is expected that many notifications are sent until the
1001  *    problem disappears.)
1002  */
1003 int ut_check_interesting (const char *name)
1004 { /* {{{ */
1005   char *name_copy = NULL;
1006   char *host = NULL;
1007   char *plugin = NULL;
1008   char *plugin_instance = NULL;
1009   char *type = NULL;
1010   char *type_instance = NULL;
1011   int status;
1012   data_set_t ds;
1013   value_list_t vl;
1014   threshold_t *th;
1015
1016   /* If there is no tree nothing is interesting. */
1017   if (threshold_tree == NULL)
1018     return (0);
1019
1020   name_copy = strdup (name);
1021   if (name_copy == NULL)
1022   {
1023     ERROR ("ut_check_interesting: strdup failed.");
1024     return (-1);
1025   }
1026
1027   status = parse_identifier (name_copy, &host,
1028       &plugin, &plugin_instance, &type, &type_instance);
1029   if (status != 0)
1030   {
1031     ERROR ("ut_check_interesting: parse_identifier failed.");
1032     sfree (name_copy);
1033     return (-1);
1034   }
1035
1036   memset (&ds, '\0', sizeof (ds));
1037   memset (&vl, '\0', sizeof (vl));
1038
1039   sstrncpy (vl.host, host, sizeof (vl.host));
1040   sstrncpy (vl.plugin, plugin, sizeof (vl.plugin));
1041   if (plugin_instance != NULL)
1042     sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
1043   sstrncpy (ds.type, type, sizeof (ds.type));
1044   sstrncpy (vl.type, type, sizeof (vl.type));
1045   if (type_instance != NULL)
1046     sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
1047
1048   sfree (name_copy);
1049   host = plugin = plugin_instance = type = type_instance = NULL;
1050
1051   th = threshold_search (&vl);
1052   if (th == NULL)
1053     return (0);
1054
1055   if ((th->flags & UT_FLAG_INTERESTING) == 0)
1056     return (0);
1057
1058   if ((th->flags & UT_FLAG_PERSIST) == 0)
1059     return (1);
1060   return (2);
1061 } /* }}} int ut_check_interesting */
1062
1063 int ut_search_threshold (const value_list_t *vl, /* {{{ */
1064     threshold_t *ret_threshold)
1065 {
1066   threshold_t *t;
1067
1068   if (vl == NULL)
1069     return (EINVAL);
1070
1071   t = threshold_search (vl);
1072   if (t == NULL)
1073     return (ENOENT);
1074
1075   memcpy (ret_threshold, t, sizeof (*ret_threshold));
1076   ret_threshold->next = NULL;
1077
1078   return (0);
1079 } /* }}} int ut_search_threshold */
1080
1081 /* vim: set sw=2 ts=8 sts=2 tw=78 et fdm=marker : */