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