Merge branch 'collectd-4.3'
[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. If you need this sensor, "
118         "please file a bug report.",
119         sensor_name_ptr, err);
120     sensor_list_remove (sensor);
121     return;
122   }
123
124   if (value_present != IPMI_RAW_VALUE_PRESENT)
125   {
126     INFO ("ipmi plugin: sensor_read_handler: Removing sensor %s, "
127         "because it provides %s.",
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             sensor_name_ptr, sensor_type, sensor_type_str);
165         sensor_list_remove (sensor);
166         return;
167       }
168   } /* switch (sensor_type) */
169
170   values[0].gauge = value;
171
172   vl.values = values;
173   vl.values_len = 1;
174   vl.time = time (NULL);
175
176   sstrncpy (vl.host, hostname_g, sizeof (vl.host));
177   sstrncpy (vl.plugin, "ipmi", sizeof (vl.plugin));
178   sstrncpy (vl.type_instance, sensor_name_ptr, sizeof (vl.type_instance));
179
180   plugin_dispatch_values (type, &vl);
181 } /* void sensor_read_handler */
182
183 static int sensor_list_add (ipmi_sensor_t *sensor)
184 {
185   ipmi_sensor_id_t sensor_id;
186   c_ipmi_sensor_list_t *list_item;
187   c_ipmi_sensor_list_t *list_prev;
188
189   sensor_id = ipmi_sensor_convert_to_id (sensor);
190
191   pthread_mutex_lock (&sensor_list_lock);
192
193   list_prev = NULL;
194   for (list_item = sensor_list;
195       list_item != NULL;
196       list_item = list_item->next)
197   {
198     if (ipmi_cmp_sensor_id (sensor_id, list_item->sensor_id) == 0)
199       break;
200     list_prev = list_item;
201   } /* for (list_item) */
202
203   if (list_item != NULL)
204   {
205     pthread_mutex_unlock (&sensor_list_lock);
206     return (0);
207   }
208
209   list_item = (c_ipmi_sensor_list_t *) calloc (1, sizeof (c_ipmi_sensor_list_t));
210   if (list_item == NULL)
211   {
212     pthread_mutex_unlock (&sensor_list_lock);
213     return (-1);
214   }
215
216   list_item->sensor_id = ipmi_sensor_convert_to_id (sensor);
217
218   if (list_prev != NULL)
219     list_prev->next = list_item;
220   else
221     sensor_list = list_item;
222
223   pthread_mutex_unlock (&sensor_list_lock);
224
225   return (0);
226 } /* int sensor_list_add */
227
228 static int sensor_list_remove (ipmi_sensor_t *sensor)
229 {
230   ipmi_sensor_id_t sensor_id;
231   c_ipmi_sensor_list_t *list_item;
232   c_ipmi_sensor_list_t *list_prev;
233
234   sensor_id = ipmi_sensor_convert_to_id (sensor);
235
236   pthread_mutex_lock (&sensor_list_lock);
237
238   list_prev = NULL;
239   for (list_item = sensor_list;
240       list_item != NULL;
241       list_item = list_item->next)
242   {
243     if (ipmi_cmp_sensor_id (sensor_id, list_item->sensor_id) == 0)
244       break;
245     list_prev = list_item;
246   } /* for (list_item) */
247
248   if (list_item == NULL)
249   {
250     pthread_mutex_unlock (&sensor_list_lock);
251     return (-1);
252   }
253
254   if (list_prev == NULL)
255     sensor_list = list_item->next;
256   else
257     list_prev->next = list_item->next;
258
259   list_prev = NULL;
260   list_item->next = NULL;
261
262   pthread_mutex_unlock (&sensor_list_lock);
263
264   free (list_item);
265   return (0);
266 } /* int sensor_list_remove */
267
268 static int sensor_list_read_all (void)
269 {
270   c_ipmi_sensor_list_t *list_item;
271
272   pthread_mutex_lock (&sensor_list_lock);
273
274   for (list_item = sensor_list;
275       list_item != NULL;
276       list_item = list_item->next)
277   {
278     ipmi_sensor_id_get_reading (list_item->sensor_id,
279         sensor_read_handler, /* user data = */ NULL);
280   } /* for (list_item) */
281
282   pthread_mutex_unlock (&sensor_list_lock);
283
284   return (0);
285 } /* int sensor_list_read_all */
286
287 static int sensor_list_remove_all (void)
288 {
289   c_ipmi_sensor_list_t *list_item;
290
291   pthread_mutex_lock (&sensor_list_lock);
292
293   list_item = sensor_list;
294   sensor_list = NULL;
295
296   pthread_mutex_unlock (&sensor_list_lock);
297
298   while (list_item != NULL)
299   {
300     c_ipmi_sensor_list_t *list_next = list_item->next;
301
302     free (list_item);
303
304     list_item = list_next;
305   } /* while (list_item) */
306
307   return (0);
308 } /* int sensor_list_remove_all */
309
310 /*
311  * Entity handlers
312  */
313 static void entity_sensor_update_handler (enum ipmi_update_e op,
314     ipmi_entity_t *entity,
315     ipmi_sensor_t *sensor,
316     void *user_data)
317 {
318   /* TODO: Ignore sensors we cannot read */
319
320   if ((op == IPMI_ADDED) || (op == IPMI_CHANGED))
321   {
322     /* Will check for duplicate entries.. */
323     sensor_list_add (sensor);
324   }
325   else if (op == IPMI_DELETED)
326   {
327     sensor_list_remove (sensor);
328   }
329 } /* void entity_sensor_update_handler */
330
331 /*
332  * Domain handlers
333  */
334 static void domain_entity_update_handler (enum ipmi_update_e op,
335     ipmi_domain_t *domain,
336     ipmi_entity_t *entity,
337     void *user_data)
338 {
339   int status;
340
341   if (op == IPMI_ADDED)
342   {
343     status = ipmi_entity_add_sensor_update_handler (entity,
344         entity_sensor_update_handler, /* user data = */ NULL);
345     if (status != 0)
346     {
347       c_ipmi_error ("ipmi_entity_add_sensor_update_handler", status);
348     }
349   }
350   else if (op == IPMI_DELETED)
351   {
352     status = ipmi_entity_remove_sensor_update_handler (entity,
353         entity_sensor_update_handler, /* user data = */ NULL);
354     if (status != 0)
355     {
356       c_ipmi_error ("ipmi_entity_remove_sensor_update_handler", status);
357     }
358   }
359 } /* void domain_entity_update_handler */
360
361 static void domain_connection_change_handler (ipmi_domain_t *domain,
362     int err,
363     unsigned int conn_num,
364     unsigned int port_num,
365     int still_connected,
366     void *user_data)
367 {
368   int status;
369
370   printf ("domain_connection_change_handler (domain = %p, err = %i, "
371       "conn_num = %u, port_num = %u, still_connected = %i, "
372       "user_data = %p);\n",
373       (void *) domain, err, conn_num, port_num, still_connected, user_data);
374
375   status = ipmi_domain_add_entity_update_handler (domain,
376       domain_entity_update_handler, /* user data = */ NULL);
377   if (status != 0)
378   {
379     c_ipmi_error ("ipmi_domain_add_entity_update_handler", status);
380   }
381 } /* void domain_connection_change_handler */
382
383 static int thread_init (os_handler_t **ret_os_handler)
384 {
385   os_handler_t *os_handler;
386   ipmi_open_option_t open_option[1];
387   ipmi_con_t *smi_connection = NULL;
388   ipmi_domain_id_t domain_id;
389   int status;
390
391   os_handler = ipmi_posix_thread_setup_os_handler (SIGUSR2);
392   if (os_handler == NULL)
393   {
394     ERROR ("ipmi plugin: ipmi_posix_thread_setup_os_handler failed.");
395     return (-1);
396   }
397
398   ipmi_init (os_handler);
399
400   status = ipmi_smi_setup_con (/* if_num = */ 0,
401       os_handler,
402       /* user data = */ NULL,
403       &smi_connection);
404   if (status != 0)
405   {
406     c_ipmi_error ("ipmi_smi_setup_con", status);
407     return (-1);
408   }
409
410   memset (open_option, 0, sizeof (open_option));
411   open_option[0].option = IPMI_OPEN_OPTION_ALL;
412   open_option[0].ival = 1;
413
414   status = ipmi_open_domain ("mydomain", &smi_connection, /* num_con = */ 1,
415       domain_connection_change_handler, /* user data = */ NULL,
416       /* domain_fully_up_handler = */ NULL, /* user data = */ NULL,
417       open_option, sizeof (open_option) / sizeof (open_option[0]),
418       &domain_id);
419   if (status != 0)
420   {
421     c_ipmi_error ("ipmi_open_domain", status);
422     return (-1);
423   }
424
425   *ret_os_handler = os_handler;
426   return (0);
427 } /* int thread_init */
428
429 static void *thread_main (void *user_data)
430 {
431   int status;
432   os_handler_t *os_handler = NULL;
433
434   status = thread_init (&os_handler);
435   if (status != 0)
436   {
437     fprintf (stderr, "ipmi plugin: thread_init failed.\n");
438     return ((void *) -1);
439   }
440
441   while (c_ipmi_active != 0)
442   {
443     struct timeval tv = { 1, 0 };
444     os_handler->perform_one_op (os_handler, &tv);
445   }
446
447   ipmi_posix_thread_free_os_handler (os_handler);
448
449   return ((void *) 0);
450 } /* void *thread_main */
451
452 static int c_ipmi_init (void)
453 {
454   int status;
455
456   c_ipmi_active = 1;
457
458   status = pthread_create (&thread_id, /* attr = */ NULL, thread_main,
459       /* user data = */ NULL);
460   if (status != 0)
461   {
462     c_ipmi_active = 0;
463     thread_id = (pthread_t) 0;
464     ERROR ("ipmi plugin: pthread_create failed.");
465     return (-1);
466   }
467
468   return (0);
469 } /* int c_ipmi_init */
470
471 static int c_ipmi_read (void)
472 {
473   if ((c_ipmi_active == 0) || (thread_id == (pthread_t) 0))
474   {
475     INFO ("ipmi plugin: c_ipmi_read: I'm not active, returning false.");
476     return (-1);
477   }
478
479   sensor_list_read_all ();
480   
481   return (0);
482 } /* int c_ipmi_read */
483
484 static int c_ipmi_shutdown (void)
485 {
486   c_ipmi_active = 0;
487
488   if (thread_id != (pthread_t) 0)
489   {
490     pthread_join (thread_id, NULL);
491     thread_id = (pthread_t) 0;
492   }
493
494   sensor_list_remove_all ();
495
496   return (0);
497 } /* int c_ipmi_shutdown */
498
499 void module_register (void)
500 {
501   plugin_register_init ("ipmi", c_ipmi_init);
502   plugin_register_read ("ipmi", c_ipmi_read);
503   plugin_register_shutdown ("ipmi", c_ipmi_shutdown);
504 } /* void module_register */
505
506 /* vim: set sw=2 sts=2 ts=8 fdm=marker et : */