/* ** 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 "zbxstr.h" #include "zbxnum.h" #include "zbxcomms.h" #include "zbxgetopt.h" #include "zbxcrypto.h" #include "zbxagentget.h" #include "zbxversion.h" #include "zbxlog.h" #include "zbxjson.h" #include "zbxbincommon.h" #ifndef _WINDOWS # include "zbxnix.h" #else # include "zbxwin32.h" #endif typedef enum { ZBX_AUTO_PROTOCOL, ZBX_JSON_PROTOCOL, ZBX_PLAINTEXT_PROTOCOL } zbx_protocol_t; ZBX_GET_CONFIG_VAR2(const char *, const char *, zbx_progname, NULL) static const char title_message[] = "zabbix_get"; static const char *usage_message[] = { "-s host-name-or-IP", "[-p port-number]", "[-I IP-address]", "[-t timeout]", "-k item-key", NULL, #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) "-s host-name-or-IP", "[-p port-number]", "[-I IP-address]", "[-t timeout]", "--tls-connect cert", "--tls-ca-file CA-file", "[--tls-crl-file CRL-file]", "[--tls-agent-cert-issuer cert-issuer]", "[--tls-agent-cert-subject cert-subject]", "--tls-cert-file cert-file", "--tls-key-file key-file", #if defined(HAVE_OPENSSL) "[--tls-cipher13 cipher-string]", #endif #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) "[--tls-cipher cipher-string]", #endif "-k item-key", NULL, "-s host-name-or-IP", "[-p port-number]", "[-I IP-address]", "[-t timeout]", "--tls-connect psk", "--tls-psk-identity PSK-identity", "--tls-psk-file PSK-file", #if defined(HAVE_OPENSSL) "[--tls-cipher13 cipher-string]", #endif #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) "[--tls-cipher cipher-string]", #endif "-k item-key", NULL, #endif "-h", NULL, "-V", NULL, NULL /* end of text */ }; ZBX_GET_CONFIG_VAR(unsigned char, zbx_program_type, ZBX_PROGRAM_TYPE_GET) #define CONFIG_GET_TIMEOUT_MIN 1 #define CONFIG_GET_TIMEOUT_MAX 30 #define CONFIG_GET_TIMEOUT_MIN_STR ZBX_STR(CONFIG_GET_TIMEOUT_MIN) #define CONFIG_GET_TIMEOUT_MAX_STR ZBX_STR(CONFIG_GET_TIMEOUT_MAX) static int CONFIG_GET_TIMEOUT = CONFIG_GET_TIMEOUT_MAX; static const char *help_message[] = { "Get data from Zabbix agent.", "", "General options:", " -s --host host-name-or-IP Specify host name or IP address of a host", " -p --port port-number Specify port number of agent running on the host", " (default: " ZBX_DEFAULT_AGENT_PORT_STR ")", " -I --source-address IP-address Specify source IP address", "", " -t --timeout seconds Specify timeout. Valid range: " CONFIG_GET_TIMEOUT_MIN_STR "-" CONFIG_GET_TIMEOUT_MAX_STR " seconds", " (default: " CONFIG_GET_TIMEOUT_MAX_STR " seconds)", "", " -k --key item-key Specify key of the item to retrieve value for", "", " --protocol value Protocol used to communicate with agent. Values:", " auto - connect using JSON protocol,", " fallback and retry with", " plaintext protocol (default)", " json - connect using JSON protocol", " plaintext - connect using plaintext protocol", " where just item key is sent", " (6.4.x and older releases)", "", "", " -h --help Display this help message", " -V --version Display version number", "", "TLS connection options:", #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) " --tls-connect value How to connect to agent. Values:", " unencrypted - connect without encryption", " (default)", " psk - connect using TLS and a pre-shared", " key", " cert - connect using TLS and a", " certificate", "", " --tls-ca-file CA-file Full pathname of a file containing the top-level", " CA(s) certificates for peer certificate", " verification", "", " --tls-crl-file CRL-file Full pathname of a file containing revoked", " certificates", "", " --tls-agent-cert-issuer cert-issuer Allowed agent certificate issuer", "", " --tls-agent-cert-subject cert-subject Allowed agent certificate subject", "", " --tls-cert-file cert-file Full pathname of a file containing the certificate", " or certificate chain", "", " --tls-key-file key-file Full pathname of a file containing the private key", "", " --tls-psk-identity PSK-identity Unique, case sensitive string used to", " identify the pre-shared key", "", " --tls-psk-file PSK-file Full pathname of a file containing the pre-shared", " key", #if defined(HAVE_OPENSSL) "", " --tls-cipher13 Cipher string for OpenSSL 1.1.1 or newer for", " TLS 1.3. Override the default ciphersuite", " selection criteria. This option is not available", " if OpenSSL version is less than 1.1.1", #endif #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) "", " --tls-cipher GnuTLS priority string (for TLS 1.2 and up) or", " OpenSSL cipher string (only for TLS 1.2).", " Override the default ciphersuite selection", " criteria", #endif #else " Not available. This 'zabbix_get' was compiled without TLS support", #endif "", "Example(s):", " zabbix_get -s 127.0.0.1 -p " ZBX_DEFAULT_AGENT_PORT_STR " -k \"system.cpu.load[all,avg1]\"", #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) "", " zabbix_get -s 127.0.0.1 -p " ZBX_DEFAULT_AGENT_PORT_STR " -k \"system.cpu.load[all,avg1]\" \\", " --tls-connect cert --tls-ca-file /home/zabbix/zabbix_ca_file \\", " --tls-agent-cert-issuer \\", " \"CN=Signing CA,OU=IT operations,O=Example Corp,DC=example,DC=com\" \\", " --tls-agent-cert-subject \\", " \"CN=server1,OU=IT operations,O=Example Corp,DC=example,DC=com\" \\", " --tls-cert-file /home/zabbix/zabbix_get.crt \\", " --tls-key-file /home/zabbix/zabbix_get.key", "", " zabbix_get -s 127.0.0.1 -p " ZBX_DEFAULT_AGENT_PORT_STR " -k \"system.cpu.load[all,avg1]\" \\", " --tls-connect psk --tls-psk-identity \"PSK ID Zabbix agentd\" \\", " --tls-psk-file /home/zabbix/zabbix_agentd.psk", #endif NULL /* end of text */ }; static zbx_config_tls_t *zbx_config_tls = NULL; int CONFIG_TCP_MAX_BACKLOG_SIZE = SOMAXCONN; /* COMMAND LINE OPTIONS */ /* long options */ struct zbx_option longopts[] = { {"host", 1, NULL, 's'}, {"port", 1, NULL, 'p'}, {"key", 1, NULL, 'k'}, {"source-address", 1, NULL, 'I'}, {"timeout", 1, NULL, 't'}, {"help", 0, NULL, 'h'}, {"version", 0, NULL, 'V'}, {"tls-connect", 1, NULL, '1'}, {"tls-ca-file", 1, NULL, '2'}, {"tls-crl-file", 1, NULL, '3'}, {"tls-agent-cert-issuer", 1, NULL, '4'}, {"tls-agent-cert-subject", 1, NULL, '5'}, {"tls-cert-file", 1, NULL, '6'}, {"tls-key-file", 1, NULL, '7'}, {"tls-psk-identity", 1, NULL, '8'}, {"tls-psk-file", 1, NULL, '9'}, {"tls-cipher13", 1, NULL, 'A'}, {"tls-cipher", 1, NULL, 'B'}, {"protocol", 1, NULL, 'P'}, {0} }; /* short options */ static char shortopts[] = "s:p:k:I:t:hVP:"; /* end of COMMAND LINE OPTIONS */ #if !defined(_WINDOWS) /****************************************************************************** * * * Purpose: process signals * * * * Parameters: sig - signal ID * * * ******************************************************************************/ static void get_signal_handler(int sig) { if (SIGPIPE == sig) /* this signal is raised when peer closes connection because of access restrictions */ return; if (SIGALRM == sig) zbx_error("Timeout while executing operation"); #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) if (ZBX_TCP_SEC_UNENCRYPTED != zbx_config_tls->connect_mode) zbx_tls_free_on_signal(); #endif exit(EXIT_FAILURE); } #endif /* not WINDOWS */ /****************************************************************************** * * * Purpose: connect to Zabbix agent, receive and print value * * * * Parameters: host - server name or IP address * * port - port number * * key - item's key * * * ******************************************************************************/ static int get_value(const char *source_ip, const char *host, unsigned short port, const char *key, int *version, zbx_protocol_t protocol) { zbx_socket_t s; int ret; ssize_t received_len = -1; char *tls_arg1, *tls_arg2; switch (zbx_config_tls->connect_mode) { case ZBX_TCP_SEC_UNENCRYPTED: tls_arg1 = NULL; tls_arg2 = NULL; break; #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) case ZBX_TCP_SEC_TLS_CERT: tls_arg1 = zbx_config_tls->server_cert_issuer; tls_arg2 = zbx_config_tls->server_cert_subject; break; case ZBX_TCP_SEC_TLS_PSK: tls_arg1 = zbx_config_tls->psk_identity; tls_arg2 = NULL; /* zbx_tls_connect() will find PSK */ break; #endif default: THIS_SHOULD_NEVER_HAPPEN; return FAIL; } if (SUCCEED == (ret = zbx_tcp_connect(&s, source_ip, host, port, CONFIG_GET_TIMEOUT + 1, zbx_config_tls->connect_mode, tls_arg1, tls_arg2))) { struct zbx_json j; const char *ptr; size_t len; int retry = 0; zbx_json_init(&j, ZBX_JSON_STAT_BUF_LEN); if (ZBX_PLAINTEXT_PROTOCOL == protocol) *version = 0; if (ZBX_COMPONENT_VERSION(7, 0, 0) <= *version) { zbx_agent_prepare_request(&j, key, CONFIG_GET_TIMEOUT); ptr = j.buffer; len = j.buffer_size; } else { ptr = key; len = strlen(key); } if (SUCCEED == (ret = zbx_tcp_send_ext(&s, ptr, len, 0, ZBX_TCP_PROTOCOL, 0))) { if (FAIL == (received_len = zbx_tcp_recv_ext(&s, 0, 0))) ret = FAIL; else (void)zbx_tcp_read_close_notify(&s, 0, NULL); } if (SUCCEED == ret) { AGENT_RESULT result; zbx_init_agent_result(&result) if (FAIL == (ret = zbx_agent_handle_response(s.buffer, s.read_bytes, received_len, host, &result, version))) { retry = 1; } else { if (SUCCEED != ret) { zbx_rtrim(result.msg, "\r\n"); printf("%s: %s\n", ZBX_NOTSUPPORTED, result.msg); } else if (0 == ZBX_ISSET_VALUE(&result)) { puts(ZBX_NODATA ": No value was received."); } else { zbx_rtrim(result.text, "\r\n"); printf("%s\n", result.text); } } zbx_free_agent_result(&result) } else zbx_error("Get value error: %s", zbx_socket_strerror()); zbx_tcp_close(&s); if (1 == retry) { if (ZBX_AUTO_PROTOCOL == protocol) { return get_value(source_ip, host, port, key, version, protocol); } else zbx_error("Get value error: JSON protocol requested but received plaintext response"); } } else zbx_error("Get value error: %s", zbx_socket_strerror()); return ret; } int main(int argc, char **argv) { int i, ret = SUCCEED, version = ZBX_COMPONENT_VERSION(7, 0, 0); char *host = NULL, *key = NULL, *source_ip = NULL, ch; unsigned short opt_count[256] = {0}, port = ZBX_DEFAULT_AGENT_PORT; zbx_protocol_t protocol = ZBX_AUTO_PROTOCOL; #if defined(_WINDOWS) char *error = NULL; #endif /* see description of 'optarg' in 'man 3 getopt' */ char *zbx_optarg = NULL; /* see description of 'optind' in 'man 3 getopt' */ int zbx_optind = 0; zbx_progname = get_program_name(argv[0]); zbx_init_library_common(zbx_log_impl, get_zbx_progname, zbx_backtrace); #ifndef _WINDOWS zbx_init_library_nix(get_zbx_progname, NULL); #endif #if !defined(_WINDOWS) && (defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL)) if (SUCCEED != zbx_coredump_disable()) { zbx_error("cannot disable core dump, exiting..."); exit(EXIT_FAILURE); } #endif zbx_config_tls = zbx_config_tls_new(); /* parse the command-line */ while ((char)EOF != (ch = (char)zbx_getopt_long(argc, argv, shortopts, longopts, NULL, &zbx_optarg, &zbx_optind))) { opt_count[(unsigned char)ch]++; switch (ch) { case 'k': if (NULL == key) key = zbx_strdup(NULL, zbx_optarg); break; case 'p': port = (unsigned short)atoi(zbx_optarg); break; case 's': if (NULL == host) host = zbx_strdup(NULL, zbx_optarg); break; case 'I': if (NULL == source_ip) source_ip = zbx_strdup(NULL, zbx_optarg); break; case 't': if (FAIL == zbx_is_uint_n_range(zbx_optarg, ZBX_MAX_UINT64_LEN, &CONFIG_GET_TIMEOUT, sizeof(CONFIG_GET_TIMEOUT), CONFIG_GET_TIMEOUT_MIN, CONFIG_GET_TIMEOUT_MAX)) { zbx_error("Invalid timeout, valid range %d:%d seconds", CONFIG_GET_TIMEOUT_MIN, CONFIG_GET_TIMEOUT_MAX); exit(EXIT_FAILURE); } break; case 'h': zbx_print_help(zbx_progname, help_message, usage_message, NULL); exit(EXIT_SUCCESS); case 'V': zbx_print_version(title_message); #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) printf("\n"); zbx_tls_version(); #endif exit(EXIT_SUCCESS); #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) case '1': zbx_config_tls->connect = zbx_strdup(zbx_config_tls->connect, zbx_optarg); break; case '2': zbx_config_tls->ca_file = zbx_strdup(zbx_config_tls->ca_file, zbx_optarg); break; case '3': zbx_config_tls->crl_file = zbx_strdup(zbx_config_tls->crl_file, zbx_optarg); break; case '4': zbx_config_tls->server_cert_issuer = zbx_strdup(zbx_config_tls->server_cert_issuer, zbx_optarg); break; case '5': zbx_config_tls->server_cert_subject = zbx_strdup(zbx_config_tls->server_cert_subject, zbx_optarg); break; case '6': zbx_config_tls->cert_file = zbx_strdup(zbx_config_tls->cert_file, zbx_optarg); break; case '7': zbx_config_tls->key_file = zbx_strdup(zbx_config_tls->key_file, zbx_optarg); break; case '8': zbx_config_tls->psk_identity = zbx_strdup(zbx_config_tls->psk_identity, zbx_optarg); break; case '9': zbx_config_tls->psk_file = zbx_strdup(zbx_config_tls->psk_file, zbx_optarg); break; case 'A': #if defined(HAVE_OPENSSL) zbx_config_tls->cipher_cmd13 = zbx_strdup(zbx_config_tls->cipher_cmd13, zbx_optarg); #elif defined(HAVE_GNUTLS) zbx_error("parameter \"--tls-cipher13\" can be used with OpenSSL 1.1.1 or newer." " zabbix_get was compiled with GnuTLS"); exit(EXIT_FAILURE); #endif break; case 'B': zbx_config_tls->cipher_cmd = zbx_strdup(zbx_config_tls->cipher_cmd, zbx_optarg); break; #else case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'A': case 'B': zbx_error("TLS parameters cannot be used: 'zabbix_get' was compiled without TLS" " support"); exit(EXIT_FAILURE); break; #endif case 'P': if (0 == strcmp(zbx_optarg, "json")) { protocol = ZBX_JSON_PROTOCOL; } else if (0 == strcmp(zbx_optarg, "plaintext")) { protocol = ZBX_PLAINTEXT_PROTOCOL; } else if (0 == strcmp(zbx_optarg, "auto")) { protocol = ZBX_AUTO_PROTOCOL; } else { zbx_error("Invalid protocol \"%s\"", zbx_optarg); exit(EXIT_FAILURE); } break; default: zbx_print_usage(zbx_progname, usage_message); exit(EXIT_FAILURE); } } #if defined(_WINDOWS) if (SUCCEED != zbx_socket_start(&error)) { zbx_error(error); zbx_free(error); exit(EXIT_FAILURE); } #endif if (NULL == host || NULL == key) { zbx_print_usage(zbx_progname, usage_message); ret = FAIL; } /* every option may be specified only once */ for (i = 0; NULL != longopts[i].name; i++) { ch = longopts[i].val; if (1 < opt_count[(unsigned char)ch]) { if (NULL == strchr(shortopts, ch)) zbx_error("option \"--%s\" specified multiple times", longopts[i].name); else zbx_error("option \"-%c\" or \"--%s\" specified multiple times", ch, longopts[i].name); ret = FAIL; } } if (FAIL == ret) goto out; /* Parameters which are not option values are invalid. The check relies on zbx_getopt_internal() which */ /* always permutes command line arguments regardless of POSIXLY_CORRECT environment variable. */ if (argc > zbx_optind) { for (i = zbx_optind; i < argc; i++) zbx_error("invalid parameter \"%s\"", argv[i]); ret = FAIL; } if (FAIL == ret) { printf("Try '%s --help' for more information.\n", zbx_progname); goto out; } if (NULL != zbx_config_tls->connect || NULL != zbx_config_tls->ca_file || NULL != zbx_config_tls->crl_file || NULL != zbx_config_tls->server_cert_issuer || NULL != zbx_config_tls->server_cert_subject || NULL != zbx_config_tls->cert_file || NULL != zbx_config_tls->key_file || NULL != zbx_config_tls->psk_identity || NULL != zbx_config_tls->psk_file || NULL != zbx_config_tls->cipher_cmd13 || NULL != zbx_config_tls->cipher_cmd) { #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) zbx_tls_validate_config(zbx_config_tls, 0, 0, get_zbx_program_type); if (ZBX_TCP_SEC_UNENCRYPTED != zbx_config_tls->connect_mode) { #if defined(_WINDOWS) zbx_tls_init_parent(get_zbx_program_type); #endif zbx_tls_init_child(zbx_config_tls, get_zbx_program_type, NULL); } #else ZBX_UNUSED(get_zbx_program_type); #endif } #if !defined(_WINDOWS) signal(SIGINT, get_signal_handler); signal(SIGQUIT, get_signal_handler); signal(SIGTERM, get_signal_handler); signal(SIGHUP, get_signal_handler); signal(SIGALRM, get_signal_handler); signal(SIGPIPE, get_signal_handler); #endif ret = get_value(source_ip, host, port, key, &version, protocol); out: zbx_free(host); zbx_free(key); zbx_free(source_ip); #if defined(HAVE_GNUTLS) || defined(HAVE_OPENSSL) if (ZBX_TCP_SEC_UNENCRYPTED != zbx_config_tls->connect_mode) { zbx_tls_free(); zbx_tls_library_deinit(ZBX_TLS_INIT_THREADS); } #endif zbx_config_tls_free(zbx_config_tls); #if defined(_WINDOWS) while (0 == WSACleanup()) ; #endif return SUCCEED == ret ? EXIT_SUCCESS : EXIT_FAILURE; }