/* ** Copyright (C) 2001-2025 Zabbix SIA ** ** This program is free software: you can redistribute it and/or modify it under the terms of ** the GNU Affero General Public License as published by the Free Software Foundation, version 3. ** ** This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; ** without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ** See the GNU Affero General Public License for more details. ** ** You should have received a copy of the GNU Affero General Public License along with this program. ** If not, see <https://www.gnu.org/licenses/>. **/ #include "zbxcommon.h" #ifdef HAVE_OPENIPMI #include "zbxnix.h" #include "zbxself.h" #include "zbxlog.h" #include "zbxipcservice.h" #include "zbxalgo.h" #include "ipmi_protocol.h" #include "ipmi.h" #include "zbxavailability.h" #include "zbx_availability_constants.h" #include "zbxtime.h" #include "zbx_item_constants.h" #include "zbxpreproc.h" #include "zbxipmi.h" #include "zbxpoller.h" #include "zbxcacheconfig.h" #include "zbxdbhigh.h" #include "zbxthreads.h" #include "zbxtimekeeper.h" /* IPMI request queued by pollers */ typedef struct { /* internal requestid */ zbx_uint64_t requestid; /* target host id */ zbx_uint64_t hostid; /* itemid, set for value requests */ zbx_uint64_t itemid; /* current item state (supported/unsupported) */ unsigned char item_state; /* current item flags (e.g. lld rule) */ unsigned char item_flags; /* request message */ zbx_ipc_message_t message; /* source client for external requests (command request) */ zbx_ipc_client_t *client; } zbx_ipmi_request_t; /* IPMI poller data */ typedef struct { /* connected IPMI poller client */ zbx_ipc_client_t *client; /* request queue */ zbx_binary_heap_t requests; /* currently processing request */ zbx_ipmi_request_t *request; /* number of hosts handled by poller */ int hosts_num; } zbx_ipmi_poller_t; ZBX_PTR_VECTOR_DECL(ipmi_poller_ptr, zbx_ipmi_poller_t *) ZBX_PTR_VECTOR_IMPL(ipmi_poller_ptr, zbx_ipmi_poller_t *) /* cached host data */ typedef struct { zbx_uint64_t hostid; int disable_until; int lastcheck; zbx_ipmi_poller_t *poller; } zbx_ipmi_manager_host_t; /* IPMI manager data */ typedef struct { /* IPMI poller vector, created during manager initialization */ zbx_vector_ipmi_poller_ptr_t pollers; /* IPMI pollers indexed by IPC service clients */ zbx_hashset_t pollers_client; /* IPMI pollers sorted by number of hosts being monitored */ zbx_binary_heap_t pollers_load; /* next poller index to be assigned to new IPC service clients */ int next_poller_index; /* monitored hosts cache */ zbx_hashset_t hosts; } zbx_ipmi_manager_t; /* pollers_client hashset support */ static zbx_hash_t poller_hash_func(const void *d) { const zbx_ipmi_poller_t *poller = *(const zbx_ipmi_poller_t **)d; zbx_hash_t hash = ZBX_DEFAULT_PTR_HASH_FUNC(&poller->client); return hash; } static int poller_compare_func(const void *d1, const void *d2) { const zbx_ipmi_poller_t *p1 = *(const zbx_ipmi_poller_t **)d1; const zbx_ipmi_poller_t *p2 = *(const zbx_ipmi_poller_t **)d2; ZBX_RETURN_IF_NOT_EQUAL(p1->client, p2->client); return 0; } /* pollers_load binary heap support */ static int ipmi_poller_compare_load(const void *d1, const void *d2) { const zbx_binary_heap_elem_t *e1 = (const zbx_binary_heap_elem_t *)d1; const zbx_binary_heap_elem_t *e2 = (const zbx_binary_heap_elem_t *)d2; const zbx_ipmi_poller_t *p1 = (const zbx_ipmi_poller_t *)e1->data; const zbx_ipmi_poller_t *p2 = (const zbx_ipmi_poller_t *)e2->data; return p1->hosts_num - p2->hosts_num; } /* pollers requests binary heap support */ static int ipmi_request_priority(const zbx_ipmi_request_t *request) { if (NULL != request->client) return 0; switch (request->message.code) { case ZBX_IPC_IPMI_VALUE_REQUEST: return 1; default: return INT_MAX; } } /* There can be two request types in the queue: ZBX_IPC_IPMI_VALUE_REQUEST and ZBX_IPC_IPMI_COMMAND_REQUEST. */ /* Prioritize command requests over value requests. */ static int ipmi_request_compare(const void *d1, const void *d2) { const zbx_binary_heap_elem_t *e1 = (const zbx_binary_heap_elem_t *)d1; const zbx_binary_heap_elem_t *e2 = (const zbx_binary_heap_elem_t *)d2; const zbx_ipmi_request_t *r1 = (const zbx_ipmi_request_t *)e1->data; const zbx_ipmi_request_t *r2 = (const zbx_ipmi_request_t *)e2->data; ZBX_RETURN_IF_NOT_EQUAL(ipmi_request_priority(r1), ipmi_request_priority(r2)); ZBX_RETURN_IF_NOT_EQUAL(r1->requestid, r2->requestid); return 0; } /****************************************************************************** * * * Purpose: creates IPMI request * * * * Parameters: hostid - [IN] target host id * * * ******************************************************************************/ static zbx_ipmi_request_t *ipmi_request_create(zbx_uint64_t hostid) { static zbx_uint64_t next_requestid = 1; zbx_ipmi_request_t *request = (zbx_ipmi_request_t *)zbx_malloc(NULL, sizeof(zbx_ipmi_request_t)); memset(request, 0, sizeof(zbx_ipmi_request_t)); request->requestid = next_requestid++; request->hostid = hostid; return request; } /****************************************************************************** * * * Purpose: frees IPMI request * * * ******************************************************************************/ static void ipmi_request_free(zbx_ipmi_request_t *request) { zbx_ipc_message_clean(&request->message); zbx_free(request); } /****************************************************************************** * * * Purpose: pops next queued request from IPMI poller request queue * * * * Return value: next request to process or NULL if queue is empty * * * ******************************************************************************/ static zbx_ipmi_request_t *ipmi_poller_pop_request(zbx_ipmi_poller_t *poller) { zbx_binary_heap_elem_t *el; zbx_ipmi_request_t *request; if (SUCCEED == zbx_binary_heap_empty(&poller->requests)) return NULL; el = zbx_binary_heap_find_min(&poller->requests); request = (zbx_ipmi_request_t *)el->data; zbx_binary_heap_remove_min(&poller->requests); return request; } /****************************************************************************** * * * Purpose: pushes requests into IPMI poller request queue * * * ******************************************************************************/ static void ipmi_poller_push_request(zbx_ipmi_poller_t *poller, zbx_ipmi_request_t *request) { zbx_binary_heap_elem_t el = {0, (void *)request}; zbx_binary_heap_insert(&poller->requests, &el); } /****************************************************************************** * * * Purpose: sends request to IPMI poller * * * ******************************************************************************/ static void ipmi_poller_send_request(zbx_ipmi_poller_t *poller, zbx_ipmi_request_t *request) { if (FAIL == zbx_ipc_client_send(poller->client, request->message.code, request->message.data, request->message.size)) { zabbix_log(LOG_LEVEL_CRIT, "cannot send data to IPMI poller"); exit(EXIT_FAILURE); } poller->request = request; } /****************************************************************************** * * * Purpose: schedules request to IPMI poller * * * ******************************************************************************/ static void ipmi_poller_schedule_request(zbx_ipmi_poller_t *poller, zbx_ipmi_request_t *request) { if (NULL == poller->request && NULL != poller->client) ipmi_poller_send_request(poller, request); else ipmi_poller_push_request(poller, request); } /****************************************************************************** * * * Purpose: frees current request processed by IPMI poller * * * ******************************************************************************/ static void ipmi_poller_free_request(zbx_ipmi_poller_t *poller) { ipmi_request_free(poller->request); poller->request = NULL; } /****************************************************************************** * * * Purpose: initializes IPMI manager * * * ******************************************************************************/ static void ipmi_manager_init(zbx_ipmi_manager_t *manager, zbx_get_config_forks_f get_config_forks) { zbx_binary_heap_elem_t elem = {0}; zabbix_log(LOG_LEVEL_DEBUG, "In %s() pollers:%d", __func__, get_config_forks(ZBX_PROCESS_TYPE_IPMIPOLLER)); zbx_vector_ipmi_poller_ptr_create(&manager->pollers); zbx_hashset_create(&manager->pollers_client, 0, poller_hash_func, poller_compare_func); zbx_binary_heap_create(&manager->pollers_load, ipmi_poller_compare_load, 0); manager->next_poller_index = 0; for (int i = 0; i < get_config_forks(ZBX_PROCESS_TYPE_IPMIPOLLER); i++) { zbx_ipmi_poller_t *poller = (zbx_ipmi_poller_t *)zbx_malloc(NULL, sizeof(zbx_ipmi_poller_t)); poller->client = NULL; poller->request = NULL; poller->hosts_num = 0; zbx_binary_heap_create(&poller->requests, ipmi_request_compare, 0); zbx_vector_ipmi_poller_ptr_append(&manager->pollers, poller); /* add poller to load balancing poller queue */ elem.data = (void *)poller; zbx_binary_heap_insert(&manager->pollers_load, &elem); } zbx_hashset_create(&manager->hosts, 0, ZBX_DEFAULT_UINT64_HASH_FUNC, ZBX_DEFAULT_UINT64_COMPARE_FUNC); zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); } /****************************************************************************** * * * Purpose: performs cleanup of monitored hosts cache * * * * Parameters: manager - [IN] * * now - [IN] * * get_config_forks - [IN] * * * ******************************************************************************/ static void ipmi_manager_host_cleanup(zbx_ipmi_manager_t *manager, int now, zbx_get_config_forks_f get_config_forks) { #define ZBX_IPMI_MANAGER_HOST_TTL SEC_PER_DAY zbx_hashset_iter_t iter; zbx_ipmi_manager_host_t *host; zabbix_log(LOG_LEVEL_DEBUG, "In %s() pollers:%d", __func__, get_config_forks(ZBX_PROCESS_TYPE_IPMIPOLLER)); zbx_hashset_iter_reset(&manager->hosts, &iter); while (NULL != (host = (zbx_ipmi_manager_host_t *)zbx_hashset_iter_next(&iter))) { if (host->lastcheck + ZBX_IPMI_MANAGER_HOST_TTL <= now) { host->poller->hosts_num--; zbx_hashset_iter_remove(&iter); } } for (int i = 0; i < manager->pollers.values_num; i++) { zbx_ipmi_poller_t *poller = manager->pollers.values[i]; if (NULL != poller->client) zbx_ipc_client_send(poller->client, ZBX_IPC_IPMI_CLEANUP_REQUEST, NULL, 0); } zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); #undef ZBX_IPMI_MANAGER_HOST_TTL } /****************************************************************************** * * * Purpose: registers IPMI poller * * * * Parameters: manager - [IN] * * client - [IN] connected IPMI poller * * message - [IN] * * * ******************************************************************************/ static zbx_ipmi_poller_t *ipmi_manager_register_poller(zbx_ipmi_manager_t *manager, zbx_ipc_client_t *client, zbx_ipc_message_t *message) { zbx_ipmi_poller_t *poller = NULL; pid_t ppid; zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __func__); memcpy(&ppid, message->data, sizeof(ppid)); if (ppid != getppid()) { zbx_ipc_client_close(client); zabbix_log(LOG_LEVEL_DEBUG, "refusing connection from foreign process"); } else { if (manager->next_poller_index == manager->pollers.values_num) { THIS_SHOULD_NEVER_HAPPEN; exit(EXIT_FAILURE); } poller = manager->pollers.values[manager->next_poller_index++]; poller->client = client; zbx_hashset_insert(&manager->pollers_client, &poller, sizeof(zbx_ipmi_poller_t *)); } zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __func__); return poller; } /****************************************************************************** * * * Purpose: returns IPMI poller by connected client * * * ******************************************************************************/ static zbx_ipmi_poller_t *ipmi_manager_get_poller_by_client(zbx_ipmi_manager_t *manager, zbx_ipc_client_t *client) { zbx_ipmi_poller_t **poller, poller_local, *plocal = &poller_local; plocal->client = client; poller = (zbx_ipmi_poller_t **)zbx_hashset_search(&manager->pollers_client, &plocal); if (NULL == poller) { THIS_SHOULD_NEVER_HAPPEN; exit(EXIT_FAILURE); } return *poller; } /******************************************************************************* * * * Purpose: returns IPMI poller to be assigned to new host * * * * Comments: This function will return IPMI poller with least monitored hosts. * * * *******************************************************************************/ static zbx_ipmi_poller_t *ipmi_manager_get_host_poller(zbx_ipmi_manager_t *manager) { zbx_ipmi_poller_t *poller; zbx_binary_heap_elem_t el; el = *zbx_binary_heap_find_min(&manager->pollers_load); zbx_binary_heap_remove_min(&manager->pollers_load); poller = (zbx_ipmi_poller_t *)el.data; poller->hosts_num++; zbx_binary_heap_insert(&manager->pollers_load, &el); return poller; } /****************************************************************************** * * * Purpose: processes IPMI poller request queue * * * * Parameters: manager - [IN] * * poller - [IN] * * now - [IN] current time * * * * Comments: This function will send the next request in queue to the poller, * * skipping requests for unreachable hosts for unreachable period. * * * ******************************************************************************/ static void ipmi_manager_process_poller_queue(zbx_ipmi_manager_t *manager, zbx_ipmi_poller_t *poller, int now) { zbx_ipmi_request_t *request; zbx_ipmi_manager_host_t *host; while (NULL != (request = ipmi_poller_pop_request(poller))) { switch (request->message.code) { case ZBX_IPC_IPMI_COMMAND_REQUEST: case ZBX_IPC_IPMI_CLEANUP_REQUEST: break; case ZBX_IPC_IPMI_VALUE_REQUEST: if (NULL != request->client) break; if (NULL == (host = (zbx_ipmi_manager_host_t *)zbx_hashset_search(&manager->hosts, &request->hostid))) { THIS_SHOULD_NEVER_HAPPEN; ipmi_request_free(request); continue; } if (now < host->disable_until) { zbx_dc_requeue_unreachable_items(&request->itemid, 1); ipmi_request_free(request); continue; } break; } ipmi_poller_send_request(poller, request); break; } } /****************************************************************************** * * * Purpose: caches host to keep local copy of its availability data * * * * Parameters: manager - [IN] * * hostid - [IN] * * now - [IN] current time * * * * Return value: cached host. * * * ******************************************************************************/ static zbx_ipmi_manager_host_t *ipmi_manager_cache_host(zbx_ipmi_manager_t *manager, zbx_uint64_t hostid, int now) { zbx_ipmi_manager_host_t *host; if (NULL == (host = (zbx_ipmi_manager_host_t *)zbx_hashset_search(&manager->hosts, &hostid))) { zbx_ipmi_manager_host_t host_local; host_local.hostid = hostid; host_local.disable_until = 0; host_local.poller = ipmi_manager_get_host_poller(manager); host_local.lastcheck = now; host = (zbx_ipmi_manager_host_t *)zbx_hashset_insert(&manager->hosts, &host_local, sizeof(host_local)); } else host->lastcheck = now; return host; } /****************************************************************************** * * * Purpose: updates cached host * * * ******************************************************************************/ static void ipmi_manager_update_host(zbx_ipmi_manager_t *manager, const zbx_dc_interface_t *interface, zbx_uint64_t hostid) { zbx_ipmi_manager_host_t *ipmi_host; if (NULL == (ipmi_host = (zbx_ipmi_manager_host_t *)zbx_hashset_search(&manager->hosts, &hostid))) { THIS_SHOULD_NEVER_HAPPEN; return; } ipmi_host->disable_until = interface->disable_until; } /****************************************************************************** * * * Purpose: tries to activate item's interface after receiving response * * * * Parameters: manager - [IN] * * itemid - [IN] * * ts - [IN] activation timestamp * * * ******************************************************************************/ static void ipmi_manager_activate_interface(zbx_ipmi_manager_t *manager, zbx_uint64_t itemid, zbx_timespec_t *ts) { zbx_dc_item_t item; int errcode; unsigned char *data = NULL; size_t data_alloc = 0, data_offset = 0; zbx_dc_config_get_items_by_itemids(&item, &itemid, &errcode, 1); if (SUCCEED == errcode) { zbx_activate_item_interface(ts, &item.interface, item.itemid, item.type, item.host.host, 0, &data, &data_alloc, &data_offset); ipmi_manager_update_host(manager, &item.interface, item.host.hostid); } zbx_dc_config_clean_items(&item, &errcode, 1); if (NULL != data) { zbx_availability_send(ZBX_IPC_AVAILABILITY_REQUEST, data, (zbx_uint32_t)data_offset, NULL); zbx_free(data); } } /****************************************************************************** * * * Purpose: tries to deactivate item's interface after receiving * * host level error * * * * Parameters: manager - [IN] * * itemid - [IN] * * ts - [IN] deactivation timestamp * * unavailable_delay - [IN] * * unreachable_period - [IN] * * unreachable_delay - [IN] * * error - [IN] * * * ******************************************************************************/ static void ipmi_manager_deactivate_interface(zbx_ipmi_manager_t *manager, zbx_uint64_t itemid, zbx_timespec_t *ts, int unavailable_delay, int unreachable_period, int unreachable_delay, const char *error) { zbx_dc_item_t item; int errcode; unsigned char *data = NULL; size_t data_alloc = 0, data_offset = 0; zbx_dc_config_get_items_by_itemids(&item, &itemid, &errcode, 1); if (SUCCEED == errcode) { zbx_deactivate_item_interface(ts, &item.interface, item.itemid, item.type, item.host.host, item.key_orig, &data, &data_alloc, &data_offset, unavailable_delay, unreachable_period, unreachable_delay, error); ipmi_manager_update_host(manager, &item.interface, item.host.hostid); } zbx_dc_config_clean_items(&item, &errcode, 1); if (NULL != data) { zbx_availability_send(ZBX_IPC_AVAILABILITY_REQUEST, data, (zbx_uint32_t)data_offset, NULL); zbx_free(data); } } /****************************************************************************** * * * Purpose: serializes IPMI poll and discovery requests * * * * Parameters: item - [IN] item to poll * * message - [OUT] message * * * ******************************************************************************/ static void ipmi_manager_serialize_request(const zbx_dc_item_t *item, zbx_ipc_message_t *message) { zbx_uint32_t size; size = zbx_ipmi_serialize_request(&message->data, item->host.hostid, item->itemid, item->interface.addr, item->interface.port, item->host.ipmi_authtype, item->host.ipmi_privilege, item->host.ipmi_username, item->host.ipmi_password, item->ipmi_sensor, 0, item->key_orig); message->code = ZBX_IPC_IPMI_VALUE_REQUEST; message->size = size; } /****************************************************************************** * * * Purpose: schedules request to host * * * * Parameters: manager - [IN] * * hostid - [IN] target host id * * request - [IN] request to schedule * * now - [IN] current timestamp * * * ******************************************************************************/ static void ipmi_manager_schedule_request(zbx_ipmi_manager_t *manager, zbx_uint64_t hostid, zbx_ipmi_request_t *request, int now) { zbx_ipmi_manager_host_t *host = ipmi_manager_cache_host(manager, hostid, now); ipmi_poller_schedule_request(host->poller, request); } /********************************************************************************* * * * Purpose: either sends or queues IPMI poll requests from configuration * * cache IPMI poller queue * * * * Parameters: manager - [IN] * * now - [IN] current time * * config_timeout - [IN] * * nextcheck - [OUT] time when next IPMI check is scheduled * * in configuration cache IPMI poller queue * * * * Return value: number of requests scheduled * * * *********************************************************************************/ static int ipmi_manager_schedule_requests(zbx_ipmi_manager_t *manager, int now, int config_timeout, int *nextcheck) { zbx_dc_item_t items[ZBX_MAX_POLLER_ITEMS]; int num = zbx_dc_config_get_ipmi_poller_items(now, ZBX_MAX_POLLER_ITEMS, config_timeout, items, nextcheck); for (int i = 0; i < num; i++) { zbx_ipmi_request_t *request; char *error = NULL; if (FAIL == zbx_ipmi_port_expand_macros(items[i].host.hostid, items[i].interface.port_orig, &items[i].interface.port, &error)) { int errcode = CONFIG_ERROR; unsigned char state = ITEM_STATE_NOTSUPPORTED; zbx_timespec_t ts; zbx_timespec(&ts); zbx_preprocess_item_value(items[i].itemid, items[i].host.hostid, items[i].value_type, items[i].flags, NULL, &ts, state, error); zbx_dc_requeue_items(&items[i].itemid, &ts.sec, &errcode, 1); zbx_free(error); continue; } request = ipmi_request_create(items[i].host.hostid); request->itemid = items[i].itemid; request->item_state = items[i].state; request->item_flags = items[i].flags; ipmi_manager_serialize_request(&items[i], &request->message); ipmi_manager_schedule_request(manager, items[i].host.hostid, request, now); } zbx_preprocessor_flush(); zbx_dc_config_clean_items(items, NULL, (size_t)num); return num; } /****************************************************************************** * * * Purpose: forwards IPMI request to poller managing specified host * * * * Parameters: manager - [IN] * * client - [IN] client asking to execute IPMI request * * message - [IN] request message * * now - [IN] current time * * code - [IN] request message code * * * ******************************************************************************/ static void ipmi_manager_process_client_request(zbx_ipmi_manager_t *manager, zbx_ipc_client_t *client, zbx_ipc_message_t *message, int now, int code) { zbx_ipmi_request_t *request; zbx_uint64_t hostid; zbx_ipmi_deserialize_request_objectid(message->data, &hostid); zbx_ipc_client_addref(client); request = ipmi_request_create(0); request->client = client; zbx_ipc_message_copy(&request->message, message); request->message.code = (zbx_uint32_t)code; ipmi_manager_schedule_request(manager, hostid, request, now); } /****************************************************************************** * * * Purpose: forwards result of request to client * * * * Parameters: manager - [IN] * * client - [IN] IPMI poller client * * message - [IN] command result message * * now - [IN] current time * * code - [IN] result message code * * * ******************************************************************************/ static void ipmi_manager_process_client_result(zbx_ipmi_manager_t *manager, zbx_ipc_client_t *client, zbx_ipc_message_t *message, int now, int code) { zbx_ipmi_poller_t *poller; if (NULL == (poller = ipmi_manager_get_poller_by_client(manager, client))) { THIS_SHOULD_NEVER_HAPPEN; return; } if (SUCCEED == zbx_ipc_client_connected(poller->request->client)) { zbx_ipc_client_send(poller->request->client, (zbx_uint32_t)code, message->data, message->size); zbx_ipc_client_release(poller->request->client); } ipmi_poller_free_request(poller); ipmi_manager_process_poller_queue(manager, poller, now); } /************************************************************************************** * * * Purpose: processes IPMI check result received from IPMI poller * * * * Parameters: manager - [IN] * * client - [IN] client (IPMI poller) * * message - [IN] received ZBX_IPC_IPMI_VALUE_RESULT message * * now - [IN] current time * * unavailable_delay - [IN] * * unreachable_period - [IN] * * unreachable_delay - [IN] * * * *************************************************************************************/ static void ipmi_manager_process_value_result(zbx_ipmi_manager_t *manager, zbx_ipc_client_t *client, zbx_ipc_message_t *message, int now, int unavailable_delay, int unreachable_period, int unreachable_delay) { char *value; zbx_timespec_t ts; unsigned char state; int errcode; AGENT_RESULT result; zbx_ipmi_poller_t *poller; zbx_uint64_t itemid; unsigned char flags; if (NULL == (poller = ipmi_manager_get_poller_by_client(manager, client))) { THIS_SHOULD_NEVER_HAPPEN; return; } if (NULL != poller->request->client) { ipmi_manager_process_client_result(manager, client, message, now, ZBX_IPC_IPMI_VALUE_RESULT); return; } itemid = poller->request->itemid; flags = poller->request->item_flags; zbx_ipmi_deserialize_result(message->data, &ts, &errcode, &value); /* update host availability */ switch (errcode) { case SUCCEED: case NOTSUPPORTED: case AGENT_ERROR: ipmi_manager_activate_interface(manager, itemid, &ts); break; case NETWORK_ERROR: case GATEWAY_ERROR: case TIMEOUT_ERROR: ipmi_manager_deactivate_interface(manager, itemid, &ts, unavailable_delay, unreachable_period, unreachable_delay, value); break; case CONFIG_ERROR: /* nothing to do */ break; } /* add received data to history cache */ switch (errcode) { case SUCCEED: state = ITEM_STATE_NORMAL; if (NULL != value) { zbx_init_agent_result(&result); SET_TEXT_RESULT(&result, value); value = NULL; zbx_preprocess_item_value(itemid, poller->request->hostid, ITEM_VALUE_TYPE_TEXT, flags, &result, &ts, state, NULL); zbx_free_agent_result(&result); } break; case NOTSUPPORTED: case AGENT_ERROR: case CONFIG_ERROR: state = ITEM_STATE_NOTSUPPORTED; zbx_preprocess_item_value(itemid, poller->request->hostid, ITEM_VALUE_TYPE_TEXT, flags, NULL, &ts, state, value); break; default: /* don't change item's state when network related error occurs */ break; } zbx_free(value); /* put back the item in configuration cache IPMI poller queue */ zbx_dc_requeue_items(&itemid, &ts.sec, &errcode, 1); ipmi_poller_free_request(poller); ipmi_manager_process_poller_queue(manager, poller, now); } ZBX_THREAD_ENTRY(zbx_ipmi_manager_thread, args) { zbx_ipc_service_t ipmi_service; char *error = NULL; zbx_ipc_client_t *client; zbx_ipc_message_t *message; zbx_ipmi_manager_t ipmi_manager; zbx_ipmi_poller_t *poller; int ret, polled_num = 0, scheduled_num = 0, tmp; double time_idle = 0, sec; zbx_timespec_t timeout = {0, 0}; const zbx_thread_info_t *info = &((zbx_thread_args_t *)args)->info; int server_num = ((zbx_thread_args_t *)args)->info.server_num; int process_num = ((zbx_thread_args_t *)args)->info.process_num; unsigned char process_type = ((zbx_thread_args_t *)args)->info.process_type; zbx_thread_ipmi_manager_args *ipmi_manager_args_in = (zbx_thread_ipmi_manager_args *) ((((zbx_thread_args_t *)args))->args); #define STAT_INTERVAL 5 /* if a process is busy and does not sleep then update status not */ /* faster than once in STAT_INTERVAL seconds */ #define ZBX_IPMI_MANAGER_DELAY 1 #define ZBX_IPMI_MANAGER_CLEANUP_DELAY SEC_PER_HOUR zbx_setproctitle("%s #%d starting", get_process_type_string(process_type), process_num); zabbix_log(LOG_LEVEL_INFORMATION, "%s #%d started [%s #%d]", get_program_type_string(info->program_type), server_num, get_process_type_string(process_type), process_num); zbx_update_selfmon_counter(info, ZBX_PROCESS_STATE_BUSY); if (FAIL == zbx_ipc_service_start(&ipmi_service, ZBX_IPC_SERVICE_IPMI, &error)) { zabbix_log(LOG_LEVEL_CRIT, "cannot start IPMI service: %s", error); zbx_free(error); exit(EXIT_FAILURE); } ipmi_manager_init(&ipmi_manager, ipmi_manager_args_in->get_config_forks); zbx_db_connect(ZBX_DB_CONNECT_NORMAL); double time_stat = zbx_time(); time_t nextcleanup = (time_t)time_stat + ZBX_IPMI_MANAGER_CLEANUP_DELAY; zbx_setproctitle("%s #%d started", get_process_type_string(process_type), process_num); while (ZBX_IS_RUNNING()) { double time_now = zbx_time(); time_t now = (time_t)time_now; if (STAT_INTERVAL < time_now - time_stat) { zbx_setproctitle("%s #%d [scheduled %d, polled %d values, idle " ZBX_FS_DBL " sec during " ZBX_FS_DBL " sec]", get_process_type_string(process_type), process_num, scheduled_num, polled_num, time_idle, time_now - time_stat); time_stat = time_now; time_idle = 0; polled_num = 0; scheduled_num = 0; } /* manager -> client */ if (FAIL == zbx_vps_monitor_capped()) { scheduled_num += ipmi_manager_schedule_requests(&ipmi_manager, now, ipmi_manager_args_in->config_timeout, &tmp); time_t nextcheck = (time_t)tmp; if (FAIL != tmp) timeout.sec = (nextcheck > now ? nextcheck - now : 0); else timeout.sec = ZBX_IPMI_MANAGER_DELAY; if (ZBX_IPMI_MANAGER_DELAY < timeout.sec) timeout.sec = ZBX_IPMI_MANAGER_DELAY; } else timeout.sec = ZBX_IPMI_MANAGER_DELAY; zbx_update_selfmon_counter(info, ZBX_PROCESS_STATE_IDLE); ret = zbx_ipc_service_recv(&ipmi_service, &timeout, &client, &message); zbx_update_selfmon_counter(info, ZBX_PROCESS_STATE_BUSY); sec = zbx_time(); zbx_update_env(get_process_type_string(process_type), sec); if (ZBX_IPC_RECV_IMMEDIATE != ret) time_idle += sec - time_now; if (NULL != message) { switch (message->code) { /* poller -> manager */ case ZBX_IPC_IPMI_REGISTER: if (NULL != (poller = ipmi_manager_register_poller(&ipmi_manager, client, message))) { ipmi_manager_process_poller_queue(&ipmi_manager, poller, now); } break; /* client -> manager */ case ZBX_IPC_IPMI_VALUE_REQUEST: ipmi_manager_process_client_request(&ipmi_manager, client, message, now, ZBX_IPC_IPMI_VALUE_REQUEST); break; /* poller -> manager or poller -> manager -> client if value request sent by client */ case ZBX_IPC_IPMI_VALUE_RESULT: ipmi_manager_process_value_result(&ipmi_manager, client, message, now, ipmi_manager_args_in->config_unavailable_delay, ipmi_manager_args_in->config_unreachable_period, ipmi_manager_args_in->config_unreachable_delay); polled_num++; break; /* client -> manager */ case ZBX_IPC_IPMI_SCRIPT_REQUEST: ipmi_manager_process_client_request(&ipmi_manager, client, message, now, ZBX_IPC_IPMI_COMMAND_REQUEST); break; /* poller -> manager -> client */ case ZBX_IPC_IPMI_COMMAND_RESULT: ipmi_manager_process_client_result(&ipmi_manager, client, message, now, ZBX_IPC_IPMI_SCRIPT_RESULT); } zbx_ipc_message_free(message); } if (NULL != client) zbx_ipc_client_release(client); if (now >= nextcleanup) { ipmi_manager_host_cleanup(&ipmi_manager, now, ipmi_manager_args_in->get_config_forks); nextcleanup = now + ZBX_IPMI_MANAGER_CLEANUP_DELAY; } } zbx_setproctitle("%s #%d [terminated]", get_process_type_string(process_type), process_num); while (1) zbx_sleep(SEC_PER_MIN); #undef STAT_INTERVAL #undef ZBX_IPMI_MANAGER_DELAY #undef ZBX_IPMI_MANAGER_CLEANUP_DELAY } #endif