src/utils_threshold.c: Improve the replacement macros.
[collectd.git] / src / utils_threshold.c
1 /**
2  * collectd - src/utils_threshold.c
3  * Copyright (C) 2007-2010  Florian octo Forster
4  * Copyright (C) 2008-2009  Sebastian Harl
5  * Copyright (C) 2009,2010  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
618   /* TODO: We could provide here a way to use meta information on thresholds
619    * directly in the future. */
620   char msg[NOTIF_MAX_MSG_LEN];
621   gauge_t *rates;
622   int rates_failed;
623   int i;
624
625   sstrncpy (msg, fmt, sizeof (msg));
626
627 #define REPLACE_FIELD(t,v) do {                                              \
628   char temp[sizeof (msg)];                                                   \
629   if (subst_string (temp, sizeof (temp), msg, (t), (v)) != NULL)             \
630     sstrncpy (msg, temp, sizeof (msg));                                      \
631 } while (0)
632
633 #define REPLACE_FIELD_DBL(t,v) do {                                          \
634   char value_str[NOTIF_MAX_MSG_LEN];                                         \
635   ssnprintf (value_str, sizeof (value_str), "%g", (v));                      \
636   REPLACE_FIELD(t, value_str);                                               \
637 } while (0)
638
639 #define REPLACE_FIELD_INT(t,v) do {                                          \
640   char value_str[NOTIF_MAX_MSG_LEN];                                         \
641   ssnprintf (value_str, sizeof (value_str), "%i", (v));                      \
642   REPLACE_FIELD(t, value_str);                                               \
643 } while (0)
644
645   REPLACE_FIELD ("%{host}", n->host);
646   REPLACE_FIELD ("%{plugin}", n->plugin);
647   REPLACE_FIELD ("%{plugin_instance}", n->plugin_instance);
648   REPLACE_FIELD ("%{type}", n->type);
649   REPLACE_FIELD ("%{type_instance}", n->type_instance);
650   REPLACE_FIELD ("%{data_source}", ds->ds[ds_index].name);
651
652   /* This is the offending value, its equivalent to %{ds:value}, if
653    * value is the data_source name. */
654   REPLACE_FIELD_DBL ("%{value}", values[ds_index]);
655
656   /* Now replace all %{ds:<template>} like target_notification does */
657   rates_failed = 0;
658   rates = NULL;
659   for (i = 0; i < ds->ds_num; i++)
660   {
661     char template[DATA_MAX_NAME_LEN];
662     char value_str[DATA_MAX_NAME_LEN];
663
664     ssnprintf (template, sizeof (template), "%%{ds:%s}", ds->ds[i].name);
665
666     if (ds->ds[i].type != DS_TYPE_GAUGE)
667     {
668       if ((rates == NULL) && (rates_failed == 0))
669       {
670         rates = uc_get_rate (ds, vl);
671         if (rates == NULL)
672           rates_failed = 1;
673       }
674     }
675
676     /* If this is a gauge value, use the current value. */
677     if (ds->ds[i].type == DS_TYPE_GAUGE)
678       ssnprintf (value_str, sizeof (value_str),
679           "%g", (double) vl->values[i].gauge);
680     /* If it's a counter, try to use the current rate. This may fail, if the
681      * value has been renamed. */
682     else if (rates != NULL)
683       ssnprintf (value_str, sizeof (value_str),
684           "%g", (double) rates[i]);
685     /* Since we don't know any better, use the string `unknown'. */
686     else
687       sstrncpy (value_str, "unknown", sizeof (value_str));
688
689     REPLACE_FIELD (template, value_str);
690   }
691   sfree (rates);
692
693   if (th != NULL)
694   {
695     if ( !isnan(th->warning_min)) {
696       REPLACE_FIELD_DBL ("%{warning_min}", th->warning_min);
697     }
698     if ( !isnan(th->warning_max)) {
699       REPLACE_FIELD_DBL ("%{warning_max}", th->warning_max);
700     }
701     if ( !isnan(th->failure_min)) {
702       REPLACE_FIELD_DBL ("%{failure_min}", th->failure_min);
703     }
704     if ( !isnan(th->failure_max)) {
705       REPLACE_FIELD_DBL ("%{failure_max}", th->failure_max);
706     }
707
708     REPLACE_FIELD_DBL ("%{hysteresis}", th->hysteresis);
709     REPLACE_FIELD_INT ("%{hits}", th->hits);
710   }
711
712   return ssnprintf (out, bufsize, "%s", msg);
713 } /* int ut_build_message */
714
715 /*
716  * int ut_report_state
717  *
718  * Checks if the `state' differs from the old state and creates a notification
719  * if appropriate.
720  * Does not fail.
721  */
722 static int ut_report_state (const data_set_t *ds,
723     const value_list_t *vl,
724     const threshold_t *th,
725     const gauge_t *values,
726     int ds_index,
727     int state)
728 { /* {{{ */
729   int state_old;
730   notification_t n;
731
732   char *buf;
733   size_t bufsize;
734
735   int status;
736
737   /* Check if hits matched */
738   if ( (th->hits != 0) )
739   {
740     int hits = uc_get_hits(ds,vl);
741     /* The STATE_OKAY always reset hits, or if hits reaise the limit */
742     if ( (state == STATE_OKAY) || (hits > th->hits) )
743     {
744         DEBUG("ut_report_state: reset uc_get_hits = 0");
745         uc_set_hits(ds,vl,0); /* reset hit counter and notify */
746     } else {
747       DEBUG("ut_report_state: th->hits = %d, uc_get_hits = %d",th->hits,uc_get_hits(ds,vl));
748       (void) uc_inc_hits(ds,vl,1); /* increase hit counter */
749       return (0);
750     }
751   } /* end check hits */
752
753   state_old = uc_get_state (ds, vl);
754
755   /* If the state didn't change, only report if `persistent' is specified and
756    * the state is not `okay'. */
757   if (state == state_old)
758   {
759     if ((th->flags & UT_FLAG_PERSIST) == 0)
760       return (0);
761     else if (state == STATE_OKAY)
762       return (0);
763   }
764
765   if (state != state_old)
766     uc_set_state (ds, vl, state);
767
768   NOTIFICATION_INIT_VL (&n, vl, ds);
769
770   buf = n.message;
771   bufsize = sizeof (n.message);
772
773   if (state == STATE_OKAY)
774     n.severity = NOTIF_OKAY;
775   else if (state == STATE_WARNING)
776     n.severity = NOTIF_WARNING;
777   else
778     n.severity = NOTIF_FAILURE;
779
780   n.time = vl->time;
781
782   /* Format custom message if present */
783   if (th->message != NULL)
784   {
785     status = ut_build_message (buf, bufsize, th->message,
786         ds, ds_index, vl, values,
787         &n, th);
788     buf += status;
789     bufsize -= status;
790   }
791   else /* No custom message. Using default message for threshold */
792   {
793     status = ssnprintf (buf, bufsize, "Host %s, plugin %s",
794         vl->host, vl->plugin);
795     buf += status;
796     bufsize -= status;
797
798     if (vl->plugin_instance[0] != '\0')
799     {
800       status = ssnprintf (buf, bufsize, " (instance %s)",
801           vl->plugin_instance);
802       buf += status;
803       bufsize -= status;
804     }
805
806     status = ssnprintf (buf, bufsize, " type %s", vl->type);
807     buf += status;
808     bufsize -= status;
809
810     if (vl->type_instance[0] != '\0')
811     {
812       status = ssnprintf (buf, bufsize, " (instance %s)",
813           vl->type_instance);
814       buf += status;
815       bufsize -= status;
816     }
817
818     /* Build okay notification message */
819     if (state == STATE_OKAY)
820     {
821       status = ssnprintf (buf, bufsize, ": All data sources are within range again.");
822       buf += status;
823       bufsize -= status;
824     }
825     else /* build non-okay notification message */
826     {
827       double min;
828       double max;
829
830       min = (state == STATE_ERROR) ? th->failure_min : th->warning_min;
831       max = (state == STATE_ERROR) ? th->failure_max : th->warning_max;
832
833       if (th->flags & UT_FLAG_INVERT)
834       {
835         if (!isnan (min) && !isnan (max))
836         {
837           status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
838               "%f. That is within the %s region of %f%s and %f%s.",
839               ds->ds[ds_index].name, values[ds_index],
840               (state == STATE_ERROR) ? "failure" : "warning",
841               min, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "",
842               max, ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
843         }
844         else
845         {
846           status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
847               "%f. That is %s the %s threshold of %f%s.",
848               ds->ds[ds_index].name, values[ds_index],
849               isnan (min) ? "below" : "above",
850               (state == STATE_ERROR) ? "failure" : "warning",
851               isnan (min) ? max : min,
852               ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
853         }
854       }
855       else /* is not inverted */
856       {
857         status = ssnprintf (buf, bufsize, ": Data source \"%s\" is currently "
858             "%f. That is %s the %s threshold of %f%s.",
859             ds->ds[ds_index].name, values[ds_index],
860             (values[ds_index] < min) ? "below" : "above",
861             (state == STATE_ERROR) ? "failure" : "warning",
862             (values[ds_index] < min) ? min : max,
863             ((th->flags & UT_FLAG_PERCENTAGE) != 0) ? "%" : "");
864       }
865       buf += status;
866       bufsize -= status;
867     }
868   }
869
870   /* adds meta to notification */
871   plugin_notification_meta_add_string (&n, "DataSource",
872       ds->ds[ds_index].name);
873   plugin_notification_meta_add_double (&n, "CurrentValue", values[ds_index]);
874   plugin_notification_meta_add_double (&n, "WarningMin", th->warning_min);
875   plugin_notification_meta_add_double (&n, "WarningMax", th->warning_max);
876   plugin_notification_meta_add_double (&n, "FailureMin", th->failure_min);
877   plugin_notification_meta_add_double (&n, "FailureMax", th->failure_max);
878
879   plugin_dispatch_notification (&n);
880
881   plugin_notification_meta_free (n.meta);
882   return (0);
883 } /* }}} int ut_report_state */
884
885 /*
886  * int ut_check_one_data_source
887  *
888  * Checks one data source against the given threshold configuration. If the
889  * `DataSource' option is set in the threshold, and the name does NOT match,
890  * `okay' is returned. If the threshold does match, its failure and warning
891  * min and max values are checked and `failure' or `warning' is returned if
892  * appropriate.
893  * Does not fail.
894  */
895 static int ut_check_one_data_source (const data_set_t *ds,
896     const value_list_t __attribute__((unused)) *vl,
897     const threshold_t *th,
898     const gauge_t *values,
899     int ds_index)
900 { /* {{{ */
901   const char *ds_name;
902   int is_warning = 0;
903   int is_failure = 0;
904   int prev_state = STATE_OKAY;
905
906   /* check if this threshold applies to this data source */
907   if (ds != NULL)
908   {
909     ds_name = ds->ds[ds_index].name;
910     if ((th->data_source[0] != 0)
911         && (strcmp (ds_name, th->data_source) != 0))
912       return (STATE_OKAY);
913   }
914
915   if ((th->flags & UT_FLAG_INVERT) != 0)
916   {
917     is_warning--;
918     is_failure--;
919   }
920
921   /* XXX: This is an experimental code, not optimized, not fast, not reliable,
922    * and probably, do not work as you expect. Enjoy! :D */
923   if ( (th->hysteresis > 0) && ((prev_state = uc_get_state(ds,vl)) != STATE_OKAY) )
924   {
925     switch(prev_state)
926     {
927       case STATE_ERROR:
928         if ( (!isnan (th->failure_min) && ((th->failure_min + th->hysteresis) < values[ds_index])) ||
929              (!isnan (th->failure_max) && ((th->failure_max - th->hysteresis) > values[ds_index])) )
930           return (STATE_OKAY);
931         else
932           is_failure++;
933       case STATE_WARNING:
934         if ( (!isnan (th->warning_min) && ((th->warning_min + th->hysteresis) < values[ds_index])) ||
935              (!isnan (th->warning_max) && ((th->warning_max - th->hysteresis) > values[ds_index])) )
936           return (STATE_OKAY);
937         else
938           is_warning++;
939      }
940   }
941   else { /* no hysteresis */
942     if ((!isnan (th->failure_min) && (th->failure_min > values[ds_index]))
943         || (!isnan (th->failure_max) && (th->failure_max < values[ds_index])))
944       is_failure++;
945
946     if ((!isnan (th->warning_min) && (th->warning_min > values[ds_index]))
947         || (!isnan (th->warning_max) && (th->warning_max < values[ds_index])))
948       is_warning++;
949  }
950
951   if (is_failure != 0)
952     return (STATE_ERROR);
953
954   if (is_warning != 0)
955     return (STATE_WARNING);
956
957   return (STATE_OKAY);
958 } /* }}} int ut_check_one_data_source */
959
960 /*
961  * int ut_check_one_threshold
962  *
963  * Checks all data sources of a value list against the given threshold, using
964  * the ut_check_one_data_source function above. Returns the worst status,
965  * which is `okay' if nothing has failed.
966  * Returns less than zero if the data set doesn't have any data sources.
967  */
968 static int ut_check_one_threshold (const data_set_t *ds,
969     const value_list_t *vl,
970     const threshold_t *th,
971     const gauge_t *values,
972     int *ret_ds_index)
973 { /* {{{ */
974   int ret = -1;
975   int ds_index = -1;
976   int i;
977   gauge_t values_copy[ds->ds_num];
978
979   memcpy (values_copy, values, sizeof (values_copy));
980
981   if ((th->flags & UT_FLAG_PERCENTAGE) != 0)
982   {
983     int num = 0;
984     gauge_t sum=0.0;
985
986     if (ds->ds_num == 1)
987     {
988       WARNING ("ut_check_one_threshold: The %s type has only one data "
989           "source, but you have configured to check this as a percentage. "
990           "That doesn't make much sense, because the percentage will always "
991           "be 100%%!", ds->type);
992     }
993
994     /* Prepare `sum' and `num'. */
995     for (i = 0; i < ds->ds_num; i++)
996       if (!isnan (values[i]))
997       {
998         num++;
999         sum += values[i];
1000       }
1001
1002     if ((num == 0) /* All data sources are undefined. */
1003         || (sum == 0.0)) /* Sum is zero, cannot calculate percentage. */
1004     {
1005       for (i = 0; i < ds->ds_num; i++)
1006         values_copy[i] = NAN;
1007     }
1008     else /* We can actually calculate the percentage. */
1009     {
1010       for (i = 0; i < ds->ds_num; i++)
1011         values_copy[i] = 100.0 * values[i] / sum;
1012     }
1013   } /* if (UT_FLAG_PERCENTAGE) */
1014
1015   for (i = 0; i < ds->ds_num; i++)
1016   {
1017     int status;
1018
1019     status = ut_check_one_data_source (ds, vl, th, values_copy, i);
1020     if (ret < status)
1021     {
1022       ret = status;
1023       ds_index = i;
1024     }
1025   } /* for (ds->ds_num) */
1026
1027   if (ret_ds_index != NULL)
1028     *ret_ds_index = ds_index;
1029
1030   return (ret);
1031 } /* }}} int ut_check_one_threshold */
1032
1033 /*
1034  * int ut_check_threshold (PUBLIC)
1035  *
1036  * Gets a list of matching thresholds and searches for the worst status by one
1037  * of the thresholds. Then reports that status using the ut_report_state
1038  * function above. 
1039  * Returns zero on success and if no threshold has been configured. Returns
1040  * less than zero on failure.
1041  */
1042 int ut_check_threshold (const data_set_t *ds, const value_list_t *vl)
1043 { /* {{{ */
1044   threshold_t *th;
1045   gauge_t *values;
1046   int status;
1047
1048   int worst_state = -1;
1049   threshold_t *worst_th = NULL;
1050   int worst_ds_index = -1;
1051
1052   if (threshold_tree == NULL)
1053     return (0);
1054
1055   /* Is this lock really necessary? So far, thresholds are only inserted at
1056    * startup. -octo */
1057   pthread_mutex_lock (&threshold_lock);
1058   th = threshold_search (vl);
1059   pthread_mutex_unlock (&threshold_lock);
1060   if (th == NULL)
1061     return (0);
1062
1063   DEBUG ("ut_check_threshold: Found matching threshold(s)");
1064
1065   values = uc_get_rate (ds, vl);
1066   if (values == NULL)
1067     return (0);
1068
1069   while (th != NULL)
1070   {
1071     int ds_index = -1;
1072
1073     status = ut_check_one_threshold (ds, vl, th, values, &ds_index);
1074     if (status < 0)
1075     {
1076       ERROR ("ut_check_threshold: ut_check_one_threshold failed.");
1077       sfree (values);
1078       return (-1);
1079     }
1080
1081     if (worst_state < status)
1082     {
1083       worst_state = status;
1084       worst_th = th;
1085       worst_ds_index = ds_index;
1086     }
1087
1088     th = th->next;
1089   } /* while (th) */
1090
1091   status = ut_report_state (ds, vl, worst_th, values,
1092       worst_ds_index, worst_state);
1093   if (status != 0)
1094   {
1095     ERROR ("ut_check_threshold: ut_report_state failed.");
1096     sfree (values);
1097     return (-1);
1098   }
1099
1100   sfree (values);
1101
1102   return (0);
1103 } /* }}} int ut_check_threshold */
1104
1105 /*
1106  * int ut_check_interesting (PUBLIC)
1107  *
1108  * Given an identification returns
1109  * 0: No threshold is defined.
1110  * 1: A threshold has been found. The flag `persist' is off.
1111  * 2: A threshold has been found. The flag `persist' is on.
1112  *    (That is, it is expected that many notifications are sent until the
1113  *    problem disappears.)
1114  */
1115 int ut_check_interesting (const char *name)
1116 { /* {{{ */
1117   char *name_copy = NULL;
1118   char *host = NULL;
1119   char *plugin = NULL;
1120   char *plugin_instance = NULL;
1121   char *type = NULL;
1122   char *type_instance = NULL;
1123   int status;
1124   data_set_t ds;
1125   value_list_t vl;
1126   threshold_t *th;
1127
1128   /* If there is no tree nothing is interesting. */
1129   if (threshold_tree == NULL)
1130     return (0);
1131
1132   name_copy = strdup (name);
1133   if (name_copy == NULL)
1134   {
1135     ERROR ("ut_check_interesting: strdup failed.");
1136     return (-1);
1137   }
1138
1139   status = parse_identifier (name_copy, &host,
1140       &plugin, &plugin_instance, &type, &type_instance);
1141   if (status != 0)
1142   {
1143     ERROR ("ut_check_interesting: parse_identifier failed.");
1144     sfree (name_copy);
1145     return (-1);
1146   }
1147
1148   memset (&ds, '\0', sizeof (ds));
1149   memset (&vl, '\0', sizeof (vl));
1150
1151   sstrncpy (vl.host, host, sizeof (vl.host));
1152   sstrncpy (vl.plugin, plugin, sizeof (vl.plugin));
1153   if (plugin_instance != NULL)
1154     sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance));
1155   sstrncpy (ds.type, type, sizeof (ds.type));
1156   sstrncpy (vl.type, type, sizeof (vl.type));
1157   if (type_instance != NULL)
1158     sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
1159
1160   sfree (name_copy);
1161   host = plugin = plugin_instance = type = type_instance = NULL;
1162
1163   th = threshold_search (&vl);
1164   if (th == NULL)
1165     return (0);
1166
1167   if ((th->flags & UT_FLAG_INTERESTING) == 0)
1168     return (0);
1169
1170   if ((th->flags & UT_FLAG_PERSIST) == 0)
1171     return (1);
1172   return (2);
1173 } /* }}} int ut_check_interesting */
1174
1175 int ut_search_threshold (const value_list_t *vl, /* {{{ */
1176     threshold_t *ret_threshold)
1177 {
1178   threshold_t *t;
1179
1180   if (vl == NULL)
1181     return (EINVAL);
1182
1183   t = threshold_search (vl);
1184   if (t == NULL)
1185     return (ENOENT);
1186
1187   memcpy (ret_threshold, t, sizeof (*ret_threshold));
1188   ret_threshold->next = NULL;
1189
1190   return (0);
1191 } /* }}} int ut_search_threshold */
1192
1193 /* vim: set sw=2 ts=8 sts=2 tw=78 et fdm=marker : */