X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fprocevent.c;h=a7a0107a3294f566829d862685ed484bc54527b5;hb=936b9757a84885a9e86e7df774d1c82c74f22bb8;hp=e4f473bed14666b496c547e3a6e4c1836ef51da1;hpb=6ff769e0d7d6363e2f507118ccb7722457370442;p=collectd.git diff --git a/src/procevent.c b/src/procevent.c index e4f473be..a7a0107a 100644 --- a/src/procevent.c +++ b/src/procevent.c @@ -29,10 +29,10 @@ #include "common.h" #include "plugin.h" #include "utils_complain.h" +#include "utils_ignorelist.h" #include #include -#include #include #include #include @@ -48,12 +48,55 @@ #include #include +#include +#include +#if HAVE_YAJL_YAJL_VERSION_H +#include +#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,17 +106,14 @@ 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; + long pid; + int32_t last_status; struct processlist_s *next; }; @@ -82,6 +122,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,23 +134,325 @@ 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 const char *config_keys[] = {"BufferLength", "Process", "ProcessRegex"}; static int config_keys_num = STATIC_ARRAY_SIZE(config_keys); /* * Private functions */ +static int gen_message_payload(int state, long 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 (%ld) %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 (%ld) 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 (%ld) %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//comm contain a process name we are interested in? -static processlist_t *process_check(int pid) { - int len, is_match, status; +static processlist_t *process_check(long pid) { + 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); + len = snprintf(file, sizeof(file), PROCDIR "/%ld/comm", pid); if ((len < 0) || (len >= BUFSIZE)) { WARNING("procevent process_check: process name too large"); @@ -118,19 +461,39 @@ static processlist_t *process_check(int pid) { if (NULL == (fh = fopen(file, "r"))) { // No /proc//comm for this pid, just ignore - DEBUG("procevent plugin: no comm file available for pid %d", pid); + DEBUG("procevent plugin: no comm file available for pid %ld", 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 %ld", + 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 (%ld)", 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//comm. If found: - // 1. If pl->pid is -1, then set pl->pid to + // 1. If pl->pid is -1, then set pl->pid to (and return that object) // 2. If pl->pid is not -1, then another process was already // found. If == pl->pid, this is an old match, so do nothing. - // If the is different, however, make a new processlist_t and + // If the is different, however, make a new processlist_t and // associate with it (with the same process name as the existing). // @@ -140,42 +503,25 @@ 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 %ld name match for %s", pid, buffer); if (pl->pid == pid) { // this is a match, and we've already stored the exact pid/name combo + DEBUG("procevent plugin: found exact match with name %s, PID %ld for " + "incoming PID %ld", + pl->process, pl->pid, pid); match = pl; break; } else if (pl->pid == -1) { // this is a match, and we've found a candidate processlist_t to store // this new pid/name combo + DEBUG("procevent plugin: reusing pl object with PID %ld for incoming " + "PID %ld", + pl->pid, pid); pl->pid = pid; match = pl; break; @@ -183,24 +529,28 @@ static processlist_t *process_check(int pid) { // this is a match, but another instance of this process has already // claimed this pid/name combo, // so keep looking + DEBUG("procevent plugin: found pl object with matching name for " + "incoming PID %ld, but object is in use by PID %ld", + pid, pl->pid); match = pl; continue; } } } - 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); + DEBUG("procevent plugin: allocating new processlist_t object for PID %ld " + "(%s)", + pid, buffer); processlist_t *pl2; char *process; - char *process_regex; pl2 = malloc(sizeof(*pl2)); if (pl2 == NULL) { @@ -211,7 +561,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 +571,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,16 +581,11 @@ static processlist_t *process_check(int pid) { pthread_mutex_unlock(&procevent_list_lock); - if (fh != NULL) { - fclose(fh); - fh = NULL; - } - return match; } // Does our map have this PID or name? -static processlist_t *process_map_check(int pid, char *process) { +static processlist_t *process_map_check(long pid, char *process) { processlist_t *pl; pthread_mutex_lock(&procevent_list_lock); @@ -391,7 +713,7 @@ static int nl_connect() { nl_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); if (nl_sock == -1) { - ERROR("procevent plugin: socket open failed."); + ERROR("procevent plugin: socket open failed: %d", errno); return -1; } @@ -401,7 +723,7 @@ static int nl_connect() { rc = bind(nl_sock, (struct sockaddr *)&sa_nl, sizeof(sa_nl)); if (rc == -1) { - ERROR("procevent plugin: socket bind failed."); + ERROR("procevent plugin: socket bind failed: %d", errno); close(nl_sock); return -1; } @@ -432,7 +754,8 @@ static int set_proc_ev_listen(bool enable) { rc = send(nl_sock, &nlcn_msg, sizeof(nlcn_msg), 0); if (rc == -1) { - ERROR("procevent plugin: subscribing to netlink process events failed."); + ERROR("procevent plugin: subscribing to netlink process events failed: %d", + errno); return -1; } @@ -456,90 +779,82 @@ static int read_event() { if (nl_sock == -1) return ret; - status = recv(nl_sock, &nlcn_msg, sizeof(nlcn_msg), 0); + while (42) { - if (status == 0) { - return 0; - } else if (status == -1) { - if (errno != EINTR) { - ERROR("procevent plugin: socket receive error: %d", errno); - return -1; - } - } + pthread_mutex_lock(&procevent_lock); + + if (procevent_thread_loop <= 0) + return ret; - 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; - 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; - proc_extra = nlcn_msg.proc_ev.event_data.exit.exit_code; - break; - default: - break; - } - - // If we're interested in this process status event, place the event - // in the ring buffer for consumption by the main polling thread. - - if (proc_status != -1) { pthread_mutex_unlock(&procevent_lock); - int next = ring.head + 1; - if (next >= ring.maxLen) - next = 0; + status = recv(nl_sock, &nlcn_msg, sizeof(nlcn_msg), 0); - 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")); + if (status == 0) { + return 0; + } else if (status == -1) { + if (errno != EINTR) { + ERROR("procevent plugin: socket receive error: %d", errno); + return -1; + } + } - 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; + switch (nlcn_msg.proc_ev.what) { + case PROC_EVENT_NONE: + case PROC_EVENT_FORK: + case PROC_EVENT_UID: + case PROC_EVENT_GID: + // Not of interest in current version + break; + case PROC_EVENT_EXEC: + proc_status = PROCEVENT_STARTED; + proc_id = nlcn_msg.proc_ev.event_data.exec.process_pid; + break; + case PROC_EVENT_EXIT: + proc_id = nlcn_msg.proc_ev.event_data.exit.process_pid; + proc_status = PROCEVENT_EXITED; + proc_extra = nlcn_msg.proc_ev.event_data.exit.exit_code; + break; + default: + break; + } + + // If we're interested in this process status event, place the event + // in the ring buffer for consumption by the main polling thread. + + if (proc_status != -1) { + pthread_mutex_lock(&procevent_lock); + + int next = ring.head + 1; + if (next >= ring.maxLen) + next = 0; + + if (next == ring.tail) { + WARNING("procevent plugin: ring buffer full"); } else { - ring.buffer[ring.head][0] = proc_id; - ring.buffer[ring.head][1] = proc_status; - ring.buffer[ring.head][2] = 0; + 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; } - ring.head = next; + pthread_mutex_unlock(&procevent_lock); } - - pthread_mutex_unlock(&procevent_lock); } return ret; @@ -675,18 +990,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 +1008,11 @@ static int procevent_init(void) /* {{{ */ return (-1); } + if (ignorelist == NULL) { + NOTICE("procevent plugin: No processes have been configured."); + return (-1); + } + return (start_thread()); } /* }}} int procevent_init */ @@ -703,59 +1020,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, "ProcessRegex") == 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 \"ProcessRegex\" option."); +#endif } else { return (-1); } @@ -763,41 +1046,52 @@ 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]; +static void procevent_dispatch_notification(long 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}; - 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)); - - 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 %ld (%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,24 +1119,35 @@ 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); - DEBUG("procevent plugin: PID %d (%s) EXITED, removing PID from process " - "list", - pl->pid, 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 %ld (%s) EXITED, removing PID from process " + "list", + pl->pid, pl->process); pl->pid = -1; + pl->last_status = -1; } } else if (ring.buffer[ring.tail][1] == PROCEVENT_STARTED) { // a new process has started, so check if we should monitor it processlist_t *pl = process_check(ring.buffer[ring.tail][0]); - if (pl != NULL) { + // If we had already seen this process name and pid combo before, + // and the last message was a "process started" message, don't send + // the notfication again + + if (pl != NULL && pl->last_status != PROCEVENT_STARTED) { // 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); - DEBUG( - "procevent plugin: PID %d (%s) STARTED, adding PID to process list", - pl->pid, pl->process); + procevent_dispatch_notification(ring.buffer[ring.tail][0], "gauge", + ring.buffer[ring.tail][1], pl->process, + ring.buffer[ring.tail][3]); + + pl->last_status = PROCEVENT_STARTED; + + DEBUG("procevent plugin: PID %ld (%s) STARTED, adding PID to process " + "list", + pl->pid, pl->process); } } @@ -856,7 +1161,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 +1180,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 +1195,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 */