Merge branch 'ff/modbus'
authorFlorian Forster <octo@leeloo.lan.home.verplant.org>
Mon, 5 Apr 2010 12:10:44 +0000 (14:10 +0200)
committerFlorian Forster <octo@leeloo.lan.home.verplant.org>
Mon, 5 Apr 2010 12:10:44 +0000 (14:10 +0200)
35 files changed:
configure.in
src/apache.c
src/collectd.conf.in
src/collectd.conf.pod
src/csv.c
src/curl.c
src/curl_json.c
src/curl_xml.c
src/dbi.c
src/java.c
src/match_regex.c
src/memcachec.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/tail.c
src/target_scale.c
src/utils_db_query.c
src/utils_db_query.h
src/utils_format_json.c
src/utils_format_json.h
src/utils_llist.c
src/utils_llist.h
src/utils_match.c
src/utils_match.h
src/utils_tail_match.c
src/utils_tail_match.h
src/write_http.c

index 1a63ded..c2cb5d6 100644 (file)
@@ -3881,7 +3881,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 888875c..cd7839d 100644 (file)
@@ -784,6 +784,7 @@ FQDNLookup   true
 #    </Match>
 #    <Match>
 #      Regex "\\<R=local_user\\>"
+#      ExcludeRegex "\\<R=local_user\\>.*mail_spool defer"
 #      DSType "CounterInc"
 #      Type "counter"
 #      Instance "local_user"
index e85856c..9cab554 100644 (file)
@@ -3114,6 +3114,7 @@ L<http://www.postgresql.org/docs/manuals/>.
     </Database>
 
     <Database bar>
+      Interval 300
       Service "service_name"
       Query backend # predefined
       Query rt36_tickets
@@ -3177,7 +3178,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
 
@@ -3315,6 +3317,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
@@ -3998,6 +4005,7 @@ user using (extended) regular expressions, as described in L<regex(7)>.
       </Match>
       <Match>
         Regex "\\<R=local_user\\>"
+        ExcludeRegex "\\<R=local_user\\>.*mail_spool defer"
         DSType "CounterInc"
         Type "counter"
         Instance "local_user"
@@ -4032,6 +4040,13 @@ want to match literal parentheses you need to do the following:
 
   Regex "SPAM \\(Score: (-?[0-9]+\\.[0-9]+)\\)"
 
+=item B<ExcludeRegex> I<regex>
+
+Sets an optional regular expression to use for excluding lines from the match.
+An example which excludes all connections from localhost from the match:
+
+  ExcludeRegex "127\\.0\\.0\\.1"
+
 =item B<DSType> I<Type>
 
 Sets how the values are cumulated. I<Type> is one of:
@@ -4900,6 +4915,12 @@ Match values where the given regular expressions match the various fields of
 the identifier of a value. If multiple regular expressions are given, B<all>
 regexen must match for a value to match.
 
