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