Merge branch 'collectd-4.5'
[collectd.git] / src / dbi.c
1 /**
2  * collectd - src/dbi.c
3  * Copyright (C) 2008  Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
17  *
18  * Authors:
19  *   Florian octo Forster <octo at verplant.org>
20  **/
21
22 #include "collectd.h"
23 #include "common.h"
24 #include "plugin.h"
25 #include "configfile.h"
26
27 #include <dbi/dbi.h>
28
29 /*
30  * Data types
31  */
32 struct cdbi_driver_option_s
33 {
34   char *key;
35   char *value;
36 };
37 typedef struct cdbi_driver_option_s cdbi_driver_option_t;
38
39 struct cdbi_query_s
40 {
41   char    *name;
42   char    *statement;
43   char    *type;
44   char   **instances;
45   size_t   instances_num;
46   char   **values;
47   size_t   values_num;
48 };
49 typedef struct cdbi_query_s cdbi_query_t;
50
51 struct cdbi_database_s
52 {
53   char *name;
54   char *select_db;
55
56   char *driver;
57   cdbi_driver_option_t *driver_options;
58   size_t driver_options_num;
59
60   cdbi_query_t **queries;
61   size_t      queries_num;
62
63   dbi_conn connection;
64 };
65 typedef struct cdbi_database_s cdbi_database_t;
66
67 /*
68  * Global variables
69  */
70 static cdbi_query_t    **queries       = NULL;
71 static size_t         queries_num   = 0;
72 static cdbi_database_t **databases     = NULL;
73 static size_t         databases_num = 0;
74
75 /*
76  * Functions
77  */
78 static const char *cdbi_strerror (dbi_conn conn, /* {{{ */
79     char *buffer, size_t buffer_size)
80 {
81   const char *msg;
82   int status;
83
84   if (conn == NULL)
85   {
86     sstrncpy (buffer, "connection is NULL", buffer_size);
87     return (buffer);
88   }
89
90   msg = NULL;
91   status = dbi_conn_error (conn, &msg);
92   if ((status >= 0) && (msg != NULL))
93     ssnprintf (buffer, buffer_size, "%s (status %i)", msg, status);
94   else
95     ssnprintf (buffer, buffer_size, "dbi_conn_error failed with status %i",
96         status);
97
98   return (buffer);
99 } /* }}} const char *cdbi_conn_error */
100
101 static int cdbi_result_get_field (dbi_result res, /* {{{ */
102     const char *name, int dst_type, value_t *ret_value)
103 {
104   value_t value;
105   unsigned int index;
106   unsigned short src_type;
107   dbi_conn connection;
108
109   index = dbi_result_get_field_idx (res, name);
110   if (index < 1)
111   {
112     ERROR ("dbi plugin: cdbi_result_get: No such column: %s.", name);
113     return (-1);
114   }
115
116   src_type = dbi_result_get_field_type_idx (res, index);
117   if (src_type == DBI_TYPE_ERROR)
118   {
119     ERROR ("dbi plugin: cdbi_result_get: "
120         "dbi_result_get_field_type_idx failed.");
121     return (-1);
122   }
123
124   if ((dst_type != DS_TYPE_COUNTER) && (dst_type != DS_TYPE_GAUGE))
125   {
126     ERROR ("dbi plugin: cdbi_result_get: Don't know how to handle "
127         "destination type %i.", dst_type);
128     return (-1);
129   }
130
131   if (src_type == DBI_TYPE_INTEGER)
132   {
133     if (dst_type == DS_TYPE_COUNTER)
134       value.counter = dbi_result_get_ulonglong_idx (res, index);
135     else
136       value.gauge = (gauge_t) dbi_result_get_longlong_idx (res, index);
137   }
138   else if (src_type == DBI_TYPE_DECIMAL)
139   {
140     value.gauge = dbi_result_get_double_idx (res, index);
141     if (dst_type == DS_TYPE_COUNTER)
142       value.counter = (counter_t) round (value.gauge);
143   }
144   else if (src_type == DBI_TYPE_STRING)
145   {
146     const char *string = dbi_result_get_string_idx (res, index);
147     char *endptr = NULL;
148
149     if (string == NULL)
150       value.gauge = NAN;
151     else if (dst_type == DS_TYPE_COUNTER)
152       value.counter = (counter_t) strtoll (string, &endptr, 0);
153     else
154       value.gauge = (gauge_t) strtod (string, &endptr);
155
156     if (string == endptr)
157     {
158       ERROR ("dbi plugin: cdbi_result_get: Can't parse string as number: %s.",
159           string);
160       return (-1);
161     }
162   }
163   else
164   {
165     ERROR ("dbi plugin: cdbi_result_get: Don't know how to handle "
166         "source type %hu.", src_type);
167     return (-1);
168   }
169
170   connection = dbi_result_get_conn (res);
171   if (dbi_conn_error (connection, NULL) != 0)
172   {
173     char errbuf[1024];
174     ERROR ("dbi plugin: cdbi_result_get: dbi_result_get_*_idx failed: %s.",
175         cdbi_strerror (connection, errbuf, sizeof (errbuf)));
176     return (-1);
177   }
178
179   *ret_value = value;
180   return (0);
181 } /* }}} int cdbi_result_get_field */
182
183 static void cdbi_query_free (cdbi_query_t *q) /* {{{ */
184 {
185   size_t i;
186
187   if (q == NULL)
188     return;
189
190   sfree (q->name);
191   sfree (q->statement);
192   sfree (q->type);
193
194   for (i = 0; i < q->instances_num; i++)
195     sfree (q->instances[i]);
196   sfree (q->instances);
197
198   for (i = 0; i < q->values_num; i++)
199     sfree (q->values[i]);
200   sfree (q->values);
201
202   sfree (q);
203 } /* }}} void cdbi_query_free */
204
205 static void cdbi_database_free (cdbi_database_t *db) /* {{{ */
206 {
207   size_t i;
208
209   if (db == NULL)
210     return;
211
212   sfree (db->name);
213   sfree (db->driver);
214
215   for (i = 0; i < db->driver_options_num; i++)
216   {
217     sfree (db->driver_options[i].key);
218     sfree (db->driver_options[i].value);
219   }
220   sfree (db->driver_options);
221
222   sfree (db);
223 } /* }}} void cdbi_database_free */
224
225 static void cdbi_submit (cdbi_database_t *db, cdbi_query_t *q, /* {{{ */
226     char **instances, value_t *values)
227 {
228   value_list_t vl = VALUE_LIST_INIT;
229
230   vl.values = values;
231   vl.values_len = (int) q->values_num;
232   vl.time = time (NULL);
233   sstrncpy (vl.host, hostname_g, sizeof (vl.host));
234   sstrncpy (vl.plugin, "dbi", sizeof (vl.plugin));
235   sstrncpy (vl.plugin_instance, db->name, sizeof (vl.type_instance));
236   sstrncpy (vl.type, q->type, sizeof (vl.type));
237   strjoin (vl.type_instance, sizeof (vl.type_instance),
238       instances, q->instances_num, "-");
239   vl.type_instance[sizeof (vl.type_instance) - 1] = 0;
240
241   plugin_dispatch_values (&vl);
242 } /* }}} void cdbi_submit */
243
244 /* Configuration handling functions {{{
245  *
246  * <Plugin dbi>
247  *   <Query "plugin_instance0">
248  *     Statement "SELECT name, value FROM table"
249  *     Type "gauge"
250  *     InstancesFrom "name"
251  *     ValuesFrom "value"
252  *   </Query>
253  *     
254  *   <Database "plugin_instance1">
255  *     Driver "mysql"
256  *     DriverOption "hostname" "localhost"
257  *     ...
258  *     Query "plugin_instance0"
259  *   </Database>
260  * </Plugin>
261  */
262
263 static int cdbi_config_set_string (char **ret_string, /* {{{ */
264     oconfig_item_t *ci)
265 {
266   char *string;
267
268   if ((ci->values_num != 1)
269       || (ci->values[0].type != OCONFIG_TYPE_STRING))
270   {
271     WARNING ("dbi plugin: The `%s' config option "
272         "needs exactly one string argument.", ci->key);
273     return (-1);
274   }
275
276   string = strdup (ci->values[0].value.string);
277   if (string == NULL)
278   {
279     ERROR ("dbi plugin: strdup failed.");
280     return (-1);
281   }
282
283   if (*ret_string != NULL)
284     free (*ret_string);
285   *ret_string = string;
286
287   return (0);
288 } /* }}} int cdbi_config_set_string */
289
290 static int cdbi_config_add_string (char ***ret_array, /* {{{ */
291     size_t *ret_array_len, oconfig_item_t *ci)
292 {
293   char **array;
294   size_t array_len;
295   int i;
296
297   if (ci->values_num < 1)
298   {
299     WARNING ("dbi plugin: The `%s' config option "
300         "needs at least one argument.", ci->key);
301     return (-1);
302   }
303
304   for (i = 0; i < ci->values_num; i++)
305   {
306     if (ci->values[i].type != OCONFIG_TYPE_STRING)
307     {
308       WARNING ("dbi plugin: Argument %i to the `%s' option "
309           "is not a string.", i + 1, ci->key);
310       return (-1);
311     }
312   }
313
314   array_len = *ret_array_len;
315   array = (char **) realloc (*ret_array,
316       sizeof (char *) * (array_len + ci->values_num));
317   if (array == NULL)
318   {
319     ERROR ("dbi plugin: realloc failed.");
320     return (-1);
321   }
322   *ret_array = array;
323
324   for (i = 0; i < ci->values_num; i++)
325   {
326     array[array_len] = strdup (ci->values[i].value.string);
327     if (array[array_len] == NULL)
328     {
329       ERROR ("dbi plugin: strdup failed.");
330       *ret_array_len = array_len;
331       return (-1);
332     }
333     array_len++;
334   }
335
336   *ret_array_len = array_len;
337   return (0);
338 } /* }}} int cdbi_config_add_string */
339
340 static int cdbi_config_add_query (oconfig_item_t *ci) /* {{{ */
341 {
342   cdbi_query_t *q;
343   int status;
344   int i;
345
346   if ((ci->values_num != 1)
347       || (ci->values[0].type != OCONFIG_TYPE_STRING))
348   {
349     WARNING ("dbi plugin: The `Query' block "
350         "needs exactly one string argument.");
351     return (-1);
352   }
353
354   q = (cdbi_query_t *) malloc (sizeof (*q));
355   if (q == NULL)
356   {
357     ERROR ("dbi plugin: malloc failed.");
358     return (-1);
359   }
360   memset (q, 0, sizeof (*q));
361
362   status = cdbi_config_set_string (&q->name, ci);
363   if (status != 0)
364   {
365     sfree (q);
366     return (status);
367   }
368
369   /* Fill the `cdbi_query_t' structure.. */
370   for (i = 0; i < ci->children_num; i++)
371   {
372     oconfig_item_t *child = ci->children + i;
373
374     if (strcasecmp ("Statement", child->key) == 0)
375       status = cdbi_config_set_string (&q->statement, child);
376     else if (strcasecmp ("Type", child->key) == 0)
377       status = cdbi_config_set_string (&q->type, child);
378     else if (strcasecmp ("InstancesFrom", child->key) == 0)
379       status = cdbi_config_add_string (&q->instances, &q->instances_num, child);
380     else if (strcasecmp ("ValuesFrom", child->key) == 0)
381       status = cdbi_config_add_string (&q->values, &q->values_num, child);
382     else
383     {
384       WARNING ("dbi plugin: Option `%s' not allowed here.", child->key);
385       status = -1;
386     }
387
388     if (status != 0)
389       break;
390   }
391
392   /* Check that all necessary options have been given. */
393   while (status == 0)
394   {
395     if (q->statement == NULL)
396     {
397       WARNING ("dbi plugin: `Statement' not given for query `%s'", q->name);
398       status = -1;
399     }
400     if (q->type == NULL)
401     {
402       WARNING ("dbi plugin: `Type' not given for query `%s'", q->name);
403       status = -1;
404     }
405     if (q->instances == NULL)
406     {
407       WARNING ("dbi plugin: `InstancesFrom' not given for query `%s'", q->name);
408       status = -1;
409     }
410     if (q->values == NULL)
411     {
412       WARNING ("dbi plugin: `ValuesFrom' not given for query `%s'", q->name);
413       status = -1;
414     }
415
416     break;
417   } /* while (status == 0) */
418
419   /* If all went well, add this query to the list of queries within the
420    * database structure. */
421   if (status == 0)
422   {
423     cdbi_query_t **temp;
424
425     temp = (cdbi_query_t **) realloc (queries,
426         sizeof (*queries) * (queries_num + 1));
427     if (temp == NULL)
428     {
429       ERROR ("dbi plugin: realloc failed");
430       status = -1;
431     }
432     else
433     {
434       queries = temp;
435       queries[queries_num] = q;
436       queries_num++;
437     }
438   }
439
440   if (status != 0)
441   {
442     cdbi_query_free (q);
443     return (-1);
444   }
445
446   return (0);
447 } /* }}} int cdbi_config_add_query */
448
449 static int cdbi_config_add_database_driver_option (cdbi_database_t *db, /* {{{ */
450     oconfig_item_t *ci)
451 {
452   cdbi_driver_option_t *option;
453
454   if ((ci->values_num != 2)
455       || (ci->values[0].type != OCONFIG_TYPE_STRING)
456       || (ci->values[1].type != OCONFIG_TYPE_STRING))
457   {
458     WARNING ("dbi plugin: The `DriverOption' config option "
459         "needs exactly two string arguments.");
460     return (-1);
461   }
462
463   option = (cdbi_driver_option_t *) realloc (db->driver_options,
464       sizeof (*option) * (db->driver_options_num + 1));
465   if (option == NULL)
466   {
467     ERROR ("dbi plugin: realloc failed");
468     return (-1);
469   }
470
471   db->driver_options = option;
472   option = db->driver_options + db->driver_options_num;
473
474   option->key = strdup (ci->values[0].value.string);
475   if (option->key == NULL)
476   {
477     ERROR ("dbi plugin: strdup failed.");
478     return (-1);
479   }
480
481   option->value = strdup (ci->values[1].value.string);
482   if (option->value == NULL)
483   {
484     ERROR ("dbi plugin: strdup failed.");
485     sfree (option->key);
486     return (-1);
487   }
488
489   db->driver_options_num++;
490   return (0);
491 } /* }}} int cdbi_config_add_database_driver_option */
492
493 static int cdbi_config_add_database_query (cdbi_database_t *db, /* {{{ */
494     oconfig_item_t *ci)
495 {
496   cdbi_query_t *q;
497   cdbi_query_t **temp;
498   size_t i;
499
500   if ((ci->values_num != 1)
501       || (ci->values[0].type != OCONFIG_TYPE_STRING))
502   {
503     WARNING ("dbi plugin: The `Query' config option "
504         "needs exactly one string argument.");
505     return (-1);
506   }
507
508   q = NULL;
509   for (i = 0; i < queries_num; i++)
510   {
511     if (strcasecmp (queries[i]->name, ci->values[0].value.string) == 0)
512     {
513       q = queries[i];
514       break;
515     }
516   }
517
518   if (q == NULL)
519   {
520     WARNING ("dbi plugin: Database `%s': Unknown query `%s'. "
521         "Please make sure that the <Query \"%s\"> block comes before "
522         "the <Database \"%s\"> block.",
523         db->name, ci->values[0].value.string,
524         ci->values[0].value.string, db->name);
525     return (-1);
526   }
527
528   temp = (cdbi_query_t **) realloc (db->queries,
529       sizeof (*db->queries) * (db->queries_num + 1));
530   if (temp == NULL)
531   {
532     ERROR ("dbi plugin: realloc failed");
533     return (-1);
534   }
535   else
536   {
537     db->queries = temp;
538     db->queries[db->queries_num] = q;
539     db->queries_num++;
540   }
541
542   return (0);
543 } /* }}} int cdbi_config_add_database_query */
544
545 static int cdbi_config_add_database (oconfig_item_t *ci) /* {{{ */
546 {
547   cdbi_database_t *db;
548   int status;
549   int i;
550
551   if ((ci->values_num != 1)
552       || (ci->values[0].type != OCONFIG_TYPE_STRING))
553   {
554     WARNING ("dbi plugin: The `Database' block "
555         "needs exactly one string argument.");
556     return (-1);
557   }
558
559   db = (cdbi_database_t *) malloc (sizeof (*db));
560   if (db == NULL)
561   {
562     ERROR ("dbi plugin: malloc failed.");
563     return (-1);
564   }
565   memset (db, 0, sizeof (*db));
566
567   status = cdbi_config_set_string (&db->name, ci);
568   if (status != 0)
569   {
570     sfree (db);
571     return (status);
572   }
573
574   /* Fill the `cdbi_database_t' structure.. */
575   for (i = 0; i < ci->children_num; i++)
576   {
577     oconfig_item_t *child = ci->children + i;
578
579     if (strcasecmp ("Driver", child->key) == 0)
580       status = cdbi_config_set_string (&db->driver, child);
581     else if (strcasecmp ("DriverOption", child->key) == 0)
582       status = cdbi_config_add_database_driver_option (db, child);
583     else if (strcasecmp ("SelectDB", child->key) == 0)
584       status = cdbi_config_set_string (&db->select_db, child);
585     else if (strcasecmp ("Query", child->key) == 0)
586       status = cdbi_config_add_database_query (db, child);
587     else
588     {
589       WARNING ("dbi plugin: Option `%s' not allowed here.", child->key);
590       status = -1;
591     }
592
593     if (status != 0)
594       break;
595   }
596
597   /* Check that all necessary options have been given. */
598   while (status == 0)
599   {
600     if (db->driver == NULL)
601     {
602       WARNING ("dbi plugin: `Driver' not given for database `%s'", db->name);
603       status = -1;
604     }
605     if (db->driver_options_num == 0)
606     {
607       WARNING ("dbi plugin: No `DriverOption' given for database `%s'. "
608           "This will likely not work.", db->name);
609     }
610
611     break;
612   } /* while (status == 0) */
613
614   /* If all went well, add this database to the global list of databases. */
615   if (status == 0)
616   {
617     cdbi_database_t **temp;
618
619     temp = (cdbi_database_t **) realloc (databases,
620         sizeof (*databases) * (databases_num + 1));
621     if (temp == NULL)
622     {
623       ERROR ("dbi plugin: realloc failed");
624       status = -1;
625     }
626     else
627     {
628       databases = temp;
629       databases[databases_num] = db;
630       databases_num++;
631     }
632   }
633
634   if (status != 0)
635   {
636     cdbi_database_free (db);
637     return (-1);
638   }
639
640   return (0);
641 } /* }}} int cdbi_config_add_database */
642
643 static int cdbi_config (oconfig_item_t *ci) /* {{{ */
644 {
645   int i;
646
647   for (i = 0; i < ci->children_num; i++)
648   {
649     oconfig_item_t *child = ci->children + i;
650     if (strcasecmp ("Query", child->key) == 0)
651       cdbi_config_add_query (child);
652     else if (strcasecmp ("Database", child->key) == 0)
653       cdbi_config_add_database (child);
654     else
655     {
656       WARNING ("snmp plugin: Ignoring unknown config option `%s'.", child->key);
657     }
658   } /* for (ci->children) */
659
660   return (0);
661 } /* }}} int cdbi_config */
662
663 /* }}} End of configuration handling functions */
664
665 static int cdbi_init (void) /* {{{ */
666 {
667   static int did_init = 0;
668   int status;
669
670   if (did_init != 0)
671     return (0);
672
673   if (queries_num == 0)
674   {
675     ERROR ("dbi plugin: No <Query> blocks have been found. Without them, "
676         "this plugin can't do anything useful, so we will returns an error.");
677     return (-1);
678   }
679
680   if (databases_num == 0)
681   {
682     ERROR ("dbi plugin: No <Database> blocks have been found. Without them, "
683         "this plugin can't do anything useful, so we will returns an error.");
684     return (-1);
685   }
686
687   status = dbi_initialize (NULL);
688   if (status < 0)
689   {
690     ERROR ("dbi plugin: cdbi_init: dbi_initialize failed with status %i.",
691         status);
692     return (-1);
693   }
694   else if (status == 0)
695   {
696     ERROR ("dbi plugin: `dbi_initialize' could not load any drivers. Please "
697         "install at least one `DBD' or check your installation.");
698     return (-1);
699   }
700   DEBUG ("dbi plugin: cdbi_init: dbi_initialize reports %i driver%s.",
701       status, (status == 1) ? "" : "s");
702
703   return (0);
704 } /* }}} int cdbi_init */
705
706 static int cdbi_read_database_query (cdbi_database_t *db, /* {{{ */
707     cdbi_query_t *q)
708 {
709   dbi_result res;
710   char **instances;
711   value_t *values;
712   const data_set_t *ds;
713   size_t i;
714   int status;
715
716   res = NULL;
717   instances = NULL;
718   values = NULL;
719
720   /* Macro that cleans up dynamically allocated memory and returns the
721    * specified status. */
722 #define BAIL_OUT(status) \
723   if (res != NULL) { dbi_result_free (res); res = NULL; } \
724   if (instances != NULL) { sfree (instances[0]); sfree (instances); } \
725   sfree (values); \
726   return (status)
727
728   ds = plugin_get_ds (q->type);
729   if (ds == NULL)
730   {
731     ERROR ("dbi plugin: cdbi_read_database_query: Query `%s': Type `%s' is not "
732         "known by the daemon. See types.db(5) for details.",
733         q->name, q->type);
734     BAIL_OUT (-1);
735   }
736
737   if (((size_t) ds->ds_num) != q->values_num)
738   {
739     ERROR ("dbi plugin: cdbi_read_database_query: Query `%s': The type `%s' "
740         "requires exactly %i value%s, but the configuration specifies %zu.",
741         q->name, q->type,
742         ds->ds_num, (ds->ds_num == 1) ? "" : "s",
743         q->values_num);
744     BAIL_OUT (-1);
745   }
746
747   /* Allocate `instances' and `values' {{{ */
748   instances = (char **) malloc (sizeof (*instances) * q->instances_num);
749   if (instances == NULL)
750   {
751     ERROR ("dbi plugin: malloc failed.");
752     BAIL_OUT (-1);
753   }
754
755   instances[0] = (char *) malloc (q->instances_num * DATA_MAX_NAME_LEN);
756   if (instances[0] == NULL)
757   {
758     ERROR ("dbi plugin: malloc failed.");
759     BAIL_OUT (-1);
760   }
761   for (i = 1; i < q->instances_num; i++)
762     instances[i] = instances[i - 1] + DATA_MAX_NAME_LEN;
763
764   values = (value_t *) malloc (sizeof (*values) * q->values_num);
765   if (values == NULL)
766   {
767     ERROR ("dbi plugin: malloc failed.");
768     BAIL_OUT (-1);
769   }
770   /* }}} */
771
772   res = dbi_conn_query (db->connection, q->statement);
773   if (res == NULL)
774   {
775     char errbuf[1024];
776     ERROR ("dbi plugin: cdbi_read_database_query (%s, %s): "
777         "dbi_conn_query failed: %s",
778         db->name, q->name,
779         cdbi_strerror (db->connection, errbuf, sizeof (errbuf)));
780     BAIL_OUT (-1);
781   }
782
783   status = dbi_result_first_row (res);
784   if (status != 1)
785   {
786     char errbuf[1024];
787     ERROR ("dbi plugin: cdbi_read_database_query (%s, %s): "
788         "dbi_result_first_row failed: %s. Maybe the statement didn't "
789         "return any rows?",
790         db->name, q->name, 
791         cdbi_strerror (db->connection, errbuf, sizeof (errbuf)));
792     BAIL_OUT (-1);
793   }
794
795   while (42)
796   {
797     /* Get instance names and values from the result: */
798     for (i = 0; i < q->instances_num; i++) /* {{{ */
799     {
800       const char *inst;
801
802       inst = dbi_result_get_string (res, q->instances[i]);
803       if (dbi_conn_error (db->connection, NULL) != 0)
804       {
805         char errbuf[1024];
806         ERROR ("dbi plugin: cdbi_read_database_query (%s, %s): "
807             "dbi_result_get_string (%s) failed: %s",
808             db->name, q->name, q->instances[i],
809             cdbi_strerror (db->connection, errbuf, sizeof (errbuf)));
810         BAIL_OUT (-1);
811       }
812
813       sstrncpy (instances[i], (inst == NULL) ? "" : inst, DATA_MAX_NAME_LEN);
814       DEBUG ("dbi plugin: cdbi_read_database_query (%s, %s): "
815           "instances[%zu] = %s;",
816           db->name, q->name, i, instances[i]);
817     } /* }}} for (i = 0; i < q->instances_num; i++) */
818
819     for (i = 0; i < q->values_num; i++) /* {{{ */
820     {
821       status = cdbi_result_get_field (res, q->values[i], ds->ds[i].type,
822           values + i);
823       if (status != 0)
824       {
825         BAIL_OUT (-1);
826       }
827
828       if (ds->ds[i].type == DS_TYPE_COUNTER)
829       {
830         DEBUG ("dbi plugin: cdbi_read_database_query (%s, %s): values[%zu] = %llu;",
831             db->name, q->name, i, values[i].counter);
832       }
833       else
834       {
835         DEBUG ("dbi plugin: cdbi_read_database_query (%s, %s): values[%zu] = %g;",
836             db->name, q->name, i, values[i].gauge);
837       }
838     } /* }}} for (i = 0; i < q->values_num; i++) */
839
840     /* Dispatch this row to the daemon. */
841     cdbi_submit (db, q, instances, values);
842
843     /* Get the next row from the database. */
844     status = dbi_result_next_row (res);
845     if (status != 1)
846     {
847       if (dbi_conn_error (db->connection, NULL) != 0)
848       {
849         char errbuf[1024];
850         WARNING ("dbi plugin: cdbi_read_database_query (%s, %s): "
851             "dbi_result_next_row failed: %s.",
852             db->name, q->name,
853             cdbi_strerror (db->connection, errbuf, sizeof (errbuf)));
854       }
855       break;
856     }
857   } /* while (42) */
858
859   BAIL_OUT (0);
860 #undef BAIL_OUT
861 } /* }}} int cdbi_read_database_query */
862
863 static int cdbi_connect_database (cdbi_database_t *db) /* {{{ */
864 {
865   dbi_driver driver;
866   dbi_conn connection;
867   size_t i;
868   int status;
869
870   if (db->connection != NULL)
871   {
872     status = dbi_conn_ping (db->connection);
873     if (status != 0) /* connection is alive */
874       return (0);
875
876     dbi_conn_close (db->connection);
877     db->connection = NULL;
878   }
879
880   driver = dbi_driver_open (db->driver);
881   if (driver == NULL)
882   {
883     ERROR ("dbi plugin: cdbi_connect_database: dbi_driver_open (%s) failed.",
884         db->driver);
885     INFO ("dbi plugin: Maybe the driver isn't installed? "
886         "Known drivers are:");
887     for (driver = dbi_driver_list (NULL);
888         driver != NULL;
889         driver = dbi_driver_list (driver))
890     {
891       INFO ("dbi plugin: * %s", dbi_driver_get_name (driver));
892     }
893     return (-1);
894   }
895
896   connection = dbi_conn_open (driver);
897   if (connection == NULL)
898   {
899     ERROR ("dbi plugin: cdbi_connect_database: dbi_conn_open (%s) failed.",
900         db->driver);
901     return (-1);
902   }
903
904   /* Set all the driver options. Because this is a very very very generic
905    * interface, the error handling is kind of long. If an invalid option is
906    * encountered, it will get a list of options understood by the driver and
907    * report that as `INFO'. This way, users hopefully don't have too much
908    * trouble finding out how to configure the plugin correctly.. */
909   for (i = 0; i < db->driver_options_num; i++)
910   {
911     DEBUG ("dbi plugin: cdbi_connect_database (%s): "
912         "key = %s; value = %s;",
913         db->name,
914         db->driver_options[i].key,
915         db->driver_options[i].value);
916
917     status = dbi_conn_set_option (connection,
918         db->driver_options[i].key, db->driver_options[i].value);
919     if (status != 0)
920     {
921       char errbuf[1024];
922       const char *opt;
923
924       ERROR ("dbi plugin: cdbi_connect_database (%s): "
925           "dbi_conn_set_option (%s, %s) failed: %s.",
926           db->name,
927           db->driver_options[i].key, db->driver_options[i].value,
928           cdbi_strerror (connection, errbuf, sizeof (errbuf)));
929
930       INFO ("dbi plugin: This is a list of all options understood "
931           "by the `%s' driver:", db->driver);
932       for (opt = dbi_conn_get_option_list (connection, NULL);
933           opt != NULL;
934           opt = dbi_conn_get_option_list (connection, opt))
935       {
936         INFO ("dbi plugin: * %s", opt);
937       }
938
939       dbi_conn_close (connection);
940       return (-1);
941     }
942   } /* for (i = 0; i < db->driver_options_num; i++) */
943
944   status = dbi_conn_connect (connection);
945   if (status != 0)
946   {
947     char errbuf[1024];
948     ERROR ("dbi plugin: cdbi_connect_database (%s): "
949         "dbi_conn_connect failed: %s",
950         db->name, cdbi_strerror (connection, errbuf, sizeof (errbuf)));
951     dbi_conn_close (connection);
952     return (-1);
953   }
954
955   if (db->select_db != NULL)
956   {
957     status = dbi_conn_select_db (connection, db->select_db);
958     if (status != 0)
959     {
960       char errbuf[1024];
961       WARNING ("dbi plugin: cdbi_connect_database (%s): "
962           "dbi_conn_select_db (%s) failed: %s. Check the `SelectDB' option.",
963           db->name, db->select_db,
964           cdbi_strerror (connection, errbuf, sizeof (errbuf)));
965       dbi_conn_close (connection);
966       return (-1);
967     }
968   }
969
970   db->connection = connection;
971   return (0);
972 } /* }}} int cdbi_connect_database */
973
974 static int cdbi_read_database (cdbi_database_t *db) /* {{{ */
975 {
976   size_t i;
977   int success;
978   int status;
979
980   status = cdbi_connect_database (db);
981   if (status != 0)
982     return (status);
983   assert (db->connection != NULL);
984
985   success = 0;
986   for (i = 0; i < db->queries_num; i++)
987   {
988     status = cdbi_read_database_query (db, db->queries[i]);
989     if (status == 0)
990       success++;
991   }
992
993   if (success == 0)
994   {
995     ERROR ("dbi plugin: All queries failed for database `%s'.", db->name);
996     return (-1);
997   }
998
999   return (0);
1000 } /* }}} int cdbi_read_database */
1001
1002 static int cdbi_read (void) /* {{{ */
1003 {
1004   size_t i;
1005   int success = 0;
1006   int status;
1007
1008   for (i = 0; i < databases_num; i++)
1009   {
1010     status = cdbi_read_database (databases[i]);
1011     if (status == 0)
1012       success++;
1013   }
1014
1015   if (success == 0)
1016   {
1017     ERROR ("dbi plugin: No database could be read. Will return an error so "
1018         "the plugin will be delayed.");
1019     return (-1);
1020   }
1021
1022   return (0);
1023 } /* }}} int cdbi_read */
1024
1025 static int cdbi_shutdown (void) /* {{{ */
1026 {
1027   size_t i;
1028
1029   for (i = 0; i < databases_num; i++)
1030   {
1031     if (databases[i]->connection != NULL)
1032     {
1033       dbi_conn_close (databases[i]->connection);
1034       databases[i]->connection = NULL;
1035     }
1036     cdbi_database_free (databases[i]);
1037   }
1038   sfree (databases);
1039   databases_num = 0;
1040
1041   for (i = 0; i < queries_num; i++)
1042     cdbi_query_free (queries[i]);
1043   sfree (queries);
1044   queries_num = 0;
1045
1046   return (0);
1047 } /* }}} int cdbi_shutdown */
1048
1049 void module_register (void) /* {{{ */
1050 {
1051   plugin_register_complex_config ("dbi", cdbi_config);
1052   plugin_register_init ("dbi", cdbi_init);
1053   plugin_register_read ("dbi", cdbi_read);
1054   plugin_register_shutdown ("dbi", cdbi_shutdown);
1055 } /* }}} void module_register */
1056
1057 /*
1058  * vim: shiftwidth=2 softtabstop=2 et fdm=marker
1059  */