+=item B<Invert> B<false>|B<true>
+
+When set to B<true>, the result of the match is inverted, i.e. all value lists
+where all regular expressions apply are not matched, all other value lists are
+matched. Defaults to B<false>.
+
 =back
 
 Example:
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 bc11c28..a533e14 100644 (file)
@@ -37,6 +37,7 @@ typedef struct web_match_s web_match_t;
 struct web_match_s /* {{{ */
 {
   char *regex;
+  char *exclude_regex;
   int dstype;
   char *type;
   char *instance;
@@ -291,6 +292,8 @@ static int cc_config_add_match (web_page_t *page, /* {{{ */
 
     if (strcasecmp ("Regex", child->key) == 0)
       status = cc_config_add_string ("Regex", &match->regex, child);
+    else if (strcasecmp ("ExcludeRegex", child->key) == 0)
+      status = cc_config_add_string ("ExcludeRegex", &match->exclude_regex, child);
     else if (strcasecmp ("DSType", child->key) == 0)
       status = cc_config_add_match_dstype (&match->dstype, child);
     else if (strcasecmp ("Type", child->key) == 0)
@@ -333,7 +336,8 @@ static int cc_config_add_match (web_page_t *page, /* {{{ */
   if (status != 0)
     return (status);
 
-  match->match = match_create_simple (match->regex, match->dstype);
+  match->match = match_create_simple (match->regex, match->exclude_regex,
+      match->dstype);
   if (match->match == NULL)
   {
     ERROR ("curl plugin: tail_match_add_match_simple failed.");
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 1fca889..1defc18 100644 (file)
@@ -58,6 +58,7 @@ struct mr_match_s
        mr_regex_t *plugin_instance;
        mr_regex_t *type;
        mr_regex_t *type_instance;
+       _Bool invert;
 };
 
 /*
@@ -195,6 +196,8 @@ static int mr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
                return (-ENOMEM);
        }
        memset (m, 0, sizeof (*m));
+       
+       m->invert = 0;
 
        status = 0;
        for (i = 0; i < ci->children_num; i++)
@@ -212,6 +215,8 @@ static int mr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
                        status = mr_config_add_regex (&m->type, child);
                else if (strcasecmp ("TypeInstance", child->key) == 0)
                        status = mr_config_add_regex (&m->type_instance, child);
+               else if (strcasecmp ("Invert", child->key) == 0)
+                       status = cf_util_get_boolean(child, &m->invert);
                else
                {
                        log_err ("The `%s' configuration option is not understood and "
@@ -263,26 +268,34 @@ static int mr_match (const data_set_t __attribute__((unused)) *ds, /* {{{ */
                void **user_data)
 {
        mr_match_t *m;
+       int match_value = FC_MATCH_MATCHES;
+       int nomatch_value = FC_MATCH_NO_MATCH;
 
        if ((user_data == NULL) || (*user_data == NULL))
                return (-1);
 
        m = *user_data;
 
+       if (m->invert)
+       {
+               match_value = FC_MATCH_NO_MATCH;
+               nomatch_value = FC_MATCH_MATCHES;
+       }
+
        if (mr_match_regexen (m->host, vl->host) == FC_MATCH_NO_MATCH)
-               return (FC_MATCH_NO_MATCH);
+               return (nomatch_value);
        if (mr_match_regexen (m->plugin, vl->plugin) == FC_MATCH_NO_MATCH)
-               return (FC_MATCH_NO_MATCH);
+               return (nomatch_value);
        if (mr_match_regexen (m->plugin_instance,
                                vl->plugin_instance) == FC_MATCH_NO_MATCH)
-               return (FC_MATCH_NO_MATCH);
+               return (nomatch_value);
        if (mr_match_regexen (m->type, vl->type) == FC_MATCH_NO_MATCH)
-               return (FC_MATCH_NO_MATCH);
+               return (nomatch_value);
        if (mr_match_regexen (m->type_instance,
                                vl->type_instance) == FC_MATCH_NO_MATCH)
-               return (FC_MATCH_NO_MATCH);
+               return (nomatch_value);
 
-       return (FC_MATCH_MATCHES);
+       return (match_value);
 } /* }}} int mr_match */
 
 void module_register (void)
index 451a853..d066501 100644 (file)
@@ -37,6 +37,7 @@ typedef struct web_match_s web_match_t;
 struct web_match_s /* {{{ */
 {
   char *regex;
+  char *exclude_regex;
   int dstype;
   char *type;
   char *instance;
@@ -220,6 +221,8 @@ static int cmc_config_add_match (web_page_t *page, /* {{{ */
 
     if (strcasecmp ("Regex", child->key) == 0)
       status = cmc_config_add_string ("Regex", &match->regex, child);
+    else if (strcasecmp ("ExcludeRegex", child->key) == 0)
+      status = cmc_config_add_string ("ExcludeRegex", &match->exclude_regex, child);
     else if (strcasecmp ("DSType", child->key) == 0)
       status = cmc_config_add_match_dstype (&match->dstype, child);
     else if (strcasecmp ("Type", child->key) == 0)
@@ -262,7 +265,8 @@ static int cmc_config_add_match (web_page_t *page, /* {{{ */
   if (status != 0)
     return (status);
 
-  match->match = match_create_simple (match->regex, match->dstype);
+  match->match = match_create_simple (match->regex, match->exclude_regex,
+      match->dstype);
   if (match->match == NULL)
   {
     ERROR ("memcachec plugin: tail_match_add_match_simple failed.");
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 8becc05..25cbcd4 100644 (file)
@@ -30,6 +30,7 @@
  *     Instance "exim"
  *     <Match>
  *       Regex "S=([1-9][0-9]*)"
+ *       ExcludeRegex "U=root.*S="
  *       DSType "CounterAdd"
  *       Type "ipt_bytes"
  *       Instance "total"
@@ -41,6 +42,7 @@
 struct ctail_config_match_s
 {
   char *regex;
+  char *excluderegex;
   int flags;
   char *type;
   char *type_instance;
@@ -157,6 +159,9 @@ static int ctail_config_add_match (cu_tail_match_t *tm,
 
     if (strcasecmp ("Regex", option->key) == 0)
       status = ctail_config_add_string ("Regex", &cm.regex, option);
+    else if (strcasecmp ("ExcludeRegex", option->key) == 0)
+      status = ctail_config_add_string ("ExcludeRegex", &cm.excluderegex,
+                                       option);
     else if (strcasecmp ("DSType", option->key) == 0)
       status = ctail_config_add_match_dstype (&cm, option);
     else if (strcasecmp ("Type", option->key) == 0)
@@ -201,8 +206,8 @@ static int ctail_config_add_match (cu_tail_match_t *tm,
 
   if (status == 0)
   {
-    status = tail_match_add_match_simple (tm, cm.regex, cm.flags,
-       "tail", plugin_instance, cm.type, cm.type_instance);
+    status = tail_match_add_match_simple (tm, cm.regex, cm.excluderegex,
+       cm.flags, "tail", plugin_instance, cm.type, cm.type_instance);
 
     if (status != 0)
     {
@@ -211,6 +216,7 @@ static int ctail_config_add_match (cu_tail_match_t *tm,
   }
 
   sfree (cm.regex);
+  sfree (cm.excluderegex);
   sfree (cm.type);
   sfree (cm.type_instance);
 
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 75a73ae..ac88c0f 100644 (file)
@@ -23,6 +23,7 @@
 #include "plugin.h"
 #include "common.h"
 
+#include "utils_cache.h"
 #include "utils_format_json.h"
 
 static int escape_string (char *buffer, size_t buffer_size, /* {{{ */
@@ -72,10 +73,11 @@ static int escape_string (char *buffer, size_t buffer_size, /* {{{ */
 } /* }}} int buffer_add_string */
 
 static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */
-                const data_set_t *ds, const value_list_t *vl)
+                const data_set_t *ds, const value_list_t *vl, int store_rates)
 {
   size_t offset = 0;
   int i;
+  gauge_t *rates = NULL;
 
   memset (buffer, 0, buffer_size);
 
@@ -84,11 +86,17 @@ static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */
   status = ssnprintf (buffer + offset, buffer_size - offset, \
       __VA_ARGS__); \
   if (status < 1) \
+  { \
+    sfree(rates); \
     return (-1); \
+  } \
   else if (((size_t) status) >= (buffer_size - offset)) \
+  { \
+    sfree(rates); \
     return (-ENOMEM); \
+  } \
   else \
-  offset += ((size_t) status); \
+    offset += ((size_t) status); \
 } while (0)
 
   BUFFER_ADD ("[");
@@ -104,6 +112,22 @@ static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */
       else
         BUFFER_ADD ("null");
     }
+    else if (store_rates)
+    {
+      if (rates == NULL)
+        rates = uc_get_rate (ds, vl);
+      if (rates == NULL)
+      {
+        WARNING ("utils_format_json: uc_get_rate failed.");
+        sfree(rates);
+        return (-1);
+      }
+
+      if(isfinite (rates[i]))
+        BUFFER_ADD ("%g", rates[i]);
+      else
+        BUFFER_ADD ("null");
+    }
     else if (ds->ds[i].type == DS_TYPE_COUNTER)
       BUFFER_ADD ("%llu", vl->values[i].counter);
     else if (ds->ds[i].type == DS_TYPE_DERIVE)
@@ -114,6 +138,7 @@ static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */
     {
       ERROR ("format_json: Unknown data source type: %i",
           ds->ds[i].type);
+      sfree (rates);
       return (-1);
     }
   } /* for ds->ds_num */
@@ -122,13 +147,87 @@ static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */
 #undef BUFFER_ADD
 
   DEBUG ("format_json: values_to_json: buffer = %s;", buffer);
-
+  sfree(rates);
   return (0);
 } /* }}} int values_to_json */
 
-static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */
+static int dstypes_to_json (char *buffer, size_t buffer_size, /* {{{ */
+                const data_set_t *ds, const value_list_t *vl)
+{
+  size_t offset = 0;
+  int i;
+
+  memset (buffer, 0, buffer_size);
+
+#define BUFFER_ADD(...) do { \
+  int status; \
+  status = ssnprintf (buffer + offset, buffer_size - offset, \
+      __VA_ARGS__); \
+  if (status < 1) \
+    return (-1); \
+  else if (((size_t) status) >= (buffer_size - offset)) \
+    return (-ENOMEM); \
+  else \
+    offset += ((size_t) status); \
+} while (0)
+
+  BUFFER_ADD ("[");
+  for (i = 0; i < ds->ds_num; i++)
+  {
+    if (i > 0)
+      BUFFER_ADD (",");
+
+    BUFFER_ADD ("\"%s\"", DS_TYPE_TO_STRING (ds->ds[i].type));
+  } /* for ds->ds_num */
+  BUFFER_ADD ("]");
+
+#undef BUFFER_ADD
+
+  DEBUG ("format_json: dstypes_to_json: buffer = %s;", buffer);
+
+  return (0);
+} /* }}} int dstypes_to_json */
+
+static int dsnames_to_json (char *buffer, size_t buffer_size, /* {{{ */
                 const data_set_t *ds, const value_list_t *vl)
 {
+  size_t offset = 0;
+  int i;
+
+  memset (buffer, 0, buffer_size);
+
+#define BUFFER_ADD(...) do { \
+  int status; \
+  status = ssnprintf (buffer + offset, buffer_size - offset, \
+      __VA_ARGS__); \
+  if (status < 1) \
+    return (-1); \
+  else if (((size_t) status) >= (buffer_size - offset)) \
+    return (-ENOMEM); \
+  else \
+    offset += ((size_t) status); \
+} while (0)
+
+  BUFFER_ADD ("[");
+  for (i = 0; i < ds->ds_num; i++)
+  {
+    if (i > 0)
+      BUFFER_ADD (",");
+
+    BUFFER_ADD ("\"%s\"", ds->ds[i].name);
+  } /* for ds->ds_num */
+  BUFFER_ADD ("]");
+
+#undef BUFFER_ADD
+
+  DEBUG ("format_json: dsnames_to_json: buffer = %s;", buffer);
+
+  return (0);
+} /* }}} int dsnames_to_json */
+
+static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */
+                const data_set_t *ds, const value_list_t *vl, int store_rates)
+{
   char temp[512];
   size_t offset = 0;
   int status;
@@ -150,11 +249,21 @@ static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */
    * a square bracket in `format_json_finalize'. */
   BUFFER_ADD (",{");
 
-  status = values_to_json (temp, sizeof (temp), ds, vl);
+  status = values_to_json (temp, sizeof (temp), ds, vl, store_rates);
   if (status != 0)
     return (status);
   BUFFER_ADD ("\"values\":%s", temp);
 
+  status = dstypes_to_json (temp, sizeof (temp), ds, vl);
+  if (status != 0)
+    return (status);
+  BUFFER_ADD (",\"dstypes\":%s", temp);
+
+  status = dsnames_to_json (temp, sizeof (temp), ds, vl);
+  if (status != 0)
+    return (status);
+  BUFFER_ADD (",\"dsnames\":%s", temp);
+
   BUFFER_ADD (",\"time\":%lu", (unsigned long) vl->time);
   BUFFER_ADD (",\"interval\":%i", vl->interval);
 
@@ -184,12 +293,12 @@ static int value_list_to_json (char *buffer, size_t buffer_size, /* {{{ */
 static int format_json_value_list_nocheck (char *buffer, /* {{{ */
     size_t *ret_buffer_fill, size_t *ret_buffer_free,
     const data_set_t *ds, const value_list_t *vl,
-    size_t temp_size)
+    int store_rates, size_t temp_size)
 {
   char temp[temp_size];
   int status;
 
-  status = value_list_to_json (temp, sizeof (temp), ds, vl);
+  status = value_list_to_json (temp, sizeof (temp), ds, vl, store_rates);
   if (status != 0)
     return (status);
   temp_size = strlen (temp);
@@ -255,7 +364,7 @@ int format_json_finalize (char *buffer, /* {{{ */
 
 int format_json_value_list (char *buffer, /* {{{ */
     size_t *ret_buffer_fill, size_t *ret_buffer_free,
-    const data_set_t *ds, const value_list_t *vl)
+    const data_set_t *ds, const value_list_t *vl, int store_rates)
 {
   if ((buffer == NULL)
       || (ret_buffer_fill == NULL) || (ret_buffer_free == NULL)
@@ -267,7 +376,7 @@ int format_json_value_list (char *buffer, /* {{{ */
 
   return (format_json_value_list_nocheck (buffer,
         ret_buffer_fill, ret_buffer_free, ds, vl,
-        (*ret_buffer_free) - 2));
+        store_rates, (*ret_buffer_free) - 2));
 } /* }}} int format_json_value_list */
 
 /* vim: set sw=2 sts=2 et fdm=marker : */
index 880a79d..c902e27 100644 (file)
@@ -29,7 +29,7 @@ int format_json_initialize (char *buffer,
     size_t *ret_buffer_fill, size_t *ret_buffer_free);
 int format_json_value_list (char *buffer,
     size_t *ret_buffer_fill, size_t *ret_buffer_free,
-    const data_set_t *ds, const value_list_t *vl);
+    const data_set_t *ds, const value_list_t *vl, int store_rates);
 int format_json_finalize (char *buffer,
     size_t *ret_buffer_fill, size_t *ret_buffer_free);
 
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);
index bdbad3f..0f87bc0 100644 (file)
 #include <regex.h>
 
 #define UTILS_MATCH_FLAGS_FREE_USER_DATA 0x01
+#define UTILS_MATCH_FLAGS_EXCLUDE_REGEX 0x02
 
 struct cu_match_s
 {
   regex_t regex;
+  regex_t excluderegex;
   int flags;
 
   int (*callback) (const char *str, char * const *matches, size_t matches_num,
@@ -210,7 +212,7 @@ static int default_callback (const char __attribute__((unused)) *str,
 /*
  * Public functions
  */
-cu_match_t *match_create_callback (const char *regex,
+cu_match_t *match_create_callback (const char *regex, const char *excluderegex,
                int (*callback) (const char *str,
                  char * const *matches, size_t matches_num, void *user_data),
                void *user_data)
@@ -218,7 +220,8 @@ cu_match_t *match_create_callback (const char *regex,
   cu_match_t *obj;
   int status;
 
-  DEBUG ("utils_match: match_create_callback: regex = %s", regex);
+  DEBUG ("utils_match: match_create_callback: regex = %s, excluderegex = %s",
+        regex, excluderegex);
 
   obj = (cu_match_t *) malloc (sizeof (cu_match_t));
   if (obj == NULL)
@@ -233,13 +236,26 @@ cu_match_t *match_create_callback (const char *regex,
     return (NULL);
   }
 
+  if (excluderegex && strcmp(excluderegex, "") != 0) {
+    status = regcomp (&obj->excluderegex, excluderegex, REG_EXTENDED);
+    if (status != 0)
+    {
+       ERROR ("Compiling the excluding regular expression \"%s\" failed.",
+              excluderegex);
+       sfree (obj);
+       return (NULL);
+    }
+    obj->flags |= UTILS_MATCH_FLAGS_EXCLUDE_REGEX;
+  }
+
   obj->callback = callback;
   obj->user_data = user_data;
 
   return (obj);
 } /* cu_match_t *match_create_callback */
 
-cu_match_t *match_create_simple (const char *regex, int match_ds_type)
+cu_match_t *match_create_simple (const char *regex,
+                                const char *excluderegex, int match_ds_type)
 {
   cu_match_value_t *user_data;
   cu_match_t *obj;
@@ -250,7 +266,8 @@ cu_match_t *match_create_simple (const char *regex, int match_ds_type)
   memset (user_data, '\0', sizeof (cu_match_value_t));
   user_data->ds_type = match_ds_type;
 
-  obj = match_create_callback (regex, default_callback, user_data);
+  obj = match_create_callback (regex, excluderegex,
+                              default_callback, user_data);
   if (obj == NULL)
   {
     sfree (user_data);
@@ -286,6 +303,17 @@ int match_apply (cu_match_t *obj, const char *str)
   if ((obj == NULL) || (str == NULL))
     return (-1);
 
+  if (obj->flags & UTILS_MATCH_FLAGS_EXCLUDE_REGEX) {
+    status = regexec (&obj->excluderegex, str,
+                     STATIC_ARRAY_SIZE (re_match), re_match,
+                     /* eflags = */ 0);
+    /* Regex did match, so exclude this line */
+    if (status == 0) {
+      DEBUG("ExludeRegex matched, don't count that line\n");
+      return (0);
+    }
+  }
+
   status = regexec (&obj->regex, str,
       STATIC_ARRAY_SIZE (re_match), re_match,
       /* eflags = */ 0);
index 5a0c033..9e47d5c 100644 (file)
@@ -82,8 +82,10 @@ typedef struct cu_match_value_s cu_match_value_t;
  *  then only the submatch (the part in the parenthesis) will be passed to the
  *  callback. If there is no submatch, then the entire string is passed to the
  *  callback.
+ *  The optional `excluderegex' allows to exclude the line from the match, if
+ *  the excluderegex matches.
  */
-cu_match_t *match_create_callback (const char *regex,
+cu_match_t *match_create_callback (const char *regex, const char *excluderegex,
                int (*callback) (const char *str,
                  char * const *matches, size_t matches_num, void *user_data),
                void *user_data);
@@ -112,7 +114,8 @@ cu_match_t *match_create_callback (const char *regex,
  *    The function will not search for anything in the string and increase
  *    value.counter by one.
  */
-cu_match_t *match_create_simple (const char *regex, int ds_type);
+cu_match_t *match_create_simple (const char *regex,
+                                const char *excluderegex, int ds_type);
 
 /*
  * NAME
index 22c7917..8ae2208 100644 (file)
@@ -191,7 +191,7 @@ int tail_match_add_match (cu_tail_match_t *obj, cu_match_t *match,
 } /* int tail_match_add_match */
 
 int tail_match_add_match_simple (cu_tail_match_t *obj,
-    const char *regex, int ds_type,
+    const char *regex, const char *excluderegex, int ds_type,
     const char *plugin, const char *plugin_instance,
     const char *type, const char *type_instance)
 {
@@ -199,7 +199,7 @@ int tail_match_add_match_simple (cu_tail_match_t *obj,
   cu_tail_match_simple_t *user_data;
   int status;
 
-  match = match_create_simple (regex, ds_type);
+  match = match_create_simple (regex, excluderegex, ds_type);
   if (match == NULL)
     return (-1);
 
index d08c728..7659745 100644 (file)
@@ -97,12 +97,13 @@ int tail_match_add_match (cu_tail_match_t *obj, cu_match_t *match,
  *  The values gathered are dispatched by the tail_match module in this case. The
  *  passed `plugin', `plugin_instance', `type', and `type_instance' are
  *  directly used when submitting these values.
+ *  With excluderegex it is possible to exlude lines from the match.
  *
  * RETURN VALUE
  *   Zero upon success, non-zero otherwise.
  */
 int tail_match_add_match_simple (cu_tail_match_t *obj,
-    const char *regex, int ds_type,
+    const char *regex, const char *excluderegex, int ds_type,
     const char *plugin, const char *plugin_instance,
     const char *type, const char *type_instance);
 
index 8c6a1b5..ab8757e 100644 (file)
@@ -304,12 +304,10 @@ static int wh_value_list_to_string (char *buffer, /* {{{ */
         BUFFER_ADD ("%lu", (unsigned long) vl->time);
 
         for (i = 0; i < ds->ds_num; i++)
-{
-        if (ds->ds[i].type == DS_TYPE_GAUGE)
-                BUFFER_ADD (":%f", vl->values[i].gauge);
-        else if (ds->ds[i].type == DS_TYPE_COUNTER)
         {
-                if (cb->store_rates != 0) 
+                if (ds->ds[i].type == DS_TYPE_GAUGE)
+                        BUFFER_ADD (":%f", vl->values[i].gauge);
+                else if (cb->store_rates)
                 {
                         if (rates == NULL)
                                 rates = uc_get_rate (ds, vl);
@@ -319,27 +317,27 @@ static int wh_value_list_to_string (char *buffer, /* {{{ */
                                                 "uc_get_rate failed.");
                                 return (-1);
                         }
-                        BUFFER_ADD (":%lf", rates[i]);
+                        BUFFER_ADD (":%g", rates[i]);
                 }
-                else
+                else if (ds->ds[i].type == DS_TYPE_COUNTER)
                         BUFFER_ADD (":%llu", vl->values[i].counter);
-        }
-        else if (ds->ds[i].type == DS_TYPE_DERIVE)
-                BUFFER_ADD (":%"PRIi64, vl->values[i].derive);
-        else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
-                BUFFER_ADD (":%"PRIu64, vl->values[i].absolute);
-        else
-        {
-                ERROR ("write_http plugin: Unknown data source type: %i",
-                                ds->ds[i].type);
-                return (-1);
-        }
-} /* for ds->ds_num */
+                else if (ds->ds[i].type == DS_TYPE_DERIVE)
+                        BUFFER_ADD (":%"PRIi64, vl->values[i].derive);
+                else if (ds->ds[i].type == DS_TYPE_ABSOLUTE)
+                        BUFFER_ADD (":%"PRIu64, vl->values[i].absolute);
+                else
+                {
+                        ERROR ("write_http plugin: Unknown data source type: %i",
+                                        ds->ds[i].type);
+                        sfree (rates);
+                        return (-1);
+                }
+        } /* for ds->ds_num */
 
 #undef BUFFER_ADD
 
-sfree (rates);
-return (0);
+        sfree (rates);
+        return (0);
 } /* }}} int wh_value_list_to_string */
 
 static int wh_write_command (const data_set_t *ds, const value_list_t *vl, /* {{{ */
@@ -448,7 +446,7 @@ static int wh_write_json (const data_set_t *ds, const value_list_t *vl, /* {{{ *
         status = format_json_value_list (cb->send_buffer,
                         &cb->send_buffer_fill,
                         &cb->send_buffer_free,
-                        ds, vl);
+                        ds, vl, cb->store_rates);
         if (status == (-ENOMEM))
         {
                 status = wh_flush_nolock (/* timeout = */ -1, cb);
@@ -462,7 +460,7 @@ static int wh_write_json (const data_set_t *ds, const value_list_t *vl, /* {{{ *
                 status = format_json_value_list (cb->send_buffer,
                                 &cb->send_buffer_fill,
                                 &cb->send_buffer_free,
-                                ds, vl);
+                                ds, vl, cb->store_rates);
         }
         if (status != 0)
         {