More styling + remove superfluous dequeue thread error logic
[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 pthread_t procevent_dequeue_thread_id;
132 static pthread_mutex_t procevent_thread_lock = PTHREAD_MUTEX_INITIALIZER;
133 static pthread_mutex_t procevent_data_lock = PTHREAD_MUTEX_INITIALIZER;
134 static pthread_cond_t procevent_cond = PTHREAD_COND_INITIALIZER;
135 static int nl_sock = -1;
136 static int buffer_length;
137 static circbuf_t ring;
138 static processlist_t *processlist_head = NULL;
139 static int event_id = 0;
140
141 static const char *config_keys[] = {"BufferLength", "Process", "ProcessRegex"};
142 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
143
144 /*
145  * Prototypes
146  */
147
148 static void procevent_dispatch_notification(long pid, const char *type,
149                                             gauge_t value, char *process,
150                                             long long unsigned int timestamp);
151
152 /*
153  * Private functions
154  */
155
156 static int gen_message_payload(int state, long pid, char *process,
157                                long long unsigned int timestamp, char **buf) {
158   const unsigned char *buf2;
159   yajl_gen g;
160   char json_str[DATA_MAX_NAME_LEN];
161
162 #if !defined(HAVE_YAJL_V2)
163   yajl_gen_config conf = {};
164
165   conf.beautify = 0;
166 #endif
167
168 #if HAVE_YAJL_V2
169   size_t len;
170   g = yajl_gen_alloc(NULL);
171   yajl_gen_config(g, yajl_gen_beautify, 0);
172 #else
173   unsigned int len;
174   g = yajl_gen_alloc(&conf, NULL);
175 #endif
176
177   yajl_gen_clear(g);
178
179   // *** BEGIN common event header ***
180
181   if (yajl_gen_map_open(g) != yajl_gen_status_ok)
182     goto err;
183
184   // domain
185   if (yajl_gen_string(g, (u_char *)PROCEVENT_DOMAIN_FIELD,
186                       strlen(PROCEVENT_DOMAIN_FIELD)) != yajl_gen_status_ok)
187     goto err;
188
189   if (yajl_gen_string(g, (u_char *)PROCEVENT_DOMAIN_VALUE,
190                       strlen(PROCEVENT_DOMAIN_VALUE)) != yajl_gen_status_ok)
191     goto err;
192
193   // eventId
194   if (yajl_gen_string(g, (u_char *)PROCEVENT_EVENT_ID_FIELD,
195                       strlen(PROCEVENT_EVENT_ID_FIELD)) != yajl_gen_status_ok)
196     goto err;
197
198   event_id = event_id + 1;
199   int event_id_len = sizeof(char) * sizeof(int) * 4 + 1;
200   memset(json_str, '\0', DATA_MAX_NAME_LEN);
201   snprintf(json_str, event_id_len, "%d", event_id);
202
203   if (yajl_gen_number(g, json_str, strlen(json_str)) != yajl_gen_status_ok) {
204     goto err;
205   }
206
207   // eventName
208   if (yajl_gen_string(g, (u_char *)PROCEVENT_EVENT_NAME_FIELD,
209                       strlen(PROCEVENT_EVENT_NAME_FIELD)) != yajl_gen_status_ok)
210     goto err;
211
212   int event_name_len = 0;
213   event_name_len = event_name_len + (sizeof(char) * sizeof(int) * 4); // pid
214   event_name_len = event_name_len + strlen(process);      // process name
215   event_name_len = event_name_len + (state == 0 ? 4 : 2); // "down" or "up"
216   event_name_len = event_name_len +
217                    13; // "process", 3 spaces, 2 parentheses and null-terminator
218   memset(json_str, '\0', DATA_MAX_NAME_LEN);
219   snprintf(json_str, event_name_len, "process %s (%ld) %s", process, pid,
220            (state == 0 ? PROCEVENT_EVENT_NAME_DOWN_VALUE
221                        : PROCEVENT_EVENT_NAME_UP_VALUE));
222
223   if (yajl_gen_string(g, (u_char *)json_str, strlen(json_str)) !=
224       yajl_gen_status_ok) {
225     goto err;
226   }
227
228   // lastEpochMicrosec
229   if (yajl_gen_string(g, (u_char *)PROCEVENT_LAST_EPOCH_MICROSEC_FIELD,
230                       strlen(PROCEVENT_LAST_EPOCH_MICROSEC_FIELD)) !=
231       yajl_gen_status_ok)
232     goto err;
233
234   int last_epoch_microsec_len =
235       sizeof(char) * sizeof(long long unsigned int) * 4 + 1;
236   memset(json_str, '\0', DATA_MAX_NAME_LEN);
237   snprintf(json_str, last_epoch_microsec_len, "%llu",
238            (long long unsigned int)CDTIME_T_TO_US(cdtime()));
239
240   if (yajl_gen_number(g, json_str, strlen(json_str)) != yajl_gen_status_ok) {
241     goto err;
242   }
243
244   // priority
245   if (yajl_gen_string(g, (u_char *)PROCEVENT_PRIORITY_FIELD,
246                       strlen(PROCEVENT_PRIORITY_FIELD)) != yajl_gen_status_ok)
247     goto err;
248
249   if (yajl_gen_string(g, (u_char *)PROCEVENT_PRIORITY_VALUE,
250                       strlen(PROCEVENT_PRIORITY_VALUE)) != yajl_gen_status_ok)
251     goto err;
252
253   // reportingEntityName
254   if (yajl_gen_string(g, (u_char *)PROCEVENT_REPORTING_ENTITY_NAME_FIELD,
255                       strlen(PROCEVENT_REPORTING_ENTITY_NAME_FIELD)) !=
256       yajl_gen_status_ok)
257     goto err;
258
259   if (yajl_gen_string(g, (u_char *)PROCEVENT_REPORTING_ENTITY_NAME_VALUE,
260                       strlen(PROCEVENT_REPORTING_ENTITY_NAME_VALUE)) !=
261       yajl_gen_status_ok)
262     goto err;
263
264   // sequence
265   if (yajl_gen_string(g, (u_char *)PROCEVENT_SEQUENCE_FIELD,
266                       strlen(PROCEVENT_SEQUENCE_FIELD)) != yajl_gen_status_ok)
267     goto err;
268
269   if (yajl_gen_number(g, PROCEVENT_SEQUENCE_VALUE,
270                       strlen(PROCEVENT_SEQUENCE_VALUE)) != yajl_gen_status_ok)
271     goto err;
272
273   // sourceName
274   if (yajl_gen_string(g, (u_char *)PROCEVENT_SOURCE_NAME_FIELD,
275                       strlen(PROCEVENT_SOURCE_NAME_FIELD)) !=
276       yajl_gen_status_ok)
277     goto err;
278
279   if (yajl_gen_string(g, (u_char *)process, strlen(process)) !=
280       yajl_gen_status_ok)
281     goto err;
282
283   // startEpochMicrosec
284   if (yajl_gen_string(g, (u_char *)PROCEVENT_START_EPOCH_MICROSEC_FIELD,
285                       strlen(PROCEVENT_START_EPOCH_MICROSEC_FIELD)) !=
286       yajl_gen_status_ok)
287     goto err;
288
289   int start_epoch_microsec_len =
290       sizeof(char) * sizeof(long long unsigned int) * 4 + 1;
291   memset(json_str, '\0', DATA_MAX_NAME_LEN);
292   snprintf(json_str, start_epoch_microsec_len, "%llu",
293            (long long unsigned int)timestamp);
294
295   if (yajl_gen_number(g, json_str, strlen(json_str)) != yajl_gen_status_ok) {
296     goto err;
297   }
298
299   // version
300   if (yajl_gen_string(g, (u_char *)PROCEVENT_VERSION_FIELD,
301                       strlen(PROCEVENT_VERSION_FIELD)) != yajl_gen_status_ok)
302     goto err;
303
304   if (yajl_gen_number(g, PROCEVENT_VERSION_VALUE,
305                       strlen(PROCEVENT_VERSION_VALUE)) != yajl_gen_status_ok)
306     goto err;
307
308   // *** END common event header ***
309
310   // *** BEGIN fault fields ***
311
312   if (yajl_gen_string(g, (u_char *)PROCEVENT_FAULT_FIELDS_FIELD,
313                       strlen(PROCEVENT_FAULT_FIELDS_FIELD)) !=
314       yajl_gen_status_ok)
315     goto err;
316
317   if (yajl_gen_map_open(g) != yajl_gen_status_ok)
318     goto err;
319
320   // alarmCondition
321   if (yajl_gen_string(g, (u_char *)PROCEVENT_ALARM_CONDITION_FIELD,
322                       strlen(PROCEVENT_ALARM_CONDITION_FIELD)) !=
323       yajl_gen_status_ok)
324     goto err;
325
326   int alarm_condition_len = 0;
327   alarm_condition_len =
328       alarm_condition_len + (sizeof(char) * sizeof(int) * 4);  // pid
329   alarm_condition_len = alarm_condition_len + strlen(process); // process name
330   alarm_condition_len =
331       alarm_condition_len + 25; // "process", "state", "change", 4 spaces, 2
332                                 // parentheses and null-terminator
333   memset(json_str, '\0', DATA_MAX_NAME_LEN);
334   snprintf(json_str, alarm_condition_len, "process %s (%ld) state change",
335            process, pid);
336
337   if (yajl_gen_string(g, (u_char *)json_str, strlen(json_str)) !=
338       yajl_gen_status_ok) {
339     goto err;
340   }
341
342   // alarmInterfaceA
343   if (yajl_gen_string(g, (u_char *)PROCEVENT_ALARM_INTERFACE_A_FIELD,
344                       strlen(PROCEVENT_ALARM_INTERFACE_A_FIELD)) !=
345       yajl_gen_status_ok)
346     goto err;
347
348   if (yajl_gen_string(g, (u_char *)process, strlen(process)) !=
349       yajl_gen_status_ok)
350     goto err;
351
352   // eventSeverity
353   if (yajl_gen_string(g, (u_char *)PROCEVENT_EVENT_SEVERITY_FIELD,
354                       strlen(PROCEVENT_EVENT_SEVERITY_FIELD)) !=
355       yajl_gen_status_ok)
356     goto err;
357
358   if (yajl_gen_string(
359           g, (u_char *)(state == 0 ? PROCEVENT_EVENT_SEVERITY_CRITICAL_VALUE
360                                    : PROCEVENT_EVENT_SEVERITY_NORMAL_VALUE),
361           strlen((state == 0 ? PROCEVENT_EVENT_SEVERITY_CRITICAL_VALUE
362                              : PROCEVENT_EVENT_SEVERITY_NORMAL_VALUE))) !=
363       yajl_gen_status_ok)
364     goto err;
365
366   // eventSourceType
367   if (yajl_gen_string(g, (u_char *)PROCEVENT_EVENT_SOURCE_TYPE_FIELD,
368                       strlen(PROCEVENT_EVENT_SOURCE_TYPE_FIELD)) !=
369       yajl_gen_status_ok)
370     goto err;
371
372   if (yajl_gen_string(g, (u_char *)PROCEVENT_EVENT_SOURCE_TYPE_VALUE,
373                       strlen(PROCEVENT_EVENT_SOURCE_TYPE_VALUE)) !=
374       yajl_gen_status_ok)
375     goto err;
376
377   // faultFieldsVersion
378   if (yajl_gen_string(g, (u_char *)PROCEVENT_FAULT_FIELDS_VERSION_FIELD,
379                       strlen(PROCEVENT_FAULT_FIELDS_VERSION_FIELD)) !=
380       yajl_gen_status_ok)
381     goto err;
382
383   if (yajl_gen_number(g, PROCEVENT_FAULT_FIELDS_VERSION_VALUE,
384                       strlen(PROCEVENT_FAULT_FIELDS_VERSION_VALUE)) !=
385       yajl_gen_status_ok)
386     goto err;
387
388   // specificProblem
389   if (yajl_gen_string(g, (u_char *)PROCEVENT_SPECIFIC_PROBLEM_FIELD,
390                       strlen(PROCEVENT_SPECIFIC_PROBLEM_FIELD)) !=
391       yajl_gen_status_ok)
392     goto err;
393
394   int specific_problem_len = 0;
395   specific_problem_len =
396       specific_problem_len + (sizeof(char) * sizeof(int) * 4);   // pid
397   specific_problem_len = specific_problem_len + strlen(process); // process name
398   specific_problem_len =
399       specific_problem_len + (state == 0 ? 4 : 2); // "down" or "up"
400   specific_problem_len =
401       specific_problem_len +
402       13; // "process", 3 spaces, 2 parentheses and null-terminator
403   memset(json_str, '\0', DATA_MAX_NAME_LEN);
404   snprintf(json_str, specific_problem_len, "process %s (%ld) %s", process, pid,
405            (state == 0 ? PROCEVENT_SPECIFIC_PROBLEM_DOWN_VALUE
406                        : PROCEVENT_SPECIFIC_PROBLEM_UP_VALUE));
407
408   if (yajl_gen_string(g, (u_char *)json_str, strlen(json_str)) !=
409       yajl_gen_status_ok) {
410     goto err;
411   }
412
413   // vfStatus
414   if (yajl_gen_string(g, (u_char *)PROCEVENT_VF_STATUS_FIELD,
415                       strlen(PROCEVENT_VF_STATUS_FIELD)) != yajl_gen_status_ok)
416     goto err;
417
418   if (yajl_gen_string(
419           g, (u_char *)(state == 0 ? PROCEVENT_VF_STATUS_CRITICAL_VALUE
420                                    : PROCEVENT_VF_STATUS_NORMAL_VALUE),
421           strlen((state == 0 ? PROCEVENT_VF_STATUS_CRITICAL_VALUE
422                              : PROCEVENT_VF_STATUS_NORMAL_VALUE))) !=
423       yajl_gen_status_ok)
424     goto err;
425
426   if (yajl_gen_map_close(g) != yajl_gen_status_ok)
427     goto err;
428
429   // *** END fault fields ***
430
431   if (yajl_gen_map_close(g) != yajl_gen_status_ok)
432     goto err;
433
434   if (yajl_gen_get_buf(g, &buf2, &len) != yajl_gen_status_ok)
435     goto err;
436
437   *buf = strdup((char *)buf2);
438
439   if (*buf == NULL) {
440     ERROR("procevent plugin: strdup failed during gen_message_payload: %s",
441           STRERRNO);
442     goto err;
443   }
444
445   yajl_gen_free(g);
446
447   return 0;
448
449 err:
450   yajl_gen_free(g);
451   ERROR("procevent plugin: gen_message_payload failed to generate JSON");
452   return -1;
453 }
454
455 // Does /proc/<pid>/comm contain a process name we are interested in?
456 // NOTE: Caller MUST hold procevent_data_lock when calling this function
457 static processlist_t *process_check(long pid) {
458   char file[BUFSIZE];
459
460   int len = snprintf(file, sizeof(file), PROCDIR "/%ld/comm", pid);
461
462   if ((len < 0) || (len >= BUFSIZE)) {
463     WARNING("procevent process_check: process name too large");
464     return NULL;
465   }
466
467   FILE *fh;
468
469   if (NULL == (fh = fopen(file, "r"))) {
470     // No /proc/<pid>/comm for this pid, just ignore
471     DEBUG("procevent plugin: no comm file available for pid %ld", pid);
472     return NULL;
473   }
474
475   char buffer[BUFSIZE];
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
589     if (pid > 0) {
590       if (pl->pid == pid)
591         match_pid = 1;
592     }
593
594     int match_process = 0;
595
596     if (process != NULL) {
597       if (strcmp(pl->process, process) == 0)
598         match_process = 1;
599     }
600
601     int match = 0;
602
603     if (pid > 0 && process == NULL && match_pid == 1)
604       match = 1;
605     else if (pid < 0 && process != NULL && match_process == 1)
606       match = 1;
607     else if (pid > 0 && process != NULL && match_pid == 1 && match_process == 1)
608       match = 1;
609
610     if (match == 1) {
611       return pl;
612     }
613   }
614
615   return NULL;
616 }
617
618 static int process_map_refresh(void) {
619   errno = 0;
620   DIR *proc = opendir(PROCDIR);
621
622   if (proc == NULL) {
623     ERROR("procevent plugin: fopen (%s): %s", PROCDIR, STRERRNO);
624     return -1;
625   }
626
627   while (42) {
628     errno = 0;
629     struct dirent *dent = readdir(proc);
630     if (dent == NULL) {
631       if (errno == 0) /* end of directory */
632         break;
633
634       ERROR("procevent plugin: failed to read directory %s: %s", PROCDIR,
635             STRERRNO);
636       closedir(proc);
637       return -1;
638     }
639
640     if (dent->d_name[0] == '.')
641       continue;
642
643     char file[BUFSIZE];
644
645     int len = snprintf(file, sizeof(file), PROCDIR "/%s", dent->d_name);
646     if ((len < 0) || (len >= BUFSIZE))
647       continue;
648
649     struct stat statbuf;
650
651     int status = stat(file, &statbuf);
652     if (status != 0) {
653       WARNING("procevent plugin: stat (%s) failed: %s", file, STRERRNO);
654       continue;
655     }
656
657     if (!S_ISDIR(statbuf.st_mode))
658       continue;
659
660     len = snprintf(file, sizeof(file), PROCDIR "/%s/comm", dent->d_name);
661     if ((len < 0) || (len >= BUFSIZE))
662       continue;
663
664     int not_number = 0;
665
666     for (int i = 0; i < strlen(dent->d_name); i++) {
667       if (!isdigit(dent->d_name[i])) {
668         not_number = 1;
669         break;
670       }
671     }
672
673     if (not_number != 0)
674       continue;
675
676     // Check if we need to store this pid/name combo in our processlist_t linked
677     // list
678     int this_pid = atoi(dent->d_name);
679     pthread_mutex_lock(&procevent_data_lock);
680     processlist_t *pl = process_check(this_pid);
681     pthread_mutex_unlock(&procevent_data_lock);
682
683     if (pl != NULL)
684       DEBUG("procevent plugin: process map refreshed for PID %d and name %s",
685             this_pid, pl->process);
686   }
687
688   closedir(proc);
689
690   return 0;
691 }
692
693 static int nl_connect() {
694   struct sockaddr_nl sa_nl = {
695       .nl_family = AF_NETLINK, .nl_groups = CN_IDX_PROC, .nl_pid = getpid(),
696   };
697
698   nl_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
699   if (nl_sock == -1) {
700     ERROR("procevent plugin: socket open failed: %d", errno);
701     return -1;
702   }
703
704   int rc = bind(nl_sock, (struct sockaddr *)&sa_nl, sizeof(sa_nl));
705   if (rc == -1) {
706     ERROR("procevent plugin: socket bind failed: %d", errno);
707     close(nl_sock);
708     return -1;
709   }
710
711   return 0;
712 }
713
714 static int set_proc_ev_listen(bool enable) {
715   struct __attribute__((aligned(NLMSG_ALIGNTO))) {
716     struct nlmsghdr nl_hdr;
717     struct __attribute__((__packed__)) {
718       struct cn_msg cn_msg;
719       enum proc_cn_mcast_op cn_mcast;
720     };
721   } nlcn_msg;
722
723   memset(&nlcn_msg, 0, sizeof(nlcn_msg));
724   nlcn_msg.nl_hdr.nlmsg_len = sizeof(nlcn_msg);
725   nlcn_msg.nl_hdr.nlmsg_pid = getpid();
726   nlcn_msg.nl_hdr.nlmsg_type = NLMSG_DONE;
727
728   nlcn_msg.cn_msg.id.idx = CN_IDX_PROC;
729   nlcn_msg.cn_msg.id.val = CN_VAL_PROC;
730   nlcn_msg.cn_msg.len = sizeof(enum proc_cn_mcast_op);
731
732   nlcn_msg.cn_mcast = enable ? PROC_CN_MCAST_LISTEN : PROC_CN_MCAST_IGNORE;
733
734   int rc = send(nl_sock, &nlcn_msg, sizeof(nlcn_msg), 0);
735   if (rc == -1) {
736     ERROR("procevent plugin: subscribing to netlink process events failed: %d",
737           errno);
738     return -1;
739   }
740
741   return 0;
742 }
743
744 // Read from netlink socket and write to ring buffer
745 static int read_event() {
746   int recv_flags = MSG_DONTWAIT;
747   struct __attribute__((aligned(NLMSG_ALIGNTO))) {
748     struct nlmsghdr nl_hdr;
749     struct __attribute__((__packed__)) {
750       struct cn_msg cn_msg;
751       struct proc_event proc_ev;
752     };
753   } nlcn_msg;
754
755   if (nl_sock == -1)
756     return 0;
757
758   while (42) {
759     pthread_mutex_lock(&procevent_thread_lock);
760
761     if (procevent_netlink_thread_loop <= 0) {
762       pthread_mutex_unlock(&procevent_thread_lock);
763       return 0;
764     }
765
766     pthread_mutex_unlock(&procevent_thread_lock);
767
768     int status = recv(nl_sock, &nlcn_msg, sizeof(nlcn_msg), recv_flags);
769
770     if (status == 0) {
771       return 0;
772     } else if (status < 0) {
773       if (errno == EAGAIN || errno == EWOULDBLOCK) {
774         pthread_mutex_lock(&procevent_data_lock);
775
776         // There was nothing more to receive for now, so...
777         // If ring head does not equal ring tail, there is data
778         // in the ring buffer for the dequeue thread to read, so
779         // signal it
780         if (ring.head != ring.tail)
781           pthread_cond_signal(&procevent_cond);
782
783         pthread_mutex_unlock(&procevent_data_lock);
784
785         // Since there was nothing to receive, set recv to block and
786         // try again
787         recv_flags = 0;
788         continue;
789       } else if (errno != EINTR) {
790         ERROR("procevent plugin: socket receive error: %d", errno);
791         return -1;
792       }
793     }
794
795     // We successfully received a message, so don't block on the next
796     // read in case there are more (and if there aren't, it will be
797     // handled above in the error-checking)
798     recv_flags = MSG_DONTWAIT;
799
800     int proc_id = -1;
801     int proc_status = -1;
802     int proc_extra = -1;
803
804     switch (nlcn_msg.proc_ev.what) {
805     case PROC_EVENT_NONE:
806     case PROC_EVENT_FORK:
807     case PROC_EVENT_UID:
808     case PROC_EVENT_GID:
809       // Not of interest in current version
810       break;
811     case PROC_EVENT_EXEC:
812       proc_status = PROCEVENT_STARTED;
813       proc_id = nlcn_msg.proc_ev.event_data.exec.process_pid;
814       break;
815     case PROC_EVENT_EXIT:
816       proc_id = nlcn_msg.proc_ev.event_data.exit.process_pid;
817       proc_status = PROCEVENT_EXITED;
818       proc_extra = nlcn_msg.proc_ev.event_data.exit.exit_code;
819       break;
820     default:
821       break;
822     }
823
824     // If we're interested in this process status event, place the event
825     // in the ring buffer for consumption by the main polling thread.
826
827     if (proc_status != -1) {
828       pthread_mutex_lock(&procevent_data_lock);
829
830       int next = ring.head + 1;
831       if (next >= ring.maxLen)
832         next = 0;
833
834       if (next == ring.tail) {
835         // Buffer is full, signal the dequeue thread to process the buffer
836         // and clean it out, and then sleep
837         WARNING("procevent plugin: ring buffer full");
838
839         pthread_cond_signal(&procevent_cond);
840         pthread_mutex_unlock(&procevent_data_lock);
841
842         usleep(1000);
843         continue;
844       } else {
845         DEBUG("procevent plugin: Process %d status is now %s at %llu", proc_id,
846               (proc_status == PROCEVENT_EXITED ? "EXITED" : "STARTED"),
847               (long long unsigned int)CDTIME_T_TO_US(cdtime()));
848
849         if (proc_status == PROCEVENT_EXITED) {
850           ring.buffer[ring.head][0] = proc_id;
851           ring.buffer[ring.head][1] = proc_status;
852           ring.buffer[ring.head][2] = proc_extra;
853           ring.buffer[ring.head][3] =
854               (long long unsigned int)CDTIME_T_TO_US(cdtime());
855         } else {
856           ring.buffer[ring.head][0] = proc_id;
857           ring.buffer[ring.head][1] = proc_status;
858           ring.buffer[ring.head][2] = 0;
859           ring.buffer[ring.head][3] =
860               (long long unsigned int)CDTIME_T_TO_US(cdtime());
861         }
862
863         ring.head = next;
864       }
865
866       pthread_mutex_unlock(&procevent_data_lock);
867     }
868   }
869
870   return 0;
871 }
872
873 // Read from ring buffer and dispatch to write plugins
874 static void read_ring_buffer() {
875   pthread_mutex_lock(&procevent_data_lock);
876
877   // If there's currently nothing to read from the buffer,
878   // then wait
879   if (ring.head == ring.tail)
880     pthread_cond_wait(&procevent_cond, &procevent_data_lock);
881
882   while (ring.head != ring.tail) {
883     int next = ring.tail + 1;
884
885     if (next >= ring.maxLen)
886       next = 0;
887
888     if (ring.buffer[ring.tail][1] == PROCEVENT_EXITED) {
889       processlist_t *pl = process_map_check(ring.buffer[ring.tail][0], NULL);
890
891       if (pl != NULL) {
892         // This process is of interest to us, so publish its EXITED status
893         procevent_dispatch_notification(ring.buffer[ring.tail][0], "gauge",
894                                         ring.buffer[ring.tail][1], pl->process,
895                                         ring.buffer[ring.tail][3]);
896         DEBUG(
897             "procevent plugin: PID %ld (%s) EXITED, removing PID from process "
898             "list",
899             pl->pid, pl->process);
900         pl->pid = -1;
901         pl->last_status = -1;
902       }
903     } else if (ring.buffer[ring.tail][1] == PROCEVENT_STARTED) {
904       // a new process has started, so check if we should monitor it
905       processlist_t *pl = process_check(ring.buffer[ring.tail][0]);
906
907       // If we had already seen this process name and pid combo before,
908       // and the last message was a "process started" message, don't send
909       // the notfication again
910
911       if (pl != NULL && pl->last_status != PROCEVENT_STARTED) {
912         // This process is of interest to us, so publish its STARTED status
913         procevent_dispatch_notification(ring.buffer[ring.tail][0], "gauge",
914                                         ring.buffer[ring.tail][1], pl->process,
915                                         ring.buffer[ring.tail][3]);
916
917         pl->last_status = PROCEVENT_STARTED;
918
919         DEBUG("procevent plugin: PID %ld (%s) STARTED, adding PID to process "
920               "list",
921               pl->pid, pl->process);
922       }
923     }
924
925     ring.tail = next;
926   }
927
928   pthread_mutex_unlock(&procevent_data_lock);
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     read_ring_buffer();
965
966     pthread_mutex_lock(&procevent_thread_lock);
967   } /* while (procevent_dequeue_thread_loop > 0) */
968
969   pthread_mutex_unlock(&procevent_thread_lock);
970
971   return (void *)0;
972 } /* }}} void *procevent_dequeue_thread */
973
974 static int start_netlink_thread(void) /* {{{ */
975 {
976   pthread_mutex_lock(&procevent_thread_lock);
977
978   if (procevent_netlink_thread_loop != 0) {
979     pthread_mutex_unlock(&procevent_thread_lock);
980     return 0;
981   }
982
983   int status;
984
985   if (nl_sock == -1) {
986     status = nl_connect();
987
988     if (status != 0) {
989       pthread_mutex_unlock(&procevent_thread_lock);
990       return status;
991     }
992
993     status = set_proc_ev_listen(true);
994     if (status == -1) {
995       pthread_mutex_unlock(&procevent_thread_lock);
996       return status;
997     }
998   }
999
1000   DEBUG("procevent plugin: socket created and bound");
1001
1002   procevent_netlink_thread_loop = 1;
1003   procevent_netlink_thread_error = 0;
1004
1005   status = plugin_thread_create(&procevent_netlink_thread_id, /* attr = */ NULL,
1006                                 procevent_netlink_thread,
1007                                 /* arg = */ (void *)0, "procevent");
1008   if (status != 0) {
1009     procevent_netlink_thread_loop = 0;
1010     ERROR("procevent plugin: Starting netlink thread failed.");
1011     pthread_mutex_unlock(&procevent_thread_lock);
1012
1013     int status2 = close(nl_sock);
1014
1015     if (status2 != 0) {
1016       ERROR("procevent plugin: failed to close socket %d: %d (%s)", nl_sock,
1017             status2, STRERRNO);
1018     } else
1019       nl_sock = -1;
1020
1021     return -1;
1022   }
1023
1024   pthread_mutex_unlock(&procevent_thread_lock);
1025
1026   return status;
1027 } /* }}} int start_netlink_thread */
1028
1029 static int start_dequeue_thread(void) /* {{{ */
1030 {
1031   pthread_mutex_lock(&procevent_thread_lock);
1032
1033   if (procevent_dequeue_thread_loop != 0) {
1034     pthread_mutex_unlock(&procevent_thread_lock);
1035     return 0;
1036   }
1037
1038   procevent_dequeue_thread_loop = 1;
1039
1040   int status = plugin_thread_create(&procevent_dequeue_thread_id,
1041                                     /* attr = */ NULL, procevent_dequeue_thread,
1042                                     /* arg = */ (void *)0, "procevent");
1043   if (status != 0) {
1044     procevent_dequeue_thread_loop = 0;
1045     ERROR("procevent plugin: Starting dequeue thread failed.");
1046     pthread_mutex_unlock(&procevent_thread_lock);
1047     return -1;
1048   }
1049
1050   pthread_mutex_unlock(&procevent_thread_lock);
1051
1052   return status;
1053 } /* }}} int start_dequeue_thread */
1054
1055 static int start_threads(void) /* {{{ */
1056 {
1057   int status = start_netlink_thread();
1058   int status2 = start_dequeue_thread();
1059
1060   if (status != 0)
1061     return status;
1062   else
1063     return status2;
1064 } /* }}} int start_threads */
1065
1066 static int stop_netlink_thread(int shutdown) /* {{{ */
1067 {
1068   int socket_status;
1069
1070   if (nl_sock != -1) {
1071     socket_status = close(nl_sock);
1072     if (socket_status != 0) {
1073       ERROR("procevent plugin: failed to close socket %d: %d (%s)", nl_sock,
1074             socket_status, strerror(errno));
1075       return -1;
1076     } else
1077       nl_sock = -1;
1078   } else
1079     socket_status = 0;
1080
1081   pthread_mutex_lock(&procevent_thread_lock);
1082
1083   if (procevent_netlink_thread_loop == 0) {
1084     pthread_mutex_unlock(&procevent_thread_lock);
1085     return -1;
1086   }
1087
1088   // Set thread termination status
1089   procevent_netlink_thread_loop = 0;
1090   pthread_mutex_unlock(&procevent_thread_lock);
1091
1092   // Let threads waiting on access to the data know to move
1093   // on such that they'll see the thread's termination status
1094   pthread_cond_broadcast(&procevent_cond);
1095
1096   int thread_status;
1097
1098   if (shutdown == 1) {
1099     // Calling pthread_cancel here in
1100     // the case of a shutdown just assures that the thread is
1101     // gone and that the process has been fully terminated.
1102
1103     DEBUG("procevent plugin: Canceling netlink thread for process shutdown");
1104
1105     thread_status = pthread_cancel(procevent_netlink_thread_id);
1106
1107     if (thread_status != 0 && thread_status != ESRCH) {
1108       ERROR("procevent plugin: Unable to cancel netlink thread: %d",
1109             thread_status);
1110       thread_status = -1;
1111     } else
1112       thread_status = 0;
1113   } else {
1114     thread_status =
1115         pthread_join(procevent_netlink_thread_id, /* return = */ NULL);
1116     if (thread_status != 0 && thread_status != ESRCH) {
1117       ERROR("procevent plugin: Stopping netlink thread failed.");
1118       thread_status = -1;
1119     } else
1120       thread_status = 0;
1121   }
1122
1123   pthread_mutex_lock(&procevent_thread_lock);
1124   memset(&procevent_netlink_thread_id, 0, sizeof(procevent_netlink_thread_id));
1125   procevent_netlink_thread_error = 0;
1126   pthread_mutex_unlock(&procevent_thread_lock);
1127
1128   DEBUG("procevent plugin: Finished requesting stop of netlink thread");
1129
1130   if (socket_status != 0)
1131     return socket_status;
1132   else
1133     return thread_status;
1134 } /* }}} int stop_netlink_thread */
1135
1136 static int stop_dequeue_thread(int shutdown) /* {{{ */
1137 {
1138   int status;
1139
1140   pthread_mutex_lock(&procevent_thread_lock);
1141
1142   if (procevent_dequeue_thread_loop == 0) {
1143     pthread_mutex_unlock(&procevent_thread_lock);
1144     return -1;
1145   }
1146
1147   procevent_dequeue_thread_loop = 0;
1148   pthread_mutex_unlock(&procevent_thread_lock);
1149
1150   pthread_cond_broadcast(&procevent_cond);
1151
1152   if (shutdown == 1) {
1153     // Calling pthread_cancel here in
1154     // the case of a shutdown just assures that the thread is
1155     // gone and that the process has been fully terminated.
1156
1157     DEBUG("procevent plugin: Canceling dequeue thread for process shutdown");
1158
1159     status = pthread_cancel(procevent_dequeue_thread_id);
1160
1161     if (status != 0 && status != ESRCH) {
1162       ERROR("procevent plugin: Unable to cancel dequeue thread: %d", status);
1163       status = -1;
1164     } else
1165       status = 0;
1166   } else {
1167     status = pthread_join(procevent_dequeue_thread_id, /* return = */ NULL);
1168     if (status != 0 && status != ESRCH) {
1169       ERROR("procevent plugin: Stopping dequeue thread failed.");
1170       status = -1;
1171     } else
1172       status = 0;
1173   }
1174
1175   pthread_mutex_lock(&procevent_thread_lock);
1176   memset(&procevent_dequeue_thread_id, 0, sizeof(procevent_dequeue_thread_id));
1177   pthread_mutex_unlock(&procevent_thread_lock);
1178
1179   DEBUG("procevent plugin: Finished requesting stop of dequeue thread");
1180
1181   return status;
1182 } /* }}} int stop_dequeue_thread */
1183
1184 static int stop_threads(int shutdown) /* {{{ */
1185 {
1186   int status = stop_netlink_thread(shutdown);
1187   int status2 = stop_dequeue_thread(shutdown);
1188
1189   if (status != 0)
1190     return status;
1191   else
1192     return status2;
1193 } /* }}} int stop_threads */
1194
1195 static int procevent_init(void) /* {{{ */
1196 {
1197   ring.head = 0;
1198   ring.tail = 0;
1199   ring.maxLen = buffer_length;
1200   ring.buffer = (long long unsigned int **)calloc(
1201       buffer_length, sizeof(long long unsigned int *));
1202
1203   for (int i = 0; i < buffer_length; i++) {
1204     ring.buffer[i] = (long long unsigned int *)calloc(
1205         PROCEVENT_FIELDS, sizeof(long long unsigned int));
1206   }
1207
1208   int status = process_map_refresh();
1209
1210   if (status == -1) {
1211     ERROR("procevent plugin: Initial process mapping failed.");
1212     return -1;
1213   }
1214
1215   if (ignorelist == NULL) {
1216     NOTICE("procevent plugin: No processes have been configured.");
1217     return -1;
1218   }
1219
1220   return start_threads();
1221 } /* }}} int procevent_init */
1222
1223 static int procevent_config(const char *key, const char *value) /* {{{ */
1224 {
1225   if (ignorelist == NULL)
1226     ignorelist = ignorelist_create(/* invert = */ 1);
1227
1228   if (strcasecmp(key, "BufferLength") == 0) {
1229     buffer_length = atoi(value);
1230   } else if (strcasecmp(key, "Process") == 0) {
1231     ignorelist_add(ignorelist, value);
1232   } else if (strcasecmp(key, "ProcessRegex") == 0) {
1233 #if HAVE_REGEX_H
1234     int status = ignorelist_add(ignorelist, value);
1235
1236     if (status != 0) {
1237       ERROR("procevent plugin: invalid regular expression: %s", value);
1238       return 1;
1239     }
1240 #else
1241     WARNING("procevent plugin: The plugin has been compiled without support "
1242             "for the \"ProcessRegex\" option.");
1243 #endif
1244   } else {
1245     return -1;
1246   }
1247
1248   return 0;
1249 } /* }}} int procevent_config */
1250
1251 static void procevent_dispatch_notification(long pid,
1252                                             const char *type, /* {{{ */
1253                                             gauge_t value, char *process,
1254                                             long long unsigned int timestamp) {
1255
1256   notification_t n = {(value == 1 ? NOTIF_OKAY : NOTIF_FAILURE),
1257                       cdtime(),
1258                       "",
1259                       "",
1260                       "procevent",
1261                       "",
1262                       "",
1263                       "",
1264                       NULL};
1265   sstrncpy(n.host, hostname_g, sizeof(n.host));
1266   sstrncpy(n.plugin_instance, process, sizeof(n.plugin_instance));
1267   sstrncpy(n.type, "gauge", sizeof(n.type));
1268   sstrncpy(n.type_instance, "process_status", sizeof(n.type_instance));
1269
1270   char *buf = NULL;
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   pthread_mutex_unlock(&procevent_thread_lock);
1318
1319   return 0;
1320 } /* }}} int procevent_read */
1321
1322 static int procevent_shutdown(void) /* {{{ */
1323 {
1324   DEBUG("procevent plugin: Shutting down threads.");
1325
1326   int status = stop_threads(1);
1327
1328   for (int i = 0; i < buffer_length; i++) {
1329     free(ring.buffer[i]);
1330   }
1331
1332   free(ring.buffer);
1333
1334   processlist_t *pl = processlist_head;
1335   while (pl != NULL) {
1336     processlist_t *pl_next;
1337
1338     pl_next = pl->next;
1339
1340     sfree(pl->process);
1341     sfree(pl);
1342
1343     pl = pl_next;
1344   }
1345
1346   ignorelist_free(ignorelist);
1347
1348   return status;
1349 } /* }}} int procevent_shutdown */
1350
1351 void module_register(void) {
1352   plugin_register_config("procevent", procevent_config, config_keys,
1353                          config_keys_num);
1354   plugin_register_init("procevent", procevent_init);
1355   plugin_register_read("procevent", procevent_read);
1356   plugin_register_shutdown("procevent", procevent_shutdown);
1357 } /* void module_register */