Merge branch 'collectd-5.7' into collectd-5.8
[collectd.git] / src / match_regex.c
index cd63016..dd4018b 100644 (file)
 
 #include "collectd.h"
 
+#include "common.h"
 #include "filter_chain.h"
+#include "meta_data.h"
+#include "utils_llist.h"
 
-#include <sys/types.h>
 #include <regex.h>
+#include <sys/types.h>
 
-#define log_err(...) ERROR ("`regex' match: " __VA_ARGS__)
-#define log_warn(...) WARNING ("`regex' match: " __VA_ARGS__)
+#define log_err(...) ERROR("`regex' match: " __VA_ARGS__)
+#define log_warn(...) WARNING("`regex' match: " __VA_ARGS__)
 
 /*
  * private data types
 
 struct mr_regex_s;
 typedef struct mr_regex_s mr_regex_t;
-struct mr_regex_s
-{
-       regex_t re;
-       char *re_str;
+struct mr_regex_s {
+  regex_t re;
+  char *re_str;
 
-       mr_regex_t *next;
+  mr_regex_t *next;
 };
 
 struct mr_match_s;
 typedef struct mr_match_s mr_match_t;
-struct mr_match_s
-{
-       mr_regex_t *host;
-       mr_regex_t *plugin;
-       mr_regex_t *plugin_instance;
-       mr_regex_t *type;
-       mr_regex_t *type_instance;
-       _Bool invert;
+struct mr_match_s {
+  mr_regex_t *host;
+  mr_regex_t *plugin;
+  mr_regex_t *plugin_instance;
+  mr_regex_t *type;
+  mr_regex_t *type_instance;
+  llist_t *meta; /* Maps each meta key into mr_regex_t* */
+  _Bool invert;
 };
 
 /*
  * internal helper functions
  */
-static void mr_free_regex (mr_regex_t *r) /* {{{ */
+static void mr_free_regex(mr_regex_t *r) /* {{{ */
 {
-       if (r == NULL)
-               return;
+  if (r == NULL)
+    return;
 
-       regfree (&r->re);
-       memset (&r->re, 0, sizeof (r->re));
-       free (r->re_str);
+  regfree(&r->re);
+  memset(&r->re, 0, sizeof(r->re));
+  sfree(r->re_str);
 
-       if (r->next != NULL)
-               mr_free_regex (r->next);
+  if (r->next != NULL)
+    mr_free_regex(r->next);
 } /* }}} void mr_free_regex */
 
-static void mr_free_match (mr_match_t *m) /* {{{ */
+static void mr_free_match(mr_match_t *m) /* {{{ */
 {
-       if (m == NULL)
-               return;
-
-       mr_free_regex (m->host);
-       mr_free_regex (m->plugin);
-       mr_free_regex (m->plugin_instance);
-       mr_free_regex (m->type);
-       mr_free_regex (m->type_instance);
-
-       free (m);
+  if (m == NULL)
+    return;
+
+  mr_free_regex(m->host);
+  mr_free_regex(m->plugin);
+  mr_free_regex(m->plugin_instance);
+  mr_free_regex(m->type);
+  mr_free_regex(m->type_instance);
+  for (llentry_t *e = llist_head(m->meta); e != NULL; e = e->next) {
+    sfree(e->key);
+    mr_free_regex((mr_regex_t *)e->value);
+  }
+  llist_destroy(m->meta);
+
+  sfree(m);
 } /* }}} void mr_free_match */
 
