Merge branch 'collectd-4.5' into collectd-4.6
[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   plugin_notification_meta_add_string (&n, "DataSource",
620       ds->ds[ds_index].name);
621   plugin_notification_meta_add_double (&n, "CurrentValue", values[ds_index]);
622   plugin_notification_meta_add_double (&n, "WarningMin", th->warning_min);
623   plugin_notification_meta_add_double (&n, "WarningMax", th->warning_max);
624   plugin_notification_meta_add_double (&n, "FailureMin", th->failure_min);
625   plugin_notification_meta_add_double (&n, "FailureMax", th->failure_max);
626
627   /* Send an okay notification */
628   if (state == STATE_OKAY)
629   {
630     status = ssnprintf (buf, bufsize, ": All data sources are within range again.");
631     buf += status;
632     bufsize -= status;
633   }
634   else
635   {
636     double min;
637     double max;
638
639     min = (state == STATE_ERROR) ? th->failure_min : th->warning_min;
640     max = (state == STATE_ERROR) ? th->failure_max : th->warning_max;
641
642     if (th->flags & UT_FLAG_INVERT)
643     {
644       if (!isnan (min) && !isnan (max))
645       {
646         status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
647             "%f. That is within the %s region of %f and %f.",
648             ds->ds[ds_index].name, values[ds_index],
649             (state == STATE_ERROR) ? "failure" : "warning",
650             min, max);
651       }
652       else
653       {
654         status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
655             "%f. That is %s the %s threshold of %f.",
656             ds->ds[ds_index].name, values[ds_index],
657             isnan (min) ? "below" : "above",
658             (state == STATE_ERROR) ? "failure" : "warning",
659             isnan (min) ? max : min);
660       }
661     }
662     else /* is not inverted */
663     {
664       status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
665           "%f. That is %s the %s threshold of %f.",
666           ds->ds[ds_index].name, values[ds_index],
667           (values[ds_index] < min) ? "below" : "above",
668           (state == STATE_ERROR) ? "failure" : "warning",
669           (values[ds_index] < min) ? min : max);
670     }
671     buf += status;
672     bufsize -= status;
673   }
674
675   plugin_dispatch_notification (&n);
676
677   plugin_notification_meta_free (n.meta);
678   return (0);
679 } /* }}} int ut_report_state */
680
681 /*
682  * int ut_check_one_data_source
683  *
684  * Checks one data source against the given threshold configuration. If the
685  * `DataSource' option is set in the threshold, and the name does NOT match,
686  * `okay' is returned. If the threshold does match, its failure and warning
687  * min and max values are checked and `failure' or `warning' is returned if
688  * appropriate.
689  * Does not fail.
690  */
691 static int ut_check_one_data_source (const data_set_t *ds,
692     const value_list_t __attribute__((unused)) *vl,
693     const threshold_t *th,
694     const gauge_t *values,
695     int ds_index)
696 { /* {{{ */
697   const char *ds_name;
698   int is_warning = 0;
699   int is_failure = 0;
700
701   /* check if this threshold applies to this data source */
702   ds_name = ds->ds[ds_index].name;
703   if ((th->data_source[0] != 0)
704       && (strcmp (ds_name, th->data_source) != 0))
705     return (STATE_OKAY);
706
707   if ((th->flags & UT_FLAG_INVERT) != 0)
708   {
709     is_warning--;
710     is_failure--;
711   }
712
713   if ((!isnan (th->failure_min) && (th->failure_min > values[ds_index]))
714       || (!isnan (th->failure_max) && (th->failure_max < values[ds_index])))
715     is_failure++;
716   if (is_failure != 0)
717     return (STATE_ERROR);
718
719   if ((!isnan (th->warning_min) && (th->warning_min > values[ds_index]))
720       || (!isnan (th->warning_max) && (th->warning_max < values[ds_index])))
721     is_warning++;
722   if (is_warning != 0)
723     return (STATE_WARNING);
724
725   return (STATE_OKAY);
726 } /* }}} int ut_check_one_data_source */
727
728 /*
729  * int ut_check_one_threshold
730  *
731  * Checks all data sources of a value list against the given threshold, using
732  * the ut_check_one_data_source function above. Returns the worst status,
733  * which is `okay' if nothing has failed.
734  * Returns less than zero if the data set doesn't have any data sources.
735  */
736 static int ut_check_one_threshold (const data_set_t *ds,
737     const value_list_t *vl,
738     const threshold_t *th,
739     const gauge_t *values,
740     int *ret_ds_index)
741 { /* {{{ */
742   int ret = -1;
743   int ds_index = -1;
744   int i;
745
746   for (i = 0; i < ds->ds_num; i++)
747   {
748     int status;
749
750     status = ut_check_one_data_source (ds, vl, th, values, i);
751     if (ret < status)
752     {
753       ret = status;
754       ds_index = i;
755     }
756   } /* for (ds->ds_num) */
757
758   if (ret_ds_index != NULL)
759     *ret_ds_index = ds_index;
760
761   return (ret);
762 } /* }}} int ut_check_one_threshold */
763
764 /*
765  * int ut_check_threshold (PUBLIC)
766  *
767  * Gets a list of matching thresholds and searches for the worst status by one
768  * of the thresholds. Then reports that status using the ut_report_state
769  * function above. 
770  * Returns zero on success and if no threshold has been configured. Returns
771  * less than zero on failure.
772  */
773 int ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
774 { /* {{{ */
775   threshold_t *th;
776   gauge_t *values;
777   int status;
778
779   int worst_state = -1;
780   threshold_t *worst_th = NULL;
781   int worst_ds_index = -1;
782
783   if (threshold_tree == NULL)
784     return (0);
785
786   /* Is this lock really necessary? So far, thresholds are only inserted at
787    * startup. -octo */
788   pthread_mutex_lock (&threshold_lock);
789   th = threshold_search (vl);
790   pthread_mutex_unlock (&threshold_lock);
791   if (th == NULL)
792     return (0);
793
794   DEBUG ("ut_check_threshold: Found matching threshold(s)");
795
796   values = uc_get_rate (ds, vl);
797   if (values == NULL)
798     return (0);
799
800   while (th != NULL)
801   {
802     int ds_index = -1;
803
804     status = ut_check_one_threshold (ds, vl, th, values, &ds_index);
805     if (status < 0)
806     {
807       ERROR ("ut_check_threshold: ut_check_one_threshold failed.");
808       sfree (values);
809       return (-1);
810     }
811
812     if (worst_state < status)
813     {
814       worst_state = status;
815       worst_th = th;
816       worst_ds_index = ds_index;
817     }
818
819     th = th->next;
820   } /* while (th) */
821
822   status = ut_report_state (ds, vl, worst_th, values,
823       worst_ds_index, worst_state);
824   if (status != 0)
825   {
826     ERROR ("ut_check_threshold: ut_report_state failed.");
827     sfree (values);
828     return (-1);
829   }
830
831   sfree (values);
832
833   return (0);
834 } /* }}} int ut_check_threshold */
835
836 /*
837  * int ut_check_interesting (PUBLIC)
838  *
839  * Given an identification returns
840  * 0: No threshold is defined.
841  * 1: A threshold has been found. The flag `persist' is off.
842  * 2: A threshold has been found. The flag `persist' is on.
843  *    (That is, it is expected that many notifications are sent until the
844  *    problem disappears.)
845  */
846 int ut_check_interesting (const char *name)
847 { /* {{{ */
848   char *name_copy = NULL;
849   char *host = NULL;
850   char *plugin = NULL;
851   char *plugin_instance = NULL;
852   char *type = NULL;
853   char *type_instance = NULL;
854   int status;
855   data_set_t ds;
856   value_list_t vl;
857   threshold_t *th;
858
859   /* If there is no tree nothing is interesting. */
860   if (threshold_tree == NULL)
861     return (0);
862
863   name_copy = strdup (name);
864   if (name_copy == NULL)
865   {
866     ERROR ("ut_check_interesting: strdup failed.");
867     return (-1);
868   }
869
870   status = parse_identifier (name_copy, &host,
871       &plugin, &plugin_instance, &type, &type_instance);
872   if (status != 0)
873   {
874     ERROR ("ut_check_interesting: parse_identifier failed.");
875     sfree (name_copy);
876     return (-1);
877   }
878
879   memset (&ds, '\0', sizeof (ds));
880   memset (&vl, '\0', sizeof (vl));
881
882   sstrncpy (vl.host, host, sizeof (vl.host));
883   sstrncpy (vl.plugin, plugin, sizeof (vl.plugin));
884   if (plugin_instance != NULL)
885     sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
886   sstrncpy (ds.type, type, sizeof (ds.type));
887   sstrncpy (vl.type, type, sizeof (vl.type));
888   if (type_instance != NULL)
889     sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
890
891   sfree (name_copy);
892   host = plugin = plugin_instance = type = type_instance = NULL;
893
894   th = threshold_search (&vl);
895   if (th == NULL)
896     return (0);
897   if ((th->flags & UT_FLAG_PERSIST) == 0)
898     return (1);
899   return (2);
900 } /* }}} int ut_check_interesting */
901
902 /* vim: set sw=2 ts=8 sts=2 tw=78 fdm=marker : */