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 "common.h"
21 
22 #ifdef HAVE_OPENIPMI
23 
24 #include "dbcache.h"
25 #include "daemon.h"
26 #include "zbxself.h"
27 #include "log.h"
28 #include "zbxipcservice.h"
29 #include "zbxalgo.h"
30 #include "zbxserver.h"
31 #include "preproc.h"
32 
33 #include "ipmi_manager.h"
34 #include "ipmi_protocol.h"
35 #include "checks_ipmi.h"
36 #include "ipmi.h"
37 
38 #include "../poller/poller.h"
39 #include "zbxavailability.h"
40 
41 #define ZBX_IPMI_MANAGER_DELAY	1
42 
43 extern unsigned char	process_type, program_type;
44 extern int		server_num, process_num;
45 
46 extern int	CONFIG_IPMIPOLLER_FORKS;
47 
48 #define ZBX_IPMI_POLLER_INIT		0
49 #define ZBX_IPMI_POLLER_READY		1
50 #define ZBX_IPMI_POLLER_BUSY		2
51 
52 #define ZBX_IPMI_MANAGER_CLEANUP_DELAY		SEC_PER_HOUR
53 #define ZBX_IPMI_MANAGER_HOST_TTL		SEC_PER_DAY
54 
55 /* IPMI request queued by pollers */
56 typedef struct
57 {
58 	/* internal requestid */
59 	zbx_uint64_t		requestid;
60 
61 	/* target host id */
62 	zbx_uint64_t		hostid;
63 
64 	/* itemid, set for value requests */
65 	zbx_uint64_t		itemid;
66 
67 	/* the current item state (supported/unsupported) */
68 	unsigned char		item_state;
69 
70 	/* the current item flags ( e.g. lld rule) */
71 	unsigned char		item_flags;
72 
73 	/* the request message */
74 	zbx_ipc_message_t	message;
75 
76 	/* the source client for external requests (command request) */
77 	zbx_ipc_client_t	*client;
78 }
79 zbx_ipmi_request_t;
80 
81 /* IPMI poller data */
82 typedef struct
83 {
84 	/* the connected IPMI poller client */
85 	zbx_ipc_client_t	*client;
86 
87 	/* the request queue */
88 	zbx_binary_heap_t	requests;
89 
90 	/* the currently processing request */
91 	zbx_ipmi_request_t	*request;
92 
93 	/* the number of hosts handled by the poller */
94 	int			hosts_num;
95 }
96 zbx_ipmi_poller_t;
97 
98 /* cached host data */
99 typedef struct
100 {
101 	zbx_uint64_t		hostid;
102 	int			disable_until;
103 	int			lastcheck;
104 	zbx_ipmi_poller_t	*poller;
105 }
106 zbx_ipmi_manager_host_t;
107 
108 /* IPMI manager data */
109 typedef struct
110 {
111 	/* IPMI poller vector, created during manager initialization */
112 	zbx_vector_ptr_t	pollers;
113 
114 	/* IPMI pollers indexed by IPC service clients */
115 	zbx_hashset_t		pollers_client;
116 
117 	/* IPMI pollers sorted by number of hosts being monitored */
118 	zbx_binary_heap_t	pollers_load;
119 
120 	/* the next poller index to be assigned to new IPC service clients */
121 	int			next_poller_index;
122 
123 	/* monitored hosts cache */
124 	zbx_hashset_t		hosts;
125 }
126 zbx_ipmi_manager_t;
127 
128 /* pollers_client hashset support */
129 
poller_hash_func(const void * d)130 static zbx_hash_t	poller_hash_func(const void *d)
131 {
132 	const zbx_ipmi_poller_t	*poller = *(const zbx_ipmi_poller_t **)d;
133 
134 	zbx_hash_t hash =  ZBX_DEFAULT_PTR_HASH_FUNC(&poller->client);
135 
136 	return hash;
137 }
138 
poller_compare_func(const void * d1,const void * d2)139 static int	poller_compare_func(const void *d1, const void *d2)
140 {
141 	const zbx_ipmi_poller_t	*p1 = *(const zbx_ipmi_poller_t **)d1;
142 	const zbx_ipmi_poller_t	*p2 = *(const zbx_ipmi_poller_t **)d2;
143 
144 	ZBX_RETURN_IF_NOT_EQUAL(p1->client, p2->client);
145 	return 0;
146 }
147 
148 /* pollers_load binary heap support */
149 
ipmi_poller_compare_load(const void * d1,const void * d2)150 static int	ipmi_poller_compare_load(const void *d1, const void *d2)
151 {
152 	const zbx_binary_heap_elem_t	*e1 = (const zbx_binary_heap_elem_t *)d1;
153 	const zbx_binary_heap_elem_t	*e2 = (const zbx_binary_heap_elem_t *)d2;
154 
155 	const zbx_ipmi_poller_t		*p1 = (const zbx_ipmi_poller_t *)e1->data;
156 	const zbx_ipmi_poller_t		*p2 = (const zbx_ipmi_poller_t *)e2->data;
157 
158 	return p1->hosts_num - p2->hosts_num;
159 }
160 
161 /* pollers requests binary heap support */
162 
ipmi_request_priority(const zbx_ipmi_request_t * request)163 static int	ipmi_request_priority(const zbx_ipmi_request_t *request)
164 {
165 	if (NULL != request->client)
166 		return 0;
167 
168 	switch (request->message.code)
169 	{
170 		case ZBX_IPC_IPMI_VALUE_REQUEST:
171 			return 1;
172 		default:
173 			return INT_MAX;
174 	}
175 }
176 
177 /* There can be two request types in the queue - ZBX_IPC_IPMI_VALUE_REQUEST and ZBX_IPC_IPMI_COMMAND_REQUEST. */
178 /* Prioritize command requests over value requests.                                                           */
ipmi_request_compare(const void * d1,const void * d2)179 static int	ipmi_request_compare(const void *d1, const void *d2)
180 {
181 	const zbx_binary_heap_elem_t	*e1 = (const zbx_binary_heap_elem_t *)d1;
182 	const zbx_binary_heap_elem_t	*e2 = (const zbx_binary_heap_elem_t *)d2;
183 
184 	const zbx_ipmi_request_t	*r1 = (const zbx_ipmi_request_t *)e1->data;
185 	const zbx_ipmi_request_t	*r2 = (const zbx_ipmi_request_t *)e2->data;
186 
187 	ZBX_RETURN_IF_NOT_EQUAL(ipmi_request_priority(r1), ipmi_request_priority(r2));
188 	ZBX_RETURN_IF_NOT_EQUAL(r1->requestid, r2->requestid);
189 
190 	return 0;
191 }
192 
193 /******************************************************************************
194  *                                                                            *
195  * Function: ipmi_request_create                                              *
196  *                                                                            *
197  * Purpose: creates an IPMI request                                           *
198  *                                                                            *
199  * Parameters: hostid - [IN] the target hostid                                *
200  *                                                                            *
201  ******************************************************************************/
ipmi_request_create(zbx_uint64_t hostid)202 static zbx_ipmi_request_t	*ipmi_request_create(zbx_uint64_t hostid)
203 {
204 	static zbx_uint64_t	next_requestid = 1;
205 	zbx_ipmi_request_t	*request;
206 
207 	request = (zbx_ipmi_request_t *)zbx_malloc(NULL, sizeof(zbx_ipmi_request_t));
208 	memset(request, 0, sizeof(zbx_ipmi_request_t));
209 	request->requestid = next_requestid++;
210 	request->hostid = hostid;
211 
212 	return request;
213 }
214 
215 /******************************************************************************
216  *                                                                            *
217  * Function: ipmi_request_free                                                *
218  *                                                                            *
219  * Purpose: frees IPMI request                                                *
220  *                                                                            *
221  ******************************************************************************/
ipmi_request_free(zbx_ipmi_request_t * request)222 static void	ipmi_request_free(zbx_ipmi_request_t *request)
223 {
224 	zbx_ipc_message_clean(&request->message);
225 	zbx_free(request);
226 }
227 
228 /******************************************************************************
229  *                                                                            *
230  * Function: ipmi_poller_pop_request                                          *
231  *                                                                            *
232  * Purpose: pops the next queued request from IPMI poller request queue       *
233  *                                                                            *
234  * Parameters: poller - [IN] the IPMI poller                                  *
235  *                                                                            *
236  * Return value: The next request to process or NULL if the queue is empty.   *
237  *                                                                            *
238  ******************************************************************************/
ipmi_poller_pop_request(zbx_ipmi_poller_t * poller)239 static zbx_ipmi_request_t	*ipmi_poller_pop_request(zbx_ipmi_poller_t *poller)
240 {
241 	zbx_binary_heap_elem_t	*el;
242 	zbx_ipmi_request_t	*request;
243 
244 	if (SUCCEED == zbx_binary_heap_empty(&poller->requests))
245 		return NULL;
246 
247 	el = zbx_binary_heap_find_min(&poller->requests);
248 	request = (zbx_ipmi_request_t *)el->data;
249 	zbx_binary_heap_remove_min(&poller->requests);
250 
251 	return request;
252 }
253 
254 /******************************************************************************
255  *                                                                            *
256  * Function: ipmi_poller_push_request                                         *
257  *                                                                            *
258  * Purpose: pushes the requests into IPMI poller request queue                *
259  *                                                                            *
260  * Parameters: poller  - [IN] the IPMI poller                                 *
261  *             request - [IN] the IPMI request to push                        *
262  *                                                                            *
263  *                                                                            *
264  ******************************************************************************/
ipmi_poller_push_request(zbx_ipmi_poller_t * poller,zbx_ipmi_request_t * request)265 static void	ipmi_poller_push_request(zbx_ipmi_poller_t *poller, zbx_ipmi_request_t *request)
266 {
267 	zbx_binary_heap_elem_t	el = {0, (void *)request};
268 
269 	zbx_binary_heap_insert(&poller->requests, &el);
270 }
271 
272 /******************************************************************************
273  *                                                                            *
274  * Function: ipmi_poller_send_request                                         *
275  *                                                                            *
276  * Purpose: sends request to IPMI poller                                      *
277  *                                                                            *
278  * Parameters: poller  - [IN] the IPMI poller                                 *
279  *             message - [IN] the message to send                             *
280  *                                                                            *
281  ******************************************************************************/
ipmi_poller_send_request(zbx_ipmi_poller_t * poller,zbx_ipmi_request_t * request)282 static void	ipmi_poller_send_request(zbx_ipmi_poller_t *poller, zbx_ipmi_request_t *request)
283 {
284 	if (FAIL == zbx_ipc_client_send(poller->client, request->message.code, request->message.data,
285 			request->message.size))
286 	{
287 		zabbix_log(LOG_LEVEL_CRIT, "cannot send data to IPMI poller");
288 		exit(EXIT_FAILURE);
289 	}
290 
291 	poller->request = request;
292 }
293 
294 /******************************************************************************
295  *                                                                            *
296  * Function: ipmi_poller_schedule_request                                     *
297  *                                                                            *
298  * Purpose: schedules request to IPMI poller                                  *
299  *                                                                            *
300  * Parameters: poller  - [IN] the IPMI poller                                 *
301  *             request - [IN] the request to send                             *
302  *                                                                            *
303  ******************************************************************************/
ipmi_poller_schedule_request(zbx_ipmi_poller_t * poller,zbx_ipmi_request_t * request)304 static void	ipmi_poller_schedule_request(zbx_ipmi_poller_t *poller, zbx_ipmi_request_t *request)
305 {
306 	if (NULL == poller->request && NULL != poller->client)
307 		ipmi_poller_send_request(poller, request);
308 	else
309 		ipmi_poller_push_request(poller, request);
310 }
311 
312 /******************************************************************************
313  *                                                                            *
314  * Function: ipmi_poller_free_request                                         *
315  *                                                                            *
316  * Purpose: frees the current request processed by IPMI poller                *
317  *                                                                            *
318  * Parameters: poller  - [IN] the IPMI poller                                 *
319  *                                                                            *
320  ******************************************************************************/
ipmi_poller_free_request(zbx_ipmi_poller_t * poller)321 static void	ipmi_poller_free_request(zbx_ipmi_poller_t *poller)
322 {
323 	ipmi_request_free(poller->request);
324 	poller->request = NULL;
325 }
326 
327 /******************************************************************************
328  *                                                                            *
329  * Function: ipmi_poller_free                                                 *
330  *                                                                            *
331  * Purpose: frees IPMI poller                                                 *
332  *                                                                            *
333  ******************************************************************************/
ipmi_poller_free(zbx_ipmi_poller_t * poller)334 static void	ipmi_poller_free(zbx_ipmi_poller_t *poller)
335 {
336 	zbx_ipmi_request_t	*request;
337 
338 	zbx_ipc_client_close(poller->client);
339 
340 	while (NULL != (request = ipmi_poller_pop_request(poller)))
341 		ipmi_request_free(request);
342 
343 	zbx_binary_heap_destroy(&poller->requests);
344 
345 	zbx_free(poller);
346 }
347 
348 /******************************************************************************
349  *                                                                            *
350  * Function: ipmi_manager_init                                                *
351  *                                                                            *
352  * Purpose: initializes IPMI manager                                          *
353  *                                                                            *
354  * Parameters: manager - [IN] the manager to initialize                       *
355  *                                                                            *
356  ******************************************************************************/
ipmi_manager_init(zbx_ipmi_manager_t * manager)357 static void	ipmi_manager_init(zbx_ipmi_manager_t *manager)
358 {
359 	int			i;
360 	zbx_ipmi_poller_t	*poller;
361 	zbx_binary_heap_elem_t	elem = {0};
362 
363 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() pollers:%d", __func__, CONFIG_IPMIPOLLER_FORKS);
364 
365 	zbx_vector_ptr_create(&manager->pollers);
366 	zbx_hashset_create(&manager->pollers_client, 0, poller_hash_func, poller_compare_func);
367 	zbx_binary_heap_create(&manager->pollers_load, ipmi_poller_compare_load, 0);
368 
369 	manager->next_poller_index = 0;
370 
371 	for (i = 0; i < CONFIG_IPMIPOLLER_FORKS; i++)
372 	{
373 		poller = (zbx_ipmi_poller_t *)zbx_malloc(NULL, sizeof(zbx_ipmi_poller_t));
374 
375 		poller->client = NULL;
376 		poller->request = NULL;
377 		poller->hosts_num = 0;
378 
379 		zbx_binary_heap_create(&poller->requests, ipmi_request_compare, 0);
380 
381 		zbx_vector_ptr_append(&manager->pollers, poller);
382 
383 		/* add poller to load balancing poller queue */
384 		elem.data = (const void *)poller;
385 		zbx_binary_heap_insert(&manager->pollers_load, &elem);
386 	}
387 
388 	zbx_hashset_create(&manager->hosts, 0, ZBX_DEFAULT_UINT64_HASH_FUNC, ZBX_DEFAULT_UINT64_COMPARE_FUNC);
389 
390 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
391 }
392 
393 /******************************************************************************
394  *                                                                            *
395  * Function: ipmi_manager_destroy                                             *
396  *                                                                            *
397  * Purpose: destroys IPMI manager                                             *
398  *                                                                            *
399  * Parameters: manager - [IN] the manager to destroy                          *
400  *                                                                            *
401  ******************************************************************************/
ipmi_manager_destroy(zbx_ipmi_manager_t * manager)402 static void	ipmi_manager_destroy(zbx_ipmi_manager_t *manager)
403 {
404 	zbx_hashset_destroy(&manager->hosts);
405 	zbx_binary_heap_destroy(&manager->pollers_load);
406 	zbx_hashset_destroy(&manager->pollers_client);
407 	zbx_vector_ptr_clear_ext(&manager->pollers, (zbx_clean_func_t)ipmi_poller_free);
408 	zbx_vector_ptr_destroy(&manager->pollers);
409 }
410 
411 /******************************************************************************
412  *                                                                            *
413  * Function: ipmi_manager_host_cleanup                                        *
414  *                                                                            *
415  * Purpose: performs cleanup of monitored hosts cache                         *
416  *                                                                            *
417  * Parameters: manager - [IN] the manager                                     *
418  *             now     - [IN] the current time                                *
419  *                                                                            *
420  ******************************************************************************/
ipmi_manager_host_cleanup(zbx_ipmi_manager_t * manager,int now)421 static void	ipmi_manager_host_cleanup(zbx_ipmi_manager_t *manager, int now)
422 {
423 	zbx_hashset_iter_t	iter;
424 	zbx_ipmi_manager_host_t	*host;
425 	zbx_ipmi_poller_t	*poller;
426 	int			i;
427 
428 	zabbix_log(LOG_LEVEL_DEBUG, "In %s() pollers:%d", __func__, CONFIG_IPMIPOLLER_FORKS);
429 
430 	zbx_hashset_iter_reset(&manager->hosts, &iter);
431 	while (NULL != (host = (zbx_ipmi_manager_host_t *)zbx_hashset_iter_next(&iter)))
432 	{
433 		if (host->lastcheck + ZBX_IPMI_MANAGER_HOST_TTL <= now)
434 		{
435 			host->poller->hosts_num--;
436 			zbx_hashset_iter_remove(&iter);
437 		}
438 	}
439 
440 	for (i = 0; i < manager->pollers.values_num; i++)
441 	{
442 		poller = (zbx_ipmi_poller_t *)manager->pollers.values[i];
443 
444 		if (NULL != poller->client)
445 			zbx_ipc_client_send(poller->client, ZBX_IPC_IPMI_CLEANUP_REQUEST, NULL, 0);
446 	}
447 
448 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
449 }
450 
451 /******************************************************************************
452  *                                                                            *
453  * Function: ipmi_manager_register_poller                                     *
454  *                                                                            *
455  * Purpose: registers IPMI poller                                             *
456  *                                                                            *
457  * Parameters: manager - [IN] the manager                                     *
458  *             client  - [IN] the connected IPMI poller                       *
459  *                                                                            *
460  ******************************************************************************/
ipmi_manager_register_poller(zbx_ipmi_manager_t * manager,zbx_ipc_client_t * client,zbx_ipc_message_t * message)461 static zbx_ipmi_poller_t	*ipmi_manager_register_poller(zbx_ipmi_manager_t *manager, zbx_ipc_client_t *client,
462 		zbx_ipc_message_t *message)
463 {
464 	zbx_ipmi_poller_t	*poller = NULL;
465 	pid_t			ppid;
466 
467 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__);
468 
469 	memcpy(&ppid, message->data, sizeof(ppid));
470 
471 	if (ppid != getppid())
472 	{
473 		zbx_ipc_client_close(client);
474 		zabbix_log(LOG_LEVEL_DEBUG, "refusing connection from foreign process");
475 	}
476 	else
477 	{
478 		if (manager->next_poller_index == manager->pollers.values_num)
479 		{
480 			THIS_SHOULD_NEVER_HAPPEN;
481 			exit(EXIT_FAILURE);
482 		}
483 
484 		poller = (zbx_ipmi_poller_t *)manager->pollers.values[manager->next_poller_index++];
485 		poller->client = client;
486 
487 		zbx_hashset_insert(&manager->pollers_client, &poller, sizeof(zbx_ipmi_poller_t *));
488 	}
489 
490 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__);
491 
492 	return poller;
493 }
494 
495 /******************************************************************************
496  *                                                                            *
497  * Function: ipmi_manager_get_poller_by_client                                *
498  *                                                                            *
499  * Purpose: returns IPMI poller by connected client                           *
500  *                                                                            *
501  * Parameters: manager - [IN] the manager                                     *
502  *             client  - [IN] the connected IPMI poller                       *
503  *                                                                            *
504  * Return value: The IPMI poller                                              *
505  *                                                                            *
506  ******************************************************************************/
ipmi_manager_get_poller_by_client(zbx_ipmi_manager_t * manager,zbx_ipc_client_t * client)507 static zbx_ipmi_poller_t	*ipmi_manager_get_poller_by_client(zbx_ipmi_manager_t *manager,
508 		zbx_ipc_client_t *client)
509 {
510 	zbx_ipmi_poller_t	**poller, poller_local, *plocal = &poller_local;
511 
512 	plocal->client = client;
513 
514 	poller = (zbx_ipmi_poller_t **)zbx_hashset_search(&manager->pollers_client, &plocal);
515 
516 	if (NULL == poller)
517 	{
518 		THIS_SHOULD_NEVER_HAPPEN;
519 		exit(EXIT_FAILURE);
520 	}
521 
522 	return *poller;
523 }
524 
525 /******************************************************************************
526  *                                                                            *
527  * Function: ipmi_manager_get_host_poller                                     *
528  *                                                                            *
529  * Purpose: returns IPMI poller to be assigned to a new host                  *
530  *                                                                            *
531  * Parameters: manager - [IN] the manager                                     *
532  *                                                                            *
533  * Return value: The IPMI poller                                              *
534  *                                                                            *
535  * Comments: This function will return IPMI poller with least monitored hosts.*
536  *                                                                            *
537  ******************************************************************************/
ipmi_manager_get_host_poller(zbx_ipmi_manager_t * manager)538 static zbx_ipmi_poller_t	*ipmi_manager_get_host_poller(zbx_ipmi_manager_t *manager)
539 {
540 	zbx_ipmi_poller_t	*poller;
541 	zbx_binary_heap_elem_t	el;
542 
543 	el = *zbx_binary_heap_find_min(&manager->pollers_load);
544 	zbx_binary_heap_remove_min(&manager->pollers_load);
545 
546 	poller = (zbx_ipmi_poller_t *)el.data;
547 	poller->hosts_num++;
548 
549 	zbx_binary_heap_insert(&manager->pollers_load, &el);
550 
551 	return poller;
552 }
553 
554 /******************************************************************************
555  *                                                                            *
556  * Function: ipmi_manager_process_poller_queue                                *
557  *                                                                            *
558  * Purpose: processes IPMI poller request queue                               *
559  *                                                                            *
560  * Parameters: manager - [IN] the IPMI manager                                *
561  *             poller  - [IN] the IPMI poller                                 *
562  *             now     - [IN] the current time                                *
563  *                                                                            *
564  * Comments: This function will send the next request in queue to the poller, *
565  *           skipping requests for unreachable hosts for unreachable period.  *
566  *                                                                            *
567  ******************************************************************************/
ipmi_manager_process_poller_queue(zbx_ipmi_manager_t * manager,zbx_ipmi_poller_t * poller,int now)568 static void	ipmi_manager_process_poller_queue(zbx_ipmi_manager_t *manager, zbx_ipmi_poller_t *poller, int now)
569 {
570 	zbx_ipmi_request_t	*request;
571 	zbx_ipmi_manager_host_t	*host;
572 
573 	while (NULL != (request = ipmi_poller_pop_request(poller)))
574 	{
575 		switch (request->message.code)
576 		{
577 			case ZBX_IPC_IPMI_COMMAND_REQUEST:
578 			case ZBX_IPC_IPMI_CLEANUP_REQUEST:
579 				break;
580 			case ZBX_IPC_IPMI_VALUE_REQUEST:
581 				if (NULL != request->client)
582 					break;
583 
584 				if (NULL == (host = (zbx_ipmi_manager_host_t *)zbx_hashset_search(&manager->hosts,
585 						&request->hostid)))
586 				{
587 					THIS_SHOULD_NEVER_HAPPEN;
588 					ipmi_request_free(request);
589 					continue;
590 				}
591 				if (now < host->disable_until)
592 				{
593 					zbx_dc_requeue_unreachable_items(&request->itemid, 1);
594 					ipmi_request_free(request);
595 					continue;
596 				}
597 				break;
598 		}
599 
600 		ipmi_poller_send_request(poller, request);
601 		break;
602 	}
603 }
604 
605 /******************************************************************************
606  *                                                                            *
607  * Function: ipmi_manager_cache_host                                          *
608  *                                                                            *
609  * Purpose: caches host to keep local copy of its availability data           *
610  *                                                                            *
611  * Parameters: manager - [IN] the IPMI manager                                *
612  *             hostid  - [IN] the host identifier                             *
613  *             now     - [IN] the current time                                *
614  *                                                                            *
615  * Return value: The cached host.                                             *
616  *                                                                            *
617  ******************************************************************************/
ipmi_manager_cache_host(zbx_ipmi_manager_t * manager,zbx_uint64_t hostid,int now)618 static zbx_ipmi_manager_host_t	*ipmi_manager_cache_host(zbx_ipmi_manager_t *manager, zbx_uint64_t hostid, int now)
619 {
620 	zbx_ipmi_manager_host_t	*host;
621 
622 	if (NULL == (host = (zbx_ipmi_manager_host_t *)zbx_hashset_search(&manager->hosts, &hostid)))
623 	{
624 		zbx_ipmi_manager_host_t	host_local;
625 
626 		host_local.hostid = hostid;
627 		host = (zbx_ipmi_manager_host_t *)zbx_hashset_insert(&manager->hosts, &host_local, sizeof(host_local));
628 
629 		host->disable_until = 0;
630 		host->poller = ipmi_manager_get_host_poller(manager);
631 	}
632 
633 	host->lastcheck = now;
634 
635 	return host;
636 }
637 
638 /******************************************************************************
639  *                                                                            *
640  * Function: ipmi_manager_update_host                                         *
641  *                                                                            *
642  * Purpose: updates cached host                                               *
643  *                                                                            *
644  * Parameters: manager   - [IN] the IPMI manager                              *
645  *             interface - [IN] the interface                                 *
646  *             hostid    - [IN] the host                                        *
647  *                                                                            *
648  ******************************************************************************/
ipmi_manager_update_host(zbx_ipmi_manager_t * manager,const DC_INTERFACE * interface,zbx_uint64_t hostid)649 static void	ipmi_manager_update_host(zbx_ipmi_manager_t *manager, const DC_INTERFACE *interface,
650 		zbx_uint64_t hostid)
651 {
652 	zbx_ipmi_manager_host_t	*ipmi_host;
653 
654 	if (NULL == (ipmi_host = (zbx_ipmi_manager_host_t *)zbx_hashset_search(&manager->hosts, &hostid)))
655 	{
656 		THIS_SHOULD_NEVER_HAPPEN;
657 		return;
658 	}
659 
660 	ipmi_host->disable_until = interface->disable_until;
661 }
662 
663 /******************************************************************************
664  *                                                                            *
665  * Function: ipmi_manager_activate_interface                                  *
666  *                                                                            *
667  * Purpose: tries to activate item's interface after receiving response       *
668  *                                                                            *
669  * Parameters: manager - [IN] the IPMI manager                                *
670  *             itemid  - [IN] the item identifier                             *
671  *             ts      - [IN] the activation timestamp                        *
672  *                                                                            *
673  ******************************************************************************/
ipmi_manager_activate_interface(zbx_ipmi_manager_t * manager,zbx_uint64_t itemid,zbx_timespec_t * ts)674 static void	ipmi_manager_activate_interface(zbx_ipmi_manager_t *manager, zbx_uint64_t itemid, zbx_timespec_t *ts)
675 {
676 	DC_ITEM		item;
677 	int		errcode;
678 	unsigned char	*data = NULL;
679 	size_t		data_alloc = 0, data_offset = 0;
680 
681 	DCconfig_get_items_by_itemids(&item, &itemid, &errcode, 1);
682 
683 	zbx_activate_item_interface(ts, &item, &data, &data_alloc, &data_offset);
684 	ipmi_manager_update_host(manager, &item.interface, item.host.hostid);
685 
686 	DCconfig_clean_items(&item, &errcode, 1);
687 
688 	if (NULL != data)
689 	{
690 		zbx_availability_flush(data, data_offset);
691 		zbx_free(data);
692 	}
693 }
694 
695 /******************************************************************************
696  *                                                                            *
697  * Function: ipmi_manager_deactivate_interface                                *
698  *                                                                            *
699  * Purpose: tries to deactivate item's interface after receiving              *
700  *          host level error                                                  *
701  *                                                                            *
702  * Parameters: manager - [IN] the IPMI manager                                *
703  *             itemid  - [IN] the item identifier                             *
704  *             ts      - [IN] the deactivation timestamp                      *
705  *             error   - [IN] the error                                       *
706  *                                                                            *
707  ******************************************************************************/
ipmi_manager_deactivate_interface(zbx_ipmi_manager_t * manager,zbx_uint64_t itemid,zbx_timespec_t * ts,const char * error)708 static void	ipmi_manager_deactivate_interface(zbx_ipmi_manager_t *manager, zbx_uint64_t itemid, zbx_timespec_t *ts,
709 		const char *error)
710 {
711 	DC_ITEM		item;
712 	int		errcode;
713 	unsigned char	*data = NULL;
714 	size_t		data_alloc = 0, data_offset = 0;
715 
716 	DCconfig_get_items_by_itemids(&item, &itemid, &errcode, 1);
717 
718 	zbx_deactivate_item_interface(ts, &item, &data, &data_alloc, &data_offset, error);
719 	ipmi_manager_update_host(manager, &item.interface, item.host.hostid);
720 
721 	DCconfig_clean_items(&item, &errcode, 1);
722 
723 	if (NULL != data)
724 	{
725 		zbx_availability_flush(data, data_offset);
726 		zbx_free(data);
727 	}
728 }
729 
730 /******************************************************************************
731  *                                                                            *
732  * Function: ipmi_manager_serialize_request                                   *
733  *                                                                            *
734  * Purpose: serializes IPMI poll and discovery requests                       *
735  *                                                                            *
736  * Parameters: item      - [IN] the item to poll                              *
737  *             command   - [IN] the command to execute                        *
738  *             key       - [IN] a valid item key                              *
739  *             message   - [OUT] the message                                  *
740  *                                                                            *
741  ******************************************************************************/
ipmi_manager_serialize_request(const DC_ITEM * item,zbx_ipc_message_t * message)742 static void	ipmi_manager_serialize_request(const DC_ITEM *item, zbx_ipc_message_t *message)
743 {
744 	zbx_uint32_t	size;
745 
746 	size = zbx_ipmi_serialize_request(&message->data, item->host.hostid, item->itemid, item->interface.addr,
747 			item->interface.port, item->host.ipmi_authtype, item->host.ipmi_privilege,
748 			item->host.ipmi_username, item->host.ipmi_password, item->ipmi_sensor, 0, item->key_orig);
749 
750 	message->code = ZBX_IPC_IPMI_VALUE_REQUEST;
751 
752 	message->size = size;
753 }
754 
755 /******************************************************************************
756  *                                                                            *
757  * Function: ipmi_manager_schedule_request                                    *
758  *                                                                            *
759  * Purpose: schedules request to the host                                     *
760  *                                                                            *
761  * Parameters: manager  - [IN] the IPMI manager                               *
762  *             hostid   - [IN] the target host id                             *
763  *             request  - [IN] the request to schedule                        *
764  *             now      - [IN] the current timestamp                          *
765  *                                                                            *
766  ******************************************************************************/
ipmi_manager_schedule_request(zbx_ipmi_manager_t * manager,zbx_uint64_t hostid,zbx_ipmi_request_t * request,int now)767 static void	ipmi_manager_schedule_request(zbx_ipmi_manager_t *manager, zbx_uint64_t hostid,
768 		zbx_ipmi_request_t *request, int now)
769 {
770 	zbx_ipmi_manager_host_t	*host;
771 
772 	host = ipmi_manager_cache_host(manager, hostid, now);
773 	ipmi_poller_schedule_request(host->poller, request);
774 }
775 
776 /******************************************************************************
777  *                                                                            *
778  * Function: ipmi_manager_schedule_requests                                   *
779  *                                                                            *
780  * Purpose: either sends or queues IPMI poll requests from configuration      *
781  *          cache IPMI poller queue                                           *
782  *                                                                            *
783  * Parameters: manager   - [IN] the IPMI manager                              *
784  *             now       - [IN] current time                                  *
785  *             nextcheck - [OUT] time when the next IPMI check is scheduled   *
786  *                         in configuration cache IPMI poller queue           *
787  *                                                                            *
788  * Return value: The number of requests scheduled.                            *
789  *                                                                            *
790  ******************************************************************************/
ipmi_manager_schedule_requests(zbx_ipmi_manager_t * manager,int now,int * nextcheck)791 static int	ipmi_manager_schedule_requests(zbx_ipmi_manager_t *manager, int now, int *nextcheck)
792 {
793 	int			i, num;
794 	DC_ITEM			items[MAX_POLLER_ITEMS];
795 	zbx_ipmi_request_t	*request;
796 	char			*error = NULL;
797 
798 	num = DCconfig_get_ipmi_poller_items(now, items, MAX_POLLER_ITEMS, nextcheck);
799 
800 	for (i = 0; i < num; i++)
801 	{
802 		zbx_timespec_t	ts;
803 		unsigned char	state = ITEM_STATE_NOTSUPPORTED;
804 		int		errcode = CONFIG_ERROR;
805 
806 		if (FAIL == zbx_ipmi_port_expand_macros(items[i].host.hostid, items[i].interface.port_orig,
807 				&items[i].interface.port, &error))
808 		{
809 
810 			zbx_timespec(&ts);
811 			zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid, items[i].value_type,
812 					items[i].flags, NULL, &ts, state, error);
813 			DCrequeue_items(&items[i].itemid, &ts.sec, &errcode, 1);
814 			zbx_free(error);
815 			continue;
816 		}
817 
818 		request = ipmi_request_create(items[i].host.hostid);
819 		request->itemid = items[i].itemid;
820 		request->item_state = items[i].state;
821 		request->item_flags = items[i].flags;
822 		ipmi_manager_serialize_request(&items[i], &request->message);
823 		ipmi_manager_schedule_request(manager, items[i].host.hostid, request, now);
824 	}
825 
826 	zbx_preprocessor_flush();
827 	DCconfig_clean_items(items, NULL, num);
828 
829 	return num;
830 }
831 
832 /******************************************************************************
833  *                                                                            *
834  * Function: ipmi_manager_process_client_request                              *
835  *                                                                            *
836  * Purpose: forwards IPMI request to the poller managing the specified host   *
837  *                                                                            *
838  * Parameters: manager - [IN] the IPMI manager                                *
839  *             client  - [IN] the client asking to execute IPMI request       *
840  *             message - [IN] the request message                             *
841  *             now     - [IN] the current time                                *
842  *             code    - [IN] the request message code                        *
843  *                                                                            *
844  ******************************************************************************/
ipmi_manager_process_client_request(zbx_ipmi_manager_t * manager,zbx_ipc_client_t * client,zbx_ipc_message_t * message,int now,int code)845 static void	ipmi_manager_process_client_request(zbx_ipmi_manager_t *manager, zbx_ipc_client_t *client,
846 		zbx_ipc_message_t *message, int now, int code)
847 {
848 	zbx_ipmi_request_t	*request;
849 	zbx_uint64_t		hostid;
850 
851 	zbx_ipmi_deserialize_request_objectid(message->data, &hostid);
852 
853 	zbx_ipc_client_addref(client);
854 
855 	request = ipmi_request_create(0);
856 	request->client = client;
857 	zbx_ipc_message_copy(&request->message, message);
858 	request->message.code = code;
859 
860 	ipmi_manager_schedule_request(manager, hostid, request, now);
861 }
862 
863 /******************************************************************************
864  *                                                                            *
865  * Function: ipmi_manager_process_client_result                               *
866  *                                                                            *
867  * Purpose: forwards result of request to the client                          *
868  *                                                                            *
869  * Parameters: manager - [IN] the IPMI manager                                *
870  *             client  - [IN] the IPMI poller client                          *
871  *             message - [IN] the command result message                      *
872  *             now     - [IN] the current time                                *
873  *             code    - [IN] the result message code                         *
874  *                                                                            *
875  ******************************************************************************/
ipmi_manager_process_client_result(zbx_ipmi_manager_t * manager,zbx_ipc_client_t * client,zbx_ipc_message_t * message,int now,int code)876 static void	ipmi_manager_process_client_result(zbx_ipmi_manager_t *manager, zbx_ipc_client_t *client,
877 		zbx_ipc_message_t *message, int now, int code)
878 {
879 	zbx_ipmi_poller_t	*poller;
880 
881 	if (NULL == (poller = ipmi_manager_get_poller_by_client(manager, client)))
882 	{
883 		THIS_SHOULD_NEVER_HAPPEN;
884 		return;
885 	}
886 
887 	if (SUCCEED == zbx_ipc_client_connected(poller->request->client))
888 	{
889 		zbx_ipc_client_send(poller->request->client, code, message->data, message->size);
890 		zbx_ipc_client_release(poller->request->client);
891 	}
892 
893 	ipmi_poller_free_request(poller);
894 	ipmi_manager_process_poller_queue(manager, poller, now);
895 }
896 
897 /******************************************************************************
898  *                                                                            *
899  * Function: ipmi_manager_process_value_result                                *
900  *                                                                            *
901  * Purpose: processes IPMI check result received from IPMI poller             *
902  *                                                                            *
903  * Parameters: manager   - [IN] the IPMI manager                              *
904  *             client    - [IN] the client (IPMI poller)                      *
905  *             message   - [IN] the received ZBX_IPC_IPMI_VALUE_RESULT message*
906  *             now       - [IN] the current time                              *
907  *                                                                            *
908  ******************************************************************************/
ipmi_manager_process_value_result(zbx_ipmi_manager_t * manager,zbx_ipc_client_t * client,zbx_ipc_message_t * message,int now)909 static void	ipmi_manager_process_value_result(zbx_ipmi_manager_t *manager, zbx_ipc_client_t *client,
910 		zbx_ipc_message_t *message, int now)
911 {
912 	char			*value;
913 	zbx_timespec_t		ts;
914 	unsigned char		state;
915 	int			errcode;
916 	AGENT_RESULT		result;
917 	zbx_ipmi_poller_t	*poller;
918 	zbx_uint64_t		itemid;
919 	unsigned char		flags;
920 
921 	if (NULL == (poller = ipmi_manager_get_poller_by_client(manager, client)))
922 	{
923 		THIS_SHOULD_NEVER_HAPPEN;
924 		return;
925 	}
926 
927 	if (NULL != poller->request->client)
928 	{
929 		ipmi_manager_process_client_result(manager, client, message, now, ZBX_IPC_IPMI_VALUE_RESULT);
930 		return;
931 	}
932 
933 	itemid = poller->request->itemid;
934 	flags = poller->request->item_flags;
935 
936 	zbx_ipmi_deserialize_result(message->data, &ts, &errcode, &value);
937 
938 	/* update host availability */
939 	switch (errcode)
940 	{
941 		case SUCCEED:
942 		case NOTSUPPORTED:
943 		case AGENT_ERROR:
944 			ipmi_manager_activate_interface(manager, itemid, &ts);
945 			break;
946 		case NETWORK_ERROR:
947 		case GATEWAY_ERROR:
948 		case TIMEOUT_ERROR:
949 			ipmi_manager_deactivate_interface(manager, itemid, &ts, value);
950 			break;
951 		case CONFIG_ERROR:
952 			/* nothing to do */
953 			break;
954 	}
955 
956 	/* add received data to history cache */
957 	switch (errcode)
958 	{
959 		case SUCCEED:
960 			state = ITEM_STATE_NORMAL;
961 			if (NULL != value)
962 			{
963 				init_result(&result);
964 				SET_TEXT_RESULT(&result, value);
965 				value = NULL;
966 				zbx_preprocess_item_value(itemid, poller->request->hostid, ITEM_VALUE_TYPE_TEXT, flags,
967 						&result, &ts, state, NULL);
968 				free_result(&result);
969 			}
970 			break;
971 
972 		case NOTSUPPORTED:
973 		case AGENT_ERROR:
974 		case CONFIG_ERROR:
975 			state = ITEM_STATE_NOTSUPPORTED;
976 			zbx_preprocess_item_value(itemid, poller->request->hostid, ITEM_VALUE_TYPE_TEXT, flags, NULL,
977 					&ts, state, value);
978 			break;
979 		default:
980 			/* don't change item's state when network related error occurs */
981 			break;
982 	}
983 
984 	zbx_free(value);
985 
986 	/* put back the item in configuration cache IPMI poller queue */
987 	DCrequeue_items(&itemid, &ts.sec, &errcode, 1);
988 
989 	ipmi_poller_free_request(poller);
990 	ipmi_manager_process_poller_queue(manager, poller, now);
991 }
992 
ZBX_THREAD_ENTRY(ipmi_manager_thread,args)993 ZBX_THREAD_ENTRY(ipmi_manager_thread, args)
994 {
995 	zbx_ipc_service_t	ipmi_service;
996 	char			*error = NULL;
997 	zbx_ipc_client_t	*client;
998 	zbx_ipc_message_t	*message;
999 	zbx_ipmi_manager_t	ipmi_manager;
1000 	zbx_ipmi_poller_t	*poller;
1001 	int			ret, nextcheck, timeout, nextcleanup, polled_num = 0, scheduled_num = 0, now;
1002 	double			time_stat, time_idle = 0, time_now, sec;
1003 
1004 #define	STAT_INTERVAL	5	/* if a process is busy and does not sleep then update status not faster than */
1005 				/* once in STAT_INTERVAL seconds */
1006 
1007 	process_type = ((zbx_thread_args_t *)args)->process_type;
1008 	server_num = ((zbx_thread_args_t *)args)->server_num;
1009 	process_num = ((zbx_thread_args_t *)args)->process_num;
1010 
1011 	zbx_setproctitle("%s #%d starting", get_process_type_string(process_type), process_num);
1012 
1013 	zabbix_log(LOG_LEVEL_INFORMATION, "%s #%d started [%s #%d]", get_program_type_string(program_type),
1014 			server_num, get_process_type_string(process_type), process_num);
1015 
1016 	update_selfmon_counter(ZBX_PROCESS_STATE_BUSY);
1017 
1018 	if (FAIL == zbx_ipc_service_start(&ipmi_service, ZBX_IPC_SERVICE_IPMI, &error))
1019 	{
1020 		zabbix_log(LOG_LEVEL_CRIT, "cannot start IPMI service: %s", error);
1021 		zbx_free(error);
1022 		exit(EXIT_FAILURE);
1023 	}
1024 
1025 	ipmi_manager_init(&ipmi_manager);
1026 
1027 	DBconnect(ZBX_DB_CONNECT_NORMAL);
1028 
1029 	nextcleanup = time(NULL) + ZBX_IPMI_MANAGER_CLEANUP_DELAY;
1030 
1031 	time_stat = zbx_time();
1032 
1033 	zbx_setproctitle("%s #%d started", get_process_type_string(process_type), process_num);
1034 
1035 	while (ZBX_IS_RUNNING())
1036 	{
1037 		time_now = zbx_time();
1038 		now = time_now;
1039 
1040 		if (STAT_INTERVAL < time_now - time_stat)
1041 		{
1042 			zbx_setproctitle("%s #%d [scheduled %d, polled %d values, idle " ZBX_FS_DBL " sec during "
1043 					ZBX_FS_DBL " sec]", get_process_type_string(process_type), process_num,
1044 					scheduled_num, polled_num, time_idle, time_now - time_stat);
1045 
1046 			time_stat = time_now;
1047 			time_idle = 0;
1048 			polled_num = 0;
1049 			scheduled_num = 0;
1050 		}
1051 
1052 		/* manager -> client */
1053 		scheduled_num += ipmi_manager_schedule_requests(&ipmi_manager, now, &nextcheck);
1054 
1055 		if (FAIL != nextcheck)
1056 			timeout = (nextcheck > now ? nextcheck - now : 0);
1057 		else
1058 			timeout = ZBX_IPMI_MANAGER_DELAY;
1059 
1060 		if (ZBX_IPMI_MANAGER_DELAY < timeout)
1061 			timeout = ZBX_IPMI_MANAGER_DELAY;
1062 
1063 		update_selfmon_counter(ZBX_PROCESS_STATE_IDLE);
1064 		ret = zbx_ipc_service_recv(&ipmi_service, timeout, &client, &message);
1065 		update_selfmon_counter(ZBX_PROCESS_STATE_BUSY);
1066 		sec = zbx_time();
1067 		zbx_update_env(sec);
1068 
1069 		if (ZBX_IPC_RECV_IMMEDIATE != ret)
1070 			time_idle += sec - time_now;
1071 
1072 		if (NULL != message)
1073 		{
1074 			switch (message->code)
1075 			{
1076 				/* poller -> manager */
1077 				case ZBX_IPC_IPMI_REGISTER:
1078 					if (NULL != (poller = ipmi_manager_register_poller(&ipmi_manager, client,
1079 							message)))
1080 					{
1081 						ipmi_manager_process_poller_queue(&ipmi_manager, poller, now);
1082 					}
1083 					break;
1084 				/* client -> manager */
1085 				case ZBX_IPC_IPMI_VALUE_REQUEST:
1086 					ipmi_manager_process_client_request(&ipmi_manager, client, message, now,
1087 							ZBX_IPC_IPMI_VALUE_REQUEST);
1088 					break;
1089 				/* poller -> manager or poller -> manager -> client if value request sent by client */
1090 				case ZBX_IPC_IPMI_VALUE_RESULT:
1091 					ipmi_manager_process_value_result(&ipmi_manager, client, message, now);
1092 					polled_num++;
1093 					break;
1094 				/* client -> manager */
1095 				case ZBX_IPC_IPMI_SCRIPT_REQUEST:
1096 					ipmi_manager_process_client_request(&ipmi_manager, client, message, now,
1097 							ZBX_IPC_IPMI_COMMAND_REQUEST);
1098 					break;
1099 				/* poller -> manager -> client */
1100 				case ZBX_IPC_IPMI_COMMAND_RESULT:
1101 					ipmi_manager_process_client_result(&ipmi_manager, client, message, now,
1102 							ZBX_IPC_IPMI_SCRIPT_RESULT);
1103 			}
1104 
1105 			zbx_ipc_message_free(message);
1106 		}
1107 
1108 		if (NULL != client)
1109 			zbx_ipc_client_release(client);
1110 
1111 		if (now >= nextcleanup)
1112 		{
1113 			ipmi_manager_host_cleanup(&ipmi_manager, now);
1114 			nextcleanup = now + ZBX_IPMI_MANAGER_CLEANUP_DELAY;
1115 		}
1116 	}
1117 
1118 	zbx_setproctitle("%s #%d [terminated]", get_process_type_string(process_type), process_num);
1119 
1120 	while (1)
1121 		zbx_sleep(SEC_PER_MIN);
1122 
1123 	zbx_ipc_service_close(&ipmi_service);
1124 	ipmi_manager_destroy(&ipmi_manager);
1125 #undef STAT_INTERVAL
1126 }
1127 
1128 #endif
1129