/* ex: set tabstop=4 shiftwidth=4 autoindent:* +-------------------------------------------------------------------------+ | Copyright (C) 2004-2026 The Cacti Group | | | | This program is free software; you can redistribute it and/or | | modify it under the terms of the GNU Lesser General Public | | License as published by the Free Software Foundation; either | | version 2.1 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 Lesser General Public License for more details. | | | | You should have received a copy of the GNU Lesser General Public | | License along with this library; if not, write to the Free Software | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | | 02110-1301, USA | | | +-------------------------------------------------------------------------+ | spine: a backend data gatherer for cacti | +-------------------------------------------------------------------------+ | This poller would not have been possible without: | | - Larry Adams (current development and enhancements) | | - Rivo Nurges (rrd support, mysql poller cache, misc functions) | | - RTG (core poller code, pthreads, snmp, autoconf examples) | | - Brady Alleman/Doug Warner (threading ideas, implementation details) | +-------------------------------------------------------------------------+ | - Cacti - http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ #ifndef SPINE_UTIL_H #define SPINE_UTIL_H /* cacti config reading functions */ extern void read_config_options(void); extern int read_spine_config(const char *file); extern void config_defaults(void); extern bool poller_transfer_status(MYSQL *source, MYSQL *destination); extern void poller_push_data_to_main(void); /* cacti logging function */ extern int spine_log(const char *format, ...) __attribute__((format(printf, 1, 2))); extern void die(const char *format, ...) __attribute__((noreturn)) __attribute__((format(printf, 1, 2))); /* option processing function */ extern void set_option(const char *setting, const char *value); /* number validation functions */ extern int is_ipaddress(const char *string); extern int all_digits(const char *str); /* what a poll returned; text is what poller_output stores, U if unknown */ typedef enum { RESULT_UNKNOWN = 0, RESULT_COUNTER, RESULT_SIGNED, RESULT_FLOAT, RESULT_HEX_COUNTER, RESULT_MULTIPART } result_kind_t; typedef struct { result_kind_t kind; union { uint64_t counter; int64_t integer; double real; } value; char text[RESULTS_BUFFER]; } classified_result_t; extern result_kind_t classify_result(const char *raw, classified_result_t *out); /* determine if a device is a debug device */ extern int is_debug_device(int device_id); extern void parse_debug_devices(char *device_list, int *devices, size_t capacity); extern bool spine_should_log_device(int host_id, int verbosity); /* string and file functions */ extern char *add_slashes(const char *string); extern int file_exists(const char *filename); extern char *strip_alpha(char *string); extern char *strncopy(char *dst, const char *src, size_t n); extern int spine_snprintf(char *output, size_t capacity, const char *format, ...) __attribute__((format(printf, 3, 4))); extern void spine_clear_sensitive(void *buffer, size_t length); extern void spine_sanitize_log_message(char *message); extern char *trim(char *str); extern char *rtrim(char *str); extern char *ltrim(char *str); extern char *reverse(char *str); extern int strpos(const char *haystack, const char *needle) ; extern int char_count(const char *str, int chr); /* custom regex replace to return a value if matches */ #define MAX_MATCHES 5 #define REGEX_NUMBER "([-+]*)([0-9]*)([.][0-9]+)" #define CAPABILITY_PROTOCOL_LIST_MAX 480 char *regex_replace(const char *exp, char *value); int format_spine_capabilities(char *output, size_t output_size, const char *auth_protocols, const char *priv_protocols); /* macro to copy string to string with an ending null */ #define STRNCOPY(dst, src) strncopy((dst), (src), sizeof(dst)) #define USTRNCOPY(dst, src) ustrncopy((dst), (src), sizeof(dst)) /* macro to duplicate string and die if fails */ #define STRDUP_OR_DIE(dst, src, reason) \ if ((dst = strdup(src)) == NULL) {\ die("FATAL: malloc() failed during strdup() for %s", reason);\ }\ /* get highres time as double */ extern double get_time_as_double(void); /* function to check to see if program has capability to use raw socket with out uid = 0 */ extern void drop_privileges(void); extern int privileges_dropped(uid_t uid, gid_t gid); extern int hasCaps(void); /* see if we can do things as root */ extern void checkAsRoot(void); /* log format */ extern void set_date_format(void); extern char *get_date_format(void); /* remote/main server synchronization */ extern void poller_push_data_to_main(void); /* MySQL supports INSERT row aliases from 8.0.20; MariaDB does not. */ extern int db_row_alias_upsert_supported(const char *version, unsigned long version_number); /* start time for spine */ extern double start_time; /* the version of Cacti as a decimal */ int get_cacti_version(MYSQL *psql, int mode); extern void spine_sleep_usec(unsigned int microseconds); extern double spine_monotonic_time(void); extern int spine_count_to_int(unsigned long long count); extern int spine_wait_readable(int fd, double deadline); extern int spine_wait_writable(int fd, double deadline); extern int spine_permits_init(spine_permits_t *permits, int count); extern int spine_permits_destroy(spine_permits_t *permits); extern int spine_permits_try_acquire(spine_permits_t *permits); extern int spine_permits_release(spine_permits_t *permits); extern int spine_permits_available(spine_permits_t *permits); /* Stack for every thread spine creates. The deepest worker path, child() * through poll_host() into the SNMP, script, ping and logging code, peaked at * 179,024 bytes (glibc, gcc ASan, live MariaDB and snmpd) and sums to 175,856 * bytes statically (gcc 15 -fcallgraph-info=su on musl). About five results * buffers of that grow with --with-results-buffer. musl's 128 KiB default * overflows on entry to poll_host(). Reserve twice 180 KiB plus five results * buffers, rounded up to 64 KiB so the size is a page multiple everywhere: * 384 KiB with the default 2048-byte results buffer. */ /* Check the unrounded expression before evaluating its size_t arithmetic. */ _Static_assert(RESULTS_BUFFER >= 2, "RESULTS_BUFFER must hold a value and terminator"); _Static_assert((size_t) RESULTS_BUFFER <= (SIZE_MAX - ((size_t) 2 * 180 * 1024 + 65535)) / 10, "RESULTS_BUFFER overflows the worker stack size"); #define SPINE_THREAD_STACK_SIZE \ (((size_t) 2 * ((size_t) 180 * 1024 + 5 * (size_t) RESULTS_BUFFER) + 65535) & ~(size_t) 65535) /*! \fn int spine_thread_attr_init(pthread_attr_t *attributes) * \brief initialize thread attributes with at least SPINE_THREAD_STACK_SIZE * \return 0, or the pthread error; on error nothing is left to destroy */ extern int spine_thread_attr_init(pthread_attr_t *attributes); /*! \fn int spine_appendf(char **cursor, size_t *remaining, const char *fmt, ...) * \brief append to a bounded buffer without walking off the end * * snprintf() returns the length it would have written, so `p += snprintf(p, * remaining, ...)` moves the cursor past the buffer the first time a value is * truncated. The next `remaining` is then negative, and as a size_t it is * effectively unbounded, at a destination already out of bounds. * * This advances the cursor by what was actually written, stops on the * terminator when the text does not fit, and says so. * * \return TRUE when the whole string was appended, FALSE on truncation or a * formatting error, in which case the buffer stays NUL-terminated */ extern int spine_appendf(char **cursor, size_t *remaining, const char *fmt, ...) __attribute__((format(printf, 3, 4))); #endif /* SPINE_UTIL_H */