Use ignorelist_t and fix leaks
[collectd.git] / src / procevent.c
index e4f473b..7fab655 100644 (file)
 #include "common.h"
 #include "plugin.h"
 #include "utils_complain.h"
+#include "utils_ignorelist.h"
 
 #include <errno.h>
 #include <pthread.h>
-#include <regex.h>
 #include <stdio.h>
 #include <string.h>
 #include <sys/socket.h>
 #include <stdlib.h>
 #include <string.h>
 
+#include <yajl/yajl_common.h>
+#include <yajl/yajl_gen.h>
+#if HAVE_YAJL_YAJL_VERSION_H
+#include <yajl/yajl_version.h>
+#endif
+#if defined(YAJL_MAJOR) && (YAJL_MAJOR > 1)
+#define HAVE_YAJL_V2 1
+#endif
+
 #define PROCEVENT_EXITED 0
 #define PROCEVENT_STARTED 1
-#define PROCEVENT_FIELDS 3 // pid, status, extra
+#define PROCEVENT_FIELDS 4 // pid, status, extra, timestamp
 #define BUFSIZE 512
 #define PROCDIR "/proc"
-#define PROCEVENT_REGEX_MATCHES 1
+
+#define PROCEVENT_DOMAIN_FIELD "domain"
+#define PROCEVENT_DOMAIN_VALUE "fault"
+#define PROCEVENT_EVENT_ID_FIELD "eventId"
+#define PROCEVENT_EVENT_NAME_FIELD "eventName"
+#define PROCEVENT_EVENT_NAME_DOWN_VALUE "down"
+#define PROCEVENT_EVENT_NAME_UP_VALUE "up"
+#define PROCEVENT_LAST_EPOCH_MICROSEC_FIELD "lastEpochMicrosec"
+#define PROCEVENT_PRIORITY_FIELD "priority"
+#define PROCEVENT_PRIORITY_VALUE "high"
+#define PROCEVENT_REPORTING_ENTITY_NAME_FIELD "reportingEntityName"
+#define PROCEVENT_REPORTING_ENTITY_NAME_VALUE "collectd procevent plugin"
+#define PROCEVENT_SEQUENCE_FIELD "sequence"
+#define PROCEVENT_SEQUENCE_VALUE "0"
+#define PROCEVENT_SOURCE_NAME_FIELD "sourceName"
+#define PROCEVENT_START_EPOCH_MICROSEC_FIELD "startEpochMicrosec"
+#define PROCEVENT_VERSION_FIELD "version"
+#define PROCEVENT_VERSION_VALUE "1.0"
+
+#define PROCEVENT_ALARM_CONDITION_FIELD "alarmCondition"
+#define PROCEVENT_ALARM_INTERFACE_A_FIELD "alarmInterfaceA"
+#define PROCEVENT_EVENT_SEVERITY_FIELD "eventSeverity"
+#define PROCEVENT_EVENT_SEVERITY_CRITICAL_VALUE "CRITICAL"
+#define PROCEVENT_EVENT_SEVERITY_NORMAL_VALUE "NORMAL"
+#define PROCEVENT_EVENT_SOURCE_TYPE_FIELD "eventSourceType"
+#define PROCEVENT_EVENT_SOURCE_TYPE_VALUE "process"
+#define PROCEVENT_FAULT_FIELDS_FIELD "faultFields"
+#define PROCEVENT_FAULT_FIELDS_VERSION_FIELD "faultFieldsVersion"
+#define PROCEVENT_FAULT_FIELDS_VERSION_VALUE "1.0"
+#define PROCEVENT_SPECIFIC_PROBLEM_FIELD "specificProblem"
+#define PROCEVENT_SPECIFIC_PROBLEM_DOWN_VALUE "down"
+#define PROCEVENT_SPECIFIC_PROBLEM_UP_VALUE "up"
+#define PROCEVENT_VF_STATUS_FIELD "vfStatus"
+#define PROCEVENT_VF_STATUS_CRITICAL_VALUE "Ready to terminate"
+#define PROCEVENT_VF_STATUS_NORMAL_VALUE "Active"
 
 /*
  * Private data types
@@ -63,16 +106,12 @@ typedef struct {
   int head;
   int tail;
   int maxLen;
-  int **buffer;
+  long long unsigned int **buffer;
 } circbuf_t;
 
 struct processlist_s {
   char *process;
-  char *process_regex;
-
-  regex_t process_regex_obj;
 
-  uint32_t is_regex;
   uint32_t pid;
 
   struct processlist_s *next;
@@ -82,6 +121,7 @@ typedef struct processlist_s processlist_t;
 /*
  * Private variables
  */
