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