Code

plugin.c: fix indentation of comment
[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  **/
26 #include "collectd.h"
27 #include "common.h"
28 #include "plugin.h"
29 #include "utils_avltree.h"
30 #include "utils_cache.h"
31 #include "utils_threshold.h"
33 #include <assert.h>
34 #include <pthread.h>
36 /*
37  * Threshold management
38  * ====================
39  * The following functions add, delete, search, etc. configured thresholds to
40  * the underlying AVL trees.
41  */
43 /*
44  * int ut_threshold_add
45  *
46  * Adds a threshold configuration to the list of thresholds. The threshold_t
47  * structure is copied and may be destroyed after this call. Returns zero on
48  * success, non-zero otherwise.
49  */
50 static int ut_threshold_add (const threshold_t *th)
51 { /* {{{ */
52   char name[6 * DATA_MAX_NAME_LEN];
53   char *name_copy;
54   threshold_t *th_copy;
55   threshold_t *th_ptr;
56   int status = 0;
58   if (format_name (name, sizeof (name), th->host,
59         th->plugin, th->plugin_instance,
60         th->type, th->type_instance) != 0)
61   {
62     ERROR ("ut_threshold_add: format_name failed.");
63     return (-1);
64   }
66   name_copy = strdup (name);
67   if (name_copy == NULL)
68   {
69     ERROR ("ut_threshold_add: strdup failed.");
70     return (-1);
71   }
73   th_copy = malloc (sizeof (*th_copy));
74   if (th_copy == NULL)
75   {
76     sfree (name_copy);
77     ERROR ("ut_threshold_add: malloc failed.");
78     return (-1);
79   }
80   memcpy (th_copy, th, sizeof (threshold_t));
81   th_ptr = NULL;
83   DEBUG ("ut_threshold_add: Adding entry `%s'", name);
85   pthread_mutex_lock (&threshold_lock);
87   th_ptr = threshold_get (th->host, th->plugin, th->plugin_instance,
88       th->type, th->type_instance);
90   while ((th_ptr != NULL) && (th_ptr->next != NULL))
91     th_ptr = th_ptr->next;
93   if (th_ptr == NULL) /* no such threshold yet */
94   {
95     status = c_avl_insert (threshold_tree, name_copy, th_copy);
96   }
97   else /* th_ptr points to the last threshold in the list */
98   {
99     th_ptr->next = th_copy;
100     /* name_copy isn't needed */
101     sfree (name_copy);
102   }
104   pthread_mutex_unlock (&threshold_lock);
106   if (status != 0)
107   {
108     ERROR ("ut_threshold_add: c_avl_insert (%s) failed.", name);
109     sfree (name_copy);
110     sfree (th_copy);
111   }
113   return (status);
114 } /* }}} int ut_threshold_add */
116 /*
117  * Configuration
118  * =============
119  * The following approximately two hundred functions are used to handle the
120  * configuration and fill the threshold list.
121  * {{{ */
122 static int ut_config_type_datasource (threshold_t *th, oconfig_item_t *ci)
124   if ((ci->values_num != 1)
125       || (ci->values[0].type != OCONFIG_TYPE_STRING))
126   {
127     WARNING ("threshold values: The `DataSource' option needs exactly one "
128         "string argument.");
129     return (-1);
130   }
132   sstrncpy (th->data_source, ci->values[0].value.string,
133       sizeof (th->data_source));
135   return (0);
136 } /* int ut_config_type_datasource */
138 static int ut_config_type_instance (threshold_t *th, oconfig_item_t *ci)
140   if ((ci->values_num != 1)
141       || (ci->values[0].type != OCONFIG_TYPE_STRING))
142   {
143     WARNING ("threshold values: The `Instance' option needs exactly one "
144         "string argument.");
145     return (-1);
146   }
148   sstrncpy (th->type_instance, ci->values[0].value.string,
149       sizeof (th->type_instance));
151   return (0);
152 } /* int ut_config_type_instance */
154 static int ut_config_type_max (threshold_t *th, oconfig_item_t *ci)
156   if ((ci->values_num != 1)
157       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
158   {
159     WARNING ("threshold values: The `%s' option needs exactly one "
160         "number argument.", ci->key);
161     return (-1);
162   }
164   if (strcasecmp (ci->key, "WarningMax") == 0)
165     th->warning_max = ci->values[0].value.number;
166   else
167     th->failure_max = ci->values[0].value.number;
169   return (0);
170 } /* int ut_config_type_max */
172 static int ut_config_type_min (threshold_t *th, oconfig_item_t *ci)
174   if ((ci->values_num != 1)
175       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
176   {
177     WARNING ("threshold values: The `%s' option needs exactly one "
178         "number argument.", ci->key);
179     return (-1);
180   }
182   if (strcasecmp (ci->key, "WarningMin") == 0)
183     th->warning_min = ci->values[0].value.number;
184   else
185     th->failure_min = ci->values[0].value.number;
187   return (0);
188 } /* int ut_config_type_min */
190 static int ut_config_type_hits (threshold_t *th, oconfig_item_t *ci)
192   if ((ci->values_num != 1)
193       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
194   {
195     WARNING ("threshold values: The `%s' option needs exactly one "
196       "number argument.", ci->key);
197     return (-1);
198   }
200   th->hits = ci->values[0].value.number;
202   return (0);
203 } /* int ut_config_type_hits */
205 static int ut_config_type_hysteresis (threshold_t *th, oconfig_item_t *ci)
207   if ((ci->values_num != 1)
208       || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
209   {
210     WARNING ("threshold values: The `%s' option needs exactly one "
211       "number argument.", ci->key);
212     return (-1);
213   }
215   th->hysteresis = ci->values[0].value.number;
217   return (0);
218 } /* int ut_config_type_hysteresis */
220 static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci)
222   int i;
223   threshold_t th;
224   int status = 0;
226   if ((ci->values_num != 1)
227       || (ci->values[0].type != OCONFIG_TYPE_STRING))
228   {
229     WARNING ("threshold values: The `Type' block needs exactly one string "
230         "argument.");
231     return (-1);
232   }
234   if (ci->children_num < 1)
235   {
236     WARNING ("threshold values: The `Type' block needs at least one option.");
237     return (-1);
238   }
240   memcpy (&th, th_orig, sizeof (th));
241   sstrncpy (th.type, ci->values[0].value.string, sizeof (th.type));
243   th.warning_min = NAN;
244   th.warning_max = NAN;
245   th.failure_min = NAN;
246   th.failure_max = NAN;
247   th.hits = 0;
248   th.hysteresis = 0;
249   th.flags = UT_FLAG_INTERESTING; /* interesting by default */
251   for (i = 0; i < ci->children_num; i++)
252   {
253     oconfig_item_t *option = ci->children + i;
255     if (strcasecmp ("Instance", option->key) == 0)
256       status = ut_config_type_instance (&th, option);
257     else if (strcasecmp ("DataSource", option->key) == 0)
258       status = ut_config_type_datasource (&th, option);
259     else if ((strcasecmp ("WarningMax", option->key) == 0)
260         || (strcasecmp ("FailureMax", option->key) == 0))
261       status = ut_config_type_max (&th, option);
262     else if ((strcasecmp ("WarningMin", option->key) == 0)
263         || (strcasecmp ("FailureMin", option->key) == 0))
264       status = ut_config_type_min (&th, option);
265     else if (strcasecmp ("Interesting", option->key) == 0)
266       status = cf_util_get_flag (option, &th.flags, UT_FLAG_INTERESTING);
267     else if (strcasecmp ("Invert", option->key) == 0)
268       status = cf_util_get_flag (option, &th.flags, UT_FLAG_INVERT);
269     else if (strcasecmp ("Persist", option->key) == 0)
270       status = cf_util_get_flag (option, &th.flags, UT_FLAG_PERSIST);
271     else if (strcasecmp ("PersistOK", option->key) == 0)
272       status = cf_util_get_flag (option, &th.flags, UT_FLAG_PERSIST_OK);
273     else if (strcasecmp ("Percentage", option->key) == 0)
274       status = cf_util_get_flag (option, &th.flags, UT_FLAG_PERCENTAGE);
275     else if (strcasecmp ("Hits", option->key) == 0)
276       status = ut_config_type_hits (&th, option);
277     else if (strcasecmp ("Hysteresis", option->key) == 0)
278       status = ut_config_type_hysteresis (&th, option);
279     else
280     {
281       WARNING ("threshold values: Option `%s' not allowed inside a `Type' "
282           "block.", option->key);
283       status = -1;
284     }
286     if (status != 0)
287       break;
288   }
290   if (status == 0)
291   {
292     status = ut_threshold_add (&th);
293   }
295   return (status);
296 } /* int ut_config_type */
298 static int ut_config_plugin_instance (threshold_t *th, oconfig_item_t *ci)
300   if ((ci->values_num != 1)
301       || (ci->values[0].type != OCONFIG_TYPE_STRING))
302   {
303     WARNING ("threshold values: The `Instance' option needs exactly one "
304         "string argument.");
305     return (-1);
306   }
308   sstrncpy (th->plugin_instance, ci->values[0].value.string,
309       sizeof (th->plugin_instance));
311   return (0);
312 } /* int ut_config_plugin_instance */
314 static int ut_config_plugin (const threshold_t *th_orig, oconfig_item_t *ci)
316   int i;
317   threshold_t th;
318   int status = 0;
320   if ((ci->values_num != 1)
321       || (ci->values[0].type != OCONFIG_TYPE_STRING))
322   {
323     WARNING ("threshold values: The `Plugin' block needs exactly one string "
324         "argument.");
325     return (-1);
326   }
328   if (ci->children_num < 1)
329   {
330     WARNING ("threshold values: The `Plugin' block needs at least one nested "
331         "block.");
332     return (-1);
333   }
335   memcpy (&th, th_orig, sizeof (th));
336   sstrncpy (th.plugin, ci->values[0].value.string, sizeof (th.plugin));
338   for (i = 0; i < ci->children_num; i++)
339   {
340     oconfig_item_t *option = ci->children + i;
342     if (strcasecmp ("Type", option->key) == 0)
343       status = ut_config_type (&th, option);
344     else if (strcasecmp ("Instance", option->key) == 0)
345       status = ut_config_plugin_instance (&th, option);
346     else
347     {
348       WARNING ("threshold values: Option `%s' not allowed inside a `Plugin' "
349           "block.", option->key);
350       status = -1;
351     }
353     if (status != 0)
354       break;
355   }
357   return (status);
358 } /* int ut_config_plugin */
360 static int ut_config_host (const threshold_t *th_orig, oconfig_item_t *ci)
362   int i;
363   threshold_t th;
364   int status = 0;
366   if ((ci->values_num != 1)
367       || (ci->values[0].type != OCONFIG_TYPE_STRING))
368   {
369     WARNING ("threshold values: The `Host' block needs exactly one string "
370         "argument.");
371     return (-1);
372   }
374   if (ci->children_num < 1)
375   {
376     WARNING ("threshold values: The `Host' block needs at least one nested "
377         "block.");
378     return (-1);
379   }
381   memcpy (&th, th_orig, sizeof (th));
382   sstrncpy (th.host, ci->values[0].value.string, sizeof (th.host));
384   for (i = 0; i < ci->children_num; i++)
385   {
386     oconfig_item_t *option = ci->children + i;
388     if (strcasecmp ("Type", option->key) == 0)
389       status = ut_config_type (&th, option);
390     else if (strcasecmp ("Plugin", option->key) == 0)
391       status = ut_config_plugin (&th, option);
392     else
393     {
394       WARNING ("threshold values: Option `%s' not allowed inside a `Host' "
395           "block.", option->key);
396       status = -1;
397     }
399     if (status != 0)
400       break;
401   }
403   return (status);
404 } /* int ut_config_host */
405 /*
406  * End of the functions used to configure threshold values.
407  */
408 /* }}} */
410 /*
411  * int ut_report_state
412  *
413  * Checks if the `state' differs from the old state and creates a notification
414  * if appropriate.
415  * Does not fail.
416  */
417 static int ut_report_state (const data_set_t *ds,
418     const value_list_t *vl,
419     const threshold_t *th,
420     const gauge_t *values,
421     int ds_index,
422     int state)
423 { /* {{{ */
424   int state_old;
425   notification_t n;
427   char *buf;
428   size_t bufsize;
430   int status;
432   /* Check if hits matched */
433   if ( (th->hits != 0) )
434   {
435     int hits = uc_get_hits(ds,vl);
436     /* STATE_OKAY resets hits unless PERSIST_OK flag is set. Hits resets if
437      * threshold is hit. */
438     if ( ( (state == STATE_OKAY) && ((th->flags & UT_FLAG_PERSIST_OK) == 0) ) || (hits > th->hits) )
439     {
440         DEBUG("ut_report_state: reset uc_get_hits = 0");
441         uc_set_hits(ds,vl,0); /* reset hit counter and notify */
442     } else {
443       DEBUG("ut_report_state: th->hits = %d, uc_get_hits = %d",th->hits,uc_get_hits(ds,vl));
444       (void) uc_inc_hits(ds,vl,1); /* increase hit counter */
445       return (0);
446     }
447   } /* end check hits */
449   state_old = uc_get_state (ds, vl);
451   /* If the state didn't change, report if `persistent' is specified. If the
452    * state is `okay', then only report if `persist_ok` flag is set. */
453   if (state == state_old)
454   {
455     if ((th->flags & UT_FLAG_PERSIST) == 0)
456       return (0);
457     else if ( (state == STATE_OKAY) && ((th->flags & UT_FLAG_PERSIST_OK) == 0) )
458       return (0);
459   }
461   if (state != state_old)
462     uc_set_state (ds, vl, state);
464   NOTIFICATION_INIT_VL (&n, vl);
466   buf = n.message;
467   bufsize = sizeof (n.message);
469   if (state == STATE_OKAY)
470     n.severity = NOTIF_OKAY;
471   else if (state == STATE_WARNING)
472     n.severity = NOTIF_WARNING;
473   else
474     n.severity = NOTIF_FAILURE;
476   n.time = vl->time;
478   status = ssnprintf (buf, bufsize, "Host %s, plugin %s",
479       vl->host, vl->plugin);
480   buf += status;
481   bufsize -= status;
483   if (vl->plugin_instance[0] != '\0')
484   {
485     status = ssnprintf (buf, bufsize, " (instance %s)",
486         vl->plugin_instance);
487     buf += status;
488     bufsize -= status;
489   }
491   status = ssnprintf (buf, bufsize, " type %s", vl->type);
492   buf += status;
493   bufsize -= status;
495   if (vl->type_instance[0] != '\0')
496   {
497     status = ssnprintf (buf, bufsize, " (instance %s)",
498         vl->type_instance);
499     buf += status;
500     bufsize -= status;
501   }
503   plugin_notification_meta_add_string (&n, "DataSource",
504       ds->ds[ds_index].name);
505   plugin_notification_meta_add_double (&n, "CurrentValue", values[ds_index]);
506   plugin_notification_meta_add_double (&n, "WarningMin", th->warning_min);
507   plugin_notification_meta_add_double (&n, "WarningMax", th->warning_max);
508   plugin_notification_meta_add_double (&n, "FailureMin", th->failure_min);
509   plugin_notification_meta_add_double (&n, "FailureMax", th->failure_max);
511   /* Send an okay notification */
512   if (state == STATE_OKAY)
513   {
514     if (state_old == STATE_MISSING)
515       ssnprintf (buf, bufsize, ": Value is no longer missing.");
516     else
517       ssnprintf (buf, bufsize,
518           ": All data sources are within range again. "
519           "Current value of \"%s\" is %f.",
520           ds->ds[ds_index].name, values[ds_index]);
521   }
522   else
523   {
524     double min;
525     double max;
527     min = (state == STATE_ERROR) ? th->failure_min : th->warning_min;
528     max = (state == STATE_ERROR) ? th->failure_max : th->warning_max;
530     if (th->flags & UT_FLAG_INVERT)
531     {
532       if (!isnan (min) && !isnan (max))
533       {
534         ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
535             "%f. That is within the %s region of %f%s and %f%s.",
536             ds->ds[ds_index].name, values[ds_index],
537             (state == STATE_ERROR) ? "failure" : "warning",
538             min, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "",
539             max, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
540       }
541       else
542       {
543         ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
544             "%f. That is %s the %s threshold of %f%s.",
545             ds->ds[ds_index].name, values[ds_index],
546             isnan (min) ? "below" : "above",
547             (state == STATE_ERROR) ? "failure" : "warning",
548             isnan (min) ? max : min,
549             ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
550       }
551     }
552     else if (th->flags & UT_FLAG_PERCENTAGE)
553     {
554       gauge_t value;
555       gauge_t sum;
556       size_t i;
558       sum = 0.0;
559       for (i = 0; i < vl->values_len; i++)
560       {
561         if (isnan (values[i]))
562           continue;
564         sum += values[i];
565       }
567       if (sum == 0.0)
568         value = NAN;
569       else
570         value = 100.0 * values[ds_index] / sum;
572       ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
573           "%g (%.2f%%). That is %s the %s threshold of %.2f%%.",
574           ds->ds[ds_index].name, values[ds_index], value,
575           (value < min) ? "below" : "above",
576           (state == STATE_ERROR) ? "failure" : "warning",
577           (value < min) ? min : max);
578     }
579     else /* is not inverted */
580     {
581       ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
582           "%f. That is %s the %s threshold of %f.",
583           ds->ds[ds_index].name, values[ds_index],
584           (values[ds_index] < min) ? "below" : "above",
585           (state == STATE_ERROR) ? "failure" : "warning",
586           (values[ds_index] < min) ? min : max);
587     }
588   }
590   plugin_dispatch_notification (&n);
592   plugin_notification_meta_free (n.meta);
593   return (0);
594 } /* }}} int ut_report_state */
596 /*
597  * int ut_check_one_data_source
598  *
599  * Checks one data source against the given threshold configuration. If the
600  * `DataSource' option is set in the threshold, and the name does NOT match,
601  * `okay' is returned. If the threshold does match, its failure and warning
602  * min and max values are checked and `failure' or `warning' is returned if
603  * appropriate.
604  * Does not fail.
605  */
606 static int ut_check_one_data_source (const data_set_t *ds,
607     const value_list_t __attribute__((unused)) *vl,
608     const threshold_t *th,
609     const gauge_t *values,
610     int ds_index)
611 { /* {{{ */
612   const char *ds_name;
613   int is_warning = 0;
614   int is_failure = 0;
615   int prev_state = STATE_OKAY;
617   /* check if this threshold applies to this data source */
618   if (ds != NULL)
619   {
620     ds_name = ds->ds[ds_index].name;
621     if ((th->data_source[0] != 0)
622         && (strcmp (ds_name, th->data_source) != 0))
623       return (STATE_OKAY);
624   }
626   if ((th->flags & UT_FLAG_INVERT) != 0)
627   {
628     is_warning--;
629     is_failure--;
630   }
632   /* XXX: This is an experimental code, not optimized, not fast, not reliable,
633    * and probably, do not work as you expect. Enjoy! :D */
634   if (th->hysteresis > 0)
635   {
636     prev_state = uc_get_state(ds,vl);
637     /* The purpose of hysteresis is elliminating flapping state when the value
638      * oscilates around the thresholds. In other words, what is important is
639      * the previous state; if the new value would trigger a transition, make
640      * sure that we artificially widen the range which is considered to apply
641      * for the previous state, and only trigger the notification if the value
642      * is outside of this expanded range.
643      *
644      * There is no hysteresis for the OKAY state.
645      * */
646     gauge_t hysteresis_for_warning = 0, hysteresis_for_failure = 0;
647     switch (prev_state)
648     {
649       case STATE_ERROR:
650         hysteresis_for_failure = th->hysteresis;
651         break;
652       case STATE_WARNING:
653         hysteresis_for_warning = th->hysteresis;
654         break;
655       case STATE_OKAY:
656         /* do nothing -- the hysteresis only applies to the non-normal states */
657         break;
658     }
660     if ((!isnan (th->failure_min) && (th->failure_min + hysteresis_for_failure > values[ds_index]))
661         || (!isnan (th->failure_max) && (th->failure_max - hysteresis_for_failure < values[ds_index])))
662       is_failure++;
664     if ((!isnan (th->warning_min) && (th->warning_min + hysteresis_for_warning > values[ds_index]))
665         || (!isnan (th->warning_max) && (th->warning_max - hysteresis_for_warning < values[ds_index])))
666       is_warning++;
668   }
669   else { /* no hysteresis */
670     if ((!isnan (th->failure_min) && (th->failure_min > values[ds_index]))
671         || (!isnan (th->failure_max) && (th->failure_max < values[ds_index])))
672       is_failure++;
674     if ((!isnan (th->warning_min) && (th->warning_min > values[ds_index]))
675         || (!isnan (th->warning_max) && (th->warning_max < values[ds_index])))
676       is_warning++;
677   }
679   if (is_failure != 0)
680     return (STATE_ERROR);
682   if (is_warning != 0)
683     return (STATE_WARNING);
685   return (STATE_OKAY);
686 } /* }}} int ut_check_one_data_source */
688 /*
689  * int ut_check_one_threshold
690  *
691  * Checks all data sources of a value list against the given threshold, using
692  * the ut_check_one_data_source function above. Returns the worst status,
693  * which is `okay' if nothing has failed.
694  * Returns less than zero if the data set doesn't have any data sources.
695  */
696 static int ut_check_one_threshold (const data_set_t *ds,
697     const value_list_t *vl,
698     const threshold_t *th,
699     const gauge_t *values,
700     int *ret_ds_index)
701 { /* {{{ */
702   int ret = -1;
703   int ds_index = -1;
704   size_t i;
705   gauge_t values_copy[ds->ds_num];
707   memcpy (values_copy, values, sizeof (values_copy));
709   if ((th->flags & UT_FLAG_PERCENTAGE) != 0)
710   {
711     int num = 0;
712     gauge_t sum=0.0;
714     if (ds->ds_num == 1)
715     {
716       WARNING ("ut_check_one_threshold: The %s type has only one data "
717           "source, but you have configured to check this as a percentage. "
718           "That doesn't make much sense, because the percentage will always "
719           "be 100%%!", ds->type);
720     }
722     /* Prepare `sum' and `num'. */
723     for (i = 0; i < ds->ds_num; i++)
724       if (!isnan (values[i]))
725       {
726         num++;
727         sum += values[i];
728       }
730     if ((num == 0) /* All data sources are undefined. */
731         || (sum == 0.0)) /* Sum is zero, cannot calculate percentage. */
732     {
733       for (i = 0; i < ds->ds_num; i++)
734         values_copy[i] = NAN;
735     }
736     else /* We can actually calculate the percentage. */
737     {
738       for (i = 0; i < ds->ds_num; i++)
739         values_copy[i] = 100.0 * values[i] / sum;
740     }
741   } /* if (UT_FLAG_PERCENTAGE) */
743   for (i = 0; i < ds->ds_num; i++)
744   {
745     int status;
747     status = ut_check_one_data_source (ds, vl, th, values_copy, i);
748     if (ret < status)
749     {
750       ret = status;
751       ds_index = i;
752     }
753   } /* for (ds->ds_num) */
755   if (ret_ds_index != NULL)
756     *ret_ds_index = ds_index;
758   return (ret);
759 } /* }}} int ut_check_one_threshold */
761 /*
762  * int ut_check_threshold
763  *
764  * Gets a list of matching thresholds and searches for the worst status by one
765  * of the thresholds. Then reports that status using the ut_report_state
766  * function above.
767  * Returns zero on success and if no threshold has been configured. Returns
768  * less than zero on failure.
769  */
770 static int ut_check_threshold (const data_set_t *ds, const value_list_t *vl,
771     __attribute__((unused)) user_data_t *ud)
772 { /* {{{ */
773   threshold_t *th;
774   gauge_t *values;
775   int status;
777   int worst_state = -1;
778   threshold_t *worst_th = NULL;
779   int worst_ds_index = -1;
781   if (threshold_tree == NULL)
782     return (0);
784   /* Is this lock really necessary? So far, thresholds are only inserted at
785    * startup. -octo */
786   pthread_mutex_lock (&threshold_lock);
787   th = threshold_search (vl);
788   pthread_mutex_unlock (&threshold_lock);
789   if (th == NULL)
790     return (0);
792   DEBUG ("ut_check_threshold: Found matching threshold(s)");
794   values = uc_get_rate (ds, vl);
795   if (values == NULL)
796     return (0);
798   while (th != NULL)
799   {
800     int ds_index = -1;
802     status = ut_check_one_threshold (ds, vl, th, values, &ds_index);
803     if (status < 0)
804     {
805       ERROR ("ut_check_threshold: ut_check_one_threshold failed.");
806       sfree (values);
807       return (-1);
808     }
810     if (worst_state < status)
811     {
812       worst_state = status;
813       worst_th = th;
814       worst_ds_index = ds_index;
815     }
817     th = th->next;
818   } /* while (th) */
820   status = ut_report_state (ds, vl, worst_th, values,
821       worst_ds_index, worst_state);
822   if (status != 0)
823   {
824     ERROR ("ut_check_threshold: ut_report_state failed.");
825     sfree (values);
826     return (-1);
827   }
829   sfree (values);
831   return (0);
832 } /* }}} int ut_check_threshold */
834 /*
835  * int ut_missing
836  *
837  * This function is called whenever a value goes "missing".
838  */
839 static int ut_missing (const value_list_t *vl,
840     __attribute__((unused)) user_data_t *ud)
841 { /* {{{ */
842   threshold_t *th;
843   cdtime_t missing_time;
844   char identifier[6 * DATA_MAX_NAME_LEN];
845   notification_t n;
846   cdtime_t now;
848   if (threshold_tree == NULL)
849     return (0);
851   th = threshold_search (vl);
852   /* dispatch notifications for "interesting" values only */
853   if ((th == NULL) || ((th->flags & UT_FLAG_INTERESTING) == 0))
854     return (0);
856   now = cdtime ();
857   missing_time = now - vl->time;
858   FORMAT_VL (identifier, sizeof (identifier), vl);
860   NOTIFICATION_INIT_VL (&n, vl);
861   ssnprintf (n.message, sizeof (n.message),
862       "%s has not been updated for %.3f seconds.",
863       identifier, CDTIME_T_TO_DOUBLE (missing_time));
864   n.time = now;
866   plugin_dispatch_notification (&n);
868   return (0);
869 } /* }}} int ut_missing */
871 static int ut_config (oconfig_item_t *ci)
872 { /* {{{ */
873   int i;
874   int status = 0;
875   int old_size = c_avl_size (threshold_tree);
877   threshold_t th;
879   if (threshold_tree == NULL)
880   {
881     threshold_tree = c_avl_create ((void *) strcmp);
882     if (threshold_tree == NULL)
883     {
884       ERROR ("ut_config: c_avl_create failed.");
885       return (-1);
886     }
887   }
889   memset (&th, '\0', sizeof (th));
890   th.warning_min = NAN;
891   th.warning_max = NAN;
892   th.failure_min = NAN;
893   th.failure_max = NAN;
895   th.hits = 0;
896   th.hysteresis = 0;
897   th.flags = UT_FLAG_INTERESTING; /* interesting by default */
899   for (i = 0; i < ci->children_num; i++)
900   {
901     oconfig_item_t *option = ci->children + i;
903     if (strcasecmp ("Type", option->key) == 0)
904       status = ut_config_type (&th, option);
905     else if (strcasecmp ("Plugin", option->key) == 0)
906       status = ut_config_plugin (&th, option);
907     else if (strcasecmp ("Host", option->key) == 0)
908       status = ut_config_host (&th, option);
909     else
910     {
911       WARNING ("threshold values: Option `%s' not allowed here.", option->key);
912       status = -1;
913     }
915     if (status != 0)
916       break;
917   }
919   /* register callbacks if this is the first time we see a valid config */
920   if ((old_size == 0) && (c_avl_size (threshold_tree) > 0))
921   {
922     plugin_register_missing ("threshold", ut_missing,
923         /* user data = */ NULL);
924     plugin_register_write ("threshold", ut_check_threshold,
925         /* user data = */ NULL);
926   }
928   return (status);
929 } /* }}} int um_config */
931 void module_register (void)
933   plugin_register_complex_config ("threshold", ut_config);
936 /* vim: set sw=2 ts=8 sts=2 tw=78 et fdm=marker : */