-static int mr_match_regexen (mr_regex_t *re_head, /* {{{ */
-               const char *string)
-{
-       if (re_head == NULL)
-               return (FC_MATCH_MATCHES);
-
-       for (mr_regex_t *re = re_head; re != NULL; re = re->next)
-       {
-               int status;
-
-               status = regexec (&re->re, string,
-                               /* nmatch = */ 0, /* pmatch = */ NULL,
-                               /* eflags = */ 0);
-               if (status == 0)
-               {
-                       DEBUG ("regex match: Regular expression `%s' matches `%s'.",
-                                       re->re_str, string);
-               }
-               else
-               {
-                       DEBUG ("regex match: Regular expression `%s' does not match `%s'.",
-                                       re->re_str, string);
-                       return (FC_MATCH_NO_MATCH);
-               }
-
-       }
-
-       return (FC_MATCH_MATCHES);
+static int mr_match_regexen(mr_regex_t *re_head, /* {{{ */
+                            const char *string) {
+  if (re_head == NULL)
+    return FC_MATCH_MATCHES;
+
+  for (mr_regex_t *re = re_head; re != NULL; re = re->next) {
+    int status;
+
+    status = regexec(&re->re, string,
+                     /* nmatch = */ 0, /* pmatch = */ NULL,
+                     /* eflags = */ 0);
+    if (status == 0) {
+      DEBUG("regex match: Regular expression `%s' matches `%s'.", re->re_str,
+            string);
+    } else {
+      DEBUG("regex match: Regular expression `%s' does not match `%s'.",
+            re->re_str, string);
+      return FC_MATCH_NO_MATCH;
+    }
+  }
+
+  return FC_MATCH_MATCHES;
 } /* }}} int mr_match_regexen */
 
-static int mr_config_add_regex (mr_regex_t **re_head, /* {{{ */
-               oconfig_item_t *ci)
-{
-       mr_regex_t *re;
-       int status;
-
-       if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
-       {
-               log_warn ("`%s' needs exactly one string argument.", ci->key);
-               return (-1);
-       }
-
-       re = calloc (1, sizeof (*re));
-       if (re == NULL)
-       {
-               log_err ("mr_config_add_regex: calloc failed.");
-               return (-1);
-       }
-       re->next = NULL;
-
-       re->re_str = strdup (ci->values[0].value.string);
-       if (re->re_str == NULL)
-       {
-               free (re);
-               log_err ("mr_config_add_regex: strdup failed.");
-               return (-1);
-       }
-
-       status = regcomp (&re->re, re->re_str, REG_EXTENDED | REG_NOSUB);
-       if (status != 0)
-       {
-               char errmsg[1024];
-               regerror (status, &re->re, errmsg, sizeof (errmsg));
-               errmsg[sizeof (errmsg) - 1] = 0;
-               log_err ("Compiling regex `%s' for `%s' failed: %s.",
-                               re->re_str, ci->key, errmsg);
-               free (re->re_str);
-               free (re);
-               return (-1);
-       }
-
-       if (*re_head == NULL)
-       {
-               *re_head = re;
-       }
-       else
-       {
-               mr_regex_t *ptr;
-
-               ptr = *re_head;
-               while (ptr->next != NULL)
-                       ptr = ptr->next;
-
-               ptr->next = re;
-       }
-
-       return (0);
+static int mr_add_regex(mr_regex_t **re_head, const char *re_str, /* {{{ */
+                        const char *option) {
+  mr_regex_t *re;
+  int status;
+
+  re = calloc(1, sizeof(*re));
+  if (re == NULL) {
+    log_err("mr_add_regex: calloc failed.");
+    return -1;
+  }
+  re->next = NULL;
+
+  re->re_str = strdup(re_str);
+  if (re->re_str == NULL) {
+    sfree(re);
+    log_err("mr_add_regex: strdup failed.");
+    return -1;
+  }
+
+  status = regcomp(&re->re, re->re_str, REG_EXTENDED | REG_NOSUB);
+  if (status != 0) {
+    char errmsg[1024];
+    regerror(status, &re->re, errmsg, sizeof(errmsg));
+    errmsg[sizeof(errmsg) - 1] = 0;
+    log_err("Compiling regex `%s' for `%s' failed: %s.", re->re_str, option,
+            errmsg);
+    sfree(re->re_str);
+    sfree(re);
+    return -1;
+  }
+
+  if (*re_head == NULL) {
+    *re_head = re;
+  } else {
+    mr_regex_t *ptr;
+
+    ptr = *re_head;
+    while (ptr->next != NULL)
+      ptr = ptr->next;
+
+    ptr->next = re;
+  }
+
+  return 0;
+} /* }}} int mr_add_regex */
+
+static int mr_config_add_regex(mr_regex_t **re_head, /* {{{ */
+                               oconfig_item_t *ci) {
+  if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING)) {
+    log_warn("`%s' needs exactly one string argument.", ci->key);
+    return -1;
+  }
+
+  return mr_add_regex(re_head, ci->values[0].value.string, ci->key);
 } /* }}} int mr_config_add_regex */
 
