fc8e4302b5eb3881af2a127f41a13c16a28af87e
[collectd.git] / src / procevent.c
1 /**
2  * collectd - src/procevent.c
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  *
22  * Authors:
23  *   Red Hat NFVPE
24  *     Andrew Bays <abays at redhat.com>
25  **/
26
27 #include "collectd.h"
28
29 #include "common.h"
30 #include "plugin.h"
31 #include "utils_complain.h"
32 #include "utils_ignorelist.h"
33
34 #include <errno.h>
35 #include <pthread.h>
36 #include <stdio.h>
37 #include <string.h>
38 #include <sys/socket.h>
39 #include <unistd.h>
40
41 #include <dirent.h>
42 #include <linux/cn_proc.h>
43 #include <linux/connector.h>
44 #include <linux/netlink.h>
45 #include <linux/rtnetlink.h>
46 #include <signal.h>
47 #include <stdbool.h>
48 #include <stdlib.h>
49 #include <string.h>
50
51 #include <yajl/yajl_common.h>
52 #include <yajl/yajl_gen.h>
53 #if HAVE_YAJL_YAJL_VERSION_H
54 #include <yajl/yajl_version.h>
55 #endif
56 #if defined(YAJL_MAJOR) && (YAJL_MAJOR > 1)
57 #define HAVE_YAJL_V2 1
58 #endif
59
60 #define PROCEVENT_EXITED 0
61 #define PROCEVENT_STARTED 1
62 #define PROCEVENT_FIELDS 4 // pid, status, extra, timestamp
63 #define BUFSIZE 512
64 #define PROCDIR "/proc"
65
66 #define PROCEVENT_DOMAIN_FIELD "domain"
67 #define PROCEVENT_DOMAIN_VALUE "fault"
68 #define PROCEVENT_EVENT_ID_FIELD "eventId"
69 #define PROCEVENT_EVENT_NAME_FIELD "eventName"
70 #define PROCEVENT_EVENT_NAME_DOWN_VALUE "down"
71 #define PROCEVENT_EVENT_NAME_UP_VALUE "up"
72 #define PROCEVENT_LAST_EPOCH_MICROSEC_FIELD "lastEpochMicrosec"
73 #define PROCEVENT_PRIORITY_FIELD "priority"
74 #define PROCEVENT_PRIORITY_VALUE "high"
75 #define PROCEVENT_REPORTING_ENTITY_NAME_FIELD "reportingEntityName"
76 #define PROCEVENT_REPORTING_ENTITY_NAME_VALUE "collectd procevent plugin"
77 #define PROCEVENT_SEQUENCE_FIELD "sequence"
78 #define PROCEVENT_SEQUENCE_VALUE "0"
79 #define PROCEVENT_SOURCE_NAME_FIELD "sourceName"
80 #define PROCEVENT_START_EPOCH_MICROSEC_FIELD "startEpochMicrosec"
81 #define PROCEVENT_VERSION_FIELD "version"
82 #define PROCEVENT_VERSION_VALUE "1.0"
83
84 #define PROCEVENT_ALARM_CONDITION_FIELD "alarmCondition"
85 #define PROCEVENT_ALARM_INTERFACE_A_FIELD "alarmInterfaceA"
86 #define PROCEVENT_EVENT_SEVERITY_FIELD "eventSeverity"
87 #define PROCEVENT_EVENT_SEVERITY_CRITICAL_VALUE "CRITICAL"
88 #define PROCEVENT_EVENT_SEVERITY_NORMAL_VALUE "NORMAL"
89 #define PROCEVENT_EVENT_SOURCE_TYPE_FIELD "eventSourceType"
90 #define PROCEVENT_EVENT_SOURCE_TYPE_VALUE "process"
91 #define PROCEVENT_FAULT_FIELDS_FIELD "faultFields"
92 #define PROCEVENT_FAULT_FIELDS_VERSION_FIELD "faultFieldsVersion"
93 #define PROCEVENT_FAULT_FIELDS_VERSION_VALUE "1.0"
94 #define PROCEVENT_SPECIFIC_PROBLEM_FIELD "specificProblem"
95 #define PROCEVENT_SPECIFIC_PROBLEM_DOWN_VALUE "down"
96 #define PROCEVENT_SPECIFIC_PROBLEM_UP_VALUE "up"
97 #define PROCEVENT_VF_STATUS_FIELD "vfStatus"
98 #define PROCEVENT_VF_STATUS_CRITICAL_VALUE "Ready to terminate"
99 #define PROCEVENT_VF_STATUS_NORMAL_VALUE "Active"
100
101 /*
102  * Private data types
103  */
104
105 typedef struct {
106   int head;
107   int tail;
108   int maxLen;
109   long long unsigned int **buffer;
110 } circbuf_t;
111
112 struct processlist_s {
113   char *process;
114
115   long pid;
116   int32_t last_status;
117
118   struct processlist_s *next;
119 };
120 typedef struct processlist_s processlist_t;
121
122 /*
123  * Private variables
124  */
125 static ignorelist_t *ignorelist = NULL;
126
127 static int procevent_netlink_thread_loop = 0;
128 static int procevent_netlink_thread_error = 0;
129 static pthread_t procevent_netlink_thread_id;
130 static int procevent_dequeue_thread_loop = 0;
131 static int procevent_dequeue_thread_error = 0;
132 static pthread_t procevent_dequeue_thread_id;
133 static pthread_mutex_t procevent_lock = PTHREAD_MUTEX_INITIALIZER;
134 static pthread_cond_t procevent_cond = PTHREAD_COND_INITIALIZER;
135 static pthread_mutex_t procevent_list_lock = PTHREAD_MUTEX_INITIALIZER;
136 static int nl_sock = -1;
137 static int buffer_length;
138 static circbuf_t ring;
139 static processlist_t *processlist_head = NULL;
140 static int event_id = 0;
141
142 static const char *config_keys[] = {"BufferLength", "Process", "ProcessRegex"};
143 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
144
145 /*
146  * Prototypes
147  */
148
149 static void procevent_dispatch_notification(long pid, const char *type,
150                                             gauge_t value, char *process,
151                                             long long unsigned int timestamp);
152
153 /*
154  * Private functions
155  */
156
157 static int gen_message_payload(int state, long pid, char *process,
158                                long long unsigned int timestamp, char **buf) {
159   const unsigned char *buf2;
160   yajl_gen g;
161   char json_str[DATA_MAX_NAME_LEN];
162
163 #if !defined(HAVE_YAJL_V2)
164   yajl_gen_config conf = {};
165
166   conf.beautify = 0;
167 #endif
168
169 #if HAVE_YAJL_V2
170   size_t len;
171   g = yajl_gen_alloc(NULL);
172   yajl_gen_config(g, yajl_gen_beautify, 0);
173 #else
174   unsigned int len;
175   g = yajl_gen_alloc(&conf, NULL);
176 #endif
177
178   yajl_gen_clear(g);
179
180   // *** BEGIN common event header ***
181
182   if (yajl_gen_map_open(g) != yajl_gen_status_ok)
183     goto err;
184
185   // domain
186   if (yajl_gen_string(g, (u_char *)PROCEVENT_DOMAIN_FIELD,
187                       strlen(PROCEVENT_DOMAIN_FIELD)) != yajl_gen_status_ok)
188     goto err;
189
190   if (yajl_gen_string(g, (u_char *)PROCEVENT_DOMAIN_VALUE,
191                       strlen(PROCEVENT_DOMAIN_VALUE)) != yajl_gen_status_ok)
192     goto err;
193
194   // eventId
195   if (yajl_gen_string(g, (u_char *)PROCEVENT_EVENT_ID_FIELD,
196                       strlen(PROCEVENT_EVENT_ID_FIELD)) != yajl_gen_status_ok)
197     goto err;
198
199   event_id = event_id + 1;
200   int event_id_len = sizeof(char) * sizeof(int) * 4 + 1;
201   memset(json_str, '\0', DATA_MAX_NAME_LEN);
202   snprintf(json_str, event_id_len, "%d", event_id);
203
204   if (yajl_gen_number(g, json_str, strlen(json_str)) != yajl_gen_status_ok) {
205     goto err;
206   }
207
208   // eventName
209   if (yajl_gen_string(g, (u_char *)PROCEVENT_EVENT_NAME_FIELD,
210                       strlen(PROCEVENT_EVENT_NAME_FIELD)) != yajl_gen_status_ok)
211     goto err;
212
213   int event_name_len = 0;
214   event_name_len = event_name_len + (sizeof(char) * sizeof(int) * 4); // pid
215   event_name_len = event_name_len + strlen(process);      // process name
216   event_name_len = event_name_len + (state == 0 ? 4 : 2); // "down" or "up"
217   event_name_len = event_name_len +
218                    13; // "process", 3 spaces, 2 parentheses and null-terminator
219   memset(json_str, '\0', DATA_MAX_NAME_LEN);
220   snprintf(json_str, event_name_len, "process %s (%ld) %s", process, pid,
221            (state == 0 ? PROCEVENT_EVENT_NAME_DOWN_VALUE
222                        : PROCEVENT_EVENT_NAME_UP_VALUE));
223
224   if (yajl_gen_string(g, (u_char *)json_str, strlen(json_str)) !=
225       yajl_gen_status_ok) {
226     goto err;
227   }
228
229   // lastEpochMicrosec
230   if (yajl_gen_string(g, (u_char *)PROCEVENT_LAST_EPOCH_MICROSEC_FIELD,
231                       strlen(PROCEVENT_LAST_EPOCH_MICROSEC_FIELD)) !=
232       yajl_gen_status_ok)
233     goto err;
234
235   int last_epoch_microsec_len =
236       sizeof(char) * sizeof(long long unsigned int) * 4 + 1;
237   memset(json_str, '\0', DATA_MAX_NAME_LEN);
238   snprintf(json_str, last_epoch_microsec_len, "%llu",
239            (long long unsigned int)CDTIME_T_TO_US(cdtime()));
240
241   if (yajl_gen_number(g, json_str, strlen(json_str)) != yajl_gen_status_ok) {
242     goto err;
243   }
244
245   // priority
246   if (yajl_gen_string(g, (u_char *)PROCEVENT_PRIORITY_FIELD,
247                       strlen(PROCEVENT_PRIORITY_FIELD)) != yajl_gen_status_ok)
248     goto err;
249
250   if (yajl_gen_string(g, (u_char *)PROCEVENT_PRIORITY_VALUE,
251                       strlen(PROCEVENT_PRIORITY_VALUE)) != yajl_gen_status_ok)
252     goto err;
253
254   // reportingEntityName
255   if (yajl_gen_string(g, (u_char *)PROCEVENT_REPORTING_ENTITY_NAME_FIELD,
256                       strlen(PROCEVENT_REPORTING_ENTITY_NAME_FIELD)) !=
257       yajl_gen_status_ok)
258     goto err;
259
260   if (yajl_gen_string(g, (u_char *)PROCEVENT_REPORTING_ENTITY_NAME_VALUE,
261                       strlen(PROCEVENT_REPORTING_ENTITY_NAME_VALUE)) !=
262       yajl_gen_status_ok)
263     goto err;
264
265   // sequence
266   if (yajl_gen_string(g, (u_char *)PROCEVENT_SEQUENCE_FIELD,
267                       strlen(PROCEVENT_SEQUENCE_FIELD)) != yajl_gen_status_ok)
268     goto err;
269
270   if (yajl_gen_number(g, PROCEVENT_SEQUENCE_VALUE,
271                       strlen(PROCEVENT_SEQUENCE_VALUE)) != yajl_gen_status_ok)
272     goto err;
273
274   // sourceName
275   if (yajl_gen_string(g, (u_char *)PROCEVENT_SOURCE_NAME_FIELD,
276                       strlen(PROCEVENT_SOURCE_NAME_FIELD)) !=
277       yajl_gen_status_ok)
278     goto err;
279
280   if (yajl_gen_string(g, (u_char *)process, strlen(process)) !=
281       yajl_gen_status_ok)
282     goto err;
283
284   // startEpochMicrosec
285   if (yajl_gen_string(g, (u_char *)PROCEVENT_START_EPOCH_MICROSEC_FIELD,
286                       strlen(PROCEVENT_START_EPOCH_MICROSEC_FIELD)) !=
287       yajl_gen_status_ok)
288     goto err;
289
290   int start_epoch_microsec_len =
291       sizeof(char) * sizeof(long long unsigned int) * 4 + 1;
292   memset(json_str, '\0', DATA_MAX_NAME_LEN);
293   snprintf(json_str, start_epoch_microsec_len, "%llu",
294            (long long unsigned int)timestamp);
295
296   if (yajl_gen_number(g, json_str, strlen(json_str)) != yajl_gen_status_ok) {
297     goto err;
298   }
299
300   // version
301   if (yajl_gen_string(g, (u_char *)PROCEVENT_VERSION_FIELD,
302                       strlen(PROCEVENT_VERSION_FIELD)) != yajl_gen_status_ok)
303     goto err;
304
305   if (yajl_gen_number(g, PROCEVENT_VERSION_VALUE,
306                       strlen(PROCEVENT_VERSION_VALUE)) != yajl_gen_status_ok)
307     goto err;
308
309   // *** END common event header ***
310
311   // *** BEGIN fault fields ***
312
313   if (yajl_gen_string(g, (u_char *)PROCEVENT_FAULT_FIELDS_FIELD,
314                       strlen(PROCEVENT_FAULT_FIELDS_FIELD)) !=
315       yajl_gen_status_ok)
316     goto err;
317
318   if (yajl_gen_map_open(g) != yajl_gen_status_ok)
319     goto err;
320
321   // alarmCondition
322   if (yajl_gen_string(g, (u_char *)PROCEVENT_ALARM_CONDITION_FIELD,
323                       strlen(PROCEVENT_ALARM_CONDITION_FIELD)) !=
324       yajl_gen_status_ok)
325     goto err;
326
327   int alarm_condition_len = 0;
328   alarm_condition_len =
329       alarm_condition_len + (sizeof(char) * sizeof(int) * 4);  // pid
330   alarm_condition_len = alarm_condition_len + strlen(process); // process name
331   alarm_condition_len =
332       alarm_condition_len + 25; // "process", "state", "change", 4 spaces, 2
333                                 // parentheses and null-terminator
334   memset(json_str, '\0', DATA_MAX_NAME_LEN);
335   snprintf(json_str, alarm_condition_len, "process %s (%ld) state change",
336            process, pid);
337
338   if (yajl_gen_string(g, (u_char *)json_str, strlen(json_str)) !=
339       yajl_gen_status_ok) {
340     goto err;
341   }
342
343   // alarmInterfaceA
344   if (yajl_gen_string(g, (u_char *)PROCEVENT_ALARM_INTERFACE_A_FIELD,
345                       strlen(PROCEVENT_ALARM_INTERFACE_A_FIELD)) !=
346       yajl_gen_status_ok)
347     goto err;
348
349   if (yajl_gen_string(g, (u_char *)process, strlen(process)) !=
350       yajl_gen_status_ok)
351     goto err;
352
353   // eventSeverity
354   if (yajl_gen_string(g, (u_char *)PROCEVENT_EVENT_SEVERITY_FIELD,
355                       strlen(PROCEVENT_EVENT_SEVERITY_FIELD)) !=
356       yajl_gen_status_ok)
357     goto err;
358
359   if (yajl_gen_string(
360           g, (u_char *)(state == 0 ? PROCEVENT_EVENT_SEVERITY_CRITICAL_VALUE
361                                    : PROCEVENT_EVENT_SEVERITY_NORMAL_VALUE),
362           strlen((state == 0 ? PROCEVENT_EVENT_SEVERITY_CRITICAL_VALUE
363                              : PROCEVENT_EVENT_SEVERITY_NORMAL_VALUE))) !=
364       yajl_gen_status_ok)
365     goto err;
366
367   // eventSourceType
368   if (yajl_gen_string(g, (u_char *)PROCEVENT_EVENT_SOURCE_TYPE_FIELD,
369                       strlen(PROCEVENT_EVENT_SOURCE_TYPE_FIELD)) !=
370       yajl_gen_status_ok)
371     goto err;
372
373   if (yajl_gen_string(g, (u_char *)PROCEVENT_EVENT_SOURCE_TYPE_VALUE,
374                       strlen(PROCEVENT_EVENT_SOURCE_TYPE_VALUE)) !=
375       yajl_gen_status_ok)
376     goto err;
377
378   // faultFieldsVersion
379   if (yajl_gen_string(g, (u_char *)PROCEVENT_FAULT_FIELDS_VERSION_FIELD,
380                       strlen(PROCEVENT_FAULT_FIELDS_VERSION_FIELD)) !=
381       yajl_gen_status_ok)
382     goto err;
383
384   if (yajl_gen_number(g, PROCEVENT_FAULT_FIELDS_VERSION_VALUE,
385                       strlen(PROCEVENT_FAULT_FIELDS_VERSION_VALUE)) !=
386       yajl_gen_status_ok)
387     goto err;
388
389   // specificProblem
390   if (yajl_gen_string(g, (u_char *)PROCEVENT_SPECIFIC_PROBLEM_FIELD,
391                       strlen(PROCEVENT_SPECIFIC_PROBLEM_FIELD)) !=
392       yajl_gen_status_ok)
393     goto err;
394
395   int specific_problem_len = 0;
396   specific_problem_len =
397       specific_problem_len + (sizeof(char) * sizeof(int) * 4);   // pid
398   specific_problem_len = specific_problem_len + strlen(process); // process name
399   specific_problem_len =
400       specific_problem_len + (state == 0 ? 4 : 2); // "down" or "up"
401   specific_problem_len =
402       specific_problem_len +
403       13; // "process", 3 spaces, 2 parentheses and null-terminator
404   memset(json_str, '\0', DATA_MAX_NAME_LEN);
405   snprintf(json_str, specific_problem_len, "process %s (%ld) %s", process, pid,
406            (state == 0 ? PROCEVENT_SPECIFIC_PROBLEM_DOWN_VALUE
407                        : PROCEVENT_SPECIFIC_PROBLEM_UP_VALUE));
408
409   if (yajl_gen_string(g, (u_char *)json_str, strlen(json_str)) !=
410       yajl_gen_status_ok) {
411     goto err;
412   }
413
414   // vfStatus
415   if (yajl_gen_string(g, (u_char *)PROCEVENT_VF_STATUS_FIELD,
416                       strlen(PROCEVENT_VF_STATUS_FIELD)) != yajl_gen_status_ok)
417     goto err;
418
419   if (yajl_gen_string(
420           g, (u_char *)(state == 0 ? PROCEVENT_VF_STATUS_CRITICAL_VALUE
421                                    : PROCEVENT_VF_STATUS_NORMAL_VALUE),
422           strlen((state == 0 ? PROCEVENT_VF_STATUS_CRITICAL_VALUE
423                              : PROCEVENT_VF_STATUS_NORMAL_VALUE))) !=
424       yajl_gen_status_ok)
425     goto err;
426
427   if (yajl_gen_map_close(g) != yajl_gen_status_ok)
428     goto err;
429
430   // *** END fault fields ***
431
432   if (yajl_gen_map_close(g) != yajl_gen_status_ok)
433     goto err;
434
435   if (yajl_gen_get_buf(g, &buf2, &len) != yajl_gen_status_ok)
436     goto err;
437
438   *buf = malloc(strlen((char *)buf2) + 1);
439
440   if (*buf == NULL) {
441     char errbuf[1024];
442     ERROR("procevent plugin: malloc failed during gen_message_payload: %s",
443           sstrerror(errno, errbuf, sizeof(errbuf)));
444     goto err;
445   }
446
447   sstrncpy(*buf, (char *)buf2, strlen((char *)buf2) + 1);
448
449   yajl_gen_free(g);
450
451   return 0;
452
453 err:
454   yajl_gen_free(g);
455   ERROR("procevent plugin: gen_message_payload failed to generate JSON");
456   return -1;
457 }
458
459 // Does /proc/<pid>/comm contain a process name we are interested in?
460 static processlist_t *process_check(long pid) {
461   int len, is_match, retval;
462   char file[BUFSIZE];
463   FILE *fh;
464   char buffer[BUFSIZE];
465
466   len = snprintf(file, sizeof(file), PROCDIR "/%ld/comm", pid);
467
468   if ((len < 0) || (len >= BUFSIZE)) {
469     WARNING("procevent process_check: process name too large");
470     return NULL;
471   }
472
473   if (NULL == (fh = fopen(file, "r"))) {
474     // No /proc/<pid>/comm for this pid, just ignore
475     DEBUG("procevent plugin: no comm file available for pid %ld", pid);
476     return NULL;
477   }
478
479   retval = fscanf(fh, "%[^\n]", buffer);
480
481   if (retval < 0) {
482     WARNING("procevent process_check: unable to read comm file for pid %ld",
483             pid);
484     fclose(fh);
485     return NULL;
486   }
487
488   // Now that we have the process name in the buffer, check if we are
489   // even interested in it
490   if (ignorelist_match(ignorelist, buffer) != 0) {
491     DEBUG("procevent process_check: ignoring process %s (%ld)", buffer, pid);
492     fclose(fh);
493     return NULL;
494   }
495
496   if (fh != NULL) {
497     fclose(fh);
498     fh = NULL;
499   }
500
501   //
502   // Go through the processlist linked list and look for the process name
503   // in /proc/<pid>/comm.  If found:
504   // 1. If pl->pid is -1, then set pl->pid to <pid> (and return that object)
505   // 2. If pl->pid is not -1, then another <process name> process was already
506   //    found.  If <pid> == pl->pid, this is an old match, so do nothing.
507   //    If the <pid> is different, however, make a new processlist_t and
508   //    associate <pid> with it (with the same process name as the existing).
509   //
510
511   pthread_mutex_lock(&procevent_list_lock);
512
513   processlist_t *pl;
514   processlist_t *match = NULL;
515
516   for (pl = processlist_head; pl != NULL; pl = pl->next) {
517
518     is_match = (strcmp(buffer, pl->process) == 0 ? 1 : 0);
519
520     if (is_match == 1) {
521       DEBUG("procevent plugin: process %ld name match for %s", pid, buffer);
522
523       if (pl->pid == pid) {
524         // this is a match, and we've already stored the exact pid/name combo
525         DEBUG("procevent plugin: found exact match with name %s, PID %ld for "
526               "incoming PID %ld",
527               pl->process, pl->pid, pid);
528         match = pl;
529         break;
530       } else if (pl->pid == -1) {
531         // this is a match, and we've found a candidate processlist_t to store
532         // this new pid/name combo
533         DEBUG("procevent plugin: reusing pl object with PID %ld for incoming "
534               "PID %ld",
535               pl->pid, pid);
536         pl->pid = pid;
537         match = pl;
538         break;
539       } else if (pl->pid != -1) {
540         // this is a match, but another instance of this process has already
541         // claimed this pid/name combo,
542         // so keep looking
543         DEBUG("procevent plugin: found pl object with matching name for "
544               "incoming PID %ld, but object is in use by PID %ld",
545               pid, pl->pid);
546         match = pl;
547         continue;
548       }
549     }
550   }
551
552   if (match == NULL ||
553       (match != NULL && match->pid != -1 && match->pid != pid)) {
554     // if there wasn't an existing match, OR
555     // if there was a match but the associated processlist_t object already
556     // contained a pid/name combo,
557     // then make a new one and add it to the linked list
558
559     DEBUG("procevent plugin: allocating new processlist_t object for PID %ld "
560           "(%s)",
561           pid, buffer);
562
563     processlist_t *pl2;
564     char *process;
565
566     pl2 = malloc(sizeof(*pl2));
567     if (pl2 == NULL) {
568       char errbuf[1024];
569       ERROR("procevent plugin: malloc failed during process_check: %s",
570             sstrerror(errno, errbuf, sizeof(errbuf)));
571       pthread_mutex_unlock(&procevent_list_lock);
572       return NULL;
573     }
574
575     process = strdup(buffer);
576     if (process == NULL) {
577       char errbuf[1024];
578       sfree(pl2);
579       ERROR("procevent plugin: strdup failed during process_check: %s",
580             sstrerror(errno, errbuf, sizeof(errbuf)));
581       pthread_mutex_unlock(&procevent_list_lock);
582       return NULL;
583     }
584
585     pl2->process = process;
586     pl2->pid = pid;
587     pl2->next = processlist_head;
588     processlist_head = pl2;
589
590     match = pl2;
591   }
592
593   pthread_mutex_unlock(&procevent_list_lock);
594
595   return match;
596 }
597
598 // Does our map have this PID or name?
599 static processlist_t *process_map_check(long pid, char *process) {
600   processlist_t *pl;
601
602   pthread_mutex_lock(&procevent_list_lock);
603
604   for (pl = processlist_head; pl != NULL; pl = pl->next) {
605     int match_pid = 0;
606     int match_process = 0;
607     int match = 0;
608
609     if (pid > 0) {
610       if (pl->pid == pid)
611         match_pid = 1;
612     }
613
614     if (process != NULL) {
615       if (strcmp(pl->process, process) == 0)
616         match_process = 1;
617     }
618
619     if (pid > 0 && process == NULL && match_pid == 1)
620       match = 1;
621     else if (pid < 0 && process != NULL && match_process == 1)
622       match = 1;
623     else if (pid > 0 && process != NULL && match_pid == 1 && match_process == 1)
624       match = 1;
625
626     if (match == 1) {
627       pthread_mutex_unlock(&procevent_list_lock);
628       return pl;
629     }
630   }
631
632   pthread_mutex_unlock(&procevent_list_lock);
633
634   return NULL;
635 }
636
637 static int process_map_refresh(void) {
638   DIR *proc;
639
640   errno = 0;
641   proc = opendir(PROCDIR);
642   if (proc == NULL) {
643     char errbuf[1024];
644     ERROR("procevent plugin: fopen (%s): %s", PROCDIR,
645           sstrerror(errno, errbuf, sizeof(errbuf)));
646     return -1;
647   }
648
649   while (42) {
650     struct dirent *dent;
651     int len;
652     char file[BUFSIZE];
653
654     struct stat statbuf;
655
656     int status;
657
658     errno = 0;
659     dent = readdir(proc);
660     if (dent == NULL) {
661       char errbuf[4096];
662
663       if (errno == 0) /* end of directory */
664         break;
665
666       ERROR("procevent plugin: failed to read directory %s: %s", PROCDIR,
667             sstrerror(errno, errbuf, sizeof(errbuf)));
668       closedir(proc);
669       return -1;
670     }
671
672     if (dent->d_name[0] == '.')
673       continue;
674
675     len = snprintf(file, sizeof(file), PROCDIR "/%s", dent->d_name);
676     if ((len < 0) || (len >= BUFSIZE))
677       continue;
678
679     status = stat(file, &statbuf);
680     if (status != 0) {
681       char errbuf[4096];
682       WARNING("procevent plugin: stat (%s) failed: %s", file,
683               sstrerror(errno, errbuf, sizeof(errbuf)));
684       continue;
685     }
686
687     if (!S_ISDIR(statbuf.st_mode))
688       continue;
689
690     len = snprintf(file, sizeof(file), PROCDIR "/%s/comm", dent->d_name);
691     if ((len < 0) || (len >= BUFSIZE))
692       continue;
693
694     int not_number = 0;
695
696     for (int i = 0; i < strlen(dent->d_name); i++) {
697       if (!isdigit(dent->d_name[i])) {
698         not_number = 1;
699         break;
700       }
701     }
702
703     if (not_number != 0)
704       continue;
705
706     // Check if we need to store this pid/name combo in our processlist_t linked
707     // list
708     int this_pid = atoi(dent->d_name);
709     processlist_t *pl = process_check(this_pid);
710
711     if (pl != NULL)
712       DEBUG("procevent plugin: process map refreshed for PID %d and name %s",
713             this_pid, pl->process);
714   }
715
716   closedir(proc);
717
718   return 0;
719 }
720
721 static int nl_connect() {
722   int rc;
723   struct sockaddr_nl sa_nl;
724
725   nl_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
726   if (nl_sock == -1) {
727     ERROR("procevent plugin: socket open failed: %d", errno);
728     return -1;
729   }
730
731   sa_nl.nl_family = AF_NETLINK;
732   sa_nl.nl_groups = CN_IDX_PROC;
733   sa_nl.nl_pid = getpid();
734
735   rc = bind(nl_sock, (struct sockaddr *)&sa_nl, sizeof(sa_nl));
736   if (rc == -1) {
737     ERROR("procevent plugin: socket bind failed: %d", errno);
738     close(nl_sock);
739     return -1;
740   }
741
742   return 0;
743 }
744
745 static int set_proc_ev_listen(bool enable) {
746   int rc;
747   struct __attribute__((aligned(NLMSG_ALIGNTO))) {
748     struct nlmsghdr nl_hdr;
749     struct __attribute__((__packed__)) {
750       struct cn_msg cn_msg;
751       enum proc_cn_mcast_op cn_mcast;
752     };
753   } nlcn_msg;
754
755   memset(&nlcn_msg, 0, sizeof(nlcn_msg));
756   nlcn_msg.nl_hdr.nlmsg_len = sizeof(nlcn_msg);
757   nlcn_msg.nl_hdr.nlmsg_pid = getpid();
758   nlcn_msg.nl_hdr.nlmsg_type = NLMSG_DONE;
759
760   nlcn_msg.cn_msg.id.idx = CN_IDX_PROC;
761   nlcn_msg.cn_msg.id.val = CN_VAL_PROC;
762   nlcn_msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
763
764   nlcn_msg.cn_mcast = enable ? PROC_CN_MCAST_LISTEN : PROC_CN_MCAST_IGNORE;
765
766   rc = send(nl_sock, &nlcn_msg, sizeof(nlcn_msg), 0);
767   if (rc == -1) {
768     ERROR("procevent plugin: subscribing to netlink process events failed: %d",
769           errno);
770     return -1;
771   }
772
773   return 0;
774 }
775
776 // Read from netlink socket and write to ring buffer
777 static int read_event() {
778   int status;
779   int ret = 0;
780   int proc_id = -1;
781   int proc_status = -1;
782   int proc_extra = -1;
783   int recv_flags = MSG_DONTWAIT;
784   struct __attribute__((aligned(NLMSG_ALIGNTO))) {
785     struct nlmsghdr nl_hdr;
786     struct __attribute__((__packed__)) {
787       struct cn_msg cn_msg;
788       struct proc_event proc_ev;
789     };
790   } nlcn_msg;
791
792   if (nl_sock == -1)
793     return ret;
794
795   while (42) {
796
797     pthread_mutex_lock(&procevent_lock);
798
799     if (procevent_netlink_thread_loop <= 0) {
800       pthread_mutex_unlock(&procevent_lock);
801       return ret;
802     }
803
804     pthread_mutex_unlock(&procevent_lock);
805
806     status = recv(nl_sock, &nlcn_msg, sizeof(nlcn_msg), recv_flags);
807
808     if (status == 0) {
809       return 0;
810     } else if (status < 0) {
811       if (errno == EAGAIN || errno == EWOULDBLOCK) {
812         pthread_mutex_lock(&procevent_lock);
813
814         // There was nothing more to receive for now, so...
815         // If ring head does not equal ring tail, there is data
816         // in the ring buffer for the dequeue thread to read, so
817         // signal it
818         if (ring.head != ring.tail)
819           pthread_cond_signal(&procevent_cond);
820
821         pthread_mutex_unlock(&procevent_lock);
822
823         // Since there was nothing to receive, set recv to block and
824         // try again
825         recv_flags = 0;
826         continue;
827       } else if (errno != EINTR) {
828         ERROR("procevent plugin: socket receive error: %d", errno);
829         return -1;
830       }
831     }
832
833     // We successfully received a message, so don't block on the next
834     // read in case there are more (and if there aren't, it will be
835     // handled above in the error-checking)
836     recv_flags = MSG_DONTWAIT;
837
838     switch (nlcn_msg.proc_ev.what) {
839     case PROC_EVENT_NONE:
840     case PROC_EVENT_FORK:
841     case PROC_EVENT_UID:
842     case PROC_EVENT_GID:
843       // Not of interest in current version
844       break;
845     case PROC_EVENT_EXEC:
846       proc_status = PROCEVENT_STARTED;
847       proc_id = nlcn_msg.proc_ev.event_data.exec.process_pid;
848       break;
849     case PROC_EVENT_EXIT:
850       proc_id = nlcn_msg.proc_ev.event_data.exit.process_pid;
851       proc_status = PROCEVENT_EXITED;
852       proc_extra = nlcn_msg.proc_ev.event_data.exit.exit_code;
853       break;
854     default:
855       break;
856     }
857
858     // If we're interested in this process status event, place the event
859     // in the ring buffer for consumption by the main polling thread.
860
861     if (proc_status != -1) {
862       pthread_mutex_lock(&procevent_lock);
863
864       int next = ring.head + 1;
865       if (next >= ring.maxLen)
866         next = 0;
867
868       if (next == ring.tail) {
869         // Buffer is full, signal the dequeue thread to process the buffer
870         // and clean it out, and then sleep
871         WARNING("procevent plugin: ring buffer full");
872
873         pthread_cond_signal(&procevent_cond);
874         pthread_mutex_unlock(&procevent_lock);
875
876         usleep(1000);
877         continue;
878       } else {
879         DEBUG("procevent plugin: Process %d status is now %s at %llu", proc_id,
880               (proc_status == PROCEVENT_EXITED ? "EXITED" : "STARTED"),
881               (long long unsigned int)CDTIME_T_TO_US(cdtime()));
882
883         if (proc_status == PROCEVENT_EXITED) {
884           ring.buffer[ring.head][0] = proc_id;
885           ring.buffer[ring.head][1] = proc_status;
886           ring.buffer[ring.head][2] = proc_extra;
887           ring.buffer[ring.head][3] =
888               (long long unsigned int)CDTIME_T_TO_US(cdtime());
889         } else {
890           ring.buffer[ring.head][0] = proc_id;
891           ring.buffer[ring.head][1] = proc_status;
892           ring.buffer[ring.head][2] = 0;
893           ring.buffer[ring.head][3] =
894               (long long unsigned int)CDTIME_T_TO_US(cdtime());
895         }
896
897         ring.head = next;
898       }
899
900       pthread_mutex_unlock(&procevent_lock);
901     }
902   }
903
904   return ret;
905 }
906
907 // Read from ring buffer and dispatch to write plugins
908 static int read_ring_buffer() {
909   pthread_mutex_lock(&procevent_lock);
910
911   // If there's currently nothing to read from the buffer,
912   // then wait
913   if (ring.head == ring.tail)
914     pthread_cond_wait(&procevent_cond, &procevent_lock);
915
916   while (ring.head != ring.tail) {
917     int next = ring.tail + 1;
918
919     if (next >= ring.maxLen)
920       next = 0;
921
922     if (ring.buffer[ring.tail][1] == PROCEVENT_EXITED) {
923       processlist_t *pl = process_map_check(ring.buffer[ring.tail][0], NULL);
924
925       if (pl != NULL) {
926         // This process is of interest to us, so publish its EXITED status
927         procevent_dispatch_notification(ring.buffer[ring.tail][0], "gauge",
928                                         ring.buffer[ring.tail][1], pl->process,
929                                         ring.buffer[ring.tail][3]);
930         DEBUG(
931             "procevent plugin: PID %ld (%s) EXITED, removing PID from process "
932             "list",
933             pl->pid, pl->process);
934         pl->pid = -1;
935         pl->last_status = -1;
936       }
937     } else if (ring.buffer[ring.tail][1] == PROCEVENT_STARTED) {
938       // a new process has started, so check if we should monitor it
939       processlist_t *pl = process_check(ring.buffer[ring.tail][0]);
940
941       // If we had already seen this process name and pid combo before,
942       // and the last message was a "process started" message, don't send
943       // the notfication again
944
945       if (pl != NULL && pl->last_status != PROCEVENT_STARTED) {
946         // This process is of interest to us, so publish its STARTED status
947         procevent_dispatch_notification(ring.buffer[ring.tail][0], "gauge",
948                                         ring.buffer[ring.tail][1], pl->process,
949                                         ring.buffer[ring.tail][3]);
950
951         pl->last_status = PROCEVENT_STARTED;
952
953         DEBUG("procevent plugin: PID %ld (%s) STARTED, adding PID to process "
954               "list",
955               pl->pid, pl->process);
956       }
957     }
958
959     ring.tail = next;
960   }
961
962   pthread_mutex_unlock(&procevent_lock);
963
964   return 0;
965 }
966
967 // Entry point for thread responsible for listening
968 // to netlink socket and writing data to ring buffer
969 static void *procevent_netlink_thread(void *arg) /* {{{ */
970 {
971   pthread_mutex_lock(&procevent_lock);
972
973   while (procevent_netlink_thread_loop > 0) {
974     int status;
975
976     pthread_mutex_unlock(&procevent_lock);
977
978     usleep(1000);
979
980     status = read_event();
981
982     pthread_mutex_lock(&procevent_lock);
983
984     if (status < 0) {
985       procevent_netlink_thread_error = 1;
986       break;
987     }
988
989     if (procevent_netlink_thread_loop <= 0)
990       break;
991   } /* while (procevent_netlink_thread_loop > 0) */
992
993   pthread_mutex_unlock(&procevent_lock);
994
995   return ((void *)0);
996 } /* }}} void *procevent_netlink_thread */
997
998 // Entry point for thread responsible for reading from
999 // ring buffer and dispatching notifications
1000 static void *procevent_dequeue_thread(void *arg) /* {{{ */
1001 {
1002   pthread_mutex_lock(&procevent_lock);
1003
1004   while (procevent_dequeue_thread_loop > 0) {
1005     int status;
1006
1007     pthread_mutex_unlock(&procevent_lock);
1008
1009     status = read_ring_buffer();
1010
1011     pthread_mutex_lock(&procevent_lock);
1012
1013     if (status < 0) {
1014       procevent_dequeue_thread_error = 1;
1015       break;
1016     }
1017
1018     if (procevent_dequeue_thread_loop <= 0)
1019       break;
1020   } /* while (procevent_dequeue_thread_loop > 0) */
1021
1022   pthread_mutex_unlock(&procevent_lock);
1023
1024   return ((void *)0);
1025 } /* }}} void *procevent_dequeue_thread */
1026
1027 static int start_netlink_thread(void) /* {{{ */
1028 {
1029   int status;
1030
1031   pthread_mutex_lock(&procevent_lock);
1032
1033   if (procevent_netlink_thread_loop != 0) {
1034     pthread_mutex_unlock(&procevent_lock);
1035     return (0);
1036   }
1037
1038   if (nl_sock == -1) {
1039     status = nl_connect();
1040
1041     if (status != 0)
1042       return status;
1043
1044     status = set_proc_ev_listen(true);
1045     if (status == -1)
1046       return status;
1047   }
1048
1049   DEBUG("procevent plugin: socket created and bound");
1050
1051   procevent_netlink_thread_loop = 1;
1052   procevent_netlink_thread_error = 0;
1053
1054   status = plugin_thread_create(&procevent_netlink_thread_id, /* attr = */ NULL,
1055                                 procevent_netlink_thread,
1056                                 /* arg = */ (void *)0, "procevent");
1057   if (status != 0) {
1058     procevent_netlink_thread_loop = 0;
1059     ERROR("procevent plugin: Starting netlink thread failed.");
1060     pthread_mutex_unlock(&procevent_lock);
1061     return (-1);
1062   }
1063
1064   pthread_mutex_unlock(&procevent_lock);
1065
1066   return status;
1067 } /* }}} int start_netlink_thread */
1068
1069 static int start_dequeue_thread(void) /* {{{ */
1070 {
1071   int status;
1072
1073   pthread_mutex_lock(&procevent_lock);
1074
1075   if (procevent_dequeue_thread_loop != 0) {
1076     pthread_mutex_unlock(&procevent_lock);
1077     return (0);
1078   }
1079
1080   procevent_dequeue_thread_loop = 1;
1081   procevent_dequeue_thread_error = 0;
1082
1083   status = plugin_thread_create(&procevent_dequeue_thread_id, /* attr = */ NULL,
1084                                 procevent_dequeue_thread,
1085                                 /* arg = */ (void *)0, "procevent");
1086   if (status != 0) {
1087     procevent_dequeue_thread_loop = 0;
1088     ERROR("procevent plugin: Starting dequeue thread failed.");
1089     pthread_mutex_unlock(&procevent_lock);
1090     return (-1);
1091   }
1092
1093   pthread_mutex_unlock(&procevent_lock);
1094
1095   return status;
1096 } /* }}} int start_dequeue_thread */
1097
1098 static int start_threads(void) /* {{{ */
1099 {
1100   int status, status2;
1101
1102   status = start_netlink_thread();
1103   status2 = start_dequeue_thread();
1104
1105   if (status < 0)
1106     return status;
1107   else
1108     return status2;
1109 } /* }}} int start_threads */
1110
1111 static int stop_netlink_thread(int shutdown) /* {{{ */
1112 {
1113   int status;
1114
1115   if (nl_sock != -1) {
1116     status = close(nl_sock);
1117     if (status != 0) {
1118       ERROR("procevent plugin: failed to close socket %d: %d (%s)", nl_sock,
1119             status, strerror(errno));
1120       return (-1);
1121     } else
1122       nl_sock = -1;
1123   }
1124
1125   pthread_mutex_lock(&procevent_lock);
1126
1127   if (procevent_netlink_thread_loop == 0) {
1128     pthread_mutex_unlock(&procevent_lock);
1129     return (-1);
1130   }
1131
1132   procevent_netlink_thread_loop = 0;
1133   pthread_cond_broadcast(&procevent_cond);
1134   pthread_mutex_unlock(&procevent_lock);
1135
1136   if (shutdown == 1) {
1137     // Calling pthread_cancel here in
1138     // the case of a shutdown just assures that the thread is
1139     // gone and that the process has been fully terminated.
1140
1141     DEBUG("procevent plugin: Canceling netlink thread for process shutdown");
1142
1143     status = pthread_cancel(procevent_netlink_thread_id);
1144
1145     if (status != 0 && status != ESRCH) {
1146       ERROR("procevent plugin: Unable to cancel netlink thread: %d", status);
1147       status = -1;
1148     } else
1149       status = 0;
1150   } else {
1151     status = pthread_join(procevent_netlink_thread_id, /* return = */ NULL);
1152     if (status != 0 && status != ESRCH) {
1153       ERROR("procevent plugin: Stopping netlink thread failed.");
1154       status = -1;
1155     } else
1156       status = 0;
1157   }
1158
1159   pthread_mutex_lock(&procevent_lock);
1160   memset(&procevent_netlink_thread_id, 0, sizeof(procevent_netlink_thread_id));
1161   procevent_netlink_thread_error = 0;
1162   pthread_mutex_unlock(&procevent_lock);
1163
1164   DEBUG("procevent plugin: Finished requesting stop of netlink thread");
1165
1166   return (status);
1167 } /* }}} int stop_netlink_thread */
1168
1169 static int stop_dequeue_thread(int shutdown) /* {{{ */
1170 {
1171   int status;
1172
1173   pthread_mutex_lock(&procevent_lock);
1174
1175   if (procevent_dequeue_thread_loop == 0) {
1176     pthread_mutex_unlock(&procevent_lock);
1177     return (-1);
1178   }
1179
1180   procevent_dequeue_thread_loop = 0;
1181   pthread_cond_broadcast(&procevent_cond);
1182   pthread_mutex_unlock(&procevent_lock);
1183
1184   if (shutdown == 1) {
1185     // Calling pthread_cancel here in
1186     // the case of a shutdown just assures that the thread is
1187     // gone and that the process has been fully terminated.
1188
1189     DEBUG("procevent plugin: Canceling dequeue thread for process shutdown");
1190
1191     status = pthread_cancel(procevent_dequeue_thread_id);
1192
1193     if (status != 0 && status != ESRCH) {
1194       ERROR("procevent plugin: Unable to cancel dequeue thread: %d", status);
1195       status = -1;
1196     } else
1197       status = 0;
1198   } else {
1199     status = pthread_join(procevent_dequeue_thread_id, /* return = */ NULL);
1200     if (status != 0 && status != ESRCH) {
1201       ERROR("procevent plugin: Stopping dequeue thread failed.");
1202       status = -1;
1203     } else
1204       status = 0;
1205   }
1206
1207   pthread_mutex_lock(&procevent_lock);
1208   memset(&procevent_dequeue_thread_id, 0, sizeof(procevent_dequeue_thread_id));
1209   procevent_dequeue_thread_error = 0;
1210   pthread_mutex_unlock(&procevent_lock);
1211
1212   DEBUG("procevent plugin: Finished requesting stop of dequeue thread");
1213
1214   return (status);
1215 } /* }}} int stop_dequeue_thread */
1216
1217 static int stop_threads(int shutdown) /* {{{ */
1218 {
1219   int status, status2;
1220
1221   status = stop_netlink_thread(shutdown);
1222   status2 = stop_dequeue_thread(shutdown);
1223
1224   if (status < 0)
1225     return status;
1226   else
1227     return status2;
1228 } /* }}} int stop_threads */
1229
1230 static int procevent_init(void) /* {{{ */
1231 {
1232   int status;
1233
1234   ring.head = 0;
1235   ring.tail = 0;
1236   ring.maxLen = buffer_length;
1237   ring.buffer = (long long unsigned int **)malloc(
1238       buffer_length * sizeof(long long unsigned int *));
1239
1240   for (int i = 0; i < buffer_length; i++) {
1241     ring.buffer[i] = (long long unsigned int *)malloc(
1242         PROCEVENT_FIELDS * sizeof(long long unsigned int));
1243   }
1244
1245   status = process_map_refresh();
1246
1247   if (status == -1) {
1248     ERROR("procevent plugin: Initial process mapping failed.");
1249     return (-1);
1250   }
1251
1252   if (ignorelist == NULL) {
1253     NOTICE("procevent plugin: No processes have been configured.");
1254     return (-1);
1255   }
1256
1257   return (start_threads());
1258 } /* }}} int procevent_init */
1259
1260 static int procevent_config(const char *key, const char *value) /* {{{ */
1261 {
1262   int status;
1263
1264   if (ignorelist == NULL)
1265     ignorelist = ignorelist_create(/* invert = */ 1);
1266
1267   if (strcasecmp(key, "BufferLength") == 0) {
1268     buffer_length = atoi(value);
1269   } else if (strcasecmp(key, "Process") == 0) {
1270     ignorelist_add(ignorelist, value);
1271   } else if (strcasecmp(key, "ProcessRegex") == 0) {
1272 #if HAVE_REGEX_H
1273     status = ignorelist_add(ignorelist, value);
1274
1275     if (status != 0) {
1276       ERROR("procevent plugin: invalid regular expression: %s", value);
1277       return (1);
1278     }
1279 #else
1280     WARNING("procevent plugin: The plugin has been compiled without support "
1281             "for the \"ProcessRegex\" option.");
1282 #endif
1283   } else {
1284     return (-1);
1285   }
1286
1287   return (0);
1288 } /* }}} int procevent_config */
1289
1290 static void procevent_dispatch_notification(long pid,
1291                                             const char *type, /* {{{ */
1292                                             gauge_t value, char *process,
1293                                             long long unsigned int timestamp) {
1294   char *buf = NULL;
1295   notification_t n = {NOTIF_FAILURE, cdtime(), "", "", "procevent", "", "", "",
1296                       NULL};
1297
1298   if (value == 1)
1299     n.severity = NOTIF_OKAY;
1300
1301   sstrncpy(n.host, hostname_g, sizeof(n.host));
1302   sstrncpy(n.plugin_instance, process, sizeof(n.plugin_instance));
1303   sstrncpy(n.type, "gauge", sizeof(n.type));
1304   sstrncpy(n.type_instance, "process_status", sizeof(n.type_instance));
1305
1306   gen_message_payload(value, pid, process, timestamp, &buf);
1307
1308   notification_meta_t *m = calloc(1, sizeof(*m));
1309
1310   if (m == NULL) {
1311     char errbuf[1024];
1312     sfree(buf);
1313     ERROR("procevent plugin: unable to allocate metadata: %s",
1314           sstrerror(errno, errbuf, sizeof(errbuf)));
1315     return;
1316   }
1317
1318   sstrncpy(m->name, "ves", sizeof(m->name));
1319   m->nm_value.nm_string = sstrdup(buf);
1320   m->type = NM_TYPE_STRING;
1321   n.meta = m;
1322
1323   DEBUG("procevent plugin: notification message: %s",
1324         n.meta->nm_value.nm_string);
1325
1326   DEBUG("procevent plugin: dispatching state %d for PID %ld (%s)", (int)value,
1327         pid, process);
1328
1329   plugin_dispatch_notification(&n);
1330   plugin_notification_meta_free(n.meta);
1331
1332   // malloc'd in gen_message_payload
1333   if (buf != NULL)
1334     sfree(buf);
1335 }
1336
1337 static int procevent_read(void) /* {{{ */
1338 {
1339   pthread_mutex_lock(&procevent_lock);
1340
1341   if (procevent_netlink_thread_error != 0) {
1342
1343     pthread_mutex_unlock(&procevent_lock);
1344
1345     ERROR("procevent plugin: The netlink thread had a problem. Restarting it.");
1346
1347     stop_netlink_thread(0);
1348
1349     start_netlink_thread();
1350
1351     return (-1);
1352   } /* if (procevent_netlink_thread_error != 0) */
1353
1354   if (procevent_dequeue_thread_error != 0) {
1355
1356     pthread_mutex_unlock(&procevent_lock);
1357
1358     ERROR("procevent plugin: The dequeue thread had a problem. Restarting it.");
1359
1360     stop_dequeue_thread(0);
1361
1362     start_dequeue_thread();
1363
1364     return (-1);
1365   } /* if (procevent_dequeue_thread_error != 0) */
1366
1367   pthread_mutex_unlock(&procevent_lock);
1368
1369   return (0);
1370 } /* }}} int procevent_read */
1371
1372 static int procevent_shutdown(void) /* {{{ */
1373 {
1374   int status;
1375   processlist_t *pl;
1376
1377   DEBUG("procevent plugin: Shutting down threads.");
1378
1379   status = stop_threads(1);
1380
1381   for (int i = 0; i < buffer_length; i++) {
1382     free(ring.buffer[i]);
1383   }
1384
1385   free(ring.buffer);
1386
1387   pl = processlist_head;
1388   while (pl != NULL) {
1389     processlist_t *pl_next;
1390
1391     pl_next = pl->next;
1392
1393     sfree(pl->process);
1394     sfree(pl);
1395
1396     pl = pl_next;
1397   }
1398
1399   return status;
1400 } /* }}} int procevent_shutdown */
1401
1402 void module_register(void) {
1403   plugin_register_config("procevent", procevent_config, config_keys,
1404                          config_keys_num);
1405   plugin_register_init("procevent", procevent_init);
1406   plugin_register_read("procevent", procevent_read);
1407   plugin_register_shutdown("procevent", procevent_shutdown);
1408 } /* void module_register */