Merge branch 'schmurfy/re_invert'
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Mon, 5 Apr 2010 12:04:18 +0000 (14:04 +0200)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Mon, 5 Apr 2010 12:04:18 +0000 (14:04 +0200)
23 files changed:
configure.in
src/apache.c
src/collectd.conf.pod
src/csv.c
src/curl_json.c
src/curl_xml.c
src/dbi.c
src/java.c
src/mysql.c
src/netapp.c
src/onewire.c
src/oracle.c
src/plugin.c
src/plugin.h
src/postgresql.c
src/python.c
src/routeros.c
src/snmp.c
src/target_scale.c
src/utils_db_query.c
src/utils_db_query.h
src/utils_llist.c
src/utils_llist.h

index 1193583..db0b083 100644 (file)
@@ -3789,7 +3789,7 @@ AC_DEFUN(
             enable_plugin="yes"
             force="yes"
      else
-            enable_plugin="no"
+            enable_plugin="no (disabled on command line)"
      fi; fi
     ],
     [
index 39849e6..3d6d957 100644 (file)
@@ -313,7 +313,8 @@ static int config_add (oconfig_item_t *ci)
                                (st->host != NULL) ? st->host : hostname_g,
                                (st->name != NULL) ? st->name : "default"),
 
-               status = plugin_register_complex_read (callback_name,
+               status = plugin_register_complex_read (/* group = */ NULL,
+                               /* name      = */ callback_name,
                                /* callback  = */ apache_read_host,
                                /* interval  = */ NULL,
                                /* user_data = */ &ud);
index c2dfaf3..e3af2e2 100644 (file)
@@ -2988,6 +2988,7 @@ L<http://www.postgresql.org/docs/manuals/>.
     </Database>
 
     <Database bar>
+      Interval 300
       Service "service_name"
       Query backend # predefined
       Query rt36_tickets
@@ -3051,7 +3052,8 @@ The username used to connect to the database.
 
 =item I<interval>
 
-The interval collectd is using (as specified by the B<Interval> option).
+The interval with which this database is queried (as specified by the database
+specific or global B<Interval> options).
 
 =back
 
@@ -3189,6 +3191,11 @@ for details.
 
 =over 4
 
+=item B<Interval> I<seconds>
+
+Specify the interval with which the database should be queried. The default is
+to use the global B<Interval> setting.
+
 =item B<Host> I<hostname>
 
 Specify the hostname or IP of the PostgreSQL server to connect to. If the
index 0b34687..96c1e3e 100644 (file)
--- a/src/csv.c
+++ b/src/csv.c
@@ -299,7 +299,7 @@ static int csv_write (const data_set_t *ds, const value_list_t *vl,
 
                fprintf (use_stdio == 1 ? stdout : stderr,
                         "PUTVAL %s interval=%i %s\n",
-                        filename, interval_g, values);
+                        filename, vl->interval, values);
                return (0);
        }
 
index 53e8abd..03ef6a3 100644 (file)
@@ -673,7 +673,7 @@ static int cj_config_add_url (oconfig_item_t *ci) /* {{{ */
     ssnprintf (cb_name, sizeof (cb_name), "curl_json-%s-%s",
                db->instance, db->url);
 
-    plugin_register_complex_read (cb_name, cj_read,
+    plugin_register_complex_read (/* group = */ NULL, cb_name, cj_read,
                                   /* interval = */ NULL, &ud);
   }
   else
index 240be66..c10955c 100644 (file)
@@ -870,7 +870,7 @@ static int cx_config_add_url (oconfig_item_t *ci) /* {{{ */
     ssnprintf (cb_name, sizeof (cb_name), "curl_xml-%s-%s",
                db->instance, db->url);
 
-    plugin_register_complex_read (cb_name, cx_read,
+    plugin_register_complex_read (/* group = */ NULL, cb_name, cx_read,
                                   /* interval = */ NULL, &ud);
   }
   else
index ce4cd02..77f393f 100644 (file)
--- a/src/dbi.c
+++ b/src/dbi.c
@@ -46,6 +46,7 @@ struct cdbi_database_s /* {{{ */
   cdbi_driver_option_t *driver_options;
   size_t driver_options_num;
 
+  udb_query_preparation_area_t **q_prep_areas;
   udb_query_t **queries;
   size_t        queries_num;
 
@@ -162,6 +163,11 @@ static void cdbi_database_free (cdbi_database_t *db) /* {{{ */
   }
   sfree (db->driver_options);
 
+  if (db->q_prep_areas)
+    for (i = 0; i < db->queries_num; ++i)
+      udb_query_delete_preparation_area (db->q_prep_areas[i]);
+  free (db->q_prep_areas);
+
   sfree (db);
 } /* }}} void cdbi_database_free */
 
@@ -328,6 +334,34 @@ static int cdbi_config_add_database (oconfig_item_t *ci) /* {{{ */
     break;
   } /* while (status == 0) */
 
+  while ((status == 0) && (db->queries_num > 0))
+  {
+    db->q_prep_areas = (udb_query_preparation_area_t **) calloc (
+        db->queries_num, sizeof (*db->q_prep_areas));
+
+    if (db->q_prep_areas == NULL)
+    {
+      WARNING ("dbi plugin: malloc failed");
+      status = -1;
+      break;
+    }
+
+    for (i = 0; i < db->queries_num; ++i)
+    {
+      db->q_prep_areas[i]
+        = udb_query_allocate_preparation_area (db->queries[i]);
+
+      if (db->q_prep_areas[i] == NULL)
+      {
+        WARNING ("dbi plugin: udb_query_allocate_preparation_area failed");
+        status = -1;
+        break;
+      }
+    }
+
+    break;
+  }
+
   /* If all went well, add this database to the global list of databases. */
   if (status == 0)
   {
@@ -422,7 +456,7 @@ static int cdbi_init (void) /* {{{ */
 } /* }}} int cdbi_init */
 
 static int cdbi_read_database_query (cdbi_database_t *db, /* {{{ */
-    udb_query_t *q)
+    udb_query_t *q, udb_query_preparation_area_t *prep_area)
 {
   const char *statement;
   dbi_result res;
@@ -530,8 +564,9 @@ static int cdbi_read_database_query (cdbi_database_t *db, /* {{{ */
     sstrncpy (column_names[i], column_name, DATA_MAX_NAME_LEN);
   } /* }}} for (i = 0; i < column_num; i++) */
 
-  udb_query_prepare_result (q, hostname_g, /* plugin = */ "dbi", db->name,
-      column_names, column_num);
+  udb_query_prepare_result (q, prep_area, hostname_g,
+      /* plugin = */ "dbi", db->name,
+      column_names, column_num, /* interval = */ -1);
 
   /* 0 = error; 1 = success; */
   status = dbi_result_first_row (res); /* {{{ */
@@ -543,7 +578,7 @@ static int cdbi_read_database_query (cdbi_database_t *db, /* {{{ */
         "return any rows?",
         db->name, udb_query_get_name (q),
         cdbi_strerror (db->connection, errbuf, sizeof (errbuf)));
-    udb_query_finish_result (q);
+    udb_query_finish_result (q, prep_area);
     BAIL_OUT (-1);
   } /* }}} */
 
