f2d7665ff785a09f92ddfb978b9aa3dd4b7bd84c
[collectd.git] / src / postgresql.c
1 /**
2  * collectd - src/postgresql.c
3  * Copyright (C) 2008  Sebastian Harl
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  * Author:
19  *   Sebastian Harl <sh at tokkee.org>
20  **/
21
22 /*
23  * This module collects PostgreSQL database statistics.
24  */
25
26 #include "collectd.h"
27 #include "common.h"
28
29 #include "configfile.h"
30 #include "plugin.h"
31
32 #include "utils_complain.h"
33
34 #include <pg_config_manual.h>
35 #include <libpq-fe.h>
36
37 #define log_err(...) ERROR ("postgresql: " __VA_ARGS__)
38 #define log_warn(...) WARNING ("postgresql: " __VA_ARGS__)
39 #define log_info(...) INFO ("postgresql: " __VA_ARGS__)
40
41 #ifndef C_PSQL_DEFAULT_CONF
42 # define C_PSQL_DEFAULT_CONF PKGDATADIR "/postgresql_default.conf"
43 #endif
44
45 /* Appends the (parameter, value) pair to the string
46  * pointed to by 'buf' suitable to be used as argument
47  * for PQconnectdb(). If value equals NULL, the pair
48  * is ignored. */
49 #define C_PSQL_PAR_APPEND(buf, buf_len, parameter, value) \
50         if ((0 < (buf_len)) && (NULL != (value)) && ('\0' != *(value))) { \
51                 int s = ssnprintf (buf, buf_len, " %s = '%s'", parameter, value); \
52                 if (0 < s) { \
53                         buf     += s; \
54                         buf_len -= s; \
55                 } \
56         }
57
58 /* Returns the tuple (major, minor, patchlevel)
59  * for the given version number. */
60 #define C_PSQL_SERVER_VERSION3(server_version) \
61         (server_version) / 10000, \
62         (server_version) / 100 - (int)((server_version) / 10000) * 100, \
63         (server_version) - (int)((server_version) / 100) * 100
64
65 /* Returns true if the given host specifies a
66  * UNIX domain socket. */
67 #define C_PSQL_IS_UNIX_DOMAIN_SOCKET(host) \
68         ((NULL == (host)) || ('\0' == *(host)) || ('/' == *(host)))
69
70 /* Returns the tuple (host, delimiter, port) for a
71  * given (host, port) pair. Depending on the value of
72  * 'host' a UNIX domain socket or a TCP socket is
73  * assumed. */
74 #define C_PSQL_SOCKET3(host, port) \
75         ((NULL == (host)) || ('\0' == *(host))) ? DEFAULT_PGSOCKET_DIR : host, \
76         C_PSQL_IS_UNIX_DOMAIN_SOCKET (host) ? "/.s.PGSQL." : ":", \
77         port
78
79 typedef enum {
80         C_PSQL_PARAM_HOST = 1,
81         C_PSQL_PARAM_DB,
82         C_PSQL_PARAM_USER,
83 } c_psql_param_t;
84
85 typedef struct {
86         char *type;
87         char *type_instance;
88         int   ds_type;
89 } c_psql_col_t;
90
91 typedef struct {
92         char *name;
93         char *query;
94
95         c_psql_param_t *params;
96         int             params_num;
97
98         c_psql_col_t *cols;
99         int           cols_num;
100 } c_psql_query_t;
101
102 typedef struct {
103         PGconn      *conn;
104         c_complain_t conn_complaint;
105
106         int max_params_num;
107
108         /* user configuration */
109         c_psql_query_t **queries;
110         int              queries_num;
111
112         char *host;
113         char *port;
114         char *database;
115         char *user;
116         char *password;
117
118         char *sslmode;
119
120         char *krbsrvname;
121
122         char *service;
123 } c_psql_database_t;
124
125 static char *def_queries[] = {
126         "user_tables",
127         "io_user_tables"
128 };
129 static int def_queries_num = STATIC_ARRAY_SIZE (def_queries);
130
131 static c_psql_query_t *queries          = NULL;
132 static int             queries_num      = 0;
133
134 static c_psql_database_t *databases     = NULL;
135 static int                databases_num = 0;
136
137 static c_psql_query_t *c_psql_query_new (const char *name)
138 {
139         c_psql_query_t *query;
140
141         ++queries_num;
142         if (NULL == (queries = (c_psql_query_t *)realloc (queries,
143                                 queries_num * sizeof (*queries)))) {
144                 log_err ("Out of memory.");
145                 exit (5);
146         }
147         query = queries + queries_num - 1;
148
149         query->name  = sstrdup (name);
150         query->query = NULL;
151
152         query->params     = NULL;
153         query->params_num = 0;
154
155         query->cols     = NULL;
156         query->cols_num = 0;
157         return query;
158 } /* c_psql_query_new */
159
160 static void c_psql_query_delete (c_psql_query_t *query)
161 {
162         int i;
163
164         sfree (query->name);
165         sfree (query->query);
166
167         sfree (query->params);
168         query->params_num = 0;
169
170         for (i = 0; i < query->cols_num; ++i) {
171                 sfree (query->cols[i].type);
172                 sfree (query->cols[i].type_instance);
173         }
174         sfree (query->cols);
175         query->cols_num = 0;
176         return;
177 } /* c_psql_query_delete */
178
179 static c_psql_query_t *c_psql_query_get (const char *name)
180 {
181         int i;
182
183         for (i = 0; i < queries_num; ++i)
184                 if (0 == strcasecmp (name, queries[i].name))
185                         return queries + i;
186         return NULL;
187 } /* c_psql_query_get */
188
189 static c_psql_database_t *c_psql_database_new (const char *name)
190 {
191         c_psql_database_t *db;
192
193         ++databases_num;
194         if (NULL == (databases = (c_psql_database_t *)realloc (databases,
195                                 databases_num * sizeof (*databases)))) {
196                 log_err ("Out of memory.");
197                 exit (5);
198         }
199
200         db = databases + (databases_num - 1);
201
202         db->conn = NULL;
203
204         db->conn_complaint.last     = 0;
205         db->conn_complaint.interval = 0;
206
207         db->max_params_num = 0;
208
209         db->queries     = NULL;
210         db->queries_num = 0;
211
212         db->database   = sstrdup (name);
213         db->host       = NULL;
214         db->port       = NULL;
215         db->user       = NULL;
216         db->password   = NULL;
217
218         db->sslmode    = NULL;
219
220         db->krbsrvname = NULL;
221
222         db->service    = NULL;
223         return db;
224 } /* c_psql_database_new */
225
226 static void c_psql_database_delete (c_psql_database_t *db)
227 {
228         PQfinish (db->conn);
229
230         sfree (db->queries);
231         db->queries_num = 0;
232
233         sfree (db->database);
234         sfree (db->host);
235         sfree (db->port);
236         sfree (db->user);
237         sfree (db->password);
238
239         sfree (db->sslmode);
240
241         sfree (db->krbsrvname);
242
243         sfree (db->service);
244         return;
245 } /* c_psql_database_delete */
246
247 static void submit (const c_psql_database_t *db,
248                 const char *type, const char *type_instance,
249                 value_t *values, size_t values_len)
250 {
251         value_list_t vl = VALUE_LIST_INIT;
252
253         vl.values     = values;
254         vl.values_len = values_len;
255         vl.time       = time (NULL);
256
257         if (C_PSQL_IS_UNIX_DOMAIN_SOCKET (db->host)
258                         || (0 == strcmp (db->host, "localhost")))
259                 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
260         else
261                 sstrncpy (vl.host, db->host, sizeof (vl.host));
262
263         sstrncpy (vl.plugin, "postgresql", sizeof (vl.plugin));
264         sstrncpy (vl.plugin_instance, db->database, sizeof (vl.plugin_instance));
265
266         sstrncpy (vl.type, type, sizeof (vl.type));
267
268         if (NULL != type_instance)
269                 sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
270
271         plugin_dispatch_values (&vl);
272         return;
273 } /* submit */
274
275 static void submit_counter (const c_psql_database_t *db,
276                 const char *type, const char *type_instance,
277                 const char *value)
278 {
279         value_t values[1];
280
281         if ((NULL == value) || ('\0' == *value))
282                 return;
283
284         values[0].counter = atoll (value);
285         submit (db, type, type_instance, values, 1);
286         return;
287 } /* submit_counter */
288
289 static void submit_gauge (const c_psql_database_t *db,
290                 const char *type, const char *type_instance,
291                 const char *value)
292 {
293         value_t values[1];
294
295         if ((NULL == value) || ('\0' == *value))
296                 return;
297
298         values[0].gauge = atof (value);
299         submit (db, type, type_instance, values, 1);
300         return;
301 } /* submit_gauge */
302
303 static int c_psql_check_connection (c_psql_database_t *db)
304 {
305         /* "ping" */
306         PQclear (PQexec (db->conn, "SELECT 42;"));
307
308         if (CONNECTION_OK != PQstatus (db->conn)) {
309                 PQreset (db->conn);
310
311                 /* trigger c_release() */
312                 if (0 == db->conn_complaint.interval)
313                         db->conn_complaint.interval = 1;
314
315                 if (CONNECTION_OK != PQstatus (db->conn)) {
316                         c_complain (LOG_ERR, &db->conn_complaint,
317                                         "Failed to connect to database %s: %s",
318                                         db->database, PQerrorMessage (db->conn));
319                         return -1;
320                 }
321         }
322
323         c_release (LOG_INFO, &db->conn_complaint,
324                         "Successfully reconnected to database %s", PQdb (db->conn));
325         return 0;
326 } /* c_psql_check_connection */
327
328 static int c_psql_exec_query (c_psql_database_t *db, int idx)
329 {
330         c_psql_query_t *query;
331         PGresult       *res;
332
333         char *params[db->max_params_num];
334
335         int rows, cols;
336         int i;
337
338         if (idx >= db->queries_num)
339                 return -1;
340
341         query = db->queries[idx];
342
343         assert (db->max_params_num >= query->params_num);
344
345         for (i = 0; i < query->params_num; ++i) {
346                 switch (query->params[i]) {
347                         case C_PSQL_PARAM_HOST:
348                                 params[i] = C_PSQL_IS_UNIX_DOMAIN_SOCKET (db->host)
349                                         ? "localhost" : db->host;
350                                 break;
351                         case C_PSQL_PARAM_DB:
352                                 params[i] = db->database;
353                                 break;
354                         case C_PSQL_PARAM_USER:
355                                 params[i] = db->user;
356                                 break;
357                         default:
358                                 assert (0);
359                 }
360         }
361
362         res = PQexecParams (db->conn, query->query, query->params_num, NULL,
363                         (const char *const *)((0 == query->params_num) ? NULL : params),
364                         NULL, NULL, /* return text data */ 0);
365
366         if (PGRES_TUPLES_OK != PQresultStatus (res)) {
367                 log_err ("Failed to execute SQL query: %s",
368                                 PQerrorMessage (db->conn));
369                 log_info ("SQL query was: %s", query->query);
370                 PQclear (res);
371                 return -1;
372         }
373
374         rows = PQntuples (res);
375         if (1 > rows)
376                 return 0;
377
378         cols = PQnfields (res);
379         if (query->cols_num != cols) {
380                 log_err ("SQL query returned wrong number of fields "
381                                 "(expected: %i, got: %i)", query->cols_num, cols);
382                 log_info ("SQL query was: %s", query->query);
383                 return -1;
384         }
385
386         for (i = 0; i < rows; ++i) {
387                 int j;
388
389                 for (j = 0; j < cols; ++j) {
390                         c_psql_col_t col = query->cols[j];
391
392                         char *value = PQgetvalue (res, i, j);
393
394                         if (col.ds_type == DS_TYPE_COUNTER)
395                                 submit_counter (db, col.type, col.type_instance, value);
396                         else if (col.ds_type == DS_TYPE_GAUGE)
397                                 submit_gauge (db, col.type, col.type_instance, value);
398                 }
399         }
400         return 0;
401 } /* c_psql_exec_query */
402
403 static int c_psql_stat_database (c_psql_database_t *db)
404 {
405         const char *const query =
406                 "SELECT numbackends, xact_commit, xact_rollback "
407                         "FROM pg_stat_database "
408                         "WHERE datname = $1;";
409
410         PGresult *res;
411
412         int n;
413
414         res = PQexecParams (db->conn, query, /* number of parameters */ 1,
415                         NULL, (const char *const *)&db->database, NULL, NULL,
416                         /* return text data */ 0);
417
418         if (PGRES_TUPLES_OK != PQresultStatus (res)) {
419                 log_err ("Failed to execute SQL query: %s",
420                                 PQerrorMessage (db->conn));
421                 log_info ("SQL query was: %s", query);
422                 PQclear (res);
423                 return -1;
424         }
425
426         n = PQntuples (res);
427         if (1 < n) {
428                 log_warn ("pg_stat_database has more than one entry "
429                                 "for database %s - ignoring additional results.",
430                                 db->database);
431         }
432         else if (1 > n) {
433                 log_err ("pg_stat_database has no entry for database %s",
434                                 db->database);
435                 PQclear (res);
436                 return -1;
437         }
438
439         submit_gauge (db, "pg_numbackends", NULL,  PQgetvalue (res, 0, 0));
440
441         submit_counter (db, "pg_xact", "commit",   PQgetvalue (res, 0, 1));
442         submit_counter (db, "pg_xact", "rollback", PQgetvalue (res, 0, 2));
443
444         PQclear (res);
445         return 0;
446 } /* c_psql_stat_database */
447
448 static int c_psql_read (void)
449 {
450         int success = 0;
451         int i;
452
453         for (i = 0; i < databases_num; ++i) {
454                 c_psql_database_t *db = databases + i;
455
456                 int j;
457
458                 assert (NULL != db->database);
459
460                 if (0 != c_psql_check_connection (db))
461                         continue;
462
463                 c_psql_stat_database (db);
464
465                 for (j = 0; j < db->queries_num; ++j)
466                         c_psql_exec_query (db, j);
467
468                 ++success;
469         }
470
471         if (! success)
472                 return -1;
473         return 0;
474 } /* c_psql_read */
475
476 static int c_psql_shutdown (void)
477 {
478         int i;
479
480         if ((NULL == databases) || (0 == databases_num))
481                 return 0;
482
483         plugin_unregister_read ("postgresql");
484         plugin_unregister_shutdown ("postgresql");
485
486         for (i = 0; i < databases_num; ++i) {
487                 c_psql_database_t *db = databases + i;
488                 c_psql_database_delete (db);
489         }
490
491         sfree (databases);
492         databases_num = 0;
493
494         for (i = 0; i < queries_num; ++i) {
495                 c_psql_query_t *query = queries + i;
496                 c_psql_query_delete (query);
497         }
498
499         sfree (queries);
500         queries_num = 0;
501         return 0;
502 } /* c_psql_shutdown */
503
504 static int c_psql_init (void)
505 {
506         int i;
507
508         if ((NULL == databases) || (0 == databases_num))
509                 return 0;
510
511         for (i = 0; i < queries_num; ++i) {
512                 c_psql_query_t *query = queries + i;
513                 int j;
514
515                 for (j = 0; j < query->cols_num; ++j) {
516                         c_psql_col_t     *col = query->cols + j;
517                         const data_set_t *ds;
518
519                         ds = plugin_get_ds (col->type);
520                         if (NULL == ds) {
521                                 log_err ("Column: Unknown type \"%s\".", col->type);
522                                 c_psql_shutdown ();
523                                 return -1;
524                         }
525
526                         if (1 != ds->ds_num) {
527                                 log_err ("Column: Invalid type \"%s\" - types defining "
528                                                 "one data source are supported only (got: %i).",
529                                                 col->type, ds->ds_num);
530                                 c_psql_shutdown ();
531                                 return -1;
532                         }
533
534                         col->ds_type = ds->ds[0].type;
535                 }
536         }
537
538         for (i = 0; i < databases_num; ++i) {
539                 c_psql_database_t *db = databases + i;
540
541                 char  conninfo[4096];
542                 char *buf     = conninfo;
543                 int   buf_len = sizeof (conninfo);
544                 int   status;
545
546                 char *server_host;
547                 int   server_version;
548
549                 status = ssnprintf (buf, buf_len, "dbname = '%s'", db->database);
550                 if (0 < status) {
551                         buf     += status;
552                         buf_len -= status;
553                 }
554
555                 C_PSQL_PAR_APPEND (buf, buf_len, "host",       db->host);
556                 C_PSQL_PAR_APPEND (buf, buf_len, "port",       db->port);
557                 C_PSQL_PAR_APPEND (buf, buf_len, "user",       db->user);
558                 C_PSQL_PAR_APPEND (buf, buf_len, "password",   db->password);
559                 C_PSQL_PAR_APPEND (buf, buf_len, "sslmode",    db->sslmode);
560                 C_PSQL_PAR_APPEND (buf, buf_len, "krbsrvname", db->krbsrvname);
561                 C_PSQL_PAR_APPEND (buf, buf_len, "service",    db->service);
562
563                 db->conn = PQconnectdb (conninfo);
564                 if (0 != c_psql_check_connection (db))
565                         continue;
566
567                 server_host    = PQhost (db->conn);
568                 server_version = PQserverVersion (db->conn);
569                 log_info ("Sucessfully connected to database %s (user %s) "
570                                 "at server %s%s%s (server version: %d.%d.%d, "
571                                 "protocol version: %d, pid: %d)",
572                                 PQdb (db->conn), PQuser (db->conn),
573                                 C_PSQL_SOCKET3 (server_host, PQport (db->conn)),
574                                 C_PSQL_SERVER_VERSION3 (server_version),
575                                 PQprotocolVersion (db->conn), PQbackendPID (db->conn));
576         }
577
578         plugin_register_read ("postgresql", c_psql_read);
579         plugin_register_shutdown ("postgresql", c_psql_shutdown);
580         return 0;
581 } /* c_psql_init */
582
583 static int config_set (char *name, char **var, const oconfig_item_t *ci)
584 {
585         if ((0 != ci->children_num) || (1 != ci->values_num)
586                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
587                 log_err ("%s expects a single string argument.", name);
588                 return 1;
589         }
590
591         sfree (*var);
592         *var = sstrdup (ci->values[0].value.string);
593         return 0;
594 } /* config_set */
595
596 static int config_set_param (c_psql_query_t *query, const oconfig_item_t *ci)
597 {
598         c_psql_param_t param;
599         char          *param_str;
600
601         if ((0 != ci->children_num) || (1 != ci->values_num)
602                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
603                 log_err ("Param expects a single string argument.");
604                 return 1;
605         }
606
607         param_str = ci->values[0].value.string;
608         if (0 == strcasecmp (param_str, "hostname"))
609                 param = C_PSQL_PARAM_HOST;
610         else if (0 == strcasecmp (param_str, "database"))
611                 param = C_PSQL_PARAM_DB;
612         else if (0 == strcasecmp (param_str, "username"))
613                 param = C_PSQL_PARAM_USER;
614         else {
615                 log_err ("Invalid parameter \"%s\".", param_str);
616                 return 1;
617         }
618
619         ++query->params_num;
620         if (NULL == (query->params = (c_psql_param_t *)realloc (query->params,
621                                 query->params_num * sizeof (*query->params)))) {
622                 log_err ("Out of memory.");
623                 exit (5);
624         }
625
626         query->params[query->params_num - 1] = param;
627         return 0;
628 } /* config_set_param */
629
630 static int config_set_column (c_psql_query_t *query, const oconfig_item_t *ci)
631 {
632         c_psql_col_t *col;
633
634         int i;
635
636         if ((0 != ci->children_num)
637                         || (1 > ci->values_num) || (2 < ci->values_num)) {
638                 log_err ("Column expects either one or two arguments.");
639                 return 1;
640         }
641
642         for (i = 0; i < ci->values_num; ++i) {
643                 if (OCONFIG_TYPE_STRING != ci->values[i].type) {
644                         log_err ("Column expects either one or two string arguments.");
645                         return 1;
646                 }
647         }
648
649         ++query->cols_num;
650         if (NULL == (query->cols = (c_psql_col_t *)realloc (query->cols,
651                                 query->cols_num * sizeof (*query->cols)))) {
652                 log_err ("Out of memory.");
653                 exit (5);
654         }
655
656         col = query->cols + query->cols_num - 1;
657
658         col->ds_type = -1;
659
660         col->type = sstrdup (ci->values[0].value.string);
661         col->type_instance = (2 == ci->values_num)
662                 ? sstrdup (ci->values[1].value.string) : NULL;
663         return 0;
664 } /* config_set_column */
665
666 static int config_set_query (c_psql_database_t *db, const oconfig_item_t *ci)
667 {
668         c_psql_query_t *query;
669
670         if ((0 != ci->children_num) || (1 != ci->values_num)
671                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
672                 log_err ("Query expects a single string argument.");
673                 return 1;
674         }
675
676         query = c_psql_query_get (ci->values[0].value.string);
677         if (NULL == query) {
678                 log_err ("Query \"%s\" not found - please check your configuration.",
679                                 ci->values[0].value.string);
680                 return 1;
681         }
682
683         ++db->queries_num;
684         if (NULL == (db->queries = (c_psql_query_t **)realloc (db->queries,
685                                 db->queries_num * sizeof (*db->queries)))) {
686                 log_err ("Out of memory.");
687                 exit (5);
688         }
689
690         if (query->params_num > db->max_params_num)
691                 db->max_params_num = query->params_num;
692
693         db->queries[db->queries_num - 1] = query;
694         return 0;
695 } /* config_set_query */
696
697 static int c_psql_config_query (oconfig_item_t *ci)
698 {
699         c_psql_query_t *query;
700
701         int i;
702
703         if ((1 != ci->values_num)
704                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
705                 log_err ("<Query> expects a single string argument.");
706                 return 1;
707         }
708
709         query = c_psql_query_new (ci->values[0].value.string);
710
711         for (i = 0; i < ci->children_num; ++i) {
712                 oconfig_item_t *c = ci->children + i;
713
714                 if (0 == strcasecmp (c->key, "Query"))
715                         config_set ("Query", &query->query, c);
716                 else if (0 == strcasecmp (c->key, "Param"))
717                         config_set_param (query, c);
718                 else if (0 == strcasecmp (c->key, "Column"))
719                         config_set_column (query, c);
720                 else
721                         log_warn ("Ignoring unknown config key \"%s\".", c->key);
722         }
723         return 0;
724 } /* c_psql_config_query */
725
726 static int c_psql_config_database (oconfig_item_t *ci)
727 {
728         c_psql_database_t *db;
729
730         int i;
731
732         if ((1 != ci->values_num)
733                         || (OCONFIG_TYPE_STRING != ci->values[0].type)) {
734                 log_err ("<Database> expects a single string argument.");
735                 return 1;
736         }
737
738         db = c_psql_database_new (ci->values[0].value.string);
739
740         for (i = 0; i < ci->children_num; ++i) {
741                 oconfig_item_t *c = ci->children + i;
742
743                 if (0 == strcasecmp (c->key, "Host"))
744                         config_set ("Host", &db->host, c);
745                 else if (0 == strcasecmp (c->key, "Port"))
746                         config_set ("Port", &db->port, c);
747                 else if (0 == strcasecmp (c->key, "User"))
748                         config_set ("User", &db->user, c);
749                 else if (0 == strcasecmp (c->key, "Password"))
750                         config_set ("Password", &db->password, c);
751                 else if (0 == strcasecmp (c->key, "SSLMode"))
752                         config_set ("SSLMode", &db->sslmode, c);
753                 else if (0 == strcasecmp (c->key, "KRBSrvName"))
754                         config_set ("KRBSrvName", &db->krbsrvname, c);
755                 else if (0 == strcasecmp (c->key, "Service"))
756                         config_set ("Service", &db->service, c);
757                 else if (0 == strcasecmp (c->key, "Query"))
758                         config_set_query (db, c);
759                 else
760                         log_warn ("Ignoring unknown config key \"%s\".", c->key);
761         }
762
763         if (NULL == db->queries) {
764                 db->queries = (c_psql_query_t **)malloc (def_queries_num
765                                 * sizeof (*db->queries));
766
767                 for (i = 0; i < def_queries_num; ++i) {
768                         db->queries[i] = c_psql_query_get (def_queries[i]);
769                         if (NULL == db->queries[i])
770                                 log_err ("Query \"%s\" not found - "
771                                                 "please check your installation.",
772                                                 def_queries[i]);
773                         else
774                                 ++db->queries_num;
775                 }
776         }
777         return 0;
778 }
779
780 static int c_psql_config (oconfig_item_t *ci)
781 {
782         static int have_def_config = 0;
783
784         int i;
785
786         if (0 == have_def_config) {
787                 oconfig_item_t *c;
788
789                 have_def_config = 1;
790
791                 c = oconfig_parse_file (C_PSQL_DEFAULT_CONF);
792                 if (NULL == c)
793                         log_err ("Failed to read default config ("C_PSQL_DEFAULT_CONF").");
794                 else
795                         c_psql_config (c);
796
797                 if (NULL == queries)
798                         log_err ("Default config ("C_PSQL_DEFAULT_CONF") did not define "
799                                         "any queries - please check your installation.");
800         }
801
802         for (i = 0; i < ci->children_num; ++i) {
803                 oconfig_item_t *c = ci->children + i;
804
805                 if (0 == strcasecmp (c->key, "Query"))
806                         c_psql_config_query (c);
807                 else if (0 == strcasecmp (c->key, "Database"))
808                         c_psql_config_database (c);
809                 else
810                         log_warn ("Ignoring unknown config key \"%s\".", c->key);
811         }
812         return 0;
813 } /* c_psql_config */
814
815 void module_register (void)
816 {
817         plugin_register_complex_config ("postgresql", c_psql_config);
818         plugin_register_init ("postgresql", c_psql_init);
819 } /* module_register */
820
821 /* vim: set sw=4 ts=4 tw=78 noexpandtab : */
822