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