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