/* ** 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 "zbxcommon.h" #include "log.h" #include "setproctitle.h" #include "zbxthreads.h" const int INTERFACE_TYPE_PRIORITY[INTERFACE_TYPE_COUNT] = { INTERFACE_TYPE_AGENT, INTERFACE_TYPE_SNMP, INTERFACE_TYPE_JMX, INTERFACE_TYPE_IPMI }; static ZBX_THREAD_LOCAL volatile sig_atomic_t zbx_timed_out; /* 0 - no timeout occurred, 1 - SIGALRM took place */ #ifdef _WINDOWS char ZABBIX_SERVICE_NAME[ZBX_SERVICE_NAME_LEN] = APPLICATION_NAME; char ZABBIX_EVENT_SOURCE[ZBX_SERVICE_NAME_LEN] = APPLICATION_NAME; #endif #if defined(_WINDOWS) || defined(__MINGW32__) int __zbx_stat(const char *path, zbx_stat_t *buf) { int ret, fd; wchar_t *wpath; wpath = zbx_utf8_to_unicode(path); if (-1 == (ret = _wstat64(wpath, buf))) goto out; if (0 != S_ISDIR(buf->st_mode) || 0 != buf->st_size) goto out; /* In the case of symlinks _wstat64 returns zero file size. */ /* Try to work around it by opening the file and using fstat. */ ret = -1; if (-1 != (fd = _wopen(wpath, O_RDONLY))) { ret = _fstat64(fd, buf); _close(fd); } out: zbx_free(wpath); return ret; } #endif /****************************************************************************** * * * Purpose: return program name without path * * * * Return value: program name without path * * * ******************************************************************************/ const char *get_program_name(const char *path) { const char *filename = NULL; for (filename = path; path && *path; path++) { if ('\\' == *path || '/' == *path) filename = path + 1; } return filename; } /****************************************************************************** * * * Purpose: allocates nmemb * size bytes of memory and fills it with zeros * * * * Return value: returns a pointer to the newly allocated memory * * * ******************************************************************************/ void *zbx_calloc2(const char *filename, int line, void *old, size_t nmemb, size_t size) { int max_attempts; void *ptr = NULL; /* old pointer must be NULL */ if (NULL != old) { zabbix_log(LOG_LEVEL_CRIT, "[file:%s,line:%d] zbx_calloc: allocating already allocated memory. " "Please report this to Zabbix developers.", filename, line); } for ( max_attempts = 10, nmemb = MAX(nmemb, 1), size = MAX(size, 1); 0 < max_attempts && NULL == ptr; ptr = calloc(nmemb, size), max_attempts-- ); if (NULL != ptr) return ptr; zabbix_log(LOG_LEVEL_CRIT, "[file:%s,line:%d] zbx_calloc: out of memory. Requested " ZBX_FS_SIZE_T " bytes.", filename, line, (zbx_fs_size_t)size); exit(EXIT_FAILURE); } /****************************************************************************** * * * Purpose: allocates size bytes of memory * * * * Return value: returns a pointer to the newly allocated memory * * * ******************************************************************************/ void *zbx_malloc2(const char *filename, int line, void *old, size_t size) { int max_attempts; void *ptr = NULL; /* old pointer must be NULL */ if (NULL != old) { zabbix_log(LOG_LEVEL_CRIT, "[file:%s,line:%d] zbx_malloc: allocating already allocated memory. " "Please report this to Zabbix developers.", filename, line); } for ( max_attempts = 10, size = MAX(size, 1); 0 < max_attempts && NULL == ptr; ptr = malloc(size), max_attempts-- ); if (NULL != ptr) return ptr; zabbix_log(LOG_LEVEL_CRIT, "[file:%s,line:%d] zbx_malloc: out of memory. Requested " ZBX_FS_SIZE_T " bytes.", filename, line, (zbx_fs_size_t)size); exit(EXIT_FAILURE); } /****************************************************************************** * * * Purpose: changes the size of the memory block pointed to by old * * to size bytes * * * * Return value: returns a pointer to the newly allocated memory * * * ******************************************************************************/ void *zbx_realloc2(const char *filename, int line, void *old, size_t size) { int max_attempts; void *ptr = NULL; for ( max_attempts = 10, size = MAX(size, 1); 0 < max_attempts && NULL == ptr; ptr = realloc(old, size), max_attempts-- ); if (NULL != ptr) return ptr; zabbix_log(LOG_LEVEL_CRIT, "[file:%s,line:%d] zbx_realloc: out of memory. Requested " ZBX_FS_SIZE_T " bytes.", filename, line, (zbx_fs_size_t)size); exit(EXIT_FAILURE); } char *zbx_strdup2(const char *filename, int line, char *old, const char *str) { int retry; char *ptr = NULL; zbx_free(old); for (retry = 10; 0 < retry && NULL == ptr; ptr = strdup(str), retry--) ; if (NULL != ptr) return ptr; zabbix_log(LOG_LEVEL_CRIT, "[file:%s,line:%d] zbx_strdup: out of memory. Requested " ZBX_FS_SIZE_T " bytes.", filename, line, (zbx_fs_size_t)(strlen(str) + 1)); exit(EXIT_FAILURE); } /**************************************************************************************** * * * Purpose: For overwriting sensitive data in memory. * * Similar to memset() but should not be optimized out by a compiler. * * * * Derived from: * * http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/protect-secrets.html * * See also: * * http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1381.pdf on secure_memset() * * * ****************************************************************************************/ void *zbx_guaranteed_memset(void *v, int c, size_t n) { volatile signed char *p = (volatile signed char *)v; while (0 != n--) *p++ = (signed char)c; return v; } /****************************************************************************** * * * Purpose: print application parameters on stdout with layout suitable for * * 80-column terminal * * * * Comments: usage_message - is global variable which must be initialized * * in each zabbix application * * * ******************************************************************************/ void zbx_usage(void) { #define ZBX_MAXCOL 79 #define ZBX_SPACE1 " " /* left margin for the first line */ #define ZBX_SPACE2 " " /* left margin for subsequent lines */ const char **p = usage_message; if (NULL != *p) printf("usage:\n"); while (NULL != *p) { size_t pos; printf("%s%s", ZBX_SPACE1, progname); pos = ZBX_CONST_STRLEN(ZBX_SPACE1) + strlen(progname); while (NULL != *p) { size_t len; len = strlen(*p); if (ZBX_MAXCOL > pos + len) { pos += len + 1; printf(" %s", *p); } else { pos = ZBX_CONST_STRLEN(ZBX_SPACE2) + len + 1; printf("\n%s %s", ZBX_SPACE2, *p); } p++; } printf("\n"); p++; } #undef ZBX_MAXCOL #undef ZBX_SPACE1 #undef ZBX_SPACE2 } static const char copyright_message[] = "Copyright (C) 2023 Zabbix SIA\n" "License GPLv2+: GNU GPL version 2 or later <https://www.gnu.org/licenses/>.\n" "This is free software: you are free to change and redistribute it according to\n" "the license. There is NO WARRANTY, to the extent permitted by law."; static const char help_message_footer[] = "Report bugs to: <https://support.zabbix.com>\n" "Zabbix home page: <http://www.zabbix.com>\n" "Documentation: <https://www.zabbix.com/documentation>"; /****************************************************************************** * * * Purpose: print help of application parameters on stdout by application * * request with parameter '-h' * * * * Comments: help_message - is global variable which must be initialized * * in each zabbix application * * * ******************************************************************************/ void zbx_help(void) { const char **p = help_message; zbx_usage(); printf("\n"); while (NULL != *p) printf("%s\n", *p++); printf("\n"); puts(help_message_footer); } /****************************************************************************** * * * Purpose: print version and compilation time of application on stdout * * by application request with parameter '-V' * * * * Comments: title_message - is global variable which must be initialized * * in each zabbix application * * * ******************************************************************************/ void zbx_version(void) { printf("%s (Zabbix) %s\n", title_message, ZABBIX_VERSION); printf("Revision %s %s, compilation time: %s %s\n\n", ZABBIX_REVISION, ZABBIX_REVDATE, __DATE__, __TIME__); puts(copyright_message); } /****************************************************************************** * * * Purpose: set process title * * * ******************************************************************************/ void zbx_setproctitle(const char *fmt, ...) { #if defined(HAVE_FUNCTION_SETPROCTITLE) || defined(PS_OVERWRITE_ARGV) || defined(PS_PSTAT_ARGV) char title[MAX_STRING_LEN]; va_list args; va_start(args, fmt); zbx_vsnprintf(title, sizeof(title), fmt, args); va_end(args); zabbix_log(LOG_LEVEL_DEBUG, "%s() title:'%s'", __func__, title); #endif #if defined(HAVE_FUNCTION_SETPROCTITLE) setproctitle("%s", title); #elif defined(PS_OVERWRITE_ARGV) || defined(PS_PSTAT_ARGV) setproctitle_set_status(title); #endif } /****************************************************************************** * * * Purpose: check if string is a valid internet hostname * * * * Parameters: hostname - [IN] hostname string to be checked * * * * Return value: SUCCEED - could be a valid hostname, * * FAIL - definitely not a valid hostname * * Comments: * * Validation is not strict. Restrictions not checked: * * - individual label (component) length 1-63, * * - hyphens ('-') allowed only as interior characters in labels, * * - underscores ('_') allowed in domain name, but not in hostname. * * * ******************************************************************************/ int zbx_validate_hostname(const char *hostname) { int component; /* periods ('.') are only allowed when they serve to delimit components */ int len = ZBX_MAX_DNSNAME_LEN; const char *p; /* the first character must be an alphanumeric character */ if (0 == isalnum(*hostname)) return FAIL; /* check only up to the first 'len' characters, the 1st character is already successfully checked */ for (p = hostname + 1, component = 1; '\0' != *p; p++) { if (0 == --len) /* hostname too long */ return FAIL; /* check for allowed characters */ if (0 != isalnum(*p) || '-' == *p || '_' == *p) component = 1; else if ('.' == *p && 1 == component) component = 0; else return FAIL; } return SUCCEED; } /****************************************************************************** * * * Purpose: get nearest index position of sorted elements in array * * * * Parameters: p - pointer to array of elements * * sz - element size * * num - number of elements * * id - index to look for * * * * Return value: index at which it would be possible to insert the element so * * that the array is still sorted * * * ******************************************************************************/ int get_nearestindex(const void *p, size_t sz, int num, zbx_uint64_t id) { int first_index, last_index, index; zbx_uint64_t element_id; if (0 == num) return 0; first_index = 0; last_index = num - 1; while (1) { index = first_index + (last_index - first_index) / 2; if (id == (element_id = *(const zbx_uint64_t *)((const char *)p + index * sz))) return index; if (last_index == first_index) { if (element_id < id) index++; return index; } if (element_id < id) first_index = index + 1; else last_index = index; } } /****************************************************************************** * * * Purpose: add uint64 value to dynamic array * * * ******************************************************************************/ int uint64_array_add(zbx_uint64_t **values, int *alloc, int *num, zbx_uint64_t value, int alloc_step) { int index; index = get_nearestindex(*values, sizeof(zbx_uint64_t), *num, value); if (index < (*num) && (*values)[index] == value) return index; if (*alloc == *num) { if (0 == alloc_step) { zbx_error("Unable to reallocate buffer"); assert(0); } *alloc += alloc_step; *values = (zbx_uint64_t *)zbx_realloc(*values, *alloc * sizeof(zbx_uint64_t)); } memmove(&(*values)[index + 1], &(*values)[index], sizeof(zbx_uint64_t) * (*num - index)); (*values)[index] = value; (*num)++; return index; } /****************************************************************************** * * * Purpose: remove uint64 values from array * * * ******************************************************************************/ void uint64_array_remove(zbx_uint64_t *values, int *num, const zbx_uint64_t *rm_values, int rm_num) { int rindex, index; for (rindex = 0; rindex < rm_num; rindex++) { index = get_nearestindex(values, sizeof(zbx_uint64_t), *num, rm_values[rindex]); if (index == *num || values[index] != rm_values[rindex]) continue; memmove(&values[index], &values[index + 1], sizeof(zbx_uint64_t) * ((*num) - index - 1)); (*num)--; } } /****************************************************************************** * * * Return value: Interface type * * * * Comments: !!! Don't forget to sync the code with PHP !!! * * * ******************************************************************************/ unsigned char get_interface_type_by_item_type(unsigned char type) { switch (type) { case ITEM_TYPE_ZABBIX: return INTERFACE_TYPE_AGENT; case ITEM_TYPE_SNMP: case ITEM_TYPE_SNMPTRAP: return INTERFACE_TYPE_SNMP; case ITEM_TYPE_IPMI: return INTERFACE_TYPE_IPMI; case ITEM_TYPE_JMX: return INTERFACE_TYPE_JMX; case ITEM_TYPE_SIMPLE: case ITEM_TYPE_EXTERNAL: case ITEM_TYPE_SSH: case ITEM_TYPE_TELNET: return INTERFACE_TYPE_ANY; case ITEM_TYPE_HTTPAGENT: case ITEM_TYPE_SCRIPT: return INTERFACE_TYPE_OPT; default: return INTERFACE_TYPE_UNKNOWN; } } void zbx_alarm_flag_set(void) { zbx_timed_out = 1; } void zbx_alarm_flag_clear(void) { zbx_timed_out = 0; } #if !defined(_WINDOWS) && !defined(__MINGW32__) unsigned int zbx_alarm_on(unsigned int seconds) { zbx_alarm_flag_clear(); return alarm(seconds); } unsigned int zbx_alarm_off(void) { unsigned int ret; ret = alarm(0); zbx_alarm_flag_clear(); return ret; } #endif int zbx_alarm_timed_out(void) { return (0 == zbx_timed_out ? FAIL : SUCCEED); } /* Since 2.26 the GNU C Library will detect when /etc/resolv.conf has been modified and reload the changed */ /* configuration. For performance reasons manual reloading should be avoided when unnecessary. */ #if !defined(_WINDOWS) && defined(HAVE_RESOLV_H) && defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 26 /****************************************************************************** * * * Purpose: react to "/etc/resolv.conf" update * * * * Comments: it is intended to call this function in the end of each process * * main loop. The purpose of calling it at the end (instead of the * * beginning of main loop) is to let the first initialization of * * libc resolver proceed internally. * * * ******************************************************************************/ static void update_resolver_conf(void) { #define ZBX_RESOLV_CONF_FILE "/etc/resolv.conf" static time_t mtime = 0; zbx_stat_t buf; if (0 == zbx_stat(ZBX_RESOLV_CONF_FILE, &buf) && mtime != buf.st_mtime) { mtime = buf.st_mtime; if (0 != res_init()) zabbix_log(LOG_LEVEL_WARNING, "update_resolver_conf(): res_init() failed"); } #undef ZBX_RESOLV_CONF_FILE } #endif /****************************************************************************** * * * Purpose: throttling of update "/etc/resolv.conf" and "stdio" to the new * * log file after rotation * * * * Parameters: time_now - [IN] the time for compare in seconds * * * ******************************************************************************/ void __zbx_update_env(double time_now) { static double time_update = 0; /* handle /etc/resolv.conf update and log rotate less often than once a second */ if (1.0 < time_now - time_update) { time_update = time_now; zbx_handle_log(); #if !defined(_WINDOWS) && defined(HAVE_RESOLV_H) && defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ < 26 update_resolver_conf(); #endif } } /****************************************************************************** * * * Purpose: Print error text to the stderr * * * * Parameters: fmt - format of message * * * ******************************************************************************/ void zbx_error(const char *fmt, ...) { va_list args; va_start(args, fmt); fprintf(stderr, "%s [%li]: ", progname, zbx_get_thread_id()); vfprintf(stderr, fmt, args); fprintf(stderr, "\n"); fflush(stderr); va_end(args); } zbx_uint64_t suffix2factor(char c) { switch (c) { case 'K': return ZBX_KIBIBYTE; case 'M': return ZBX_MEBIBYTE; case 'G': return ZBX_GIBIBYTE; case 'T': return ZBX_TEBIBYTE; case 's': return 1; case 'm': return SEC_PER_MIN; case 'h': return SEC_PER_HOUR; case 'd': return SEC_PER_DAY; case 'w': return SEC_PER_WEEK; default: return 1; } }