+static ignorelist_t *ignorelist = NULL;
 
 static int procevent_thread_loop = 0;
 static int procevent_thread_error = 0;
@@ -93,6 +133,7 @@ static int nl_sock = -1;
 static int buffer_length;
 static circbuf_t ring;
 static processlist_t *processlist_head = NULL;
+static int event_id = 0;
 
 static const char *config_keys[] = {"BufferLength", "Process", "RegexProcess"};
 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
@@ -101,13 +142,314 @@ static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
  * Private functions
  */
 
+static int gen_message_payload(int state, int pid, char *process,
+                               long long unsigned int timestamp, char **buf) {
+  const unsigned char *buf2;
+  yajl_gen g;
+  char json_str[DATA_MAX_NAME_LEN];
+
+#if !defined(HAVE_YAJL_V2)
+  yajl_gen_config conf = {};
+
+  conf.beautify = 0;
+#endif
+
+#if HAVE_YAJL_V2
+  size_t len;
+  g = yajl_gen_alloc(NULL);
+  yajl_gen_config(g, yajl_gen_beautify, 0);
+#else
+  unsigned int len;
+  g = yajl_gen_alloc(&conf, NULL);
+#endif
+
+  yajl_gen_clear(g);
+
+  // *** BEGIN common event header ***
+
+  if (yajl_gen_map_open(g) != yajl_gen_status_ok)
+    goto err;
+
+  // domain
+  if (yajl_gen_string(g, (u_char *)PROCEVENT_DOMAIN_FIELD,
+                      strlen(PROCEVENT_DOMAIN_FIELD)) != yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_string(g, (u_char *)PROCEVENT_DOMAIN_VALUE,
+                      strlen(PROCEVENT_DOMAIN_VALUE)) != yajl_gen_status_ok)
+    goto err;
+
+  // eventId
+  if (yajl_gen_string(g, (u_char *)PROCEVENT_EVENT_ID_FIELD,
+                      strlen(PROCEVENT_EVENT_ID_FIELD)) != yajl_gen_status_ok)
+    goto err;
+
+  event_id = event_id + 1;
+  int event_id_len = sizeof(char) * sizeof(int) * 4 + 1;
+  memset(json_str, '\0', DATA_MAX_NAME_LEN);
+  snprintf(json_str, event_id_len, "%d", event_id);
+
+  if (yajl_gen_number(g, json_str, strlen(json_str)) != yajl_gen_status_ok) {
+    goto err;
+  }
+
+  // eventName
+  if (yajl_gen_string(g, (u_char *)PROCEVENT_EVENT_NAME_FIELD,
+                      strlen(PROCEVENT_EVENT_NAME_FIELD)) != yajl_gen_status_ok)
+    goto err;
+
+  int event_name_len = 0;
+  event_name_len = event_name_len + (sizeof(char) * sizeof(int) * 4); // pid
+  event_name_len = event_name_len + strlen(process);      // process name
+  event_name_len = event_name_len + (state == 0 ? 4 : 2); // "down" or "up"
+  event_name_len = event_name_len +
+                   13; // "process", 3 spaces, 2 parentheses and null-terminator
+  memset(json_str, '\0', DATA_MAX_NAME_LEN);
+  snprintf(json_str, event_name_len, "process %s (%d) %s", process, pid,
+           (state == 0 ? PROCEVENT_EVENT_NAME_DOWN_VALUE
+                       : PROCEVENT_EVENT_NAME_UP_VALUE));
+
+  if (yajl_gen_string(g, (u_char *)json_str, strlen(json_str)) !=
+      yajl_gen_status_ok) {
+    goto err;
+  }
+
+  // lastEpochMicrosec
+  if (yajl_gen_string(g, (u_char *)PROCEVENT_LAST_EPOCH_MICROSEC_FIELD,
+                      strlen(PROCEVENT_LAST_EPOCH_MICROSEC_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  int last_epoch_microsec_len =
+      sizeof(char) * sizeof(long long unsigned int) * 4 + 1;
+  memset(json_str, '\0', DATA_MAX_NAME_LEN);
+  snprintf(json_str, last_epoch_microsec_len, "%llu",
+           (long long unsigned int)CDTIME_T_TO_US(cdtime()));
+
+  if (yajl_gen_number(g, json_str, strlen(json_str)) != yajl_gen_status_ok) {
+    goto err;
+  }
+
+  // priority
+  if (yajl_gen_string(g, (u_char *)PROCEVENT_PRIORITY_FIELD,
+                      strlen(PROCEVENT_PRIORITY_FIELD)) != yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_string(g, (u_char *)PROCEVENT_PRIORITY_VALUE,
+                      strlen(PROCEVENT_PRIORITY_VALUE)) != yajl_gen_status_ok)
+    goto err;
+
+  // reportingEntityName
+  if (yajl_gen_string(g, (u_char *)PROCEVENT_REPORTING_ENTITY_NAME_FIELD,
+                      strlen(PROCEVENT_REPORTING_ENTITY_NAME_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_string(g, (u_char *)PROCEVENT_REPORTING_ENTITY_NAME_VALUE,
+                      strlen(PROCEVENT_REPORTING_ENTITY_NAME_VALUE)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  // sequence
+  if (yajl_gen_string(g, (u_char *)PROCEVENT_SEQUENCE_FIELD,
+                      strlen(PROCEVENT_SEQUENCE_FIELD)) != yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_number(g, PROCEVENT_SEQUENCE_VALUE,
+                      strlen(PROCEVENT_SEQUENCE_VALUE)) != yajl_gen_status_ok)
+    goto err;
+
+  // sourceName
+  if (yajl_gen_string(g, (u_char *)PROCEVENT_SOURCE_NAME_FIELD,
+                      strlen(PROCEVENT_SOURCE_NAME_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_string(g, (u_char *)process, strlen(process)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  // startEpochMicrosec
+  if (yajl_gen_string(g, (u_char *)PROCEVENT_START_EPOCH_MICROSEC_FIELD,
+                      strlen(PROCEVENT_START_EPOCH_MICROSEC_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  int start_epoch_microsec_len =
+      sizeof(char) * sizeof(long long unsigned int) * 4 + 1;
+  memset(json_str, '\0', DATA_MAX_NAME_LEN);
+  snprintf(json_str, start_epoch_microsec_len, "%llu",
+           (long long unsigned int)timestamp);
+
+  if (yajl_gen_number(g, json_str, strlen(json_str)) != yajl_gen_status_ok) {
+    goto err;
+  }
+
+  // version
+  if (yajl_gen_string(g, (u_char *)PROCEVENT_VERSION_FIELD,
+                      strlen(PROCEVENT_VERSION_FIELD)) != yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_number(g, PROCEVENT_VERSION_VALUE,
+                      strlen(PROCEVENT_VERSION_VALUE)) != yajl_gen_status_ok)
+    goto err;
+
+  // *** END common event header ***
+
+  // *** BEGIN fault fields ***
+
+  if (yajl_gen_string(g, (u_char *)PROCEVENT_FAULT_FIELDS_FIELD,
+                      strlen(PROCEVENT_FAULT_FIELDS_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_map_open(g) != yajl_gen_status_ok)
+    goto err;
+
+  // alarmCondition
+  if (yajl_gen_string(g, (u_char *)PROCEVENT_ALARM_CONDITION_FIELD,
+                      strlen(PROCEVENT_ALARM_CONDITION_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  int alarm_condition_len = 0;
+  alarm_condition_len =
+      alarm_condition_len + (sizeof(char) * sizeof(int) * 4);  // pid
+  alarm_condition_len = alarm_condition_len + strlen(process); // process name
+  alarm_condition_len =
+      alarm_condition_len + 25; // "process", "state", "change", 4 spaces, 2
+                                // parentheses and null-terminator
+  memset(json_str, '\0', DATA_MAX_NAME_LEN);
+  snprintf(json_str, alarm_condition_len, "process %s (%d) state change",
+           process, pid);
+
+  if (yajl_gen_string(g, (u_char *)json_str, strlen(json_str)) !=
+      yajl_gen_status_ok) {
+    goto err;
+  }
+
+  // alarmInterfaceA
+  if (yajl_gen_string(g, (u_char *)PROCEVENT_ALARM_INTERFACE_A_FIELD,
+                      strlen(PROCEVENT_ALARM_INTERFACE_A_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_string(g, (u_char *)process, strlen(process)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  // eventSeverity
+  if (yajl_gen_string(g, (u_char *)PROCEVENT_EVENT_SEVERITY_FIELD,
+                      strlen(PROCEVENT_EVENT_SEVERITY_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_string(
+          g, (u_char *)(state == 0 ? PROCEVENT_EVENT_SEVERITY_CRITICAL_VALUE
+                                   : PROCEVENT_EVENT_SEVERITY_NORMAL_VALUE),
+          strlen((state == 0 ? PROCEVENT_EVENT_SEVERITY_CRITICAL_VALUE
+                             : PROCEVENT_EVENT_SEVERITY_NORMAL_VALUE))) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  // eventSourceType
+  if (yajl_gen_string(g, (u_char *)PROCEVENT_EVENT_SOURCE_TYPE_FIELD,
+                      strlen(PROCEVENT_EVENT_SOURCE_TYPE_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_string(g, (u_char *)PROCEVENT_EVENT_SOURCE_TYPE_VALUE,
+                      strlen(PROCEVENT_EVENT_SOURCE_TYPE_VALUE)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  // faultFieldsVersion
+  if (yajl_gen_string(g, (u_char *)PROCEVENT_FAULT_FIELDS_VERSION_FIELD,
+                      strlen(PROCEVENT_FAULT_FIELDS_VERSION_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_number(g, PROCEVENT_FAULT_FIELDS_VERSION_VALUE,
+                      strlen(PROCEVENT_FAULT_FIELDS_VERSION_VALUE)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  // specificProblem
+  if (yajl_gen_string(g, (u_char *)PROCEVENT_SPECIFIC_PROBLEM_FIELD,
+                      strlen(PROCEVENT_SPECIFIC_PROBLEM_FIELD)) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  int specific_problem_len = 0;
+  specific_problem_len =
+      specific_problem_len + (sizeof(char) * sizeof(int) * 4);   // pid
+  specific_problem_len = specific_problem_len + strlen(process); // process name
+  specific_problem_len =
+      specific_problem_len + (state == 0 ? 4 : 2); // "down" or "up"
+  specific_problem_len =
+      specific_problem_len +
+      13; // "process", 3 spaces, 2 parentheses and null-terminator
+  memset(json_str, '\0', DATA_MAX_NAME_LEN);
+  snprintf(json_str, specific_problem_len, "process %s (%d) %s", process, pid,
+           (state == 0 ? PROCEVENT_SPECIFIC_PROBLEM_DOWN_VALUE
+                       : PROCEVENT_SPECIFIC_PROBLEM_UP_VALUE));
+
+  if (yajl_gen_string(g, (u_char *)json_str, strlen(json_str)) !=
+      yajl_gen_status_ok) {
+    goto err;
+  }
+
+  // vfStatus
+  if (yajl_gen_string(g, (u_char *)PROCEVENT_VF_STATUS_FIELD,
+                      strlen(PROCEVENT_VF_STATUS_FIELD)) != yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_string(
+          g, (u_char *)(state == 0 ? PROCEVENT_VF_STATUS_CRITICAL_VALUE
+                                   : PROCEVENT_VF_STATUS_NORMAL_VALUE),
+          strlen((state == 0 ? PROCEVENT_VF_STATUS_CRITICAL_VALUE
+                             : PROCEVENT_VF_STATUS_NORMAL_VALUE))) !=
+      yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_map_close(g) != yajl_gen_status_ok)
+    goto err;
+
+  // *** END fault fields ***
+
+  if (yajl_gen_map_close(g) != yajl_gen_status_ok)
+    goto err;
+
+  if (yajl_gen_get_buf(g, &buf2, &len) != yajl_gen_status_ok)
+    goto err;
+
+  *buf = malloc(strlen((char *)buf2) + 1);
+
+  if (*buf == NULL) {
+    char errbuf[1024];
+    ERROR("procevent plugin: malloc failed during gen_message_payload: %s",
+          sstrerror(errno, errbuf, sizeof(errbuf)));
+    goto err;
+  }
+
+  sstrncpy(*buf, (char *)buf2, strlen((char *)buf2) + 1);
+
+  yajl_gen_free(g);
+
+  return 0;
+
+err:
+  yajl_gen_free(g);
+  ERROR("procevent plugin: gen_message_payload failed to generate JSON");
+  return -1;
+}
+
 // Does /proc/<pid>/comm contain a process name we are interested in?
 static processlist_t *process_check(int pid) {
-  int len, is_match, status;
+  int len, is_match, retval;
   char file[BUFSIZE];
   FILE *fh;
   char buffer[BUFSIZE];
-  regmatch_t matches[PROCEVENT_REGEX_MATCHES];
 
   len = snprintf(file, sizeof(file), PROCDIR "/%d/comm", pid);
 
@@ -122,15 +464,35 @@ static processlist_t *process_check(int pid) {
     return NULL;
   }
 
-  fscanf(fh, "%[^\n]", buffer);
+  retval = fscanf(fh, "%[^\n]", buffer);
+
+  if (retval < 0) {
+    WARNING("procevent process_check: unable to read comm file for pid %d",
+            pid);
+    fclose(fh);
+    return NULL;
+  }
+
+  // Now that we have the process name in the buffer, check if we are
+  // even interested in it
+  if (ignorelist_match(ignorelist, buffer) != 0) {
+    DEBUG("procevent process_check: ignoring process %s (%d)", buffer, pid);
+    fclose(fh);
+    return NULL;
+  }
+
+  if (fh != NULL) {
+    fclose(fh);
+    fh = NULL;
+  }
 
   //
   // Go through the processlist linked list and look for the process name
   // in /proc/<pid>/comm.  If found:
-  // 1. If pl->pid is -1, then set pl->pid to <pid>
+  // 1. If pl->pid is -1, then set pl->pid to <pid> (and return that object)
   // 2. If pl->pid is not -1, then another <process name> process was already
   //    found.  If <pid> == pl->pid, this is an old match, so do nothing.
-  //    If the <pid> is different, however,  make a new processlist_t and
+  //    If the <pid> is different, however, make a new processlist_t and
   //    associate <pid> with it (with the same process name as the existing).
   //
 
@@ -140,34 +502,11 @@ static processlist_t *process_check(int pid) {
   processlist_t *match = NULL;
 
   for (pl = processlist_head; pl != NULL; pl = pl->next) {
-    if (pl->is_regex != 0) {
-      is_match = (regexec(&pl->process_regex_obj, buffer,
-                          PROCEVENT_REGEX_MATCHES, matches, 0) == 0
-                      ? 1
-                      : 0);
-    } else {
-      is_match = (strcmp(buffer, pl->process) == 0 ? 1 : 0);
-    }
+
+    is_match = (strcmp(buffer, pl->process) == 0 ? 1 : 0);
 
     if (is_match == 1) {
-      DEBUG("procevent plugin: process %d name match (pattern: %s) for %s", pid,
-            (pl->is_regex == 0 ? pl->process : pl->process_regex), buffer);
-
-      if (pl->is_regex == 1) {
-        // If this is a regex name, copy the actual process name into the object
-        // for cleaner log reporting
-
-        if (pl->process != NULL)
-          sfree(pl->process);
-        pl->process = strdup(buffer);
-        if (pl->process == NULL) {
-          char errbuf[1024];
-          ERROR("procevent plugin: strdup failed during process_check: %s",
-                sstrerror(errno, errbuf, sizeof(errbuf)));
-          pthread_mutex_unlock(&procevent_list_lock);
-          return NULL;
-        }
-      }
+      DEBUG("procevent plugin: process %d name match for %s", pid, buffer);
 
       if (pl->pid == pid) {
         // this is a match, and we've already stored the exact pid/name combo
@@ -189,18 +528,19 @@ static processlist_t *process_check(int pid) {
     }
   }
 
-  if (match != NULL && match->pid != -1 && match->pid != pid) {
+  if (match == NULL ||
+      (match != NULL && match->pid != -1 && match->pid != pid)) {
+    // if there wasn't an existing match, OR
     // if there was a match but the associated processlist_t object already
     // contained a pid/name combo,
     // then make a new one and add it to the linked list
 
     DEBUG(
         "procevent plugin: allocating new processlist_t object for PID %d (%s)",
-        pid, match->process);
+        pid, buffer);
 
     processlist_t *pl2;
     char *process;
-    char *process_regex;
 
     pl2 = malloc(sizeof(*pl2));
     if (pl2 == NULL) {
@@ -211,7 +551,7 @@ static processlist_t *process_check(int pid) {
       return NULL;
     }
 
-    process = strdup(match->process);
+    process = strdup(buffer);
     if (process == NULL) {
       char errbuf[1024];
       sfree(pl2);
@@ -221,29 +561,6 @@ static processlist_t *process_check(int pid) {
       return NULL;
     }
 
-    if (match->is_regex == 1) {
-      pl2->is_regex = 1;
-      status =
-          regcomp(&pl2->process_regex_obj, match->process_regex, REG_EXTENDED);
-
-      if (status != 0) {
-        ERROR("procevent plugin: invalid regular expression: %s",
-              match->process_regex);
-        return NULL;
-      }
-
-      process_regex = strdup(match->process_regex);
-      if (process_regex == NULL) {
-        char errbuf[1024];
-        sfree(pl);
-        ERROR("procevent plugin: strdup failed during process_check: %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
-        return NULL;
-      }
-
-      pl2->process_regex = process_regex;
-    }
-
     pl2->process = process;
     pl2->pid = pid;
     pl2->next = processlist_head;
@@ -254,11 +571,6 @@ static processlist_t *process_check(int pid) {
 
   pthread_mutex_unlock(&procevent_list_lock);
 
-  if (fh != NULL) {
-    fclose(fh);
-    fh = NULL;
-  }
-
   return match;
 }
 
@@ -469,38 +781,15 @@ static int read_event() {
 
   switch (nlcn_msg.proc_ev.what) {
   case PROC_EVENT_NONE:
-    // printf("set mcast listen ok\n");
-    break;
   case PROC_EVENT_FORK:
-    // printf("fork: parent tid=%d pid=%d -> child tid=%d pid=%d\n",
-    //         nlcn_msg.proc_ev.event_data.fork.parent_pid,
-    //         nlcn_msg.proc_ev.event_data.fork.parent_tgid,
-    //         nlcn_msg.proc_ev.event_data.fork.child_pid,
-    //         nlcn_msg.proc_ev.event_data.fork.child_tgid);
-    // proc_status = PROCEVENT_STARTED;
-    // proc_id = nlcn_msg.proc_ev.event_data.fork.child_pid;
+  case PROC_EVENT_UID:
+  case PROC_EVENT_GID:
+    // Not of interest in current version
     break;
   case PROC_EVENT_EXEC:
-    // printf("exec: tid=%d pid=%d\n",
-    //         nlcn_msg.proc_ev.event_data.exec.process_pid,
-    //         nlcn_msg.proc_ev.event_data.exec.process_tgid);
     proc_status = PROCEVENT_STARTED;
     proc_id = nlcn_msg.proc_ev.event_data.exec.process_pid;
     break;
-  case PROC_EVENT_UID:
-    // printf("uid change: tid=%d pid=%d from %d to %d\n",
-    //         nlcn_msg.proc_ev.event_data.id.process_pid,
-    //         nlcn_msg.proc_ev.event_data.id.process_tgid,
-    //         nlcn_msg.proc_ev.event_data.id.r.ruid,
-    //         nlcn_msg.proc_ev.event_data.id.e.euid);
-    break;
-  case PROC_EVENT_GID:
-    // printf("gid change: tid=%d pid=%d from %d to %d\n",
-    //         nlcn_msg.proc_ev.event_data.id.process_pid,
-    //         nlcn_msg.proc_ev.event_data.id.process_tgid,
-    //         nlcn_msg.proc_ev.event_data.id.r.rgid,
-    //         nlcn_msg.proc_ev.event_data.id.e.egid);
-    break;
   case PROC_EVENT_EXIT:
     proc_id = nlcn_msg.proc_ev.event_data.exit.process_pid;
     proc_status = PROCEVENT_EXITED;
@@ -523,17 +812,22 @@ static int read_event() {
     if (next == ring.tail) {
       WARNING("procevent plugin: ring buffer full");
     } else {
-      DEBUG("procevent plugin: Process %d status is now %s", proc_id,
-            (proc_status == PROCEVENT_EXITED ? "EXITED" : "STARTED"));
+      DEBUG("procevent plugin: Process %d status is now %s at %llu", proc_id,
+            (proc_status == PROCEVENT_EXITED ? "EXITED" : "STARTED"),
+            (long long unsigned int)CDTIME_T_TO_US(cdtime()));
 
       if (proc_status == PROCEVENT_EXITED) {
         ring.buffer[ring.head][0] = proc_id;
         ring.buffer[ring.head][1] = proc_status;
         ring.buffer[ring.head][2] = proc_extra;
+        ring.buffer[ring.head][3] =
+            (long long unsigned int)CDTIME_T_TO_US(cdtime());
       } else {
         ring.buffer[ring.head][0] = proc_id;
         ring.buffer[ring.head][1] = proc_status;
         ring.buffer[ring.head][2] = 0;
+        ring.buffer[ring.head][3] =
+            (long long unsigned int)CDTIME_T_TO_US(cdtime());
       }
 
       ring.head = next;
@@ -675,18 +969,15 @@ static int procevent_init(void) /* {{{ */
 {
   int status;
 
-  if (processlist_head == NULL) {
-    NOTICE("procevent plugin: No processes have been configured.");
-    return (-1);
-  }
-
   ring.head = 0;
   ring.tail = 0;
   ring.maxLen = buffer_length;
-  ring.buffer = (int **)malloc(buffer_length * sizeof(int *));
+  ring.buffer = (long long unsigned int **)malloc(
+      buffer_length * sizeof(long long unsigned int *));
 
   for (int i = 0; i < buffer_length; i++) {
-    ring.buffer[i] = (int *)malloc(PROCEVENT_FIELDS * sizeof(int));
+    ring.buffer[i] = (long long unsigned int *)malloc(
+        PROCEVENT_FIELDS * sizeof(long long unsigned int));
   }
 
   status = process_map_refresh();
@@ -696,6 +987,11 @@ static int procevent_init(void) /* {{{ */
     return (-1);
   }
 
+  if (processlist_head == NULL) {
+    NOTICE("procevent plugin: No processes have been configured.");
+    return (-1);
+  }
+
   return (start_thread());
 } /* }}} int procevent_init */
 
@@ -703,59 +999,25 @@ static int procevent_config(const char *key, const char *value) /* {{{ */
 {
   int status;
 
+  if (ignorelist == NULL)
+    ignorelist = ignorelist_create(/* invert = */ 1);
+
   if (strcasecmp(key, "BufferLength") == 0) {
     buffer_length = atoi(value);
-  } else if (strcasecmp(key, "Process") == 0 ||
-             strcasecmp(key, "RegexProcess") == 0) {
-
-    processlist_t *pl;
-    char *process;
-    char *process_regex;
-
-    pl = malloc(sizeof(*pl));
-    if (pl == NULL) {
-      char errbuf[1024];
-      ERROR("procevent plugin: malloc failed during procevent_config: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
-      return (1);
-    }
+  } else if (strcasecmp(key, "Process") == 0) {
+    ignorelist_add(ignorelist, value);
+  } else if (strcasecmp(key, "RegexProcess") == 0) {
+#if HAVE_REGEX_H
+    status = ignorelist_add(ignorelist, value);
 
-    process = strdup(value);
-    if (process == NULL) {
-      char errbuf[1024];
-      sfree(pl);
-      ERROR("procevent plugin: strdup failed during procevent_config: %s",
-            sstrerror(errno, errbuf, sizeof(errbuf)));
+    if (status != 0) {
+      ERROR("procevent plugin: invalid regular expression: %s", value);
       return (1);
     }
-
-    if (strcasecmp(key, "RegexProcess") == 0) {
-      pl->is_regex = 1;
-      status = regcomp(&pl->process_regex_obj, value, REG_EXTENDED);
-
-      if (status != 0) {
-        ERROR("procevent plugin: invalid regular expression: %s", value);
-        return (1);
-      }
-
-      process_regex = strdup(value);
-      if (process_regex == NULL) {
-        char errbuf[1024];
-        sfree(pl);
-        ERROR("procevent plugin: strdup failed during procevent_config: %s",
-              sstrerror(errno, errbuf, sizeof(errbuf)));
-        return (1);
-      }
-
-      pl->process_regex = process_regex;
-    } else {
-      pl->is_regex = 0;
-    }
-
-    pl->process = process;
-    pl->pid = -1;
-    pl->next = processlist_head;
-    processlist_head = pl;
+#else
+    WARNING("procevent plugin: The plugin has been compiled without support "
+            "for the \"RegexProcess\" option.");
+#endif
   } else {
     return (-1);
   }
@@ -763,41 +1025,51 @@ static int procevent_config(const char *key, const char *value) /* {{{ */
   return (0);
 } /* }}} int procevent_config */
 
-static void submit(int pid, const char *type, /* {{{ */
-                   gauge_t value, const char *process) {
-  value_list_t vl = VALUE_LIST_INIT;
-  char hostname[1024];
-
-  vl.values = &(value_t){.gauge = value};
-  vl.values_len = 1;
-  sstrncpy(vl.plugin, "procevent", sizeof(vl.plugin));
-  sstrncpy(vl.plugin_instance, process, sizeof(vl.plugin_instance));
-  sstrncpy(vl.type, type, sizeof(vl.type));
+static void procevent_dispatch_notification(int pid, const char *type, /* {{{ */
+                                            gauge_t value, char *process,
+                                            long long unsigned int timestamp) {
+  char *buf = NULL;
+  notification_t n = {NOTIF_FAILURE, cdtime(), "", "", "procevent", "", "", "",
+                      NULL};
 
-  DEBUG("procevent plugin: dispatching state %d for PID %d (%s)", (int)value,
-        pid, process);
+  if (value == 1)
+    n.severity = NOTIF_OKAY;
 
-  // Create metadata to store JSON key-values
-  meta_data_t *meta = meta_data_create();
+  sstrncpy(n.host, hostname_g, sizeof(n.host));
+  sstrncpy(n.plugin_instance, process, sizeof(n.plugin_instance));
+  sstrncpy(n.type, "gauge", sizeof(n.type));
+  sstrncpy(n.type_instance, "process_status", sizeof(n.type_instance));
 
-  vl.meta = meta;
+  gen_message_payload(value, pid, process, timestamp, &buf);
 
-  gethostname(hostname, sizeof(hostname));
+  notification_meta_t *m = calloc(1, sizeof(*m));
 
-  if (value == 1) {
-    meta_data_add_string(meta, "condition", "process_up");
-    meta_data_add_string(meta, "entity", process);
-    meta_data_add_string(meta, "source", hostname);
-    meta_data_add_string(meta, "dest", "process_down");
-  } else {
-    meta_data_add_string(meta, "condition", "process_down");
-    meta_data_add_string(meta, "entity", process);
-    meta_data_add_string(meta, "source", hostname);
-    meta_data_add_string(meta, "dest", "process_up");
+  if (m == NULL) {
+    char errbuf[1024];
+    sfree(buf);
+    ERROR("procevent plugin: unable to allocate metadata: %s",
+          sstrerror(errno, errbuf, sizeof(errbuf)));
+    return;
   }
 
-  plugin_dispatch_values(&vl);
-} /* }}} void interface_submit */
+  sstrncpy(m->name, "ves", sizeof(m->name));
+  m->nm_value.nm_string = sstrdup(buf);
+  m->type = NM_TYPE_STRING;
+  n.meta = m;
+
+  DEBUG("procevent plugin: notification message: %s",
+        n.meta->nm_value.nm_string);
+
+  DEBUG("procevent plugin: dispatching state %d for PID %d (%s)", (int)value,
+        pid, process);
+
+  plugin_dispatch_notification(&n);
+  plugin_notification_meta_free(n.meta);
+
+  // malloc'd in gen_message_payload
+  if (buf != NULL)
+    sfree(buf);
+}
 
 static int procevent_read(void) /* {{{ */
 {
@@ -825,8 +1097,9 @@ static int procevent_read(void) /* {{{ */
 
       if (pl != NULL) {
         // This process is of interest to us, so publish its EXITED status
-        submit(ring.buffer[ring.tail][0], "gauge", ring.buffer[ring.tail][1],
-               pl->process);
+        procevent_dispatch_notification(ring.buffer[ring.tail][0], "gauge",
+                                        ring.buffer[ring.tail][1], pl->process,
+                                        ring.buffer[ring.tail][3]);
         DEBUG("procevent plugin: PID %d (%s) EXITED, removing PID from process "
               "list",
               pl->pid, pl->process);
@@ -838,8 +1111,9 @@ static int procevent_read(void) /* {{{ */
 
       if (pl != NULL) {
         // This process is of interest to us, so publish its STARTED status
-        submit(ring.buffer[ring.tail][0], "gauge", ring.buffer[ring.tail][1],
-               pl->process);
+        procevent_dispatch_notification(ring.buffer[ring.tail][0], "gauge",
+                                        ring.buffer[ring.tail][1], pl->process,
+                                        ring.buffer[ring.tail][3]);
         DEBUG(
             "procevent plugin: PID %d (%s) STARTED, adding PID to process list",
             pl->pid, pl->process);
@@ -856,7 +1130,6 @@ static int procevent_read(void) /* {{{ */
 
 static int procevent_shutdown(void) /* {{{ */
 {
-  // int status = 0;
   processlist_t *pl;
 
   DEBUG("procevent plugin: Shutting down thread.");
@@ -876,11 +1149,6 @@ static int procevent_shutdown(void) /* {{{ */
 
     pl_next = pl->next;
 
-    if (pl->is_regex == 1) {
-      sfree(pl->process_regex);
-      regfree(&pl->process_regex_obj);
-    }
-
     sfree(pl->process);
     sfree(pl);
 
@@ -896,4 +1164,4 @@ void module_register(void) {
   plugin_register_init("procevent", procevent_init);
   plugin_register_read("procevent", procevent_read);
   plugin_register_shutdown("procevent", procevent_shutdown);
-} /* void module_register */
\ No newline at end of file
+} /* void module_register */