src/utils_threshold.c: Implement the “Hits” and “Hysteresis” config options.
[collectd.git] / src / utils_threshold.c
1 /**
2  * collectd - src/utils_threshold.c
3  * Copyright (C) 2007,2008  Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Author:
19  *   Florian octo Forster <octo at verplant.org>
20  **/
21
22 #include "collectd.h"
23 #include "common.h"
24 #include "plugin.h"
25 #include "utils_avltree.h"
26 #include "utils_cache.h"
27
28 #include <assert.h>
29 #include <pthread.h>
30
31 /*
32  * Private data structures
33  * {{{ */
34 #define UT_FLAG_INVERT  0x01
35 #define UT_FLAG_PERSIST 0x02
36 #define UT_FLAG_PERCENTAGE 0x04
37
38 typedef struct threshold_s
39 {
40   char host[DATA_MAX_NAME_LEN];
41   char plugin[DATA_MAX_NAME_LEN];
42   char plugin_instance[DATA_MAX_NAME_LEN];
43   char type[DATA_MAX_NAME_LEN];
44   char type_instance[DATA_MAX_NAME_LEN];
45   char data_source[DATA_MAX_NAME_LEN];
46   gauge_t warning_min;
47   gauge_t warning_max;
48   gauge_t failure_min;
49   gauge_t failure_max;
50   gauge_t hysteresis;
51   int flags;
52   int hits;
53   struct threshold_s *next;
54 } threshold_t;
55 /* }}} */
56
57 /*
58  * Private (static) variables
59  * {{{ */
60 static c_avl_tree_t   *threshold_tree = NULL;
61 static pthread_mutex_t threshold_lock = PTHREAD_MUTEX_INITIALIZER;
62 /* }}} */
63
64 /*
65  * Threshold management
66  * ====================
67  * The following functions add, delete, search, etc. configured thresholds to
68  * the underlying AVL trees.
69  * {{{ */
70 static threshold_t *threshold_get (const char *hostname,
71     const char *plugin, const char *plugin_instance,
72     const char *type, const char *type_instance)
73 {
74   char name[6 * DATA_MAX_NAME_LEN];
75   threshold_t *th = NULL;
76
77   format_name (name, sizeof (name),
78       (hostname == NULL) ? "" : hostname,
79       (plugin == NULL) ? "" : plugin, plugin_instance,
80       (type == NULL) ? "" : type, type_instance);
81   name[sizeof (name) - 1] = '\0';
82
83   if (c_avl_get (threshold_tree, name, (void *) &th) == 0)
84     return (th);
85   else
86     return (NULL);
87 } /* threshold_t *threshold_get */
88
89 static int ut_threshold_add (const threshold_t *th)
90 {
91   char name[6 * DATA_MAX_NAME_LEN];
92   char *name_copy;
93   threshold_t *th_copy;
94   threshold_t *th_ptr;
95   int status = 0;
96
97   if (format_name (name, sizeof (name), th->host,
98         th->plugin, th->plugin_instance,
99         th->type, th->type_instance) != 0)
100   {
101     ERROR ("ut_threshold_add: format_name failed.");
102     return (-1);
103   }
104
105   name_copy = strdup (name);
106   if (name_copy == NULL)
107   {
108     ERROR ("ut_threshold_add: strdup failed.");
109     return (-1);
110   }
111
112   th_copy = (threshold_t *) malloc (sizeof (threshold_t));
113   if (th_copy == NULL)
114   {
115     sfree (name_copy);
116     ERROR ("ut_threshold_add: malloc failed.");
117     return (-1);
118   }
119   memcpy (th_copy, th, sizeof (threshold_t));
120   th_ptr = NULL;
121
122   DEBUG ("ut_threshold_add: Adding entry `%s'", name);
123
124   pthread_mutex_lock (&threshold_lock);
125
126   th_ptr = threshold_get (th->host, th->plugin, th->plugin_instance,
127       th->type, th->type_instance);
128
129   while ((th_ptr != NULL) && (th_ptr->next != NULL))
130     th_ptr = th_ptr->next;
131
132   if (th_ptr == NULL) /* no such threshold yet */
133   {
134     status = c_avl_insert (threshold_tree, name_copy, th_copy);
135   }
136   else /* th_ptr points to the last threshold in the list */
137   {
138     th_ptr->next = th_copy;
139     /* name_copy isn't needed */
140     sfree (name_copy);
141   }
142
143   pthread_mutex_unlock (&threshold_lock);
144
145   if (status != 0)
146   {
147     ERROR ("ut_threshold_add: c_avl_insert (%s) failed.", name);
148     sfree (name_copy);
149     sfree (th_copy);
150   }
151
152   return (status);
153 } /* int ut_threshold_add */
154 /*
155  * End of the threshold management functions
156  * }}} */
157
158 /*
159  * Configuration
160  * =============
161  * The following approximately two hundred functions are used to handle the
162  * configuration and fill the threshold list.
163  * {{{ */
164 static int ut_config_type_datasource (threshold_t *th, oconfig_item_t *ci)
165 {
166   if ((ci->values_num != 1)
167       || (ci->values[0].type != OCONFIG_TYPE_STRING))
168   {
169     WARNING ("threshold values: The `DataSource' option needs exactly one "
170         "string argument.");
171     return (-1);
172   }
173
174   sstrncpy (th->data_source, ci->values[0].value.string,
175       sizeof (th->data_source));
176
177   return (0);
178 } /* int ut_config_type_datasource */
179
180 static int ut_config_type_instance (threshold_t *th, oconfig_item_t *ci)
181 {
182   if ((ci->values_num != 1)
183       || (ci->values[0].type != OCONFIG_TYPE_STRING))
184   {
185     WARNING ("threshold values: The `Instance' option needs exactly one "
186         "string argument.");
187     return (-1);
188   }
189
190   sstrncpy (th->type_instance, ci->values[0].value.string,
191       sizeof (th->type_instance));
192
193   return (0);
194 } /* int ut_config_type_instance */
195
196 static int ut_config_type_max (threshold_t *th, oconfig_item_t *ci)
197 {
198   if ((ci->values_num != 1)
199       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
200   {
201     WARNING ("threshold values: The `%s' option needs exactly one "
202         "number argument.", ci->key);
203     return (-1);
204   }
205
206   if (strcasecmp (ci->key, "WarningMax") == 0)
207     th->warning_max = ci->values[0].value.number;
208   else
209     th->failure_max = ci->values[0].value.number;
210
211   return (0);
212 } /* int ut_config_type_max */
213
214 static int ut_config_type_min (threshold_t *th, oconfig_item_t *ci)
215 {
216   if ((ci->values_num != 1)
217       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
218   {
219     WARNING ("threshold values: The `%s' option needs exactly one "
220         "number argument.", ci->key);
221     return (-1);
222   }
223
224   if (strcasecmp (ci->key, "WarningMin") == 0)
225     th->warning_min = ci->values[0].value.number;
226   else
227     th->failure_min = ci->values[0].value.number;
228
229   return (0);
230 } /* int ut_config_type_min */
231
232 static int ut_config_type_invert (threshold_t *th, oconfig_item_t *ci)
233 {
234   if ((ci->values_num != 1)
235       || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
236   {
237     WARNING ("threshold values: The `Invert' option needs exactly one "
238         "boolean argument.");
239     return (-1);
240   }
241
242   if (ci->values[0].value.boolean)
243     th->flags |= UT_FLAG_INVERT;
244   else
245     th->flags &= ~UT_FLAG_INVERT;
246
247   return (0);
248 } /* int ut_config_type_invert */
249
250 static int ut_config_type_persist (threshold_t *th, oconfig_item_t *ci)
251 {
252   if ((ci->values_num != 1)
253       || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
254   {
255     WARNING ("threshold values: The `Persist' option needs exactly one "
256         "boolean argument.");
257     return (-1);
258   }
259
260   if (ci->values[0].value.boolean)
261     th->flags |= UT_FLAG_PERSIST;
262   else
263     th->flags &= ~UT_FLAG_PERSIST;
264
265   return (0);
266 } /* int ut_config_type_persist */
267
268 static int ut_config_type_percentage(threshold_t *th, oconfig_item_t *ci)
269 {
270   if ((ci->values_num != 1)
271       || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
272   {
273     WARNING ("threshold values: The `Percentage' option needs exactly one "
274         "boolean argument.");
275     return (-1);
276   }
277
278   if (ci->values[0].value.boolean)
279     th->flags |= UT_FLAG_PERCENTAGE;
280   else
281     th->flags &= ~UT_FLAG_PERCENTAGE;
282
283   return (0);
284 } /* int ut_config_type_percentage */
285
286 static int ut_config_type_hits (threshold_t *th, oconfig_item_t *ci)
287 {
288   if ((ci->values_num != 1)
289       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
290   {
291     WARNING ("threshold values: The `%s' option needs exactly one "
292       "number argument.", ci->key);
293     return (-1);
294   }
295
296   th->hits = ci->values[0].value.number;
297
298   return (0);
299 } /* int ut_config_type_hits */
300
301 static int ut_config_type_hysteresis (threshold_t *th, oconfig_item_t *ci)
302 {
303   if ((ci->values_num != 1)
304       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
305   {
306     WARNING ("threshold values: The `%s' option needs exactly one "
307       "number argument.", ci->key);
308     return (-1);
309   }
310
311   th->hysteresis = ci->values[0].value.number;
312
313   return (0);
314 } /* int ut_config_type_hysteresis */
315
316 static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci)
317 {
318   int i;
319   threshold_t th;
320   int status = 0;
321
322   if ((ci->values_num != 1)
323       || (ci->values[0].type != OCONFIG_TYPE_STRING))
324   {
325     WARNING ("threshold values: The `Type' block needs exactly one string "
326         "argument.");
327     return (-1);
328   }
329
330   if (ci->children_num < 1)
331   {
332     WARNING ("threshold values: The `Type' block needs at least one option.");
333     return (-1);
334   }
335
336   memcpy (&th, th_orig, sizeof (th));
337   sstrncpy (th.type, ci->values[0].value.string, sizeof (th.type));
338
339   th.warning_min = NAN;
340   th.warning_max = NAN;
341   th.failure_min = NAN;
342   th.failure_max = NAN;
343   th.hits = 0;
344   th.hysteresis = 0;
345
346   for (i = 0; i < ci->children_num; i++)
347   {
348     oconfig_item_t *option = ci->children + i;
349     status = 0;
350
351     if (strcasecmp ("Instance", option->key) == 0)
352       status = ut_config_type_instance (&th, option);
353     else if (strcasecmp ("DataSource", option->key) == 0)
354       status = ut_config_type_datasource (&th, option);
355     else if ((strcasecmp ("WarningMax", option->key) == 0)
356         || (strcasecmp ("FailureMax", option->key) == 0))
357       status = ut_config_type_max (&th, option);
358     else if ((strcasecmp ("WarningMin", option->key) == 0)
359         || (strcasecmp ("FailureMin", option->key) == 0))
360       status = ut_config_type_min (&th, option);
361     else if (strcasecmp ("Invert", option->key) == 0)
362       status = ut_config_type_invert (&th, option);
363     else if (strcasecmp ("Persist", option->key) == 0)
364       status = ut_config_type_persist (&th, option);
365     else if (strcasecmp ("Percentage", option->key) == 0)
366       status = ut_config_type_percentage (&th, option);
367     else if (strcasecmp ("Hits", option->key) == 0)
368       status = ut_config_type_hits (&th, option);
369     else if (strcasecmp ("Hysteresis", option->key) == 0)
370       status = ut_config_type_hysteresis (&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     
533   for (i = 0; i < ci->children_num; i++)
534   {
535     oconfig_item_t *option = ci->children + i;
536     status = 0;
537
538     if (strcasecmp ("Type", option->key) == 0)
539       status = ut_config_type (&th, option);
540     else if (strcasecmp ("Plugin", option->key) == 0)
541       status = ut_config_plugin (&th, option);
542     else if (strcasecmp ("Host", option->key) == 0)
543       status = ut_config_host (&th, option);
544     else
545     {
546       WARNING ("threshold values: Option `%s' not allowed here.", option->key);
547       status = -1;
548     }
549
550     if (status != 0)
551       break;
552   }
553
554   return (status);
555 } /* int um_config */
556 /*
557  * End of the functions used to configure threshold values.
558  */
559 /* }}} */
560
561 static threshold_t *threshold_search (const value_list_t *vl)
562 {
563   threshold_t *th;
564
565   if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
566           vl->type, vl->type_instance)) != NULL)
567     return (th);
568   else if ((th = threshold_get (vl->host, vl->plugin, vl->plugin_instance,
569           vl->type, NULL)) != NULL)
570     return (th);
571   else if ((th = threshold_get (vl->host, vl->plugin, NULL,
572           vl->type, vl->type_instance)) != NULL)
573     return (th);
574   else if ((th = threshold_get (vl->host, vl->plugin, NULL,
575           vl->type, NULL)) != NULL)
576     return (th);
577   else if ((th = threshold_get (vl->host, "", NULL,
578           vl->type, vl->type_instance)) != NULL)
579     return (th);
580   else if ((th = threshold_get (vl->host, "", NULL,
581           vl->type, NULL)) != NULL)
582     return (th);
583   else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
584           vl->type, vl->type_instance)) != NULL)
585     return (th);
586   else if ((th = threshold_get ("", vl->plugin, vl->plugin_instance,
587           vl->type, NULL)) != NULL)
588     return (th);
589   else if ((th = threshold_get ("", vl->plugin, NULL,
590           vl->type, vl->type_instance)) != NULL)
591     return (th);
592   else if ((th = threshold_get ("", vl->plugin, NULL,
593           vl->type, NULL)) != NULL)
594     return (th);
595   else if ((th = threshold_get ("", "", NULL,
596           vl->type, vl->type_instance)) != NULL)
597     return (th);
598   else if ((th = threshold_get ("", "", NULL,
599           vl->type, NULL)) != NULL)
600     return (th);
601
602   return (NULL);
603 } /* threshold_t *threshold_search */
604
605 /*
606  * int ut_report_state
607  *
608  * Checks if the `state' differs from the old state and creates a notification
609  * if appropriate.
610  * Does not fail.
611  */
612 static int ut_report_state (const data_set_t *ds,
613     const value_list_t *vl,
614     const threshold_t *th,
615     const gauge_t *values,
616     int ds_index,
617     int state)
618 { /* {{{ */
619   int state_old;
620   notification_t n;
621
622   char *buf;
623   size_t bufsize;
624
625   int status;
626
627   /* Check if hits matched */
628   if ( (th->hits != 0) )
629   {
630     int hits = uc_get_hits(ds,vl);
631     /* The STATE_OKAY always reset hits, or if hits reaise the limit */
632     if ( (state == STATE_OKAY) || (hits > th->hits) )
633     {
634         DEBUG("ut_report_state: reset uc_get_hits = 0");
635         uc_set_hits(ds,vl,0); /* reset hit counter and notify */
636     } else {
637       DEBUG("ut_report_state: th->hits = %d, uc_get_hits = %d",th->hits,uc_get_hits(ds,vl));
638       (void) uc_inc_hits(ds,vl,1); /* increase hit counter */
639       return (0);
640     }
641   } /* end check hits */
642
643   state_old = uc_get_state (ds, vl);
644
645   /* If the state didn't change, only report if `persistent' is specified and
646    * the state is not `okay'. */
647   if (state == state_old)
648   {
649     if ((th->flags & UT_FLAG_PERSIST) == 0)
650       return (0);
651     else if (state == STATE_OKAY)
652       return (0);
653   }
654
655   if (state != state_old)
656     uc_set_state (ds, vl, state);
657
658   NOTIFICATION_INIT_VL (&n, vl, ds);
659
660   buf = n.message;
661   bufsize = sizeof (n.message);
662
663   if (state == STATE_OKAY)
664     n.severity = NOTIF_OKAY;
665   else if (state == STATE_WARNING)
666     n.severity = NOTIF_WARNING;
667   else
668     n.severity = NOTIF_FAILURE;
669
670   n.time = vl->time;
671
672   status = ssnprintf (buf, bufsize, "Host %s, plugin %s",
673       vl->host, vl->plugin);
674   buf += status;
675   bufsize -= status;
676
677   if (vl->plugin_instance[0] != '\0')
678   {
679     status = ssnprintf (buf, bufsize, " (instance %s)",
680         vl->plugin_instance);
681     buf += status;
682     bufsize -= status;
683   }
684
685   status = ssnprintf (buf, bufsize, " type %s", vl->type);
686   buf += status;
687   bufsize -= status;
688
689   if (vl->type_instance[0] != '\0')
690   {
691     status = ssnprintf (buf, bufsize, " (instance %s)",
692         vl->type_instance);
693     buf += status;
694     bufsize -= status;
695   }
696
697   plugin_notification_meta_add_string (&n, "DataSource",
698       ds->ds[ds_index].name);
699   plugin_notification_meta_add_double (&n, "CurrentValue", values[ds_index]);
700   plugin_notification_meta_add_double (&n, "WarningMin", th->warning_min);
701   plugin_notification_meta_add_double (&n, "WarningMax", th->warning_max);
702   plugin_notification_meta_add_double (&n, "FailureMin", th->failure_min);
703   plugin_notification_meta_add_double (&n, "FailureMax", th->failure_max);
704
705   /* Send an okay notification */
706   if (state == STATE_OKAY)
707   {
708     status = ssnprintf (buf, bufsize, ": All data sources are within range again.");
709     buf += status;
710     bufsize -= status;
711   }
712   else
713   {
714     double min;
715     double max;
716
717     min = (state == STATE_ERROR) ? th->failure_min : th->warning_min;
718     max = (state == STATE_ERROR) ? th->failure_max : th->warning_max;
719
720     if (th->flags & UT_FLAG_INVERT)
721     {
722       if (!isnan (min) && !isnan (max))
723       {
724         status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
725             "%f. That is within the %s region of %f%s and %f%s.",
726             ds->ds[ds_index].name, values[ds_index],
727             (state == STATE_ERROR) ? "failure" : "warning",
728             min, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "",
729             max, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
730       }
731       else
732       {
733         status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
734             "%f. That is %s the %s threshold of %f%s.",
735             ds->ds[ds_index].name, values[ds_index],
736             isnan (min) ? "below" : "above",
737             (state == STATE_ERROR) ? "failure" : "warning",
738             isnan (min) ? max : min,
739             ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
740       }
741     }
742     else /* is not inverted */
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           (values[ds_index] < min) ? "below" : "above",
748           (state == STATE_ERROR) ? "failure" : "warning",
749           (values[ds_index] < min) ? min : max,
750           ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
751     }
752     buf += status;
753     bufsize -= status;
754   }
755
756   plugin_dispatch_notification (&n);
757
758   plugin_notification_meta_free (n.meta);
759   return (0);
760 } /* }}} int ut_report_state */
761
762 /*
763  * int ut_check_one_data_source
764  *
765  * Checks one data source against the given threshold configuration. If the
766  * `DataSource' option is set in the threshold, and the name does NOT match,
767  * `okay' is returned. If the threshold does match, its failure and warning
768  * min and max values are checked and `failure' or `warning' is returned if
769  * appropriate.
770  * Does not fail.
771  */
772 static int ut_check_one_data_source (const data_set_t *ds,
773     const value_list_t __attribute__((unused)) *vl,
774     const threshold_t *th,
775     const gauge_t *values,
776     int ds_index)
777 { /* {{{ */
778   const char *ds_name;
779   int is_warning = 0;
780   int is_failure = 0;
781   int prev_state = STATE_OKAY;
782
783   /* check if this threshold applies to this data source */
784   if (ds != NULL)
785   {
786     ds_name = ds->ds[ds_index].name;
787     if ((th->data_source[0] != 0)
788         && (strcmp (ds_name, th->data_source) != 0))
789       return (STATE_OKAY);
790   }
791
792   if ((th->flags & UT_FLAG_INVERT) != 0)
793   {
794     is_warning--;
795     is_failure--;
796   }
797
798   /* XXX: This is an experimental code, not optimized, not fast, not reliable,
799    * and probably, do not work as you expect. Enjoy! :D */
800   if ( (th->hysteresis > 0) && ((prev_state = uc_get_state(ds,vl)) != STATE_OKAY) )
801   {
802     switch(prev_state)
803     {
804       case STATE_ERROR:
805         if ( (!isnan (th->failure_min) && ((th->failure_min + th->hysteresis) < values[ds_index])) ||
806              (!isnan (th->failure_max) && ((th->failure_max - th->hysteresis) > values[ds_index])) )
807           return (STATE_OKAY);
808         else
809           is_failure++;
810       case STATE_WARNING:
811         if ( (!isnan (th->warning_min) && ((th->warning_min + th->hysteresis) < values[ds_index])) ||
812              (!isnan (th->warning_max) && ((th->warning_max - th->hysteresis) > values[ds_index])) )
813           return (STATE_OKAY);
814         else
815           is_warning++;
816      }
817   }
818   else { /* no hysteresis */
819     if ((!isnan (th->failure_min) && (th->failure_min > values[ds_index]))
820         || (!isnan (th->failure_max) && (th->failure_max < values[ds_index])))
821       is_failure++;
822
823     if ((!isnan (th->warning_min) && (th->warning_min > values[ds_index]))
824         || (!isnan (th->warning_max) && (th->warning_max < values[ds_index])))
825       is_warning++;
826  }
827
828   if (is_failure != 0)
829     return (STATE_ERROR);
830
831   if (is_warning != 0)
832     return (STATE_WARNING);
833
834   return (STATE_OKAY);
835 } /* }}} int ut_check_one_data_source */
836
837 /*
838  * int ut_check_one_threshold
839  *
840  * Checks all data sources of a value list against the given threshold, using
841  * the ut_check_one_data_source function above. Returns the worst status,
842  * which is `okay' if nothing has failed.
843  * Returns less than zero if the data set doesn't have any data sources.
844  */
845 static int ut_check_one_threshold (const data_set_t *ds,
846     const value_list_t *vl,
847     const threshold_t *th,
848     const gauge_t *values,
849     int *ret_ds_index)
850 { /* {{{ */
851   int ret = -1;
852   int ds_index = -1;
853   int i;
854   gauge_t values_copy[ds->ds_num];
855
856   memcpy (values_copy, values, sizeof (values_copy));
857
858   if ((th->flags & UT_FLAG_PERCENTAGE) != 0)
859   {
860     int num = 0;
861     gauge_t sum=0.0;
862
863     if (ds->ds_num == 1)
864     {
865       WARNING ("ut_check_one_threshold: The %s type has only one data "
866           "source, but you have configured to check this as a percentage. "
867           "That doesn't make much sense, because the percentage will always "
868           "be 100%%!", ds->type);
869     }
870
871     /* Prepare `sum' and `num'. */
872     for (i = 0; i < ds->ds_num; i++)
873       if (!isnan (values[i]))
874       {
875         num++;
876         sum += values[i];
877       }
878
879     if ((num == 0) /* All data sources are undefined. */
880         || (sum == 0.0)) /* Sum is zero, cannot calculate percentage. */
881     {
882       for (i = 0; i < ds->ds_num; i++)
883         values_copy[i] = NAN;
884     }
885     else /* We can actually calculate the percentage. */
886     {
887       for (i = 0; i < ds->ds_num; i++)
888         values_copy[i] = 100.0 * values[i] / sum;
889     }
890   } /* if (UT_FLAG_PERCENTAGE) */
891
892   for (i = 0; i < ds->ds_num; i++)
893   {
894     int status;
895
896     status = ut_check_one_data_source (ds, vl, th, values_copy, i);
897     if (ret < status)
898     {
899       ret = status;
900       ds_index = i;
901     }
902   } /* for (ds->ds_num) */
903
904   if (ret_ds_index != NULL)
905     *ret_ds_index = ds_index;
906
907   return (ret);
908 } /* }}} int ut_check_one_threshold */
909
910 /*
911  * int ut_check_threshold (PUBLIC)
912  *
913  * Gets a list of matching thresholds and searches for the worst status by one
914  * of the thresholds. Then reports that status using the ut_report_state
915  * function above. 
916  * Returns zero on success and if no threshold has been configured. Returns
917  * less than zero on failure.
918  */
919 int ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
920 { /* {{{ */
921   threshold_t *th;
922   gauge_t *values;
923   int status;
924
925   int worst_state = -1;
926   threshold_t *worst_th = NULL;
927   int worst_ds_index = -1;
928
929   if (threshold_tree == NULL)
930     return (0);
931
932   /* Is this lock really necessary? So far, thresholds are only inserted at
933    * startup. -octo */
934   pthread_mutex_lock (&threshold_lock);
935   th = threshold_search (vl);
936   pthread_mutex_unlock (&threshold_lock);
937   if (th == NULL)
938     return (0);
939
940   DEBUG ("ut_check_threshold: Found matching threshold(s)");
941
942   values = uc_get_rate (ds, vl);
943   if (values == NULL)
944     return (0);
945
946   while (th != NULL)
947   {
948     int ds_index = -1;
949
950     status = ut_check_one_threshold (ds, vl, th, values, &ds_index);
951     if (status < 0)
952     {
953       ERROR ("ut_check_threshold: ut_check_one_threshold failed.");
954       sfree (values);
955       return (-1);
956     }
957
958     if (worst_state < status)
959     {
960       worst_state = status;
961       worst_th = th;
962       worst_ds_index = ds_index;
963     }
964
965     th = th->next;
966   } /* while (th) */
967
968   status = ut_report_state (ds, vl, worst_th, values,
969       worst_ds_index, worst_state);
970   if (status != 0)
971   {
972     ERROR ("ut_check_threshold: ut_report_state failed.");
973     sfree (values);
974     return (-1);
975   }
976
977   sfree (values);
978
979   return (0);
980 } /* }}} int ut_check_threshold */
981
982 /*
983  * int ut_check_interesting (PUBLIC)
984  *
985  * Given an identification returns
986  * 0: No threshold is defined.
987  * 1: A threshold has been found. The flag `persist' is off.
988  * 2: A threshold has been found. The flag `persist' is on.
989  *    (That is, it is expected that many notifications are sent until the
990  *    problem disappears.)
991  */
992 int ut_check_interesting (const char *name)
993 { /* {{{ */
994   char *name_copy = NULL;
995   char *host = NULL;
996   char *plugin = NULL;
997   char *plugin_instance = NULL;
998   char *type = NULL;
999   char *type_instance = NULL;
1000   int status;
1001   data_set_t ds;
1002   value_list_t vl;
1003   threshold_t *th;
1004
1005   /* If there is no tree nothing is interesting. */
1006   if (threshold_tree == NULL)
1007     return (0);
1008
1009   name_copy = strdup (name);
1010   if (name_copy == NULL)
1011   {
1012     ERROR ("ut_check_interesting: strdup failed.");
1013     return (-1);
1014   }
1015
1016   status = parse_identifier (name_copy, &host,
1017       &plugin, &plugin_instance, &type, &type_instance);
1018   if (status != 0)
1019   {
1020     ERROR ("ut_check_interesting: parse_identifier failed.");
1021     sfree (name_copy);
1022     return (-1);
1023   }
1024
1025   memset (&ds, '\0', sizeof (ds));
1026   memset (&vl, '\0', sizeof (vl));
1027
1028   sstrncpy (vl.host, host, sizeof (vl.host));
1029   sstrncpy (vl.plugin, plugin, sizeof (vl.plugin));
1030   if (plugin_instance != NULL)
1031     sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
1032   sstrncpy (ds.type, type, sizeof (ds.type));
1033   sstrncpy (vl.type, type, sizeof (vl.type));
1034   if (type_instance != NULL)
1035     sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
1036
1037   sfree (name_copy);
1038   host = plugin = plugin_instance = type = type_instance = NULL;
1039
1040   th = threshold_search (&vl);
1041   if (th == NULL)
1042     return (0);
1043   if ((th->flags & UT_FLAG_PERSIST) == 0)
1044     return (1);
1045   return (2);
1046 } /* }}} int ut_check_interesting */
1047
1048 /* vim: set sw=2 ts=8 sts=2 tw=78 et fdm=marker : */