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