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