mcelog: code review fix
[collectd.git] / src / mcelog.c
1 /*-
2  * collectd - src/mcelog.c
3  * MIT License
4  *
5  * Copyright(c) 2016 Intel Corporation. All rights reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation
10  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11  * and/or sell copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23  * DEALINGS IN THE SOFTWARE.
24
25  * Authors:
26  *   Maryam Tahhan <maryam.tahhan@intel.com>
27  *   Volodymyr Mytnyk <volodymyrx.mytnyk@intel.com>
28  *   Taras Chornyi <tarasx.chornyi@intel.com>
29  *   Krzysztof Matczak <krzysztofx.matczak@intel.com>
30  */
31
32 #include "collectd.h"
33
34 #include "common.h"
35 #include "utils_llist.h"
36
37 #include <poll.h>
38 #include <sys/socket.h>
39 #include <sys/un.h>
40 #include <unistd.h>
41
42 #define MCELOG_PLUGIN "mcelog"
43 #define MCELOG_BUFF_SIZE 1024
44 #define MCELOG_POLL_TIMEOUT 1000 /* ms */
45 #define MCELOG_SOCKET_STR "SOCKET"
46 #define MCELOG_DIMM_NAME "DMI_NAME"
47 #define MCELOG_CORRECTED_ERR "corrected memory errors"
48 #define MCELOG_UNCORRECTED_ERR "uncorrected memory errors"
49 #define MCELOG_CORRECTED_ERR_TYPE_INS "corrected_memory_errors"
50 #define MCELOG_UNCORRECTED_ERR_TYPE_INS "uncorrected_memory_errors"
51
52 typedef struct mcelog_config_s {
53   char logfile[PATH_MAX]; /* mcelog logfile */
54   pthread_t tid;          /* poll thread id */
55   llist_t *dimms_list;    /* DIMMs list */
56   /* lock for dimms cache */
57   pthread_mutex_t dimms_lock;
58 } mcelog_config_t;
59
60 typedef struct socket_adapter_s socket_adapter_t;
61
62 struct socket_adapter_s {
63   int sock_fd;                  /* mcelog server socket fd */
64   struct sockaddr_un unix_sock; /* mcelog client socket */
65   pthread_rwlock_t lock;
66   /* function pointers for socket operations */
67   int (*write)(socket_adapter_t *self, const char *msg, const size_t len);
68   int (*reinit)(socket_adapter_t *self);
69   int (*receive)(socket_adapter_t *self, FILE **p_file);
70   int (*close)(socket_adapter_t *self);
71 };
72
73 typedef struct mcelog_memory_rec_s {
74   int corrected_err_total; /* x total*/
75   int corrected_err_timed; /* x in 24h*/
76   char corrected_err_timed_period[DATA_MAX_NAME_LEN];
77   int uncorrected_err_total; /* x total*/
78   int uncorrected_err_timed; /* x in 24h*/
79   char uncorrected_err_timed_period[DATA_MAX_NAME_LEN];
80   char location[DATA_MAX_NAME_LEN];  /* SOCKET x CHANNEL x DIMM x*/
81   char dimm_name[DATA_MAX_NAME_LEN]; /* DMI_NAME "DIMM_F1" */
82 } mcelog_memory_rec_t;
83
84 static int socket_close(socket_adapter_t *self);
85 static int socket_write(socket_adapter_t *self, const char *msg,
86                         const size_t len);
87 static int socket_reinit(socket_adapter_t *self);
88 static int socket_receive(socket_adapter_t *self, FILE **p_file);
89
90 static mcelog_config_t g_mcelog_config = {
91     .logfile = "/var/log/mcelog",
92 };
93
94 static socket_adapter_t socket_adapter = {
95     .sock_fd = -1,
96     .unix_sock =
97         {
98             .sun_family = AF_UNIX, .sun_path = "/var/run/mcelog-client",
99         },
100     .lock = PTHREAD_RWLOCK_INITIALIZER,
101     .close = socket_close,
102     .write = socket_write,
103     .reinit = socket_reinit,
104     .receive = socket_receive,
105 };
106
107 static _Bool mcelog_thread_running;
108
109 static void mcelog_free_dimms_list_records(llist_t *dimms_list) {
110
111   for (llentry_t *e = llist_head(dimms_list); e != NULL; e = e->next) {
112     sfree(e->key);
113     sfree(e->value);
114   }
115
116 }
117
118 /* Create or get dimm by dimm name/location */
119 static llentry_t *mcelog_dimm(const mcelog_memory_rec_t *rec,
120                               llist_t *dimms_list) {
121
122   char dimm_name[DATA_MAX_NAME_LEN];
123
124   if (strlen(rec->dimm_name) > 0) {
125     ssnprintf(dimm_name, sizeof(dimm_name), "%s_%s", rec->location,
126               rec->dimm_name);
127   } else
128     sstrncpy(dimm_name, rec->location, sizeof(dimm_name));
129
130   llentry_t *dimm_le = llist_search(g_mcelog_config.dimms_list, dimm_name);
131
132   if (dimm_le == NULL) {
133     mcelog_memory_rec_t *dimm_mr = calloc(1, sizeof(*dimm_mr));
134     if (dimm_mr == NULL) {
135       ERROR(MCELOG_PLUGIN ": Error allocating dimm memory item");
136       return NULL;
137     }
138     char *p_name = strdup(dimm_name);
139     if (p_name == NULL) {
140       ERROR(MCELOG_PLUGIN ": strdup: error");
141       return NULL;
142     }
143
144     /* add new dimm */
145     dimm_le = llentry_create(p_name, dimm_mr);
146     if (dimm_le == NULL) {
147       ERROR(MCELOG_PLUGIN ": llentry_create(): error");
148       free(dimm_mr);
149       return NULL;
150     }
151     pthread_mutex_lock(&g_mcelog_config.dimms_lock);
152     llist_append(g_mcelog_config.dimms_list, dimm_le);
153     pthread_mutex_unlock(&g_mcelog_config.dimms_lock);
154   }
155
156   return dimm_le;
157 }
158
159 static void mcelog_update_dimm_stats(llentry_t *dimm,
160                                      const mcelog_memory_rec_t *rec) {
161   pthread_mutex_lock(&g_mcelog_config.dimms_lock);
162   memcpy(dimm->value, rec, sizeof(mcelog_memory_rec_t));
163   pthread_mutex_unlock(&g_mcelog_config.dimms_lock);
164
165 }
166
167 static int mcelog_config(oconfig_item_t *ci) {
168   for (int i = 0; i < ci->children_num; i++) {
169     oconfig_item_t *child = ci->children + i;
170     if (strcasecmp("McelogClientSocket", child->key) == 0) {
171       if (cf_util_get_string_buffer(child, socket_adapter.unix_sock.sun_path,
172                                     sizeof(socket_adapter.unix_sock.sun_path)) <
173           0) {
174         ERROR(MCELOG_PLUGIN ": Invalid configuration option: \"%s\".",
175               child->key);
176         return (-1);
177       }
178     } else if (strcasecmp("McelogLogfile", child->key) == 0) {
179       if (cf_util_get_string_buffer(child, g_mcelog_config.logfile,
180                                     sizeof(g_mcelog_config.logfile)) < 0) {
181         ERROR(MCELOG_PLUGIN ": Invalid configuration option: \"%s\".",
182               child->key);
183         return (-1);
184       }
185     } else {
186       ERROR(MCELOG_PLUGIN ": Invalid configuration option: \"%s\".",
187             child->key);
188       return (-1);
189     }
190   }
191   return (0);
192 }
193
194 static int socket_close(socket_adapter_t *self) {
195   int ret = 0;
196   pthread_rwlock_rdlock(&self->lock);
197   if (fcntl(self->sock_fd, F_GETFL) != -1) {
198     char errbuf[MCELOG_BUFF_SIZE];
199     if (shutdown(self->sock_fd, SHUT_RDWR) != 0) {
200       ERROR(MCELOG_PLUGIN ": Socket shutdown failed: %s",
201             sstrerror(errno, errbuf, sizeof(errbuf)));
202       ret = -1;
203     }
204     if (close(self->sock_fd) != 0) {
205       ERROR(MCELOG_PLUGIN ": Socket close failed: %s",
206             sstrerror(errno, errbuf, sizeof(errbuf)));
207       ret = -1;
208     }
209   }
210   pthread_rwlock_unlock(&self->lock);
211   return (ret);
212 }
213
214 static int socket_write(socket_adapter_t *self, const char *msg,
215                         const size_t len) {
216   int ret = 0;
217   pthread_rwlock_rdlock(&self->lock);
218   if (swrite(self->sock_fd, msg, len) < 0)
219     ret = -1;
220   pthread_rwlock_unlock(&self->lock);
221   return (ret);
222 }
223
224 static void mcelog_dispatch_notification(notification_t *n) {
225   if (!n) {
226     ERROR(MCELOG_PLUGIN ": %s: NULL pointer", __FUNCTION__);
227     return;
228   }
229
230   sstrncpy(n->host, hostname_g, sizeof(n->host));
231   sstrncpy(n->type, "gauge", sizeof(n->type));
232   plugin_dispatch_notification(n);
233   if (n->meta)
234     plugin_notification_meta_free(n->meta);
235 }
236
237 static int socket_reinit(socket_adapter_t *self) {
238   char errbuff[MCELOG_BUFF_SIZE];
239   int ret = -1;
240   cdtime_t interval = plugin_get_interval();
241   struct timeval socket_timeout = CDTIME_T_TO_TIMEVAL(interval);
242
243   /* synchronization via write lock since sock_fd may be changed here */
244   pthread_rwlock_wrlock(&self->lock);
245   self->sock_fd =
246       socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
247   if (self->sock_fd < 0) {
248     ERROR(MCELOG_PLUGIN ": Could not create a socket. %s",
249           sstrerror(errno, errbuff, sizeof(errbuff)));
250     pthread_rwlock_unlock(&self->lock);
251     return (ret);
252   }
253
254   /* Set socket timeout option */
255   if (setsockopt(self->sock_fd, SOL_SOCKET, SO_SNDTIMEO, &socket_timeout,
256                  sizeof(socket_timeout)) < 0)
257     ERROR(MCELOG_PLUGIN ": Failed to set the socket timeout option.");
258
259   /* downgrading to read lock due to possible recursive read locks
260    * in self->close(self) call */
261   pthread_rwlock_unlock(&self->lock);
262   pthread_rwlock_rdlock(&self->lock);
263   if (connect(self->sock_fd, (struct sockaddr *)&(self->unix_sock),
264               sizeof(self->unix_sock)) < 0) {
265     ERROR(MCELOG_PLUGIN ": Failed to connect to mcelog server. %s",
266           sstrerror(errno, errbuff, sizeof(errbuff)));
267     self->close(self);
268     ret = -1;
269   } else {
270     ret = 0;
271     mcelog_dispatch_notification(
272         &(notification_t){.severity = NOTIF_OKAY,
273                           .time = cdtime(),
274                           .message = "Connected to mcelog server",
275                           .plugin = MCELOG_PLUGIN,
276                           .type_instance = "mcelog_status"});
277   }
278   pthread_rwlock_unlock(&self->lock);
279   return (ret);
280 }
281
282 static int mcelog_dispatch_mem_notifications(const mcelog_memory_rec_t *mr) {
283   notification_t n = {.severity = NOTIF_WARNING,
284                       .time = cdtime(),
285                       .plugin = MCELOG_PLUGIN,
286                       .type = "errors"};
287
288   int dispatch_corrected_notifs = 0, dispatch_uncorrected_notifs = 0;
289
290   if (mr == NULL)
291     return (-1);
292
293   llentry_t *dimm = mcelog_dimm(mr, g_mcelog_config.dimms_list);
294   if (dimm == NULL) {
295       ERROR(MCELOG_PLUGIN ": Error adding/getting dimm memory item to/from cache");
296       return -1;
297   }
298
299   mcelog_memory_rec_t *mr_old = dimm->value;
300
301   if (mr_old->corrected_err_total != mr->corrected_err_total ||
302       mr_old->corrected_err_timed != mr->corrected_err_timed)
303     dispatch_corrected_notifs = 1;
304
305   if (mr_old->uncorrected_err_total != mr->uncorrected_err_total ||
306       mr_old->uncorrected_err_timed != mr->uncorrected_err_timed)
307     dispatch_uncorrected_notifs = 1;
308
309   if (!dispatch_corrected_notifs && !dispatch_uncorrected_notifs) {
310     DEBUG("%s: No new notifications to dispatch", MCELOG_PLUGIN);
311     return (0);
312   }
313
314   sstrncpy(n.host, hostname_g, sizeof(n.host));
315
316   if (mr->dimm_name[0] != '\0')
317     ssnprintf(n.plugin_instance, sizeof(n.plugin_instance), "%s_%s",
318               mr->location, mr->dimm_name);
319   else
320     sstrncpy(n.plugin_instance, mr->location, sizeof(n.plugin_instance));
321
322   if (dispatch_corrected_notifs) {
323     /* Corrected Error Notifications */
324     if (mr->corrected_err_total > 0 || mr->corrected_err_timed > 0) {
325       if (plugin_notification_meta_add_signed_int(
326               &n, MCELOG_CORRECTED_ERR, mr->corrected_err_total) < 0) {
327         ERROR(MCELOG_PLUGIN ": add corrected errors meta data failed");
328         plugin_notification_meta_free(n.meta);
329         return (-1);
330       }
331       if (plugin_notification_meta_add_signed_int(
332               &n, "corrected memory timed errors", mr->corrected_err_timed) <
333           0) {
334         ERROR(MCELOG_PLUGIN ": add corrected timed errors meta data failed");
335         plugin_notification_meta_free(n.meta);
336         return (-1);
337       }
338       ssnprintf(n.message, sizeof(n.message), "Corrected Memory Errors");
339       sstrncpy(n.type_instance, MCELOG_CORRECTED_ERR_TYPE_INS,
340                sizeof(n.type_instance));
341       plugin_dispatch_notification(&n);
342
343       if (n.meta)
344         plugin_notification_meta_free(n.meta);
345     }
346   }
347
348   if (dispatch_uncorrected_notifs) {
349     /* Uncorrected Error Notifications */
350     if (mr->uncorrected_err_total > 0 || mr->uncorrected_err_timed > 0) {
351       if (plugin_notification_meta_add_signed_int(
352               &n, MCELOG_UNCORRECTED_ERR, mr->uncorrected_err_total) < 0) {
353         ERROR(MCELOG_PLUGIN ": add uncorrected errors meta data failed");
354         plugin_notification_meta_free(n.meta);
355         return (-1);
356       }
357       if (plugin_notification_meta_add_signed_int(
358               &n, "uncorrected memory timed errors",
359               mr->uncorrected_err_timed) < 0) {
360         ERROR(MCELOG_PLUGIN ": add uncorrected timed errors meta data failed");
361         plugin_notification_meta_free(n.meta);
362         return (-1);
363       }
364       ssnprintf(n.message, sizeof(n.message), "Uncorrected Memory Errors");
365       sstrncpy(n.type_instance, MCELOG_UNCORRECTED_ERR_TYPE_INS,
366                sizeof(n.type_instance));
367       n.severity = NOTIF_FAILURE;
368       plugin_dispatch_notification(&n);
369
370       if (n.meta)
371         plugin_notification_meta_free(n.meta);
372     }
373   }
374
375   return (0);
376 }
377
378 static int mcelog_submit(const mcelog_memory_rec_t *mr) {
379
380   if (!mr) {
381     ERROR(MCELOG_PLUGIN ": %s: NULL pointer", __FUNCTION__);
382     return (-1);
383   }
384
385   llentry_t *dimm = mcelog_dimm(mr, g_mcelog_config.dimms_list);
386   if (dimm == NULL) {
387       ERROR(MCELOG_PLUGIN ": Error adding/getting dimm memory item to/from cache");
388       return -1;
389   }
390
391   value_list_t vl = {
392       .values_len = 1,
393       .values = &(value_t){.derive = (derive_t)mr->corrected_err_total},
394       .time = cdtime(),
395       .plugin = MCELOG_PLUGIN,
396       .type = "errors",
397       .type_instance = MCELOG_CORRECTED_ERR_TYPE_INS};
398
399   mcelog_update_dimm_stats(dimm, mr);
400
401   if (mr->dimm_name[0] != '\0')
402     ssnprintf(vl.plugin_instance, sizeof(vl.plugin_instance), "%s_%s",
403               mr->location, mr->dimm_name);
404   else
405     sstrncpy(vl.plugin_instance, mr->location, sizeof(vl.plugin_instance));
406
407   plugin_dispatch_values(&vl);
408
409   ssnprintf(vl.type_instance, sizeof(vl.type_instance),
410             "corrected_memory_errors_in_%s", mr->corrected_err_timed_period);
411   vl.values = &(value_t){.derive = (derive_t)mr->corrected_err_timed};
412   plugin_dispatch_values(&vl);
413
414   sstrncpy(vl.type_instance, MCELOG_UNCORRECTED_ERR_TYPE_INS,
415            sizeof(vl.type_instance));
416   vl.values = &(value_t){.derive = (derive_t)mr->uncorrected_err_total};
417   plugin_dispatch_values(&vl);
418
419   ssnprintf(vl.type_instance, sizeof(vl.type_instance),
420             "uncorrected_memory_errors_in_%s",
421             mr->uncorrected_err_timed_period);
422   vl.values = &(value_t){.derive = (derive_t)mr->uncorrected_err_timed};
423   plugin_dispatch_values(&vl);
424
425   return (0);
426 }
427
428 static int parse_memory_info(FILE *p_file, mcelog_memory_rec_t *memory_record) {
429   char buf[DATA_MAX_NAME_LEN] = {0};
430   while (fgets(buf, sizeof(buf), p_file)) {
431     /* Got empty line or "done" */
432     if ((!strncmp("\n", buf, strlen(buf))) ||
433         (!strncmp(buf, "done\n", strlen(buf))))
434       return (1);
435     if (strlen(buf) < 5)
436       continue;
437     if (!strncmp(buf, MCELOG_SOCKET_STR, strlen(MCELOG_SOCKET_STR))) {
438       sstrncpy(memory_record->location, buf, strlen(buf));
439       /* replace spaces with '_' */
440       for (size_t i = 0; i < strlen(memory_record->location); i++)
441         if (memory_record->location[i] == ' ')
442           memory_record->location[i] = '_';
443       DEBUG(MCELOG_PLUGIN ": Got SOCKET INFO %s", memory_record->location);
444     }
445     if (!strncmp(buf, MCELOG_DIMM_NAME, strlen(MCELOG_DIMM_NAME))) {
446       char *name = NULL;
447       char *saveptr = NULL;
448       name = strtok_r(buf, "\"", &saveptr);
449       if (name != NULL && saveptr != NULL) {
450         name = strtok_r(NULL, "\"", &saveptr);
451         if (name != NULL) {
452           sstrncpy(memory_record->dimm_name, name,
453                    sizeof(memory_record->dimm_name));
454           DEBUG(MCELOG_PLUGIN ": Got DIMM NAME %s", memory_record->dimm_name);
455         }
456       }
457     }
458     if (!strncmp(buf, MCELOG_CORRECTED_ERR, strlen(MCELOG_CORRECTED_ERR))) {
459       /* Get next line*/
460       if (fgets(buf, sizeof(buf), p_file) != NULL) {
461         sscanf(buf, "\t%d total", &(memory_record->corrected_err_total));
462         DEBUG(MCELOG_PLUGIN ": Got corrected error total %d",
463               memory_record->corrected_err_total);
464       }
465       if (fgets(buf, sizeof(buf), p_file) != NULL) {
466         sscanf(buf, "\t%d in %s", &(memory_record->corrected_err_timed),
467                memory_record->corrected_err_timed_period);
468         DEBUG(MCELOG_PLUGIN ": Got timed corrected errors %d in %s",
469               memory_record->corrected_err_total,
470               memory_record->corrected_err_timed_period);
471       }
472     }
473     if (!strncmp(buf, MCELOG_UNCORRECTED_ERR, strlen(MCELOG_UNCORRECTED_ERR))) {
474       if (fgets(buf, sizeof(buf), p_file) != NULL) {
475         sscanf(buf, "\t%d total", &(memory_record->uncorrected_err_total));
476         DEBUG(MCELOG_PLUGIN ": Got uncorrected error total %d",
477               memory_record->uncorrected_err_total);
478       }
479       if (fgets(buf, sizeof(buf), p_file) != NULL) {
480         sscanf(buf, "\t%d in %s", &(memory_record->uncorrected_err_timed),
481                memory_record->uncorrected_err_timed_period);
482         DEBUG(MCELOG_PLUGIN ": Got timed uncorrected errors %d in %s",
483               memory_record->uncorrected_err_total,
484               memory_record->uncorrected_err_timed_period);
485       }
486     }
487     memset(buf, 0, sizeof(buf));
488   }
489   /* parsing definitely finished */
490   return (0);
491 }
492
493 static void poll_worker_cleanup(void *arg) {
494   mcelog_thread_running = 0;
495   FILE *p_file = *((FILE **)arg);
496   if (p_file != NULL)
497     fclose(p_file);
498   free(arg);
499 }
500
501 static int socket_receive(socket_adapter_t *self, FILE **pp_file) {
502   int res = -1;
503   pthread_rwlock_rdlock(&self->lock);
504   struct pollfd poll_fd = {
505       .fd = self->sock_fd, .events = POLLIN | POLLPRI,
506   };
507
508   if ((res = poll(&poll_fd, 1, MCELOG_POLL_TIMEOUT)) <= 0) {
509     if (res != 0 && errno != EINTR) {
510       char errbuf[MCELOG_BUFF_SIZE];
511       ERROR("mcelog: poll failed: %s",
512             sstrerror(errno, errbuf, sizeof(errbuf)));
513     }
514     pthread_rwlock_unlock(&self->lock);
515     return (res);
516   }
517
518   if (poll_fd.revents & (POLLERR | POLLHUP | POLLNVAL)) {
519     /* connection is broken */
520     ERROR(MCELOG_PLUGIN ": Connection to socket is broken");
521     if (poll_fd.revents & (POLLERR | POLLHUP)) {
522       mcelog_dispatch_notification(
523           &(notification_t){.severity = NOTIF_FAILURE,
524                             .time = cdtime(),
525                             .message = "Connection to mcelog socket is broken.",
526                             .plugin = MCELOG_PLUGIN,
527                             .type_instance = "mcelog_status"});
528     }
529     pthread_rwlock_unlock(&self->lock);
530     return (-1);
531   }
532
533   if (!(poll_fd.revents & (POLLIN | POLLPRI))) {
534     INFO(MCELOG_PLUGIN ": No data to read");
535     pthread_rwlock_unlock(&self->lock);
536     return (0);
537   }
538
539   if ((*pp_file = fdopen(dup(self->sock_fd), "r")) == NULL)
540     res = -1;
541
542   pthread_rwlock_unlock(&self->lock);
543   return (res);
544 }
545
546 static void *poll_worker(__attribute__((unused)) void *arg) {
547   char errbuf[MCELOG_BUFF_SIZE];
548   mcelog_thread_running = 1;
549   FILE **pp_file = calloc(1, sizeof(*pp_file));
550   if (pp_file == NULL) {
551     ERROR("mcelog: memory allocation failed: %s",
552           sstrerror(errno, errbuf, sizeof(errbuf)));
553     pthread_exit((void *)1);
554   }
555
556   pthread_cleanup_push(poll_worker_cleanup, pp_file);
557
558   while (1) {
559     /* blocking call */
560     int res = socket_adapter.receive(&socket_adapter, pp_file);
561     if (res < 0) {
562       socket_adapter.close(&socket_adapter);
563       while (socket_adapter.reinit(&socket_adapter) != 0) {
564         nanosleep(&CDTIME_T_TO_TIMESPEC(MS_TO_CDTIME_T(MCELOG_POLL_TIMEOUT)),
565                   NULL);
566       }
567       continue;
568     }
569     /* timeout or no data to read */
570     else if (res == 0)
571       continue;
572
573     if (*pp_file == NULL)
574       continue;
575
576     mcelog_memory_rec_t memory_record = {0};
577     while (parse_memory_info(*pp_file, &memory_record)) {
578       /* Check if location was successfully parsed */
579       if (memory_record.location[0] == '\0') {
580         memset(&memory_record, 0, sizeof(memory_record));
581         continue;
582       }
583
584       if (mcelog_dispatch_mem_notifications(&memory_record) != 0)
585         ERROR(MCELOG_PLUGIN ": Failed to submit memory errors notification");
586       if (mcelog_submit(&memory_record) != 0)
587         ERROR(MCELOG_PLUGIN ": Failed to submit memory errors");
588       memset(&memory_record, 0, sizeof(memory_record));
589     }
590
591     fclose(*pp_file);
592     *pp_file = NULL;
593   }
594
595   mcelog_thread_running = 0;
596   pthread_cleanup_pop(1);
597   return (NULL);
598 }
599
600 static int mcelog_init(void) {
601   g_mcelog_config.dimms_list = llist_create();
602   int err = pthread_mutex_init(&g_mcelog_config.dimms_lock, NULL);
603   if (err < 0) {
604     ERROR(MCELOG_PLUGIN ": plugin: failed to initialize cache lock");
605     return (-1);
606   }
607
608   if (socket_adapter.reinit(&socket_adapter) != 0) {
609     ERROR(MCELOG_PLUGIN ": Cannot connect to client socket");
610     return (-1);
611   }
612
613   if (plugin_thread_create(&g_mcelog_config.tid, NULL, poll_worker, NULL,
614                            NULL) != 0) {
615     ERROR(MCELOG_PLUGIN ": Error creating poll thread.");
616     return (-1);
617   }
618   return (0);
619 }
620
621 static int get_memory_machine_checks(void) {
622   static const char dump[] = "dump all bios\n";
623   int ret = socket_adapter.write(&socket_adapter, dump, sizeof(dump));
624   if (ret != 0)
625     ERROR(MCELOG_PLUGIN ": SENT DUMP REQUEST FAILED");
626   else
627     DEBUG(MCELOG_PLUGIN ": SENT DUMP REQUEST OK");
628   return (ret);
629 }
630
631 static int mcelog_read(__attribute__((unused)) user_data_t *ud) {
632   DEBUG(MCELOG_PLUGIN ": %s", __FUNCTION__);
633
634   if (get_memory_machine_checks() != 0)
635     ERROR(MCELOG_PLUGIN ": MACHINE CHECK INFO NOT AVAILABLE");
636
637   return (0);
638 }
639
640 static int mcelog_shutdown(void) {
641   int ret = 0;
642   if (mcelog_thread_running) {
643     pthread_cancel(g_mcelog_config.tid);
644     if (pthread_join(g_mcelog_config.tid, NULL) != 0) {
645       ERROR(MCELOG_PLUGIN ": Stopping thread failed.");
646       ret = -1;
647     }
648   }
649   pthread_mutex_lock(&g_mcelog_config.dimms_lock);
650   mcelog_free_dimms_list_records(g_mcelog_config.dimms_list);
651   llist_destroy(g_mcelog_config.dimms_list);
652   pthread_mutex_unlock(&g_mcelog_config.dimms_lock);
653   pthread_mutex_destroy(&g_mcelog_config.dimms_lock);
654   g_mcelog_config.dimms_list = NULL;
655   ret = socket_adapter.close(&socket_adapter) || ret;
656   pthread_rwlock_destroy(&(socket_adapter.lock));
657   return (-ret);
658 }
659
660 void module_register(void) {
661   plugin_register_complex_config(MCELOG_PLUGIN, mcelog_config);
662   plugin_register_init(MCELOG_PLUGIN, mcelog_init);
663   plugin_register_complex_read(NULL, MCELOG_PLUGIN, mcelog_read, 0, NULL);
664   plugin_register_shutdown(MCELOG_PLUGIN, mcelog_shutdown);
665 }