@@ -572,7 +607,7 @@ static int cdbi_read_database_query (cdbi_database_t *db, /* {{{ */
      * to dispatch the row to the daemon. */
     if (status == 0) /* {{{ */
     {
-      status = udb_query_handle_result (q, column_values);
+      status = udb_query_handle_result (q, prep_area, column_values);
       if (status != 0)
       {
         ERROR ("dbi plugin: cdbi_read_database_query (%s, %s): "
@@ -598,7 +633,7 @@ static int cdbi_read_database_query (cdbi_database_t *db, /* {{{ */
   } /* }}} while (42) */
 
   /* Tell the db query interface that we're done with this query. */
-  udb_query_finish_result (q);
+  udb_query_finish_result (q, prep_area);
 
   /* Clean up and return `status = 0' (success) */
   BAIL_OUT (0);
@@ -741,7 +776,8 @@ static int cdbi_read_database (cdbi_database_t *db) /* {{{ */
         && (udb_query_check_version (db->queries[i], db_version) == 0))
       continue;
 
-    status = cdbi_read_database_query (db, db->queries[i]);
+    status = cdbi_read_database_query (db,
+        db->queries[i], db->q_prep_areas[i]);
     if (status == 0)
       success++;
   }
index 87b189f..528ec9c 100644 (file)
@@ -1419,7 +1419,7 @@ static jint JNICALL cjni_api_register_read (JNIEnv *jvm_env, /* {{{ */
   ud.data = (void *) cbi;
   ud.free_func = cjni_callback_info_destroy;
 
-  plugin_register_complex_read (cbi->name, cjni_read,
+  plugin_register_complex_read (/* group = */ NULL, cbi->name, cjni_read,
       /* interval = */ NULL, &ud);
 
   (*jvm_env)->DeleteLocalRef (jvm_env, o_read);
index 30f5504..c7b796b 100644 (file)
@@ -342,7 +342,8 @@ static int mysql_config (oconfig_item_t *ci) /* {{{ */
                else
                        sstrncpy (cb_name, "mysql", sizeof (cb_name));
 
-               plugin_register_complex_read (cb_name, mysql_read,
+               plugin_register_complex_read (/* group = */ NULL, cb_name,
+                                             mysql_read,
                                              /* interval = */ NULL, &ud);
        }
        else
index fc65358..317b0fe 100644 (file)
@@ -2559,7 +2559,7 @@ static int cna_config (oconfig_item_t *ci) { /* {{{ */
                        ud.data = host;
                        ud.free_func = (void (*) (void *)) free_host_config;
 
-                       plugin_register_complex_read (cb_name,
+                       plugin_register_complex_read (/* group = */ NULL, cb_name,
                                        /* callback  = */ cna_read, 
                                        /* interval  = */ (host->interval > 0) ? &interval : NULL,
                                        /* user data = */ &ud);
index cae0d63..462458c 100644 (file)
@@ -310,7 +310,7 @@ static int cow_init (void)
   if (ow_interval > 0)
     cb_interval.tv_sec = (time_t) ow_interval;
 
-  plugin_register_complex_read ("onewire", cow_read,
+  plugin_register_complex_read (/* group = */ NULL, "onewire", cow_read,
       &cb_interval, /* user data = */ NULL);
   plugin_register_shutdown ("onewire", cow_shutdown);
 
index 7a8ccc6..3fe2125 100644 (file)
@@ -62,6 +62,7 @@ struct o_database_s
   char *username;
   char *password;
 
+  udb_query_preparation_area_t **q_prep_areas;
   udb_query_t **queries;
   size_t        queries_num;
 
@@ -121,6 +122,8 @@ static void o_report_error (const char *where, /* {{{ */
 
 static void o_database_free (o_database_t *db) /* {{{ */
 {
+  size_t i;
+
   if (db == NULL)
     return;
 
@@ -130,6 +133,11 @@ static void o_database_free (o_database_t *db) /* {{{ */
   sfree (db->password);
   sfree (db->queries);
 
+  if (db->q_prep_areas != NULL)
+    for (i = 0; i < db->queries_num; ++i)
+      udb_query_delete_preparation_area (db->q_prep_areas[i]);
+  free (db->q_prep_areas);
+
   sfree (db);
 } /* }}} void o_database_free */
 
@@ -256,6 +264,34 @@ static int o_config_add_database (oconfig_item_t *ci) /* {{{ */
     break;
   } /* while (status == 0) */
 
+  while ((status == 0) && (db->queries_num > 0))
+  {
+    db->q_prep_areas = (udb_query_preparation_area_t **) calloc (
+        db->queries_num, sizeof (*db->q_prep_areas));
+
+    if (db->q_prep_areas == NULL)
+    {
+      WARNING ("oracle plugin: malloc failed");
+      status = -1;
+      break;
+    }
+
+    for (i = 0; i < db->queries_num; ++i)
+    {
+      db->q_prep_areas[i]
+        = udb_query_allocate_preparation_area (db->queries[i]);
+
+      if (db->q_prep_areas[i] == NULL)
+      {
+        WARNING ("oracle plugin: udb_query_allocate_preparation_area failed");
+        status = -1;
+        break;
+      }
+    }
+
+    break;
+  }
+
   /* If all went well, add this query to the list of queries within the
    * database structure. */
   if (status == 0)
@@ -349,7 +385,7 @@ static int o_init (void) /* {{{ */
 } /* }}} int o_init */
 
 static int o_read_database_query (o_database_t *db, /* {{{ */
-    udb_query_t *q)
+    udb_query_t *q, udb_query_preparation_area_t *prep_area)
 {
   char **column_names;
   char **column_values;
@@ -548,8 +584,9 @@ static int o_read_database_query (o_database_t *db, /* {{{ */
   } /* for (j = 1; j <= param_counter; j++) */
   /* }}} End of the ``define'' stuff. */
 
-  status = udb_query_prepare_result (q, hostname_g, /* plugin = */ "oracle",
-      db->name, column_names, column_num);
+  status = udb_query_prepare_result (q, prep_area, hostname_g,
+      /* plugin = */ "oracle", db->name, column_names, column_num,
+      /* interval = */ -1);
   if (status != 0)
   {
     ERROR ("oracle plugin: o_read_database_query (%s, %s): "
@@ -576,7 +613,7 @@ static int o_read_database_query (o_database_t *db, /* {{{ */
       break;
     }
 
-    status = udb_query_handle_result (q, column_values);
+    status = udb_query_handle_result (q, prep_area, column_values);
     if (status != 0)
     {
       WARNING ("oracle plugin: o_read_database_query (%s, %s): "
@@ -661,7 +698,7 @@ static int o_read_database (o_database_t *db) /* {{{ */
       db->connect_id, db->oci_service_context);
 
   for (i = 0; i < db->queries_num; i++)
-    o_read_database_query (db, db->queries[i]);
+    o_read_database_query (db, db->queries[i], db->q_prep_areas[i]);
 
   return (0);
 } /* }}} int o_read_database */
index 1eed532..a134446 100644 (file)
@@ -59,6 +59,7 @@ struct read_func_s
 #define rf_callback rf_super.cf_callback
 #define rf_udata rf_super.cf_udata
        callback_func_t rf_super;
+       char rf_group[DATA_MAX_NAME_LEN];
        char rf_name[DATA_MAX_NAME_LEN];
        int rf_type;
        struct timespec rf_interval;
@@ -772,6 +773,7 @@ int plugin_register_read (const char *name,
        rf->rf_callback = (void *) callback;
        rf->rf_udata.data = NULL;
        rf->rf_udata.free_func = NULL;
+       rf->rf_group[0] = '\0';
        sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
        rf->rf_type = RF_SIMPLE;
        rf->rf_interval.tv_sec = 0;
@@ -781,7 +783,7 @@ int plugin_register_read (const char *name,
        return (plugin_insert_read (rf));
 } /* int plugin_register_read */
 
-int plugin_register_complex_read (const char *name,
+int plugin_register_complex_read (const char *group, const char *name,
                plugin_read_cb callback,
                const struct timespec *interval,
                user_data_t *user_data)
@@ -797,6 +799,10 @@ int plugin_register_complex_read (const char *name,
 
        memset (rf, 0, sizeof (read_func_t));
        rf->rf_callback = (void *) callback;
+       if (group != NULL)
+               sstrncpy (rf->rf_group, group, sizeof (rf->rf_group));
+       else
+               rf->rf_group[0] = '\0';
        sstrncpy (rf->rf_name, name, sizeof (rf->rf_name));
        rf->rf_type = RF_COMPLEX;
        if (interval != NULL)
@@ -948,6 +954,67 @@ int plugin_unregister_read (const char *name) /* {{{ */
        return (0);
 } /* }}} int plugin_unregister_read */
 
+static int compare_read_func_group (llentry_t *e, void *ud) /* {{{ */
+{
+       read_func_t *rf    = e->value;
+       char        *group = ud;
+
+       return strcmp (rf->rf_group, (const char *)group);
+} /* }}} int compare_read_func_group */
+
+int plugin_unregister_read_group (const char *group) /* {{{ */
+{
+       llentry_t *le;
+       read_func_t *rf;
+
+       int found = 0;
+
+       if (group == NULL)
+               return (-ENOENT);
+
+       pthread_mutex_lock (&read_lock);
+
+       if (read_list == NULL)
+       {
+               pthread_mutex_unlock (&read_lock);
+               return (-ENOENT);
+       }
+
+       while (42)
+       {
+               le = llist_search_custom (read_list,
+                               compare_read_func_group, (void *)group);
+
+               if (le == NULL)
+                       break;
+
+               ++found;
+
+               llist_remove (read_list, le);
+
+               rf = le->value;
+               assert (rf != NULL);
+               rf->rf_type = RF_REMOVE;
+
+               llentry_destroy (le);
+
+               DEBUG ("plugin_unregister_read_group: "
+                               "Marked `%s' (group `%s') for removal.",
+                               rf->rf_name, group);
+       }
+
+       pthread_mutex_unlock (&read_lock);
+
+       if (found == 0)
+       {
+               WARNING ("plugin_unregister_read_group: No such "
+                               "group of read function: %s", group);
+               return (-ENOENT);
+       }
+
+       return (0);
+} /* }}} int plugin_unregister_read_group */
+
 int plugin_unregister_write (const char *name)
 {
        return (plugin_unregister (list_write, name));
index 868f44a..8b9449e 100644 (file)
@@ -264,7 +264,7 @@ int plugin_register_init (const char *name,
                plugin_init_cb callback);
 int plugin_register_read (const char *name,
                int (*callback) (void));
-int plugin_register_complex_read (const char *name,
+int plugin_register_complex_read (const char *group, const char *name,
                plugin_read_cb callback,
                const struct timespec *interval,
                user_data_t *user_data);
@@ -284,6 +284,7 @@ int plugin_unregister_config (const char *name);
 int plugin_unregister_complex_config (const char *name);
 int plugin_unregister_init (const char *name);
 int plugin_unregister_read (const char *name);
+int plugin_unregister_read_group (const char *group);
 int plugin_unregister_write (const char *name);
 int plugin_unregister_flush (const char *name);
 int plugin_unregister_shutdown (const char *name);
index e6527b9..dd53cb4 100644 (file)
@@ -2,19 +2,30 @@
  * collectd - src/postgresql.c
  * Copyright (C) 2008, 2009  Sebastian Harl
  * Copyright (C) 2009        Florian Forster
+ * All rights reserved.
  *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; only version 2 of the License is applicable.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
  *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
  *
  * Authors:
  *   Sebastian Harl <sh at tokkee.org>
@@ -102,9 +113,12 @@ typedef struct {
        int max_params_num;
 
        /* user configuration */
+       udb_query_preparation_area_t **q_prep_areas;
        udb_query_t    **queries;
        size_t           queries_num;
 
+       int interval;
+
        char *host;
        char *port;
        char *database;
@@ -132,22 +146,16 @@ static int def_queries_num = STATIC_ARRAY_SIZE (def_queries);
 static udb_query_t      **queries       = NULL;
 static size_t             queries_num   = 0;
 
-static c_psql_database_t *databases     = NULL;
-static int                databases_num = 0;
-
 static c_psql_database_t *c_psql_database_new (const char *name)
 {
        c_psql_database_t *db;
 
-       ++databases_num;
-       if (NULL == (databases = (c_psql_database_t *)realloc (databases,
-                               databases_num * sizeof (*databases)))) {
+       db = (c_psql_database_t *)malloc (sizeof (*db));
+       if (NULL == db) {
                log_err ("Out of memory.");
-               exit (5);
+               return NULL;
        }
 
-       db = databases + (databases_num - 1);
-
        db->conn = NULL;
 
        C_COMPLAIN_INIT (&db->conn_complaint);
@@ -157,9 +165,12 @@ static c_psql_database_t *c_psql_database_new (const char *name)
 
        db->max_params_num = 0;
 
+       db->q_prep_areas   = NULL;
        db->queries        = NULL;
        db->queries_num    = 0;
 
+       db->interval   = 0;
+
        db->database   = sstrdup (name);
        db->host       = NULL;
        db->port       = NULL;
@@ -174,11 +185,20 @@ static c_psql_database_t *c_psql_database_new (const char *name)
        return db;
 } /* c_psql_database_new */
 
-static void c_psql_database_delete (c_psql_database_t *db)
+static void c_psql_database_delete (void *data)
 {
+       size_t i;
+
+       c_psql_database_t *db = data;
+
        PQfinish (db->conn);
        db->conn = NULL;
 
+       if (db->q_prep_areas)
+               for (i = 0; i < db->queries_num; ++i)
+                       udb_query_delete_preparation_area (db->q_prep_areas[i]);
+       free (db->q_prep_areas);
+
        sfree (db->queries);
        db->queries_num = 0;
 
@@ -196,8 +216,49 @@ static void c_psql_database_delete (c_psql_database_t *db)
        return;
 } /* c_psql_database_delete */
 
+static int c_psql_connect (c_psql_database_t *db)
+{
+       char  conninfo[4096];
+       char *buf     = conninfo;
+       int   buf_len = sizeof (conninfo);
+       int   status;
+
+       if (! db)
+               return -1;
+
+       status = ssnprintf (buf, buf_len, "dbname = '%s'", db->database);
+       if (0 < status) {
+               buf     += status;
+               buf_len -= status;
+       }
+
+       C_PSQL_PAR_APPEND (buf, buf_len, "host",       db->host);
+       C_PSQL_PAR_APPEND (buf, buf_len, "port",       db->port);
+       C_PSQL_PAR_APPEND (buf, buf_len, "user",       db->user);
+       C_PSQL_PAR_APPEND (buf, buf_len, "password",   db->password);
+       C_PSQL_PAR_APPEND (buf, buf_len, "sslmode",    db->sslmode);
+       C_PSQL_PAR_APPEND (buf, buf_len, "krbsrvname", db->krbsrvname);
+       C_PSQL_PAR_APPEND (buf, buf_len, "service",    db->service);
+
+       db->conn = PQconnectdb (conninfo);
+       db->proto_version = PQprotocolVersion (db->conn);
+       return 0;
+} /* c_psql_connect */
+
 static int c_psql_check_connection (c_psql_database_t *db)
 {
+       _Bool init = 0;
+
+       if (! db->conn) {
+               init = 1;
+
+               /* trigger c_release() */
+               if (0 == db->conn_complaint.interval)
+                       db->conn_complaint.interval = 1;
+
+               c_psql_connect (db);
+       }
+
        /* "ping" */
        PQclear (PQexec (db->conn, "SELECT 42;"));
 
@@ -216,15 +277,30 @@ static int c_psql_check_connection (c_psql_database_t *db)
                }
 
                db->proto_version = PQprotocolVersion (db->conn);
-               if (3 > db->proto_version)
-                       log_warn ("Protocol version %d does not support parameters.",
-                                       db->proto_version);
        }
 
        db->server_version = PQserverVersion (db->conn);
 
-       c_release (LOG_INFO, &db->conn_complaint,
-                       "Successfully reconnected to database %s", PQdb (db->conn));
+       if (c_would_release (&db->conn_complaint)) {
+               char *server_host;
+               int   server_version;
+
+               server_host    = PQhost (db->conn);
+               server_version = PQserverVersion (db->conn);
+
+               c_do_release (LOG_INFO, &db->conn_complaint,
+                               "Successfully %sconnected to database %s (user %s) "
+                               "at server %s%s%s (server version: %d.%d.%d, "
+                               "protocol version: %d, pid: %d)", init ? "" : "re",
+                               PQdb (db->conn), PQuser (db->conn),
+                               C_PSQL_SOCKET3 (server_host, PQport (db->conn)),
+                               C_PSQL_SERVER_VERSION3 (server_version),
+                               db->proto_version, PQbackendPID (db->conn));
+
+               if (3 > db->proto_version)
+                       log_warn ("Protocol version %d does not support parameters.",
+                                       db->proto_version);
+       }
        return 0;
 } /* c_psql_check_connection */
 
@@ -259,7 +335,8 @@ static PGresult *c_psql_exec_query_params (c_psql_database_t *db,
                                params[i] = db->user;
                                break;
                        case C_PSQL_PARAM_INTERVAL:
-                               ssnprintf (interval, sizeof (interval), "%i", interval_g);
+                               ssnprintf (interval, sizeof (interval), "%i",
+                                               db->interval > 0 ? db->interval : interval_g);
                                params[i] = interval;
                                break;
                        default:
@@ -273,7 +350,8 @@ static PGresult *c_psql_exec_query_params (c_psql_database_t *db,
                        NULL, NULL, /* return text data */ 0);
 } /* c_psql_exec_query_params */
 
-static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q)
+static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q,
+               udb_query_preparation_area_t *prep_area)
 {
        PGresult *res;
 
@@ -356,8 +434,8 @@ static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q)
        else
                host = db->host;
 
-       status = udb_query_prepare_result (q, host, "postgresql",
-                       db->database, column_names, (size_t) column_num);
+       status = udb_query_prepare_result (q, prep_area, host, "postgresql",
+                       db->database, column_names, (size_t) column_num, db->interval);
        if (0 != status) {
                log_err ("udb_query_prepare_result failed with status %i.",
                                status);
@@ -380,46 +458,52 @@ static int c_psql_exec_query (c_psql_database_t *db, udb_query_t *q)
                if (col < column_num)
                        continue;
 
-               status = udb_query_handle_result (q, column_values);
+               status = udb_query_handle_result (q, prep_area, column_values);
                if (status != 0) {
                        log_err ("udb_query_handle_result failed with status %i.",
                                        status);
                }
        } /* for (row = 0; row < rows_num; ++row) */
 
+       udb_query_finish_result (q, prep_area);
+
        BAIL_OUT (0);
 #undef BAIL_OUT
 } /* c_psql_exec_query */
 
-static int c_psql_read (void)
+static int c_psql_read (user_data_t *ud)
 {
+       c_psql_database_t *db;
+
        int success = 0;
        int i;
 
-       for (i = 0; i < databases_num; ++i) {
-               c_psql_database_t *db = databases + i;
+       if ((ud == NULL) || (ud->data == NULL)) {
+               log_err ("c_psql_read: Invalid user data.");
+               return -1;
+       }
 
-               int j;
+       db = ud->data;
 
-               assert (NULL != db->database);
+       assert (NULL != db->database);
 
-               if (0 != c_psql_check_connection (db))
-                       continue;
+       if (0 != c_psql_check_connection (db))
+               return -1;
 
-               for (j = 0; j < db->queries_num; ++j)
-               {
-                       udb_query_t *q;
+       for (i = 0; i < db->queries_num; ++i)
+       {
+               udb_query_preparation_area_t *prep_area;
+               udb_query_t *q;
 
-                       q = db->queries[j];
+               prep_area = db->q_prep_areas[i];
+               q = db->queries[i];
 
-                       if ((0 != db->server_version)
+               if ((0 != db->server_version)
                                && (udb_query_check_version (q, db->server_version) <= 0))
-                               continue;
-
-                       c_psql_exec_query (db, q);
-               }
+                       continue;
 
-               ++success;
+               if (0 == c_psql_exec_query (db, q, prep_area))
+                       success = 1;
        }
 
        if (! success)
@@ -429,19 +513,7 @@ static int c_psql_read (void)
 
 static int c_psql_shutdown (void)
 {
-       int i;
-
-       if ((NULL == databases) || (0 == databases_num))
-               return 0;
-
-       plugin_unregister_read ("postgresql");
-       plugin_unregister_shutdown ("postgresql");
-
-       for (i = 0; i < databases_num; ++i)
-               c_psql_database_delete (databases + i);
-
-       sfree (databases);
-       databases_num = 0;
+       plugin_unregister_read_group ("postgresql");
 
        udb_query_free (queries, queries_num);
        queries = NULL;
@@ -450,70 +522,6 @@ static int c_psql_shutdown (void)
        return 0;
 } /* c_psql_shutdown */
 
-static int c_psql_init (void)
-{
-       int i;
-
-       if ((NULL == databases) || (0 == databases_num))
-               return 0;
-
-       for (i = 0; i < databases_num; ++i) {
-               c_psql_database_t *db = databases + i;
-
-               char  conninfo[4096];
-               char *buf     = conninfo;
-               int   buf_len = sizeof (conninfo);
-               int   status;
-
-               char *server_host;
-               int   server_version;
-
-               /* this will happen during reinitialization */
-               if (NULL != db->conn) {
-                       c_psql_check_connection (db);
-                       continue;
-               }
-
-               status = ssnprintf (buf, buf_len, "dbname = '%s'", db->database);
-               if (0 < status) {
-                       buf     += status;
-                       buf_len -= status;
-               }
-
-               C_PSQL_PAR_APPEND (buf, buf_len, "host",       db->host);
-               C_PSQL_PAR_APPEND (buf, buf_len, "port",       db->port);
-               C_PSQL_PAR_APPEND (buf, buf_len, "user",       db->user);
-               C_PSQL_PAR_APPEND (buf, buf_len, "password",   db->password);
-               C_PSQL_PAR_APPEND (buf, buf_len, "sslmode",    db->sslmode);
-               C_PSQL_PAR_APPEND (buf, buf_len, "krbsrvname", db->krbsrvname);
-               C_PSQL_PAR_APPEND (buf, buf_len, "service",    db->service);
-
-               db->conn = PQconnectdb (conninfo);
-               if (0 != c_psql_check_connection (db))
-                       continue;
-
-               db->proto_version = PQprotocolVersion (db->conn);
-
-               server_host    = PQhost (db->conn);
-               server_version = PQserverVersion (db->conn);
-               log_info ("Successfully connected to database %s (user %s) "
-                               "at server %s%s%s (server version: %d.%d.%d, "
-                               "protocol version: %d, pid: %d)",
-                               PQdb (db->conn), PQuser (db->conn),
-                               C_PSQL_SOCKET3 (server_host, PQport (db->conn)),
-                               C_PSQL_SERVER_VERSION3 (server_version),
-                               db->proto_version, PQbackendPID (db->conn));
-
-               if (3 > db->proto_version)
-                       log_warn ("Protocol version %d does not support parameters.",
-                                       db->proto_version);
-       }
-
-       plugin_register_read ("postgresql", c_psql_read);
-       plugin_register_shutdown ("postgresql", c_psql_shutdown);
-       return 0;
-} /* c_psql_init */
-
 static int config_set_s (char *name, char **var, const oconfig_item_t *ci)
 {
        if ((0 != ci->children_num) || (1 != ci->values_num)
@@ -527,6 +535,28 @@ static int config_set_s (char *name, char **var, const oconfig_item_t *ci)
        return 0;
 } /* config_set_s */
 
+static int config_set_i (char *name, int *var,
+               const oconfig_item_t *ci, int min)
+{
+       int value;
+
+       if ((0 != ci->children_num) || (1 != ci->values_num)
+                       || (OCONFIG_TYPE_NUMBER != ci->values[0].type)) {
+               log_err ("%s expects a single number argument.", name);
+               return 1;
+       }
+
+       value = (int)ci->values[0].value.number;
+
+       if (value < min) {
+               log_err ("%s expects a number greater or equal to %i.", name, min);
+               return 1;
+       }
+
+       *var = value;
+       return 0;
+} /* config_set_s */
+
 static int config_query_param_add (udb_query_t *q, oconfig_item_t *ci)
 {
        c_psql_user_data_t *data;
@@ -587,6 +617,10 @@ static int c_psql_config_database (oconfig_item_t *ci)
 {
        c_psql_database_t *db;
 
+       char cb_name[DATA_MAX_NAME_LEN];
+       struct timespec cb_interval;
+       user_data_t ud;
+
        int i;
 
        if ((1 != ci->values_num)
@@ -595,7 +629,11 @@ static int c_psql_config_database (oconfig_item_t *ci)
                return 1;
        }
 
+       memset (&ud, 0, sizeof (ud));
+
        db = c_psql_database_new (ci->values[0].value.string);
+       if (db == NULL)
+               return -1;
 
        for (i = 0; i < ci->children_num; ++i) {
                oconfig_item_t *c = ci->children + i;
@@ -617,25 +655,58 @@ static int c_psql_config_database (oconfig_item_t *ci)
                else if (0 == strcasecmp (c->key, "Query"))
                        udb_query_pick_from_list (c, queries, queries_num,
                                        &db->queries, &db->queries_num);
+               else if (0 == strcasecmp (c->key, "Interval"))
+                       config_set_i ("Interval", &db->interval, c, /* min = */ 1);
                else
                        log_warn ("Ignoring unknown config key \"%s\".", c->key);
        }
 
        /* If no `Query' options were given, add the default queries.. */
-       if (db->queries_num == 0)
-       {
+       if (db->queries_num == 0) {
                for (i = 0; i < def_queries_num; i++)
                        udb_query_pick_from_list_by_name (def_queries[i],
                                        queries, queries_num,
                                        &db->queries, &db->queries_num);
        }
 
+       if (db->queries_num > 0) {
+               db->q_prep_areas = (udb_query_preparation_area_t **) calloc (
+                               db->queries_num, sizeof (*db->q_prep_areas));
+
+               if (db->q_prep_areas == NULL) {
+                       log_err ("Out of memory.");
+                       c_psql_database_delete (db);
+                       return -1;
+               }
+       }
+
        for (i = 0; (size_t)i < db->queries_num; ++i) {
                c_psql_user_data_t *data;
                data = udb_query_get_user_data (db->queries[i]);
                if ((data != NULL) && (data->params_num > db->max_params_num))
                        db->max_params_num = data->params_num;
+
+               db->q_prep_areas[i]
+                       = udb_query_allocate_preparation_area (db->queries[i]);
+
+               if (db->q_prep_areas[i] == NULL) {
+                       log_err ("Out of memory.");
+                       c_psql_database_delete (db);
+                       return -1;
+               }
        }
+
+       ud.data = db;
+       ud.free_func = c_psql_database_delete;
+
+       ssnprintf (cb_name, sizeof (cb_name), "postgresql-%s", db->database);
+
+       memset (&cb_interval, 0, sizeof (cb_interval));
+       if (db->interval > 0)
+               cb_interval.tv_sec = (time_t)db->interval;
+
+       plugin_register_complex_read ("postgresql", cb_name, c_psql_read,
+                       /* interval = */ &cb_interval, &ud);
        return 0;
 } /* c_psql_config_database */
 
@@ -679,7 +750,7 @@ static int c_psql_config (oconfig_item_t *ci)
 void module_register (void)
 {
        plugin_register_complex_config ("postgresql", c_psql_config);
-       plugin_register_init ("postgresql", c_psql_init);
+       plugin_register_shutdown ("postgresql", c_psql_shutdown);
 } /* module_register */
 
 /* vim: set sw=4 ts=4 tw=78 noexpandtab : */
index 5664b0c..2f4f01e 100644 (file)
@@ -568,7 +568,8 @@ static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwd
        user_data->data = c;
        ts.tv_sec = interval;
        ts.tv_nsec = (interval - ts.tv_sec) * 1000000000;
-       plugin_register_complex_read(buf, cpy_read_callback, &ts, user_data);
+       plugin_register_complex_read(/* group = */ NULL, buf,
+                       cpy_read_callback, &ts, user_data);
        return cpy_string_to_unicode_or_bytes(buf);
 }
 
index 2512613..a020692 100644 (file)
@@ -408,8 +408,8 @@ static int cr_config_router (oconfig_item_t *ci) /* {{{ */
   user_data.data = router_data;
   user_data.free_func = (void *) cr_free_data;
   if (status == 0)
-    status = plugin_register_complex_read (read_name, cr_read,
-       /* interval = */ NULL, &user_data);
+    status = plugin_register_complex_read (/* group = */ NULL, read_name,
+       cr_read, /* interval = */ NULL, &user_data);
 
   if (status != 0)
     cr_free_data (router_data);
index 00df377..2470761 100644 (file)
@@ -655,8 +655,9 @@ static int csnmp_config_add_host (oconfig_item_t *ci)
   if (hd->interval != 0)
     cb_interval.tv_sec = (time_t) hd->interval;
 
-  status = plugin_register_complex_read (cb_name, csnmp_read_host,
-      /* interval = */ &cb_interval, /* user_data = */ &cb_data);
+  status = plugin_register_complex_read (/* group = */ NULL, cb_name,
+      csnmp_read_host, /* interval = */ &cb_interval,
+      /* user_data = */ &cb_data);
   if (status != 0)
   {
     ERROR ("snmp plugin: Registering complex read function failed.");
index 6b261c7..29fecdf 100644 (file)
@@ -302,11 +302,15 @@ static int ts_config_set_double (double *ret, oconfig_item_t *ci) /* {{{ */
 
 static int ts_destroy (void **user_data) /* {{{ */
 {
+       ts_data_t **data;
+
        if (user_data == NULL)
                return (-EINVAL);
 
-       free (*user_data);
-       *user_data = NULL;
+       data = (ts_data_t **) user_data;
+
+       free (*data);
+       *data = NULL;
 
        return (0);
 } /* }}} int ts_destroy */
index 5f892a4..7d594d8 100644 (file)
@@ -39,13 +39,6 @@ struct udb_result_s
   char   **values;
   size_t   values_num;
 
-  /* Preparation area */
-  const   data_set_t *ds;
-  size_t *instances_pos;
-  size_t *values_pos;
-  char  **instances_buffer;
-  char  **values_buffer;
-
   /* Legacy data */
   int legacy_mode;
   size_t legacy_position;
@@ -69,13 +62,31 @@ struct udb_query_s /* {{{ */
   unsigned int min_version;
   unsigned int max_version;
 
-  /* Preparation area */
+  udb_result_t *results;
+}; /* }}} */
+
+struct udb_result_preparation_area_s /* {{{ */
+{
+  const   data_set_t *ds;
+  size_t *instances_pos;
+  size_t *values_pos;
+  char  **instances_buffer;
+  char  **values_buffer;
+
+  struct udb_result_preparation_area_s *next;
+}; /* }}} */
+typedef struct udb_result_preparation_area_s udb_result_preparation_area_t;
+
+struct udb_query_preparation_area_s /* {{{ */
+{
   size_t column_num;
   char *host;
   char *plugin;
   char *db_name;
 
-  udb_result_t *results;
+  int interval;
+
+  udb_result_preparation_area_t *result_prep_areas;
 }; /* }}} */
 
 /*
@@ -182,43 +193,49 @@ static int udb_config_set_uint (unsigned int *ret_value, /* {{{ */
 /*
  * Legacy result private functions
  */
-static void udb_legacy_result_finish_result (udb_result_t *r) /* {{{ */
+static void udb_legacy_result_finish_result (const udb_result_t const *r, /* {{{ */
+    udb_result_preparation_area_t *prep_area)
 {
-  if (r == NULL)
+  if ((r == NULL) || (prep_area))
     return;
 
   assert (r->legacy_mode == 1);
 
-  r->ds = NULL;
+  prep_area->ds = NULL;
 } /* }}} void udb_legacy_result_finish_result */
 
 static int udb_legacy_result_handle_result (udb_result_t *r, /* {{{ */
-    udb_query_t *q, char **column_values)
+    udb_query_preparation_area_t *q_area,
+    udb_result_preparation_area_t *r_area,
+    const udb_query_t const *q, char **column_values)
 {
   value_list_t vl = VALUE_LIST_INIT;
   value_t value;
   char *value_str;
 
   assert (r->legacy_mode == 1);
-  assert (r->ds != NULL);
-  assert (r->ds->ds_num == 1);
+  assert (r_area->ds != NULL);
+  assert (r_area->ds->ds_num == 1);
 
   vl.values = &value;
   vl.values_len = 1;
 
   value_str = column_values[r->legacy_position];
-  if (0 != parse_value (value_str, &vl.values[0], r->ds->ds[0].type))
+  if (0 != parse_value (value_str, &vl.values[0], r_area->ds->ds[0].type))
   {
     ERROR ("db query utils: udb_legacy_result_handle_result: "
         "Parsing `%s' as %s failed.", value_str,
-        DS_TYPE_TO_STRING (r->ds->ds[0].type));
+        DS_TYPE_TO_STRING (r_area->ds->ds[0].type));
     errno = EINVAL;
     return (-1);
   }
 
-  sstrncpy (vl.host, q->host, sizeof (vl.host));
-  sstrncpy (vl.plugin, q->plugin, sizeof (vl.plugin));
-  sstrncpy (vl.plugin_instance, q->db_name, sizeof (vl.type_instance));
+  if (q_area->interval > 0)
+    vl.interval = q_area->interval;
+
+  sstrncpy (vl.host, q_area->host, sizeof (vl.host));
+  sstrncpy (vl.plugin, q_area->plugin, sizeof (vl.plugin));
+  sstrncpy (vl.plugin_instance, q_area->db_name, sizeof (vl.type_instance));
   sstrncpy (vl.type, r->type, sizeof (vl.type));
 
   if (r->instance_prefix != NULL)
@@ -230,7 +247,8 @@ static int udb_legacy_result_handle_result (udb_result_t *r, /* {{{ */
   return (0);
 } /* }}} int udb_legacy_result_handle_result */
 
-static int udb_legacy_result_prepare_result (udb_result_t *r, /* {{{ */
+static int udb_legacy_result_prepare_result (const udb_result_t const *r, /* {{{ */
+    udb_result_preparation_area_t *prep_area,
     char **column_names, size_t column_num)
 {
   if (r == NULL)
@@ -239,7 +257,7 @@ static int udb_legacy_result_prepare_result (udb_result_t *r, /* {{{ */
   assert (r->legacy_mode == 1);
 
   /* Make sure previous preparations are cleaned up. */
-  udb_legacy_result_finish_result (r);
+  udb_legacy_result_finish_result (r, prep_area);
 
   if (r->legacy_position >= column_num)
   {
@@ -250,8 +268,8 @@ static int udb_legacy_result_prepare_result (udb_result_t *r, /* {{{ */
   }
 
   /* Read `ds' and check number of values {{{ */
-  r->ds = plugin_get_ds (r->type);
-  if (r->ds == NULL)
+  prep_area->ds = plugin_get_ds (r->type);
+  if (prep_area->ds == NULL)
   {
     ERROR ("db query utils: udb_result_prepare_result: Type `%s' is not "
         "known by the daemon. See types.db(5) for details.",
@@ -259,13 +277,13 @@ static int udb_legacy_result_prepare_result (udb_result_t *r, /* {{{ */
     return (-1);
   }
 
-  if (r->ds->ds_num != 1)
+  if (prep_area->ds->ds_num != 1)
   {
     ERROR ("db query utils: udb_result_prepare_result: The type `%s' "
         "requires exactly %i values, but the legacy configuration "
         "requires exactly one!",
         r->type,
-        r->ds->ds_num);
+        prep_area->ds->ds_num);
     return (-1);
   }
   /* }}} */
@@ -342,40 +360,45 @@ static int udb_legacy_result_create (const char *query_name, /* {{{ */
 /*
  * Result private functions
  */
-static int udb_result_submit (udb_result_t *r, udb_query_t *q) /* {{{ */
+static int udb_result_submit (udb_result_t *r, /* {{{ */
+    udb_result_preparation_area_t *r_area,
+    const udb_query_t const *q, udb_query_preparation_area_t *q_area)
 {
   value_list_t vl = VALUE_LIST_INIT;
   size_t i;
 
   assert (r != NULL);
   assert (r->legacy_mode == 0);
-  assert (r->ds != NULL);
-  assert (((size_t) r->ds->ds_num) == r->values_num);
+  assert (r_area->ds != NULL);
+  assert (((size_t) r_area->ds->ds_num) == r->values_num);
 
-  vl.values = (value_t *) calloc (r->ds->ds_num, sizeof (value_t));
+  vl.values = (value_t *) calloc (r_area->ds->ds_num, sizeof (value_t));
   if (vl.values == NULL)
   {
     ERROR ("db query utils: malloc failed.");
     return (-1);
   }
-  vl.values_len = r->ds->ds_num;
+  vl.values_len = r_area->ds->ds_num;
 
   for (i = 0; i < r->values_num; i++)
   {
-    char *value_str = r->values_buffer[i];
+    char *value_str = r_area->values_buffer[i];
 
-    if (0 != parse_value (value_str, &vl.values[i], r->ds->ds[i].type))
+    if (0 != parse_value (value_str, &vl.values[i], r_area->ds->ds[i].type))
     {
       ERROR ("db query utils: udb_result_submit: Parsing `%s' as %s failed.",
-          value_str, DS_TYPE_TO_STRING (r->ds->ds[i].type));
+          value_str, DS_TYPE_TO_STRING (r_area->ds->ds[i].type));
       errno = EINVAL;
       return (-1);
     }
   }
 
-  sstrncpy (vl.host, q->host, sizeof (vl.host));
-  sstrncpy (vl.plugin, q->plugin, sizeof (vl.plugin));
-  sstrncpy (vl.plugin_instance, q->db_name, sizeof (vl.type_instance));
+  if (q_area->interval > 0)
+    vl.interval = q_area->interval;
+
+  sstrncpy (vl.host, q_area->host, sizeof (vl.host));
+  sstrncpy (vl.plugin, q_area->plugin, sizeof (vl.plugin));
+  sstrncpy (vl.plugin_instance, q_area->db_name, sizeof (vl.type_instance));
   sstrncpy (vl.type, r->type, sizeof (vl.type));
 
   /* Set vl.type_instance {{{ */
@@ -392,13 +415,14 @@ static int udb_result_submit (udb_result_t *r, udb_query_t *q) /* {{{ */
     if (r->instance_prefix == NULL)
     {
       strjoin (vl.type_instance, sizeof (vl.type_instance),
-          r->instances_buffer, r->instances_num, "-");
+          r_area->instances_buffer, r->instances_num, "-");
     }
     else
     {
       char tmp[DATA_MAX_NAME_LEN];
 
-      strjoin (tmp, sizeof (tmp), r->instances_buffer, r->instances_num, "-");
+      strjoin (tmp, sizeof (tmp), r_area->instances_buffer,
+          r->instances_num, "-");
       tmp[sizeof (tmp) - 1] = 0;
 
       snprintf (vl.type_instance, sizeof (vl.type_instance), "%s-%s",
@@ -414,74 +438,82 @@ static int udb_result_submit (udb_result_t *r, udb_query_t *q) /* {{{ */
   return (0);
 } /* }}} void udb_result_submit */
 
-static void udb_result_finish_result (udb_result_t *r) /* {{{ */
+static void udb_result_finish_result (const udb_result_t const *r, /* {{{ */
+    udb_result_preparation_area_t *prep_area)
 {
-  if (r == NULL)
+  if ((r == NULL) || (prep_area == NULL))
     return;
 
   if (r->legacy_mode == 1)
   {
-    udb_legacy_result_finish_result (r);
+    udb_legacy_result_finish_result (r, prep_area);
     return;
   }
 
   assert (r->legacy_mode == 0);
 
-  r->ds = NULL;
-  sfree (r->instances_pos);
-  sfree (r->values_pos);
-  sfree (r->instances_buffer);
-  sfree (r->values_buffer);
+  prep_area->ds = NULL;
+  sfree (prep_area->instances_pos);
+  sfree (prep_area->values_pos);
+  sfree (prep_area->instances_buffer);
+  sfree (prep_area->values_buffer);
 } /* }}} void udb_result_finish_result */
 
 static int udb_result_handle_result (udb_result_t *r, /* {{{ */
-    udb_query_t *q, char **column_values)
+    udb_query_preparation_area_t *q_area,
+    udb_result_preparation_area_t *r_area,
+    const udb_query_t const *q, char **column_values)
 {
   size_t i;
 
+  assert (r && q_area && r_area);
+
   if (r->legacy_mode == 1)
-    return (udb_legacy_result_handle_result (r, q, column_values));
+    return (udb_legacy_result_handle_result (r, q_area, r_area,
+          q, column_values));
 
   assert (r->legacy_mode == 0);
 
   for (i = 0; i < r->instances_num; i++)
-    r->instances_buffer[i] = column_values[r->instances_pos[i]];
+    r_area->instances_buffer[i] = column_values[r_area->instances_pos[i]];
 
   for (i = 0; i < r->values_num; i++)
-    r->values_buffer[i] = column_values[r->values_pos[i]];
+    r_area->values_buffer[i] = column_values[r_area->values_pos[i]];
 
-  return udb_result_submit (r, q);
+  return udb_result_submit (r, r_area, q, q_area);
 } /* }}} int udb_result_handle_result */
 
-static int udb_result_prepare_result (udb_result_t *r, /* {{{ */
+static int udb_result_prepare_result (const udb_result_t const *r, /* {{{ */
+    udb_result_preparation_area_t *prep_area,
     char **column_names, size_t column_num)
 {
   size_t i;
 
-  if (r == NULL)
+  if ((r == NULL) || (prep_area == NULL))
     return (-EINVAL);
 
   if (r->legacy_mode == 1)
-    return (udb_legacy_result_prepare_result (r, column_names, column_num));
+    return (udb_legacy_result_prepare_result (r, prep_area,
+          column_names, column_num));
 
   assert (r->legacy_mode == 0);
 
 #define BAIL_OUT(status) \
-  r->ds = NULL; \
-  sfree (r->instances_pos); \
-  sfree (r->values_pos); \
-  sfree (r->instances_buffer); \
-  sfree (r->values_buffer); \
+  prep_area->ds = NULL; \
+  sfree (prep_area->instances_pos); \
+  sfree (prep_area->values_pos); \
+  sfree (prep_area->instances_buffer); \
+  sfree (prep_area->values_buffer); \
   return (status)
 
   /* Make sure previous preparations are cleaned up. */
-  udb_result_finish_result (r);
-  r->instances_pos = NULL;
-  r->values_pos = NULL;
+  udb_result_finish_result (r, prep_area);
+  prep_area->instances_pos = NULL;
+  prep_area->values_pos = NULL;
 
   /* Read `ds' and check number of values {{{ */
-  r->ds = plugin_get_ds (r->type);
-  if (r->ds == NULL)
+  prep_area->ds = plugin_get_ds (r->type);
+  if (prep_area->ds == NULL)
   {
     ERROR ("db query utils: udb_result_prepare_result: Type `%s' is not "
         "known by the daemon. See types.db(5) for details.",
@@ -489,12 +521,12 @@ static int udb_result_prepare_result (udb_result_t *r, /* {{{ */
     BAIL_OUT (-1);
   }
 
-  if (((size_t) r->ds->ds_num) != r->values_num)
+  if (((size_t) prep_area->ds->ds_num) != r->values_num)
   {
     ERROR ("db query utils: udb_result_prepare_result: The type `%s' "
         "requires exactly %i value%s, but the configuration specifies %zu.",
         r->type,
-        r->ds->ds_num, (r->ds->ds_num == 1) ? "" : "s",
+        prep_area->ds->ds_num, (prep_area->ds->ds_num == 1) ? "" : "s",
         r->values_num);
     BAIL_OUT (-1);
   }
@@ -504,30 +536,34 @@ static int udb_result_prepare_result (udb_result_t *r, /* {{{ */
    * r->values_buffer {{{ */
   if (r->instances_num > 0)
   {
-    r->instances_pos = (size_t *) calloc (r->instances_num, sizeof (size_t));
-    if (r->instances_pos == NULL)
+    prep_area->instances_pos
+      = (size_t *) calloc (r->instances_num, sizeof (size_t));
+    if (prep_area->instances_pos == NULL)
     {
       ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
       BAIL_OUT (-ENOMEM);
     }
 
-    r->instances_buffer = (char **) calloc (r->instances_num, sizeof (char *));
-    if (r->instances_buffer == NULL)
+    prep_area->instances_buffer
+      = (char **) calloc (r->instances_num, sizeof (char *));
+    if (prep_area->instances_buffer == NULL)
     {
       ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
       BAIL_OUT (-ENOMEM);
     }
   } /* if (r->instances_num > 0) */
 
-  r->values_pos = (size_t *) calloc (r->values_num, sizeof (size_t));
-  if (r->values_pos == NULL)
+  prep_area->values_pos
+    = (size_t *) calloc (r->values_num, sizeof (size_t));
+  if (prep_area->values_pos == NULL)
   {
     ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
     BAIL_OUT (-ENOMEM);
   }
 
-  r->values_buffer = (char **) calloc (r->values_num, sizeof (char *));
-  if (r->values_buffer == NULL)
+  prep_area->values_buffer
+    = (char **) calloc (r->values_num, sizeof (char *));
+  if (prep_area->values_buffer == NULL)
   {
     ERROR ("db query utils: udb_result_prepare_result: malloc failed.");
     BAIL_OUT (-ENOMEM);
@@ -543,7 +579,7 @@ static int udb_result_prepare_result (udb_result_t *r, /* {{{ */
     {
       if (strcasecmp (r->instances[i], column_names[j]) == 0)
       {
-        r->instances_pos[i] = j;
+        prep_area->instances_pos[i] = j;
         break;
       }
     }
@@ -566,7 +602,7 @@ static int udb_result_prepare_result (udb_result_t *r, /* {{{ */
     {
       if (strcasecmp (r->values[i], column_names[j]) == 0)
       {
-        r->values_pos[i] = j;
+        prep_area->values_pos[i] = j;
         break;
       }
     }
@@ -1029,33 +1065,45 @@ int udb_query_check_version (udb_query_t *q, unsigned int version) /* {{{ */
   return (1);
 } /* }}} int udb_query_check_version */
 
-void udb_query_finish_result (udb_query_t *q) /* {{{ */
+void udb_query_finish_result (const udb_query_t const *q, /* {{{ */
+    udb_query_preparation_area_t *prep_area)
 {
+  udb_result_preparation_area_t *r_area;
   udb_result_t *r;
 
-  if (q == NULL)
+  if ((q == NULL) || (prep_area == NULL))
     return;
 
-  q->column_num = 0;
-  sfree (q->host);
-  sfree (q->plugin);
-  sfree (q->db_name);
+  prep_area->column_num = 0;
+  sfree (prep_area->host);
+  sfree (prep_area->plugin);
+  sfree (prep_area->db_name);
 
-  for (r = q->results; r != NULL; r = r->next)
-    udb_result_finish_result (r);
+  prep_area->interval = -1;
+
+  for (r = q->results, r_area = prep_area->result_prep_areas;
+      r != NULL; r = r->next, r_area = r_area->next)
+  {
+    /* this may happen during error conditions of the caller */
+    if (r_area == NULL)
+      break;
+    udb_result_finish_result (r, r_area);
+  }
 } /* }}} void udb_query_finish_result */
 
-int udb_query_handle_result (udb_query_t *q, char **column_values) /* {{{ */
+int udb_query_handle_result (const udb_query_t const *q, /* {{{ */
+    udb_query_preparation_area_t *prep_area, char **column_values)
 {
+  udb_result_preparation_area_t *r_area;
   udb_result_t *r;
   int success;
   int status;
 
-  if (q == NULL)
+  if ((q == NULL) || (prep_area == NULL))
     return (-EINVAL);
 
-  if ((q->column_num < 1) || (q->host == NULL) || (q->plugin == NULL)
-      || (q->db_name == NULL))
+  if ((prep_area->column_num < 1) || (prep_area->host == NULL)
+      || (prep_area->plugin == NULL) || (prep_area->db_name == NULL))
   {
     ERROR ("db query utils: Query `%s': Query is not prepared; "
         "can't handle result.", q->name);
@@ -1067,19 +1115,21 @@ int udb_query_handle_result (udb_query_t *q, char **column_values) /* {{{ */
   {
     size_t i;
 
-    for (i = 0; i < q->column_num; i++)
+    for (i = 0; i < prep_area->column_num; i++)
     {
       DEBUG ("db query utils: udb_query_handle_result (%s, %s): "
           "column[%zu] = %s;",
-          q->db_name, q->name, i, column_values[i]);
+          prep_area->db_name, q->name, i, column_values[i]);
     }
   } while (0);
 #endif /* }}} */
 
   success = 0;
-  for (r = q->results; r != NULL; r = r->next)
+  for (r = q->results, r_area = prep_area->result_prep_areas;
+      r != NULL; r = r->next, r_area = r_area->next)
   {
-    status = udb_result_handle_result (r, q, column_values);
+    status = udb_result_handle_result (r, prep_area, r_area,
+        q, column_values);
     if (status == 0)
       success++;
   }
@@ -1087,34 +1137,39 @@ int udb_query_handle_result (udb_query_t *q, char **column_values) /* {{{ */
   if (success == 0)
   {
     ERROR ("db query utils: udb_query_handle_result (%s, %s): "
-        "All results failed.", q->db_name, q->name);
+        "All results failed.", prep_area->db_name, q->name);
     return (-1);
   }
 
   return (0);
 } /* }}} int udb_query_handle_result */
 
-int udb_query_prepare_result (udb_query_t *q, /* {{{ */
+int udb_query_prepare_result (const udb_query_t const *q, /* {{{ */
+    udb_query_preparation_area_t *prep_area,
     const char *host, const char *plugin, const char *db_name,
-    char **column_names, size_t column_num)
+    char **column_names, size_t column_num, int interval)
 {
+  udb_result_preparation_area_t *r_area;
   udb_result_t *r;
   int status;
 
-  if (q == NULL)
+  if ((q == NULL) || (prep_area == NULL))
     return (-EINVAL);
 
-  udb_query_finish_result (q);
+  udb_query_finish_result (q, prep_area);
+
+  prep_area->column_num = column_num;
+  prep_area->host = strdup (host);
+  prep_area->plugin = strdup (plugin);
+  prep_area->db_name = strdup (db_name);
 
-  q->column_num = column_num;
-  q->host = strdup (host);
-  q->plugin = strdup (plugin);
-  q->db_name = strdup (db_name);
+  prep_area->interval = interval;
 
-  if ((q->host == NULL) || (q->plugin == NULL) || (q->db_name == NULL))
+  if ((prep_area->host == NULL) || (prep_area->plugin == NULL)
+      || (prep_area->db_name == NULL))
   {
     ERROR ("db query utils: Query `%s': Prepare failed: Out of memory.", q->name);
-    udb_query_finish_result (q);
+    udb_query_finish_result (q, prep_area);
     return (-ENOMEM);
   }
 
@@ -1132,12 +1187,21 @@ int udb_query_prepare_result (udb_query_t *q, /* {{{ */
   } while (0);
 #endif
 
-  for (r = q->results; r != NULL; r = r->next)
+  for (r = q->results, r_area = prep_area->result_prep_areas;
+      r != NULL; r = r->next, r_area = r_area->next)
   {
-    status = udb_result_prepare_result (r, column_names, column_num);
+    if (! r_area)
+    {
+      ERROR ("db query utils: Query `%s': Invalid number of result "
+          "preparation areas.", q->name);
+      udb_query_finish_result (q, prep_area);
+      return (-EINVAL);
+    }
+
+    status = udb_result_prepare_result (r, r_area, column_names, column_num);
     if (status != 0)
     {
-      udb_query_finish_result (q);
+      udb_query_finish_result (q, prep_area);
       return (status);
     }
   }
@@ -1145,4 +1209,72 @@ int udb_query_prepare_result (udb_query_t *q, /* {{{ */
   return (0);
 } /* }}} int udb_query_prepare_result */
 
+udb_query_preparation_area_t *
+udb_query_allocate_preparation_area (udb_query_t *q) /* {{{ */
+{
+  udb_query_preparation_area_t   *q_area;
+  udb_result_preparation_area_t **next_r_area;
+  udb_result_t *r;
+
+  q_area = (udb_query_preparation_area_t *)malloc (sizeof (*q_area));
+  if (q_area == NULL)
+    return NULL;
+
+  memset (q_area, 0, sizeof (*q_area));
+
+  next_r_area = &q_area->result_prep_areas;
+  for (r = q->results; r != NULL; r = r->next)
+  {
+    udb_result_preparation_area_t *r_area;
+
+    r_area = (udb_result_preparation_area_t *)malloc (sizeof (*r_area));
+    if (r_area == NULL)
+    {
+      for (r_area = q_area->result_prep_areas;
+          r_area != NULL; r_area = r_area->next)
+      {
+        free (r_area);
+      }
+      free (q_area);
+      return NULL;
+    }
+
+    memset (r_area, 0, sizeof (*r_area));
+
+    *next_r_area = r_area;
+    next_r_area  = &r_area->next;
+  }
+
+  return (q_area);
+} /* }}} udb_query_preparation_area_t *udb_query_allocate_preparation_area */
+
+void
+udb_query_delete_preparation_area (udb_query_preparation_area_t *q_area) /* {{{ */
+{
+  udb_result_preparation_area_t *r_area;
+
+  if (q_area == NULL)
+    return;
+
+  r_area = q_area->result_prep_areas;
+  while (r_area != NULL)
+  {
+    udb_result_preparation_area_t *area = r_area;
+
+    r_area = r_area->next;
+
+    sfree (area->instances_pos);
+    sfree (area->values_pos);
+    sfree (area->instances_buffer);
+    sfree (area->values_buffer);
+    free (area);
+  }
+
+  sfree (q_area->host);
+  sfree (q_area->plugin);
+  sfree (q_area->db_name);
+
+  free (q_area);
+} /* }}} void udb_query_delete_preparation_area */
+
 /* vim: set sw=2 sts=2 et fdm=marker : */
index 6703e92..fa2b288 100644 (file)
@@ -30,6 +30,9 @@
 struct udb_query_s;
 typedef struct udb_query_s udb_query_t;
 
+struct udb_query_preparation_area_s;
+typedef struct udb_query_preparation_area_s udb_query_preparation_area_t;
+
 typedef int (*udb_query_create_callback_t) (udb_query_t *q,
     oconfig_item_t *ci);
 
@@ -62,11 +65,19 @@ void *udb_query_get_user_data (udb_query_t *q);
  */
 int udb_query_check_version (udb_query_t *q, unsigned int version);
 
-int udb_query_prepare_result (udb_query_t *q,
+int udb_query_prepare_result (const udb_query_t const *q,
+    udb_query_preparation_area_t *prep_area,
     const char *host, const char *plugin, const char *db_name,
-    char **column_names, size_t column_num);
-int udb_query_handle_result (udb_query_t *q, char **column_values);
-void udb_query_finish_result (udb_query_t *q);
+    char **column_names, size_t column_num, int interval);
+int udb_query_handle_result (const udb_query_t const *q,
+    udb_query_preparation_area_t *prep_area, char **column_values);
+void udb_query_finish_result (const udb_query_t const *q,
+    udb_query_preparation_area_t *prep_area);
+
+udb_query_preparation_area_t *
+udb_query_allocate_preparation_area (udb_query_t *q);
+void
+udb_query_delete_preparation_area (udb_query_preparation_area_t *q_area);
 
 #endif /* UTILS_DB_QUERY_H */
 /* vim: set sw=2 sts=2 et : */
index bf5f7e4..6a0c6f0 100644 (file)
@@ -139,17 +139,36 @@ int llist_size (llist_t *l)
        return (l ? l->size : 0);
 }
 
+static int llist_strcmp (llentry_t *e, void *ud)
+{
+       if ((e == NULL) || (ud == NULL))
+               return (-1);
+       return (strcmp (e->key, (const char *)ud));
+}
+
 llentry_t *llist_search (llist_t *l, const char *key)
 {
+       return (llist_search_custom (l, llist_strcmp, (void *)key));
+}
+
+llentry_t *llist_search_custom (llist_t *l,
+               int (*compare) (llentry_t *, void *), void *user_data)
+{
        llentry_t *e;
 
        if (l == NULL)
                return (NULL);
 
-       for (e = l->head; e != NULL; e = e->next)
-               if (strcmp (key, e->key) == 0)
+       e = l->head;
+       while (e != NULL) {
+               llentry_t *next = e->next;
+
+               if (compare (e, user_data) == 0)
                        break;
 
+               e = next;
+       }
+
        return (e);
 }
 
index c3753d8..19d8d94 100644 (file)
@@ -54,6 +54,8 @@ void llist_remove (llist_t *l, llentry_t *e);
 int llist_size (llist_t *l);
 
 llentry_t *llist_search (llist_t *l, const char *key);
+llentry_t *llist_search_custom (llist_t *l,
+               int (*compare) (llentry_t *, void *), void *user_data);
 
 llentry_t *llist_head (llist_t *l);
 llentry_t *llist_tail (llist_t *l);