Additional mutex + styling changes
[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_thread_lock = PTHREAD_MUTEX_INITIALIZER;
134 static pthread_mutex_t procevent_data_lock = PTHREAD_MUTEX_INITIALIZER;
135 static pthread_cond_t procevent_cond = PTHREAD_COND_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 = strdup((char *)buf2);
439
440   if (*buf == NULL) {
441     ERROR("procevent plugin: strdup failed during gen_message_payload: %s",
442           STRERRNO);
443     goto err;
444   }
445
446   yajl_gen_free(g);
447
448   return 0;
449
450 err:
451   yajl_gen_free(g);
452   ERROR("procevent plugin: gen_message_payload failed to generate JSON");
453   return -1;
454 }
455
456 // Does /proc/<pid>/comm contain a process name we are interested in?
457 // NOTE: Caller MUST hold procevent_data_lock when calling this function
458 static processlist_t *process_check(long pid) {
459   char file[BUFSIZE];
460   FILE *fh;
461   char buffer[BUFSIZE];
462
463   int len = snprintf(file, sizeof(file), PROCDIR "/%ld/comm", pid);
464
465   if ((len < 0) || (len >= BUFSIZE)) {
466     WARNING("procevent process_check: process name too large");
467     return NULL;
468   }
469
470   if (NULL == (fh = fopen(file, "r"))) {
471     // No /proc/<pid>/comm for this pid, just ignore
472     DEBUG("procevent plugin: no comm file available for pid %ld", pid);
473     return NULL;
474   }
475
476   int retval = fscanf(fh, "%[^\n]", buffer);
477
478   if (retval < 0) {
479     WARNING("procevent process_check: unable to read comm file for pid %ld",
480             pid);
481     fclose(fh);
482     return NULL;
483   }
484
485   // Now that we have the process name in the buffer, check if we are
486   // even interested in it
487   if (ignorelist_match(ignorelist, buffer) != 0) {
488     DEBUG("procevent process_check: ignoring process %s (%ld)", buffer, pid);
489     fclose(fh);
490     return NULL;
491   }
492
493   if (fh != NULL) {
494     fclose(fh);
495     fh = NULL;
496   }
497
498   //
499   // Go through the processlist linked list and look for the process name
500   // in /proc/<pid>/comm.  If found:
501   // 1. If pl->pid is -1, then set pl->pid to <pid> (and return that object)
502   // 2. If pl->pid is not -1, then another <process name> process was already
503   //    found.  If <pid> == pl->pid, this is an old match, so do nothing.
504   //    If the <pid> is different, however, make a new processlist_t and
505   //    associate <pid> with it (with the same process name as the existing).
506   //
507
508   processlist_t *match = NULL;
509
510   for (processlist_t *pl = processlist_head; pl != NULL; pl = pl->next) {
511
512     int is_match = (strcmp(buffer, pl->process) == 0 ? 1 : 0);
513
514     if (is_match == 1) {
515       DEBUG("procevent plugin: process %ld name match for %s", pid, buffer);
516
517       if (pl->pid == pid) {
518         // this is a match, and we've already stored the exact pid/name combo
519         DEBUG("procevent plugin: found exact match with name %s, PID %ld for "
520               "incoming PID %ld",
521               pl->process, pl->pid, pid);
522         match = pl;
523         break;
524       } else if (pl->pid == -1) {
525         // this is a match, and we've found a candidate processlist_t to store
526         // this new pid/name combo
527         DEBUG("procevent plugin: reusing pl object with PID %ld for incoming "
528               "PID %ld",
529               pl->pid, pid);
530         pl->pid = pid;
531         match = pl;
532         break;
533       } else if (pl->pid != -1) {
534         // this is a match, but another instance of this process has already
535         // claimed this pid/name combo,
536         // so keep looking
537         DEBUG("procevent plugin: found pl object with matching name for "
538               "incoming PID %ld, but object is in use by PID %ld",
539               pid, pl->pid);
540         match = pl;
541         continue;
542       }
543     }
544   }
545
546   if (match == NULL ||
547       (match != NULL && match->pid != -1 && match->pid != pid)) {
548     // if there wasn't an existing match, OR
549     // if there was a match but the associated processlist_t object already
550     // contained a pid/name combo,
551     // then make a new one and add it to the linked list
552
553     DEBUG("procevent plugin: allocating new processlist_t object for PID %ld "
554           "(%s)",
555           pid, buffer);
556
557     processlist_t *pl2 = calloc(1, sizeof(*pl2));
558     if (pl2 == NULL) {
559       ERROR("procevent plugin: calloc failed during process_check: %s",
560             STRERRNO);
561       return NULL;
562     }
563
564     char *process = strdup(buffer);
565     if (process == NULL) {
566       sfree(pl2);
567       ERROR("procevent plugin: strdup failed during process_check: %s",
568             STRERRNO);
569       return NULL;
570     }
571
572     pl2->process = process;
573     pl2->pid = pid;
574     pl2->next = processlist_head;
575     processlist_head = pl2;
576
577     match = pl2;
578   }
579
580   return match;
581 }
582
583 // Does our map have this PID or name?
584 // NOTE: Caller MUST hold procevent_data_lock when calling this function
585 static processlist_t *process_map_check(long pid, char *process) {
586   for (processlist_t *pl = processlist_head; pl != NULL; pl = pl->next) {
587     int match_pid = 0;
588     int match_process = 0;
589     int match = 0;
590
591     if (pid > 0) {
592       if (pl->pid == pid)
593         match_pid = 1;
594     }
595
596     if (process != NULL) {
597       if (strcmp(pl->process, process) == 0)
598         match_process = 1;
599     }
600
601     if (pid > 0 && process == NULL && match_pid == 1)
602       match = 1;
603     else if (pid < 0 && process != NULL && match_process == 1)
604       match = 1;
605     else if (pid > 0 && process != NULL && match_pid == 1 && match_process == 1)
606       match = 1;
607
608     if (match == 1) {
609       return pl;
610     }
611   }
612
613   return NULL;
614 }
615
616 static int process_map_refresh(void) {
617   errno = 0;
618   DIR *proc = opendir(PROCDIR);
619
620   if (proc == NULL) {
621     ERROR("procevent plugin: fopen (%s): %s", PROCDIR, STRERRNO);
622     return -1;
623   }
624
625   while (42) {
626     char file[BUFSIZE];
627
628     struct stat statbuf;
629
630     errno = 0;
631     struct dirent *dent = readdir(proc);
632     if (dent == NULL) {
633       if (errno == 0) /* end of directory */
634         break;
635
636       ERROR("procevent plugin: failed to read directory %s: %s", PROCDIR,
637             STRERRNO);
638       closedir(proc);
639       return -1;
640     }
641
642     if (dent->d_name[0] == '.')
643       continue;
644
645     int len = snprintf(file, sizeof(file), PROCDIR "/%s", dent->d_name);
646     if ((len < 0) || (len >= BUFSIZE))
647       continue;
648
649     int status = stat(file, &statbuf);
650     if (status != 0) {
651       WARNING("procevent plugin: stat (%s) failed: %s", file, STRERRNO);
652       continue;
653     }
654
655     if (!S_ISDIR(statbuf.st_mode))
656       continue;
657
658     len = snprintf(file, sizeof(file), PROCDIR "/%s/comm", dent->d_name);
659     if ((len < 0) || (len >= BUFSIZE))
660       continue;
661
662     int not_number = 0;
663
664     for (int i = 0; i < strlen(dent->d_name); i++) {
665       if (!isdigit(dent->d_name[i])) {
666         not_number = 1;
667         break;
668       }
669     }
670
671     if (not_number != 0)
672       continue;
673
674     // Check if we need to store this pid/name combo in our processlist_t linked
675     // list
676     int this_pid = atoi(dent->d_name);
677     pthread_mutex_lock(&procevent_data_lock);
678     processlist_t *pl = process_check(this_pid);
679     pthread_mutex_unlock(&procevent_data_lock);
680
681     if (pl != NULL)
682       DEBUG("procevent plugin: process map refreshed for PID %d and name %s",
683             this_pid, pl->process);
684   }
685
686   closedir(proc);
687
688   return 0;
689 }
690
691 static int nl_connect() {
692   struct sockaddr_nl sa_nl = {
693       .nl_family = AF_NETLINK, .nl_groups = CN_IDX_PROC, .nl_pid = getpid(),
694   };
695
696   nl_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
697   if (nl_sock == -1) {
698     ERROR("procevent plugin: socket open failed: %d", errno);
699     return -1;
700   }
701
702   int rc = bind(nl_sock, (struct sockaddr *)&sa_nl, sizeof(sa_nl));
703   if (rc == -1) {
704     ERROR("procevent plugin: socket bind failed: %d", errno);
705     close(nl_sock);
706     return -1;
707   }
708
709   return 0;
710 }
711
712 static int set_proc_ev_listen(bool enable) {
713   struct __attribute__((aligned(NLMSG_ALIGNTO))) {
714     struct nlmsghdr nl_hdr;
715     struct __attribute__((__packed__)) {
716       struct cn_msg cn_msg;
717       enum proc_cn_mcast_op cn_mcast;
718     };
719   } nlcn_msg;
720
721   memset(&nlcn_msg, 0, sizeof(nlcn_msg));
722   nlcn_msg.nl_hdr.nlmsg_len = sizeof(nlcn_msg);
723   nlcn_msg.nl_hdr.nlmsg_pid = getpid();
724   nlcn_msg.nl_hdr.nlmsg_type = NLMSG_DONE;
725
726   nlcn_msg.cn_msg.id.idx = CN_IDX_PROC;
727   nlcn_msg.cn_msg.id.val = CN_VAL_PROC;
728   nlcn_msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
729
730   nlcn_msg.cn_mcast = enable ? PROC_CN_MCAST_LISTEN : PROC_CN_MCAST_IGNORE;
731
732   int rc = send(nl_sock, &nlcn_msg, sizeof(nlcn_msg), 0);
733   if (rc == -1) {
734     ERROR("procevent plugin: subscribing to netlink process events failed: %d",
735           errno);
736     return -1;
737   }
738
739   return 0;
740 }
741
742 // Read from netlink socket and write to ring buffer
743 static int read_event() {
744   int recv_flags = MSG_DONTWAIT;
745   struct __attribute__((aligned(NLMSG_ALIGNTO))) {
746     struct nlmsghdr nl_hdr;
747     struct __attribute__((__packed__)) {
748       struct cn_msg cn_msg;
749       struct proc_event proc_ev;
750     };
751   } nlcn_msg;
752
753   if (nl_sock == -1)
754     return 0;
755
756   while (42) {
757     int proc_id = -1;
758     int proc_status = -1;
759     int proc_extra = -1;
760
761     pthread_mutex_lock(&procevent_thread_lock);
762
763     if (procevent_netlink_thread_loop <= 0) {
764       pthread_mutex_unlock(&procevent_thread_lock);
765       return 0;
766     }
767
768     pthread_mutex_unlock(&procevent_thread_lock);
769
770     int status = recv(nl_sock, &nlcn_msg, sizeof(nlcn_msg), recv_flags);
771
772     if (status == 0) {
773       return 0;
774     } else if (status < 0) {
775       if (errno == EAGAIN || errno == EWOULDBLOCK) {
776         pthread_mutex_lock(&procevent_data_lock);
777
778         // There was nothing more to receive for now, so...
779         // If ring head does not equal ring tail, there is data
780         // in the ring buffer for the dequeue thread to read, so
781         // signal it
782         if (ring.head != ring.tail)
783           pthread_cond_signal(&procevent_cond);
784
785         pthread_mutex_unlock(&procevent_data_lock);
786
787         // Since there was nothing to receive, set recv to block and
788         // try again
789         recv_flags = 0;
790         continue;
791       } else if (errno != EINTR) {
792         ERROR("procevent plugin: socket receive error: %d", errno);
793         return -1;
794       }
795     }
796
797     // We successfully received a message, so don't block on the next
798     // read in case there are more (and if there aren't, it will be
799     // handled above in the error-checking)
800     recv_flags = MSG_DONTWAIT;
801
802     switch (nlcn_msg.proc_ev.what) {
803     case PROC_EVENT_NONE:
804     case PROC_EVENT_FORK:
805     case PROC_EVENT_UID:
806     case PROC_EVENT_GID:
807       // Not of interest in current version
808       break;
809     case PROC_EVENT_EXEC:
810       proc_status = PROCEVENT_STARTED;
811       proc_id = nlcn_msg.proc_ev.event_data.exec.process_pid;
812       break;
813     case PROC_EVENT_EXIT:
814       proc_id = nlcn_msg.proc_ev.event_data.exit.process_pid;
815       proc_status = PROCEVENT_EXITED;
816       proc_extra = nlcn_msg.proc_ev.event_data.exit.exit_code;
817       break;
818     default:
819       break;
820     }
821
822     // If we're interested in this process status event, place the event
823     // in the ring buffer for consumption by the main polling thread.
824
825     if (proc_status != -1) {
826       pthread_mutex_lock(&procevent_data_lock);
827
828       int next = ring.head + 1;
829       if (next >= ring.maxLen)
830         next = 0;
831
832       if (next == ring.tail) {
833         // Buffer is full, signal the dequeue thread to process the buffer
834         // and clean it out, and then sleep
835         WARNING("procevent plugin: ring buffer full");
836
837         pthread_cond_signal(&procevent_cond);
838         pthread_mutex_unlock(&procevent_data_lock);
839
840         usleep(1000);
841         continue;
842       } else {
843         DEBUG("procevent plugin: Process %d status is now %s at %llu", proc_id,
844               (proc_status == PROCEVENT_EXITED ? "EXITED" : "STARTED"),
845               (long long unsigned int)CDTIME_T_TO_US(cdtime()));
846
847         if (proc_status == PROCEVENT_EXITED) {
848           ring.buffer[ring.head][0] = proc_id;
849           ring.buffer[ring.head][1] = proc_status;
850           ring.buffer[ring.head][2] = proc_extra;
851           ring.buffer[ring.head][3] =
852               (long long unsigned int)CDTIME_T_TO_US(cdtime());
853         } else {
854           ring.buffer[ring.head][0] = proc_id;
855           ring.buffer[ring.head][1] = proc_status;
856           ring.buffer[ring.head][2] = 0;
857           ring.buffer[ring.head][3] =
858               (long long unsigned int)CDTIME_T_TO_US(cdtime());
859         }
860
861         ring.head = next;
862       }
863
864       pthread_mutex_unlock(&procevent_data_lock);
865     }
866   }
867
868   return 0;
869 }
870
871 // Read from ring buffer and dispatch to write plugins
872 static int read_ring_buffer() {
873   pthread_mutex_lock(&procevent_data_lock);
874
875   // If there's currently nothing to read from the buffer,
876   // then wait
877   if (ring.head == ring.tail)
878     pthread_cond_wait(&procevent_cond, &procevent_data_lock);
879
880   while (ring.head != ring.tail) {
881     int next = ring.tail + 1;
882
883     if (next >= ring.maxLen)
884       next = 0;
885
886     if (ring.buffer[ring.tail][1] == PROCEVENT_EXITED) {
887       processlist_t *pl = process_map_check(ring.buffer[ring.tail][0], NULL);
888
889       if (pl != NULL) {
890         // This process is of interest to us, so publish its EXITED status
891         procevent_dispatch_notification(ring.buffer[ring.tail][0], "gauge",
892                                         ring.buffer[ring.tail][1], pl->process,
893                                         ring.buffer[ring.tail][3]);
894         DEBUG(
895             "procevent plugin: PID %ld (%s) EXITED, removing PID from process "
896             "list",
897             pl->pid, pl->process);
898         pl->pid = -1;
899         pl->last_status = -1;
900       }
901     } else if (ring.buffer[ring.tail][1] == PROCEVENT_STARTED) {
902       // a new process has started, so check if we should monitor it
903       processlist_t *pl = process_check(ring.buffer[ring.tail][0]);
904
905       // If we had already seen this process name and pid combo before,
906       // and the last message was a "process started" message, don't send
907       // the notfication again
908
909       if (pl != NULL && pl->last_status != PROCEVENT_STARTED) {
910         // This process is of interest to us, so publish its STARTED status
911         procevent_dispatch_notification(ring.buffer[ring.tail][0], "gauge",
912                                         ring.buffer[ring.tail][1], pl->process,
913                                         ring.buffer[ring.tail][3]);
914
915         pl->last_status = PROCEVENT_STARTED;
916
917         DEBUG("procevent plugin: PID %ld (%s) STARTED, adding PID to process "
918               "list",
919               pl->pid, pl->process);
920       }
921     }
922
923     ring.tail = next;
924   }
925
926   pthread_mutex_unlock(&procevent_data_lock);
927
928   return 0;
929 }
930
931 // Entry point for thread responsible for listening
932 // to netlink socket and writing data to ring buffer
933 static void *procevent_netlink_thread(void *arg) /* {{{ */
934 {
935   pthread_mutex_lock(&procevent_thread_lock);
936
937   while (procevent_netlink_thread_loop > 0) {
938     pthread_mutex_unlock(&procevent_thread_lock);
939
940     int status = read_event();
941
942     pthread_mutex_lock(&procevent_thread_lock);
943
944     if (status < 0) {
945       procevent_netlink_thread_error = 1;
946       break;
947     }
948   } /* while (procevent_netlink_thread_loop > 0) */
949
950   pthread_mutex_unlock(&procevent_thread_lock);
951
952   return ((void *)0);
953 } /* }}} void *procevent_netlink_thread */
954
955 // Entry point for thread responsible for reading from
956 // ring buffer and dispatching notifications
957 static void *procevent_dequeue_thread(void *arg) /* {{{ */
958 {
959   pthread_mutex_lock(&procevent_thread_lock);
960
961   while (procevent_dequeue_thread_loop > 0) {
962     pthread_mutex_unlock(&procevent_thread_lock);
963
964     int status = read_ring_buffer();
965
966     pthread_mutex_lock(&procevent_thread_lock);
967
968     if (status < 0) {
969       procevent_dequeue_thread_error = 1;
970       break;
971     }
972   } /* while (procevent_dequeue_thread_loop > 0) */
973
974   pthread_mutex_unlock(&procevent_thread_lock);
975
976   return ((void *)0);
977 } /* }}} void *procevent_dequeue_thread */
978
979 static int start_netlink_thread(void) /* {{{ */
980 {
981   int status;
982
983   pthread_mutex_lock(&procevent_thread_lock);
984
985   if (procevent_netlink_thread_loop != 0) {
986     pthread_mutex_unlock(&procevent_thread_lock);
987     return (0);
988   }
989
990   if (nl_sock == -1) {
991     status = nl_connect();
992
993     if (status != 0) {
994       pthread_mutex_unlock(&procevent_thread_lock);
995       return status;
996     }
997
998     status = set_proc_ev_listen(true);
999     if (status == -1) {
1000       pthread_mutex_unlock(&procevent_thread_lock);
1001       return status;
1002     }
1003   }
1004
1005   DEBUG("procevent plugin: socket created and bound");
1006
1007   procevent_netlink_thread_loop = 1;
1008   procevent_netlink_thread_error = 0;
1009
1010   status = plugin_thread_create(&procevent_netlink_thread_id, /* attr = */ NULL,
1011                                 procevent_netlink_thread,
1012                                 /* arg = */ (void *)0, "procevent");
1013   if (status != 0) {
1014     procevent_netlink_thread_loop = 0;
1015     ERROR("procevent plugin: Starting netlink thread failed.");
1016     pthread_mutex_unlock(&procevent_thread_lock);
1017
1018     int status2 = close(nl_sock);
1019
1020     if (status2 != 0) {
1021       ERROR("procevent plugin: failed to close socket %d: %d (%s)", nl_sock,
1022             status2, STRERRNO);
1023     } else
1024       nl_sock = -1;
1025
1026     return (-1);
1027   }
1028
1029   pthread_mutex_unlock(&procevent_thread_lock);
1030
1031   return status;
1032 } /* }}} int start_netlink_thread */
1033
1034 static int start_dequeue_thread(void) /* {{{ */
1035 {
1036   int status;
1037
1038   pthread_mutex_lock(&procevent_thread_lock);
1039
1040   if (procevent_dequeue_thread_loop != 0) {
1041     pthread_mutex_unlock(&procevent_thread_lock);
1042     return (0);
1043   }
1044
1045   procevent_dequeue_thread_loop = 1;
1046   procevent_dequeue_thread_error = 0;
1047
1048   status = plugin_thread_create(&procevent_dequeue_thread_id, /* attr = */ NULL,
1049                                 procevent_dequeue_thread,
1050                                 /* arg = */ (void *)0, "procevent");
1051   if (status != 0) {
1052     procevent_dequeue_thread_loop = 0;
1053     ERROR("procevent plugin: Starting dequeue thread failed.");
1054     pthread_mutex_unlock(&procevent_thread_lock);
1055     return (-1);
1056   }
1057
1058   pthread_mutex_unlock(&procevent_thread_lock);
1059
1060   return status;
1061 } /* }}} int start_dequeue_thread */
1062
1063 static int start_threads(void) /* {{{ */
1064 {
1065   int status = start_netlink_thread();
1066   int status2 = start_dequeue_thread();
1067
1068   if (status != 0)
1069     return status;
1070   else
1071     return status2;
1072 } /* }}} int start_threads */
1073
1074 static int stop_netlink_thread(int shutdown) /* {{{ */
1075 {
1076   int socket_status, thread_status;
1077
1078   if (nl_sock != -1) {
1079     socket_status = close(nl_sock);
1080     if (socket_status != 0) {
1081       ERROR("procevent plugin: failed to close socket %d: %d (%s)", nl_sock,
1082             socket_status, strerror(errno));
1083       return (-1);
1084     } else
1085       nl_sock = -1;
1086   } else
1087     socket_status = 0;
1088
1089   pthread_mutex_lock(&procevent_thread_lock);
1090
1091   if (procevent_netlink_thread_loop == 0) {
1092     pthread_mutex_unlock(&procevent_thread_lock);
1093     return (-1);
1094   }
1095
1096   procevent_netlink_thread_loop = 0;
1097   pthread_mutex_unlock(&procevent_thread_lock);
1098
1099   pthread_cond_broadcast(&procevent_cond);
1100
1101   if (shutdown == 1) {
1102     // Calling pthread_cancel here in
1103     // the case of a shutdown just assures that the thread is
1104     // gone and that the process has been fully terminated.
1105
1106     DEBUG("procevent plugin: Canceling netlink thread for process shutdown");
1107
1108     thread_status = pthread_cancel(procevent_netlink_thread_id);
1109
1110     if (thread_status != 0 && thread_status != ESRCH) {
1111       ERROR("procevent plugin: Unable to cancel netlink thread: %d",
1112             thread_status);
1113       thread_status = -1;
1114     } else
1115       thread_status = 0;
1116   } else {
1117     thread_status =
1118         pthread_join(procevent_netlink_thread_id, /* return = */ NULL);
1119     if (thread_status != 0 && thread_status != ESRCH) {
1120       ERROR("procevent plugin: Stopping netlink thread failed.");
1121       thread_status = -1;
1122     } else
1123       thread_status = 0;
1124   }
1125
1126   pthread_mutex_lock(&procevent_thread_lock);
1127   memset(&procevent_netlink_thread_id, 0, sizeof(procevent_netlink_thread_id));
1128   procevent_netlink_thread_error = 0;
1129   pthread_mutex_unlock(&procevent_thread_lock);
1130
1131   DEBUG("procevent plugin: Finished requesting stop of netlink thread");
1132
1133   if (socket_status != 0)
1134     return socket_status;
1135   else
1136     return thread_status;
1137 } /* }}} int stop_netlink_thread */
1138
1139 static int stop_dequeue_thread(int shutdown) /* {{{ */
1140 {
1141   int status;
1142
1143   pthread_mutex_lock(&procevent_thread_lock);
1144
1145   if (procevent_dequeue_thread_loop == 0) {
1146     pthread_mutex_unlock(&procevent_thread_lock);
1147     return (-1);
1148   }
1149
1150   procevent_dequeue_thread_loop = 0;
1151   pthread_mutex_unlock(&procevent_thread_lock);
1152
1153   pthread_cond_broadcast(&procevent_cond);
1154
1155   if (shutdown == 1) {
1156     // Calling pthread_cancel here in
1157     // the case of a shutdown just assures that the thread is
1158     // gone and that the process has been fully terminated.
1159
1160     DEBUG("procevent plugin: Canceling dequeue thread for process shutdown");
1161
1162     status = pthread_cancel(procevent_dequeue_thread_id);
1163
1164     if (status != 0 && status != ESRCH) {
1165       ERROR("procevent plugin: Unable to cancel dequeue thread: %d", status);
1166       status = -1;
1167     } else
1168       status = 0;
1169   } else {
1170     status = pthread_join(procevent_dequeue_thread_id, /* return = */ NULL);
1171     if (status != 0 && status != ESRCH) {
1172       ERROR("procevent plugin: Stopping dequeue thread failed.");
1173       status = -1;
1174     } else
1175       status = 0;
1176   }
1177
1178   pthread_mutex_lock(&procevent_thread_lock);
1179   memset(&procevent_dequeue_thread_id, 0, sizeof(procevent_dequeue_thread_id));
1180   procevent_dequeue_thread_error = 0;
1181   pthread_mutex_unlock(&procevent_thread_lock);
1182
1183   DEBUG("procevent plugin: Finished requesting stop of dequeue thread");
1184
1185   return (status);
1186 } /* }}} int stop_dequeue_thread */
1187
1188 static int stop_threads(int shutdown) /* {{{ */
1189 {
1190   int status = stop_netlink_thread(shutdown);
1191   int status2 = stop_dequeue_thread(shutdown);
1192
1193   if (status < 0)
1194     return status;
1195   else
1196     return status2;
1197 } /* }}} int stop_threads */
1198
1199 static int procevent_init(void) /* {{{ */
1200 {
1201   ring.head = 0;
1202   ring.tail = 0;
1203   ring.maxLen = buffer_length;
1204   ring.buffer = (long long unsigned int **)calloc(
1205       buffer_length, sizeof(long long unsigned int *));
1206
1207   for (int i = 0; i < buffer_length; i++) {
1208     ring.buffer[i] = (long long unsigned int *)calloc(
1209         PROCEVENT_FIELDS, sizeof(long long unsigned int));
1210   }
1211
1212   int status = process_map_refresh();
1213
1214   if (status == -1) {
1215     ERROR("procevent plugin: Initial process mapping failed.");
1216     return (-1);
1217   }
1218
1219   if (ignorelist == NULL) {
1220     NOTICE("procevent plugin: No processes have been configured.");
1221     return (-1);
1222   }
1223
1224   return (start_threads());
1225 } /* }}} int procevent_init */
1226
1227 static int procevent_config(const char *key, const char *value) /* {{{ */
1228 {
1229   if (ignorelist == NULL)
1230     ignorelist = ignorelist_create(/* invert = */ 1);
1231
1232   if (strcasecmp(key, "BufferLength") == 0) {
1233     buffer_length = atoi(value);
1234   } else if (strcasecmp(key, "Process") == 0) {
1235     ignorelist_add(ignorelist, value);
1236   } else if (strcasecmp(key, "ProcessRegex") == 0) {
1237 #if HAVE_REGEX_H
1238     int status = ignorelist_add(ignorelist, value);
1239
1240     if (status != 0) {
1241       ERROR("procevent plugin: invalid regular expression: %s", value);
1242       return (1);
1243     }
1244 #else
1245     WARNING("procevent plugin: The plugin has been compiled without support "
1246             "for the \"ProcessRegex\" option.");
1247 #endif
1248   } else {
1249     return (-1);
1250   }
1251
1252   return (0);
1253 } /* }}} int procevent_config */
1254
1255 static void procevent_dispatch_notification(long pid,
1256                                             const char *type, /* {{{ */
1257                                             gauge_t value, char *process,
1258                                             long long unsigned int timestamp) {
1259   char *buf = NULL;
1260   notification_t n = {NOTIF_FAILURE, cdtime(), "", "", "procevent", "", "", "",
1261                       NULL};
1262
1263   if (value == 1)
1264     n.severity = NOTIF_OKAY;
1265
1266   sstrncpy(n.host, hostname_g, sizeof(n.host));
1267   sstrncpy(n.plugin_instance, process, sizeof(n.plugin_instance));
1268   sstrncpy(n.type, "gauge", sizeof(n.type));
1269   sstrncpy(n.type_instance, "process_status", sizeof(n.type_instance));
1270
1271   gen_message_payload(value, pid, process, timestamp, &buf);
1272
1273   notification_meta_t *m = calloc(1, sizeof(*m));
1274
1275   if (m == NULL) {
1276     sfree(buf);
1277     ERROR("procevent plugin: unable to allocate metadata: %s", STRERRNO);
1278     return;
1279   }
1280
1281   sstrncpy(m->name, "ves", sizeof(m->name));
1282   m->nm_value.nm_string = sstrdup(buf);
1283   m->type = NM_TYPE_STRING;
1284   n.meta = m;
1285
1286   DEBUG("procevent plugin: notification message: %s",
1287         n.meta->nm_value.nm_string);
1288
1289   DEBUG("procevent plugin: dispatching state %d for PID %ld (%s)", (int)value,
1290         pid, process);
1291
1292   plugin_dispatch_notification(&n);
1293   plugin_notification_meta_free(n.meta);
1294
1295   // strdup'd in gen_message_payload
1296   if (buf != NULL)
1297     sfree(buf);
1298 }
1299
1300 static int procevent_read(void) /* {{{ */
1301 {
1302   pthread_mutex_lock(&procevent_thread_lock);
1303
1304   if (procevent_netlink_thread_error != 0) {
1305
1306     pthread_mutex_unlock(&procevent_thread_lock);
1307
1308     ERROR("procevent plugin: The netlink thread had a problem. Restarting it.");
1309
1310     stop_netlink_thread(0);
1311
1312     start_netlink_thread();
1313
1314     return (-1);
1315   } /* if (procevent_netlink_thread_error != 0) */
1316
1317   if (procevent_dequeue_thread_error != 0) {
1318
1319     pthread_mutex_unlock(&procevent_thread_lock);
1320
1321     ERROR("procevent plugin: The dequeue thread had a problem. Restarting it.");
1322
1323     stop_dequeue_thread(0);
1324
1325     start_dequeue_thread();
1326
1327     return (-1);
1328   } /* if (procevent_dequeue_thread_error != 0) */
1329
1330   pthread_mutex_unlock(&procevent_thread_lock);
1331
1332   return (0);
1333 } /* }}} int procevent_read */
1334
1335 static int procevent_shutdown(void) /* {{{ */
1336 {
1337   DEBUG("procevent plugin: Shutting down threads.");
1338
1339   int status = stop_threads(1);
1340
1341   for (int i = 0; i < buffer_length; i++) {
1342     free(ring.buffer[i]);
1343   }
1344
1345   free(ring.buffer);
1346
1347   processlist_t *pl = processlist_head;
1348   while (pl != NULL) {
1349     processlist_t *pl_next;
1350
1351     pl_next = pl->next;
1352
1353     sfree(pl->process);
1354     sfree(pl);
1355
1356     pl = pl_next;
1357   }
1358
1359   return status;
1360 } /* }}} int procevent_shutdown */
1361
1362 void module_register(void) {
1363   plugin_register_config("procevent", procevent_config, config_keys,
1364                          config_keys_num);
1365   plugin_register_init("procevent", procevent_init);
1366   plugin_register_read("procevent", procevent_read);
1367   plugin_register_shutdown("procevent", procevent_shutdown);
1368 } /* void module_register */