32d6e510a836f44d01ab10d51f5ca719334a5f5f
[collectd.git] / src / ipmi.c
1 /**
2  * collectd - src/ipmi.c
3  * Copyright (C) 2008-2009  Florian octo Forster
4  * Copyright (C) 2008       Peter Holik
5  * Copyright (C) 2009       Bruno PrĂ©mont
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the
9  * Free Software Foundation; only version 2 of the License is applicable.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19  *
20  * Authors:
21  *   Florian octo Forster <octo at collectd.org>
22  *   Peter Holik <peter at holik.at>
23  *   Bruno PrĂ©mont <bonbons at linux-vserver.org>
24  *   Pavel Rochnyak <pavel2000 ngs.ru>
25  **/
26
27 #include "collectd.h"
28
29 #include "common.h"
30 #include "plugin.h"
31 #include "utils_ignorelist.h"
32
33 #include <OpenIPMI/ipmi_auth.h>
34 #include <OpenIPMI/ipmi_conn.h>
35 #include <OpenIPMI/ipmi_err.h>
36 #include <OpenIPMI/ipmi_lan.h>
37 #include <OpenIPMI/ipmi_posix.h>
38 #include <OpenIPMI/ipmi_smi.h>
39 #include <OpenIPMI/ipmiif.h>
40
41 /*
42  * Private data types
43  */
44 struct c_ipmi_sensor_list_s;
45 typedef struct c_ipmi_sensor_list_s c_ipmi_sensor_list_t;
46
47 struct c_ipmi_instance_s {
48   char *name;
49   ignorelist_t *ignorelist;
50   _Bool notify_add;
51   _Bool notify_remove;
52   _Bool notify_notpresent;
53   _Bool notify_conn;
54   _Bool sel_enabled;
55   _Bool sel_clear_event;
56
57   char *host;
58   char *connaddr;
59   char *username;
60   char *password;
61   unsigned int authtype;
62
63   _Bool connected;
64   ipmi_con_t *connection;
65   pthread_mutex_t sensor_list_lock;
66   c_ipmi_sensor_list_t *sensor_list;
67
68   _Bool active;
69   pthread_t thread_id;
70   int init_in_progress;
71
72   struct c_ipmi_instance_s *next;
73 };
74 typedef struct c_ipmi_instance_s c_ipmi_instance_t;
75
76 struct c_ipmi_sensor_list_s {
77   ipmi_sensor_id_t sensor_id;
78   char sensor_name[DATA_MAX_NAME_LEN];
79   char sensor_type[DATA_MAX_NAME_LEN];
80   int sensor_not_present;
81   c_ipmi_sensor_list_t *next;
82   c_ipmi_instance_t *instance;
83   unsigned int use;
84 };
85
86 /*
87  * Module global variables
88  */
89 static os_handler_t *os_handler;
90 static c_ipmi_instance_t *instances = NULL;
91
92 /*
93  * Misc private functions
94  */
95 static void c_ipmi_error(c_ipmi_instance_t *st, const char *func, int status) {
96   char errbuf[4096] = {0};
97
98   if (IPMI_IS_OS_ERR(status) || IPMI_IS_RMCPP_ERR(status) ||
99       IPMI_IS_IPMI_ERR(status)) {
100     ipmi_get_error_string(status, errbuf, sizeof(errbuf));
101   }
102
103   if (errbuf[0] == 0) {
104     snprintf(errbuf, sizeof(errbuf), "Unknown error %#x", status);
105   }
106   errbuf[sizeof(errbuf) - 1] = 0;
107
108   ERROR("ipmi plugin: %s failed for `%s`: %s", func, st->name, errbuf);
109 } /* void c_ipmi_error */
110
111 static void c_ipmi_log(os_handler_t *handler, const char *format,
112                        enum ipmi_log_type_e log_type, va_list ap) {
113   char msg[1024];
114
115   vsnprintf(msg, sizeof(msg), format, ap);
116
117   switch (log_type) {
118   case IPMI_LOG_INFO:
119     INFO("ipmi plugin: %s", msg);
120     break;
121   case IPMI_LOG_WARNING:
122     NOTICE("ipmi plugin: %s", msg);
123     break;
124   case IPMI_LOG_SEVERE:
125     WARNING("ipmi plugin: %s", msg);
126     break;
127   case IPMI_LOG_FATAL:
128     ERROR("ipmi plugin: %s", msg);
129     break;
130   case IPMI_LOG_ERR_INFO:
131     ERROR("ipmi plugin: %s", msg);
132     break;
133 #if COLLECT_DEBUG
134   case IPMI_LOG_DEBUG_START:
135   case IPMI_LOG_DEBUG:
136     fprintf(stderr, "ipmi plugin: %s\n", msg);
137     break;
138   case IPMI_LOG_DEBUG_CONT:
139   case IPMI_LOG_DEBUG_END:
140     fprintf(stderr, "%s\n", msg);
141     break;
142 #else
143   case IPMI_LOG_DEBUG_START:
144   case IPMI_LOG_DEBUG:
145   case IPMI_LOG_DEBUG_CONT:
146   case IPMI_LOG_DEBUG_END:
147     break;
148 #endif
149   }
150 } /* void c_ipmi_log */
151
152 /*
153  * Sensor handlers
154  */
155 /* Prototype for sensor_list_remove, so sensor_read_handler can call it. */
156 static int sensor_list_remove(c_ipmi_instance_t *st, ipmi_sensor_t *sensor);
157
158 static void sensor_read_handler(ipmi_sensor_t *sensor, int err,
159                                 enum ipmi_value_present_e value_present,
160                                 unsigned int __attribute__((unused)) raw_value,
161                                 double value, ipmi_states_t *states,
162                                 void *user_data) {
163   value_list_t vl = VALUE_LIST_INIT;
164
165   c_ipmi_sensor_list_t *list_item = (c_ipmi_sensor_list_t *)user_data;
166   c_ipmi_instance_t *st = list_item->instance;
167
168   list_item->use--;
169
170   if (err != 0) {
171     if (IPMI_IS_IPMI_ERR(err) &&
172         IPMI_GET_IPMI_ERR(err) == IPMI_NOT_PRESENT_CC) {
173       if (list_item->sensor_not_present == 0) {
174         list_item->sensor_not_present = 1;
175
176         INFO("ipmi plugin: sensor_read_handler: sensor `%s` of `%s` "
177              "not present.",
178              list_item->sensor_name, st->name);
179
180         if (st->notify_notpresent) {
181           notification_t n = {
182               NOTIF_WARNING, cdtime(), "", "", "ipmi", "", "", "", NULL};
183
184           sstrncpy(n.host, (st->host != NULL) ? st->host : hostname_g,
185                    sizeof(n.host));
186           sstrncpy(n.type_instance, list_item->sensor_name,
187                    sizeof(n.type_instance));
188           sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
189           snprintf(n.message, sizeof(n.message), "sensor %s not present",
190                    list_item->sensor_name);
191
192           plugin_dispatch_notification(&n);
193         }
194       }
195     } else if (IPMI_IS_IPMI_ERR(err) &&
196                IPMI_GET_IPMI_ERR(err) ==
197                    IPMI_NOT_SUPPORTED_IN_PRESENT_STATE_CC) {
198       INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` not ready.",
199            list_item->sensor_name, st->name);
200     } else if (IPMI_IS_IPMI_ERR(err) &&
201                IPMI_GET_IPMI_ERR(err) == IPMI_TIMEOUT_CC) {
202       INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` timed out.",
203            list_item->sensor_name, st->name);
204     } else {
205       char errbuf[128] = {0};
206       ipmi_get_error_string(err, errbuf, sizeof(errbuf) - 1);
207
208       if (IPMI_IS_IPMI_ERR(err))
209         INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed: "
210              "%s.",
211              list_item->sensor_name, st->name, errbuf);
212       else if (IPMI_IS_OS_ERR(err))
213         INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed: "
214              "%s (%#x).",
215              list_item->sensor_name, st->name, errbuf, IPMI_GET_OS_ERR(err));
216       else if (IPMI_IS_RMCPP_ERR(err))
217         INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed: "
218              "%s.",
219              list_item->sensor_name, st->name, errbuf);
220       else if (IPMI_IS_SOL_ERR(err))
221         INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed: "
222              "%s (%#x).",
223              list_item->sensor_name, st->name, errbuf, IPMI_GET_SOL_ERR(err));
224       else
225         INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed "
226              "with error %#x. of class %#x",
227              list_item->sensor_name, st->name, err & 0xff, err & 0xffffff00);
228     }
229     return;
230   } else if (list_item->sensor_not_present == 1) {
231     list_item->sensor_not_present = 0;
232
233     INFO("ipmi plugin: sensor_read_handler: sensor `%s` of `%s` present.",
234          list_item->sensor_name, st->name);
235
236     if (st->notify_notpresent) {
237       notification_t n = {NOTIF_OKAY, cdtime(), "", "",  "ipmi",
238                           "",         "",       "", NULL};
239
240       sstrncpy(n.host, (st->host != NULL) ? st->host : hostname_g,
241                sizeof(n.host));
242       sstrncpy(n.type_instance, list_item->sensor_name,
243                sizeof(n.type_instance));
244       sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
245       snprintf(n.message, sizeof(n.message), "sensor %s present",
246                list_item->sensor_name);
247
248       plugin_dispatch_notification(&n);
249     }
250   }
251
252   if (value_present != IPMI_BOTH_VALUES_PRESENT) {
253     INFO("ipmi plugin: sensor_read_handler: Removing sensor `%s` of `%s`, "
254          "because it provides %s. If you need this sensor, "
255          "please file a bug report.",
256          list_item->sensor_name, st->name,
257          (value_present == IPMI_RAW_VALUE_PRESENT) ? "only the raw value"
258                                                    : "no value");
259     sensor_list_remove(st, sensor);
260     return;
261   }
262
263   if (!ipmi_is_sensor_scanning_enabled(states)) {
264     DEBUG("ipmi plugin: sensor_read_handler: Skipping sensor `%s` of `%s`, "
265           "it is in 'scanning disabled' state.",
266           list_item->sensor_name, st->name);
267     return;
268   }
269
270   if (ipmi_is_initial_update_in_progress(states)) {
271     DEBUG("ipmi plugin: sensor_read_handler: Skipping sensor `%s` of `%s`, "
272           "it is in 'initial update in progress' state.",
273           list_item->sensor_name, st->name);
274     return;
275   }
276
277   vl.values = &(value_t){.gauge = value};
278   vl.values_len = 1;
279
280   if (st->host != NULL)
281     sstrncpy(vl.host, st->host, sizeof(vl.host));
282   sstrncpy(vl.plugin, "ipmi", sizeof(vl.plugin));
283   sstrncpy(vl.type, list_item->sensor_type, sizeof(vl.type));
284   sstrncpy(vl.type_instance, list_item->sensor_name, sizeof(vl.type_instance));
285
286   plugin_dispatch_values(&vl);
287 } /* void sensor_read_handler */
288
289 static void sensor_get_name(ipmi_sensor_t *sensor, char *buffer, int buf_len) {
290   char temp[DATA_MAX_NAME_LEN] = {0};
291   ipmi_entity_t *ent = ipmi_sensor_get_entity(sensor);
292   const char *entity_id_string = ipmi_entity_get_entity_id_string(ent);
293   char sensor_name[DATA_MAX_NAME_LEN] = "";
294   char *sensor_name_ptr;
295
296   if ((buffer == NULL) || (buf_len == 0))
297     return;
298
299   ipmi_sensor_get_name(sensor, temp, sizeof(temp));
300   temp[sizeof(temp) - 1] = 0;
301
302   if (entity_id_string != NULL && strlen(temp))
303     snprintf(sensor_name, sizeof(sensor_name), "%s %s", temp, entity_id_string);
304   else if (entity_id_string != NULL)
305     sstrncpy(sensor_name, entity_id_string, sizeof(sensor_name));
306   else
307     sstrncpy(sensor_name, temp, sizeof(sensor_name));
308
309   if (strlen(temp)) {
310     sstrncpy(temp, sensor_name, sizeof(temp));
311     sensor_name_ptr = strstr(temp, ").");
312     if (sensor_name_ptr != NULL) {
313       /* If name is something like "foo (123).bar",
314        * change that to "bar (123)".
315        * Both, sensor_name_ptr and sensor_id_ptr point to memory within the
316        * `temp' array, which holds a copy of the current `sensor_name'. */
317       char *sensor_id_ptr;
318
319       /* `sensor_name_ptr' points to ").bar". */
320       sensor_name_ptr[1] = 0;
321       /* `temp' holds "foo (123)\0bar\0". */
322       sensor_name_ptr += 2;
323       /* `sensor_name_ptr' now points to "bar". */
324
325       sensor_id_ptr = strstr(temp, "(");
326       if (sensor_id_ptr != NULL) {
327         /* `sensor_id_ptr' now points to "(123)". */
328         snprintf(sensor_name, sizeof(sensor_name), "%s %s", sensor_name_ptr,
329                  sensor_id_ptr);
330       }
331       /* else: don't touch sensor_name. */
332     }
333   }
334   sstrncpy(buffer, sensor_name, buf_len);
335 }
336
337 static int sensor_list_add(c_ipmi_instance_t *st, ipmi_sensor_t *sensor) {
338   ipmi_sensor_id_t sensor_id;
339   c_ipmi_sensor_list_t *list_item;
340   c_ipmi_sensor_list_t *list_prev;
341
342   char buffer[DATA_MAX_NAME_LEN] = {0};
343   char *sensor_name_ptr = buffer;
344   int sensor_type;
345   const char *type;
346
347   sensor_id = ipmi_sensor_convert_to_id(sensor);
348   sensor_get_name(sensor, buffer, sizeof(buffer));
349
350   DEBUG("ipmi plugin: sensor_list_add: Found sensor `%s` of `%s`,"
351         " Type: %#x"
352         " Event reading type: %#x"
353         " Direction: %#x"
354         " Event support: %#x",
355         sensor_name_ptr, st->name, ipmi_sensor_get_sensor_type(sensor),
356         ipmi_sensor_get_event_reading_type(sensor),
357         ipmi_sensor_get_sensor_direction(sensor),
358         ipmi_sensor_get_event_support(sensor));
359
360   /* Both `ignorelist' and `sensor_name_ptr' may be NULL. */
361   if (ignorelist_match(st->ignorelist, sensor_name_ptr) != 0)
362     return 0;
363
364   /* FIXME: Use rate unit or base unit to scale the value */
365
366   sensor_type = ipmi_sensor_get_sensor_type(sensor);
367
368   /*
369    * ipmitool/lib/ipmi_sdr.c sdr_sensor_has_analog_reading() has a notice
370    * about 'Threshold sensors' and 'analog readings'. Discrete sensor may
371    * have analog data, but discrete sensors support is not implemented
372    * in Collectd yet.
373    *
374    * ipmi_sensor_id_get_reading() supports only 'Threshold' sensors.
375    * See lib/sensor.c:4842, stand_ipmi_sensor_get_reading() for details.
376   */
377   if (!ipmi_sensor_get_is_readable(sensor)) {
378     INFO("ipmi plugin: sensor_list_add: Ignore sensor `%s` of `%s`, "
379          "because it don't readable! Its type: (%#x, %s). ",
380          sensor_name_ptr, st->name, sensor_type,
381          ipmi_sensor_get_sensor_type_string(sensor));
382     return -1;
383   }
384
385   if (ipmi_sensor_get_event_reading_type(sensor) !=
386       IPMI_EVENT_READING_TYPE_THRESHOLD) {
387     INFO("ipmi plugin: sensor_list_add: Ignore sensor `%s` of `%s`, "
388          "because it is discrete (%#x)! Its type: (%#x, %s). ",
389          sensor_name_ptr, st->name, sensor_type,
390          ipmi_sensor_get_event_reading_type(sensor),
391          ipmi_sensor_get_sensor_type_string(sensor));
392     return -1;
393   }
394
395   switch (sensor_type) {
396   case IPMI_SENSOR_TYPE_TEMPERATURE:
397     type = "temperature";
398     break;
399
400   case IPMI_SENSOR_TYPE_VOLTAGE:
401     type = "voltage";
402     break;
403
404   case IPMI_SENSOR_TYPE_CURRENT:
405     type = "current";
406     break;
407
408   case IPMI_SENSOR_TYPE_FAN:
409     type = "fanspeed";
410     break;
411
412   default: {
413     INFO("ipmi plugin: sensor_list_add: Ignore sensor `%s` of `%s`, "
414          "because I don't know how to handle its type (%#x, %s). "
415          "If you need this sensor, please file a bug report.",
416          sensor_name_ptr, st->name, sensor_type,
417          ipmi_sensor_get_sensor_type_string(sensor));
418     return -1;
419   }
420   } /* switch (sensor_type) */
421
422   pthread_mutex_lock(&st->sensor_list_lock);
423
424   list_prev = NULL;
425   for (list_item = st->sensor_list; list_item != NULL;
426        list_item = list_item->next) {
427     if (ipmi_cmp_sensor_id(sensor_id, list_item->sensor_id) == 0)
428       break;
429     list_prev = list_item;
430   } /* for (list_item) */
431
432   if (list_item != NULL) {
433     pthread_mutex_unlock(&st->sensor_list_lock);
434     return 0;
435   }
436
437   list_item = (c_ipmi_sensor_list_t *)calloc(1, sizeof(c_ipmi_sensor_list_t));
438   if (list_item == NULL) {
439     pthread_mutex_unlock(&st->sensor_list_lock);
440     return -1;
441   }
442
443   list_item->instance = st;
444   list_item->sensor_id = ipmi_sensor_convert_to_id(sensor);
445
446   if (list_prev != NULL)
447     list_prev->next = list_item;
448   else
449     st->sensor_list = list_item;
450
451   sstrncpy(list_item->sensor_name, sensor_name_ptr,
452            sizeof(list_item->sensor_name));
453   sstrncpy(list_item->sensor_type, type, sizeof(list_item->sensor_type));
454
455   pthread_mutex_unlock(&st->sensor_list_lock);
456
457   if (st->notify_add && (st->init_in_progress == 0)) {
458     notification_t n = {NOTIF_OKAY, cdtime(), "", "", "ipmi", "", "", "", NULL};
459
460     sstrncpy(n.host, (st->host != NULL) ? st->host : hostname_g,
461              sizeof(n.host));
462     sstrncpy(n.type_instance, list_item->sensor_name, sizeof(n.type_instance));
463     sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
464     snprintf(n.message, sizeof(n.message), "sensor %s added",
465              list_item->sensor_name);
466
467     plugin_dispatch_notification(&n);
468   }
469
470   return 0;
471 } /* int sensor_list_add */
472
473 static int sensor_list_remove(c_ipmi_instance_t *st, ipmi_sensor_t *sensor) {
474   ipmi_sensor_id_t sensor_id;
475   c_ipmi_sensor_list_t *list_item;
476   c_ipmi_sensor_list_t *list_prev;
477
478   sensor_id = ipmi_sensor_convert_to_id(sensor);
479
480   pthread_mutex_lock(&st->sensor_list_lock);
481
482   list_prev = NULL;
483   for (list_item = st->sensor_list; list_item != NULL;
484        list_item = list_item->next) {
485     if (ipmi_cmp_sensor_id(sensor_id, list_item->sensor_id) == 0)
486       break;
487     list_prev = list_item;
488   } /* for (list_item) */
489
490   if (list_item == NULL) {
491     pthread_mutex_unlock(&st->sensor_list_lock);
492     return -1;
493   }
494
495   if (list_prev == NULL)
496     st->sensor_list = list_item->next;
497   else
498     list_prev->next = list_item->next;
499
500   list_prev = NULL;
501   list_item->next = NULL;
502
503   pthread_mutex_unlock(&st->sensor_list_lock);
504
505   if (st->notify_remove && st->active) {
506     notification_t n = {NOTIF_WARNING, cdtime(), "", "", "ipmi", "", "", "",
507                         NULL};
508
509     sstrncpy(n.host, (st->host != NULL) ? st->host : hostname_g,
510              sizeof(n.host));
511     sstrncpy(n.type_instance, list_item->sensor_name, sizeof(n.type_instance));
512     sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
513     snprintf(n.message, sizeof(n.message), "sensor %s removed",
514              list_item->sensor_name);
515
516     plugin_dispatch_notification(&n);
517   }
518
519   free(list_item);
520   return 0;
521 } /* int sensor_list_remove */
522
523 static int sensor_list_read_all(c_ipmi_instance_t *st) {
524   pthread_mutex_lock(&st->sensor_list_lock);
525
526   for (c_ipmi_sensor_list_t *list_item = st->sensor_list; list_item != NULL;
527        list_item = list_item->next) {
528     DEBUG("ipmi plugin: try read sensor `%s` of `%s`, use: %d",
529           list_item->sensor_name, st->name, list_item->use);
530
531     /* Reading already initiated */
532     if (list_item->use)
533       continue;
534
535     list_item->use++;
536     ipmi_sensor_id_get_reading(list_item->sensor_id, sensor_read_handler,
537                                /* user data = */ (void *)list_item);
538   } /* for (list_item) */
539
540   pthread_mutex_unlock(&st->sensor_list_lock);
541
542   return 0;
543 } /* int sensor_list_read_all */
544
545 static int sensor_list_remove_all(c_ipmi_instance_t *st) {
546   c_ipmi_sensor_list_t *list_item;
547
548   pthread_mutex_lock(&st->sensor_list_lock);
549
550   list_item = st->sensor_list;
551   st->sensor_list = NULL;
552
553   pthread_mutex_unlock(&st->sensor_list_lock);
554
555   while (list_item != NULL) {
556     c_ipmi_sensor_list_t *list_next = list_item->next;
557
558     free(list_item);
559
560     list_item = list_next;
561   } /* while (list_item) */
562
563   return 0;
564 } /* int sensor_list_remove_all */
565
566 static int sensor_convert_threshold_severity(enum ipmi_thresh_e severity) {
567   switch (severity) {
568   case IPMI_LOWER_NON_CRITICAL:
569   case IPMI_UPPER_NON_CRITICAL:
570     return NOTIF_OKAY;
571   case IPMI_LOWER_CRITICAL:
572   case IPMI_UPPER_CRITICAL:
573     return NOTIF_WARNING;
574   case IPMI_LOWER_NON_RECOVERABLE:
575   case IPMI_UPPER_NON_RECOVERABLE:
576     return NOTIF_FAILURE;
577   default:
578     return NOTIF_OKAY;
579   } /* switch (severity) */
580 } /* int sensor_convert_threshold_severity */
581
582 static void add_event_common_data(notification_t *n, ipmi_sensor_t *sensor,
583                                   enum ipmi_event_dir_e dir,
584                                   ipmi_event_t *event) {
585   ipmi_entity_t *ent = ipmi_sensor_get_entity(sensor);
586
587   plugin_notification_meta_add_string(n, "entity_name",
588                                       ipmi_entity_get_entity_id_string(ent));
589   plugin_notification_meta_add_signed_int(n, "entity_id",
590                                           ipmi_entity_get_entity_id(ent));
591   plugin_notification_meta_add_signed_int(n, "entity_instance",
592                                           ipmi_entity_get_entity_instance(ent));
593   plugin_notification_meta_add_boolean(n, "assert", dir == IPMI_ASSERTION);
594
595   if (event)
596     plugin_notification_meta_add_signed_int(n, "event_type",
597                                             ipmi_event_get_type(event));
598 } /* void add_event_sensor_meta_data */
599
600 static int sensor_threshold_event_handler(
601     ipmi_sensor_t *sensor, enum ipmi_event_dir_e dir,
602     enum ipmi_thresh_e threshold, enum ipmi_event_value_dir_e high_low,
603     enum ipmi_value_present_e value_present, unsigned int raw_value,
604     double value, void *cb_data, ipmi_event_t *event) {
605
606   c_ipmi_instance_t *st = (c_ipmi_instance_t *)cb_data;
607
608   /* From the IPMI specification Chapter 2: Events.
609    * If a callback handles the event, then all future callbacks called due to
610    * the event will receive a NULL for the event. So be ready to handle a NULL
611    * event in all your event handlers. A NULL may also be passed to an event
612    * handler if the callback was not due to an event. */
613   if (event == NULL)
614     return IPMI_EVENT_NOT_HANDLED;
615
616   /* offset is a table index and it's represented as enum of strings that are
617      organized in the way - high and low for each threshold severity level */
618   notification_t n = {NOTIF_OKAY, cdtime(), "", "", "ipmi", "", "", "", NULL};
619   unsigned int offset = (2 * threshold) + high_low;
620   unsigned int event_type = ipmi_sensor_get_event_reading_type(sensor);
621   unsigned int sensor_type = ipmi_sensor_get_sensor_type(sensor);
622   const char *event_state =
623       ipmi_get_reading_name(event_type, sensor_type, offset);
624   sensor_get_name(sensor, n.type_instance, sizeof(n.type_instance));
625   if (value_present != IPMI_NO_VALUES_PRESENT)
626     snprintf(n.message, sizeof(n.message),
627              "sensor %s received event: %s, value is %f", n.type_instance,
628              event_state, value);
629   else
630     snprintf(n.message, sizeof(n.message),
631              "sensor %s received event: %s, value not provided",
632              n.type_instance, event_state);
633
634   DEBUG("Threshold event received for sensor %s", n.type_instance);
635
636   sstrncpy(n.host, (st->host != NULL) ? st->host : hostname_g, sizeof(n.host));
637   sstrncpy(n.type, ipmi_sensor_get_sensor_type_string(sensor), sizeof(n.type));
638   n.severity = sensor_convert_threshold_severity(threshold);
639   n.time = NS_TO_CDTIME_T(ipmi_event_get_timestamp(event));
640
641   plugin_notification_meta_add_string(&n, "severity",
642                                       ipmi_get_threshold_string(threshold));
643   plugin_notification_meta_add_string(&n, "direction",
644                                       ipmi_get_value_dir_string(high_low));
645
646   switch (value_present) {
647   case IPMI_BOTH_VALUES_PRESENT:
648     plugin_notification_meta_add_double(&n, "val", value);
649   /* both values present, so fall-through to add raw value too */
650   case IPMI_RAW_VALUE_PRESENT: {
651     char buf[DATA_MAX_NAME_LEN] = {0};
652     snprintf(buf, sizeof(buf), "0x%2.2x", raw_value);
653     plugin_notification_meta_add_string(&n, "raw", buf);
654   } break;
655   default:
656     break;
657   } /* switch (value_present) */
658
659   add_event_common_data(&n, sensor, dir, event);
660
661   plugin_dispatch_notification(&n);
662   plugin_notification_meta_free(n.meta);
663
664   /* Delete handled ipmi event from the list */
665   if (st->sel_clear_event) {
666     ipmi_event_delete(event, NULL, NULL);
667     return IPMI_EVENT_HANDLED;
668   }
669
670   return IPMI_EVENT_NOT_HANDLED;
671 } /* int sensor_threshold_event_handler */
672
673 static int sensor_discrete_event_handler(ipmi_sensor_t *sensor,
674                                          enum ipmi_event_dir_e dir, int offset,
675                                          int severity, int prev_severity,
676                                          void *cb_data, ipmi_event_t *event) {
677
678   c_ipmi_instance_t *st = (c_ipmi_instance_t *)cb_data;
679
680   /* From the IPMI specification Chapter 2: Events.
681    * If a callback handles the event, then all future callbacks called due to
682    * the event will receive a NULL for the event. So be ready to handle a NULL
683    * event in all your event handlers. A NULL may also be passed to an event
684    * handler if the callback was not due to an event. */
685   if (event == NULL)
686     return IPMI_EVENT_NOT_HANDLED;
687
688   notification_t n = {NOTIF_OKAY, cdtime(), "", "", "ipmi", "", "", "", NULL};
689   unsigned int event_type = ipmi_sensor_get_event_reading_type(sensor);
690   unsigned int sensor_type = ipmi_sensor_get_sensor_type(sensor);
691   const char *event_state =
692       ipmi_get_reading_name(event_type, sensor_type, offset);
693   sensor_get_name(sensor, n.type_instance, sizeof(n.type_instance));
694   snprintf(n.message, sizeof(n.message), "sensor %s received event: %s",
695            n.type_instance, event_state);
696
697   DEBUG("Discrete event received for sensor %s", n.type_instance);
698
699   sstrncpy(n.host, (st->host != NULL) ? st->host : hostname_g, sizeof(n.host));
700   sstrncpy(n.type, ipmi_sensor_get_sensor_type_string(sensor), sizeof(n.type));
701   n.time = NS_TO_CDTIME_T(ipmi_event_get_timestamp(event));
702
703   plugin_notification_meta_add_signed_int(&n, "offset", offset);
704
705   if (severity != -1)
706     plugin_notification_meta_add_signed_int(&n, "severity", severity);
707
708   if (prev_severity != -1)
709     plugin_notification_meta_add_signed_int(&n, "prevseverity", prev_severity);
710
711   add_event_common_data(&n, sensor, dir, event);
712
713   plugin_dispatch_notification(&n);
714   plugin_notification_meta_free(n.meta);
715
716   /* Delete handled ipmi event from the list */
717   if (st->sel_clear_event) {
718     ipmi_event_delete(event, NULL, NULL);
719     return IPMI_EVENT_HANDLED;
720   }
721
722   return IPMI_EVENT_NOT_HANDLED;
723 } /* int sensor_discrete_event_handler */
724
725 /*
726  * Entity handlers
727  */
728 static void
729 entity_sensor_update_handler(enum ipmi_update_e op,
730                              ipmi_entity_t __attribute__((unused)) * entity,
731                              ipmi_sensor_t *sensor, void *user_data) {
732   c_ipmi_instance_t *st = (c_ipmi_instance_t *)user_data;
733
734   if ((op == IPMI_ADDED) || (op == IPMI_CHANGED)) {
735     /* Will check for duplicate entries.. */
736     sensor_list_add(st, sensor);
737
738     if (st->sel_enabled) {
739       int status = 0;
740       /* register threshold event if threshold sensor support events */
741       if ((ipmi_sensor_get_event_reading_type(sensor) ==
742            IPMI_EVENT_READING_TYPE_THRESHOLD) &&
743           (ipmi_sensor_get_threshold_access(sensor) !=
744            IPMI_THRESHOLD_ACCESS_SUPPORT_NONE))
745         status = ipmi_sensor_add_threshold_event_handler(
746             sensor, sensor_threshold_event_handler, st);
747       /* register discrete handler if discrete/specific sensor support events */
748       else if (ipmi_sensor_get_event_support(sensor) != IPMI_EVENT_SUPPORT_NONE)
749         status = ipmi_sensor_add_discrete_event_handler(
750             sensor, sensor_discrete_event_handler, st);
751
752       if (status) {
753         char buf[DATA_MAX_NAME_LEN] = {0};
754         sensor_get_name(sensor, buf, sizeof(buf));
755         ERROR("Unable to add sensor %s event handler, status: %d", buf, status);
756       }
757     }
758   } else if (op == IPMI_DELETED) {
759     sensor_list_remove(st, sensor);
760
761     if (st->sel_enabled) {
762       if (ipmi_sensor_get_event_reading_type(sensor) ==
763           IPMI_EVENT_READING_TYPE_THRESHOLD)
764         ipmi_sensor_remove_threshold_event_handler(
765             sensor, sensor_threshold_event_handler, st);
766       else
767         ipmi_sensor_remove_discrete_event_handler(
768             sensor, sensor_discrete_event_handler, st);
769     }
770   }
771 } /* void entity_sensor_update_handler */
772
773 /*
774  * Domain handlers
775  */
776 static void
777 domain_entity_update_handler(enum ipmi_update_e op,
778                              ipmi_domain_t __attribute__((unused)) * domain,
779                              ipmi_entity_t *entity, void *user_data) {
780   int status;
781   c_ipmi_instance_t *st = (c_ipmi_instance_t *)user_data;
782
783   if (op == IPMI_ADDED) {
784     status = ipmi_entity_add_sensor_update_handler(
785         entity, entity_sensor_update_handler, /* user data = */ (void *)st);
786     if (status != 0) {
787       c_ipmi_error(st, "ipmi_entity_add_sensor_update_handler", status);
788     }
789   } else if (op == IPMI_DELETED) {
790     status = ipmi_entity_remove_sensor_update_handler(
791         entity, entity_sensor_update_handler, /* user data = */ (void *)st);
792     if (status != 0) {
793       c_ipmi_error(st, "ipmi_entity_remove_sensor_update_handler", status);
794     }
795   }
796 } /* void domain_entity_update_handler */
797
798 static void smi_event_handler(ipmi_con_t __attribute__((unused)) * ipmi,
799                               const ipmi_addr_t __attribute__((unused)) * addr,
800                               unsigned int __attribute__((unused)) addr_len,
801                               ipmi_event_t *event, void *cb_data) {
802   unsigned int type = ipmi_event_get_type(event);
803   ipmi_domain_t *domain = cb_data;
804
805   DEBUG("%s: Event received: type %u", __FUNCTION__, type);
806
807   if (type != 0x02)
808     /* It's not a standard IPMI event. */
809     return;
810
811   /* force domain to reread SELs */
812   ipmi_domain_reread_sels(domain, NULL, NULL);
813 }
814
815 static void domain_connection_change_handler(ipmi_domain_t *domain, int err,
816                                              unsigned int conn_num,
817                                              unsigned int port_num,
818                                              int still_connected,
819                                              void *user_data) {
820
821   DEBUG("domain_connection_change_handler (domain = %p, err = %i, "
822         "conn_num = %u, port_num = %u, still_connected = %i, "
823         "user_data = %p);",
824         (void *)domain, err, conn_num, port_num, still_connected, user_data);
825
826   c_ipmi_instance_t *st = (c_ipmi_instance_t *)user_data;
827
828   if (err != 0)
829     c_ipmi_error(st, "domain_connection_change_handler", err);
830
831   if (!still_connected) {
832
833     if (st->notify_conn && st->connected && st->init_in_progress == 0) {
834       notification_t n = {NOTIF_FAILURE, cdtime(), "", "", "ipmi", "", "", "",
835                           NULL};
836
837       sstrncpy(n.host, (st->host != NULL) ? st->host : hostname_g,
838                sizeof(n.host));
839       sstrncpy(n.message, "IPMI connection lost", sizeof(n.plugin));
840
841       plugin_dispatch_notification(&n);
842     }
843
844     st->connected = 0;
845     return;
846   }
847
848   if (st->notify_conn && !st->connected && st->init_in_progress == 0) {
849     notification_t n = {NOTIF_OKAY, cdtime(), "", "", "ipmi", "", "", "", NULL};
850
851     sstrncpy(n.host, (st->host != NULL) ? st->host : hostname_g,
852              sizeof(n.host));
853     sstrncpy(n.message, "IPMI connection restored", sizeof(n.plugin));
854
855     plugin_dispatch_notification(&n);
856   }
857
858   st->connected = 1;
859
860   int status = ipmi_domain_add_entity_update_handler(
861       domain, domain_entity_update_handler, /* user data = */ st);
862   if (status != 0) {
863     c_ipmi_error(st, "ipmi_domain_add_entity_update_handler", status);
864   }
865
866   status = st->connection->add_event_handler(st->connection, smi_event_handler,
867                                              (void *)domain);
868
869   if (status != 0)
870     c_ipmi_error(st, "Failed to register smi event handler", status);
871 } /* void domain_connection_change_handler */
872
873 static int c_ipmi_thread_init(c_ipmi_instance_t *st) {
874   ipmi_domain_id_t domain_id;
875   int status;
876
877   if (st->connaddr != NULL) {
878     char *ip_addrs[1] = {NULL}, *ports[1] = {NULL};
879
880     ip_addrs[0] = strdup(st->connaddr);
881     ports[0] = strdup(IPMI_LAN_STD_PORT_STR);
882
883     status = ipmi_ip_setup_con(ip_addrs, ports, 1, st->authtype,
884                                (unsigned int)IPMI_PRIVILEGE_USER, st->username,
885                                strlen(st->username), st->password,
886                                strlen(st->password), os_handler,
887                                /* user data = */ NULL, &st->connection);
888     if (status != 0) {
889       c_ipmi_error(st, "ipmi_ip_setup_con", status);
890       return -1;
891     }
892   } else {
893     status = ipmi_smi_setup_con(/* if_num = */ 0, os_handler,
894                                 /* user data = */ NULL, &st->connection);
895     if (status != 0) {
896       c_ipmi_error(st, "ipmi_smi_setup_con", status);
897       return -1;
898     }
899   }
900
901   size_t open_option_num = 0;
902   ipmi_open_option_t open_option[2];
903
904   open_option[open_option_num].option = IPMI_OPEN_OPTION_ALL;
905   open_option[open_option_num].ival = 1;
906   open_option_num++;
907
908 #ifdef IPMI_OPEN_OPTION_USE_CACHE
909   // This option appeared in OpenIPMI-2.0.17
910   open_option[open_option_num].option = IPMI_OPEN_OPTION_USE_CACHE;
911   open_option[open_option_num].ival = 0; /* Disable SDR cache in local file */
912   open_option_num++;
913 #endif
914
915   /*
916    * NOTE: Domain names must be unique. There is static `domains_list` common
917    * to all threads inside lib/domain.c and some ops are done by name.
918    */
919   status = ipmi_open_domain(
920       st->name, &st->connection, /* num_con = */ 1,
921       domain_connection_change_handler, /* user data = */ (void *)st,
922       /* domain_fully_up_handler = */ NULL, /* user data = */ NULL, open_option,
923       open_option_num, &domain_id);
924   if (status != 0) {
925     c_ipmi_error(st, "ipmi_open_domain", status);
926     return -1;
927   }
928
929   return 0;
930 } /* int c_ipmi_thread_init */
931
932 static void *c_ipmi_thread_main(void *user_data) {
933   c_ipmi_instance_t *st = (c_ipmi_instance_t *)user_data;
934
935   int status = c_ipmi_thread_init(st);
936   if (status != 0) {
937     ERROR("ipmi plugin: c_ipmi_thread_init failed.");
938     st->active = 0;
939     return (void *)-1;
940   }
941
942   while (st->active != 0) {
943     struct timeval tv = {1, 0};
944     os_handler->perform_one_op(os_handler, &tv);
945   }
946   return (void *)0;
947 } /* void *c_ipmi_thread_main */
948
949 static c_ipmi_instance_t *c_ipmi_init_instance() {
950   c_ipmi_instance_t *st;
951
952   st = calloc(1, sizeof(*st));
953   if (st == NULL) {
954     ERROR("ipmi plugin: calloc failed.");
955     return NULL;
956   }
957
958   st->name = strdup("main");
959   if (st->name == NULL) {
960     sfree(st);
961     ERROR("ipmi plugin: strdup() failed.");
962     return NULL;
963   }
964
965   st->ignorelist = ignorelist_create(/* invert = */ 1);
966   if (st->ignorelist == NULL) {
967     sfree(st->name);
968     sfree(st);
969     ERROR("ipmi plugin: ignorelist_create() failed.");
970     return NULL;
971   }
972
973   st->sensor_list = NULL;
974   pthread_mutex_init(&st->sensor_list_lock, /* attr = */ NULL);
975
976   st->host = NULL;
977   st->connaddr = NULL;
978   st->username = NULL;
979   st->password = NULL;
980   st->authtype = IPMI_AUTHTYPE_DEFAULT;
981
982   st->next = NULL;
983
984   return st;
985 } /* c_ipmi_instance_t *c_ipmi_init_instance */
986
987 static void c_ipmi_free_instance(c_ipmi_instance_t *st) {
988   if (st == NULL)
989     return;
990
991   assert(st->next == NULL);
992
993   sfree(st->name);
994   sfree(st->host);
995   sfree(st->connaddr);
996   sfree(st->username);
997   sfree(st->password);
998
999   ignorelist_free(st->ignorelist);
1000   pthread_mutex_destroy(&st->sensor_list_lock);
1001   sfree(st);
1002 } /* void c_ipmi_free_instance */
1003
1004 static void c_ipmi_add_instance(c_ipmi_instance_t *instance) {
1005   if (instances == NULL) {
1006     instances = instance;
1007     return;
1008   }
1009
1010   c_ipmi_instance_t *last = instances;
1011
1012   while (last->next != NULL)
1013     last = last->next;
1014
1015   last->next = instance;
1016 } /* void c_ipmi_add_instance */
1017
1018 static int c_ipmi_config_add_instance(oconfig_item_t *ci) {
1019   int status = 0;
1020   c_ipmi_instance_t *st = c_ipmi_init_instance();
1021   if (st == NULL)
1022     return ENOMEM;
1023
1024   if (strcasecmp(ci->key, "Instance") == 0)
1025     status = cf_util_get_string(ci, &st->name);
1026
1027   if (status != 0) {
1028     c_ipmi_free_instance(st);
1029     return status;
1030   }
1031
1032   for (int i = 0; i < ci->children_num; i++) {
1033     oconfig_item_t *child = ci->children + i;
1034
1035     if (strcasecmp("Sensor", child->key) == 0)
1036       ignorelist_add(st->ignorelist, ci->values[0].value.string);
1037     else if (strcasecmp("IgnoreSelected", child->key) == 0) {
1038       if (ci->values[0].value.boolean)
1039         ignorelist_set_invert(st->ignorelist, /* invert = */ 0);
1040       else
1041         ignorelist_set_invert(st->ignorelist, /* invert = */ 1);
1042     } else if (strcasecmp("NotifyIPMIConnectionState", child->key) == 0) {
1043       if (ci->values[0].value.boolean)
1044         st->notify_conn = 1;
1045     } else if (strcasecmp("NotifySensorAdd", child->key) == 0) {
1046       if (ci->values[0].value.boolean)
1047         st->notify_add = 1;
1048     } else if (strcasecmp("NotifySensorRemove", child->key) == 0) {
1049       if (ci->values[0].value.boolean)
1050         st->notify_remove = 1;
1051     } else if (strcasecmp("NotifySensorNotPresent", child->key) == 0) {
1052       if (ci->values[0].value.boolean)
1053         st->notify_notpresent = 1;
1054     } else if (strcasecmp("SELEnabled", child->key) == 0) {
1055       if (ci->values[0].value.boolean)
1056         st->sel_enabled = 1;
1057     } else if (strcasecmp("SELClearEvent", child->key) == 0) {
1058       if (ci->values[0].value.boolean)
1059         st->sel_clear_event = 1;
1060     } else if (strcasecmp("Host", child->key) == 0)
1061       status = cf_util_get_string(child, &st->host);
1062     else if (strcasecmp("Address", child->key) == 0)
1063       status = cf_util_get_string(child, &st->connaddr);
1064     else if (strcasecmp("Username", child->key) == 0)
1065       status = cf_util_get_string(child, &st->username);
1066     else if (strcasecmp("Password", child->key) == 0)
1067       status = cf_util_get_string(child, &st->password);
1068     else if (strcasecmp("AuthType", child->key) == 0) {
1069       char tmp[8];
1070       status = cf_util_get_string_buffer(child, tmp, sizeof(tmp));
1071       if (status != 0)
1072         break;
1073
1074       if (strcasecmp("MD5", tmp) == 0)
1075         st->authtype = IPMI_AUTHTYPE_MD5;
1076       else if (strcasecmp("rmcp+", tmp) == 0)
1077         st->authtype = IPMI_AUTHTYPE_RMCP_PLUS;
1078       else
1079         WARNING("ipmi plugin: The value \"%s\" is not valid for the "
1080                 "\"AuthType\" option.",
1081                 tmp);
1082     } else {
1083       WARNING("ipmi plugin: Option `%s' not allowed here.", child->key);
1084       status = -1;
1085     }
1086
1087     if (status != 0)
1088       break;
1089   }
1090
1091   if (status != 0) {
1092     c_ipmi_free_instance(st);
1093     return status;
1094   }
1095
1096   c_ipmi_add_instance(st);
1097
1098   return 0;
1099 } /* int c_ipmi_config_add_instance */
1100
1101 static int c_ipmi_config(oconfig_item_t *ci) {
1102   _Bool have_instance_block = 0;
1103
1104   for (int i = 0; i < ci->children_num; i++) {
1105     oconfig_item_t *child = ci->children + i;
1106
1107     if (strcasecmp("Instance", child->key) == 0) {
1108       c_ipmi_config_add_instance(child);
1109       have_instance_block = 1;
1110     } else if (!have_instance_block) {
1111       /* Non-instance option: Assume legacy configuration (without <Instance />
1112        * blocks) and call c_ipmi_config_add_instance with the <Plugin /> block.
1113        */
1114       return c_ipmi_config_add_instance(ci);
1115     } else
1116       WARNING("ipmi plugin: The configuration option "
1117               "\"%s\" is not allowed here. Did you "
1118               "forget to add an <Instance /> block "
1119               "around the configuration?",
1120               child->key);
1121   } /* for (ci->children) */
1122
1123   return 0;
1124 } /* int c_ipmi_config */
1125
1126 static int c_ipmi_read(user_data_t *user_data) {
1127   c_ipmi_instance_t *st = user_data->data;
1128
1129   if ((st->active == 0) || (st->thread_id == (pthread_t)0)) {
1130     INFO("ipmi plugin: c_ipmi_read: I'm not active, returning false.");
1131     return -1;
1132   }
1133
1134   if (st->connected == 0)
1135     return 0;
1136
1137   sensor_list_read_all(st);
1138
1139   if (st->init_in_progress > 0)
1140     st->init_in_progress--;
1141   else
1142     st->init_in_progress = 0;
1143
1144   return 0;
1145 } /* int c_ipmi_read */
1146
1147 static int c_ipmi_init(void) {
1148   int status;
1149   c_ipmi_instance_t *st;
1150   char callback_name[3 * DATA_MAX_NAME_LEN];
1151
1152   os_handler = ipmi_posix_thread_setup_os_handler(SIGIO);
1153   if (os_handler == NULL) {
1154     ERROR("ipmi plugin: ipmi_posix_thread_setup_os_handler failed.");
1155     return -1;
1156   }
1157
1158   os_handler->set_log_handler(os_handler, c_ipmi_log);
1159
1160   if (ipmi_init(os_handler) != 0) {
1161     ERROR("ipmi plugin: ipmi_init() failed.");
1162     os_handler->free_os_handler(os_handler);
1163     return -1;
1164   };
1165
1166   /* Don't send `ADD' notifications during startup (~ 1 minute) */
1167   time_t iv = CDTIME_T_TO_TIME_T(plugin_get_interval());
1168
1169   if (instances == NULL) {
1170     /* No instances were configured, let's start a default instance. */
1171     st = c_ipmi_init_instance();
1172     if (st == NULL)
1173       return ENOMEM;
1174
1175     c_ipmi_add_instance(st);
1176   }
1177
1178   st = instances;
1179   while (NULL != st) {
1180     /* The `st->name` is used as "domain name" for ipmi_open_domain().
1181      * That value should be unique, so we do plugin_register_complex_read()
1182      * at first as it checks the uniqueness. */
1183     snprintf(callback_name, sizeof(callback_name), "ipmi/%s", st->name);
1184
1185     user_data_t ud = {
1186         .data = st,
1187     };
1188
1189     status = plugin_register_complex_read(
1190         /* group     = */ "ipmi",
1191         /* name      = */ callback_name,
1192         /* callback  = */ c_ipmi_read,
1193         /* interval  = */ 0,
1194         /* user_data = */ &ud);
1195
1196     if (status != 0) {
1197       st->active = 0;
1198       st = st->next;
1199       continue;
1200     }
1201
1202     st->init_in_progress = 1 + (60 / iv);
1203     st->active = 1;
1204
1205     status = plugin_thread_create(&st->thread_id, /* attr = */ NULL,
1206                                   c_ipmi_thread_main,
1207                                   /* user data = */ (void *)st, "ipmi");
1208
1209     if (status != 0) {
1210       st->active = 0;
1211       st->thread_id = (pthread_t)0;
1212
1213       plugin_unregister_read(callback_name);
1214
1215       ERROR("ipmi plugin: pthread_create failed for `%s`.", callback_name);
1216     }
1217
1218     st = st->next;
1219   }
1220
1221   return 0;
1222 } /* int c_ipmi_init */
1223
1224 static int c_ipmi_shutdown(void) {
1225   c_ipmi_instance_t *st = instances;
1226   instances = NULL;
1227
1228   while (st != NULL) {
1229     c_ipmi_instance_t *next = st->next;
1230
1231     st->next = NULL;
1232     st->active = 0;
1233
1234     if (st->thread_id != (pthread_t)0) {
1235       pthread_join(st->thread_id, NULL);
1236       st->thread_id = (pthread_t)0;
1237     }
1238
1239     sensor_list_remove_all(st);
1240     c_ipmi_free_instance(st);
1241
1242     st = next;
1243   }
1244
1245   os_handler->free_os_handler(os_handler);
1246
1247   return 0;
1248 } /* int c_ipmi_shutdown */
1249
1250 void module_register(void) {
1251   plugin_register_complex_config("ipmi", c_ipmi_config);
1252   plugin_register_init("ipmi", c_ipmi_init);
1253   plugin_register_shutdown("ipmi", c_ipmi_shutdown);
1254 } /* void module_register */