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