X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fprocevent.c;h=a7a0107a3294f566829d862685ed484bc54527b5;hb=936b9757a84885a9e86e7df774d1c82c74f22bb8;hp=368e7c416d1f0d4b4fe7a14a3ded815f82ed0f8e;hpb=48622c3d7c8c0d3ab07cda8a1947c33bfe31df73;p=collectd.git diff --git a/src/procevent.c b/src/procevent.c index 368e7c41..a7a0107a 100644 --- a/src/procevent.c +++ b/src/procevent.c @@ -112,7 +112,8 @@ typedef struct { struct processlist_s { char *process; - uint32_t pid; + long pid; + int32_t last_status; struct processlist_s *next; }; @@ -135,14 +136,14 @@ 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, int pid, char *process, +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; @@ -205,7 +206,7 @@ static int gen_message_payload(int state, int pid, char *process, 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, + snprintf(json_str, event_name_len, "process %s (%ld) %s", process, pid, (state == 0 ? PROCEVENT_EVENT_NAME_DOWN_VALUE : PROCEVENT_EVENT_NAME_UP_VALUE)); @@ -320,7 +321,7 @@ static int gen_message_payload(int state, int pid, char *process, 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", + 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)) != @@ -390,7 +391,7 @@ static int gen_message_payload(int state, int pid, char *process, 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, + snprintf(json_str, specific_problem_len, "process %s (%ld) %s", process, pid, (state == 0 ? PROCEVENT_SPECIFIC_PROBLEM_DOWN_VALUE : PROCEVENT_SPECIFIC_PROBLEM_UP_VALUE)); @@ -445,13 +446,13 @@ err: } // Does /proc//comm contain a process name we are interested in? -static processlist_t *process_check(int pid) { +static processlist_t *process_check(long pid) { int len, is_match, retval; char file[BUFSIZE]; FILE *fh; char buffer[BUFSIZE]; - 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"); @@ -460,14 +461,14 @@ 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; } retval = fscanf(fh, "%[^\n]", buffer); if (retval < 0) { - WARNING("procevent process_check: unable to read comm file for pid %d", + WARNING("procevent process_check: unable to read comm file for pid %ld", pid); fclose(fh); return NULL; @@ -476,7 +477,7 @@ static processlist_t *process_check(int pid) { // 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); + DEBUG("procevent process_check: ignoring process %s (%ld)", buffer, pid); fclose(fh); return NULL; } @@ -506,15 +507,21 @@ static processlist_t *process_check(int pid) { is_match = (strcmp(buffer, pl->process) == 0 ? 1 : 0); if (is_match == 1) { - DEBUG("procevent plugin: process %d name match for %s", pid, buffer); + 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; @@ -522,6 +529,9 @@ 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; } @@ -535,9 +545,9 @@ static processlist_t *process_check(int pid) { // 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, buffer); + DEBUG("procevent plugin: allocating new processlist_t object for PID %ld " + "(%s)", + pid, buffer); processlist_t *pl2; char *process; @@ -575,7 +585,7 @@ static processlist_t *process_check(int pid) { } // 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); @@ -703,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; } @@ -713,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; } @@ -744,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; } @@ -768,72 +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; + + pthread_mutex_unlock(&procevent_lock); + + status = recv(nl_sock, &nlcn_msg, sizeof(nlcn_msg), 0); + + if (status == 0) { + return 0; + } else if (status == -1) { + if (errno != EINTR) { + ERROR("procevent plugin: socket receive error: %d", errno); + return -1; + } } - } - 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; - } + 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 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); + if (proc_status != -1) { + pthread_mutex_lock(&procevent_lock); - int next = ring.head + 1; - if (next >= ring.maxLen) - next = 0; + int next = ring.head + 1; + if (next >= ring.maxLen) + next = 0; - if (next == ring.tail) { - WARNING("procevent plugin: ring buffer full"); - } else { - 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()); + 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; - ring.buffer[ring.head][3] = - (long long unsigned int)CDTIME_T_TO_US(cdtime()); + 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; @@ -987,7 +1008,7 @@ static int procevent_init(void) /* {{{ */ return (-1); } - if (processlist_head == NULL) { + if (ignorelist == NULL) { NOTICE("procevent plugin: No processes have been configured."); return (-1); } @@ -1006,7 +1027,7 @@ static int procevent_config(const char *key, const char *value) /* {{{ */ buffer_length = atoi(value); } else if (strcasecmp(key, "Process") == 0) { ignorelist_add(ignorelist, value); - } else if (strcasecmp(key, "RegexProcess") == 0) { + } else if (strcasecmp(key, "ProcessRegex") == 0) { #if HAVE_REGEX_H status = ignorelist_add(ignorelist, value); @@ -1016,7 +1037,7 @@ static int procevent_config(const char *key, const char *value) /* {{{ */ } #else WARNING("procevent plugin: The plugin has been compiled without support " - "for the \"RegexProcess\" option."); + "for the \"ProcessRegex\" option."); #endif } else { return (-1); @@ -1025,7 +1046,8 @@ static int procevent_config(const char *key, const char *value) /* {{{ */ return (0); } /* }}} int procevent_config */ -static void procevent_dispatch_notification(int pid, const char *type, /* {{{ */ +static void procevent_dispatch_notification(long pid, + const char *type, /* {{{ */ gauge_t value, char *process, long long unsigned int timestamp) { char *buf = NULL; @@ -1060,7 +1082,7 @@ static void procevent_dispatch_notification(int pid, const char *type, /* {{{ */ DEBUG("procevent plugin: notification message: %s", n.meta->nm_value.nm_string); - DEBUG("procevent plugin: dispatching state %d for PID %d (%s)", (int)value, + DEBUG("procevent plugin: dispatching state %d for PID %ld (%s)", (int)value, pid, process); plugin_dispatch_notification(&n); @@ -1100,23 +1122,32 @@ static int procevent_read(void) /* {{{ */ 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); + 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 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); + + pl->last_status = PROCEVENT_STARTED; + + DEBUG("procevent plugin: PID %ld (%s) STARTED, adding PID to process " + "list", + pl->pid, pl->process); } }