Merge branch 'collectd-4.4'
[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  **/
21
22 #include "collectd.h"
23 #include "common.h"
24 #include "plugin.h"
25 #include "utils_ignorelist.h"
26
27 #include <pthread.h>
28
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>
34
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;
40
41 struct c_ipmi_sensor_list_s
42 {
43   ipmi_sensor_id_t sensor_id;
44   c_ipmi_sensor_list_t *next;
45 };
46
47 /*
48  * Module global variables
49  */
50 static pthread_mutex_t sensor_list_lock = PTHREAD_MUTEX_INITIALIZER;
51 static c_ipmi_sensor_list_t *sensor_list = NULL;
52
53 static int c_ipmi_active = 0;
54 static pthread_t thread_id = (pthread_t) 0;
55
56 static const char *config_keys[] =
57 {
58         "Sensor",
59         "IgnoreSelected"
60 };
61 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
62
63 static ignorelist_t *ignorelist = NULL;
64
65 /*
66  * Misc private functions
67  */
68 static void c_ipmi_error (const char *func, int status)
69 {
70   char errbuf[4096];
71
72   memset (errbuf, 0, sizeof (errbuf));
73
74   if (IPMI_IS_OS_ERR (status))
75   {
76     sstrerror_r (IPMI_GET_OS_ERR (status), errbuf, sizeof (errbuf));
77   }
78   else if (IPMI_IS_IPMI_ERR (status))
79   {
80     ipmi_get_error_string (IPMI_GET_IPMI_ERR (status), errbuf, sizeof (errbuf));
81   }
82
83   if (errbuf[0] == 0)
84   {
85     ssnprintf (errbuf, sizeof (errbuf), "Unknown error %#x", status);
86   }
87   errbuf[sizeof (errbuf) - 1] = 0;
88
89   ERROR ("ipmi plugin: %s failed: %s", func, errbuf);
90 } /* void c_ipmi_error */
91
92 /*
93  * Sensor handlers
94  */
95 /* Prototype for sensor_list_remove, so sensor_read_handler can call it. */
96 static int sensor_list_remove (ipmi_sensor_t *sensor);
97
98 static void sensor_read_handler (ipmi_sensor_t *sensor,
99     int err,
100     enum ipmi_value_present_e value_present,
101     unsigned int raw_value,
102     double value,
103     ipmi_states_t *states,
104     void *user_data)
105 {
106   value_t values[1];
107   value_list_t vl = VALUE_LIST_INIT;
108
109   char sensor_name[IPMI_SENSOR_NAME_LEN];
110   char *sensor_name_ptr;
111   int sensor_type;
112   const char *type;
113
114   memset (sensor_name, 0, sizeof (sensor_name));
115   ipmi_sensor_get_name (sensor, sensor_name, sizeof (sensor_name));
116   sensor_name[sizeof (sensor_name) - 1] = 0;
117
118   sensor_name_ptr = strstr (sensor_name, ").");
119   if (sensor_name_ptr == NULL)
120     sensor_name_ptr = sensor_name;
121   else
122     sensor_name_ptr += 2;
123
124   if (err != 0)
125   {
126     INFO ("ipmi plugin: sensor_read_handler: Removing sensor %s, "
127         "because it failed with status %#x.",
128         sensor_name_ptr, err);
129     sensor_list_remove (sensor);
130     return;
131   }
132
133   if (value_present != IPMI_BOTH_VALUES_PRESENT)
134   {
135     INFO ("ipmi plugin: sensor_read_handler: Removing sensor %s, "
136         "because it provides %s. If you need this sensor, "
137         "please file a bug report.",
138         sensor_name_ptr,
139         (value_present == IPMI_RAW_VALUE_PRESENT)
140         ? "only the raw value"
141         : "no value");
142     sensor_list_remove (sensor);
143     return;
144   }
145
146   /* Both `ignorelist' and `plugin_instance' may be NULL. */
147   if (ignorelist_match (ignorelist, sensor_name_ptr) != 0)
148   {
149     sensor_list_remove (sensor);
150     return;
151   }
152
153   /* FIXME: Use rate unit or base unit to scale the value */
154
155   sensor_type = ipmi_sensor_get_sensor_type (sensor);
156   switch (sensor_type)
157   {
158     case IPMI_SENSOR_TYPE_TEMPERATURE:
159       type = "temperature";
160       break;
161
162     case IPMI_SENSOR_TYPE_VOLTAGE:
163       type = "voltage";
164       break;
165
166     case IPMI_SENSOR_TYPE_CURRENT:
167       type = "current";
168       break;
169
170     case IPMI_SENSOR_TYPE_FAN:
171       type = "fanspeed";
172       break;
173
174     default:
175       {
176         const char *sensor_type_str;
177         
178         sensor_type_str = ipmi_sensor_get_sensor_type_string (sensor);
179         INFO ("ipmi plugin: sensor_read_handler: Removing sensor %s, "
180             "because I don't know how to handle its type (%#x, %s). "
181             "If you need this sensor, please file a bug report.",
182             sensor_name_ptr, sensor_type, sensor_type_str);
183         sensor_list_remove (sensor);
184         return;
185       }
186   } /* switch (sensor_type) */
187
188   values[0].gauge = value;
189
190   vl.values = values;
191   vl.values_len = 1;
192   vl.time = time (NULL);
193
194   sstrncpy (vl.host, hostname_g, sizeof (vl.host));
195   sstrncpy (vl.plugin, "ipmi", sizeof (vl.plugin));
196   sstrncpy (vl.type, type, sizeof (vl.type));
197   sstrncpy (vl.type_instance, sensor_name_ptr, sizeof (vl.type_instance));
198
199   plugin_dispatch_values (&vl);
200 } /* void sensor_read_handler */
201
202 static int sensor_list_add (ipmi_sensor_t *sensor)
203 {
204   ipmi_sensor_id_t sensor_id;
205   c_ipmi_sensor_list_t *list_item;
206   c_ipmi_sensor_list_t *list_prev;
207
208   sensor_id = ipmi_sensor_convert_to_id (sensor);
209
210   pthread_mutex_lock (&sensor_list_lock);
211
212   list_prev = NULL;
213   for (list_item = sensor_list;
214       list_item != NULL;
215       list_item = list_item->next)
216   {
217     if (ipmi_cmp_sensor_id (sensor_id, list_item->sensor_id) == 0)
218       break;
219     list_prev = list_item;
220   } /* for (list_item) */
221
222   if (list_item != NULL)
223   {
224     pthread_mutex_unlock (&sensor_list_lock);
225     return (0);
226   }
227
228   list_item = (c_ipmi_sensor_list_t *) calloc (1, sizeof (c_ipmi_sensor_list_t));
229   if (list_item == NULL)
230   {
231     pthread_mutex_unlock (&sensor_list_lock);
232     return (-1);
233   }
234
235   list_item->sensor_id = ipmi_sensor_convert_to_id (sensor);
236
237   if (list_prev != NULL)
238     list_prev->next = list_item;
239   else
240     sensor_list = list_item;
241
242   pthread_mutex_unlock (&sensor_list_lock);
243
244   return (0);
245 } /* int sensor_list_add */
246
247 static int sensor_list_remove (ipmi_sensor_t *sensor)
248 {
249   ipmi_sensor_id_t sensor_id;
250   c_ipmi_sensor_list_t *list_item;
251   c_ipmi_sensor_list_t *list_prev;
252
253   sensor_id = ipmi_sensor_convert_to_id (sensor);
254
255   pthread_mutex_lock (&sensor_list_lock);
256
257   list_prev = NULL;
258   for (list_item = sensor_list;
259       list_item != NULL;
260       list_item = list_item->next)
261   {
262     if (ipmi_cmp_sensor_id (sensor_id, list_item->sensor_id) == 0)
263       break;
264     list_prev = list_item;
265   } /* for (list_item) */
266
267   if (list_item == NULL)
268   {
269     pthread_mutex_unlock (&sensor_list_lock);
270     return (-1);
271   }
272
273   if (list_prev == NULL)
274     sensor_list = list_item->next;
275   else
276     list_prev->next = list_item->next;
277
278   list_prev = NULL;
279   list_item->next = NULL;
280
281   pthread_mutex_unlock (&sensor_list_lock);
282
283   free (list_item);
284   return (0);
285 } /* int sensor_list_remove */
286
287 static int sensor_list_read_all (void)
288 {
289   c_ipmi_sensor_list_t *list_item;
290
291   pthread_mutex_lock (&sensor_list_lock);
292
293   for (list_item = sensor_list;
294       list_item != NULL;
295       list_item = list_item->next)
296   {
297     ipmi_sensor_id_get_reading (list_item->sensor_id,
298         sensor_read_handler, /* user data = */ NULL);
299   } /* for (list_item) */
300
301   pthread_mutex_unlock (&sensor_list_lock);
302
303   return (0);
304 } /* int sensor_list_read_all */
305
306 static int sensor_list_remove_all (void)
307 {
308   c_ipmi_sensor_list_t *list_item;
309
310   pthread_mutex_lock (&sensor_list_lock);
311
312   list_item = sensor_list;
313   sensor_list = NULL;
314
315   pthread_mutex_unlock (&sensor_list_lock);
316
317   while (list_item != NULL)
318   {
319     c_ipmi_sensor_list_t *list_next = list_item->next;
320
321     free (list_item);
322
323     list_item = list_next;
324   } /* while (list_item) */
325
326   return (0);
327 } /* int sensor_list_remove_all */
328
329 /*
330  * Entity handlers
331  */
332 static void entity_sensor_update_handler (enum ipmi_update_e op,
333     ipmi_entity_t *entity,
334     ipmi_sensor_t *sensor,
335     void *user_data)
336 {
337   /* TODO: Ignore sensors we cannot read */
338
339   if ((op == IPMI_ADDED) || (op == IPMI_CHANGED))
340   {
341     /* Will check for duplicate entries.. */
342     sensor_list_add (sensor);
343   }
344   else if (op == IPMI_DELETED)
345   {
346     sensor_list_remove (sensor);
347   }
348 } /* void entity_sensor_update_handler */
349
350 /*
351  * Domain handlers
352  */
353 static void domain_entity_update_handler (enum ipmi_update_e op,
354     ipmi_domain_t *domain,
355     ipmi_entity_t *entity,
356     void *user_data)
357 {
358   int status;
359
360   if (op == IPMI_ADDED)
361   {
362     status = ipmi_entity_add_sensor_update_handler (entity,
363         entity_sensor_update_handler, /* user data = */ NULL);
364     if (status != 0)
365     {
366       c_ipmi_error ("ipmi_entity_add_sensor_update_handler", status);
367     }
368   }
369   else if (op == IPMI_DELETED)
370   {
371     status = ipmi_entity_remove_sensor_update_handler (entity,
372         entity_sensor_update_handler, /* user data = */ NULL);
373     if (status != 0)
374     {
375       c_ipmi_error ("ipmi_entity_remove_sensor_update_handler", status);
376     }
377   }
378 } /* void domain_entity_update_handler */
379
380 static void domain_connection_change_handler (ipmi_domain_t *domain,
381     int err,
382     unsigned int conn_num,
383     unsigned int port_num,
384     int still_connected,
385     void *user_data)
386 {
387   int status;
388
389   printf ("domain_connection_change_handler (domain = %p, err = %i, "
390       "conn_num = %u, port_num = %u, still_connected = %i, "
391       "user_data = %p);\n",
392       (void *) domain, err, conn_num, port_num, still_connected, user_data);
393
394   status = ipmi_domain_add_entity_update_handler (domain,
395       domain_entity_update_handler, /* user data = */ NULL);
396   if (status != 0)
397   {
398     c_ipmi_error ("ipmi_domain_add_entity_update_handler", status);
399   }
400 } /* void domain_connection_change_handler */
401
402 static int thread_init (os_handler_t **ret_os_handler)
403 {
404   os_handler_t *os_handler;
405   ipmi_open_option_t open_option[1];
406   ipmi_con_t *smi_connection = NULL;
407   ipmi_domain_id_t domain_id;
408   int status;
409
410   os_handler = ipmi_posix_thread_setup_os_handler (SIGUSR2);
411   if (os_handler == NULL)
412   {
413     ERROR ("ipmi plugin: ipmi_posix_thread_setup_os_handler failed.");
414     return (-1);
415   }
416
417   ipmi_init (os_handler);
418
419   status = ipmi_smi_setup_con (/* if_num = */ 0,
420       os_handler,
421       /* user data = */ NULL,
422       &smi_connection);
423   if (status != 0)
424   {
425     c_ipmi_error ("ipmi_smi_setup_con", status);
426     return (-1);
427   }
428
429   memset (open_option, 0, sizeof (open_option));
430   open_option[0].option = IPMI_OPEN_OPTION_ALL;
431   open_option[0].ival = 1;
432
433   status = ipmi_open_domain ("mydomain", &smi_connection, /* num_con = */ 1,
434       domain_connection_change_handler, /* user data = */ NULL,
435       /* domain_fully_up_handler = */ NULL, /* user data = */ NULL,
436       open_option, sizeof (open_option) / sizeof (open_option[0]),
437       &domain_id);
438   if (status != 0)
439   {
440     c_ipmi_error ("ipmi_open_domain", status);
441     return (-1);
442   }
443
444   *ret_os_handler = os_handler;
445   return (0);
446 } /* int thread_init */
447
448 static void *thread_main (void *user_data)
449 {
450   int status;
451   os_handler_t *os_handler = NULL;
452
453   status = thread_init (&os_handler);
454   if (status != 0)
455   {
456     fprintf (stderr, "ipmi plugin: thread_init failed.\n");
457     return ((void *) -1);
458   }
459
460   while (c_ipmi_active != 0)
461   {
462     struct timeval tv = { 1, 0 };
463     os_handler->perform_one_op (os_handler, &tv);
464   }
465
466   ipmi_posix_thread_free_os_handler (os_handler);
467
468   return ((void *) 0);
469 } /* void *thread_main */
470
471 static int c_ipmi_config (const char *key, const char *value)
472 {
473   if (ignorelist == NULL)
474     ignorelist = ignorelist_create (/* invert = */ 1);
475   if (ignorelist == NULL)
476     return (1);
477
478   if (strcasecmp ("Sensor", key) == 0)
479   {
480     ignorelist_add (ignorelist, value);
481   }
482   else if (strcasecmp ("IgnoreSelected", key) == 0)
483   {
484     int invert = 1;
485     if ((strcasecmp ("True", value) == 0)
486         || (strcasecmp ("Yes", value) == 0)
487         || (strcasecmp ("On", value) == 0))
488       invert = 0;
489     ignorelist_set_invert (ignorelist, invert);
490   }
491   else
492   {
493     return (-1);
494   }
495
496   return (0);
497 } /* int c_ipmi_config */
498
499 static int c_ipmi_init (void)
500 {
501   int status;
502
503   c_ipmi_active = 1;
504
505   status = pthread_create (&thread_id, /* attr = */ NULL, thread_main,
506       /* user data = */ NULL);
507   if (status != 0)
508   {
509     c_ipmi_active = 0;
510     thread_id = (pthread_t) 0;
511     ERROR ("ipmi plugin: pthread_create failed.");
512     return (-1);
513   }
514
515   return (0);
516 } /* int c_ipmi_init */
517
518 static int c_ipmi_read (void)
519 {
520   if ((c_ipmi_active == 0) || (thread_id == (pthread_t) 0))
521   {
522     INFO ("ipmi plugin: c_ipmi_read: I'm not active, returning false.");
523     return (-1);
524   }
525
526   sensor_list_read_all ();
527   
528   return (0);
529 } /* int c_ipmi_read */
530
531 static int c_ipmi_shutdown (void)
532 {
533   c_ipmi_active = 0;
534
535   if (thread_id != (pthread_t) 0)
536   {
537     pthread_join (thread_id, NULL);
538     thread_id = (pthread_t) 0;
539   }
540
541   sensor_list_remove_all ();
542
543   return (0);
544 } /* int c_ipmi_shutdown */
545
546 void module_register (void)
547 {
548   plugin_register_config ("ipmi", c_ipmi_config,
549       config_keys, config_keys_num);
550   plugin_register_init ("ipmi", c_ipmi_init);
551   plugin_register_read ("ipmi", c_ipmi_read);
552   plugin_register_shutdown ("ipmi", c_ipmi_shutdown);
553 } /* void module_register */
554
555 /* vim: set sw=2 sts=2 ts=8 fdm=marker et : */