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