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