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