-static int mr_create (const oconfig_item_t *ci, void **user_data) /* {{{ */
+static int mr_config_add_meta_regex(llist_t **meta, /* {{{ */
+                                    oconfig_item_t *ci) {
+  char *meta_key;
+  llentry_t *entry;
+  mr_regex_t *re_head;
+  int status;
+  char buffer[1024];
+
+  if ((ci->values_num != 2) || (ci->values[0].type != OCONFIG_TYPE_STRING) ||
+      (ci->values[1].type != OCONFIG_TYPE_STRING)) {
+    log_warn("`%s' needs exactly two string arguments.", ci->key);
+    return -1;
+  }
+
+  if (*meta == NULL) {
+    *meta = llist_create();
+    if (*meta == NULL) {
+      log_err("mr_config_add_meta_regex: llist_create failed.");
+      return -1;
+    }
+  }
+
+  meta_key = ci->values[0].value.string;
+  entry = llist_search(*meta, meta_key);
+  if (entry == NULL) {
+    meta_key = strdup(meta_key);
+    if (meta_key == NULL) {
+      log_err("mr_config_add_meta_regex: strdup failed.");
+      return -1;
+    }
+    entry = llentry_create(meta_key, NULL);
+    if (entry == NULL) {
+      log_err("mr_config_add_meta_regex: llentry_create failed.");
+      sfree(meta_key);
+      return -1;
+    }
+    /* meta_key and entry will now be freed by mr_free_match(). */
+    llist_append(*meta, entry);
+  }
+
+  snprintf(buffer, sizeof(buffer), "%s `%s'", ci->key, meta_key);
+  /* Can't pass &entry->value into mr_add_regex, so copy in/out. */
+  re_head = entry->value;
+  status = mr_add_regex(&re_head, ci->values[1].value.string, buffer);
+  if (status == 0) {
+    entry->value = re_head;
+  }
+  return status;
+} /* }}} int mr_config_add_meta_regex */
+
+static int mr_create(const oconfig_item_t *ci, void **user_data) /* {{{ */
 {
-       mr_match_t *m;
-       int status;
-
-       m = calloc (1, sizeof (*m));
-       if (m == NULL)
-       {
-               log_err ("mr_create: calloc failed.");
-               return (-ENOMEM);
-       }
-
-       m->invert = 0;
-
-       status = 0;
-       for (int i = 0; i < ci->children_num; i++)
-       {
-               oconfig_item_t *child = ci->children + i;
-
-               if ((strcasecmp ("Host", child->key) == 0)
-                               || (strcasecmp ("Hostname", child->key) == 0))
-                       status = mr_config_add_regex (&m->host, child);
-               else if (strcasecmp ("Plugin", child->key) == 0)
-                       status = mr_config_add_regex (&m->plugin, child);
-               else if (strcasecmp ("PluginInstance", child->key) == 0)
-                       status = mr_config_add_regex (&m->plugin_instance, child);
-               else if (strcasecmp ("Type", child->key) == 0)
-                       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 "
-                                       "will be ignored.", child->key);
-                       status = 0;
-               }
-
-               if (status != 0)
-                       break;
-       }
-
-       /* Additional sanity-checking */
-       while (status == 0)
-       {
-               if ((m->host == NULL)
-                               && (m->plugin == NULL)
-                               && (m->plugin_instance == NULL)
-                               && (m->type == NULL)
-                               && (m->type_instance == NULL))
-               {
-                       log_err ("No (valid) regular expressions have been configured. "
-                                       "This match will be ignored.");
-                       status = -1;
-               }
-
-               break;
-       }
-
-       if (status != 0)
-       {
-               mr_free_match (m);
-               return (status);
-       }
-
-       *user_data = m;
-       return (0);
+  mr_match_t *m;
+  int status;
+
+  m = calloc(1, sizeof(*m));
+  if (m == NULL) {
+    log_err("mr_create: calloc failed.");
+    return -ENOMEM;
+  }
+
+  m->invert = 0;
+
+  status = 0;
+  for (int i = 0; i < ci->children_num; i++) {
+    oconfig_item_t *child = ci->children + i;
+
+    if ((strcasecmp("Host", child->key) == 0) ||
+        (strcasecmp("Hostname", child->key) == 0))
+      status = mr_config_add_regex(&m->host, child);
+    else if (strcasecmp("Plugin", child->key) == 0)
+      status = mr_config_add_regex(&m->plugin, child);
+    else if (strcasecmp("PluginInstance", child->key) == 0)
+      status = mr_config_add_regex(&m->plugin_instance, child);
+    else if (strcasecmp("Type", child->key) == 0)
+      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("MetaData", child->key) == 0)
+      status = mr_config_add_meta_regex(&m->meta, 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 "
+              "will be ignored.",
+              child->key);
+      status = 0;
+    }
+
+    if (status != 0)
+      break;
+  }
+
+  /* Additional sanity-checking */
+  while (status == 0) {
+    if ((m->host == NULL) && (m->plugin == NULL) &&
+        (m->plugin_instance == NULL) && (m->type == NULL) &&
+        (m->type_instance == NULL) && (m->meta == NULL)) {
+      log_err("No (valid) regular expressions have been configured. "
+              "This match will be ignored.");
+      status = -1;
+    }
+
+    break;
+  }
+
+  if (status != 0) {
+    mr_free_match(m);
+    return status;
+  }
+
+  *user_data = m;
+  return 0;
 } /* }}} int mr_create */
 
