Merge branch 'collectd-5.7' into collectd-5.8
[collectd.git] / src / match_regex.c
index 9c70898..dd4018b 100644 (file)
 
 #include "collectd.h"
 
+#include "common.h"
 #include "filter_chain.h"
+#include "meta_data.h"
+#include "utils_llist.h"
 
 #include <regex.h>
 #include <sys/types.h>
@@ -62,6 +65,7 @@ struct mr_match_s {
   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;
 };
 
@@ -75,7 +79,7 @@ static void mr_free_regex(mr_regex_t *r) /* {{{ */
 
   regfree(&r->re);
   memset(&r->re, 0, sizeof(r->re));
-  free(r->re_str);
+  sfree(r->re_str);
 
   if (r->next != NULL)
     mr_free_regex(r->next);
@@ -91,14 +95,19 @@ static void mr_free_match(mr_match_t *m) /* {{{ */
   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);
 
-  free(m);
+  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);
+    return FC_MATCH_MATCHES;
 
   for (mr_regex_t *re = re_head; re != NULL; re = re->next) {
     int status;
@@ -112,35 +121,30 @@ static int mr_match_regexen(mr_regex_t *re_head, /* {{{ */
     } else {
       DEBUG("regex match: Regular expression `%s' does not match `%s'.",
             re->re_str, string);
-      return (FC_MATCH_NO_MATCH);
+      return FC_MATCH_NO_MATCH;
     }
   }
 
-  return (FC_MATCH_MATCHES);
+  return FC_MATCH_MATCHES;
 } /* }}} int mr_match_regexen */
 
-static int mr_config_add_regex(mr_regex_t **re_head, /* {{{ */
-                               oconfig_item_t *ci) {
+static int mr_add_regex(mr_regex_t **re_head, const char *re_str, /* {{{ */
+                        const char *option) {
   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);
+    log_err("mr_add_regex: calloc failed.");
+    return -1;
   }
   re->next = NULL;
 
-  re->re_str = strdup(ci->values[0].value.string);
+  re->re_str = strdup(re_str);
   if (re->re_str == NULL) {
-    free(re);
-    log_err("mr_config_add_regex: strdup failed.");
-    return (-1);
+    sfree(re);
+    log_err("mr_add_regex: strdup failed.");
+    return -1;
   }
 
   status = regcomp(&re->re, re->re_str, REG_EXTENDED | REG_NOSUB);
@@ -148,11 +152,11 @@ static int mr_config_add_regex(mr_regex_t **re_head, /* {{{ */
     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,
+    log_err("Compiling regex `%s' for `%s' failed: %s.", re->re_str, option,
             errmsg);
-    free(re->re_str);
-    free(re);
-    return (-1);
+    sfree(re->re_str);
+    sfree(re);
+    return -1;
   }
 
   if (*re_head == NULL) {
@@ -167,9 +171,69 @@ static int mr_config_add_regex(mr_regex_t **re_head, /* {{{ */
     ptr->next = re;
   }
 
-  return (0);
+  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_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;
@@ -178,7 +242,7 @@ static int mr_create(const oconfig_item_t *ci, void **user_data) /* {{{ */
   m = calloc(1, sizeof(*m));
   if (m == NULL) {
     log_err("mr_create: calloc failed.");
-    return (-ENOMEM);
+    return -ENOMEM;
   }
 
   m->invert = 0;
@@ -198,6 +262,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("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 {
@@ -215,7 +281,7 @@ static int mr_create(const oconfig_item_t *ci, void **user_data) /* {{{ */
   while (status == 0) {
     if ((m->host == NULL) && (m->plugin == NULL) &&
         (m->plugin_instance == NULL) && (m->type == NULL) &&
-        (m->type_instance == NULL)) {
+        (m->type_instance == NULL) && (m->meta == NULL)) {
       log_err("No (valid) regular expressions have been configured. "
               "This match will be ignored.");
       status = -1;
@@ -226,18 +292,18 @@ static int mr_create(const oconfig_item_t *ci, void **user_data) /* {{{ */
 
   if (status != 0) {
     mr_free_match(m);
-    return (status);
+    return status;
   }
 
   *user_data = m;
-  return (0);
+  return 0;
 } /* }}} int mr_create */
 
 static int mr_destroy(void **user_data) /* {{{ */
 {
   if ((user_data != NULL) && (*user_data != NULL))
     mr_free_match(*user_data);
-  return (0);
+  return 0;
 } /* }}} int mr_destroy */
 
 static int mr_match(const data_set_t __attribute__((unused)) * ds, /* {{{ */
@@ -249,7 +315,7 @@ static int mr_match(const data_set_t __attribute__((unused)) * ds, /* {{{ */
   int nomatch_value = FC_MATCH_NO_MATCH;
 
   if ((user_data == NULL) || (*user_data == NULL))
-    return (-1);
+    return -1;
 
   m = *user_data;
 
@@ -259,19 +325,35 @@ static int mr_match(const data_set_t __attribute__((unused)) * ds, /* {{{ */
   }
 
   if (mr_match_regexen(m->host, vl->host) == FC_MATCH_NO_MATCH)
-    return (nomatch_value);
+    return nomatch_value;
   if (mr_match_regexen(m->plugin, vl->plugin) == FC_MATCH_NO_MATCH)
-    return (nomatch_value);
+    return nomatch_value;
   if (mr_match_regexen(m->plugin_instance, vl->plugin_instance) ==
       FC_MATCH_NO_MATCH)
-    return (nomatch_value);
+    return nomatch_value;
   if (mr_match_regexen(m->type, vl->type) == FC_MATCH_NO_MATCH)
-    return (nomatch_value);
+    return nomatch_value;
   if (mr_match_regexen(m->type_instance, vl->type_instance) ==
       FC_MATCH_NO_MATCH)
-    return (nomatch_value);
+    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);
+  return match_value;
 } /* }}} int mr_match */
 
 void module_register(void) {
@@ -282,5 +364,3 @@ void module_register(void) {
   mproc.match = mr_match;
   fc_register_match("regex", mproc);
 } /* module_register */
-
-/* vim: set sw=4 ts=4 tw=78 noexpandtab fdm=marker : */