/* ** Zabbix ** Copyright (C) 2001-2023 Zabbix SIA ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** 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 General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ #include "rtc_server.h" #include "log.h" #include "zbxha.h" #include "zbxdiag.h" #include "zbxtypes.h" #include "zbxcommon.h" #include "zbxservice.h" #include "zbx_rtc_constants.h" #include "zbxjson.h" #include "zbxtime.h" static int rtc_parse_options_server(const char *opt, zbx_uint32_t *code, struct zbx_json *j, char **error) { const char *param; if (0 == strcmp(opt, ZBX_SECRETS_RELOAD)) { *code = ZBX_RTC_SECRETS_RELOAD; return SUCCEED; } if (0 == strcmp(opt, ZBX_SERVICE_CACHE_RELOAD)) { *code = ZBX_RTC_SERVICE_CACHE_RELOAD; return SUCCEED; } if (0 == strcmp(opt, ZBX_TRIGGER_HOUSEKEEPER_EXECUTE)) { *code = ZBX_RTC_TRIGGER_HOUSEKEEPER_EXECUTE; return SUCCEED; } if (0 == strcmp(opt, ZBX_HA_STATUS)) { *code = ZBX_RTC_HA_STATUS; return SUCCEED; } if (0 == strncmp(opt, ZBX_HA_REMOVE_NODE, ZBX_CONST_STRLEN(ZBX_HA_REMOVE_NODE))) { param = opt + ZBX_CONST_STRLEN(ZBX_HA_REMOVE_NODE); if ('=' == *param) { *code = ZBX_RTC_HA_REMOVE_NODE; zbx_json_addstring(j, ZBX_PROTO_TAG_NODE, param + 1, ZBX_JSON_TYPE_STRING); return SUCCEED; } if ('\0' == *param) { *error = zbx_strdup(NULL, "missing node cuid or name parameter"); return FAIL; } /* not ha_remove_node runtime control option */ } if (0 == strncmp(opt, ZBX_HA_SET_FAILOVER_DELAY, ZBX_CONST_STRLEN(ZBX_HA_SET_FAILOVER_DELAY))) { int delay; param = opt + ZBX_CONST_STRLEN(ZBX_HA_SET_FAILOVER_DELAY); if ('=' == *param) { if (SUCCEED == zbx_is_time_suffix(param + 1, &delay, ZBX_LENGTH_UNLIMITED)) { if (delay < 10 || delay > 15 * SEC_PER_MIN) { *error = zbx_strdup(NULL, "failover delay must be in range from 10s to 15m"); return FAIL; } *code = ZBX_RTC_HA_SET_FAILOVER_DELAY; zbx_json_addint64(j, ZBX_PROTO_TAG_FAILOVER_DELAY, delay); return SUCCEED; } else { *error = zbx_dsprintf(NULL, "invalid HA failover delay parameter: %s\n", param + 1); return FAIL; } } if ('\0' == *param) { *error = zbx_strdup(NULL, "missing failover delay parameter"); return FAIL; } } if (0 == strncmp(opt, ZBX_PROXY_CONFIG_CACHE_RELOAD, ZBX_CONST_STRLEN(ZBX_PROXY_CONFIG_CACHE_RELOAD))) { param = opt + ZBX_CONST_STRLEN(ZBX_PROXY_CONFIG_CACHE_RELOAD); if ('=' == *param) { char *token, *p; if ('\0' == *(param + 1)) { *error = zbx_strdup(NULL, "missing proxy name(s)"); return FAIL; } zbx_json_addarray(j, ZBX_PROTO_TAG_PROXY_NAMES); p = zbx_strdup(NULL, param + 1); token = strtok(p, ","); while (NULL != token) { zbx_json_addstring(j, NULL, token, ZBX_JSON_TYPE_STRING); token = strtok(NULL, ","); } zbx_free(p); *code = ZBX_RTC_PROXY_CONFIG_CACHE_RELOAD; return SUCCEED; } if ('\0' == *param) { *code = ZBX_RTC_PROXY_CONFIG_CACHE_RELOAD; return SUCCEED; } } return SUCCEED; } #if defined(HAVE_SIGQUEUE) /****************************************************************************** * * * Purpose: process loglevel runtime control option * * * * Parameters: direction - [IN] the loglevel change direction: * * (1) - increase, (-1) - decrease * * data - [IN] the runtime control parameter (optional) * * result - [OUT] the runtime control result * * * * Return value: SUCCEED - the loglevel command was processed * * FAIL - the loglevel command must be processed by the * * default loglevel command handler * * * ******************************************************************************/ static int rtc_process_server_loglevel_option(int direction, const char *data, char **result) { int proc_num, proc_type; pid_t pid; if (SUCCEED != zbx_rtc_get_command_target(data, &pid, &proc_type, &proc_num, NULL, result)) return SUCCEED; /* change loglevel for all processes */ if (0 == pid && ZBX_PROCESS_TYPE_UNKNOWN == proc_type) { (void)zbx_ha_change_loglevel(direction, result); return FAIL; } if (ZBX_PROCESS_TYPE_HA_MANAGER == proc_type) { if (0 != proc_num && 1 != proc_num) { *result = zbx_dsprintf(NULL, "Invalid option parameter \"%d\"\n", proc_num); } else { (void)zbx_ha_change_loglevel(direction, result); *result = zbx_strdup(NULL, "Changed HA manager log level\n"); } return SUCCEED; } return FAIL; } #endif /****************************************************************************** * * * Purpose: process diaginfo runtime control option * * * * Parameters: data - [IN] the runtime control parameter (optional) * * result - [OUT] the runtime control result * * * * Return value: SUCCEED - the rtc command was processed * * FAIL - the rtc command must be processed by the default * * rtc command handler * * * ******************************************************************************/ static int rtc_process_diaginfo(const char *data, char **result) { struct zbx_json_parse jp; char buf[MAX_STRING_LEN]; unsigned int scope = 0; int ret = FAIL; if (FAIL == zbx_json_open(data, &jp) || SUCCEED != zbx_json_value_by_name(&jp, ZBX_PROTO_TAG_SECTION, buf, sizeof(buf), NULL)) { *result = zbx_dsprintf(NULL, "Invalid parameter \"%s\"\n", data); return FAIL; } if (0 == strcmp(buf, "all")) { scope = (1 << ZBX_DIAGINFO_VALUECACHE) | (1 << ZBX_DIAGINFO_LLD) | (1 << ZBX_DIAGINFO_ALERTING) | (1 << ZBX_DIAGINFO_CONNECTOR); } else if (0 == strcmp(buf, ZBX_DIAG_VALUECACHE)) { scope = 1 << ZBX_DIAGINFO_VALUECACHE; ret = SUCCEED; } else if (0 == strcmp(buf, ZBX_DIAG_LLD)) { scope = 1 << ZBX_DIAGINFO_LLD; ret = SUCCEED; } else if (0 == strcmp(buf, ZBX_DIAG_ALERTING)) { scope = 1 << ZBX_DIAGINFO_ALERTING; ret = SUCCEED; } if (0 != scope) zbx_diag_log_info(scope, result); return ret; } /****************************************************************************** * * * Purpose: process ha_status runtime command * * * ******************************************************************************/ static void rtc_ha_status(char **out) { char *nodes = NULL, *error = NULL; struct zbx_json_parse jp, jp_node; size_t out_alloc = 0, out_offset = 0; int failover_delay; if (SUCCEED != zbx_ha_get_failover_delay(&failover_delay, &error)) { zbx_strlog_alloc(LOG_LEVEL_ERR, out, &out_alloc, &out_offset, "cannot get failover delay: %s", error); zbx_free(error); return; } if (SUCCEED != zbx_ha_get_nodes(&nodes, &error)) { zbx_strlog_alloc(LOG_LEVEL_ERR, out, &out_alloc, &out_offset, "cannot get HA node information: %s", error); zbx_free(error); return; } #define ZBX_HA_REPORT_FMT "%-25s %-25s %-30s %-11s %s" if (SUCCEED == zbx_json_open(nodes, &jp)) { const char *pnext; char name[256], address[261], id[26], buffer[256]; int status, lastaccess_age, index = 1; zbx_strlog_alloc(LOG_LEVEL_INFORMATION, out, &out_alloc, &out_offset, "failover delay: %d seconds", failover_delay); zbx_strlog_alloc(LOG_LEVEL_INFORMATION, out, &out_alloc, &out_offset, "cluster status:"); zbx_strlog_alloc(LOG_LEVEL_INFORMATION, out, &out_alloc, &out_offset, " %2s " ZBX_HA_REPORT_FMT, "#", "ID", "Name", "Address", "Status", "Last Access"); for (pnext = NULL; NULL != (pnext = zbx_json_next(&jp, pnext));) { if (FAIL == zbx_json_brackets_open(pnext, &jp_node)) { THIS_SHOULD_NEVER_HAPPEN; continue; } if (SUCCEED != zbx_json_value_by_name(&jp_node, ZBX_PROTO_TAG_ID, id, sizeof(id), NULL)) { THIS_SHOULD_NEVER_HAPPEN; continue; } if (SUCCEED != zbx_json_value_by_name(&jp_node, ZBX_PROTO_TAG_NAME, name, sizeof(name), NULL)) { THIS_SHOULD_NEVER_HAPPEN; continue; } if (SUCCEED != zbx_json_value_by_name(&jp_node, ZBX_PROTO_TAG_STATUS, buffer, sizeof(buffer), NULL)) { THIS_SHOULD_NEVER_HAPPEN; continue; } status = atoi(buffer); if (SUCCEED != zbx_json_value_by_name(&jp_node, ZBX_PROTO_TAG_LASTACCESS_AGE, buffer, sizeof(buffer), NULL)) { THIS_SHOULD_NEVER_HAPPEN; continue; } lastaccess_age = atoi(buffer); if (SUCCEED != zbx_json_value_by_name(&jp_node, ZBX_PROTO_TAG_ADDRESS, address, sizeof(address), NULL)) { THIS_SHOULD_NEVER_HAPPEN; continue; } zbx_strlog_alloc(LOG_LEVEL_INFORMATION, out, &out_alloc, &out_offset, " %2d. " ZBX_HA_REPORT_FMT, index++, id, '\0' != *name ? name : "<standalone server>", address, zbx_ha_status_str(status), zbx_age2str(lastaccess_age)); } } else { zbx_strlog_alloc(LOG_LEVEL_ERR, out, &out_alloc, &out_offset, "invalid response: %s", nodes); } zbx_free(nodes); #undef ZBX_HA_REPORT_FMT } /****************************************************************************** * * * Purpose: process ha_remove_node runtime command * * * ******************************************************************************/ static void rtc_ha_remove_node(const char *data, char **out) { char *error = NULL; struct zbx_json_parse jp; char buf[MAX_STRING_LEN]; size_t out_alloc = 0, out_offset = 0; if (FAIL == zbx_json_open(data, &jp)) { *out = zbx_dsprintf(NULL, "Invalid parameter format \"%s\"\n", data); return; } if (SUCCEED != zbx_json_value_by_name(&jp, ZBX_PROTO_TAG_NODE, buf, sizeof(buf), NULL)) { *out = zbx_dsprintf(NULL, "Missing node parameter \"%s\"\n", data); return; } if (SUCCEED != zbx_ha_remove_node(buf, out, &error)) { zbx_strlog_alloc(LOG_LEVEL_ERR, out, &out_alloc, &out_offset, "cannot remove HA node: %s", error); zbx_free(error); return; } } /****************************************************************************** * * * Purpose: process ha_failover_delay runtime command * * * ******************************************************************************/ static void rtc_ha_failover_delay(const char *data, char **out) { char *error = NULL; struct zbx_json_parse jp; char buf[MAX_STRING_LEN]; int failover_delay; size_t out_alloc = 0, out_offset = 0; if (FAIL == zbx_json_open(data, &jp)) { *out = zbx_dsprintf(NULL, "Invalid parameter format \"%s\"\n", data); return; } if (SUCCEED != zbx_json_value_by_name(&jp, ZBX_PROTO_TAG_FAILOVER_DELAY, buf, sizeof(buf), NULL)) { *out = zbx_dsprintf(NULL, "Missing failover_delay parameter \"%s\"\n", data); return; } if (10 > (failover_delay = atoi(buf)) || 15 * SEC_PER_MIN < failover_delay) { *out = zbx_dsprintf(NULL, "Invalid failover delay value \"%s\"\n", buf); return; } if (SUCCEED != zbx_ha_set_failover_delay(failover_delay, &error)) { zbx_strlog_alloc(LOG_LEVEL_ERR, out, &out_alloc, &out_offset, "cannot set HA failover delay: %s", error); zbx_free(error); return; } *out = zbx_dsprintf(NULL, "HA failover delay set to %d seconds\n", failover_delay); } /****************************************************************************** * * * Purpose: process runtime control option * * * * Parameters: rtc - [IN] the RTC service * * code - [IN] the request code * * data - [IN] the runtime control parameter (optional) * * result - [OUT] the runtime control result * * * * Return value: SUCCEED - the rtc command was processed * * FAIL - the rtc command must be processed by the default * * rtc command handler * * * ******************************************************************************/ int rtc_process_request_ex_server(zbx_rtc_t *rtc, zbx_uint32_t code, const unsigned char *data, char **result) { switch (code) { #if defined(HAVE_SIGQUEUE) case ZBX_RTC_LOG_LEVEL_INCREASE: return rtc_process_server_loglevel_option(1, (const char *)data, result); case ZBX_RTC_LOG_LEVEL_DECREASE: return rtc_process_server_loglevel_option(-1, (const char *)data, result); #endif case ZBX_RTC_CONFIG_CACHE_RELOAD: zbx_service_reload_cache(); return FAIL; case ZBX_RTC_SERVICE_CACHE_RELOAD: zbx_service_reload_cache(); return SUCCEED; case ZBX_RTC_SECRETS_RELOAD: zbx_rtc_notify(rtc, ZBX_PROCESS_TYPE_CONFSYNCER, 0, ZBX_RTC_SECRETS_RELOAD, NULL, 0); return SUCCEED; case ZBX_RTC_TRIGGER_HOUSEKEEPER_EXECUTE: zbx_rtc_notify(rtc, ZBX_PROCESS_TYPE_TRIGGERHOUSEKEEPER, 0, ZBX_RTC_TRIGGER_HOUSEKEEPER_EXECUTE, NULL, 0); return SUCCEED; case ZBX_RTC_DIAGINFO: return rtc_process_diaginfo((const char *)data, result); case ZBX_RTC_HA_STATUS: rtc_ha_status(result); return SUCCEED; case ZBX_RTC_HA_SET_FAILOVER_DELAY: rtc_ha_failover_delay((const char *)data, result); return SUCCEED; case ZBX_RTC_HA_REMOVE_NODE: rtc_ha_remove_node((const char *)data, result); return SUCCEED; case ZBX_RTC_PROXY_CONFIG_CACHE_RELOAD: zbx_rtc_notify(rtc, ZBX_PROCESS_TYPE_TASKMANAGER, 0, ZBX_RTC_PROXY_CONFIG_CACHE_RELOAD, (const char *)data, (zbx_uint32_t)strlen((const char *)data) + 1); return SUCCEED; case ZBX_RTC_PROXYPOLLER_PROCESS: zbx_rtc_notify(rtc, ZBX_PROCESS_TYPE_PROXYPOLLER, 0, ZBX_RTC_PROXYPOLLER_PROCESS, NULL, 0); return SUCCEED; } return FAIL; } /****************************************************************************** * * * Purpose: process runtime control option and print result * * * * Parameters: option - [IN] runtime control option * * config_timeout - [IN] * * error - [OUT] error message * * * * Return value: SUCCEED - the runtime control option was processed * * FAIL - otherwise * * * ******************************************************************************/ int rtc_process(const char *option, int config_timeout, char **error) { zbx_uint32_t code = ZBX_RTC_UNKNOWN; char *data; int ret = FAIL; struct zbx_json j; zbx_json_init(&j, 1024); if (SUCCEED != zbx_rtc_parse_options(option, &code, &j, error)) goto out; if (ZBX_RTC_UNKNOWN == code) { if (SUCCEED != rtc_parse_options_server(option, &code, &j, error)) goto out; if (ZBX_RTC_UNKNOWN == code) { *error = zbx_dsprintf(NULL, "unknown option \"%s\"", option); goto out; } } data = zbx_strdup(NULL, j.buffer); ret = zbx_rtc_async_exchange(&data, code, config_timeout, error); out: zbx_json_free(&j); return ret; } /****************************************************************************** * * * Purpose: reset the RTC service state by removing subscriptions and hooks * * * ******************************************************************************/ void rtc_reset(zbx_rtc_t *rtc) { int i; zbx_vector_rtc_sub_clear_ext(&rtc->subs, zbx_rtc_sub_free); for (i = 0; i < rtc->hooks.values_num; i++) zbx_free(rtc->hooks.values[i]); zbx_vector_rtc_hook_clear(&rtc->hooks); } int rtc_open(zbx_ipc_async_socket_t *asocket, int timeout, char **error) { if (FAIL == zbx_ipc_async_socket_open(asocket, ZBX_IPC_SERVICE_RTC, timeout, error)) return FAIL; return SUCCEED; }