0ef728a2c3153f43e6e9f1be3d02f8d5e65fb663
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 **/
28 #include "collectd.h"
29 #include "common.h"
30 #include "plugin.h"
31 #include "utils_avltree.h"
32 #include "utils_cache.h"
34 #include <assert.h>
35 #include <pthread.h>
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 /* }}} */
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 /* }}} */
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;
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';
97 if (c_avl_get (threshold_tree, name, (void *) &th) == 0)
98 return (th);
99 else
100 return (NULL);
101 } /* }}} threshold_t *threshold_get */
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;
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 }
126 name_copy = strdup (name);
127 if (name_copy == NULL)
128 {
129 ERROR ("ut_threshold_add: strdup failed.");
130 return (-1);
131 }
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;
143 DEBUG ("ut_threshold_add: Adding entry `%s'", name);
145 pthread_mutex_lock (&threshold_lock);
147 th_ptr = threshold_get (th->host, th->plugin, th->plugin_instance,
148 th->type, th->type_instance);
150 while ((th_ptr != NULL) && (th_ptr->next != NULL))
151 th_ptr = th_ptr->next;
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 }
164 pthread_mutex_unlock (&threshold_lock);
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 }
173 return (status);
174 } /* }}} int ut_threshold_add */
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;
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);
225 return (NULL);
226 } /* }}} threshold_t *threshold_search */
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 }
244 sstrncpy (th->data_source, ci->values[0].value.string,
245 sizeof (th->data_source));
247 return (0);
248 } /* int ut_config_type_datasource */
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 }
260 sstrncpy (th->type_instance, ci->values[0].value.string,
261 sizeof (th->type_instance));
263 return (0);
264 } /* int ut_config_type_instance */
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 }
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;
281 return (0);
282 } /* int ut_config_type_max */
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 }
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;
299 return (0);
300 } /* int ut_config_type_min */
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 }
312 th->hits = ci->values[0].value.number;
314 return (0);
315 } /* int ut_config_type_hits */
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 }
327 th->hysteresis = ci->values[0].value.number;
329 return (0);
330 } /* int ut_config_type_hysteresis */
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;
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 }
346 if (ci->children_num < 1)
347 {
348 WARNING ("threshold values: The `Type' block needs at least one option.");
349 return (-1);
350 }
352 memcpy (&th, th_orig, sizeof (th));
353 sstrncpy (th.type, ci->values[0].value.string, sizeof (th.type));
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 */
363 for (i = 0; i < ci->children_num; i++)
364 {
365 oconfig_item_t *option = ci->children + i;
366 status = 0;
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 }
399 if (status != 0)
400 break;
401 }
403 if (status == 0)
404 {
405 status = ut_threshold_add (&th);
406 }
408 return (status);
409 } /* int ut_config_type */
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 }
421 sstrncpy (th->plugin_instance, ci->values[0].value.string,
422 sizeof (th->plugin_instance));
424 return (0);
425 } /* int ut_config_plugin_instance */
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;
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 }
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 }
448 memcpy (&th, th_orig, sizeof (th));
449 sstrncpy (th.plugin, ci->values[0].value.string, sizeof (th.plugin));
451 for (i = 0; i < ci->children_num; i++)
452 {
453 oconfig_item_t *option = ci->children + i;
454 status = 0;
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 }
467 if (status != 0)
468 break;
469 }
471 return (status);
472 } /* int ut_config_plugin */
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;
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 }
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 }
495 memcpy (&th, th_orig, sizeof (th));
496 sstrncpy (th.host, ci->values[0].value.string, sizeof (th.host));
498 for (i = 0; i < ci->children_num; i++)
499 {
500 oconfig_item_t *option = ci->children + i;
501 status = 0;
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 }
514 if (status != 0)
515 break;
516 }
518 return (status);
519 } /* int ut_config_host */
520 /*
521 * End of the functions used to configure threshold values.
522 */
523 /* }}} */
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;
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 }
555 if ((th->flags & UT_FLAG_INVERT) != 0)
556 {
557 is_warning--;
558 is_failure--;
559 }
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++;
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 }
591 if (is_failure != 0)
592 return (STATE_ERROR);
594 if (is_warning != 0)
595 return (STATE_WARNING);
597 return (STATE_OKAY);
598 } /* }}} int ut_check_one_data_source */
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];
619 memcpy (values_copy, values, sizeof (values_copy));
621 if ((th->flags & UT_FLAG_PERCENTAGE) != 0)
622 {
623 int num = 0;
624 gauge_t sum=0.0;
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 }
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 }
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) */
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) */
665 return (ret);
666 } /* }}} int ut_check_one_threshold */
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;
684 memset(statuses, 0, vl->values_len * sizeof(*statuses));
687 if (threshold_tree == NULL)
688 return 0;
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);
698 DEBUG ("ut_check_threshold: Found matching threshold(s)");
700 values = uc_get_rate (ds, vl);
701 if (values == NULL)
702 return (0);
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 }
714 th = th->next;
715 } /* while (th) */
717 sfree (values);
719 return (0);
720 } /* }}} int ut_check_threshold */
722 int write_riemann_threshold_config (oconfig_item_t *ci)
723 { /* {{{ */
724 int i;
725 int status = 0;
727 threshold_t th;
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 }
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;
745 th.hits = 0;
746 th.hysteresis = 0;
747 th.flags = UT_FLAG_INTERESTING; /* interesting by default */
749 for (i = 0; i < ci->children_num; i++)
750 {
751 oconfig_item_t *option = ci->children + i;
752 status = 0;
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 }
766 if (status != 0)
767 break;
768 }
770 return (status);
771 } /* }}} int um_config */
773 /* vim: set sw=2 ts=8 sts=2 tw=78 et fdm=marker : */