modbus plugin: Make sure variable is initialized.
[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 #include "utils_threshold.h"
32
33 #include <assert.h>
34 #include <pthread.h>
35
36 /*
37  * Threshold management
38  * ====================
39  * The following functions add, delete, search, etc. configured thresholds to
40  * the underlying AVL trees.
41  */
42
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;
57
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   }
65
66   name_copy = strdup (name);
67   if (name_copy == NULL)
68   {
69     ERROR ("ut_threshold_add: strdup failed.");
70     return (-1);
71   }
72
73   th_copy = (threshold_t *) malloc (sizeof (threshold_t));
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;
82
83   DEBUG ("ut_threshold_add: Adding entry `%s'", name);
84
85   pthread_mutex_lock (&threshold_lock);
86
87   th_ptr = threshold_get (th->host, th->plugin, th->plugin_instance,
88       th->type, th->type_instance);
89
90   while ((th_ptr != NULL) && (th_ptr->next != NULL))
91     th_ptr = th_ptr->next;
92
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   }
103
104   pthread_mutex_unlock (&threshold_lock);
105
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   }
112
113   return (status);
114 } /* }}} int ut_threshold_add */
115
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)
123 {
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   }
131
132   sstrncpy (th->data_source, ci->values[0].value.string,
133       sizeof (th->data_source));
134
135   return (0);
136 } /* int ut_config_type_datasource */
137
138 static int ut_config_type_instance (threshold_t *th, oconfig_item_t *ci)
139 {
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   }
147
148   sstrncpy (th->type_instance, ci->values[0].value.string,
149       sizeof (th->type_instance));
150
151   return (0);
152 } /* int ut_config_type_instance */
153
154 static int ut_config_type_max (threshold_t *th, oconfig_item_t *ci)
155 {
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   }
163
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;
168
169   return (0);
170 } /* int ut_config_type_max */
171
172 static int ut_config_type_min (threshold_t *th, oconfig_item_t *ci)
173 {
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   }
181
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;
186
187   return (0);
188 } /* int ut_config_type_min */
189
190 static int ut_config_type_hits (threshold_t *th, oconfig_item_t *ci)
191 {
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   }
199
200   th->hits = ci->values[0].value.number;
201
202   return (0);
203 } /* int ut_config_type_hits */
204
205 static int ut_config_type_hysteresis (threshold_t *th, oconfig_item_t *ci)
206 {
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   }
214
215   th->hysteresis = ci->values[0].value.number;
216
217   return (0);
218 } /* int ut_config_type_hysteresis */
219
220 static int ut_config_type (const threshold_t *th_orig, oconfig_item_t *ci)
221 {
222   int i;
223   threshold_t th;
224   int status = 0;
225
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   }
233
234   if (ci->children_num < 1)
235   {
236     WARNING ("threshold values: The `Type' block needs at least one option.");
237     return (-1);
238   }
239
240   memcpy (&th, th_orig, sizeof (th));
241   sstrncpy (th.type, ci->values[0].value.string, sizeof (th.type));
242
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 */
250
251   for (i = 0; i < ci->children_num; i++)
252   {
253     oconfig_item_t *option = ci->children + i;
254
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     }
285
286     if (status != 0)
287       break;
288   }
289
290   if (status == 0)
291   {
292     status = ut_threshold_add (&th);
293   }
294
295   return (status);
296 } /* int ut_config_type */
297
298 static int ut_config_plugin_instance (threshold_t *th, oconfig_item_t *ci)
299 {
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   }
307
308   sstrncpy (th->plugin_instance, ci->values[0].value.string,
309       sizeof (th->plugin_instance));
310
311   return (0);
312 } /* int ut_config_plugin_instance */
313
314 static int ut_config_plugin (const threshold_t *th_orig, oconfig_item_t *ci)
315 {
316   int i;
317   threshold_t th;
318   int status = 0;
319
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   }
327
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   }
334
335   memcpy (&th, th_orig, sizeof (th));
336   sstrncpy (th.plugin, ci->values[0].value.string, sizeof (th.plugin));
337
338   for (i = 0; i < ci->children_num; i++)
339   {
340     oconfig_item_t *option = ci->children + i;
341
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     }
352
353     if (status != 0)
354       break;
355   }
356
357   return (status);
358 } /* int ut_config_plugin */
359
360 static int ut_config_host (const threshold_t *th_orig, oconfig_item_t *ci)
361 {
362   int i;
363   threshold_t th;
364   int status = 0;
365
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   }
373
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   }
380
381   memcpy (&th, th_orig, sizeof (th));
382   sstrncpy (th.host, ci->values[0].value.string, sizeof (th.host));
383
384   for (i = 0; i < ci->children_num; i++)
385   {
386     oconfig_item_t *option = ci->children + i;
387
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     }
398
399     if (status != 0)
400       break;
401   }
402
403   return (status);
404 } /* int ut_config_host */
405 /*
406  * End of the functions used to configure threshold values.
407  */
408 /* }}} */
409
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;
426
427   char *buf;
428   size_t bufsize;
429
430   int status;
431
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 */
448
449   state_old = uc_get_state (ds, vl);
450
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   }
460
461   if (state != state_old)
462     uc_set_state (ds, vl, state);
463
464   NOTIFICATION_INIT_VL (&n, vl);
465
466   buf = n.message;
467   bufsize = sizeof (n.message);
468
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;
475
476   n.time = vl->time;
477
478   status = ssnprintf (buf, bufsize, "Host %s, plugin %s",
479       vl->host, vl->plugin);
480   buf += status;
481   bufsize -= status;
482
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   }
490
491   status = ssnprintf (buf, bufsize, " type %s", vl->type);
492   buf += status;
493   bufsize -= status;
494
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   }
502
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);
510
511   /* Send an okay notification */
512   if (state == STATE_OKAY)
513   {
514     if (state_old == STATE_MISSING)
515       status = ssnprintf (buf, bufsize,
516           ": Value is no longer missing.");
517     else
518       status = ssnprintf (buf, bufsize,
519           ": All data sources are within range again. "
520           "Current value of \"%s\" is %f.",
521           ds->ds[ds_index].name, values[ds_index]);
522     buf += status;
523     bufsize -= status;
524   }
525   else
526   {
527     double min;
528     double max;
529
530     min = (state == STATE_ERROR) ? th->failure_min : th->warning_min;
531     max = (state == STATE_ERROR) ? th->failure_max : th->warning_max;
532
533     if (th->flags & UT_FLAG_INVERT)
534     {
535       if (!isnan (min) && !isnan (max))
536       {
537         status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
538             "%f. That is within the %s region of %f%s and %f%s.",
539             ds->ds[ds_index].name, values[ds_index],
540             (state == STATE_ERROR) ? "failure" : "warning",
541             min, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "",
542             max, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
543       }
544       else
545       {
546         status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
547             "%f. That is %s the %s threshold of %f%s.",
548             ds->ds[ds_index].name, values[ds_index],
549             isnan (min) ? "below" : "above",
550             (state == STATE_ERROR) ? "failure" : "warning",
551             isnan (min) ? max : min,
552             ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
553       }
554     }
555     else if (th->flags & UT_FLAG_PERCENTAGE)
556     {
557       gauge_t value;
558       gauge_t sum;
559       int i;
560
561       sum = 0.0;
562       for (i = 0; i < vl->values_len; i++)
563       {
564         if (isnan (values[i]))
565           continue;
566
567         sum += values[i];
568       }
569
570       if (sum == 0.0)
571         value = NAN;
572       else
573         value = 100.0 * values[ds_index] / sum;
574
575       status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
576           "%g (%.2f%%). That is %s the %s threshold of %.2f%%.",
577           ds->ds[ds_index].name, values[ds_index], value,
578           (value < min) ? "below" : "above",
579           (state == STATE_ERROR) ? "failure" : "warning",
580           (value < min) ? min : max);
581     }
582     else /* is not inverted */
583     {
584       status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
585           "%f. That is %s the %s threshold of %f.",
586           ds->ds[ds_index].name, values[ds_index],
587           (values[ds_index] < min) ? "below" : "above",
588           (state == STATE_ERROR) ? "failure" : "warning",
589           (values[ds_index] < min) ? min : max);
590     }
591     buf += status;
592     bufsize -= status;
593   }
594
595   plugin_dispatch_notification (&n);
596
597   plugin_notification_meta_free (n.meta);
598   return (0);
599 } /* }}} int ut_report_state */
600
601 /*
602  * int ut_check_one_data_source
603  *
604  * Checks one data source against the given threshold configuration. If the
605  * `DataSource' option is set in the threshold, and the name does NOT match,
606  * `okay' is returned. If the threshold does match, its failure and warning
607  * min and max values are checked and `failure' or `warning' is returned if
608  * appropriate.
609  * Does not fail.
610  */
611 static int ut_check_one_data_source (const data_set_t *ds,
612     const value_list_t __attribute__((unused)) *vl,
613     const threshold_t *th,
614     const gauge_t *values,
615     int ds_index)
616 { /* {{{ */
617   const char *ds_name;
618   int is_warning = 0;
619   int is_failure = 0;
620   int prev_state = STATE_OKAY;
621
622   /* check if this threshold applies to this data source */
623   if (ds != NULL)
624   {
625     ds_name = ds->ds[ds_index].name;
626     if ((th->data_source[0] != 0)
627         && (strcmp (ds_name, th->data_source) != 0))
628       return (STATE_OKAY);
629   }
630
631   if ((th->flags & UT_FLAG_INVERT) != 0)
632   {
633     is_warning--;
634     is_failure--;
635   }
636
637   /* XXX: This is an experimental code, not optimized, not fast, not reliable,
638    * and probably, do not work as you expect. Enjoy! :D */
639   if (th->hysteresis > 0)
640   {
641     prev_state = uc_get_state(ds,vl);
642     /* The purpose of hysteresis is elliminating flapping state when the value
643      * oscilates around the thresholds. In other words, what is important is
644      * the previous state; if the new value would trigger a transition, make
645      * sure that we artificially widen the range which is considered to apply
646      * for the previous state, and only trigger the notification if the value
647      * is outside of this expanded range.
648      *
649      * There is no hysteresis for the OKAY state.
650      * */
651     gauge_t hysteresis_for_warning = 0, hysteresis_for_failure = 0;
652     switch (prev_state)
653     {
654       case STATE_ERROR:
655         hysteresis_for_failure = th->hysteresis;
656         break;
657       case STATE_WARNING:
658         hysteresis_for_warning = th->hysteresis;
659         break;
660       case STATE_OKAY:
661         /* do nothing -- the hysteresis only applies to the non-normal states */
662         break;
663     }
664
665     if ((!isnan (th->failure_min) && (th->failure_min + hysteresis_for_failure > values[ds_index]))
666         || (!isnan (th->failure_max) && (th->failure_max - hysteresis_for_failure < values[ds_index])))
667       is_failure++;
668
669     if ((!isnan (th->warning_min) && (th->warning_min + hysteresis_for_warning > values[ds_index]))
670         || (!isnan (th->warning_max) && (th->warning_max - hysteresis_for_warning < values[ds_index])))
671       is_warning++;
672
673   }
674   else { /* no hysteresis */
675     if ((!isnan (th->failure_min) && (th->failure_min > values[ds_index]))
676         || (!isnan (th->failure_max) && (th->failure_max < values[ds_index])))
677       is_failure++;
678
679     if ((!isnan (th->warning_min) && (th->warning_min > values[ds_index]))
680         || (!isnan (th->warning_max) && (th->warning_max < values[ds_index])))
681       is_warning++;
682   }
683
684   if (is_failure != 0)
685     return (STATE_ERROR);
686
687   if (is_warning != 0)
688     return (STATE_WARNING);
689
690   return (STATE_OKAY);
691 } /* }}} int ut_check_one_data_source */
692
693 /*
694  * int ut_check_one_threshold
695  *
696  * Checks all data sources of a value list against the given threshold, using
697  * the ut_check_one_data_source function above. Returns the worst status,
698  * which is `okay' if nothing has failed.
699  * Returns less than zero if the data set doesn't have any data sources.
700  */
701 static int ut_check_one_threshold (const data_set_t *ds,
702     const value_list_t *vl,
703     const threshold_t *th,
704     const gauge_t *values,
705     int *ret_ds_index)
706 { /* {{{ */
707   int ret = -1;
708   int ds_index = -1;
709   int i;
710   gauge_t values_copy[ds->ds_num];
711
712   memcpy (values_copy, values, sizeof (values_copy));
713
714   if ((th->flags & UT_FLAG_PERCENTAGE) != 0)
715   {
716     int num = 0;
717     gauge_t sum=0.0;
718
719     if (ds->ds_num == 1)
720     {
721       WARNING ("ut_check_one_threshold: The %s type has only one data "
722           "source, but you have configured to check this as a percentage. "
723           "That doesn't make much sense, because the percentage will always "
724           "be 100%%!", ds->type);
725     }
726
727     /* Prepare `sum' and `num'. */
728     for (i = 0; i < ds->ds_num; i++)
729       if (!isnan (values[i]))
730       {
731         num++;
732         sum += values[i];
733       }
734
735     if ((num == 0) /* All data sources are undefined. */
736         || (sum == 0.0)) /* Sum is zero, cannot calculate percentage. */
737     {
738       for (i = 0; i < ds->ds_num; i++)
739         values_copy[i] = NAN;
740     }
741     else /* We can actually calculate the percentage. */
742     {
743       for (i = 0; i < ds->ds_num; i++)
744         values_copy[i] = 100.0 * values[i] / sum;
745     }
746   } /* if (UT_FLAG_PERCENTAGE) */
747
748   for (i = 0; i < ds->ds_num; i++)
749   {
750     int status;
751
752     status = ut_check_one_data_source (ds, vl, th, values_copy, i);
753     if (ret < status)
754     {
755       ret = status;
756       ds_index = i;
757     }
758   } /* for (ds->ds_num) */
759
760   if (ret_ds_index != NULL)
761     *ret_ds_index = ds_index;
762
763   return (ret);
764 } /* }}} int ut_check_one_threshold */
765
766 /*
767  * int ut_check_threshold
768  *
769  * Gets a list of matching thresholds and searches for the worst status by one
770  * of the thresholds. Then reports that status using the ut_report_state
771  * function above.
772  * Returns zero on success and if no threshold has been configured. Returns
773  * less than zero on failure.
774  */
775 static int ut_check_threshold (const data_set_t *ds, const value_list_t *vl,
776     __attribute__((unused)) user_data_t *ud)
777 { /* {{{ */
778   threshold_t *th;
779   gauge_t *values;
780   int status;
781
782   int worst_state = -1;
783   threshold_t *worst_th = NULL;
784   int worst_ds_index = -1;
785
786   if (threshold_tree == NULL)
787     return (0);
788
789   /* Is this lock really necessary? So far, thresholds are only inserted at
790    * startup. -octo */
791   pthread_mutex_lock (&threshold_lock);
792   th = threshold_search (vl);
793   pthread_mutex_unlock (&threshold_lock);
794   if (th == NULL)
795     return (0);
796
797   DEBUG ("ut_check_threshold: Found matching threshold(s)");
798
799   values = uc_get_rate (ds, vl);
800   if (values == NULL)
801     return (0);
802
803   while (th != NULL)
804   {
805     int ds_index = -1;
806
807     status = ut_check_one_threshold (ds, vl, th, values, &ds_index);
808     if (status < 0)
809     {
810       ERROR ("ut_check_threshold: ut_check_one_threshold failed.");
811       sfree (values);
812       return (-1);
813     }
814
815     if (worst_state < status)
816     {
817       worst_state = status;
818       worst_th = th;
819       worst_ds_index = ds_index;
820     }
821
822     th = th->next;
823   } /* while (th) */
824
825   status = ut_report_state (ds, vl, worst_th, values,
826       worst_ds_index, worst_state);
827   if (status != 0)
828   {
829     ERROR ("ut_check_threshold: ut_report_state failed.");
830     sfree (values);
831     return (-1);
832   }
833
834   sfree (values);
835
836   return (0);
837 } /* }}} int ut_check_threshold */
838
839 /*
840  * int ut_missing
841  *
842  * This function is called whenever a value goes "missing".
843  */
844 static int ut_missing (const value_list_t *vl,
845     __attribute__((unused)) user_data_t *ud)
846 { /* {{{ */
847   threshold_t *th;
848   cdtime_t missing_time;
849   char identifier[6 * DATA_MAX_NAME_LEN];
850   notification_t n;
851   cdtime_t now;
852
853   if (threshold_tree == NULL)
854     return (0);
855
856   th = threshold_search (vl);
857   /* dispatch notifications for "interesting" values only */
858   if ((th == NULL) || ((th->flags & UT_FLAG_INTERESTING) == 0))
859     return (0);
860
861   now = cdtime ();
862   missing_time = now - vl->time;
863   FORMAT_VL (identifier, sizeof (identifier), vl);
864
865   NOTIFICATION_INIT_VL (&n, vl);
866   ssnprintf (n.message, sizeof (n.message),
867       "%s has not been updated for %.3f seconds.",
868       identifier, CDTIME_T_TO_DOUBLE (missing_time));
869   n.time = now;
870
871   plugin_dispatch_notification (&n);
872
873   return (0);
874 } /* }}} int ut_missing */
875
876 int ut_config (oconfig_item_t *ci)
877 { /* {{{ */
878   int i;
879   int status = 0;
880
881   threshold_t th;
882
883   if (threshold_tree == NULL)
884   {
885     threshold_tree = c_avl_create ((void *) strcmp);
886     if (threshold_tree == NULL)
887     {
888       ERROR ("ut_config: c_avl_create failed.");
889       return (-1);
890     }
891   }
892
893   memset (&th, '\0', sizeof (th));
894   th.warning_min = NAN;
895   th.warning_max = NAN;
896   th.failure_min = NAN;
897   th.failure_max = NAN;
898
899   th.hits = 0;
900   th.hysteresis = 0;
901   th.flags = UT_FLAG_INTERESTING; /* interesting by default */
902
903   for (i = 0; i < ci->children_num; i++)
904   {
905     oconfig_item_t *option = ci->children + i;
906
907     if (strcasecmp ("Type", option->key) == 0)
908       status = ut_config_type (&th, option);
909     else if (strcasecmp ("Plugin", option->key) == 0)
910       status = ut_config_plugin (&th, option);
911     else if (strcasecmp ("Host", option->key) == 0)
912       status = ut_config_host (&th, option);
913     else
914     {
915       WARNING ("threshold values: Option `%s' not allowed here.", option->key);
916       status = -1;
917     }
918
919     if (status != 0)
920       break;
921   }
922
923   if (c_avl_size (threshold_tree) > 0) {
924     plugin_register_missing ("threshold", ut_missing,
925         /* user data = */ NULL);
926     plugin_register_write ("threshold", ut_check_threshold,
927         /* user data = */ NULL);
928   }
929
930   return (status);
931 } /* }}} int um_config */
932
933 void module_register (void)
934 {
935   plugin_register_complex_config ("threshold", ut_config);
936 }
937
938 /* vim: set sw=2 ts=8 sts=2 tw=78 et fdm=marker : */