da1b0cdb635ff8aad084964f73d71e3e2df44345
1 /**
2 * collectd - src/ipmi.c
3 * Copyright (C) 2008 Florian octo Forster
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; only version 2 of the License is applicable.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * Authors:
19 * Florian octo Forster <octo at verplant.org>
20 **/
22 #include "collectd.h"
23 #include "common.h"
24 #include "plugin.h"
25 #include "utils_ignorelist.h"
27 #include <pthread.h>
29 #include <OpenIPMI/ipmiif.h>
30 #include <OpenIPMI/ipmi_err.h>
31 #include <OpenIPMI/ipmi_posix.h>
32 #include <OpenIPMI/ipmi_conn.h>
33 #include <OpenIPMI/ipmi_smi.h>
35 /*
36 * Private data types
37 */
38 struct c_ipmi_sensor_list_s;
39 typedef struct c_ipmi_sensor_list_s c_ipmi_sensor_list_t;
41 struct c_ipmi_sensor_list_s
42 {
43 ipmi_sensor_id_t sensor_id;
44 char sensor_name[DATA_MAX_NAME_LEN];
45 char sensor_type[DATA_MAX_NAME_LEN];
46 c_ipmi_sensor_list_t *next;
47 };
49 /*
50 * Module global variables
51 */
52 static pthread_mutex_t sensor_list_lock = PTHREAD_MUTEX_INITIALIZER;
53 static c_ipmi_sensor_list_t *sensor_list = NULL;
55 static int c_ipmi_init_in_progress = 0;
56 static int c_ipmi_active = 0;
57 static pthread_t thread_id = (pthread_t) 0;
59 static const char *config_keys[] =
60 {
61 "Sensor",
62 "IgnoreSelected",
63 "NotifySensorAdd",
64 "NotifySensorRemove"
65 };
66 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
68 static ignorelist_t *ignorelist = NULL;
70 static int c_ipmi_nofiy_add = 0;
71 static int c_ipmi_nofiy_remove = 0;
73 /*
74 * Misc private functions
75 */
76 static void c_ipmi_error (const char *func, int status)
77 {
78 char errbuf[4096];
80 memset (errbuf, 0, sizeof (errbuf));
82 if (IPMI_IS_OS_ERR (status))
83 {
84 sstrerror (IPMI_GET_OS_ERR (status), errbuf, sizeof (errbuf));
85 }
86 else if (IPMI_IS_IPMI_ERR (status))
87 {
88 ipmi_get_error_string (IPMI_GET_IPMI_ERR (status), errbuf, sizeof (errbuf));
89 }
91 if (errbuf[0] == 0)
92 {
93 ssnprintf (errbuf, sizeof (errbuf), "Unknown error %#x", status);
94 }
95 errbuf[sizeof (errbuf) - 1] = 0;
97 ERROR ("ipmi plugin: %s failed: %s", func, errbuf);
98 } /* void c_ipmi_error */
100 /*
101 * Sensor handlers
102 */
103 /* Prototype for sensor_list_remove, so sensor_read_handler can call it. */
104 static int sensor_list_remove (ipmi_sensor_t *sensor);
106 static void sensor_read_handler (ipmi_sensor_t *sensor,
107 int err,
108 enum ipmi_value_present_e value_present,
109 unsigned int raw_value,
110 double value,
111 ipmi_states_t *states,
112 void *user_data)
113 {
114 value_t values[1];
115 value_list_t vl = VALUE_LIST_INIT;
117 c_ipmi_sensor_list_t *list_item = (c_ipmi_sensor_list_t *)user_data;
119 if (err != 0)
120 {
121 INFO ("ipmi plugin: sensor_read_handler: Removing sensor %s, "
122 "because it failed with status %#x.",
123 list_item->sensor_name, err);
124 sensor_list_remove (sensor);
125 return;
126 }
128 if (value_present != IPMI_BOTH_VALUES_PRESENT)
129 {
130 INFO ("ipmi plugin: sensor_read_handler: Removing sensor %s, "
131 "because it provides %s. If you need this sensor, "
132 "please file a bug report.",
133 list_item->sensor_name,
134 (value_present == IPMI_RAW_VALUE_PRESENT)
135 ? "only the raw value"
136 : "no value");
137 sensor_list_remove (sensor);
138 return;
139 }
141 values[0].gauge = value;
143 vl.values = values;
144 vl.values_len = 1;
145 vl.time = time (NULL);
147 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
148 sstrncpy (vl.plugin, "ipmi", sizeof (vl.plugin));
149 sstrncpy (vl.type, list_item->sensor_type, sizeof (vl.type));
150 sstrncpy (vl.type_instance, list_item->sensor_name, sizeof (vl.type_instance));
152 plugin_dispatch_values (&vl);
153 } /* void sensor_read_handler */
155 static int sensor_list_add (ipmi_sensor_t *sensor)
156 {
157 ipmi_sensor_id_t sensor_id;
158 c_ipmi_sensor_list_t *list_item;
159 c_ipmi_sensor_list_t *list_prev;
161 char sensor_name[DATA_MAX_NAME_LEN];
162 char *sensor_name_ptr;
163 int sensor_type, len;
164 const char *type;
165 ipmi_entity_t *ent = ipmi_sensor_get_entity(sensor);
167 sensor_id = ipmi_sensor_convert_to_id (sensor);
169 memset (sensor_name, 0, sizeof (sensor_name));
170 ipmi_sensor_get_name (sensor, sensor_name, sizeof (sensor_name));
171 sensor_name[sizeof (sensor_name) - 1] = 0;
173 len = DATA_MAX_NAME_LEN - strlen(sensor_name);
174 strncat(sensor_name, " ", len--);
175 strncat(sensor_name, ipmi_entity_get_entity_id_string(ent), len);
177 sensor_name_ptr = strstr (sensor_name, ").");
178 if (sensor_name_ptr == NULL)
179 sensor_name_ptr = sensor_name;
180 else
181 {
182 char *sensor_name_ptr_id = strstr (sensor_name, "(");
184 sensor_name_ptr += 2;
185 len = DATA_MAX_NAME_LEN - strlen(sensor_name);
186 strncat(sensor_name, " ", len--);
187 strncat(sensor_name, sensor_name_ptr_id,
188 MIN(sensor_name_ptr - sensor_name_ptr_id - 1, len));
189 }
191 /* Both `ignorelist' and `plugin_instance' may be NULL. */
192 if (ignorelist_match (ignorelist, sensor_name_ptr) != 0)
193 return (0);
195 /* FIXME: Use rate unit or base unit to scale the value */
197 sensor_type = ipmi_sensor_get_sensor_type (sensor);
198 switch (sensor_type)
199 {
200 case IPMI_SENSOR_TYPE_TEMPERATURE:
201 type = "temperature";
202 break;
204 case IPMI_SENSOR_TYPE_VOLTAGE:
205 type = "voltage";
206 break;
208 case IPMI_SENSOR_TYPE_CURRENT:
209 type = "current";
210 break;
212 case IPMI_SENSOR_TYPE_FAN:
213 type = "fanspeed";
214 break;
216 default:
217 {
218 const char *sensor_type_str;
220 sensor_type_str = ipmi_sensor_get_sensor_type_string (sensor);
221 INFO ("ipmi plugin: sensor_list_add: Ignore sensor %s, "
222 "because I don't know how to handle its type (%#x, %s). "
223 "If you need this sensor, please file a bug report.",
224 sensor_name_ptr, sensor_type, sensor_type_str);
225 return (-1);
226 }
227 } /* switch (sensor_type) */
229 pthread_mutex_lock (&sensor_list_lock);
231 list_prev = NULL;
232 for (list_item = sensor_list;
233 list_item != NULL;
234 list_item = list_item->next)
235 {
236 if (ipmi_cmp_sensor_id (sensor_id, list_item->sensor_id) == 0)
237 break;
238 list_prev = list_item;
239 } /* for (list_item) */
241 if (list_item != NULL)
242 {
243 pthread_mutex_unlock (&sensor_list_lock);
244 return (0);
245 }
247 list_item = (c_ipmi_sensor_list_t *) calloc (1, sizeof (c_ipmi_sensor_list_t));
248 if (list_item == NULL)
249 {
250 pthread_mutex_unlock (&sensor_list_lock);
251 return (-1);
252 }
254 list_item->sensor_id = ipmi_sensor_convert_to_id (sensor);
256 if (list_prev != NULL)
257 list_prev->next = list_item;
258 else
259 sensor_list = list_item;
261 sstrncpy (list_item->sensor_name, sensor_name_ptr,
262 sizeof (list_item->sensor_name));
263 sstrncpy (list_item->sensor_type, type, sizeof (list_item->sensor_type));
265 pthread_mutex_unlock (&sensor_list_lock);
267 if (c_ipmi_nofiy_add && (c_ipmi_init_in_progress == 0))
268 {
269 notification_t n = { NOTIF_OKAY, time(NULL), "", "", "ipmi",
270 "", "", "", NULL };
272 sstrncpy (n.host, hostname_g, sizeof (n.host));
273 sstrncpy (n.type_instance, list_item->sensor_name,
274 sizeof (n.type_instance));
275 sstrncpy (n.type, list_item->sensor_type, sizeof (n.type));
276 ssnprintf (n.message, sizeof (n.message),
277 "sensor %s added", list_item->sensor_name);
279 plugin_dispatch_notification (&n);
280 }
282 return (0);
283 } /* int sensor_list_add */
285 static int sensor_list_remove (ipmi_sensor_t *sensor)
286 {
287 ipmi_sensor_id_t sensor_id;
288 c_ipmi_sensor_list_t *list_item;
289 c_ipmi_sensor_list_t *list_prev;
291 sensor_id = ipmi_sensor_convert_to_id (sensor);
293 pthread_mutex_lock (&sensor_list_lock);
295 list_prev = NULL;
296 for (list_item = sensor_list;
297 list_item != NULL;
298 list_item = list_item->next)
299 {
300 if (ipmi_cmp_sensor_id (sensor_id, list_item->sensor_id) == 0)
301 break;
302 list_prev = list_item;
303 } /* for (list_item) */
305 if (list_item == NULL)
306 {
307 pthread_mutex_unlock (&sensor_list_lock);
308 return (-1);
309 }
311 if (list_prev == NULL)
312 sensor_list = list_item->next;
313 else
314 list_prev->next = list_item->next;
316 list_prev = NULL;
317 list_item->next = NULL;
319 pthread_mutex_unlock (&sensor_list_lock);
321 if (c_ipmi_nofiy_remove && c_ipmi_active)
322 {
323 notification_t n = { NOTIF_WARNING, time(NULL), "", "",
324 "ipmi", "", "", "", NULL };
326 sstrncpy (n.host, hostname_g, sizeof (n.host));
327 sstrncpy (n.type_instance, list_item->sensor_name,
328 sizeof (n.type_instance));
329 sstrncpy (n.type, list_item->sensor_type, sizeof (n.type));
330 ssnprintf (n.message, sizeof (n.message),
331 "sensor %s removed", list_item->sensor_name);
333 plugin_dispatch_notification (&n);
334 }
336 free (list_item);
337 return (0);
338 } /* int sensor_list_remove */
340 static int sensor_list_read_all (void)
341 {
342 c_ipmi_sensor_list_t *list_item;
344 pthread_mutex_lock (&sensor_list_lock);
346 for (list_item = sensor_list;
347 list_item != NULL;
348 list_item = list_item->next)
349 {
350 ipmi_sensor_id_get_reading (list_item->sensor_id,
351 sensor_read_handler, /* user data = */ list_item);
352 } /* for (list_item) */
354 pthread_mutex_unlock (&sensor_list_lock);
356 return (0);
357 } /* int sensor_list_read_all */
359 static int sensor_list_remove_all (void)
360 {
361 c_ipmi_sensor_list_t *list_item;
363 pthread_mutex_lock (&sensor_list_lock);
365 list_item = sensor_list;
366 sensor_list = NULL;
368 pthread_mutex_unlock (&sensor_list_lock);
370 while (list_item != NULL)
371 {
372 c_ipmi_sensor_list_t *list_next = list_item->next;
374 free (list_item);
376 list_item = list_next;
377 } /* while (list_item) */
379 return (0);
380 } /* int sensor_list_remove_all */
382 /*
383 * Entity handlers
384 */
385 static void entity_sensor_update_handler (enum ipmi_update_e op,
386 ipmi_entity_t *entity,
387 ipmi_sensor_t *sensor,
388 void *user_data)
389 {
390 /* TODO: Ignore sensors we cannot read */
392 if ((op == IPMI_ADDED) || (op == IPMI_CHANGED))
393 {
394 /* Will check for duplicate entries.. */
395 sensor_list_add (sensor);
396 }
397 else if (op == IPMI_DELETED)
398 {
399 sensor_list_remove (sensor);
400 }
401 } /* void entity_sensor_update_handler */
403 /*
404 * Domain handlers
405 */
406 static void domain_entity_update_handler (enum ipmi_update_e op,
407 ipmi_domain_t *domain,
408 ipmi_entity_t *entity,
409 void *user_data)
410 {
411 int status;
413 if (op == IPMI_ADDED)
414 {
415 status = ipmi_entity_add_sensor_update_handler (entity,
416 entity_sensor_update_handler, /* user data = */ NULL);
417 if (status != 0)
418 {
419 c_ipmi_error ("ipmi_entity_add_sensor_update_handler", status);
420 }
421 }
422 else if (op == IPMI_DELETED)
423 {
424 status = ipmi_entity_remove_sensor_update_handler (entity,
425 entity_sensor_update_handler, /* user data = */ NULL);
426 if (status != 0)
427 {
428 c_ipmi_error ("ipmi_entity_remove_sensor_update_handler", status);
429 }
430 }
431 } /* void domain_entity_update_handler */
433 static void domain_connection_change_handler (ipmi_domain_t *domain,
434 int err,
435 unsigned int conn_num,
436 unsigned int port_num,
437 int still_connected,
438 void *user_data)
439 {
440 int status;
442 printf ("domain_connection_change_handler (domain = %p, err = %i, "
443 "conn_num = %u, port_num = %u, still_connected = %i, "
444 "user_data = %p);\n",
445 (void *) domain, err, conn_num, port_num, still_connected, user_data);
447 status = ipmi_domain_add_entity_update_handler (domain,
448 domain_entity_update_handler, /* user data = */ NULL);
449 if (status != 0)
450 {
451 c_ipmi_error ("ipmi_domain_add_entity_update_handler", status);
452 }
453 } /* void domain_connection_change_handler */
455 static int thread_init (os_handler_t **ret_os_handler)
456 {
457 os_handler_t *os_handler;
458 ipmi_open_option_t open_option[1];
459 ipmi_con_t *smi_connection = NULL;
460 ipmi_domain_id_t domain_id;
461 int status;
463 os_handler = ipmi_posix_thread_setup_os_handler (SIGUSR2);
464 if (os_handler == NULL)
465 {
466 ERROR ("ipmi plugin: ipmi_posix_thread_setup_os_handler failed.");
467 return (-1);
468 }
470 ipmi_init (os_handler);
472 status = ipmi_smi_setup_con (/* if_num = */ 0,
473 os_handler,
474 /* user data = */ NULL,
475 &smi_connection);
476 if (status != 0)
477 {
478 c_ipmi_error ("ipmi_smi_setup_con", status);
479 return (-1);
480 }
482 memset (open_option, 0, sizeof (open_option));
483 open_option[0].option = IPMI_OPEN_OPTION_ALL;
484 open_option[0].ival = 1;
486 status = ipmi_open_domain ("mydomain", &smi_connection, /* num_con = */ 1,
487 domain_connection_change_handler, /* user data = */ NULL,
488 /* domain_fully_up_handler = */ NULL, /* user data = */ NULL,
489 open_option, sizeof (open_option) / sizeof (open_option[0]),
490 &domain_id);
491 if (status != 0)
492 {
493 c_ipmi_error ("ipmi_open_domain", status);
494 return (-1);
495 }
497 *ret_os_handler = os_handler;
498 return (0);
499 } /* int thread_init */
501 static void *thread_main (void *user_data)
502 {
503 int status;
504 os_handler_t *os_handler = NULL;
506 status = thread_init (&os_handler);
507 if (status != 0)
508 {
509 fprintf (stderr, "ipmi plugin: thread_init failed.\n");
510 return ((void *) -1);
511 }
513 while (c_ipmi_active != 0)
514 {
515 struct timeval tv = { 1, 0 };
516 os_handler->perform_one_op (os_handler, &tv);
517 }
519 ipmi_posix_thread_free_os_handler (os_handler);
521 return ((void *) 0);
522 } /* void *thread_main */
524 static int c_ipmi_config (const char *key, const char *value)
525 {
526 if (ignorelist == NULL)
527 ignorelist = ignorelist_create (/* invert = */ 1);
528 if (ignorelist == NULL)
529 return (1);
531 if (strcasecmp ("Sensor", key) == 0)
532 {
533 ignorelist_add (ignorelist, value);
534 }
535 else if (strcasecmp ("IgnoreSelected", key) == 0)
536 {
537 int invert = 1;
538 if ((strcasecmp ("True", value) == 0)
539 || (strcasecmp ("Yes", value) == 0)
540 || (strcasecmp ("On", value) == 0))
541 invert = 0;
542 ignorelist_set_invert (ignorelist, invert);
543 }
544 else if (strcasecmp ("NotifySensorAdd", key) == 0)
545 {
546 if ((strcasecmp ("True", value) == 0)
547 || (strcasecmp ("Yes", value) == 0)
548 || (strcasecmp ("On", value) == 0))
549 c_ipmi_nofiy_add = 1;
550 }
551 else if (strcasecmp ("NotifySensorRemove", key) == 0)
552 {
553 if ((strcasecmp ("True", value) == 0)
554 || (strcasecmp ("Yes", value) == 0)
555 || (strcasecmp ("On", value) == 0))
556 c_ipmi_nofiy_remove = 1;
557 }
558 else
559 {
560 return (-1);
561 }
563 return (0);
564 } /* int c_ipmi_config */
566 static int c_ipmi_init (void)
567 {
568 int status;
570 /* Don't send `ADD' notifications during startup (~ 1 minute) */
571 c_ipmi_init_in_progress = 1 + (60 / interval_g);
573 c_ipmi_active = 1;
575 status = pthread_create (&thread_id, /* attr = */ NULL, thread_main,
576 /* user data = */ NULL);
577 if (status != 0)
578 {
579 c_ipmi_active = 0;
580 thread_id = (pthread_t) 0;
581 ERROR ("ipmi plugin: pthread_create failed.");
582 return (-1);
583 }
585 return (0);
586 } /* int c_ipmi_init */
588 static int c_ipmi_read (void)
589 {
590 if ((c_ipmi_active == 0) || (thread_id == (pthread_t) 0))
591 {
592 INFO ("ipmi plugin: c_ipmi_read: I'm not active, returning false.");
593 return (-1);
594 }
596 sensor_list_read_all ();
598 if (c_ipmi_init_in_progress > 0)
599 c_ipmi_init_in_progress--;
600 else
601 c_ipmi_init_in_progress = 0;
603 return (0);
604 } /* int c_ipmi_read */
606 static int c_ipmi_shutdown (void)
607 {
608 c_ipmi_active = 0;
610 if (thread_id != (pthread_t) 0)
611 {
612 pthread_join (thread_id, NULL);
613 thread_id = (pthread_t) 0;
614 }
616 sensor_list_remove_all ();
618 return (0);
619 } /* int c_ipmi_shutdown */
621 void module_register (void)
622 {
623 plugin_register_config ("ipmi", c_ipmi_config,
624 config_keys, config_keys_num);
625 plugin_register_init ("ipmi", c_ipmi_init);
626 plugin_register_read ("ipmi", c_ipmi_read);
627 plugin_register_shutdown ("ipmi", c_ipmi_shutdown);
628 } /* void module_register */
630 /* vim: set sw=2 sts=2 ts=8 fdm=marker et : */