1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 **/
19
20 #include "checks_ipmi.h"
21
22 #ifdef HAVE_OPENIPMI
23
24 /* Theoretically it should be enough max 16 bytes for sensor ID and terminating '\0' (see SDR record format in IPMI */
25 /* v2 spec). OpenIPMI author Corey Minyard explained at */
26 /* www.mail-archive.com/openipmi-developer@lists.sourceforge.net/msg02013.html: */
27 /* "...Since you can use BCD and the field is 16 bytes max, you can get up to 32 bytes in the ID string. Adding the */
28 /* sensor sharing and that's another three bytes (I believe 142 is the maximum number you can get), so 35 bytes is */
29 /* the maximum, I believe." */
30 #define IPMI_SENSOR_ID_SZ 36
31
32 /* delete inactive hosts after this period */
33 #define INACTIVE_HOST_LIMIT 3 * SEC_PER_HOUR
34
35 #include "log.h"
36
37 #include <OpenIPMI/ipmiif.h>
38 #include <OpenIPMI/ipmi_posix.h>
39 #include <OpenIPMI/ipmi_lan.h>
40 #include <OpenIPMI/ipmi_auth.h>
41
42 #define RETURN_IF_CB_DATA_NULL(x, y) \
43 if (NULL == (x)) \
44 { \
45 zabbix_log(LOG_LEVEL_WARNING, "%s() called with cb_data:NULL", (y)); \
46 return; \
47 }
48
49 typedef union
50 {
51 double threshold;
52 zbx_uint64_t discrete;
53 }
54 zbx_ipmi_sensor_value_t;
55
56 typedef struct
57 {
58 ipmi_sensor_t *sensor;
59 char id[IPMI_SENSOR_ID_SZ];
60 enum ipmi_str_type_e id_type; /* For sensors IPMI specifications mention Unicode, BCD plus, */
61 /* 6-bit ASCII packed, 8-bit ASCII+Latin1. */
62 int id_sz; /* "id" value length in bytes */
63 zbx_ipmi_sensor_value_t value;
64 int reading_type; /* "Event/Reading Type Code", e.g. Threshold, */
65 /* Discrete, 'digital' Discrete. */
66 int type; /* "Sensor Type Code", e.g. Temperature, Voltage, */
67 /* Current, Fan, Physical Security (Chassis Intrusion), etc. */
68 }
69 zbx_ipmi_sensor_t;
70
71 typedef struct
72 {
73 ipmi_control_t *control;
74 char *c_name;
75 int num_values; /* order of structure elements changed to avoid padding */
76 int *val; /* when the structure is an element of array */
77 }
78 zbx_ipmi_control_t;
79
80 typedef struct zbx_ipmi_host
81 {
82 char *ip;
83 int port;
84 int authtype;
85 int privilege;
86 int ret;
87 char *username;
88 char *password;
89 zbx_ipmi_sensor_t *sensors;
90 zbx_ipmi_control_t *controls;
91 int sensor_count;
92 int control_count;
93 ipmi_con_t *con;
94 int domain_up;
95 int done;
96 time_t lastaccess; /* Time of last access attempt. Used to detect and delete inactive */
97 /* (disabled) IPMI hosts from OpenIPMI to stop polling them. */
98 unsigned int domain_nr; /* Domain number. It is converted to text string and used as */
99 /* domain name. */
100 char *err;
101 struct zbx_ipmi_host *next;
102 }
103 zbx_ipmi_host_t;
104
105 static unsigned int domain_nr = 0; /* for making a sequence of domain names "0", "1", "2", ... */
106 static zbx_ipmi_host_t *hosts = NULL; /* head of single-linked list of monitored hosts */
107 static os_handler_t *os_hnd;
108
zbx_sensor_id_to_str(char * str,size_t str_sz,const char * id,enum ipmi_str_type_e id_type,int id_sz)109 static char *zbx_sensor_id_to_str(char *str, size_t str_sz, const char *id, enum ipmi_str_type_e id_type, int id_sz)
110 {
111 /* minimum size of 'str' buffer, str_sz, is 35 bytes to avoid truncation */
112 int i;
113 char *p = str;
114 size_t id_len;
115
116 if (0 == id_sz) /* id is meaningful only if length > 0 (see SDR record format in IPMI v2 spec) */
117 {
118 *str = '\0';
119 return str;
120 }
121
122 if (IPMI_SENSOR_ID_SZ < id_sz)
123 {
124 zbx_strlcpy(str, "ILLEGAL-SENSOR-ID-SIZE", str_sz);
125 THIS_SHOULD_NEVER_HAPPEN;
126 return str;
127 }
128
129 switch (id_type)
130 {
131 case IPMI_ASCII_STR:
132 case IPMI_UNICODE_STR:
133 id_len = str_sz > (size_t)id_sz ? (size_t)id_sz : str_sz - 1;
134 memcpy(str, id, id_len);
135 *(str + id_len) = '\0';
136 break;
137 case IPMI_BINARY_STR:
138 /* "BCD Plus" or "6-bit ASCII packed" encoding - print it as a hex string. */
139
140 *p++ = '0'; /* prefix to distinguish from ASCII/Unicode strings */
141 *p++ = 'x';
142 for (i = 0; i < id_sz; i++, p += 2)
143 {
144 zbx_snprintf(p, str_sz - (size_t)(2 + i + i), "%02x",
145 (unsigned int)(unsigned char)*(id + i));
146 }
147 *p = '\0';
148 break;
149 default:
150 zbx_strlcpy(str, "ILLEGAL-SENSOR-ID-TYPE", str_sz);
151 THIS_SHOULD_NEVER_HAPPEN;
152 }
153 return str;
154 }
155
156 /******************************************************************************
157 * *
158 * Function: zbx_get_ipmi_host *
159 * *
160 * Purpose: Find element in the global list 'hosts' using parameters as *
161 * search criteria *
162 * *
163 * Return value: pointer to list element with host data *
164 * NULL if not found *
165 * *
166 ******************************************************************************/
zbx_get_ipmi_host(const char * ip,const int port,int authtype,int privilege,const char * username,const char * password)167 static zbx_ipmi_host_t *zbx_get_ipmi_host(const char *ip, const int port, int authtype, int privilege,
168 const char *username, const char *password)
169 {
170 const char *__function_name = "zbx_get_ipmi_host";
171 zbx_ipmi_host_t *h;
172
173 zabbix_log(LOG_LEVEL_DEBUG, "In %s() host:'[%s]:%d'", __function_name, ip, port);
174
175 h = hosts;
176 while (NULL != h)
177 {
178 if (0 == strcmp(ip, h->ip) && port == h->port && authtype == h->authtype &&
179 privilege == h->privilege && 0 == strcmp(username, h->username) &&
180 0 == strcmp(password, h->password))
181 {
182 break;
183 }
184
185 h = h->next;
186 }
187
188 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%p", __function_name, h);
189
190 return h;
191 }
192
193 /******************************************************************************
194 * *
195 * Function: zbx_allocate_ipmi_host *
196 * *
197 * Purpose: create a new element in the global list 'hosts' *
198 * *
199 * Return value: pointer to the new list element with host data *
200 * *
201 ******************************************************************************/
zbx_allocate_ipmi_host(const char * ip,int port,int authtype,int privilege,const char * username,const char * password)202 static zbx_ipmi_host_t *zbx_allocate_ipmi_host(const char *ip, int port, int authtype, int privilege,
203 const char *username, const char *password)
204 {
205 const char *__function_name = "zbx_allocate_ipmi_host";
206 zbx_ipmi_host_t *h;
207
208 zabbix_log(LOG_LEVEL_DEBUG, "In %s() host:'[%s]:%d'", __function_name, ip, port);
209
210 h = zbx_malloc(NULL, sizeof(zbx_ipmi_host_t));
211
212 memset(h, 0, sizeof(zbx_ipmi_host_t));
213
214 h->ip = strdup(ip);
215 h->port = port;
216 h->authtype = authtype;
217 h->privilege = privilege;
218 h->username = strdup(username);
219 h->password = strdup(password);
220 h->domain_nr = domain_nr++;
221
222 h->next = hosts;
223 hosts = h;
224
225 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%p", __function_name, h);
226
227 return h;
228 }
229
zbx_get_ipmi_sensor(zbx_ipmi_host_t * h,ipmi_sensor_t * sensor)230 static zbx_ipmi_sensor_t *zbx_get_ipmi_sensor(zbx_ipmi_host_t *h, ipmi_sensor_t *sensor)
231 {
232 const char *__function_name = "zbx_get_ipmi_sensor";
233 int i;
234 zbx_ipmi_sensor_t *s = NULL;
235
236 zabbix_log(LOG_LEVEL_DEBUG, "In %s() phost:%p psensor:%p", __function_name, h, sensor);
237
238 for (i = 0; i < h->sensor_count; i++)
239 {
240 if (h->sensors[i].sensor == sensor)
241 {
242 s = &h->sensors[i];
243 break;
244 }
245 }
246
247 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%p", __function_name, s);
248
249 return s;
250 }
251
zbx_get_ipmi_sensor_by_id(zbx_ipmi_host_t * h,const char * id)252 static zbx_ipmi_sensor_t *zbx_get_ipmi_sensor_by_id(zbx_ipmi_host_t *h, const char *id)
253 {
254 const char *__function_name = "zbx_get_ipmi_sensor_by_id";
255 int i;
256 zbx_ipmi_sensor_t *s = NULL;
257
258 zabbix_log(LOG_LEVEL_DEBUG, "In %s() sensor:'%s@[%s]:%d'", __function_name, id, h->ip, h->port);
259
260 for (i = 0; i < h->sensor_count; i++)
261 {
262 if (0 == strcmp(h->sensors[i].id, id))
263 {
264 /* Some devices present a sensor as both a threshold sensor and a discrete sensor. We work */
265 /* around this by preferring the threshold sensor in such case, as it is most widely used. */
266
267 s = &h->sensors[i];
268
269 if (IPMI_EVENT_READING_TYPE_THRESHOLD == s->reading_type)
270 break;
271 }
272 }
273
274 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%p", __function_name, s);
275
276 return s;
277 }
278
zbx_allocate_ipmi_sensor(zbx_ipmi_host_t * h,ipmi_sensor_t * sensor)279 static zbx_ipmi_sensor_t *zbx_allocate_ipmi_sensor(zbx_ipmi_host_t *h, ipmi_sensor_t *sensor)
280 {
281 const char *__function_name = "zbx_allocate_ipmi_sensor";
282 char id_str[2 * IPMI_SENSOR_ID_SZ + 1];
283 zbx_ipmi_sensor_t *s;
284 char id[IPMI_SENSOR_ID_SZ];
285 enum ipmi_str_type_e id_type;
286 int id_sz;
287 size_t sz;
288
289 id_sz = ipmi_sensor_get_id_length(sensor);
290 memset(id, 0, sizeof(id));
291 ipmi_sensor_get_id(sensor, id, sizeof(id));
292 id_type = ipmi_sensor_get_id_type(sensor);
293
294 zabbix_log(LOG_LEVEL_DEBUG, "In %s() sensor:'%s@[%s]:%d'", __function_name,
295 zbx_sensor_id_to_str(id_str, sizeof(id_str), id, id_type, id_sz), h->ip, h->port);
296
297 h->sensor_count++;
298 sz = (size_t)h->sensor_count * sizeof(zbx_ipmi_sensor_t);
299
300 if (NULL == h->sensors)
301 h->sensors = zbx_malloc(h->sensors, sz);
302 else
303 h->sensors = zbx_realloc(h->sensors, sz);
304
305 s = &h->sensors[h->sensor_count - 1];
306 s->sensor = sensor;
307 memcpy(s->id, id, sizeof(id));
308 s->id_type = id_type;
309 s->id_sz = id_sz;
310 memset(&s->value, 0, sizeof(s->value));
311 s->reading_type = ipmi_sensor_get_event_reading_type(sensor);
312 s->type = ipmi_sensor_get_sensor_type(sensor);
313
314 if (SUCCEED == zabbix_check_log_level(LOG_LEVEL_DEBUG))
315 {
316 char full_name[MAX_STRING_LEN];
317
318 ipmi_sensor_get_name(s->sensor, full_name, sizeof(full_name));
319
320 zabbix_log(LOG_LEVEL_DEBUG, "Added sensor: host:'%s:%d' id_type:%d id_sz:%d id:'%s'"
321 " reading_type:0x%x ('%s') type:0x%x ('%s') full_name:'%s'", h->ip, h->port,
322 s->id_type, s->id_sz, zbx_sensor_id_to_str(id_str, sizeof(id_str), s->id, s->id_type,
323 s->id_sz), s->reading_type, ipmi_sensor_get_event_reading_type_string(s->sensor),
324 s->type, ipmi_sensor_get_sensor_type_string(s->sensor), full_name);
325 }
326
327 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%p", __function_name, s);
328
329 return s;
330 }
331
zbx_delete_ipmi_sensor(zbx_ipmi_host_t * h,ipmi_sensor_t * sensor)332 static void zbx_delete_ipmi_sensor(zbx_ipmi_host_t *h, ipmi_sensor_t *sensor)
333 {
334 const char *__function_name = "zbx_delete_ipmi_sensor";
335 char id_str[2 * IPMI_SENSOR_ID_SZ + 1];
336 int i;
337 size_t sz;
338
339 zabbix_log(LOG_LEVEL_DEBUG, "In %s() phost:%p psensor:%p", __function_name, h, sensor);
340
341 for (i = 0; i < h->sensor_count; i++)
342 {
343 if (h->sensors[i].sensor != sensor)
344 continue;
345
346 sz = sizeof(zbx_ipmi_sensor_t);
347
348 zabbix_log(LOG_LEVEL_DEBUG, "sensor '%s@[%s]:%d' deleted",
349 zbx_sensor_id_to_str(id_str, sizeof(id_str), h->sensors[i].id, h->sensors[i].id_type,
350 h->sensors[i].id_sz), h->ip, h->port);
351
352 h->sensor_count--;
353 if (h->sensor_count != i)
354 memmove(&h->sensors[i], &h->sensors[i + 1], sz * (size_t)(h->sensor_count - i));
355 h->sensors = zbx_realloc(h->sensors, sz * (size_t)h->sensor_count);
356
357 break;
358 }
359
360 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
361 }
362
zbx_get_ipmi_control(zbx_ipmi_host_t * h,ipmi_control_t * control)363 static zbx_ipmi_control_t *zbx_get_ipmi_control(zbx_ipmi_host_t *h, ipmi_control_t *control)
364 {
365 const char *__function_name = "zbx_get_ipmi_control";
366 int i;
367 zbx_ipmi_control_t *c = NULL;
368
369 zabbix_log(LOG_LEVEL_DEBUG, "In %s() phost:%p pcontrol:%p", __function_name, h, control);
370
371 for (i = 0; i < h->control_count; i++)
372 {
373 if (h->controls[i].control == control)
374 {
375 c = &h->controls[i];
376 break;
377 }
378 }
379
380 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%p", __function_name, c);
381
382 return c;
383 }
384
zbx_get_ipmi_control_by_name(zbx_ipmi_host_t * h,const char * c_name)385 static zbx_ipmi_control_t *zbx_get_ipmi_control_by_name(zbx_ipmi_host_t *h, const char *c_name)
386 {
387 const char *__function_name = "zbx_get_ipmi_control_by_name";
388 int i;
389 zbx_ipmi_control_t *c = NULL;
390
391 zabbix_log(LOG_LEVEL_DEBUG, "In %s() %s@[%s]:%d", __function_name, c_name, h->ip, h->port);
392
393 for (i = 0; i < h->control_count; i++)
394 {
395 if (0 == strcmp(h->controls[i].c_name, c_name))
396 {
397 c = &h->controls[i];
398 break;
399 }
400 }
401
402 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%p", __function_name, c);
403
404 return c;
405 }
406
zbx_allocate_ipmi_control(zbx_ipmi_host_t * h,ipmi_control_t * control)407 static zbx_ipmi_control_t *zbx_allocate_ipmi_control(zbx_ipmi_host_t *h, ipmi_control_t *control)
408 {
409 const char *__function_name = "zbx_allocate_ipmi_control";
410 size_t sz;
411 zbx_ipmi_control_t *c;
412 char *c_name = NULL;
413
414 sz = (size_t)ipmi_control_get_id_length(control);
415 c_name = zbx_malloc(c_name, sz + 1);
416 ipmi_control_get_id(control, c_name, sz);
417
418 zabbix_log(LOG_LEVEL_DEBUG, "In %s() control:'%s@[%s]:%d'", __function_name, c_name, h->ip, h->port);
419
420 h->control_count++;
421 sz = (size_t)h->control_count * sizeof(zbx_ipmi_control_t);
422
423 if (NULL == h->controls)
424 h->controls = zbx_malloc(h->controls, sz);
425 else
426 h->controls = zbx_realloc(h->controls, sz);
427
428 c = &h->controls[h->control_count - 1];
429
430 memset(c, 0, sizeof(zbx_ipmi_control_t));
431
432 c->control = control;
433 c->c_name = c_name;
434 c->num_values = ipmi_control_get_num_vals(control);
435 sz = sizeof(int) * (size_t)c->num_values;
436 c->val = zbx_malloc(c->val, sz);
437 memset(c->val, 0, sz);
438
439 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%p", __function_name, c);
440
441 return c;
442 }
443
zbx_delete_ipmi_control(zbx_ipmi_host_t * h,ipmi_control_t * control)444 static void zbx_delete_ipmi_control(zbx_ipmi_host_t *h, ipmi_control_t *control)
445 {
446 const char *__function_name = "zbx_delete_ipmi_control";
447 int i;
448 size_t sz;
449
450 zabbix_log(LOG_LEVEL_DEBUG, "In %s() phost:%p pcontrol:%p", __function_name, h, control);
451
452 for (i = 0; i < h->control_count; i++)
453 {
454 if (h->controls[i].control != control)
455 continue;
456
457 sz = sizeof(zbx_ipmi_control_t);
458
459 zabbix_log(LOG_LEVEL_DEBUG, "control '%s@[%s]:%d' deleted", h->controls[i].c_name, h->ip, h->port);
460
461 zbx_free(h->controls[i].c_name);
462 zbx_free(h->controls[i].val);
463
464 h->control_count--;
465 if (h->control_count != i)
466 memmove(&h->controls[i], &h->controls[i + 1], sz * (size_t)(h->control_count - i));
467 h->controls = zbx_realloc(h->controls, sz * (size_t)h->control_count);
468
469 break;
470 }
471
472 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
473 }
474
475 /* callback function invoked from OpenIPMI */
zbx_got_thresh_reading_cb(ipmi_sensor_t * sensor,int err,enum ipmi_value_present_e value_present,unsigned int raw_value,double val,ipmi_states_t * states,void * cb_data)476 static void zbx_got_thresh_reading_cb(ipmi_sensor_t *sensor, int err, enum ipmi_value_present_e value_present,
477 unsigned int raw_value, double val, ipmi_states_t *states, void *cb_data)
478 {
479 const char *__function_name = "zbx_got_thresh_reading_cb";
480 char id_str[2 * IPMI_SENSOR_ID_SZ + 1];
481 zbx_ipmi_host_t *h = cb_data;
482 zbx_ipmi_sensor_t *s;
483
484 RETURN_IF_CB_DATA_NULL(cb_data, __function_name);
485
486 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
487
488 if (0 != err)
489 {
490 zabbix_log(LOG_LEVEL_DEBUG, "%s() fail: %s", __function_name, zbx_strerror(err));
491
492 h->err = zbx_dsprintf(h->err, "error 0x%x while reading threshold sensor", err);
493 h->ret = NETWORK_ERROR;
494 goto out;
495 }
496
497 if (0 == ipmi_is_sensor_scanning_enabled(states) || 0 != ipmi_is_initial_update_in_progress(states))
498 {
499 h->err = zbx_strdup(h->err, "sensor data is not available");
500 h->ret = NOTSUPPORTED;
501 goto out;
502 }
503
504 s = zbx_get_ipmi_sensor(h, sensor);
505
506 if (NULL == s)
507 {
508 THIS_SHOULD_NEVER_HAPPEN;
509 h->err = zbx_strdup(h->err, "fatal error");
510 h->ret = NOTSUPPORTED;
511 goto out;
512 }
513
514 switch (value_present)
515 {
516 case IPMI_NO_VALUES_PRESENT:
517 case IPMI_RAW_VALUE_PRESENT:
518 h->err = zbx_strdup(h->err, "no value present for threshold sensor");
519 h->ret = NOTSUPPORTED;
520 break;
521 case IPMI_BOTH_VALUES_PRESENT:
522 s->value.threshold = val;
523
524 if (SUCCEED == zabbix_check_log_level(LOG_LEVEL_DEBUG))
525 {
526 const char *percent = "", *base, *mod_use = "", *modifier = "", *rate;
527 const char *e_string, *s_type_string, *s_reading_type_string;
528
529 e_string = ipmi_entity_get_entity_id_string(ipmi_sensor_get_entity(sensor));
530 s_type_string = ipmi_sensor_get_sensor_type_string(sensor);
531 s_reading_type_string = ipmi_sensor_get_event_reading_type_string(sensor);
532 base = ipmi_sensor_get_base_unit_string(sensor);
533
534 if (0 != ipmi_sensor_get_percentage(sensor))
535 percent = "%";
536
537 switch (ipmi_sensor_get_modifier_unit_use(sensor))
538 {
539 case IPMI_MODIFIER_UNIT_NONE:
540 break;
541 case IPMI_MODIFIER_UNIT_BASE_DIV_MOD:
542 mod_use = "/";
543 modifier = ipmi_sensor_get_modifier_unit_string(sensor);
544 break;
545 case IPMI_MODIFIER_UNIT_BASE_MULT_MOD:
546 mod_use = "*";
547 modifier = ipmi_sensor_get_modifier_unit_string(sensor);
548 break;
549 default:
550 THIS_SHOULD_NEVER_HAPPEN;
551 }
552 rate = ipmi_sensor_get_rate_unit_string(sensor);
553
554 zabbix_log(LOG_LEVEL_DEBUG, "Value [%s | %s | %s | %s | " ZBX_FS_DBL "%s %s%s%s%s]",
555 zbx_sensor_id_to_str(id_str, sizeof(id_str), s->id, s->id_type,
556 s->id_sz), e_string, s_type_string, s_reading_type_string, val, percent,
557 base, mod_use, modifier, rate);
558 }
559 break;
560 default:
561 THIS_SHOULD_NEVER_HAPPEN;
562 }
563 out:
564 h->done = 1;
565
566 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(h->ret));
567 }
568
569 /* callback function invoked from OpenIPMI */
zbx_got_discrete_states_cb(ipmi_sensor_t * sensor,int err,ipmi_states_t * states,void * cb_data)570 static void zbx_got_discrete_states_cb(ipmi_sensor_t *sensor, int err, ipmi_states_t *states, void *cb_data)
571 {
572 const char *__function_name = "zbx_got_discrete_states_cb";
573 char id_str[2 * IPMI_SENSOR_ID_SZ + 1];
574 int id, i, val, ret, is_state_set;
575 zbx_ipmi_host_t *h = cb_data;
576 zbx_ipmi_sensor_t *s;
577
578 RETURN_IF_CB_DATA_NULL(cb_data, __function_name);
579
580 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
581
582 if (0 == ipmi_is_sensor_scanning_enabled(states) || 0 != ipmi_is_initial_update_in_progress(states))
583 {
584 h->err = zbx_strdup(h->err, "sensor data is not available");
585 h->ret = NOTSUPPORTED;
586 goto out;
587 }
588
589 s = zbx_get_ipmi_sensor(h, sensor);
590
591 if (NULL == s)
592 {
593 THIS_SHOULD_NEVER_HAPPEN;
594 h->err = zbx_strdup(h->err, "fatal error");
595 h->ret = NOTSUPPORTED;
596 goto out;
597 }
598
599 if (0 != err)
600 {
601 h->err = zbx_dsprintf(h->err, "error 0x%x while reading a discrete sensor %s@[%s]:%d", err,
602 zbx_sensor_id_to_str(id_str, sizeof(id_str), s->id, s->id_type, s->id_sz), h->ip,
603 h->port);
604 h->ret = NOTSUPPORTED;
605 goto out;
606 }
607
608 id = ipmi_entity_get_entity_id(ipmi_sensor_get_entity(sensor));
609
610 /* Discrete values are 16-bit. We're storing them into a 64-bit uint. */
611 #define MAX_DISCRETE_STATES 15
612
613 s->value.discrete = 0;
614 for (i = 0; i < MAX_DISCRETE_STATES; i++)
615 {
616 ret = ipmi_sensor_discrete_event_readable(sensor, i, &val);
617 if (0 != ret || 0 == val)
618 continue;
619
620 is_state_set = ipmi_is_state_set(states, i);
621
622 if (SUCCEED == zabbix_check_log_level(LOG_LEVEL_DEBUG))
623 {
624 zabbix_log(LOG_LEVEL_DEBUG, "State [%s | %s | %s | %s | state %d value is %d]",
625 zbx_sensor_id_to_str(id_str, sizeof(id_str), s->id, s->id_type, s->id_sz),
626 ipmi_get_entity_id_string(id), ipmi_sensor_get_sensor_type_string(sensor),
627 ipmi_sensor_get_event_reading_type_string(sensor), i, is_state_set);
628 }
629
630 if (0 != is_state_set)
631 s->value.discrete |= 1 << i;
632 }
633 #undef MAX_DISCRETE_STATES
634 out:
635 h->done = 1;
636
637 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(h->ret));
638 }
639
640 /******************************************************************************
641 * *
642 * Function: zbx_perform_openipmi_ops *
643 * *
644 * Purpose: Pass control to OpenIPMI library to process events *
645 * *
646 * Return value: SUCCEED - no errors *
647 * FAIL - an error occurred while processing events *
648 * *
649 ******************************************************************************/
zbx_perform_openipmi_ops(zbx_ipmi_host_t * h,const char * func_name)650 static int zbx_perform_openipmi_ops(zbx_ipmi_host_t *h, const char *func_name)
651 {
652 const char *__function_name = "zbx_perform_openipmi_ops";
653 struct timeval tv;
654
655 if (SUCCEED == zabbix_check_log_level(LOG_LEVEL_DEBUG))
656 {
657 zabbix_log(LOG_LEVEL_DEBUG, "In %s() host:'[%s]:%d' phost:%p from %s()", __function_name, h->ip,
658 h->port, h, func_name);
659 }
660
661 tv.tv_sec = 10; /* set timeout for one operation */
662 tv.tv_usec = 0;
663
664 while (0 == h->done)
665 {
666 int res;
667
668 if (0 == (res = os_hnd->perform_one_op(os_hnd, &tv)))
669 continue;
670
671 if (SUCCEED == zabbix_check_log_level(LOG_LEVEL_DEBUG))
672 {
673 zabbix_log(LOG_LEVEL_DEBUG, "End %s() from %s(): error: %s", __function_name, func_name,
674 zbx_strerror(res));
675 }
676
677 return FAIL;
678 }
679
680 if (SUCCEED == zabbix_check_log_level(LOG_LEVEL_DEBUG))
681 zabbix_log(LOG_LEVEL_DEBUG, "End %s() from %s()", __function_name, func_name);
682
683 return SUCCEED;
684 }
685
zbx_read_ipmi_sensor(zbx_ipmi_host_t * h,zbx_ipmi_sensor_t * s)686 static void zbx_read_ipmi_sensor(zbx_ipmi_host_t *h, zbx_ipmi_sensor_t *s)
687 {
688 const char *__function_name = "zbx_read_ipmi_sensor";
689 char id_str[2 * IPMI_SENSOR_ID_SZ + 1];
690 int ret;
691 const char *s_reading_type_string;
692
693 /* copy sensor details at start - it can go away and we won't be able to make an error message */
694 zbx_sensor_id_to_str(id_str, sizeof(id_str), s->id, s->id_type, s->id_sz);
695
696 zabbix_log(LOG_LEVEL_DEBUG, "In %s() sensor:'%s@[%s]:%d'", __function_name, id_str, h->ip, h->port);
697
698 h->ret = SUCCEED;
699 h->done = 0;
700
701 switch (s->reading_type)
702 {
703 case IPMI_EVENT_READING_TYPE_THRESHOLD:
704 if (0 != (ret = ipmi_sensor_get_reading(s->sensor, zbx_got_thresh_reading_cb, h)))
705 {
706 /* do not use pointer to sensor here - the sensor may have disappeared during */
707 /* ipmi_sensor_get_reading(), as domain might be closed due to communication failure */
708 h->err = zbx_dsprintf(h->err, "Cannot read sensor \"%s\"."
709 " ipmi_sensor_get_reading() return error: 0x%x", id_str, ret);
710 h->ret = NOTSUPPORTED;
711 goto out;
712 }
713 break;
714 case IPMI_EVENT_READING_TYPE_DISCRETE_USAGE:
715 case IPMI_EVENT_READING_TYPE_DISCRETE_STATE:
716 case IPMI_EVENT_READING_TYPE_DISCRETE_PREDICTIVE_FAILURE:
717 case IPMI_EVENT_READING_TYPE_DISCRETE_LIMIT_EXCEEDED:
718 case IPMI_EVENT_READING_TYPE_DISCRETE_PERFORMANCE_MET:
719 case IPMI_EVENT_READING_TYPE_DISCRETE_SEVERITY:
720 case IPMI_EVENT_READING_TYPE_DISCRETE_DEVICE_PRESENCE:
721 case IPMI_EVENT_READING_TYPE_DISCRETE_DEVICE_ENABLE:
722 case IPMI_EVENT_READING_TYPE_DISCRETE_AVAILABILITY:
723 case IPMI_EVENT_READING_TYPE_DISCRETE_REDUNDANCY:
724 case IPMI_EVENT_READING_TYPE_DISCRETE_ACPI_POWER:
725 case IPMI_EVENT_READING_TYPE_SENSOR_SPECIFIC:
726 case 0x70: /* reading types 70h-7Fh are for OEM discrete sensors */
727 case 0x71:
728 case 0x72:
729 case 0x73:
730 case 0x74:
731 case 0x75:
732 case 0x76:
733 case 0x77:
734 case 0x78:
735 case 0x79:
736 case 0x7a:
737 case 0x7b:
738 case 0x7c:
739 case 0x7d:
740 case 0x7e:
741 case 0x7f:
742 if (0 != (ret = ipmi_sensor_get_states(s->sensor, zbx_got_discrete_states_cb, h)))
743 {
744 /* do not use pointer to sensor here - the sensor may have disappeared during */
745 /* ipmi_sensor_get_states(), as domain might be closed due to communication failure */
746 h->err = zbx_dsprintf(h->err, "Cannot read sensor \"%s\"."
747 " ipmi_sensor_get_states() return error: 0x%x", id_str, ret);
748 h->ret = NOTSUPPORTED;
749 goto out;
750 }
751 break;
752 default:
753 s_reading_type_string = ipmi_sensor_get_event_reading_type_string(s->sensor);
754
755 h->err = zbx_dsprintf(h->err, "Cannot read sensor \"%s\"."
756 " IPMI reading type \"%s\" is not supported", id_str, s_reading_type_string);
757 h->ret = NOTSUPPORTED;
758 goto out;
759 }
760
761 zbx_perform_openipmi_ops(h, __function_name); /* ignore returned result */
762 out:
763 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(h->ret));
764 }
765
766 /* callback function invoked from OpenIPMI */
zbx_got_control_reading_cb(ipmi_control_t * control,int err,int * val,void * cb_data)767 static void zbx_got_control_reading_cb(ipmi_control_t *control, int err, int *val, void *cb_data)
768 {
769 const char *__function_name = "zbx_got_control_reading_cb";
770 zbx_ipmi_host_t *h = cb_data;
771 int n;
772 zbx_ipmi_control_t *c;
773 const char *e_string;
774 size_t sz;
775
776 RETURN_IF_CB_DATA_NULL(cb_data, __function_name);
777
778 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
779
780 if (0 != err)
781 {
782 zabbix_log(LOG_LEVEL_DEBUG, "%s() fail: %s", __function_name, zbx_strerror(err));
783
784 h->err = zbx_dsprintf(h->err, "error 0x%x while reading control", err);
785 h->ret = NETWORK_ERROR;
786 goto out;
787 }
788
789 c = zbx_get_ipmi_control(h, control);
790
791 if (NULL == c)
792 {
793 THIS_SHOULD_NEVER_HAPPEN;
794 h->err = zbx_strdup(h->err, "fatal error");
795 h->ret = NOTSUPPORTED;
796 goto out;
797 }
798
799 if (c->num_values == 0)
800 {
801 THIS_SHOULD_NEVER_HAPPEN;
802 h->err = zbx_strdup(h->err, "no value present for control");
803 h->ret = NOTSUPPORTED;
804 goto out;
805 }
806
807 e_string = ipmi_entity_get_entity_id_string(ipmi_control_get_entity(control));
808
809 for (n = 0; n < c->num_values; n++)
810 {
811 zabbix_log(LOG_LEVEL_DEBUG, "control values [%s | %s | %d:%d]",
812 c->c_name, e_string, n + 1, val[n]);
813 }
814
815 sz = sizeof(int) * (size_t)c->num_values;
816 memcpy(c->val, val, sz);
817 out:
818 h->done = 1;
819
820 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(h->ret));
821 }
822
823 /* callback function invoked from OpenIPMI */
zbx_got_control_setting_cb(ipmi_control_t * control,int err,void * cb_data)824 static void zbx_got_control_setting_cb(ipmi_control_t *control, int err, void *cb_data)
825 {
826 const char *__function_name = "zbx_got_control_setting_cb";
827 zbx_ipmi_host_t *h = cb_data;
828 zbx_ipmi_control_t *c;
829
830 RETURN_IF_CB_DATA_NULL(cb_data, __function_name);
831
832 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
833
834 if (0 != err)
835 {
836 zabbix_log(LOG_LEVEL_DEBUG, "%s() fail: %s", __function_name, zbx_strerror(err));
837
838 h->err = zbx_dsprintf(h->err, "error 0x%x while set control", err);
839 h->ret = NETWORK_ERROR;
840 h->done = 1;
841 return;
842 }
843
844 c = zbx_get_ipmi_control(h, control);
845
846 if (NULL == c)
847 {
848 THIS_SHOULD_NEVER_HAPPEN;
849 h->err = zbx_strdup(h->err, "fatal error");
850 h->ret = NOTSUPPORTED;
851 h->done = 1;
852 return;
853 }
854
855 zabbix_log(LOG_LEVEL_DEBUG, "set value completed for control %s@[%s]:%d", c->c_name, h->ip, h->port);
856
857 h->done = 1;
858
859 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(h->ret));
860 }
861
zbx_read_ipmi_control(zbx_ipmi_host_t * h,zbx_ipmi_control_t * c)862 static void zbx_read_ipmi_control(zbx_ipmi_host_t *h, zbx_ipmi_control_t *c)
863 {
864 const char *__function_name = "zbx_read_ipmi_control";
865 int ret;
866 char control_name[128]; /* internally defined CONTROL_ID_LEN is 32 in OpenIPMI 2.0.22 */
867
868 zabbix_log(LOG_LEVEL_DEBUG, "In %s() control:'%s@[%s]:%d'", __function_name, c->c_name, h->ip, h->port);
869
870 if (0 == ipmi_control_is_readable(c->control))
871 {
872 h->err = zbx_strdup(h->err, "control is not readable");
873 h->ret = NOTSUPPORTED;
874 goto out;
875 }
876
877 /* copy control name - it can go away and we won't be able to make an error message */
878 zbx_strlcpy(control_name, c->c_name, sizeof(control_name));
879
880 h->ret = SUCCEED;
881 h->done = 0;
882
883 if (0 != (ret = ipmi_control_get_val(c->control, zbx_got_control_reading_cb, h)))
884 {
885 /* do not use pointer to control here - the control may have disappeared during */
886 /* ipmi_control_get_val(), as domain might be closed due to communication failure */
887 h->err = zbx_dsprintf(h->err, "Cannot read control %s. ipmi_control_get_val() return error: 0x%x",
888 control_name, ret);
889 h->ret = NOTSUPPORTED;
890 goto out;
891 }
892
893 zbx_perform_openipmi_ops(h, __function_name); /* ignore returned result */
894 out:
895 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(h->ret));
896 }
897
zbx_set_ipmi_control(zbx_ipmi_host_t * h,zbx_ipmi_control_t * c,int value)898 static void zbx_set_ipmi_control(zbx_ipmi_host_t *h, zbx_ipmi_control_t *c, int value)
899 {
900 const char *__function_name = "zbx_set_ipmi_control";
901 int ret;
902 char control_name[128]; /* internally defined CONTROL_ID_LEN is 32 in OpenIPMI 2.0.22 */
903
904 zabbix_log(LOG_LEVEL_DEBUG, "In %s() control:'%s@[%s]:%d' value:%d",
905 __function_name, c->c_name, h->ip, h->port, value);
906
907 if (c->num_values == 0)
908 {
909 THIS_SHOULD_NEVER_HAPPEN;
910 h->err = zbx_strdup(h->err, "no value present for control");
911 h->ret = NOTSUPPORTED;
912 h->done = 1;
913 goto out;
914 }
915
916 if (0 == ipmi_control_is_settable(c->control))
917 {
918 h->err = zbx_strdup(h->err, "control is not settable");
919 h->ret = NOTSUPPORTED;
920 goto out;
921 }
922
923 /* copy control name - it can go away and we won't be able to make an error message */
924 zbx_strlcpy(control_name, c->c_name, sizeof(control_name));
925
926 c->val[0] = value;
927 h->ret = SUCCEED;
928 h->done = 0;
929
930 if (0 != (ret = ipmi_control_set_val(c->control, c->val, zbx_got_control_setting_cb, h)))
931 {
932 /* do not use pointer to control here - the control may have disappeared during */
933 /* ipmi_control_set_val(), as domain might be closed due to communication failure */
934 h->err = zbx_dsprintf(h->err, "Cannot set control %s. ipmi_control_set_val() return error: 0x%x",
935 control_name, ret);
936 h->ret = NOTSUPPORTED;
937 goto out;
938 }
939
940 zbx_perform_openipmi_ops(h, __function_name); /* ignore returned result */
941 out:
942 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(h->ret));
943 }
944
945 /* callback function invoked from OpenIPMI */
zbx_sensor_change_cb(enum ipmi_update_e op,ipmi_entity_t * ent,ipmi_sensor_t * sensor,void * cb_data)946 static void zbx_sensor_change_cb(enum ipmi_update_e op, ipmi_entity_t *ent, ipmi_sensor_t *sensor, void *cb_data)
947 {
948 const char *__function_name = "zbx_sensor_change_cb";
949 zbx_ipmi_host_t *h = cb_data;
950
951 RETURN_IF_CB_DATA_NULL(cb_data, __function_name);
952
953 zabbix_log(LOG_LEVEL_DEBUG, "In %s() host:'[%s]:%d' phost:%p ent:%p sensor:%p op:%d",
954 __function_name, h->ip, h->port, h, ent, sensor, op);
955
956 /* ignore non-readable sensors (e.g. Event-only) */
957 if (0 != ipmi_sensor_get_is_readable(sensor))
958 {
959 switch (op)
960 {
961 case IPMI_ADDED:
962 if (NULL == zbx_get_ipmi_sensor(h, sensor))
963 zbx_allocate_ipmi_sensor(h, sensor);
964 break;
965 case IPMI_DELETED:
966 zbx_delete_ipmi_sensor(h, sensor);
967 break;
968 case IPMI_CHANGED:
969 break;
970 default:
971 THIS_SHOULD_NEVER_HAPPEN;
972 }
973 }
974
975 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
976 }
977
978 /* callback function invoked from OpenIPMI */
zbx_control_change_cb(enum ipmi_update_e op,ipmi_entity_t * ent,ipmi_control_t * control,void * cb_data)979 static void zbx_control_change_cb(enum ipmi_update_e op, ipmi_entity_t *ent, ipmi_control_t *control, void *cb_data)
980 {
981 const char *__function_name = "zbx_control_change_cb";
982 zbx_ipmi_host_t *h = cb_data;
983
984 RETURN_IF_CB_DATA_NULL(cb_data, __function_name);
985
986 zabbix_log(LOG_LEVEL_DEBUG, "In %s() host:'[%s]:%d' phost:%p ent:%p control:%p op:%d",
987 __function_name, h->ip, h->port, h, ent, control, op);
988
989 switch (op)
990 {
991 case IPMI_ADDED:
992 if (NULL == zbx_get_ipmi_control(h, control))
993 zbx_allocate_ipmi_control(h, control);
994 break;
995 case IPMI_DELETED:
996 zbx_delete_ipmi_control(h, control);
997 break;
998 case IPMI_CHANGED:
999 break;
1000 default:
1001 THIS_SHOULD_NEVER_HAPPEN;
1002 }
1003
1004 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
1005 }
1006
1007 /* callback function invoked from OpenIPMI */
zbx_entity_change_cb(enum ipmi_update_e op,ipmi_domain_t * domain,ipmi_entity_t * entity,void * cb_data)1008 static void zbx_entity_change_cb(enum ipmi_update_e op, ipmi_domain_t *domain, ipmi_entity_t *entity, void *cb_data)
1009 {
1010 const char *__function_name = "zbx_entity_change_cb";
1011 int ret;
1012 zbx_ipmi_host_t *h = cb_data;
1013
1014 RETURN_IF_CB_DATA_NULL(cb_data, __function_name);
1015
1016 if (SUCCEED == zabbix_check_log_level(LOG_LEVEL_DEBUG))
1017 {
1018 char entity_name[IPMI_ENTITY_NAME_LEN];
1019
1020 ipmi_entity_get_name(entity, entity_name, sizeof(entity_name));
1021
1022 zabbix_log(LOG_LEVEL_DEBUG, "In %s() host:'[%s]:%d' phost:%p domain:%p entity:%p:'%s' op:%d",
1023 __function_name, h->ip, h->port, h, domain, entity, entity_name, op);
1024 }
1025
1026 if (op == IPMI_ADDED)
1027 {
1028 if (0 != (ret = ipmi_entity_add_sensor_update_handler(entity, zbx_sensor_change_cb, h)))
1029 zabbix_log(LOG_LEVEL_DEBUG, "ipmi_entity_set_sensor_update_handler() return error: 0x%x", ret);
1030
1031 if (0 != (ret = ipmi_entity_add_control_update_handler(entity, zbx_control_change_cb, h)))
1032 zabbix_log(LOG_LEVEL_DEBUG, "ipmi_entity_add_control_update_handler() return error: 0x%x", ret);
1033 }
1034
1035 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
1036 }
1037
1038 /* callback function invoked from OpenIPMI */
zbx_domain_closed_cb(void * cb_data)1039 static void zbx_domain_closed_cb(void *cb_data)
1040 {
1041 const char *__function_name = "zbx_domain_closed_cb";
1042 zbx_ipmi_host_t *h = cb_data;
1043
1044 RETURN_IF_CB_DATA_NULL(cb_data, __function_name);
1045
1046 zabbix_log(LOG_LEVEL_DEBUG, "In %s() phost:%p host:'[%s]:%d'", __function_name, h, h->ip, h->port);
1047
1048 h->domain_up = 0;
1049 h->done = 1;
1050
1051 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
1052 }
1053
1054 /* callback function invoked from OpenIPMI */
zbx_connection_change_cb(ipmi_domain_t * domain,int err,unsigned int conn_num,unsigned int port_num,int still_connected,void * cb_data)1055 static void zbx_connection_change_cb(ipmi_domain_t *domain, int err, unsigned int conn_num, unsigned int port_num,
1056 int still_connected, void *cb_data)
1057 {
1058 /* this function is called when a connection comes up or goes down */
1059
1060 const char *__function_name = "zbx_connection_change_cb";
1061 int ret;
1062 zbx_ipmi_host_t *h = cb_data;
1063
1064 RETURN_IF_CB_DATA_NULL(cb_data, __function_name);
1065
1066 zabbix_log(LOG_LEVEL_DEBUG, "In %s() host:'[%s]:%d' phost:%p domain:%p err:%d conn_num:%u port_num:%u"
1067 " still_connected:%d cb_data:%p", __function_name, h->ip, h->port, h, domain, err, conn_num,
1068 port_num, still_connected, cb_data);
1069
1070 if (0 != err)
1071 {
1072 zabbix_log(LOG_LEVEL_DEBUG, "%s() fail: %s", __function_name, zbx_strerror(err));
1073
1074 h->err = zbx_dsprintf(h->err, "cannot connect to IPMI host: %s", zbx_strerror(err));
1075 h->ret = NETWORK_ERROR;
1076
1077 if (0 != (ret = ipmi_domain_close(domain, zbx_domain_closed_cb, h)))
1078 zabbix_log(LOG_LEVEL_DEBUG, "cannot close IPMI domain: [0x%x]", ret);
1079
1080 goto out;
1081 }
1082
1083 if (0 != (ret = ipmi_domain_add_entity_update_handler(domain, zbx_entity_change_cb, h)))
1084 zabbix_log(LOG_LEVEL_DEBUG, "ipmi_domain_add_entity_update_handler() return error: [0x%x]", ret);
1085 out:
1086 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(h->ret));
1087 }
1088
1089 /* callback function invoked from OpenIPMI */
zbx_domain_up_cb(ipmi_domain_t * domain,void * cb_data)1090 static void zbx_domain_up_cb(ipmi_domain_t *domain, void *cb_data)
1091 {
1092 const char *__function_name = "zbx_domain_up_cb";
1093 zbx_ipmi_host_t *h = cb_data;
1094
1095 RETURN_IF_CB_DATA_NULL(cb_data, __function_name);
1096
1097 zabbix_log(LOG_LEVEL_DEBUG, "In %s() host:'[%s]:%d' domain:%p cb_data:%p", __function_name, h->ip,
1098 h->port, domain, cb_data);
1099
1100 h->domain_up = 1;
1101 h->done = 1;
1102
1103 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
1104 }
1105
zbx_vlog(os_handler_t * handler,const char * format,enum ipmi_log_type_e log_type,va_list ap)1106 static void zbx_vlog(os_handler_t *handler, const char *format, enum ipmi_log_type_e log_type, va_list ap)
1107 {
1108 char type[8], str[MAX_STRING_LEN];
1109
1110 switch (log_type)
1111 {
1112 case IPMI_LOG_INFO : zbx_strlcpy(type, "INFO: ", sizeof(type)); break;
1113 case IPMI_LOG_WARNING : zbx_strlcpy(type, "WARN: ", sizeof(type)); break;
1114 case IPMI_LOG_SEVERE : zbx_strlcpy(type, "SEVR: ", sizeof(type)); break;
1115 case IPMI_LOG_FATAL : zbx_strlcpy(type, "FATL: ", sizeof(type)); break;
1116 case IPMI_LOG_ERR_INFO : zbx_strlcpy(type, "EINF: ", sizeof(type)); break;
1117 case IPMI_LOG_DEBUG_START :
1118 case IPMI_LOG_DEBUG : zbx_strlcpy(type, "DEBG: ", sizeof(type)); break;
1119 case IPMI_LOG_DEBUG_CONT :
1120 case IPMI_LOG_DEBUG_END : *type = '\0'; break;
1121 default : THIS_SHOULD_NEVER_HAPPEN;
1122 }
1123
1124 zbx_vsnprintf(str, sizeof(str), format, ap);
1125
1126 zabbix_log(LOG_LEVEL_DEBUG, "%s%s", type, str);
1127 }
1128
zbx_init_ipmi_handler(void)1129 int zbx_init_ipmi_handler(void)
1130 {
1131 const char *__function_name = "zbx_init_ipmi_handler";
1132
1133 int res, ret = FAIL;
1134
1135 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
1136
1137 if (NULL == (os_hnd = ipmi_posix_setup_os_handler()))
1138 {
1139 zabbix_log(LOG_LEVEL_WARNING, "unable to allocate IPMI handler");
1140 goto out;
1141 }
1142
1143 os_hnd->set_log_handler(os_hnd, zbx_vlog);
1144
1145 if (0 != (res = ipmi_init(os_hnd)))
1146 {
1147 zabbix_log(LOG_LEVEL_WARNING, "unable to initialize the OpenIPMI library."
1148 " ipmi_init() return error: 0x%x", res);
1149 goto out;
1150 }
1151
1152 ret = SUCCEED;
1153 out:
1154 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
1155
1156 return ret;
1157 }
1158
zbx_free_ipmi_host(zbx_ipmi_host_t * h)1159 static void zbx_free_ipmi_host(zbx_ipmi_host_t *h)
1160 {
1161 const char *__function_name = "zbx_free_ipmi_host";
1162 int i;
1163
1164 zabbix_log(LOG_LEVEL_DEBUG, "In %s() host:'[%s]:%d' h:%p", __function_name, h->ip, h->port, h);
1165
1166 for (i = 0; i < h->control_count; i++)
1167 {
1168 zbx_free(h->controls[i].c_name);
1169 zbx_free(h->controls[i].val);
1170 }
1171
1172 zbx_free(h->sensors);
1173 zbx_free(h->controls);
1174 zbx_free(h->ip);
1175 zbx_free(h->username);
1176 zbx_free(h->password);
1177 zbx_free(h->err);
1178
1179 zbx_free(h);
1180
1181 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
1182 }
1183
zbx_free_ipmi_handler(void)1184 void zbx_free_ipmi_handler(void)
1185 {
1186 const char *__function_name = "zbx_free_ipmi_handler";
1187
1188 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
1189
1190 while (NULL != hosts)
1191 {
1192 zbx_ipmi_host_t *h;
1193
1194 h = hosts;
1195 hosts = hosts->next;
1196
1197 zbx_free_ipmi_host(h);
1198 }
1199
1200 os_hnd->free_os_handler(os_hnd);
1201
1202 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
1203 }
1204
zbx_init_ipmi_host(const char * ip,int port,int authtype,int privilege,const char * username,const char * password)1205 static zbx_ipmi_host_t *zbx_init_ipmi_host(const char *ip, int port, int authtype, int privilege, const char *username,
1206 const char *password)
1207 {
1208 const char *__function_name = "zbx_init_ipmi_host";
1209 zbx_ipmi_host_t *h;
1210 ipmi_open_option_t options[4];
1211
1212 /* Although we use only one address and port we pass them in 2-element arrays. The reason is */
1213 /* OpenIPMI v.2.0.16 - 2.0.24 file lib/ipmi_lan.c, function ipmi_lanp_setup_con() ending with loop */
1214 /* in OpenIPMI file lib/ipmi_lan.c, function ipmi_lanp_setup_con() ending with */
1215 /* for (i=0; i<MAX_IP_ADDR; i++) { */
1216 /* if (!ports[i]) */
1217 /* ports[i] = IPMI_LAN_STD_PORT_STR; */
1218 /* } */
1219 /* MAX_IP_ADDR is '#define MAX_IP_ADDR 2' in OpenIPMI and not available to library users. */
1220 /* The loop is running two times regardless of number of addresses supplied by the caller, so we use */
1221 /* 2-element arrays to match OpenIPMI internals. */
1222 char *addrs[2] = {NULL}, *ports[2] = {NULL};
1223
1224 char domain_name[11]; /* max int length */
1225 int ret;
1226
1227 zabbix_log(LOG_LEVEL_DEBUG, "In %s() host:'[%s]:%d'", __function_name, ip, port);
1228
1229 /* Host already in the list? */
1230
1231 if (NULL != (h = zbx_get_ipmi_host(ip, port, authtype, privilege, username, password)))
1232 {
1233 if (1 == h->domain_up)
1234 goto out;
1235 }
1236 else
1237 h = zbx_allocate_ipmi_host(ip, port, authtype, privilege, username, password);
1238
1239 h->ret = SUCCEED;
1240 h->done = 0;
1241
1242 addrs[0] = strdup(h->ip);
1243 ports[0] = zbx_dsprintf(NULL, "%d", h->port);
1244
1245 if (0 != (ret = ipmi_ip_setup_con(addrs, ports, 1,
1246 h->authtype == -1 ? (unsigned int)IPMI_AUTHTYPE_DEFAULT : (unsigned int)h->authtype,
1247 (unsigned int)h->privilege, h->username, strlen(h->username),
1248 h->password, strlen(h->password), os_hnd, NULL, &h->con)))
1249 {
1250 h->err = zbx_dsprintf(h->err, "Cannot connect to IPMI host [%s]:%d."
1251 " ipmi_ip_setup_con() returned error 0x%x",
1252 h->ip, h->port, ret);
1253 h->ret = NETWORK_ERROR;
1254 goto out;
1255 }
1256
1257 if (0 != (ret = h->con->start_con(h->con)))
1258 {
1259 h->err = zbx_dsprintf(h->err, "Cannot connect to IPMI host [%s]:%d."
1260 " start_con() returned error 0x%x",
1261 h->ip, h->port, ret);
1262 h->ret = NETWORK_ERROR;
1263 goto out;
1264 }
1265
1266 options[0].option = IPMI_OPEN_OPTION_ALL;
1267 options[0].ival = 0;
1268 options[1].option = IPMI_OPEN_OPTION_SDRS; /* scan SDRs */
1269 options[1].ival = 1;
1270 options[2].option = IPMI_OPEN_OPTION_IPMB_SCAN; /* scan IPMB bus to find out as much as possible */
1271 options[2].ival = 1;
1272 options[3].option = IPMI_OPEN_OPTION_LOCAL_ONLY; /* scan only local resources */
1273 options[3].ival = 1;
1274
1275 zbx_snprintf(domain_name, sizeof(domain_name), "%u", h->domain_nr);
1276
1277 if (0 != (ret = ipmi_open_domain(domain_name, &h->con, 1, zbx_connection_change_cb, h, zbx_domain_up_cb, h,
1278 options, ARRSIZE(options), NULL)))
1279 {
1280 h->err = zbx_dsprintf(h->err, "Cannot connect to IPMI host [%s]:%d. ipmi_open_domain() failed: %s",
1281 h->ip, h->port, zbx_strerror(ret));
1282 h->ret = NETWORK_ERROR;
1283 goto out;
1284 }
1285
1286 zbx_perform_openipmi_ops(h, __function_name); /* ignore returned result */
1287 out:
1288 zbx_free(addrs[0]);
1289 zbx_free(ports[0]);
1290
1291 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%p domain_nr:%u", __function_name, h, h->domain_nr);
1292
1293 return h;
1294 }
1295
1296 static ipmi_domain_id_t domain_id; /* global variable for passing OpenIPMI domain ID between callbacks */
1297 static int domain_id_found; /* A flag to indicate whether the 'domain_id' carries a valid value. */
1298 /* Values: 0 - not found, 1 - found. The flag is used because we */
1299 /* cannot set 'domain_id' to NULL. */
1300 static int domain_close_ok;
1301
1302 /* callback function invoked from OpenIPMI */
zbx_get_domain_id_by_name_cb(ipmi_domain_t * domain,void * cb_data)1303 static void zbx_get_domain_id_by_name_cb(ipmi_domain_t *domain, void *cb_data)
1304 {
1305 char name[IPMI_DOMAIN_NAME_LEN], *domain_name = cb_data;
1306
1307 RETURN_IF_CB_DATA_NULL(cb_data, "zbx_get_domain_id_by_name_cb");
1308
1309 /* from 'domain' pointer find the domain name */
1310 ipmi_domain_get_name(domain, name, sizeof(name));
1311
1312 /* if the domain name matches the name we are searching for then store the domain ID into global variable */
1313 if (0 == strcmp(domain_name, name))
1314 {
1315 domain_id = ipmi_domain_convert_to_id(domain);
1316 domain_id_found = 1;
1317 }
1318 }
1319
1320 /* callback function invoked from OpenIPMI */
zbx_domain_close_cb(ipmi_domain_t * domain,void * cb_data)1321 static void zbx_domain_close_cb(ipmi_domain_t *domain, void *cb_data)
1322 {
1323 zbx_ipmi_host_t *h = cb_data;
1324 int ret;
1325
1326 RETURN_IF_CB_DATA_NULL(cb_data, "zbx_domain_close_cb");
1327
1328 if (0 != (ret = ipmi_domain_close(domain, zbx_domain_closed_cb, h)))
1329 zabbix_log(LOG_LEVEL_DEBUG, "cannot close IPMI domain: [0x%x]", ret);
1330 else
1331 domain_close_ok = 1;
1332 }
1333
zbx_close_inactive_host(zbx_ipmi_host_t * h)1334 static int zbx_close_inactive_host(zbx_ipmi_host_t *h)
1335 {
1336 const char *__function_name = "zbx_close_inactive_host";
1337
1338 char domain_name[11]; /* max int length */
1339 int ret = FAIL;
1340
1341 zabbix_log(LOG_LEVEL_DEBUG, "In %s(): %s", __function_name, h->ip);
1342
1343 zbx_snprintf(domain_name, sizeof(domain_name), "%u", h->domain_nr);
1344
1345 /* Search the list of domains in OpenIPMI library and find which one to close. It could happen that */
1346 /* the domain is not found (e.g. if Zabbix allocated an IPMI host during network problem and the domain was */
1347 /* closed by OpenIPMI library but the host is still in our 'hosts' list). */
1348
1349 domain_id_found = 0;
1350 ipmi_domain_iterate_domains(zbx_get_domain_id_by_name_cb, domain_name);
1351
1352 h->done = 0;
1353 domain_close_ok = 0;
1354
1355 if (1 == domain_id_found)
1356 {
1357 int res;
1358
1359 if (0 != (res = ipmi_domain_pointer_cb(domain_id, zbx_domain_close_cb, h)))
1360 {
1361 zabbix_log(LOG_LEVEL_DEBUG, "%s(): ipmi_domain_pointer_cb() return error: %s", __function_name,
1362 zbx_strerror(res));
1363 goto out;
1364 }
1365
1366 if (1 != domain_close_ok || SUCCEED != zbx_perform_openipmi_ops(h, __function_name))
1367 goto out;
1368 }
1369
1370 /* The domain was either successfully closed or not found. */
1371 zbx_free_ipmi_host(h);
1372 ret = SUCCEED;
1373 out:
1374 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
1375
1376 return ret;
1377 }
1378
zbx_delete_inactive_ipmi_hosts(time_t last_check)1379 void zbx_delete_inactive_ipmi_hosts(time_t last_check)
1380 {
1381 const char *__function_name = "zbx_delete_inactive_ipmi_hosts";
1382
1383 zbx_ipmi_host_t *h = hosts, *prev = NULL, *next;
1384
1385 zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
1386
1387 while (NULL != h)
1388 {
1389 if (last_check - h->lastaccess > INACTIVE_HOST_LIMIT)
1390 {
1391 next = h->next;
1392
1393 if (SUCCEED == zbx_close_inactive_host(h))
1394 {
1395 if (NULL == prev)
1396 hosts = next;
1397 else
1398 prev->next = next;
1399
1400 h = next;
1401
1402 continue;
1403 }
1404 }
1405
1406 prev = h;
1407 h = h->next;
1408 }
1409
1410 zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
1411 }
1412
get_value_ipmi(DC_ITEM * item,AGENT_RESULT * value)1413 int get_value_ipmi(DC_ITEM *item, AGENT_RESULT *value)
1414 {
1415 const char *__function_name = "get_value_ipmi";
1416 zbx_ipmi_host_t *h;
1417 zbx_ipmi_sensor_t *s;
1418 zbx_ipmi_control_t *c = NULL;
1419
1420 zabbix_log(LOG_LEVEL_DEBUG, "In %s() key:'%s:%s'", __function_name, item->host.host, item->key_orig);
1421
1422 if (NULL == os_hnd)
1423 {
1424 SET_MSG_RESULT(value, strdup("IPMI handler is not initialised"));
1425 return CONFIG_ERROR;
1426 }
1427
1428 h = zbx_init_ipmi_host(item->interface.addr, item->interface.port, item->host.ipmi_authtype,
1429 item->host.ipmi_privilege, item->host.ipmi_username, item->host.ipmi_password);
1430
1431 h->lastaccess = time(NULL);
1432
1433 if (0 == h->domain_up)
1434 {
1435 if (NULL != h->err)
1436 {
1437 SET_MSG_RESULT(value, strdup(h->err));
1438 }
1439 return h->ret;
1440 }
1441
1442 s = zbx_get_ipmi_sensor_by_id(h, item->ipmi_sensor);
1443 if (NULL == s)
1444 c = zbx_get_ipmi_control_by_name(h, item->ipmi_sensor);
1445
1446 if (NULL == s && NULL == c)
1447 {
1448 SET_MSG_RESULT(value, zbx_dsprintf(NULL, "sensor or control %s@[%s]:%d does not exist",
1449 item->ipmi_sensor, h->ip, h->port));
1450 return NOTSUPPORTED;
1451 }
1452
1453 if (NULL != s)
1454 zbx_read_ipmi_sensor(h, s);
1455 else
1456 zbx_read_ipmi_control(h, c);
1457
1458 if (h->ret != SUCCEED)
1459 {
1460 if (NULL != h->err)
1461 {
1462 SET_MSG_RESULT(value, strdup(h->err));
1463 }
1464 return h->ret;
1465 }
1466
1467 if (NULL != s)
1468 {
1469 if (IPMI_EVENT_READING_TYPE_THRESHOLD == s->reading_type)
1470 SET_DBL_RESULT(value, s->value.threshold);
1471 else
1472 SET_UI64_RESULT(value, s->value.discrete);
1473 }
1474 if (NULL != c)
1475 SET_DBL_RESULT(value, c->val[0]);
1476
1477 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(h->ret));
1478
1479 return h->ret;
1480 }
1481
1482 /* function 'zbx_parse_ipmi_command' requires 'c_name' with size 'ITEM_IPMI_SENSOR_LEN_MAX' */
zbx_parse_ipmi_command(const char * command,char * c_name,int * val,char * error,size_t max_error_len)1483 int zbx_parse_ipmi_command(const char *command, char *c_name, int *val, char *error, size_t max_error_len)
1484 {
1485 const char *__function_name = "zbx_parse_ipmi_command";
1486
1487 const char *p;
1488 size_t sz_c_name;
1489 int ret = FAIL;
1490
1491 zabbix_log(LOG_LEVEL_DEBUG, "In %s() command:'%s'", __function_name, command);
1492
1493 while ('\0' != *command && NULL != strchr(" \t", *command))
1494 command++;
1495
1496 for (p = command; '\0' != *p && NULL == strchr(" \t", *p); p++)
1497 ;
1498
1499 if (0 == (sz_c_name = p - command))
1500 {
1501 zbx_strlcpy(error, "IPMI command is empty", max_error_len);
1502 goto fail;
1503 }
1504
1505 if (ITEM_IPMI_SENSOR_LEN_MAX <= sz_c_name)
1506 {
1507 zbx_snprintf(error, max_error_len, "IPMI command is too long [%.*s]", (int)sz_c_name, command);
1508 goto fail;
1509 }
1510
1511 memcpy(c_name, command, sz_c_name);
1512 c_name[sz_c_name] = '\0';
1513
1514 while ('\0' != *p && NULL != strchr(" \t", *p))
1515 p++;
1516
1517 if ('\0' == *p || 0 == strcasecmp(p, "on"))
1518 *val = 1;
1519 else if (0 == strcasecmp(p, "off"))
1520 *val = 0;
1521 else if (SUCCEED != is_uint31(p, val))
1522 {
1523 zbx_snprintf(error, max_error_len, "IPMI command value is not supported [%s]", p);
1524 goto fail;
1525 }
1526
1527 ret = SUCCEED;
1528 fail:
1529 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
1530
1531 return ret;
1532 }
1533
zbx_set_ipmi_control_value(DC_ITEM * item,int value,char * error,size_t max_error_len)1534 int zbx_set_ipmi_control_value(DC_ITEM *item, int value, char *error, size_t max_error_len)
1535 {
1536 zbx_ipmi_host_t *h;
1537 zbx_ipmi_control_t *c;
1538
1539 zabbix_log(LOG_LEVEL_DEBUG, "In zbx_set_ipmi_control_value(control:%s, value:%d)", item->ipmi_sensor, value);
1540
1541 if (NULL == os_hnd)
1542 {
1543 zbx_strlcpy(error, "IPMI handler is not initialised", max_error_len);
1544 zabbix_log(LOG_LEVEL_DEBUG, "%s", error);
1545 return NOTSUPPORTED;
1546 }
1547
1548 h = zbx_init_ipmi_host(item->interface.addr, item->interface.port, item->host.ipmi_authtype,
1549 item->host.ipmi_privilege, item->host.ipmi_username, item->host.ipmi_password);
1550
1551 if (0 == h->domain_up)
1552 {
1553 if (NULL != h->err)
1554 {
1555 zbx_strlcpy(error, h->err, max_error_len);
1556 zabbix_log(LOG_LEVEL_DEBUG, "%s", h->err);
1557 }
1558 return h->ret;
1559 }
1560
1561 c = zbx_get_ipmi_control_by_name(h, item->ipmi_sensor);
1562
1563 if (NULL == c)
1564 {
1565 zbx_snprintf(error, max_error_len, "control %s@[%s]:%d does not exist",
1566 item->ipmi_sensor, h->ip, h->port);
1567 zabbix_log(LOG_LEVEL_DEBUG, "%s", error);
1568 return NOTSUPPORTED;
1569 }
1570
1571 zbx_set_ipmi_control(h, c, value);
1572
1573 if (h->ret != SUCCEED)
1574 {
1575 if (NULL != h->err)
1576 {
1577 zbx_strlcpy(error, h->err, max_error_len);
1578 zabbix_log(LOG_LEVEL_DEBUG, "%s", h->err);
1579 }
1580 }
1581
1582 return h->ret;
1583 }
1584
1585 #endif /* HAVE_OPENIPMI */
1586