-static int mr_destroy (void **user_data) /* {{{ */
+static int mr_destroy(void **user_data) /* {{{ */
 {
-       if ((user_data != NULL) && (*user_data != NULL))
-               mr_free_match (*user_data);
-       return (0);
+  if ((user_data != NULL) && (*user_data != NULL))
+    mr_free_match(*user_data);
+  return 0;
 } /* }}} int mr_destroy */
 
-static int mr_match (const data_set_t __attribute__((unused)) *ds, /* {{{ */
-               const value_list_t *vl,
-               notification_meta_t __attribute__((unused)) **meta,
-               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 (nomatch_value);
-       if (mr_match_regexen (m->plugin, vl->plugin) == FC_MATCH_NO_MATCH)
-               return (nomatch_value);
-       if (mr_match_regexen (m->plugin_instance,
-                               vl->plugin_instance) == FC_MATCH_NO_MATCH)
-               return (nomatch_value);
-       if (mr_match_regexen (m->type, vl->type) == FC_MATCH_NO_MATCH)
-               return (nomatch_value);
-       if (mr_match_regexen (m->type_instance,
-                               vl->type_instance) == FC_MATCH_NO_MATCH)
-               return (nomatch_value);
-
-       return (match_value);
+static int mr_match(const data_set_t __attribute__((unused)) * ds, /* {{{ */
+                    const value_list_t *vl,
+                    notification_meta_t __attribute__((unused)) * *meta,
+                    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 nomatch_value;
+  if (mr_match_regexen(m->plugin, vl->plugin) == FC_MATCH_NO_MATCH)
+    return nomatch_value;
+  if (mr_match_regexen(m->plugin_instance, vl->plugin_instance) ==
+      FC_MATCH_NO_MATCH)
+    return nomatch_value;
+  if (mr_match_regexen(m->type, vl->type) == FC_MATCH_NO_MATCH)
+    return nomatch_value;
+  if (mr_match_regexen(m->type_instance, vl->type_instance) ==
+      FC_MATCH_NO_MATCH)
+    return nomatch_value;
+  if (vl->meta != NULL) {
+    for (llentry_t *e = llist_head(m->meta); e != NULL; e = e->next) {
+      mr_regex_t *meta_re = (mr_regex_t *)e->value;
+      char *value;
+      int status = meta_data_get_string(vl->meta, e->key, &value);
+      if (status == (-ENOENT)) /* key is not present */
+        return nomatch_value;
+      if (status != 0) /* some other problem */
+        continue;      /* error will have already been printed. */
+      if (mr_match_regexen(meta_re, value) == FC_MATCH_NO_MATCH) {
+        sfree(value);
+        return nomatch_value;
+      }
+      sfree(value);
+    }
+  }
+
+  return match_value;
 } /* }}} int mr_match */
 
-void module_register (void)
-{
-       match_proc_t mproc = { 0 };
+void module_register(void) {
+  match_proc_t mproc = {0};
 
-       mproc.create  = mr_create;
-       mproc.destroy = mr_destroy;
-       mproc.match   = mr_match;
-       fc_register_match ("regex", mproc);
+  mproc.create = mr_create;
+  mproc.destroy = mr_destroy;
+  mproc.match = mr_match;
+  fc_register_match("regex", mproc);
 } /* module_register */
-
-/* vim: set sw=4 ts=4 tw=78 noexpandtab fdm=marker : */
-