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
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.
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.
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
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>
31 #include "utils_ignorelist.h"
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>
44 struct c_ipmi_sensor_list_s;
45 typedef struct c_ipmi_sensor_list_s c_ipmi_sensor_list_t;
47 struct c_ipmi_instance_s {
49 ignorelist_t *ignorelist;
52 _Bool notify_notpresent;
54 _Bool sel_clear_event;
60 unsigned int authtype;
63 ipmi_con_t *connection;
64 pthread_mutex_t sensor_list_lock;
65 c_ipmi_sensor_list_t *sensor_list;
71 struct c_ipmi_instance_s *next;
73 typedef struct c_ipmi_instance_s c_ipmi_instance_t;
75 struct c_ipmi_sensor_list_s {
76 ipmi_sensor_id_t sensor_id;
77 char sensor_name[DATA_MAX_NAME_LEN];
78 char sensor_type[DATA_MAX_NAME_LEN];
79 int sensor_not_present;
80 c_ipmi_sensor_list_t *next;
81 c_ipmi_instance_t *instance;
86 * Module global variables
88 static os_handler_t *os_handler;
89 static c_ipmi_instance_t *instances = NULL;
92 * Misc private functions
94 static void c_ipmi_error(c_ipmi_instance_t *st, const char *func, int status) {
95 char errbuf[4096] = {0};
97 if (IPMI_IS_OS_ERR(status) || IPMI_IS_RMCPP_ERR(status) ||
98 IPMI_IS_IPMI_ERR(status)) {
99 ipmi_get_error_string(status, errbuf, sizeof(errbuf));
102 if (errbuf[0] == 0) {
103 snprintf(errbuf, sizeof(errbuf), "Unknown error %#x", status);
105 errbuf[sizeof(errbuf) - 1] = 0;
107 ERROR("ipmi plugin: %s failed for `%s`: %s", func, st->name, errbuf);
108 } /* void c_ipmi_error */
110 static void c_ipmi_log(os_handler_t *handler, const char *format,
111 enum ipmi_log_type_e log_type, va_list ap) {
114 vsnprintf(msg, sizeof(msg), format, ap);
118 INFO("ipmi plugin: %s", msg);
120 case IPMI_LOG_WARNING:
121 NOTICE("ipmi plugin: %s", msg);
123 case IPMI_LOG_SEVERE:
124 WARNING("ipmi plugin: %s", msg);
127 ERROR("ipmi plugin: %s", msg);
129 case IPMI_LOG_ERR_INFO:
130 ERROR("ipmi plugin: %s", msg);
133 case IPMI_LOG_DEBUG_START:
135 fprintf(stderr, "ipmi plugin: %s\n", msg);
137 case IPMI_LOG_DEBUG_CONT:
138 case IPMI_LOG_DEBUG_END:
139 fprintf(stderr, "%s\n", msg);
142 case IPMI_LOG_DEBUG_START:
144 case IPMI_LOG_DEBUG_CONT:
145 case IPMI_LOG_DEBUG_END:
149 } /* void c_ipmi_log */
154 /* Prototype for sensor_list_remove, so sensor_read_handler can call it. */
155 static int sensor_list_remove(c_ipmi_instance_t *st, ipmi_sensor_t *sensor);
157 static void sensor_read_handler(ipmi_sensor_t *sensor, int err,
158 enum ipmi_value_present_e value_present,
159 unsigned int __attribute__((unused)) raw_value,
160 double value, ipmi_states_t *states,
162 value_list_t vl = VALUE_LIST_INIT;
164 c_ipmi_sensor_list_t *list_item = (c_ipmi_sensor_list_t *)user_data;
165 c_ipmi_instance_t *st = list_item->instance;
170 if (IPMI_IS_IPMI_ERR(err) &&
171 IPMI_GET_IPMI_ERR(err) == IPMI_NOT_PRESENT_CC) {
172 if (list_item->sensor_not_present == 0) {
173 list_item->sensor_not_present = 1;
175 INFO("ipmi plugin: sensor_read_handler: sensor `%s` of `%s` "
177 list_item->sensor_name, st->name);
179 if (st->notify_notpresent) {
181 NOTIF_WARNING, cdtime(), "", "", "ipmi", "", "", "", NULL};
183 sstrncpy(n.host, (st->host != NULL) ? st->host : hostname_g,
185 sstrncpy(n.type_instance, list_item->sensor_name,
186 sizeof(n.type_instance));
187 sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
188 snprintf(n.message, sizeof(n.message), "sensor %s not present",
189 list_item->sensor_name);
191 plugin_dispatch_notification(&n);
194 } else if (IPMI_IS_IPMI_ERR(err) &&
195 IPMI_GET_IPMI_ERR(err) ==
196 IPMI_NOT_SUPPORTED_IN_PRESENT_STATE_CC) {
197 INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` not ready.",
198 list_item->sensor_name, st->name);
199 } else if (IPMI_IS_IPMI_ERR(err) &&
200 IPMI_GET_IPMI_ERR(err) == IPMI_TIMEOUT_CC) {
201 INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` timed out.",
202 list_item->sensor_name, st->name);
204 char errbuf[128] = {0};
205 ipmi_get_error_string(err, errbuf, sizeof(errbuf) - 1);
207 if (IPMI_IS_IPMI_ERR(err))
208 INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed: "
210 list_item->sensor_name, st->name, errbuf);
211 else if (IPMI_IS_OS_ERR(err))
212 INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed: "
214 list_item->sensor_name, st->name, errbuf, IPMI_GET_OS_ERR(err));
215 else if (IPMI_IS_RMCPP_ERR(err))
216 INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed: "
218 list_item->sensor_name, st->name, errbuf);
219 else if (IPMI_IS_SOL_ERR(err))
220 INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed: "
222 list_item->sensor_name, st->name, errbuf, IPMI_GET_SOL_ERR(err));
224 INFO("ipmi plugin: sensor_read_handler: Sensor `%s` of `%s` failed "
225 "with error %#x. of class %#x",
226 list_item->sensor_name, st->name, err & 0xff, err & 0xffffff00);
229 } else if (list_item->sensor_not_present == 1) {
230 list_item->sensor_not_present = 0;
232 INFO("ipmi plugin: sensor_read_handler: sensor `%s` of `%s` present.",
233 list_item->sensor_name, st->name);
235 if (st->notify_notpresent) {
236 notification_t n = {NOTIF_OKAY, cdtime(), "", "", "ipmi",
239 sstrncpy(n.host, (st->host != NULL) ? st->host : hostname_g,
241 sstrncpy(n.type_instance, list_item->sensor_name,
242 sizeof(n.type_instance));
243 sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
244 snprintf(n.message, sizeof(n.message), "sensor %s present",
245 list_item->sensor_name);
247 plugin_dispatch_notification(&n);
251 if (value_present != IPMI_BOTH_VALUES_PRESENT) {
252 INFO("ipmi plugin: sensor_read_handler: Removing sensor `%s` of `%s`, "
253 "because it provides %s. If you need this sensor, "
254 "please file a bug report.",
255 list_item->sensor_name, st->name,
256 (value_present == IPMI_RAW_VALUE_PRESENT) ? "only the raw value"
258 sensor_list_remove(st, sensor);
262 if (!ipmi_is_sensor_scanning_enabled(states)) {
263 DEBUG("ipmi plugin: sensor_read_handler: Skipping sensor `%s` of `%s`, "
264 "it is in 'scanning disabled' state.",
265 list_item->sensor_name, st->name);
269 if (ipmi_is_initial_update_in_progress(states)) {
270 DEBUG("ipmi plugin: sensor_read_handler: Skipping sensor `%s` of `%s`, "
271 "it is in 'initial update in progress' state.",
272 list_item->sensor_name, st->name);
276 vl.values = &(value_t){.gauge = value};
279 if (st->host != NULL)
280 sstrncpy(vl.host, st->host, sizeof(vl.host));
281 sstrncpy(vl.plugin, "ipmi", sizeof(vl.plugin));
282 sstrncpy(vl.type, list_item->sensor_type, sizeof(vl.type));
283 sstrncpy(vl.type_instance, list_item->sensor_name, sizeof(vl.type_instance));
285 plugin_dispatch_values(&vl);
286 } /* void sensor_read_handler */
288 static void sensor_get_name(ipmi_sensor_t *sensor, char *buffer, int buf_len) {
289 char temp[DATA_MAX_NAME_LEN] = {0};
290 ipmi_entity_t *ent = ipmi_sensor_get_entity(sensor);
291 const char *entity_id_string = ipmi_entity_get_entity_id_string(ent);
292 char sensor_name[DATA_MAX_NAME_LEN] = "";
293 char *sensor_name_ptr;
295 if ((buffer == NULL) || (buf_len == 0))
298 ipmi_sensor_get_name(sensor, temp, sizeof(temp));
299 temp[sizeof(temp) - 1] = 0;
301 if (entity_id_string != NULL && strlen(temp))
302 snprintf(sensor_name, sizeof(sensor_name), "%s %s", temp, entity_id_string);
303 else if (entity_id_string != NULL)
304 sstrncpy(sensor_name, entity_id_string, sizeof(sensor_name));
306 sstrncpy(sensor_name, temp, sizeof(sensor_name));
309 sstrncpy(temp, sensor_name, sizeof(temp));
310 sensor_name_ptr = strstr(temp, ").");
311 if (sensor_name_ptr != NULL) {
312 /* If name is something like "foo (123).bar",
313 * change that to "bar (123)".
314 * Both, sensor_name_ptr and sensor_id_ptr point to memory within the
315 * `temp' array, which holds a copy of the current `sensor_name'. */
318 /* `sensor_name_ptr' points to ").bar". */
319 sensor_name_ptr[1] = 0;
320 /* `temp' holds "foo (123)\0bar\0". */
321 sensor_name_ptr += 2;
322 /* `sensor_name_ptr' now points to "bar". */
324 sensor_id_ptr = strstr(temp, "(");
325 if (sensor_id_ptr != NULL) {
326 /* `sensor_id_ptr' now points to "(123)". */
327 snprintf(sensor_name, sizeof(sensor_name), "%s %s", sensor_name_ptr,
330 /* else: don't touch sensor_name. */
333 sstrncpy(buffer, sensor_name, buf_len);
336 static int sensor_list_add(c_ipmi_instance_t *st, ipmi_sensor_t *sensor) {
337 ipmi_sensor_id_t sensor_id;
338 c_ipmi_sensor_list_t *list_item;
339 c_ipmi_sensor_list_t *list_prev;
341 char buffer[DATA_MAX_NAME_LEN] = {0};
342 char *sensor_name_ptr = buffer;
346 sensor_id = ipmi_sensor_convert_to_id(sensor);
347 sensor_get_name(sensor, buffer, sizeof(buffer));
349 DEBUG("ipmi plugin: sensor_list_add: Found sensor `%s` of `%s`,"
351 " Event reading type: %#x"
353 " Event support: %#x",
354 sensor_name_ptr, st->name, ipmi_sensor_get_sensor_type(sensor),
355 ipmi_sensor_get_event_reading_type(sensor),
356 ipmi_sensor_get_sensor_direction(sensor),
357 ipmi_sensor_get_event_support(sensor));
359 /* Both `ignorelist' and `sensor_name_ptr' may be NULL. */
360 if (ignorelist_match(st->ignorelist, sensor_name_ptr) != 0)
363 /* FIXME: Use rate unit or base unit to scale the value */
365 sensor_type = ipmi_sensor_get_sensor_type(sensor);
368 * ipmitool/lib/ipmi_sdr.c sdr_sensor_has_analog_reading() has a notice
369 * about 'Threshold sensors' and 'analog readings'. Discrete sensor may
370 * have analog data, but discrete sensors support is not implemented
373 * ipmi_sensor_id_get_reading() supports only 'Threshold' sensors.
374 * See lib/sensor.c:4842, stand_ipmi_sensor_get_reading() for details.
376 if (!ipmi_sensor_get_is_readable(sensor)) {
377 INFO("ipmi plugin: sensor_list_add: Ignore sensor `%s` of `%s`, "
378 "because it don't readable! Its type: (%#x, %s). ",
379 sensor_name_ptr, st->name, sensor_type,
380 ipmi_sensor_get_sensor_type_string(sensor));
384 if (ipmi_sensor_get_event_reading_type(sensor) !=
385 IPMI_EVENT_READING_TYPE_THRESHOLD) {
386 INFO("ipmi plugin: sensor_list_add: Ignore sensor `%s` of `%s`, "
387 "because it is discrete (%#x)! Its type: (%#x, %s). ",
388 sensor_name_ptr, st->name, sensor_type,
389 ipmi_sensor_get_event_reading_type(sensor),
390 ipmi_sensor_get_sensor_type_string(sensor));
394 switch (sensor_type) {
395 case IPMI_SENSOR_TYPE_TEMPERATURE:
396 type = "temperature";
399 case IPMI_SENSOR_TYPE_VOLTAGE:
403 case IPMI_SENSOR_TYPE_CURRENT:
407 case IPMI_SENSOR_TYPE_FAN:
412 INFO("ipmi plugin: sensor_list_add: Ignore sensor `%s` of `%s`, "
413 "because I don't know how to handle its type (%#x, %s). "
414 "If you need this sensor, please file a bug report.",
415 sensor_name_ptr, st->name, sensor_type,
416 ipmi_sensor_get_sensor_type_string(sensor));
419 } /* switch (sensor_type) */
421 pthread_mutex_lock(&st->sensor_list_lock);
424 for (list_item = st->sensor_list; list_item != NULL;
425 list_item = list_item->next) {
426 if (ipmi_cmp_sensor_id(sensor_id, list_item->sensor_id) == 0)
428 list_prev = list_item;
429 } /* for (list_item) */
431 if (list_item != NULL) {
432 pthread_mutex_unlock(&st->sensor_list_lock);
436 list_item = (c_ipmi_sensor_list_t *)calloc(1, sizeof(c_ipmi_sensor_list_t));
437 if (list_item == NULL) {
438 pthread_mutex_unlock(&st->sensor_list_lock);
442 list_item->instance = st;
443 list_item->sensor_id = ipmi_sensor_convert_to_id(sensor);
445 if (list_prev != NULL)
446 list_prev->next = list_item;
448 st->sensor_list = list_item;
450 sstrncpy(list_item->sensor_name, sensor_name_ptr,
451 sizeof(list_item->sensor_name));
452 sstrncpy(list_item->sensor_type, type, sizeof(list_item->sensor_type));
454 pthread_mutex_unlock(&st->sensor_list_lock);
456 if (st->notify_add && (st->init_in_progress == 0)) {
457 notification_t n = {NOTIF_OKAY, cdtime(), "", "", "ipmi", "", "", "", NULL};
459 sstrncpy(n.host, (st->host != NULL) ? st->host : hostname_g,
461 sstrncpy(n.type_instance, list_item->sensor_name, sizeof(n.type_instance));
462 sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
463 snprintf(n.message, sizeof(n.message), "sensor %s added",
464 list_item->sensor_name);
466 plugin_dispatch_notification(&n);
470 } /* int sensor_list_add */
472 static int sensor_list_remove(c_ipmi_instance_t *st, ipmi_sensor_t *sensor) {
473 ipmi_sensor_id_t sensor_id;
474 c_ipmi_sensor_list_t *list_item;
475 c_ipmi_sensor_list_t *list_prev;
477 sensor_id = ipmi_sensor_convert_to_id(sensor);
479 pthread_mutex_lock(&st->sensor_list_lock);
482 for (list_item = st->sensor_list; list_item != NULL;
483 list_item = list_item->next) {
484 if (ipmi_cmp_sensor_id(sensor_id, list_item->sensor_id) == 0)
486 list_prev = list_item;
487 } /* for (list_item) */
489 if (list_item == NULL) {
490 pthread_mutex_unlock(&st->sensor_list_lock);
494 if (list_prev == NULL)
495 st->sensor_list = list_item->next;
497 list_prev->next = list_item->next;
500 list_item->next = NULL;
502 pthread_mutex_unlock(&st->sensor_list_lock);
504 if (st->notify_remove && st->active) {
505 notification_t n = {NOTIF_WARNING, cdtime(), "", "", "ipmi", "", "", "",
508 sstrncpy(n.host, (st->host != NULL) ? st->host : hostname_g,
510 sstrncpy(n.type_instance, list_item->sensor_name, sizeof(n.type_instance));
511 sstrncpy(n.type, list_item->sensor_type, sizeof(n.type));
512 snprintf(n.message, sizeof(n.message), "sensor %s removed",
513 list_item->sensor_name);
515 plugin_dispatch_notification(&n);
520 } /* int sensor_list_remove */
522 static int sensor_list_read_all(c_ipmi_instance_t *st) {
523 pthread_mutex_lock(&st->sensor_list_lock);
525 for (c_ipmi_sensor_list_t *list_item = st->sensor_list; list_item != NULL;
526 list_item = list_item->next) {
527 DEBUG("ipmi plugin: try read sensor `%s` of `%s`, use: %d",
528 list_item->sensor_name, st->name, list_item->use);
530 /* Reading already initiated */
535 ipmi_sensor_id_get_reading(list_item->sensor_id, sensor_read_handler,
536 /* user data = */ (void *)list_item);
537 } /* for (list_item) */
539 pthread_mutex_unlock(&st->sensor_list_lock);
542 } /* int sensor_list_read_all */
544 static int sensor_list_remove_all(c_ipmi_instance_t *st) {
545 c_ipmi_sensor_list_t *list_item;
547 pthread_mutex_lock(&st->sensor_list_lock);
549 list_item = st->sensor_list;
550 st->sensor_list = NULL;
552 pthread_mutex_unlock(&st->sensor_list_lock);
554 while (list_item != NULL) {
555 c_ipmi_sensor_list_t *list_next = list_item->next;
559 list_item = list_next;
560 } /* while (list_item) */
563 } /* int sensor_list_remove_all */
565 static int sensor_convert_threshold_severity(enum ipmi_thresh_e severity) {
567 case IPMI_LOWER_NON_CRITICAL:
568 case IPMI_UPPER_NON_CRITICAL:
570 case IPMI_LOWER_CRITICAL:
571 case IPMI_UPPER_CRITICAL:
572 return NOTIF_WARNING;
573 case IPMI_LOWER_NON_RECOVERABLE:
574 case IPMI_UPPER_NON_RECOVERABLE:
575 return NOTIF_FAILURE;
578 } /* switch (severity) */
579 } /* int sensor_convert_threshold_severity */
581 static void add_event_common_data(notification_t *n, ipmi_sensor_t *sensor,
582 enum ipmi_event_dir_e dir,
583 ipmi_event_t *event) {
584 ipmi_entity_t *ent = ipmi_sensor_get_entity(sensor);
586 plugin_notification_meta_add_string(n, "entity_name",
587 ipmi_entity_get_entity_id_string(ent));
588 plugin_notification_meta_add_signed_int(n, "entity_id",
589 ipmi_entity_get_entity_id(ent));
590 plugin_notification_meta_add_signed_int(n, "entity_instance",
591 ipmi_entity_get_entity_instance(ent));
592 plugin_notification_meta_add_boolean(n, "assert", dir == IPMI_ASSERTION);
595 plugin_notification_meta_add_signed_int(n, "event_type",
596 ipmi_event_get_type(event));
597 } /* void add_event_sensor_meta_data */
599 static int sensor_threshold_event_handler(
600 ipmi_sensor_t *sensor, enum ipmi_event_dir_e dir,
601 enum ipmi_thresh_e threshold, enum ipmi_event_value_dir_e high_low,
602 enum ipmi_value_present_e value_present, unsigned int raw_value,
603 double value, void *cb_data, ipmi_event_t *event) {
605 c_ipmi_instance_t *st = (c_ipmi_instance_t *)cb_data;
607 /* From the IPMI specification Chapter 2: Events.
608 * If a callback handles the event, then all future callbacks called due to
609 * the event will receive a NULL for the event. So be ready to handle a NULL
610 * event in all your event handlers. A NULL may also be passed to an event
611 * handler if the callback was not due to an event. */
613 return IPMI_EVENT_NOT_HANDLED;
615 /* offset is a table index and it's represented as enum of strings that are
616 organized in the way - high and low for each threshold severity level */
617 notification_t n = {NOTIF_OKAY, cdtime(), "", "", "ipmi", "", "", "", NULL};
618 unsigned int offset = (2 * threshold) + high_low;
619 unsigned int event_type = ipmi_sensor_get_event_reading_type(sensor);
620 unsigned int sensor_type = ipmi_sensor_get_sensor_type(sensor);
621 const char *event_state =
622 ipmi_get_reading_name(event_type, sensor_type, offset);
623 sensor_get_name(sensor, n.type_instance, sizeof(n.type_instance));
624 if (value_present != IPMI_NO_VALUES_PRESENT)
625 snprintf(n.message, sizeof(n.message),
626 "sensor %s received event: %s, value is %f", n.type_instance,
629 snprintf(n.message, sizeof(n.message),
630 "sensor %s received event: %s, value not provided",
631 n.type_instance, event_state);
633 DEBUG("Threshold event received for sensor %s", n.type_instance);
635 sstrncpy(n.host, (st->host != NULL) ? st->host : hostname_g, sizeof(n.host));
636 sstrncpy(n.type, ipmi_sensor_get_sensor_type_string(sensor), sizeof(n.type));
637 n.severity = sensor_convert_threshold_severity(threshold);
638 n.time = NS_TO_CDTIME_T(ipmi_event_get_timestamp(event));
640 plugin_notification_meta_add_string(&n, "severity",
641 ipmi_get_threshold_string(threshold));
642 plugin_notification_meta_add_string(&n, "direction",
643 ipmi_get_value_dir_string(high_low));
645 switch (value_present) {
646 case IPMI_BOTH_VALUES_PRESENT:
647 plugin_notification_meta_add_double(&n, "val", value);
648 /* both values present, so fall-through to add raw value too */
649 case IPMI_RAW_VALUE_PRESENT: {
650 char buf[DATA_MAX_NAME_LEN] = {0};
651 snprintf(buf, sizeof(buf), "0x%2.2x", raw_value);
652 plugin_notification_meta_add_string(&n, "raw", buf);
656 } /* switch (value_present) */
658 add_event_common_data(&n, sensor, dir, event);
660 plugin_dispatch_notification(&n);
661 plugin_notification_meta_free(n.meta);
663 /* Delete handled ipmi event from the list */
664 if (st->sel_clear_event) {
665 ipmi_event_delete(event, NULL, NULL);
666 return IPMI_EVENT_HANDLED;
669 return IPMI_EVENT_NOT_HANDLED;
670 } /* int sensor_threshold_event_handler */
672 static int sensor_discrete_event_handler(ipmi_sensor_t *sensor,
673 enum ipmi_event_dir_e dir, int offset,
674 int severity, int prev_severity,
675 void *cb_data, ipmi_event_t *event) {
677 c_ipmi_instance_t *st = (c_ipmi_instance_t *)cb_data;
679 /* From the IPMI specification Chapter 2: Events.
680 * If a callback handles the event, then all future callbacks called due to
681 * the event will receive a NULL for the event. So be ready to handle a NULL
682 * event in all your event handlers. A NULL may also be passed to an event
683 * handler if the callback was not due to an event. */
685 return IPMI_EVENT_NOT_HANDLED;
687 notification_t n = {NOTIF_OKAY, cdtime(), "", "", "ipmi", "", "", "", NULL};
688 unsigned int event_type = ipmi_sensor_get_event_reading_type(sensor);
689 unsigned int sensor_type = ipmi_sensor_get_sensor_type(sensor);
690 const char *event_state =
691 ipmi_get_reading_name(event_type, sensor_type, offset);
692 sensor_get_name(sensor, n.type_instance, sizeof(n.type_instance));
693 snprintf(n.message, sizeof(n.message), "sensor %s received event: %s",
694 n.type_instance, event_state);
696 DEBUG("Discrete event received for sensor %s", n.type_instance);
698 sstrncpy(n.host, (st->host != NULL) ? st->host : hostname_g, sizeof(n.host));
699 sstrncpy(n.type, ipmi_sensor_get_sensor_type_string(sensor), sizeof(n.type));
700 n.time = NS_TO_CDTIME_T(ipmi_event_get_timestamp(event));
702 plugin_notification_meta_add_signed_int(&n, "offset", offset);
705 plugin_notification_meta_add_signed_int(&n, "severity", severity);
707 if (prev_severity != -1)
708 plugin_notification_meta_add_signed_int(&n, "prevseverity", prev_severity);
710 add_event_common_data(&n, sensor, dir, event);
712 plugin_dispatch_notification(&n);
713 plugin_notification_meta_free(n.meta);
715 /* Delete handled ipmi event from the list */
716 if (st->sel_clear_event) {
717 ipmi_event_delete(event, NULL, NULL);
718 return IPMI_EVENT_HANDLED;
721 return IPMI_EVENT_NOT_HANDLED;
722 } /* int sensor_discrete_event_handler */
728 entity_sensor_update_handler(enum ipmi_update_e op,
729 ipmi_entity_t __attribute__((unused)) * entity,
730 ipmi_sensor_t *sensor, void *user_data) {
731 c_ipmi_instance_t *st = (c_ipmi_instance_t *)user_data;
733 if ((op == IPMI_ADDED) || (op == IPMI_CHANGED)) {
734 /* Will check for duplicate entries.. */
735 sensor_list_add(st, sensor);
737 if (st->sel_enabled) {
739 /* register threshold event if threshold sensor support events */
740 if ((ipmi_sensor_get_event_reading_type(sensor) ==
741 IPMI_EVENT_READING_TYPE_THRESHOLD) &&
742 (ipmi_sensor_get_threshold_access(sensor) !=
743 IPMI_THRESHOLD_ACCESS_SUPPORT_NONE))
744 status = ipmi_sensor_add_threshold_event_handler(
745 sensor, sensor_threshold_event_handler, st);
746 /* register discrete handler if discrete/specific sensor support events */
747 else if (ipmi_sensor_get_event_support(sensor) != IPMI_EVENT_SUPPORT_NONE)
748 status = ipmi_sensor_add_discrete_event_handler(
749 sensor, sensor_discrete_event_handler, st);
752 char buf[DATA_MAX_NAME_LEN] = {0};
753 sensor_get_name(sensor, buf, sizeof(buf));
754 ERROR("Unable to add sensor %s event handler, status: %d", buf, status);
757 } else if (op == IPMI_DELETED) {
758 sensor_list_remove(st, sensor);
760 if (st->sel_enabled) {
761 if (ipmi_sensor_get_event_reading_type(sensor) ==
762 IPMI_EVENT_READING_TYPE_THRESHOLD)
763 ipmi_sensor_remove_threshold_event_handler(
764 sensor, sensor_threshold_event_handler, st);
766 ipmi_sensor_remove_discrete_event_handler(
767 sensor, sensor_discrete_event_handler, st);
770 } /* void entity_sensor_update_handler */
776 domain_entity_update_handler(enum ipmi_update_e op,
777 ipmi_domain_t __attribute__((unused)) * domain,
778 ipmi_entity_t *entity, void *user_data) {
780 c_ipmi_instance_t *st = (c_ipmi_instance_t *)user_data;
782 if (op == IPMI_ADDED) {
783 status = ipmi_entity_add_sensor_update_handler(
784 entity, entity_sensor_update_handler, /* user data = */ (void *)st);
786 c_ipmi_error(st, "ipmi_entity_add_sensor_update_handler", status);
788 } else if (op == IPMI_DELETED) {
789 status = ipmi_entity_remove_sensor_update_handler(
790 entity, entity_sensor_update_handler, /* user data = */ (void *)st);
792 c_ipmi_error(st, "ipmi_entity_remove_sensor_update_handler", status);
795 } /* void domain_entity_update_handler */
797 static void smi_event_handler(ipmi_con_t __attribute__((unused)) * ipmi,
798 const ipmi_addr_t __attribute__((unused)) * addr,
799 unsigned int __attribute__((unused)) addr_len,
800 ipmi_event_t *event, void *cb_data) {
801 unsigned int type = ipmi_event_get_type(event);
802 ipmi_domain_t *domain = cb_data;
804 DEBUG("%s: Event received: type %u", __FUNCTION__, type);
807 /* It's not a standard IPMI event. */
810 /* force domain to reread SELs */
811 ipmi_domain_reread_sels(domain, NULL, NULL);
814 static void domain_connection_change_handler(ipmi_domain_t *domain, int err,
815 unsigned int conn_num,
816 unsigned int port_num,
820 DEBUG("domain_connection_change_handler (domain = %p, err = %i, "
821 "conn_num = %u, port_num = %u, still_connected = %i, "
823 (void *)domain, err, conn_num, port_num, still_connected, user_data);
825 c_ipmi_instance_t *st = (c_ipmi_instance_t *)user_data;
828 c_ipmi_error(st, "domain_connection_change_handler", err);
830 if (!still_connected) {
837 int status = ipmi_domain_add_entity_update_handler(
838 domain, domain_entity_update_handler, /* user data = */ st);
840 c_ipmi_error(st, "ipmi_domain_add_entity_update_handler", status);
843 status = st->connection->add_event_handler(st->connection, smi_event_handler,
847 c_ipmi_error(st, "Failed to register smi event handler", status);
848 } /* void domain_connection_change_handler */
850 static int c_ipmi_thread_init(c_ipmi_instance_t *st) {
851 ipmi_domain_id_t domain_id;
854 if (st->connaddr != NULL) {
855 char *ip_addrs[1] = {NULL}, *ports[1] = {NULL};
857 ip_addrs[0] = strdup(st->connaddr);
858 ports[0] = strdup(IPMI_LAN_STD_PORT_STR);
860 status = ipmi_ip_setup_con(ip_addrs, ports, 1, st->authtype,
861 (unsigned int)IPMI_PRIVILEGE_USER, st->username,
862 strlen(st->username), st->password,
863 strlen(st->password), os_handler,
864 /* user data = */ NULL, &st->connection);
866 c_ipmi_error(st, "ipmi_ip_setup_con", status);
870 status = ipmi_smi_setup_con(/* if_num = */ 0, os_handler,
871 /* user data = */ NULL, &st->connection);
873 c_ipmi_error(st, "ipmi_smi_setup_con", status);
878 size_t open_option_num = 0;
879 ipmi_open_option_t open_option[2];
881 open_option[open_option_num].option = IPMI_OPEN_OPTION_ALL;
882 open_option[open_option_num].ival = 1;
885 #ifdef IPMI_OPEN_OPTION_USE_CACHE
886 // This option appeared in OpenIPMI-2.0.17
887 open_option[open_option_num].option = IPMI_OPEN_OPTION_USE_CACHE;
888 open_option[open_option_num].ival = 0; /* Disable SDR cache in local file */
893 * NOTE: Domain names must be unique. There is static `domains_list` common
894 * to all threads inside lib/domain.c and some ops are done by name.
896 status = ipmi_open_domain(
897 st->name, &st->connection, /* num_con = */ 1,
898 domain_connection_change_handler, /* user data = */ (void *)st,
899 /* domain_fully_up_handler = */ NULL, /* user data = */ NULL, open_option,
900 open_option_num, &domain_id);
902 c_ipmi_error(st, "ipmi_open_domain", status);
907 } /* int c_ipmi_thread_init */
909 static void *c_ipmi_thread_main(void *user_data) {
910 c_ipmi_instance_t *st = (c_ipmi_instance_t *)user_data;
912 int status = c_ipmi_thread_init(st);
914 ERROR("ipmi plugin: c_ipmi_thread_init failed.");
919 while (st->active != 0) {
920 struct timeval tv = {1, 0};
921 os_handler->perform_one_op(os_handler, &tv);
924 } /* void *c_ipmi_thread_main */
926 static c_ipmi_instance_t *c_ipmi_init_instance() {
927 c_ipmi_instance_t *st;
929 st = calloc(1, sizeof(*st));
931 ERROR("ipmi plugin: calloc failed.");
935 st->name = strdup("main");
936 if (st->name == NULL) {
938 ERROR("ipmi plugin: strdup() failed.");
942 st->ignorelist = ignorelist_create(/* invert = */ 1);
943 if (st->ignorelist == NULL) {
946 ERROR("ipmi plugin: ignorelist_create() failed.");
950 st->sensor_list = NULL;
951 pthread_mutex_init(&st->sensor_list_lock, /* attr = */ NULL);
957 st->authtype = IPMI_AUTHTYPE_DEFAULT;
962 } /* c_ipmi_instance_t *c_ipmi_init_instance */
964 static void c_ipmi_free_instance(c_ipmi_instance_t *st) {
968 assert(st->next == NULL);
976 ignorelist_free(st->ignorelist);
977 pthread_mutex_destroy(&st->sensor_list_lock);
979 } /* void c_ipmi_free_instance */
981 static void c_ipmi_add_instance(c_ipmi_instance_t *instance) {
982 if (instances == NULL) {
983 instances = instance;
987 c_ipmi_instance_t *last = instances;
989 while (last->next != NULL)
992 last->next = instance;
993 } /* void c_ipmi_add_instance */
995 static int c_ipmi_config_add_instance(oconfig_item_t *ci) {
997 c_ipmi_instance_t *st = c_ipmi_init_instance();
1001 if (strcasecmp(ci->key, "Instance") == 0)
1002 status = cf_util_get_string(ci, &st->name);
1005 c_ipmi_free_instance(st);
1009 for (int i = 0; i < ci->children_num; i++) {
1010 oconfig_item_t *child = ci->children + i;
1012 if (strcasecmp("Sensor", child->key) == 0)
1013 ignorelist_add(st->ignorelist, ci->values[0].value.string);
1014 else if (strcasecmp("IgnoreSelected", child->key) == 0) {
1015 if (ci->values[0].value.boolean)
1016 ignorelist_set_invert(st->ignorelist, /* invert = */ 0);
1018 ignorelist_set_invert(st->ignorelist, /* invert = */ 1);
1019 } else if (strcasecmp("NotifySensorAdd", child->key) == 0) {
1020 if (ci->values[0].value.boolean)
1022 } else if (strcasecmp("NotifySensorRemove", child->key) == 0) {
1023 if (ci->values[0].value.boolean)
1024 st->notify_remove = 1;
1025 } else if (strcasecmp("NotifySensorNotPresent", child->key) == 0) {
1026 if (ci->values[0].value.boolean)
1027 st->notify_notpresent = 1;
1028 } else if (strcasecmp("SELEnabled", child->key) == 0) {
1029 if (ci->values[0].value.boolean)
1030 st->sel_enabled = 1;
1031 } else if (strcasecmp("SELClearEvent", child->key) == 0) {
1032 if (ci->values[0].value.boolean)
1033 st->sel_clear_event = 1;
1034 } else if (strcasecmp("Host", child->key) == 0)
1035 status = cf_util_get_string(child, &st->host);
1036 else if (strcasecmp("Address", child->key) == 0)
1037 status = cf_util_get_string(child, &st->connaddr);
1038 else if (strcasecmp("Username", child->key) == 0)
1039 status = cf_util_get_string(child, &st->username);
1040 else if (strcasecmp("Password", child->key) == 0)
1041 status = cf_util_get_string(child, &st->password);
1042 else if (strcasecmp("AuthType", child->key) == 0) {
1044 status = cf_util_get_string_buffer(child, tmp, sizeof(tmp));
1048 if (strcasecmp("MD5", tmp) == 0)
1049 st->authtype = IPMI_AUTHTYPE_MD5;
1050 else if (strcasecmp("rmcp+", tmp) == 0)
1051 st->authtype = IPMI_AUTHTYPE_RMCP_PLUS;
1053 WARNING("ipmi plugin: The value \"%s\" is not valid for the "
1054 "\"AuthType\" option.",
1057 WARNING("ipmi plugin: Option `%s' not allowed here.", child->key);
1066 c_ipmi_free_instance(st);
1070 c_ipmi_add_instance(st);
1073 } /* int c_ipmi_config_add_instance */
1075 static int c_ipmi_config(oconfig_item_t *ci) {
1076 _Bool have_instance_block = 0;
1078 for (int i = 0; i < ci->children_num; i++) {
1079 oconfig_item_t *child = ci->children + i;
1081 if (strcasecmp("Instance", child->key) == 0) {
1082 c_ipmi_config_add_instance(child);
1083 have_instance_block = 1;
1084 } else if (!have_instance_block) {
1085 /* Non-instance option: Assume legacy configuration (without <Instance />
1086 * blocks) and call c_ipmi_config_add_instance with the <Plugin /> block.
1088 return c_ipmi_config_add_instance(ci);
1090 WARNING("ipmi plugin: The configuration option "
1091 "\"%s\" is not allowed here. Did you "
1092 "forget to add an <Instance /> block "
1093 "around the configuration?",
1095 } /* for (ci->children) */
1098 } /* int c_ipmi_config */
1100 static int c_ipmi_read(user_data_t *user_data) {
1101 c_ipmi_instance_t *st = user_data->data;
1103 if ((st->active == 0) || (st->thread_id == (pthread_t)0)) {
1104 INFO("ipmi plugin: c_ipmi_read: I'm not active, returning false.");
1108 if (st->connected == 0)
1111 sensor_list_read_all(st);
1113 if (st->init_in_progress > 0)
1114 st->init_in_progress--;
1116 st->init_in_progress = 0;
1119 } /* int c_ipmi_read */
1121 static int c_ipmi_init(void) {
1123 c_ipmi_instance_t *st;
1124 char callback_name[3 * DATA_MAX_NAME_LEN];
1126 os_handler = ipmi_posix_thread_setup_os_handler(SIGIO);
1127 if (os_handler == NULL) {
1128 ERROR("ipmi plugin: ipmi_posix_thread_setup_os_handler failed.");
1132 os_handler->set_log_handler(os_handler, c_ipmi_log);
1134 if (ipmi_init(os_handler) != 0) {
1135 ERROR("ipmi plugin: ipmi_init() failed.");
1136 os_handler->free_os_handler(os_handler);
1140 /* Don't send `ADD' notifications during startup (~ 1 minute) */
1141 time_t iv = CDTIME_T_TO_TIME_T(plugin_get_interval());
1143 if (instances == NULL) {
1144 /* No instances were configured, let's start a default instance. */
1145 st = c_ipmi_init_instance();
1149 c_ipmi_add_instance(st);
1153 while (NULL != st) {
1154 /* The `st->name` is used as "domain name" for ipmi_open_domain().
1155 * That value should be unique, so we do plugin_register_complex_read()
1156 * at first as it checks the uniqueness. */
1157 snprintf(callback_name, sizeof(callback_name), "ipmi/%s", st->name);
1163 status = plugin_register_complex_read(
1164 /* group = */ "ipmi",
1165 /* name = */ callback_name,
1166 /* callback = */ c_ipmi_read,
1168 /* user_data = */ &ud);
1176 st->init_in_progress = 1 + (60 / iv);
1179 status = plugin_thread_create(&st->thread_id, /* attr = */ NULL,
1181 /* user data = */ (void *)st, "ipmi");
1185 st->thread_id = (pthread_t)0;
1187 plugin_unregister_read(callback_name);
1189 ERROR("ipmi plugin: pthread_create failed for `%s`.", callback_name);
1196 } /* int c_ipmi_init */
1198 static int c_ipmi_shutdown(void) {
1199 c_ipmi_instance_t *st = instances;
1202 while (st != NULL) {
1203 c_ipmi_instance_t *next = st->next;
1208 if (st->thread_id != (pthread_t)0) {
1209 pthread_join(st->thread_id, NULL);
1210 st->thread_id = (pthread_t)0;
1213 sensor_list_remove_all(st);
1214 c_ipmi_free_instance(st);
1219 os_handler->free_os_handler(os_handler);
1222 } /* int c_ipmi_shutdown */
1224 void module_register(void) {
1225 plugin_register_complex_config("ipmi", c_ipmi_config);
1226 plugin_register_init("ipmi", c_ipmi_init);
1227 plugin_register_shutdown("ipmi", c_ipmi_shutdown);
1228 } /* void module_register */