Merge branch 'collectd-5.7' into collectd-5.8
[collectd.git] / src / dbi.c
1 /**
2  * collectd - src/dbi.c
3  * Copyright (C) 2008-2015  Florian octo Forster
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  *
23  * Authors:
24  *   Florian octo Forster <octo at collectd.org>
25  **/
26
27 #include "collectd.h"
28
29 #include "common.h"
30 #include "plugin.h"
31 #include "utils_db_query.h"
32
33 #include <dbi/dbi.h>
34
35 /* libdbi 0.9.0 introduced a new thread-safe interface and marked the old
36  * functions "deprecated". These macros convert the new functions to their old
37  * counterparts for backwards compatibility. */
38 #if !defined(LIBDBI_VERSION) || (LIBDBI_VERSION < 900)
39 #define HAVE_LEGACY_LIBDBI 1
40 #define dbi_initialize_r(a, inst) dbi_initialize(a)
41 #define dbi_shutdown_r(inst) dbi_shutdown()
42 #define dbi_set_verbosity_r(a, inst) dbi_set_verbosity(a)
43 #define dbi_driver_list_r(a, inst) dbi_driver_list(a)
44 #define dbi_driver_open_r(a, inst) dbi_driver_open(a)
45 #endif
46
47 /*
48  * Data types
49  */
50 struct cdbi_driver_option_s /* {{{ */
51 {
52   char *key;
53   union {
54     char *string;
55     int numeric;
56   } value;
57   _Bool is_numeric;
58 };
59 typedef struct cdbi_driver_option_s cdbi_driver_option_t; /* }}} */
60
61 struct cdbi_database_s /* {{{ */
62 {
63   char *name;
64   char *select_db;
65   char *plugin_name;
66
67   cdtime_t interval;
68
69   char *driver;
70   char *host;
71   cdbi_driver_option_t *driver_options;
72   size_t driver_options_num;
73
74   udb_query_preparation_area_t **q_prep_areas;
75   udb_query_t **queries;
76   size_t queries_num;
77
78   dbi_conn connection;
79 };
80 typedef struct cdbi_database_s cdbi_database_t; /* }}} */
81
82 /*
83  * Global variables
84  */
85 #if !defined(HAVE_LEGACY_LIBDBI) || !HAVE_LEGACY_LIBDBI
86 static dbi_inst dbi_instance = 0;
87 #endif
88 static udb_query_t **queries = NULL;
89 static size_t queries_num = 0;
90 static cdbi_database_t **databases = NULL;
91 static size_t databases_num = 0;
92
93 static int cdbi_read_database(user_data_t *ud);
94
95 /*
96  * Functions
97  */
98 static const char *cdbi_strerror(dbi_conn conn, /* {{{ */
99                                  char *buffer, size_t buffer_size) {
100   const char *msg;
101   int status;
102
103   if (conn == NULL) {
104     sstrncpy(buffer, "connection is NULL", buffer_size);
105     return buffer;
106   }
107
108   msg = NULL;
109   status = dbi_conn_error(conn, &msg);
110   if ((status >= 0) && (msg != NULL))
111     snprintf(buffer, buffer_size, "%s (status %i)", msg, status);
112   else
113     snprintf(buffer, buffer_size, "dbi_conn_error failed with status %i",
114              status);
115
116   return buffer;
117 } /* }}} const char *cdbi_conn_error */
118
119 static int cdbi_result_get_field(dbi_result res, /* {{{ */
120                                  unsigned int index, char *buffer,
121                                  size_t buffer_size) {
122   unsigned short src_type;
123
124   src_type = dbi_result_get_field_type_idx(res, index);
125   if (src_type == DBI_TYPE_ERROR) {
126     ERROR("dbi plugin: cdbi_result_get: "
127           "dbi_result_get_field_type_idx failed.");
128     return -1;
129   }
130
131   if (src_type == DBI_TYPE_INTEGER) {
132     long long value;
133
134     value = dbi_result_get_longlong_idx(res, index);
135     snprintf(buffer, buffer_size, "%lli", value);
136   } else if (src_type == DBI_TYPE_DECIMAL) {
137     double value;
138
139     value = dbi_result_get_double_idx(res, index);
140     snprintf(buffer, buffer_size, "%63.15g", value);
141   } else if (src_type == DBI_TYPE_STRING) {
142     const char *value;
143
144     value = dbi_result_get_string_idx(res, index);
145     if (value == NULL)
146       sstrncpy(buffer, "", buffer_size);
147     else if (strcmp("ERROR", value) == 0)
148       return -1;
149     else
150       sstrncpy(buffer, value, buffer_size);
151   }
152   /* DBI_TYPE_BINARY */
153   /* DBI_TYPE_DATETIME */
154   else {
155     const char *field_name;
156
157     field_name = dbi_result_get_field_name(res, index);
158     if (field_name == NULL)
159       field_name = "<unknown>";
160
161     ERROR("dbi plugin: Column `%s': Don't know how to handle "
162           "source type %hu.",
163           field_name, src_type);
164     return -1;
165   }
166
167   return 0;
168 } /* }}} int cdbi_result_get_field */
169
170 static void cdbi_database_free(cdbi_database_t *db) /* {{{ */
171 {
172   if (db == NULL)
173     return;
174
175   sfree(db->name);
176   sfree(db->select_db);
177   sfree(db->plugin_name);
178   sfree(db->driver);
179   sfree(db->host);
180
181   for (size_t i = 0; i < db->driver_options_num; i++) {
182     sfree(db->driver_options[i].key);
183     if (!db->driver_options[i].is_numeric)
184       sfree(db->driver_options[i].value.string);
185   }
186   sfree(db->driver_options);
187
188   if (db->q_prep_areas)
189     for (size_t i = 0; i < db->queries_num; ++i)
190       udb_query_delete_preparation_area(db->q_prep_areas[i]);
191   sfree(db->q_prep_areas);
192   /* N.B.: db->queries references objects "owned" by the global queries
193    * variable. Free the array here, but not the content. */
194   sfree(db->queries);
195
196   sfree(db);
197 } /* }}} void cdbi_database_free */
198
199 /* Configuration handling functions {{{
200  *
201  * <Plugin dbi>
202  *   <Query "plugin_instance0">
203  *     Statement "SELECT name, value FROM table"
204  *     <Result>
205  *       Type "gauge"
206  *       InstancesFrom "name"
207  *       ValuesFrom "value"
208  *     </Result>
209  *     ...
210  *   </Query>
211  *
212  *   <Database "plugin_instance1">
213  *     Driver "mysql"
214  *     Interval 120
215  *     DriverOption "hostname" "localhost"
216  *     ...
217  *     Query "plugin_instance0"
218  *   </Database>
219  * </Plugin>
220  */
221
222 static int cdbi_config_add_database_driver_option(cdbi_database_t *db, /* {{{ */
223                                                   oconfig_item_t *ci) {
224   cdbi_driver_option_t *option;
225
226   if ((ci->values_num != 2) || (ci->values[0].type != OCONFIG_TYPE_STRING) ||
227       ((ci->values[1].type != OCONFIG_TYPE_STRING) &&
228        (ci->values[1].type != OCONFIG_TYPE_NUMBER))) {
229     WARNING("dbi plugin: The `DriverOption' config option "
230             "needs exactly two arguments.");
231     return -1;
232   }
233
234   option = realloc(db->driver_options,
235                    sizeof(*option) * (db->driver_options_num + 1));
236   if (option == NULL) {
237     ERROR("dbi plugin: realloc failed");
238     return -1;
239   }
240
241   db->driver_options = option;
242   option = db->driver_options + db->driver_options_num;
243   memset(option, 0, sizeof(*option));
244
245   option->key = strdup(ci->values[0].value.string);
246   if (option->key == NULL) {
247     ERROR("dbi plugin: strdup failed.");
248     return -1;
249   }
250
251   if (ci->values[1].type == OCONFIG_TYPE_STRING) {
252     option->value.string = strdup(ci->values[1].value.string);
253     if (option->value.string == NULL) {
254       ERROR("dbi plugin: strdup failed.");
255       sfree(option->key);
256       return -1;
257     }
258   } else {
259     assert(ci->values[1].type == OCONFIG_TYPE_NUMBER);
260     option->value.numeric = (int)(ci->values[1].value.number + .5);
261     option->is_numeric = 1;
262   }
263
264   db->driver_options_num++;
265   return 0;
266 } /* }}} int cdbi_config_add_database_driver_option */
267
268 static int cdbi_config_add_database(oconfig_item_t *ci) /* {{{ */
269 {
270   cdbi_database_t *db;
271   int status;
272
273   if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
274     WARNING("dbi plugin: The `Database' block "
275             "needs exactly one string argument.");
276     return -1;
277   }
278
279   db = calloc(1, sizeof(*db));
280   if (db == NULL) {
281     ERROR("dbi plugin: calloc failed.");
282     return -1;
283   }
284
285   status = cf_util_get_string(ci, &db->name);
286   if (status != 0) {
287     sfree(db);
288     return status;
289   }
290
291   /* Fill the `cdbi_database_t' structure.. */
292   for (int i = 0; i < ci->children_num; i++) {
293     oconfig_item_t *child = ci->children + i;
294
295     if (strcasecmp("Driver", child->key) == 0)
296       status = cf_util_get_string(child, &db->driver);
297     else if (strcasecmp("DriverOption", child->key) == 0)
298       status = cdbi_config_add_database_driver_option(db, child);
299     else if (strcasecmp("SelectDB", child->key) == 0)
300       status = cf_util_get_string(child, &db->select_db);
301     else if (strcasecmp("Query", child->key) == 0)
302       status = udb_query_pick_from_list(child, queries, queries_num,
303                                         &db->queries, &db->queries_num);
304     else if (strcasecmp("Host", child->key) == 0)
305       status = cf_util_get_string(child, &db->host);
306     else if (strcasecmp("Interval", child->key) == 0)
307       status = cf_util_get_cdtime(child, &db->interval);
308     else if (strcasecmp("Plugin", child->key) == 0)
309       status = cf_util_get_string(child, &db->plugin_name);
310     else {
311       WARNING("dbi plugin: Option `%s' not allowed here.", child->key);
312       status = -1;
313     }
314
315     if (status != 0)
316       break;
317   }
318
319   /* Check that all necessary options have been given. */
320   while (status == 0) {
321     if (db->driver == NULL) {
322       WARNING("dbi plugin: `Driver' not given for database `%s'", db->name);
323       status = -1;
324     }
325     if (db->driver_options_num == 0) {
326       WARNING("dbi plugin: No `DriverOption' given for database `%s'. "
327               "This will likely not work.",
328               db->name);
329     }
330
331     break;
332   } /* while (status == 0) */
333
334   while ((status == 0) && (db->queries_num > 0)) {
335     db->q_prep_areas = calloc(db->queries_num, sizeof(*db->q_prep_areas));
336     if (db->q_prep_areas == NULL) {
337       WARNING("dbi plugin: calloc failed");
338       status = -1;
339       break;
340     }
341
342     for (size_t i = 0; i < db->queries_num; ++i) {
343       db->q_prep_areas[i] = udb_query_allocate_preparation_area(db->queries[i]);
344
345       if (db->q_prep_areas[i] == NULL) {
346         WARNING("dbi plugin: udb_query_allocate_preparation_area failed");
347         status = -1;
348         break;
349       }
350     }
351
352     break;
353   }
354
355   /* If all went well, add this database to the global list of databases. */
356   if (status == 0) {
357     cdbi_database_t **temp;
358
359     temp = realloc(databases, sizeof(*databases) * (databases_num + 1));
360     if (temp == NULL) {
361       ERROR("dbi plugin: realloc failed");
362       status = -1;
363     } else {
364       databases = temp;
365       databases[databases_num] = db;
366       databases_num++;
367
368       char *name = ssnprintf_alloc("dbi:%s", db->name);
369       plugin_register_complex_read(
370           /* group = */ NULL,
371           /* name = */ name ? name : db->name,
372           /* callback = */ cdbi_read_database,
373           /* interval = */ (db->interval > 0) ? db->interval : 0,
374           &(user_data_t){
375               .data = db,
376           });
377       sfree(name);
378     }
379   }
380
381   if (status != 0) {
382     cdbi_database_free(db);
383     return -1;
384   }
385
386   return 0;
387 } /* }}} int cdbi_config_add_database */
388
389 static int cdbi_config(oconfig_item_t *ci) /* {{{ */
390 {
391   for (int i = 0; i < ci->children_num; i++) {
392     oconfig_item_t *child = ci->children + i;
393     if (strcasecmp("Query", child->key) == 0)
394       udb_query_create(&queries, &queries_num, child,
395                        /* callback = */ NULL);
396     else if (strcasecmp("Database", child->key) == 0)
397       cdbi_config_add_database(child);
398     else {
399       WARNING("dbi plugin: Ignoring unknown config option `%s'.", child->key);
400     }
401   } /* for (ci->children) */
402
403   return 0;
404 } /* }}} int cdbi_config */
405
406 /* }}} End of configuration handling functions */
407
408 static int cdbi_init(void) /* {{{ */
409 {
410   static int did_init = 0;
411   int status;
412
413   if (did_init != 0)
414     return 0;
415
416   if (queries_num == 0) {
417     ERROR("dbi plugin: No <Query> blocks have been found. Without them, "
418           "this plugin can't do anything useful, so we will return an error.");
419     return -1;
420   }
421
422   if (databases_num == 0) {
423     ERROR("dbi plugin: No <Database> blocks have been found. Without them, "
424           "this plugin can't do anything useful, so we will return an error.");
425     return -1;
426   }
427
428   status = dbi_initialize_r(/* driverdir = */ NULL, &dbi_instance);
429   if (status < 0) {
430     ERROR("dbi plugin: cdbi_init: dbi_initialize_r failed with status %i.",
431           status);
432     return -1;
433   } else if (status == 0) {
434     ERROR("dbi plugin: `dbi_initialize_r' could not load any drivers. Please "
435           "install at least one `DBD' or check your installation.");
436     return -1;
437   }
438   DEBUG("dbi plugin: cdbi_init: dbi_initialize_r reports %i driver%s.", status,
439         (status == 1) ? "" : "s");
440
441   return 0;
442 } /* }}} int cdbi_init */
443
444 static int cdbi_read_database_query(cdbi_database_t *db, /* {{{ */
445                                     udb_query_t *q,
446                                     udb_query_preparation_area_t *prep_area) {
447   const char *statement;
448   dbi_result res;
449   size_t column_num;
450   char **column_names;
451   char **column_values;
452   int status;
453
454 /* Macro that cleans up dynamically allocated memory and returns the
455  * specified status. */
456 #define BAIL_OUT(status)                                                       \
457   if (column_names != NULL) {                                                  \
458     sfree(column_names[0]);                                                    \
459     sfree(column_names);                                                       \
460   }                                                                            \
461   if (column_values != NULL) {                                                 \
462     sfree(column_values[0]);                                                   \
463     sfree(column_values);                                                      \
464   }                                                                            \
465   if (res != NULL) {                                                           \
466     dbi_result_free(res);                                                      \
467     res = NULL;                                                                \
468   }                                                                            \
469   return (status)
470
471   column_names = NULL;
472   column_values = NULL;
473
474   statement = udb_query_get_statement(q);
475   assert(statement != NULL);
476
477   res = dbi_conn_query(db->connection, statement);
478   if (res == NULL) {
479     char errbuf[1024];
480     ERROR("dbi plugin: cdbi_read_database_query (%s, %s): "
481           "dbi_conn_query failed: %s",
482           db->name, udb_query_get_name(q),
483           cdbi_strerror(db->connection, errbuf, sizeof(errbuf)));
484     BAIL_OUT(-1);
485   } else /* Get the number of columns */
486   {
487     unsigned int db_status;
488
489     db_status = dbi_result_get_numfields(res);
490     if (db_status == DBI_FIELD_ERROR) {
491       char errbuf[1024];
492       ERROR("dbi plugin: cdbi_read_database_query (%s, %s): "
493             "dbi_result_get_numfields failed: %s",
494             db->name, udb_query_get_name(q),
495             cdbi_strerror(db->connection, errbuf, sizeof(errbuf)));
496       BAIL_OUT(-1);
497     }
498
499     column_num = (size_t)db_status;
500     DEBUG("cdbi_read_database_query (%s, %s): There are %zu columns.", db->name,
501           udb_query_get_name(q), column_num);
502   }
503
504   /* Allocate `column_names' and `column_values'. {{{ */
505   column_names = calloc(column_num, sizeof(*column_names));
506   if (column_names == NULL) {
507     ERROR("dbi plugin: calloc failed.");
508     BAIL_OUT(-1);
509   }
510
511   column_names[0] = calloc(column_num, DATA_MAX_NAME_LEN);
512   if (column_names[0] == NULL) {
513     ERROR("dbi plugin: calloc failed.");
514     BAIL_OUT(-1);
515   }
516   for (size_t i = 1; i < column_num; i++)
517     column_names[i] = column_names[i - 1] + DATA_MAX_NAME_LEN;
518
519   column_values = calloc(column_num, sizeof(*column_values));
520   if (column_values == NULL) {
521     ERROR("dbi plugin: calloc failed.");
522     BAIL_OUT(-1);
523   }
524
525   column_values[0] = calloc(column_num, DATA_MAX_NAME_LEN);
526   if (column_values[0] == NULL) {
527     ERROR("dbi plugin: calloc failed.");
528     BAIL_OUT(-1);
529   }
530   for (size_t i = 1; i < column_num; i++)
531     column_values[i] = column_values[i - 1] + DATA_MAX_NAME_LEN;
532   /* }}} */
533
534   /* Copy the field names to `column_names' */
535   for (size_t i = 0; i < column_num; i++) /* {{{ */
536   {
537     const char *column_name;
538
539     column_name = dbi_result_get_field_name(res, (unsigned int)(i + 1));
540     if (column_name == NULL) {
541       ERROR("dbi plugin: cdbi_read_database_query (%s, %s): "
542             "Cannot retrieve name of field %zu.",
543             db->name, udb_query_get_name(q), i + 1);
544       BAIL_OUT(-1);
545     }
546
547     sstrncpy(column_names[i], column_name, DATA_MAX_NAME_LEN);
548   } /* }}} for (i = 0; i < column_num; i++) */
549
550   udb_query_prepare_result(
551       q, prep_area, (db->host ? db->host : hostname_g),
552       /* plugin = */ (db->plugin_name != NULL) ? db->plugin_name : "dbi",
553       db->name, column_names, column_num,
554       /* interval = */ (db->interval > 0) ? db->interval : 0);
555
556   /* 0 = error; 1 = success; */
557   status = dbi_result_first_row(res); /* {{{ */
558   if (status != 1) {
559     char errbuf[1024];
560     ERROR("dbi plugin: cdbi_read_database_query (%s, %s): "
561           "dbi_result_first_row failed: %s. Maybe the statement didn't "
562           "return any rows?",
563           db->name, udb_query_get_name(q),
564           cdbi_strerror(db->connection, errbuf, sizeof(errbuf)));
565     udb_query_finish_result(q, prep_area);
566     BAIL_OUT(-1);
567   } /* }}} */
568
569   /* Iterate over all rows and call `udb_query_handle_result' with each list of
570    * values. */
571   while (42) /* {{{ */
572   {
573     status = 0;
574     /* Copy the value of the columns to `column_values' */
575     for (size_t i = 0; i < column_num; i++) /* {{{ */
576     {
577       status = cdbi_result_get_field(res, (unsigned int)(i + 1),
578                                      column_values[i], DATA_MAX_NAME_LEN);
579
580       if (status != 0) {
581         ERROR("dbi plugin: cdbi_read_database_query (%s, %s): "
582               "cdbi_result_get_field (%zu) failed.",
583               db->name, udb_query_get_name(q), i + 1);
584         status = -1;
585         break;
586       }
587     } /* }}} for (i = 0; i < column_num; i++) */
588
589     /* If all values were copied successfully, call `udb_query_handle_result'
590      * to dispatch the row to the daemon. */
591     if (status == 0) /* {{{ */
592     {
593       status = udb_query_handle_result(q, prep_area, column_values);
594       if (status != 0) {
595         ERROR("dbi plugin: cdbi_read_database_query (%s, %s): "
596               "udb_query_handle_result failed.",
597               db->name, udb_query_get_name(q));
598       }
599     } /* }}} */
600
601     /* Get the next row from the database. */
602     status = dbi_result_next_row(res); /* {{{ */
603     if (status != 1) {
604       if (dbi_conn_error(db->connection, NULL) != 0) {
605         char errbuf[1024];
606         WARNING("dbi plugin: cdbi_read_database_query (%s, %s): "
607                 "dbi_result_next_row failed: %s.",
608                 db->name, udb_query_get_name(q),
609                 cdbi_strerror(db->connection, errbuf, sizeof(errbuf)));
610       }
611       break;
612     } /* }}} */
613   }   /* }}} while (42) */
614
615   /* Tell the db query interface that we're done with this query. */
616   udb_query_finish_result(q, prep_area);
617
618   /* Clean up and return `status = 0' (success) */
619   BAIL_OUT(0);
620 #undef BAIL_OUT
621 } /* }}} int cdbi_read_database_query */
622
623 static int cdbi_connect_database(cdbi_database_t *db) /* {{{ */
624 {
625   dbi_driver driver;
626   dbi_conn connection;
627   int status;
628
629   if (db->connection != NULL) {
630     status = dbi_conn_ping(db->connection);
631     if (status != 0) /* connection is alive */
632       return 0;
633
634     dbi_conn_close(db->connection);
635     db->connection = NULL;
636   }
637
638   driver = dbi_driver_open_r(db->driver, dbi_instance);
639   if (driver == NULL) {
640     ERROR("dbi plugin: cdbi_connect_database: dbi_driver_open_r (%s) failed.",
641           db->driver);
642     INFO("dbi plugin: Maybe the driver isn't installed? "
643          "Known drivers are:");
644     for (driver = dbi_driver_list_r(NULL, dbi_instance); driver != NULL;
645          driver = dbi_driver_list_r(driver, dbi_instance)) {
646       INFO("dbi plugin: * %s", dbi_driver_get_name(driver));
647     }
648     return -1;
649   }
650
651   connection = dbi_conn_open(driver);
652   if (connection == NULL) {
653     ERROR("dbi plugin: cdbi_connect_database: dbi_conn_open (%s) failed.",
654           db->driver);
655     return -1;
656   }
657
658   /* Set all the driver options. Because this is a very very very generic
659    * interface, the error handling is kind of long. If an invalid option is
660    * encountered, it will get a list of options understood by the driver and
661    * report that as `INFO'. This way, users hopefully don't have too much
662    * trouble finding out how to configure the plugin correctly.. */
663   for (size_t i = 0; i < db->driver_options_num; i++) {
664     if (db->driver_options[i].is_numeric) {
665       status =
666           dbi_conn_set_option_numeric(connection, db->driver_options[i].key,
667                                       db->driver_options[i].value.numeric);
668       if (status != 0) {
669         char errbuf[1024];
670         ERROR("dbi plugin: cdbi_connect_database (%s): "
671               "dbi_conn_set_option_numeric (\"%s\", %i) failed: %s.",
672               db->name, db->driver_options[i].key,
673               db->driver_options[i].value.numeric,
674               cdbi_strerror(connection, errbuf, sizeof(errbuf)));
675       }
676     } else {
677       status = dbi_conn_set_option(connection, db->driver_options[i].key,
678                                    db->driver_options[i].value.string);
679       if (status != 0) {
680         char errbuf[1024];
681         ERROR("dbi plugin: cdbi_connect_database (%s): "
682               "dbi_conn_set_option (\"%s\", \"%s\") failed: %s.",
683               db->name, db->driver_options[i].key,
684               db->driver_options[i].value.string,
685               cdbi_strerror(connection, errbuf, sizeof(errbuf)));
686       }
687     }
688
689     if (status != 0) {
690       INFO("dbi plugin: This is a list of all options understood "
691            "by the `%s' driver:",
692            db->driver);
693       for (const char *opt = dbi_conn_get_option_list(connection, NULL);
694            opt != NULL; opt = dbi_conn_get_option_list(connection, opt)) {
695         INFO("dbi plugin: * %s", opt);
696       }
697
698       dbi_conn_close(connection);
699       return -1;
700     }
701   } /* for (i = 0; i < db->driver_options_num; i++) */
702
703   status = dbi_conn_connect(connection);
704   if (status != 0) {
705     char errbuf[1024];
706     ERROR("dbi plugin: cdbi_connect_database (%s): "
707           "dbi_conn_connect failed: %s",
708           db->name, cdbi_strerror(connection, errbuf, sizeof(errbuf)));
709     dbi_conn_close(connection);
710     return -1;
711   }
712
713   if (db->select_db != NULL) {
714     status = dbi_conn_select_db(connection, db->select_db);
715     if (status != 0) {
716       char errbuf[1024];
717       WARNING(
718           "dbi plugin: cdbi_connect_database (%s): "
719           "dbi_conn_select_db (%s) failed: %s. Check the `SelectDB' option.",
720           db->name, db->select_db,
721           cdbi_strerror(connection, errbuf, sizeof(errbuf)));
722       dbi_conn_close(connection);
723       return -1;
724     }
725   }
726
727   db->connection = connection;
728   return 0;
729 } /* }}} int cdbi_connect_database */
730
731 static int cdbi_read_database(user_data_t *ud) /* {{{ */
732 {
733   cdbi_database_t *db = (cdbi_database_t *)ud->data;
734   int success;
735   int status;
736
737   unsigned int db_version;
738
739   status = cdbi_connect_database(db);
740   if (status != 0)
741     return status;
742   assert(db->connection != NULL);
743
744   db_version = dbi_conn_get_engine_version(db->connection);
745   /* TODO: Complain if `db_version == 0' */
746
747   success = 0;
748   for (size_t i = 0; i < db->queries_num; i++) {
749     /* Check if we know the database's version and if so, if this query applies
750      * to that version. */
751     if ((db_version != 0) &&
752         (udb_query_check_version(db->queries[i], db_version) == 0))
753       continue;
754
755     status = cdbi_read_database_query(db, db->queries[i], db->q_prep_areas[i]);
756     if (status == 0)
757       success++;
758   }
759
760   if (success == 0) {
761     ERROR("dbi plugin: All queries failed for database `%s'.", db->name);
762     return -1;
763   }
764
765   return 0;
766 } /* }}} int cdbi_read_database */
767
768 static int cdbi_shutdown(void) /* {{{ */
769 {
770   for (size_t i = 0; i < databases_num; i++) {
771     if (databases[i]->connection != NULL) {
772       dbi_conn_close(databases[i]->connection);
773       databases[i]->connection = NULL;
774     }
775     cdbi_database_free(databases[i]);
776   }
777   sfree(databases);
778   databases_num = 0;
779
780   udb_query_free(queries, queries_num);
781   queries = NULL;
782   queries_num = 0;
783
784   return 0;
785 } /* }}} int cdbi_shutdown */
786
787 void module_register(void) /* {{{ */
788 {
789   plugin_register_complex_config("dbi", cdbi_config);
790   plugin_register_init("dbi", cdbi_init);
791   plugin_register_shutdown("dbi", cdbi_shutdown);
792 } /* }}} void module_register */