Code

da1b0cdb635ff8aad084964f73d71e3e2df44345
[collectd.git] / src / ipmi.c
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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 : */