Files
spine/spine_types.h
Thomas Vincent 898d8c692c fix(privileges): drop root before input and retain owned ICMP sockets (#639)
Open and own the required ICMP sockets before permanently dropping
setuid root, then parse input without elevated identity. Close-on-exec
socket ownership, shared IPv4/IPv6 dispatch and fault-injected
identity/capability calls are covered. The owned fixture tests check
deny/allow behavior without contacting external devices.

Step 2/14 of the complete C17/native-correctness series. Base: the
preceding series branch; merge/review in order so this PR shows only its
own step.

Earlier validation, before this review follow-up, in isolated copied
snapshots on Linux arm64:

- Clean GCC build and make check: 13 passed, 1 existing prerequisite
skips, 0 failures.
- ASan/UBSan: 12 passed, 2 existing privilege/allocator-interposition
prerequisite skips, 0 failures; no sanitizer findings.
- Production source/profile integrity verification against disposable
MariaDB and loopback SNMP fixtures passed.
- Release archive extracted into a separate source tree and built/tested
out of tree; fuzz checks passed.

Earlier local profiles retain source hashes for the copied validation
tree; GitHub checks validate the published commit. No production
database/device, host installation or live privilege change is part of
validation. Existing prerequisite skips were retained; no test or
finding was suppressed to obtain a pass.


Series navigation: [Series
index](https://github.com/Cacti/spine/pull/638); [previous
step](https://github.com/Cacti/spine/pull/638). [Next
step](https://github.com/Cacti/spine/pull/640).

Review follow-up (2026-10-09): Restacked on the corrected preceding
branches. The SNMP status fix is introduced in #638, the
reindex/completion fixes in #640, and focused regression coverage in
#650.

Restacked tip validation: isolated Linux arm64 build and make check
passed (18 passed, 1 setuid prerequisite skip, no failures).
GitHub checks validate the updated published commits; earlier
profile/archive/sanitizer evidence above belongs to the previous
revisions.

---------

Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
2026-10-10 21:08:52 -04:00

670 lines
18 KiB
C

/*
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_TYPES_H
#define SPINE_TYPES_H
#include "common.h"
/* Defines */
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#define LOCAL 0
#define REMOTE 1
#define SPINE_NONE 0
#define SPINE_IPV4 1
#define SPINE_IPV6 2
#ifndef __GNUC__
# define __attribute__(x) /* NOTHING */
#endif
/* Windows does not support stderr. Therefore, don't use it. */
#ifdef __CYGWIN__
#define DISABLE_STDERR
#endif
#ifdef HAS_EXECINFO_H
#include <execinfo.h>
#endif
/* if a host is legal, return TRUE */
#define HOSTID_DEFINED(x) ((x) >= 0)
/* warning-suppression macros
*
* There are times when we cannot avoid using a parameter or variable which
* is not used, and these correctly generate compiler warnings. But when we
* *know* that the variable is actually intended to be unused, we can use one
* of these macros inside the function to suppress it. This has the effect
* of suppressing the warning (a good thing), plus documenting to the reader
* that this is intentional.
*
* Both do the same thing - they're just for different semantics.
*/
#define UNUSED_VARIABLE(p) (void)(p)
#define UNUSED_PARAMETER(p) (void)(p)
/* logging macros
*
* These all perform conditional logging based on the current runtime logging
* level, and it relies on a bit of tricky (but entirely portable) preprocessor
* techniques.
*
* Standard C does not support variadic macros (macros with a variable number
* of parameters), and though GNU C does, it's not at all portable. So we instead
* rely on the fact that putting parens around something turn multiple params
* into one:
*
* SPINE_LOG_DEBUG(("n=%d string=%s foo=%f", n, string, foo));
*
* This macros has *one* parameter:
*
* ("n=%d string=%s foo=%f", n, string, foo)
*
* and the parentheses are part of it. When we call this macro, we pass the
* "single" parameter unadorned, so that
*
* spine_log args
*
* expands to
*
* spine_log ("n=%d string=%s foo=%f", n, string, foo)
*
* Voila: it's a normal printf-like call.
*
* The second part of this is the conditional test, and the obvious approach
* of using an "if" statement is exceptionally bad form: there are all kinds
* of pitfalls which arise in this case. Instead, we should try to use an
* *expression*, which has none of these problems.
*
* The conditional tests are modelled after the assert() mechanism, which
* checks the first parameter, and if it's true, it evaluates the second
* parameter. If the test is not true, then the second part is *guaranteed*
* not to be evaluated.
*
* The (void) prefix is to forestall compiler warnings about expressions
* not being used.
*/
#define SPINE_LOG(format_and_args) (spine_log format_and_args)
#define SPINE_LOG_LOW(format_and_args) (void)(set.logging.log_level >= POLLER_VERBOSITY_LOW && spine_log format_and_args)
#define SPINE_LOG_MEDIUM(format_and_args) (void)(set.logging.log_level >= POLLER_VERBOSITY_MEDIUM && spine_log format_and_args)
#define SPINE_LOG_HIGH(format_and_args) (void)(set.logging.log_level >= POLLER_VERBOSITY_HIGH && spine_log format_and_args)
#define SPINE_LOG_DEBUG(format_and_args) (void)(set.logging.log_level >= POLLER_VERBOSITY_DEBUG && spine_log format_and_args)
#define SPINE_LOG_DEVDBG(format_and_args) (void)(set.logging.log_level >= POLLER_VERBOSITY_DEVDBG && spine_log format_and_args)
#define SPINE_LOG_DEVICE(host_id, verbosity, format_and_args) \
(void)(spine_should_log_device(host_id, verbosity) && spine_log format_and_args)
/* general constants */
#define MAX_THREADS 100
#define MAX_DEBUG_DEVICES 100
#define TINY_BUFSIZE 16
#define SMALL_BUFSIZE 256
#define MEDIUM_BUFSIZE 512
#define BUFSIZE 1024
#define DBL_BUFSIZE 2048
#define LRG_BUFSIZE 8096
#define BIG_BUFSIZE 65535
#define MEGA_BUFSIZE 1024000
#define HUGE_BUFSIZE 2048000
#define LOGSIZE 65535
#define LRG_LOGSIZE 1024000
#define BITSINBYTE 8
#define THIRTYTWO 4294967295ul
#define SIXTYFOUR 18446744073709551615ul
#define STAT_DESCRIP_ERROR 99
#define SPINE_PARENT 1
#define SPINE_FORK 0
/* locations to search for the config file */
#define CONFIG_PATHS 4
#define CONFIG_PATH_1 ""
#define CONFIG_PATH_2 "/etc/"
#define CONFIG_PATH_3 "/etc/cacti/"
#define CONFIG_PATH_4 "../etc/"
/* config file defaults */
#define DEFAULT_CONF_FILE "spine.conf"
#define DEFAULT_THREADS 5
#define DEFAULT_DB_HOST "localhost"
#define DEFAULT_DB_DB "cacti"
#define DEFAULT_DB_USER "cactiuser"
#define DEFAULT_DB_PASS "cactiuser"
#define DEFAULT_DB_PORT 3306
#define DEFAULT_DB_PREG 0
#define DEFAULT_LOGFILE "/var/www/html/cacti/log/cacti.log"
#define DEFAULT_TIMEOUT 294000000
/* threads constants */
#define LOCK_SNMP 0
#define LOCK_ICMP 2
#define LOCK_GHBN 3
#define LOCK_POOL 4
#define LOCK_PHP 6
#define LOCK_PHP_PROC_0 7
#define LOCK_PHP_PROC_1 8
#define LOCK_PHP_PROC_2 9
#define LOCK_PHP_PROC_3 10
#define LOCK_PHP_PROC_4 11
#define LOCK_PHP_PROC_5 12
#define LOCK_PHP_PROC_6 13
#define LOCK_PHP_PROC_7 14
#define LOCK_PHP_PROC_8 15
#define LOCK_PHP_PROC_9 16
#define LOCK_PHP_PROC_10 17
#define LOCK_PHP_PROC_11 18
#define LOCK_PHP_PROC_12 19
#define LOCK_PHP_PROC_13 20
#define LOCK_PHP_PROC_14 21
#define LOCK_THDET 40
#define LOCK_HOST_TIME 41
#define LOCK_SNMP_O 0
#define LOCK_ICMP_O 2
#define LOCK_GHBN_O 3
#define LOCK_POOL_O 4
#define LOCK_PHP_O 6
#define LOCK_PHP_PROC_0_O 7
#define LOCK_PHP_PROC_1_O 8
#define LOCK_PHP_PROC_2_O 9
#define LOCK_PHP_PROC_3_O 10
#define LOCK_PHP_PROC_4_O 11
#define LOCK_PHP_PROC_5_O 12
#define LOCK_PHP_PROC_6_O 13
#define LOCK_PHP_PROC_7_O 14
#define LOCK_PHP_PROC_8_O 15
#define LOCK_PHP_PROC_9_O 16
#define LOCK_PHP_PROC_10_O 17
#define LOCK_PHP_PROC_11_O 18
#define LOCK_PHP_PROC_12_O 19
#define LOCK_PHP_PROC_13_O 20
#define LOCK_PHP_PROC_14_O 21
#define LOCK_THDET_O 40
#define LOCK_HOST_TIME_O 41
/* poller actions */
#define POLLER_ACTION_SNMP 0
#define POLLER_ACTION_SCRIPT 1
#define POLLER_ACTION_PHP_SCRIPT_SERVER 2
#define POLLER_ACTION_SNMP_COUNT 10
#define POLLER_ACTION_SCRIPT_COUNT 11
#define POLLER_ACTION_PHP_SCRIPT_SERVER_COUNT 12
/* reindex constants */
#define POLLER_COMMAND_REINDEX 1
/* log destinations */
#define LOGDEST_FILE 1
#define LOGDEST_BOTH 2
#define LOGDEST_SYSLOG 3
#define LOGDEST_STDOUT 4
#define IS_LOGGING_TO_FILE() ((set.logging.log_destination) == LOGDEST_FILE || (set.logging.log_destination) == LOGDEST_BOTH)
#define IS_LOGGING_TO_SYSLOG() ((set.logging.log_destination) == LOGDEST_SYSLOG || (set.logging.log_destination) == LOGDEST_BOTH)
#define IS_LOGGING_TO_STDOUT() ((set.logging.log_destination) == LOGDEST_STDOUT )
#define SPINE_FREE(s) do { if (s) { free((void *)s); s = NULL; } } while(0)
/* logging levels */
#define POLLER_VERBOSITY_NONE 1
#define POLLER_VERBOSITY_LOW 2
#define POLLER_VERBOSITY_MEDIUM 3
#define POLLER_VERBOSITY_HIGH 4
#define POLLER_VERBOSITY_DEBUG 5
#define POLLER_VERBOSITY_DEVDBG 6
/* logging separator constants */
#define GDC_MIN 0
#define GDC_HYPHEN 0
#define GDC_SLASH 1
#define GDC_DOT 2
#define GDC_MAX 2
#define GDC_DEFAULT 1
/* logging format constants */
#define GD_FMT_SIZE 21
#define GD_MIN 0
#define GD_MO_D_Y 0
#define GD_MN_D_Y 1
#define GD_D_MO_Y 2
#define GD_D_MN_Y 3
#define GD_Y_MO_D 4
#define GD_Y_MN_D 5
#define GD_MAX 5
#define GD_DEFAULT 5
/* host availability statistics */
#define AVAIL_NONE 0
#define AVAIL_SNMP_AND_PING 1
#define AVAIL_SNMP 2
#define AVAIL_PING 3
#define AVAIL_SNMP_OR_PING 4
#define AVAIL_SNMP_GET_SYSDESC 5
#define AVAIL_SNMP_GET_NEXT 6
#define AVAIL_STREAM 7
#define PING_ICMP 1
#define PING_UDP 2
#define PING_TCP 3
#define PING_SNMP 4
#define PING_TCP_CLOSED 5
#define HOST_UNKNOWN 0
#define HOST_DOWN 1
#define HOST_RECOVERING 2
#define HOST_UP 3
/* required for ICMP and UDP ping */
#define ICMP_ECHO 8
#define ICMP_HDR_SIZE 8
/* required for PHP Script Server */
#define MAX_PHP_SERVERS 15
#define PHP_READY 0
#define PHP_BUSY 1
#define PHP_INIT 999
#define PHP_ERROR 99
/* required for validation of script results */
#define RESULT_INIT 0
#define RESULT_ARGX 1
#define RESULT_VALX 2
#define RESULT_SEPARATOR 3
#define RESULT_SPACE 4
#define RESULT_ALPHA 5
#define RESULT_DIGIT 6
/* snmp session status */
#define SNMP_1 0
#define SNMP_2c 1
#define SNMP_3 3
#define SNMP_NONE 4
/* Constants for remote polling */
#define REMOTE_ONLINE 0
#define REMOTE_OFFLINE 1
#define REMOTE_RECOVERY 2
/* These are used to perform string matches, returning TRUE/VALUE values.
* For strcmp() this is not really that useful, but the case-insensitive
* one has slight portability issues. Better to abstract them here.
*/
#define STRMATCH(a,b) (strcmp((a),(b)) == 0)
#define STRIMATCH(a,b) (strcasecmp((a),(b)) == 0)
/* When any kind of poller wants to set an undefined value; this particular
* value used ('U') springs from the requirements of rrdupdate. We also
* include the corresponding test macro which looks for the literal string
* "U". This *could* use strcmp(), but this is more efficient.
*/
#define SET_UNDEFINED(buf) ( (buf)[0] = 'U', (buf)[1] = '\0' )
#define IS_UNDEFINED(buf) ( (buf)[0] == 'U' && (buf)[1] == '\0' )
/*! Config Structure
*
* This structure holds Spine database configuration information and/or override values
* obtained via either accessing the database or reading the runtime options. In addition,
* it contains runtime status information.
*
*/
/* Console configuration and runtime state. */
typedef struct {
int stdout_notty;
int stderr_notty;
} spine_console_config_t;
/* Poller configuration and runtime state. */
typedef struct {
int poller_id;
int poller_interval;
int parent_fork;
int num_parent_processes;
int active_profiles;
int threads;
int threads_set;
int snmponly;
int SQL_readonly;
int mode;
} spine_poller_config_t;
/* Hosts configuration and runtime state. */
typedef struct {
int start_host_id;
int end_host_id;
char host_id_list[BIG_BUFSIZE];
int has_device_0;
int has_output_regex;
} spine_hosts_config_t;
/* Database configuration and runtime state. */
typedef struct {
char host[BUFSIZE];
char database[BUFSIZE];
char user[BUFSIZE];
char password[BUFSIZE];
int ssl;
char ssl_key[BUFSIZE];
char ssl_cert[BUFSIZE];
char ssl_ca[BUFSIZE];
unsigned int port;
char version[BUFSIZE];
int onupdate;
} spine_database_config_t;
/* Php configuration and runtime state. */
typedef struct {
char path_php[BUFSIZE];
char path_php_server[BUFSIZE];
int script_timeout;
int php_required;
int php_initialized;
int php_servers;
int php_current_server;
} spine_php_config_t;
/* Logging configuration and runtime state. */
typedef struct {
char path_logfile[DBL_BUFSIZE];
int logfile_processed;
int log_level;
int log_destination;
int log_perror;
int log_pwarn;
int log_pstats;
char selective_device_debug[LRG_BUFSIZE];
int spine_log_level;
int log_datetime_separator;
int log_datetime_format;
} spine_logging_config_t;
/* Availability configuration and runtime state. */
typedef struct {
int icmp_avail;
int icmp_uses_caps;
int availability_method;
int ping_method;
int ping_retries;
int ping_timeout;
int ping_failure_count;
int ping_recovery_count;
int ping_only;
} spine_availability_config_t;
/* Snmp configuration and runtime state. */
typedef struct {
int total_snmp_ports;
int snmp_max_get_size;
int snmp_retries;
char snmp_clientaddr[BUFSIZE];
int mibs;
} spine_snmp_config_t;
/* Boost configuration and runtime state. */
typedef struct {
int boost_enabled;
int boost_redirect;
} spine_boost_config_t;
/* Exit configuration and runtime state. */
typedef struct {
int exit_code;
size_t exit_size;
void* exit_stack[10];
} spine_exit_config_t;
/* Process-local aggregate. A full rebuild is required when its layout changes. */
typedef struct config_struct {
spine_console_config_t console;
spine_poller_config_t poller;
spine_hosts_config_t hosts;
spine_database_config_t database;
spine_database_config_t remote_database;
spine_php_config_t php;
spine_logging_config_t logging;
spine_availability_config_t availability;
spine_snmp_config_t snmp;
spine_boost_config_t boost;
spine_exit_config_t exit;
int d_b;
int cacti_version;
int cygwinshloc;
} config_t;
/*! Target Structure
*
* This structure holds the contents of the Poller Items table and the results
* of each polling action.
*
*/
/* Owned SNMP identity and transport options. The borrowed snmp_connection_t
* adapter remains separate; these arrays are never external pointers. */
typedef struct {
char community[100];
int version;
char username[50];
char password[50];
char auth_protocol[7];
char priv_passphrase[200];
char priv_protocol[8];
char context[65];
char engine_id[30];
int port;
int timeout;
} snmp_profile_t;
typedef struct {
int method;
int ping_method;
int port;
int timeout;
int retries;
} device_availability_t;
typedef struct target_struct {
int target_id;
char result[RESULTS_BUFFER];
int local_data_id;
int action;
char command[256];
char hostname[250];
char rrd_name[30];
char rrd_path[255];
int rrd_num;
char arg1[1024];
char arg2[255];
char arg3[255];
char output_regex[255];
snmp_profile_t snmp;
device_availability_t availability;
} target_t;
/*! SNMP OID's Structure
*
* This structure holds SNMP get results temporarily while polling is taking place.
*
*/
typedef struct snmp_oids {
int array_position;
char oid[1024];
char result[RESULTS_BUFFER];
} snmp_oids_t;
/*! Poller Structure
*
* This structure holds thread polling instructions.
*
*/
/* Process-local permits use a mutex because Darwin does not implement
* unnamed POSIX semaphores. No caller needs a blocking semaphore wait. */
typedef struct spine_permits {
pthread_mutex_t mutex;
int available;
} spine_permits_t;
typedef struct poller_thread {
int device_counter;
int host_id;
int host_thread;
int host_threads;
int host_data_ids;
int threads_complete;
int complete;
int output_failed;
int poll_failed;
char host_time[40];
double host_time_double;
spine_permits_t *thread_init_sem;
} poller_thread_t;
/*! PHP Script Server Structure
*
* This structure holds status and PID information for all the running
* PHP Script Server processes.
*
*/
typedef struct php_processes {
int php_state;
pid_t php_pid;
int php_write_fd;
int php_read_fd;
} php_t;
/*! Host Structure
*
* This structure holds host information from the host table and is used throughout
* the application.
*
*/
typedef struct {
snmp_profile_t profile;
int retries;
int max_oids;
void* session;
int status;
} host_snmp_t;
typedef struct {
char snmp_sysDescr[600];
char snmp_sysObjectID[160];
unsigned long long snmp_sysUpTimeInstance;
char snmp_sysContact[300];
char snmp_sysName[300];
char snmp_sysLocation[600];
} host_system_t;
typedef struct {
int status;
int status_event_count;
char status_fail_date[40];
char status_rec_date[40];
char status_last_error[BUFSIZE * 2 + 2];
} host_state_t;
typedef struct {
double min_time;
double max_time;
double cur_time;
double avg_time;
int total_polls;
int failed_polls;
double availability;
} host_statistics_t;
typedef struct host_struct {
int id;
char hostname[250];
int ignore_host;
host_snmp_t snmp;
host_system_t system;
device_availability_t availability;
host_state_t state;
host_statistics_t statistics;
} host_t;
/*! Host Reindex Structure
*
* This structure holds the results of the host re-index checks and values.
*
*/
typedef struct host_reindex_struct {
char op[4];
char assert_value[100];
char arg1[1024];
int data_query_id;
int action;
} reindex_t;
/*! Ping Result Structure
*
* This structure holds the results of a host ping.
*
*/
typedef struct ping_results {
char hostname[BUFSIZE];
char ping_status[50];
char ping_response[BUFSIZE];
char snmp_status[50];
char snmp_response[BUFSIZE];
} ping_t;
/*! Name Result Structure
*
* This structure holds the results of a name/port split
*
*/
typedef struct name_port {
// Method = 0 - default, 1 - tcp, 2 - udp
char hostname[BUFSIZE];
int method;
int port;
} name_t;
/*! MySQL Connection Pool Structure
*
* This structure holds the mysql connection pool object.
*/
typedef struct db_connection {
int id;
int free;
MYSQL mysql;
} pool_t;
#endif /* SPINE_TYPES_H */