cacti-spine-1.2.30/0000775000175000017500000000000014770060157013027 5ustar markvmarkvcacti-spine-1.2.30/poller.h0000664000175000017500000000567714770060157014514 0ustar markvmarkv/* ex: set tabstop=4 shiftwidth=4 autoindent: +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 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, implimentation details) | +-------------------------------------------------------------------------+ | - Cacti - http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ extern void *child(void *arg); extern void child_cleanup(void *arg); extern void child_cleanup_thread(void *arg); extern void child_cleanup_script(void *arg); extern void poll_host(int device_counter, int host_id, int host_thread, int host_threads, int host_data_ids, char *host_time, int *host_errors, double host_time_double); extern char *exec_poll(host_t *current_host, char *command, int id, char *type); extern void get_system_information(host_t *host, MYSQL *mysql, int system); extern int is_multipart_output(char *result); extern int validate_result(char *result); extern void buffer_output_errors(char * error_string, int * buf_size, int * buf_errors, int device_id, int thread_id, int local_data_id, bool flush); cacti-spine-1.2.30/debug0000775000175000017500000000344214770060157014046 0ustar markvmarkv#!/bin/bash # # +-------------------------------------------------------------------------+ # | Copyright (C) 2004-2024 The Cacti Group | # | | # | 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. | # +-------------------------------------------------------------------------+ # | Cacti: The Complete RRDtool-based Graphing Solution | # +-------------------------------------------------------------------------+ # | This code is designed, written, and maintained by the Cacti Group. See | # | about.php and/or the AUTHORS file for specific developer information. | # +-------------------------------------------------------------------------+ # | http://www.cacti.net/ | # +-------------------------------------------------------------------------+ if [[ -z $SPINE_CONFIG ]]; then export SPINE_CONFIG="/etc/spine.conf"; fi make if [[ $? -eq 0 ]]; then echo echo ------ echo Debugging using SPINE_CONFIG = $SPINE_CONFIG echo echo gdb -quiet -ex run --args ./spine -R -V 6 -C $SPINE_CONFIG fi cacti-spine-1.2.30/spine.h0000664000175000017500000004340014770060157014317 0ustar markvmarkv/* ex: set tabstop=4 shiftwidth=4 autoindent: +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 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, implimentation details) | +-------------------------------------------------------------------------+ | - Cacti - http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ #ifndef _SPINE_H_ #define _SPINE_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 #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 * paramater. 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.log_level >= POLLER_VERBOSITY_LOW && spine_log format_and_args) #define SPINE_LOG_MEDIUM(format_and_args) (void)(set.log_level >= POLLER_VERBOSITY_MEDIUM && spine_log format_and_args) #define SPINE_LOG_HIGH(format_and_args) (void)(set.log_level >= POLLER_VERBOSITY_HIGH && spine_log format_and_args) #define SPINE_LOG_DEBUG(format_and_args) (void)(set.log_level >= POLLER_VERBOSITY_DEBUG && spine_log format_and_args) #define SPINE_LOG_DEVDBG(format_and_args) (void)(set.log_level >= POLLER_VERBOSITY_DEVDBG && spine_log format_and_args) /* general constants */ #define MAX_THREADS 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_SETEUID 2 #define LOCK_GHBN 3 #define LOCK_POOL 4 #define LOCK_SYSLOG 5 #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_SETEUID_O 2 #define LOCK_GHBN_O 3 #define LOCK_POOL_O 4 #define LOCK_SYSLOG_O 5 #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.log_destination) == LOGDEST_FILE || (set.log_destination) == LOGDEST_BOTH) #define IS_LOGGING_TO_SYSLOG() ((set.log_destination) == LOGDEST_SYSLOG || (set.log_destination) == LOGDEST_BOTH) #define IS_LOGGING_TO_STDOUT() ((set.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 statics */ #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 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. * */ typedef struct config_struct { /* stdout, stderr TTY protection */ int stdout_notty; int stderr_notty; /* general configuration/runtime settings */ int poller_id; int poller_id_exists; int poller_interval; int parent_fork; int num_parent_processes; int script_timeout; int active_profiles; int total_snmp_ports; int threads; int threads_set; int logfile_processed; int boost_enabled; int boost_redirect; int cygwinshloc; /* debugging options */ int snmponly; int SQL_readonly; /* host range to be poller with this spine process */ int start_host_id; int end_host_id; char host_id_list[BIG_BUFSIZE]; int has_device_0; /* database connection information */ char db_host[SMALL_BUFSIZE]; char db_db[SMALL_BUFSIZE]; char db_user[SMALL_BUFSIZE]; char db_pass[SMALL_BUFSIZE]; int db_ssl; char db_ssl_key[BIG_BUFSIZE]; char db_ssl_cert[BIG_BUFSIZE]; char db_ssl_ca[BIG_BUFSIZE]; int d_b; unsigned int db_port; char dbversion[SMALL_BUFSIZE]; int dbonupdate; int cacti_version; /* path information */ char path_logfile[DBL_BUFSIZE]; char path_php[BUFSIZE]; char path_php_server[BUFSIZE]; /* logging options */ int log_level; int log_destination; int log_perror; int log_pwarn; int log_pstats; char selective_device_debug[SMALL_BUFSIZE]; int spine_log_level; int log_datetime_separator; int log_datetime_format; /* ping settings */ int icmp_avail; int availability_method; int ping_method; int ping_retries; int ping_timeout; int ping_failure_count; int ping_recovery_count; int ping_only; /* snmp options */ int snmp_max_get_size; int snmp_retries; char snmp_clientaddr[SMALL_BUFSIZE]; int mibs; /* PHP Script Server Options */ int php_required; int php_initialized; int php_servers; int php_current_server; /* Exit code if we need it */ int exit_code; size_t exit_size; void* exit_stack[10]; /* Remote polling mode */ int mode; /* remote database connection information */ char rdb_host[SMALL_BUFSIZE]; char rdb_db[SMALL_BUFSIZE]; char rdb_user[SMALL_BUFSIZE]; char rdb_pass[SMALL_BUFSIZE]; int rdb_ssl; char rdb_ssl_key[BIG_BUFSIZE]; char rdb_ssl_cert[BIG_BUFSIZE]; char rdb_ssl_ca[BIG_BUFSIZE]; unsigned int rdb_port; char rdbversion[SMALL_BUFSIZE]; int rdbonupdate; } config_t; /*! Target Structure * * This structure holds the contents of the Poller Items table and the results * of each polling action. * */ typedef struct target_struct { int target_id; char result[RESULTS_BUFFER]; int local_data_id; int action; char command[256]; char hostname[250]; char snmp_community[100]; int snmp_version; char snmp_username[50]; char snmp_password[50]; char snmp_auth_protocol[7]; char snmp_priv_passphrase[200]; char snmp_priv_protocol[8]; char snmp_context[65]; char snmp_engine_id[30]; int snmp_port; int snmp_timeout; int availability_method; int ping_method; int ping_port; int ping_timeout; int ping_retries; char rrd_name[30]; char rrd_path[255]; int rrd_num; char arg1[512]; char arg2[255]; char arg3[255]; } 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[512]; char result[RESULTS_BUFFER]; } snmp_oids_t; /*! Poller Structure * * This structure holds thread polling instructions. * */ 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; char host_time[40]; double host_time_double; sem_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 host_struct { int id; char hostname[250]; char snmp_community[100]; int snmp_version; char snmp_username[50]; char snmp_password[50]; char snmp_auth_protocol[7]; char snmp_priv_passphrase[200]; char snmp_priv_protocol[8]; char snmp_context[65]; char snmp_engine_id[30]; int snmp_port; int snmp_timeout; int snmp_retries; 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]; int max_oids; int availability_method; int ping_method; int ping_port; int ping_timeout; int ping_retries; int status; int status_event_count; char status_fail_date[40]; char status_rec_date[40]; char status_last_error[BUFSIZE]; double min_time; double max_time; double cur_time; double avg_time; int total_polls; int failed_polls; double availability; int ignore_host; void *snmp_session; int snmp_status; } 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[512]; 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[SMALL_BUFSIZE]; char ping_status[50]; char ping_response[SMALL_BUFSIZE]; char snmp_status[50]; char snmp_response[SMALL_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[SMALL_BUFSIZE]; int method; int port; } name_t; /*! MySQL Connection Pool Structure * * This structure holds the mysql connetion pool object. */ typedef struct db_connection { int id; int free; MYSQL mysql; } pool_t; /* Include all Standard Spine Headers */ #include "poller.h" #include "locks.h" #include "keywords.h" #include "snmp.h" #include "php.h" #include "ping.h" #include "sql.h" #include "util.h" #include "nft_popen.h" #include "error.h" /* Globals */ extern config_t set; extern php_t *php_processes; extern char start_datetime[20]; extern char config_paths[CONFIG_PATHS][BUFSIZE]; extern sem_t available_threads; extern sem_t available_scripts; extern pool_t *db_pool_remote; extern pool_t *db_pool_local; #endif /* not _SPINE_H_ */ cacti-spine-1.2.30/util.c0000664000175000017500000015761714770060157014171 0ustar markvmarkv/* ex: set tabstop=4 shiftwidth=4 autoindent: +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 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, implimentation details) | +-------------------------------------------------------------------------+ | - Cacti - http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ #include "common.h" #include "spine.h" static int nopts = 0; /*! Override Options Structure * * When we fetch a setting from the database, we allow the user to override * it from the command line. These overrides are provided with the --option * parameter and stored in this table: we *use* them when the config code * reads from the DB. * * It's not an error to set an option which is unknown, but maybe should be. * */ static struct { const char *opt; const char *val; } opttable[256]; /*! \fn void set_option(const char *option, const char *value) * \brief Override spine setting from the Cacti settings table. * * Called from the command-line processing code, this provides a value * to replace any DB-stored option settings. * */ void set_option(const char *option, const char *value) { opttable[nopts ].opt = option; opttable[nopts++].val = value; } /*! \fn static const char *getsetting(MYSQL *psql, int mode, const char *setting) * \brief Returns a character pointer to a Cacti setting. * * Given a pointer to a database and the name of a setting, return the string * which represents the value from the settings table. Return NULL if we * can't find a setting for whatever reason. * * NOTE: if the user has provided one of these options on the command line, * it's intercepted here and returned, overriding the database setting. * * \return the database option setting * */ static const char *getsetting(MYSQL *psql, int mode, const char *setting) { char qstring[256]; char *retval; MYSQL_RES *result; MYSQL_ROW mysql_row; int i; assert(psql != 0); assert(setting != 0); /* see if it's in the option table */ for (i=0; i 0) { mysql_row = mysql_fetch_row(result); if (mysql_row != NULL) { retval = strdup(mysql_row[0]); db_free_result(result); return retval; }else{ return 0; } }else{ db_free_result(result); return 0; } }else{ return 0; } } /*! \fn int putsetting(MYSQL *psql, const char *setting, const char *value) * \brief Set's a specific Cacti setting. * * Given a pointer to a database and the name of a setting, and value of that setting * set the Cacti setting in the database to the value. * * \return true for sucessful or false for failed * */ int putsetting(MYSQL *psql, int mode, const char *mysetting, const char *myvalue) { char qstring[512]; int result = 0; assert(psql != 0); assert(mysetting != 0); assert(myvalue != 0); if (set.dbonupdate == 0) { sprintf(qstring, "INSERT INTO settings (name, value) " "VALUES ('%s', '%s') " "ON DUPLICATE KEY UPDATE value = VALUES(value)", mysetting, myvalue); } else { sprintf(qstring, "INSERT INTO settings (name, value) " "VALUES ('%s', '%s') AS rs " "ON DUPLICATE KEY UPDATE value = rs.value", mysetting, myvalue); } result = db_insert(psql, mode, qstring); if (result == 0) { return TRUE; } else { return FALSE; } } /*! \fn static const char *getpsetting(MYSQL *psql, const char *setting) * \brief Returns a character pointer to a Cacti poller setting. * * Given a pointer to a database and the name of a setting, * return the string which represents the value from the poller table. * Return NULL if we can't find a setting for whatever reason. * * NOTE: if the user has provided one of these options on the command line, * it's intercepted here and returned, overriding the database setting. * * \return the database option setting * */ static const char *getpsetting(MYSQL *psql, int mode, const char *setting) { char qstring[256]; char *retval; MYSQL_RES *result; MYSQL_ROW mysql_row; int i; assert(psql != 0); assert(setting != 0); /* see if it's in the option table */ for (i=0; i 0) { mysql_row = mysql_fetch_row(result); if (mysql_row != NULL) { retval = strdup(mysql_row[0]); db_free_result(result); return retval; } else { return 0; } } else { db_free_result(result); return 0; } } else { return 0; } } /*! \fn static int getboolsetting(MYSQL *psql, int mode, const char *setting, int dflt) * \brief Obtains a boolean option from the database. * * Given the parameters for fetching a setting from the database, * do so for a *Boolean* value. We parse the usual set of words * meaning true/false, and if we don't get a value, or if we don't * understand what we fetched, we use the default value provided. * * \return boolean TRUE or FALSE based upon database setting or the DEFAULT if not found */ static int getboolsetting(MYSQL *psql, int mode, const char *setting, int dflt) { const char *rc; assert(psql != 0); assert(setting != 0); rc = getsetting(psql, mode, setting); if (rc == 0) return dflt; if (STRIMATCH(rc, "on" ) || STRIMATCH(rc, "yes" ) || STRIMATCH(rc, "true") || STRIMATCH(rc, "1" ) ) { free((char *)rc); return TRUE; } if (STRIMATCH(rc, "off" ) || STRIMATCH(rc, "no" ) || STRIMATCH(rc, "false") || STRIMATCH(rc, "0" ) ) { free((char *)rc); return FALSE; } /* doesn't really match one of our keywords: what to do? */ free((char *)rc); return dflt; } /*! \fn static const char *getglobalvariable(MYSQL *psql, const char *setting) * \brief Returns a character pointer to a MySQL global variable setting. * * Given a pointer to a database and the name of a global variable, return the string * which represents that value from the settings table. Return NULL if we * can't find a variable for whatever reason. * * \return the database global variable setting * */ static const char *getglobalvariable(MYSQL *psql, int mode, const char *setting) { char qstring[256]; char *retval; MYSQL_RES *result; MYSQL_ROW mysql_row; int i; assert(psql != 0); assert(setting != 0); /* see if it's in the option table */ for (i=0; i 0) { mysql_row = mysql_fetch_row(result); if (mysql_row != NULL) { retval = strdup(mysql_row[1]); db_free_result(result); return retval; } else { return 0; } } else { db_free_result(result); return 0; } } else { return 0; } } /*! \fn int is_debug_device(int device_id) * \brief Determine if a device is a debug device * */ int is_debug_device(int device_id) { extern int *debug_devices; int i = 0; while (i < 100) { if (debug_devices[i] == '\0') break; if (debug_devices[i] == device_id) { return TRUE; } i++; } return FALSE; } /*! \fn void read_config_options(void) * \brief Reads the default Spine runtime parameters from the database and set's the global array * * load default values from the database for poller processing * */ void read_config_options() { MYSQL mysql; MYSQL mysqlr; MYSQL_RES *result; int num_rows; int mode; char web_root[BUFSIZE]; char sqlbuf[SMALL_BUFSIZE], *sqlp = sqlbuf; const char *res; char spine_capabilities[SMALL_BUFSIZE]; /* publish spine snmpv3 capabilities to the database */ memset(spine_capabilities, 0, sizeof(spine_capabilities)); db_connect(LOCAL, &mysql); if (set.poller_id > 1 && set.mode == REMOTE_ONLINE) { db_connect(REMOTE, &mysqlr); mode = REMOTE; } else { mode = LOCAL; } /* get the mysql server version */ if ((res = getglobalvariable(&mysql, LOCAL, "version")) != 0) { snprintf(set.dbversion, SMALL_BUFSIZE, "%s", res); free((char *)res); } if (STRIMATCH(set.dbversion, "mariadb")) { set.dbonupdate = 0; } else if (strpos(set.dbversion, "8.") == 0) { set.dbonupdate = 1; } else { set.dbonupdate = 0; } /* get the cacti version from the database */ set.cacti_version = get_cacti_version(&mysql, LOCAL); /* log the path_webroot variable */ SPINE_LOG_DEBUG(("DEBUG: The binary Cacti version is %d", set.cacti_version)); /* get logging level from database - overrides spine.conf */ if ((res = getsetting(&mysql, LOCAL, "log_verbosity")) != 0) { const int n = atoi(res); free((char *)res); if (n != 0) set.log_level = n; } /* determine script server path operation and default log file processing */ if ((res = getsetting(&mysql, LOCAL, "path_webroot")) != 0) { snprintf(set.path_php_server, SMALL_BUFSIZE, "%s/script_server.php", res); snprintf(web_root, BUFSIZE, "%s", res); free((char *)res); } /* determine logfile path */ if ((res = getsetting(&mysql, LOCAL, "path_cactilog")) != 0) { if (strlen(res) != 0) { snprintf(set.path_logfile, DBL_BUFSIZE, "%s", res); } else { if (strlen(web_root) != 0) { snprintf(set.path_logfile, DBL_BUFSIZE, "%s/log/cacti.log", web_root); } else { set.path_logfile[0] ='\0'; } } free((char *)res); } else { snprintf(set.path_logfile, DBL_BUFSIZE, "%s/log/cacti.log", web_root); } /* get log separator */ if ((res = getsetting(&mysql, LOCAL, "default_datechar")) != 0) { set.log_datetime_separator = atoi(res); free((char *)res); if (set.log_datetime_separator < GDC_MIN || set.log_datetime_separator > GDC_MAX) { set.log_datetime_separator = GDC_DEFAULT; } } /* get log separator */ if ((res = getsetting(&mysql, LOCAL, "default_datechar")) != 0) { set.log_datetime_separator = atoi(res); free((char *)res); if (set.log_datetime_separator < GDC_MIN || set.log_datetime_separator > GDC_MAX) { set.log_datetime_separator = GDC_DEFAULT; } } /* determine log file, syslog or both, default is 1 or log file only */ if ((res = getsetting(&mysql, LOCAL, "log_destination")) != 0) { set.log_destination = parse_logdest(res, LOGDEST_FILE); free((char *)res); } else { set.log_destination = LOGDEST_FILE; } /* log the path_webroot variable */ SPINE_LOG_DEBUG(("DEBUG: The path_php_server variable is %s", set.path_php_server)); /* log the path_cactilog variable */ SPINE_LOG_DEBUG(("DEBUG: The path_cactilog variable is %s", set.path_logfile)); /* the version variable */ SPINE_LOG_DEBUG(("DEBUG: The version variable is %s", set.dbversion)); /* log the log_destination variable */ SPINE_LOG_DEBUG(("DEBUG: The log_destination variable is %i (%s)", set.log_destination, printable_logdest(set.log_destination))); set.logfile_processed = TRUE; /* get PHP Path Information for Scripting */ if ((res = getsetting(&mysql, LOCAL, "path_php_binary")) != 0) { STRNCOPY(set.path_php, res); free((char *)res); } /* log the path_php variable */ SPINE_LOG_DEBUG(("DEBUG: The path_php variable is %s", set.path_php)); /* set availability_method */ if ((res = getsetting(&mysql, LOCAL, "availability_method")) != 0) { set.availability_method = atoi(res); free((char *)res); } /* log the availability_method variable */ SPINE_LOG_DEBUG(("DEBUG: The availability_method variable is %i", set.availability_method)); /* set ping_recovery_count */ if ((res = getsetting(&mysql, LOCAL, "ping_recovery_count")) != 0) { set.ping_recovery_count = atoi(res); free((char *)res); } /* log the ping_recovery_count variable */ SPINE_LOG_DEBUG(("DEBUG: The ping_recovery_count variable is %i", set.ping_recovery_count)); /* set ping_failure_count */ if ((res = getsetting(&mysql, LOCAL, "ping_failure_count")) != 0) { set.ping_failure_count = atoi(res); free((char *)res); } /* log the ping_failure_count variable */ SPINE_LOG_DEBUG(("DEBUG: The ping_failure_count variable is %i", set.ping_failure_count)); /* set ping_method */ if ((res = getsetting(&mysql, LOCAL, "ping_method")) != 0) { set.ping_method = atoi(res); free((char *)res); } /* log the ping_method variable */ SPINE_LOG_DEBUG(("DEBUG: The ping_method variable is %i", set.ping_method)); /* set ping_retries */ if ((res = getsetting(&mysql, LOCAL, "ping_retries")) != 0) { set.ping_retries = atoi(res); free((char *)res); } /* log the ping_retries variable */ SPINE_LOG_DEBUG(("DEBUG: The ping_retries variable is %i", set.ping_retries)); /* set ping_timeout */ if ((res = getsetting(&mysql, LOCAL, "ping_timeout")) != 0) { set.ping_timeout = atoi(res); free((char *)res); } else { set.ping_timeout = 400; } /* log the ping_timeout variable */ SPINE_LOG_DEBUG(("DEBUG: The ping_timeout variable is %i", set.ping_timeout)); /* set snmp_retries */ if ((res = getsetting(&mysql, LOCAL, "snmp_retries")) != 0) { set.snmp_retries = atoi(res); free((char *)res); } else { set.snmp_retries = 3; } /* log the snmp_retries variable */ SPINE_LOG_DEBUG(("DEBUG: The snmp_retries variable is %i", set.snmp_retries)); /* set logging option for errors */ set.log_perror = getboolsetting(&mysql, LOCAL, "log_perror", FALSE); /* log the log_perror variable */ SPINE_LOG_DEBUG(("DEBUG: The log_perror variable is %i", set.log_perror)); /* set logging option for errors */ set.log_pwarn = getboolsetting(&mysql, LOCAL, "log_pwarn", FALSE); /* log the log_pwarn variable */ SPINE_LOG_DEBUG(("DEBUG: The log_pwarn variable is %i", set.log_pwarn)); /* set option to increase insert performance */ set.boost_redirect = getboolsetting(&mysql, LOCAL, "boost_redirect", FALSE); /* log the boost_redirect variable */ SPINE_LOG_DEBUG(("DEBUG: The boost_redirect variable is %i", set.boost_redirect)); /* set option for determining if boost is enabled */ set.boost_enabled = getboolsetting(&mysql, LOCAL, "boost_rrd_update_enable", FALSE); /* log the boost_rrd_update_enable variable */ SPINE_LOG_DEBUG(("DEBUG: The boost_rrd_update_enable variable is %i", set.boost_enabled)); /* set logging option for statistics */ set.log_pstats = getboolsetting(&mysql, LOCAL, "log_pstats", FALSE); /* log the log_pstats variable */ SPINE_LOG_DEBUG(("DEBUG: The log_pstats variable is %i", set.log_pstats)); /* get Cacti defined max threads override spine.conf */ if (set.threads_set == FALSE) { if ((res = getpsetting(&mysql, mode, "threads")) != 0) { set.threads = atoi(res); free((char *)res); if (set.threads > MAX_THREADS) { set.threads = MAX_THREADS; } } } /* log the threads variable */ SPINE_LOG_DEBUG(("DEBUG: The threads variable is %i", set.threads)); /* get the poller_interval for those who have elected to go with a 1 minute polling interval */ if ((res = getsetting(&mysql, LOCAL, "poller_interval")) != 0) { set.poller_interval = atoi(res); free((char *)res); } else { set.poller_interval = 0; } /* log the poller_interval variable */ if (set.poller_interval == 0) { SPINE_LOG_DEBUG(("DEBUG: The polling interval is the system default")); } else { SPINE_LOG_DEBUG(("DEBUG: The polling interval is %i seconds", set.poller_interval)); } /* get the concurrent_processes variable to determine thread sleep values */ if ((res = getsetting(&mysql, LOCAL, "concurrent_processes")) != 0) { set.num_parent_processes = atoi(res); free((char *)res); } else { set.num_parent_processes = 1; } /* log the concurrent processes variable */ SPINE_LOG_DEBUG(("DEBUG: The number of concurrent processes is %i", set.num_parent_processes)); /* get the script timeout to establish timeouts */ if ((res = getsetting(&mysql, LOCAL, "script_timeout")) != 0) { set.script_timeout = atoi(res); free((char *)res); if (set.script_timeout < 5) { set.script_timeout = 5; } } else { set.script_timeout = 25; } /* log the script timeout value */ SPINE_LOG_DEBUG(("DEBUG: The script timeout is %i", set.script_timeout)); /* get selective_device_debug string */ if ((res = getsetting(&mysql, LOCAL, "selective_device_debug")) != 0) { STRNCOPY(set.selective_device_debug, res); free((char *)res); } /* log the selective_device_debug variable */ SPINE_LOG_DEBUG(("DEBUG: The selective_device_debug variable is %s", set.selective_device_debug)); /* get spine_log_level */ if ((res = getsetting(&mysql, LOCAL, "spine_log_level")) != 0) { set.spine_log_level = atoi(res); free((char *)res); } /* log the spine_log_level variable */ SPINE_LOG_DEBUG(("DEBUG: The spine_log_level variable is %i", set.spine_log_level)); /* get the number of script server processes to run */ if ((res = getsetting(&mysql, LOCAL, "php_servers")) != 0) { set.php_servers = atoi(res); free((char *)res); if (set.php_servers > MAX_PHP_SERVERS) { set.php_servers = MAX_PHP_SERVERS; } if (set.php_servers <= 0) { set.php_servers = 1; } } else { set.php_servers = 2; } /* log the script timeout value */ SPINE_LOG_DEBUG(("DEBUG: The number of php script servers to run is %i", set.php_servers)); /* get the number of active profiles on the system run */ if ((res = getsetting(&mysql, LOCAL, "active_profiles")) != 0) { set.active_profiles = atoi(res); free((char *)res); if (set.active_profiles <= 0) { set.active_profiles = 0; } } else { set.active_profiles = 0; } /* log the script timeout value */ SPINE_LOG_DEBUG(("DEBUG: The number of active data source profiles is %i", set.active_profiles)); /* get the number of snmp_ports in use */ if ((res = getsetting(&mysql, LOCAL, "total_snmp_ports")) != 0) { set.total_snmp_ports = atoi(res); free((char *)res); if (set.total_snmp_ports <= 0) { set.total_snmp_ports = 0; } } else { set.total_snmp_ports = 0; } /* log the script timeout value */ SPINE_LOG_DEBUG(("DEBUG: The number of snmp ports on the system is %i", set.total_snmp_ports)); /*---------------------------------------------------------------- * determine if the php script server is required by searching for * all the host records for an action of POLLER_ACTION_PHP_SCRIPT_SERVER. * If we get even one, it means we have to deal with the PHP script * server. * */ set.php_required = FALSE; /* assume no */ /* log the requirement for the script server */ if (!strlen(set.host_id_list)) { sqlp = sqlbuf; sqlp += sprintf(sqlp, "SELECT SQL_NO_CACHE action FROM poller_item"); sqlp += sprintf(sqlp, " WHERE action=%d", POLLER_ACTION_PHP_SCRIPT_SERVER); sqlp += append_hostrange(sqlp, "host_id"); if (set.poller_id_exists) { sqlp += sprintf(sqlp, " AND poller_id=%i", set.poller_id); } sqlp += sprintf(sqlp, " LIMIT 1"); result = db_query(&mysql, LOCAL, sqlbuf); num_rows = mysql_num_rows(result); db_free_result(result); if (num_rows > 0) set.php_required = TRUE; SPINE_LOG_DEBUG(("DEBUG: StartDevice='%i', EndDevice='%i', TotalPHPScripts='%i'", set.start_host_id, set.end_host_id, num_rows)); } else { sqlp = sqlbuf; sqlp += sprintf(sqlp, "SELECT SQL_NO_CACHE action FROM poller_item"); sqlp += sprintf(sqlp, " WHERE action=%d", POLLER_ACTION_PHP_SCRIPT_SERVER); sqlp += sprintf(sqlp, " AND host_id IN(%s)", set.host_id_list); if (set.poller_id_exists) { sqlp += sprintf(sqlp, " AND poller_id=%i", set.poller_id); } sqlp += sprintf(sqlp, " LIMIT 1"); result = db_query(&mysql, LOCAL, sqlbuf); num_rows = mysql_num_rows(result); db_free_result(result); if (num_rows > 0) set.php_required = TRUE; SPINE_LOG_DEBUG(("DEBUG: Device List to be polled='%s', TotalPHPScripts='%i'", set.host_id_list, num_rows)); } SPINE_LOG_DEBUG(("DEBUG: The PHP Script Server is %sRequired", set.php_required ? "" : "Not ")); /* determine the maximum oid's to obtain in a single get request */ if ((res = getsetting(&mysql, LOCAL, "max_get_size")) != 0) { set.snmp_max_get_size = atoi(res); free((char *)res); if (set.snmp_max_get_size > 128) { set.snmp_max_get_size = 128; } } else { set.snmp_max_get_size = 25; } /* log the snmp_max_get_size variable */ SPINE_LOG_DEBUG(("DEBUG: The Maximum SNMP OID Get Size is %i", set.snmp_max_get_size)); strcat(spine_capabilities, "{ authProtocols: \""); #ifndef NETSNMP_DISABLE_MD5 strcat(spine_capabilities, "MD5,"); #endif strcat(spine_capabilities, "SHA"); #if defined(HAVE_EVP_SHA224) && defined(usmHMAC192SHA256AuthProtocol) strcat(spine_capabilities, ",SHA224,SHA256"); #endif #if defined(HAVE_EVP_SHA384) && defined(usmHMAC384SHA512AuthProtocol) strcat(spine_capabilities, ",SHA384,SHA512"); #endif strcat(spine_capabilities, "\""); strcat(spine_capabilities, ", privProtocols: \""); #ifndef NETSNMP_DISABLE_DES strcat(spine_capabilities, "DES"); #endif #ifdef HAVE_AES strcat(spine_capabilities, ",AES128"); #if defined(NETSNMP_DRAFT_BLUMENTHAL_AES_04) && defined(usmAES192PrivProtocol) strcat(spine_capabilities, ",AES192"); #endif #if defined(NETSNMP_DRAFT_BLUMENTHAL_AES_04) && defined(usmAES256PrivProtocol) strcat(spine_capabilities, ",AES256"); #endif #endif strcat(spine_capabilities, "\" }"); if (set.poller_id == 1) { putsetting(&mysql, LOCAL, "spine_capabilities", spine_capabilities); } db_disconnect(&mysql); if (set.poller_id > 1 && set.mode == REMOTE_ONLINE) { db_disconnect(&mysqlr); } } void poller_push_data_to_main() { MYSQL mysql; MYSQL mysqlr; MYSQL_RES *result; MYSQL_ROW row; int num_rows; int rows; char sqlbuf[HUGE_BUFSIZE]; char *sqlp = sqlbuf; char query[MEGA_BUFSIZE]; char prefix[BUFSIZE]; char suffix[BUFSIZE]; // tmpstr needs to be greater than 2 * the maximum column size being processed below char tmpstr[DBL_BUFSIZE]; db_connect(LOCAL, &mysql); db_connect(REMOTE, &mysqlr); /* Since MySQL 5.7 the sql_mode defaults are too strict for cacti */ db_insert(&mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'NO_ZERO_DATE', ''))"); db_insert(&mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY', ''))"); db_insert(&mysqlr, REMOTE, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'NO_ZERO_DATE', ''))"); db_insert(&mysqlr, REMOTE, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY', ''))"); SPINE_LOG_MEDIUM(("Pushing Host Status to Main Server")); if (strlen(set.host_id_list)) { snprintf(query, MEGA_BUFSIZE, "SELECT SQL_NO_CACHE id, snmp_sysDescr, snmp_sysObjectID, " "snmp_sysUpTimeInstance, snmp_sysContact, snmp_sysName, snmp_sysLocation, " "status, status_event_count, status_fail_date, status_rec_date, " "status_last_error, min_time, max_time, cur_time, avg_time, polling_time, " "total_polls, failed_polls, availability, last_updated " "FROM host " "WHERE poller_id = %d " "AND id IN (%s)", set.poller_id, set.host_id_list); } else { snprintf(query, MEGA_BUFSIZE, "SELECT SQL_NO_CACHE id, snmp_sysDescr, snmp_sysObjectID, " "snmp_sysUpTimeInstance, snmp_sysContact, snmp_sysName, snmp_sysLocation, " "status, status_event_count, status_fail_date, status_rec_date, " "status_last_error, min_time, max_time, cur_time, avg_time, polling_time, " "total_polls, failed_polls, availability, last_updated " "FROM host " "WHERE poller_id = %d", set.poller_id); } snprintf(prefix, BUFSIZE, "INSERT INTO host (id, snmp_sysDescr, snmp_sysObjectID, " "snmp_sysUpTimeInstance, snmp_sysContact, snmp_sysName, snmp_sysLocation, " "status, status_event_count, status_fail_date, status_rec_date, " "status_last_error, min_time, max_time, cur_time, avg_time, polling_time, " "total_polls, failed_polls, availability, last_updated) VALUES "); if (set.dbonupdate == 0) { snprintf(suffix, BUFSIZE, " ON DUPLICATE KEY UPDATE " "snmp_sysDescr=VALUES(snmp_sysDescr), " "snmp_sysObjectID=VALUES(snmp_sysObjectID), " "snmp_sysUpTimeInstance=VALUES(snmp_sysUpTimeInstance), " "snmp_sysContact=VALUES(snmp_sysContact), " "snmp_sysName=VALUES(snmp_sysName), " "snmp_sysLocation=VALUES(snmp_sysLocation), " "status=VALUES(status), " "status_event_count=VALUES(status_event_count), " "status_fail_date=VALUES(status_fail_date), " "status_rec_date=VALUES(status_rec_date), " "status_last_error=VALUES(status_last_error), " "min_time=VALUES(min_time), " "max_time=VALUES(max_time), " "cur_time=VALUES(cur_time), " "avg_time=VALUES(avg_time), " "polling_time=VALUES(polling_time), " "total_polls=VALUES(total_polls), " "failed_polls=VALUES(failed_polls), " "availability=VALUES(availability), " "last_updated=VALUES(last_updated)"); } else { snprintf(suffix, BUFSIZE, " AS rs ON DUPLICATE KEY UPDATE " "snmp_sysDescr=rs.snmp_sysDescr, " "snmp_sysObjectID=rs.snmp_sysObjectID, " "snmp_sysUpTimeInstance=rs.snmp_sysUpTimeInstance, " "snmp_sysContact=rs.snmp_sysContact, " "snmp_sysName=rs.snmp_sysName, " "snmp_sysLocation=rs.snmp_sysLocation, " "status=rs.status, " "status_event_count=rs.status_event_count, " "status_fail_date=rs.status_fail_date, " "status_rec_date=rs.status_rec_date, " "status_last_error=rs.status_last_error, " "min_time=rs.min_time, " "max_time=rs.max_time, " "cur_time=rs.cur_time, " "avg_time=rs.avg_time, " "polling_time=rs.polling_time, " "total_polls=rs.total_polls, " "failed_polls=rs.failed_polls, " "availability=rs.availability, " "last_updated=rs.last_updated"); } if ((result = db_query(&mysql, LOCAL, query)) != 0) { num_rows = mysql_num_rows(result); rows = 0; if (num_rows > 0) { while ((row = mysql_fetch_row(result))) { if (rows < 500) { if (rows == 0) { sqlp = sqlbuf; sqlp += sprintf(sqlp, "%s", prefix); sqlp += sprintf(sqlp, " ("); } else { sqlp += sprintf(sqlp, ", ("); } sqlp += sprintf(sqlp, "%s, ", row[0]); // id mediumint db_escape(&mysql, tmpstr, sizeof(tmpstr), row[1]); // snmp_sysDescr varchar(300) sqlp += sprintf(sqlp, "'%s', ", tmpstr); db_escape(&mysql, tmpstr, sizeof(tmpstr), row[2]); // snmp_sysObjectID varchar(128) sqlp += sprintf(sqlp, "'%s', ", tmpstr); db_escape(&mysql, tmpstr, sizeof(tmpstr), row[3]); // snmp_sysUpTimeInstance bigint sqlp += sprintf(sqlp, "'%s', ", tmpstr); db_escape(&mysql, tmpstr, sizeof(tmpstr), row[4]); // snmp_sysContact varchar(300) sqlp += sprintf(sqlp, "'%s', ", tmpstr); db_escape(&mysql, tmpstr, sizeof(tmpstr), row[5]); // snmp_sysName varchar(300) sqlp += sprintf(sqlp, "'%s', ", tmpstr); db_escape(&mysql, tmpstr, sizeof(tmpstr), row[6]); // snmp_sysLocation varchar(300) sqlp += sprintf(sqlp, "'%s', ", tmpstr); db_escape(&mysql, tmpstr, sizeof(tmpstr), row[7]); // status tinyint sqlp += sprintf(sqlp, "'%s', ", tmpstr); sqlp += sprintf(sqlp, "%s, ", row[8]); // status_event_count mediumint db_escape(&mysql, tmpstr, sizeof(tmpstr), row[9]); // status_fail_date timestamp sqlp += sprintf(sqlp, "'%s', ", tmpstr); db_escape(&mysql, tmpstr, sizeof(tmpstr), row[10]); // status_rec_date timestamp sqlp += sprintf(sqlp, "'%s', ", tmpstr); db_escape(&mysql, tmpstr, sizeof(tmpstr), row[11]); // status_last_error varchar(255) sqlp += sprintf(sqlp, "'%s', ", tmpstr); sqlp += sprintf(sqlp, "%s, ", row[12]); // min_time decimal(10,5) sqlp += sprintf(sqlp, "%s, ", row[13]); // max_time decimal(10,5) sqlp += sprintf(sqlp, "%s, ", row[14]); // cur_time decimal(10,5) sqlp += sprintf(sqlp, "%s, ", row[15]); // avg_time decimal(10,5) sqlp += sprintf(sqlp, "%s, ", row[16]); // polling_time double sqlp += sprintf(sqlp, "%s, ", row[17]); // total_polls int sqlp += sprintf(sqlp, "%s, ", row[18]); // failed_polls int sqlp += sprintf(sqlp, "%s, ", row[19]); // availability decimal(8,5) db_escape(&mysql, tmpstr, sizeof(tmpstr), row[20]); // last_updated timestamp sqlp += sprintf(sqlp, "'%s'", tmpstr); sqlp += sprintf(sqlp, ")"); rows++; } else { sqlp += sprintf(sqlp, "%s", suffix); db_insert(&mysqlr, REMOTE, sqlbuf); rows = 0; } } } if (rows > 0) { sqlp += sprintf(sqlp, "%s", suffix); db_insert(&mysqlr, REMOTE, sqlbuf); } } db_free_result(result); SPINE_LOG_MEDIUM(("Pushing Poller Item RRD Next Step to Main Server")); if (strlen(set.host_id_list)) { snprintf(query, MEGA_BUFSIZE, "SELECT SQL_NO_CACHE local_data_id, host_id, rrd_name, rrd_step, rrd_next_step " "FROM poller_item " "WHERE poller_id = %d " "AND host_id IN (%s)", set.poller_id, set.host_id_list); } else { snprintf(query, MEGA_BUFSIZE, "SELECT SQL_NO_CACHE local_data_id, host_id, rrd_name, rrd_step, rrd_next_step " "FROM poller_item " "WHERE poller_id = %d ", set.poller_id); } snprintf(prefix, BUFSIZE, "INSERT INTO poller_item (local_data_id, host_id, rrd_name, rrd_step, rrd_next_step) VALUES "); if (set.dbonupdate == 0) { snprintf(suffix, BUFSIZE, " ON DUPLICATE KEY UPDATE " "rrd_next_step=VALUES(rrd_next_step)"); } else { snprintf(suffix, BUFSIZE, " AS rs ON DUPLICATE KEY UPDATE " "rrd_next_step=rs.rrd_next_step"); } if ((result = db_query(&mysql, LOCAL, query)) != 0) { num_rows = mysql_num_rows(result); rows = 0; if (num_rows > 0) { while ((row = mysql_fetch_row(result))) { if (rows < 10000) { if (rows == 0) { sqlp = sqlbuf; sqlp += sprintf(sqlp, "%s", prefix); sqlp += sprintf(sqlp, " ("); } else { sqlp += sprintf(sqlp, ", ("); } sqlp += sprintf(sqlp, "%s, ", row[0]); // local_data_id sqlp += sprintf(sqlp, "%s, ", row[1]); // host_id db_escape(&mysql, tmpstr, sizeof(tmpstr), row[2]); // rrd_name sqlp += sprintf(sqlp, "'%s', ", tmpstr); sqlp += sprintf(sqlp, "%s, ", row[3]); // rrd_step sqlp += sprintf(sqlp, "%s", row[4]); // rrd_next_step sqlp += sprintf(sqlp, ")"); rows++; } else { sqlp += sprintf(sqlp, "%s", suffix); db_insert(&mysqlr, REMOTE, sqlbuf); rows = 0; } } } if (rows > 0) { sqlp += sprintf(sqlp, "%s", suffix); db_insert(&mysqlr, REMOTE, sqlbuf); rows = 0; } } db_free_result(result); db_disconnect(&mysql); db_disconnect(&mysqlr); } /*! \fn int read_spine_config(char *file) * \brief obtain default startup variables from the spine.conf file. * \param file the spine config file * * \return 0 if successful or -1 if the file could not be opened */ int read_spine_config(char *file) { FILE *fp; char buff[BUFSIZE]; char *buffer; char p1[BUFSIZE]; char p2[BUFSIZE]; if ((fp = fopen(file, "rb")) == NULL) { if (set.log_level == POLLER_VERBOSITY_DEBUG) { if (!set.stderr_notty) { fprintf(stderr, "ERROR: Could not open config file [%s]\n", file); } } return -1; } else { if (!set.stdout_notty) { fprintf(stdout, "SPINE: Using spine config file [%s]\n", file); } while (!feof(fp)) { buffer = fgets(buff, BUFSIZE, fp); if (!feof(fp) && *buff != '#' && *buff != ' ' && *buff != '\n') { sscanf(buff, "%15s %255s", p1, p2); if (STRIMATCH(p1, "RDB_Host")) STRNCOPY(set.rdb_host, p2); else if (STRIMATCH(p1, "RDB_Database")) STRNCOPY(set.rdb_db, p2); else if (STRIMATCH(p1, "RDB_User")) STRNCOPY(set.rdb_user, p2); else if (STRIMATCH(p1, "RDB_Pass")) STRNCOPY(set.rdb_pass, p2); else if (STRIMATCH(p1, "RDB_Port")) set.rdb_port = atoi(p2); else if (STRIMATCH(p1, "RDB_UseSSL")) set.rdb_ssl = atoi(p2); else if (STRIMATCH(p1, "RDB_SSL_Key")) STRNCOPY(set.rdb_ssl_key, p2); else if (STRIMATCH(p1, "RDB_SSL_Cert")) STRNCOPY(set.rdb_ssl_cert, p2); else if (STRIMATCH(p1, "RDB_SSL_CA")) STRNCOPY(set.rdb_ssl_ca, p2); else if (STRIMATCH(p1, "DB_Host")) STRNCOPY(set.db_host, p2); else if (STRIMATCH(p1, "DB_Database")) STRNCOPY(set.db_db, p2); else if (STRIMATCH(p1, "DB_User")) STRNCOPY(set.db_user, p2); else if (STRIMATCH(p1, "DB_Pass")) STRNCOPY(set.db_pass, p2); else if (STRIMATCH(p1, "DB_Port")) set.db_port = atoi(p2); else if (STRIMATCH(p1, "DB_UseSSL")) set.db_ssl = atoi(p2); else if (STRIMATCH(p1, "DB_SSL_Key")) STRNCOPY(set.db_ssl_key, p2); else if (STRIMATCH(p1, "DB_SSL_Cert")) STRNCOPY(set.db_ssl_cert, p2); else if (STRIMATCH(p1, "DB_SSL_CA")) STRNCOPY(set.db_ssl_ca, p2); else if (STRIMATCH(p1, "Poller")) set.poller_id = atoi(p2); else if (STRIMATCH(p1, "DB_PreG")) { if (!set.stderr_notty) { fprintf(stderr,"WARNING: DB_PreG is no longer supported\n"); } } else if (STRIMATCH(p1, "Cacti_Log")) { STRNCOPY(set.path_logfile, p2); set.logfile_processed = 1; set.log_destination = LOGDEST_BOTH; } else if (STRIMATCH(p1, "SNMP_Clientaddr")) STRNCOPY(set.snmp_clientaddr, p2); else if (!set.stderr_notty) { fprintf(stderr,"WARNING: Unrecongized directive: %s=%s in %s\n", p1, p2, file); } *p1 = '\0'; *p2 = '\0'; } } if (strlen(set.db_pass) == 0) *set.db_pass = '\0'; return 0; } } /*! \fn void config_defaults(void) * \brief populates the global configuration structure with default spine.conf file settings * \param *set global runtime parameters * */ void config_defaults() { set.threads = DEFAULT_THREADS; /* default server */ set.db_port = DEFAULT_DB_PORT; STRNCOPY(set.db_host, DEFAULT_DB_HOST); STRNCOPY(set.db_db, DEFAULT_DB_DB ); STRNCOPY(set.db_user, DEFAULT_DB_USER); STRNCOPY(set.db_pass, DEFAULT_DB_PASS); /* remote default server */ set.rdb_port = DEFAULT_DB_PORT; STRNCOPY(set.rdb_host, DEFAULT_DB_HOST); STRNCOPY(set.rdb_db, DEFAULT_DB_DB ); STRNCOPY(set.rdb_user, DEFAULT_DB_USER); STRNCOPY(set.rdb_pass, DEFAULT_DB_PASS); STRNCOPY(config_paths[0], CONFIG_PATH_1); STRNCOPY(config_paths[1], CONFIG_PATH_2); STRNCOPY(config_paths[2], CONFIG_PATH_3); STRNCOPY(config_paths[3], CONFIG_PATH_4); set.log_destination = LOGDEST_FILE; } /*! \fn void die(const char *format, ...) * \brief a method to end Spine while returning the fatal error to stderr * * Given a printf-style argument list, format it to the standard * error, append a newline, then exit Spine. * */ void die(const char *format, ...) { va_list args; char logmessage[BUFSIZE]; char flogmessage[DBL_BUFSIZE]; int old_errno = errno; va_start(args, format); vsprintf(logmessage, format, args); va_end(args); if (set.log_perror) { char perr[BUFSIZE]; snprintf(perr, BUFSIZE, " [%d, %s]", old_errno, strerror(old_errno)); strcat(logmessage,perr); } if (set.logfile_processed) { if (set.parent_fork == SPINE_PARENT) { snprintf(flogmessage, DBL_BUFSIZE, "%s (Spine parent)", logmessage); } else { snprintf(flogmessage, DBL_BUFSIZE, "%s (Spine thread)", logmessage); } } else { snprintf(flogmessage, DBL_BUFSIZE, "%s (Spine init)", logmessage); } fprintf(stderr, "%s", flogmessage); if (set.parent_fork == SPINE_PARENT) { if (set.php_initialized) { php_close(PHP_INIT); } } exit(set.exit_code); } char * get_date_format() { char *log_fmt; if (!(log_fmt = (char *) malloc(GD_FMT_SIZE))) { die("ERROR: Fatal malloc error: util.c get_date_format!"); } char log_sep = '/'; if (set.log_datetime_separator < GDC_MIN || set.log_datetime_separator > GDC_MAX) { set.log_datetime_separator = GDC_DEFAULT; } if (set.log_datetime_format < GD_MIN || set.log_datetime_format > GD_MAX) { set.log_datetime_format = GD_DEFAULT; } switch (set.log_datetime_separator) { case GDC_DOT: log_sep = '.'; break; case GDC_HYPHEN: log_sep = '-'; break; default: log_sep = '/'; break; } switch (set.log_datetime_format) { case GD_MO_D_Y: snprintf(log_fmt, GD_FMT_SIZE, "%%m%c%%d%c%%Y %%H:%%M:%%S - ", log_sep, log_sep); case GD_MN_D_Y: snprintf(log_fmt, GD_FMT_SIZE, "%%b%c%%d%c%%Y %%H:%%M:%%S - ", log_sep, log_sep); case GD_D_MO_Y: snprintf(log_fmt, GD_FMT_SIZE, "%%d%c%%m%c%%Y %%H:%%M:%%S - ", log_sep, log_sep); case GD_D_MN_Y: snprintf(log_fmt, GD_FMT_SIZE, "%%d%c%%b%c%%Y %%H:%%M:%%S - ", log_sep, log_sep); case GD_Y_MO_D: snprintf(log_fmt, GD_FMT_SIZE, "%%Y%c%%m%c%%d %%H:%%M:%%S - ", log_sep, log_sep); case GD_Y_MN_D: snprintf(log_fmt, GD_FMT_SIZE, "%%Y%c%%b%c%%d %%H:%%M:%%S - ", log_sep, log_sep); default: snprintf(log_fmt, GD_FMT_SIZE, "%%Y%c%%m%c%%d %%H:%%M:%%S - ", log_sep, log_sep); } return (log_fmt); } /*! \fn void spine_log(const char *format, ...) * \brief output's log information to the desired cacti logfile. * \param *logmessage a pointer to the pre-formated log message. * */ int spine_log(const char *format, ...) { va_list args; FILE *log_file = NULL; FILE *fp = NULL; /* variables for time display */ time_t nowbin; struct tm now_time; struct tm *now_ptr; struct timeval now; /* keep track of an errored log file */ static int log_error = FALSE; char logprefix[SMALL_BUFSIZE]; /* Formatted Log Prefix */ char ulogmessage[LOGSIZE]; /* Un-Formatted Log Message */ char flogmessage[LOGSIZE]; /* Formatted Log Message */ char stdoutmessage[LRG_LOGSIZE]; /* Message for stdout */ double cur_time; va_start(args, format); vsnprintf(ulogmessage, LOGSIZE - 1, format, args); va_end(args); /* default for "console" messages to go to stdout */ fp = stdout; /* log message prefix */ snprintf(logprefix, SMALL_BUFSIZE, "SPINE: Poller[%i] PID[%i] PT[%ld] ", set.poller_id, getpid(), (unsigned long int)pthread_self()); /* get time for poller_output table */ nowbin = time(&nowbin); localtime_r(&nowbin,&now_time); now_ptr = &now_time; if (IS_LOGGING_TO_STDOUT()) { cur_time = get_time_as_double(); sprintf(stdoutmessage, "Total[%3.4f] %s", cur_time - start_time, ulogmessage); puts(stdoutmessage); return TRUE; } char * log_fmt = get_date_format(); if (strlen(log_fmt) == 0) { #ifdef DISABLE_STDERR fp = stdout; #else fp = stderr; #endif if ((set.stderr_notty) && (fp == stderr)) { /* do nothing stderr does not exist */ } else if ((set.stdout_notty) && (fp == stdout)) { /* do nothing stdout does not exist */ } else { fprintf(fp, "ERROR: Could not get format from get_date_format()\n"); } } if (strftime(flogmessage, 50, log_fmt, now_ptr) == (size_t) 0) { #ifdef DISABLE_STDERR fp = stdout; #else fp = stderr; #endif if ((set.stderr_notty) && (fp == stderr)) { /* do nothing stderr does not exist */ } else if ((set.stdout_notty) && (fp == stdout)) { /* do nothing stdout does not exist */ } else { fprintf(fp, "ERROR: Could not get string from strftime()\n"); } } strncat(flogmessage, logprefix, sizeof(flogmessage) - 1); strncat(flogmessage, ulogmessage, sizeof(flogmessage) - 50); /* output to syslog/eventlog */ if (IS_LOGGING_TO_SYSLOG()) { thread_mutex_lock(LOCK_SYSLOG); openlog("Cacti", LOG_NDELAY | LOG_PID, LOG_SYSLOG); if ((strstr(flogmessage,"ERROR") || (strstr(flogmessage, "FATAL"))) && (set.log_perror)) { syslog(LOG_CRIT,"%s\n", flogmessage); } if ((strstr(flogmessage,"WARNING")) && (set.log_pwarn)){ syslog(LOG_WARNING,"%s\n", flogmessage); } if ((strstr(flogmessage,"STATS")) && (set.log_pstats)){ syslog(LOG_NOTICE,"%s\n", flogmessage); } closelog(); thread_mutex_unlock(LOCK_SYSLOG); } /* append a line feed to the log message if needed */ if (!strstr(flogmessage, "\n")) { strcat(flogmessage, "\n"); } if ((IS_LOGGING_TO_FILE() && (set.log_level != POLLER_VERBOSITY_NONE) && (strlen(set.path_logfile) != 0))) { if (set.logfile_processed) { if (!file_exists(set.path_logfile)) { log_file = fopen(set.path_logfile, "w"); } else { log_file = fopen(set.path_logfile, "a"); } if (log_file) { fputs(flogmessage, log_file); fclose(log_file); } else { if (!log_error) { printf("ERROR: Spine Log File Could Not Be Opened/Created\n"); log_error = TRUE; } } } } if (set.log_level >= POLLER_VERBOSITY_NONE) { if ((strstr(flogmessage,"ERROR")) || (strstr(flogmessage,"WARNING")) || (strstr(flogmessage,"FATAL"))) { #ifdef DISABLE_STDERR fp = stdout; #else fp = stderr; #endif } if ((set.stderr_notty) && (fp == stderr)) { /* do nothing stderr does not exist */ } else if ((set.stdout_notty) && (fp == stdout)) { /* do nothing stdout does not exist */ } else { fprintf(fp, "%s", flogmessage); } } free(log_fmt); return TRUE; } /*! \fn int file_exists(const char *filename) * \brief checks for the existance of a file. * \param *filename the name of the file to check for. * * \return TRUE if found FALSE if not. * */ int file_exists(const char *filename) { struct stat file_stat; if (stat(filename, &file_stat)) { return FALSE; } else { return TRUE; } } /*! \fn all_digits(const char *string) * \brief verifies that a string is contains only numeric characters * \param string the string to check * * This function has no leeway: spaces and minus signs and decimal points * are not digits, and an empty string is (by convention) not * all-digits too. * * \return TRUE if not alpha or special characters found, FALSE if non numeric found * */ int all_digits(const char *string) { /* empty string is not all digits */ if ( *string == '\0' ) return FALSE; while ( isdigit((int)*string) ) string++; return *string == '\0'; } /*! \fn is_ipaddress(const char *string) * \brief verifies that a string is an ip address either v4 or v6 * \param string the string to check * * This function simply checks to see if a string object is an ip address. * If it is, it returns true else false. * * \return TRUE if an ip address, or FALSE if non * */ int is_ipaddress(const char *string) { while (*string) { if ((isdigit((int)*string)) || (*string == '.') || (*string == ':')) { string++; continue; } return FALSE; } return TRUE; } /*! \fn int is_numeric(const char *string) * \brief check to see if a string is long or double * \param string the string to check * * \return TRUE if long or double, FALSE if not * */ int is_numeric(char *string) { long local_lval; double local_dval; char *end_ptr_long, *end_ptr_double; int conv_base=10; int length; length = strlen(trim(string)); if (!length) { return FALSE; } /* check for an integer */ errno = 0; local_lval = strtol(string, &end_ptr_long, conv_base); if (errno != ERANGE) { if (end_ptr_long == string + length) { /* integer string */ return TRUE; } else if (end_ptr_long == string) { if (*end_ptr_long != '\0' && *end_ptr_long != '.' && *end_ptr_long != '-' && *end_ptr_long != '+') { /* ignore partial string matches but doubles can begin with '+', '-', '.' */ return FALSE; } } } else { end_ptr_long = NULL; } /* check for a float */ errno = 0; local_dval = strtod(string, &end_ptr_double); if (errno != ERANGE) { if (end_ptr_double == string + length) { /* floating point string */ return TRUE; } } else { end_ptr_double = NULL; } return FALSE; } /*! \fn int is_hexadecimal(const char *str, const short ignore_space) * \brief test whether a string represents a hex number. * \param str string to test * \param ignore_space nonzero to skip tabs and spaces * * \return TRUE if the string is valid hex, FALSE otherwise * * The function is modified where the string needs to include * at least one of the following string ' ', '-', or ':' * */ int is_hexadecimal(const char * str, const short ignore_special) { int i = 0; int delim_found = FALSE; if (!str) return FALSE; while (*str) { switch (*str) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'a': case 'A': case 'b': case 'B': case 'c': case 'C': case 'd': case 'D': case 'e': case 'E': case 'f': case 'F': case '"': break; case '-': case ':': case ' ': delim_found = TRUE; break; case '\t': if (ignore_special) { break; } default: return FALSE; } str++; i++; } if ((i < 3) || delim_found == FALSE) { return FALSE; } return TRUE; } /*! \fn char *strip_alpha(char *string) * \brief remove trailing alpha characters from a string. * \param string the string to strip characters from * * \return a pointer to the modified string * */ char *strip_alpha(char *string) { int i; int j; i = strlen(string); j = 0; /* trim trailing characters */ while (i >= 0) { if (isdigit((int)string[i])) { break; } else { string[i] = '\0'; } i--; } /* trim leading characters */ while (j < i) { if (isdigit((int)string[j])) { break; } else if (string[j] == '-') { break; } else if (string[j] == '+') { j++; } else { j++; } } string = &string[j]; return string; } /*! \fn char *add_slashes(char *string) * \brief add escaping to back slashes on for Windows type commands. * \param string the string to replace slashes * * \return a pointer to the modified string. Variable must be freed by parent. * */ char *add_slashes(char *string) { int length; int position; int new_position; char *return_str; if (!(return_str = (char *) malloc(BUFSIZE))) { die("ERROR: Fatal malloc error: util.c add_slashes!"); } return_str[0] = '\0'; length = strlen(string); position = 0; new_position = 0; /* simply return on blank string */ if (!length) { return return_str; } while (position < length) { /* backslash detected, change to forward slash */ if (string[position] == '\\') { return_str[new_position] = '\\'; new_position++; return_str[new_position] = '\\'; } else { return_str[new_position] = string[position]; } new_position++; position++; } return_str[new_position] = '\0'; return(return_str); } /*! \fn char *strncopy(char *dst, const char *src, size_t obuf) * \brief copies source to destination add a NUL terminator * * Copy from source to destination, insuring a NUL termination. * The size of the buffer *includes* the terminating NUL. Note * that strncpy() does NOT NUL terminate if the source is the * size of the destination (yuck). * * NOTE: it's very common to call this as: * * strncopy(buf, src, sizeof buf) * * so we provide an STRNCOPY() macro which adds the size. * * \return pointer to destination string * */ #pragma GCC diagnostic push #if (defined(__GNUC__) && (__GNUC__ > 7)) || (__GNUC__ == 7 && defined(__GNUC_MINOR__) && __GNUC_MINOR__ > 1) #pragma GCC diagnostic ignored "-Wstringop-overflow" #endif char *strncopy(char *dst, const char *src, size_t obuf) { assert(dst != 0); assert(src != 0); size_t len; len = (strlen(src) < obuf) ? strlen(src) : obuf; if (len) { strncpy(dst, src, len); } dst[len] = '\0'; return dst; } #pragma GCC diagnostic pop /*! \fn double get_time_as_double() * \brief fetches system time as a double-precison value * * \return system time (at microsecond resolution) as a double */ double get_time_as_double(void) { struct timeval now; gettimeofday(&now, NULL); return (now).tv_sec + ((double) (now).tv_usec / 1000000); } /*! \fn trim() * \brief removes leading and trailing blanks, tabs, line feeds and * carriage returns from a string. * * \return the trimmed string. */ char *trim(char *str) { return ltrim(rtrim(str)); } /*! \fn rtrim() * \brief removes trailing blanks, tabs, line feeds, carriage returns * single and double quotes and back-slashed from a string. * * \return the trimmed string. */ char *rtrim(char *str) { char *end; char *trim = " \"\'\\\t\n\r"; if (!str) return NULL; end = str + strlen(str); while (end-- > str) { if (!strchr(trim, *end)) return str; *end = 0; } return str; } /*! \fn ltrim() * \brief removes leading blanks, tabs, line feeds, carriage returns * single and double quotes and back-slashed from a string. * * \return the trimmed string. */ char *ltrim(char *str) { char *trim = " \"\'\\\t\n\r"; if (!str) return NULL; while (*str) { if (!strchr(trim, *str)) return str; ++str; } return str; } /*! \fn reverse() * \brief reverses a string in place. * * \return the reversed string. */ char *reverse(char* str) { int end = strlen(str)-1; int start = 0; while (start < end) { str[start] ^= str[end]; str[end] ^= str[start]; str[start] ^= str[end]; ++start; --end; } return str; } /*! \fn strpos() * \brief looks for the position of needle in haystack * * \return the position of -1 if not found */ int strpos(char *haystack, char *needle) { char *p = strstr(haystack, needle); if (p) { return p - haystack; } return -1; } /*! \fn char_count() * \brief counts occurrences of char in string. * * \return number of occurrences. */ int char_count(const char *str, int chr) { const unsigned char *my_str = (const unsigned char *) str; const unsigned char my_chr = chr; int count = 0; if (!my_chr) return 1; while (*my_str) { if (*my_str++ == my_chr) { count++; } } return count; } unsigned long long hex2dec(char *str) { int i = 0; unsigned long long number = 0; if (!str) return 0; /* first revers the string */ reverse(str); while (*str) { switch (*str) { case '0': i++; break; case '1': number += pow(16, i) * 1; i++; break; case '2': number += pow(16, i) * 2; i++; break; case '3': number += pow(16, i) * 3; i++; break; case '4': number += pow(16, i) * 4; i++; break; case '5': number += pow(16, i) * 5; i++; break; case '6': number += pow(16, i) * 6; i++; break; case '7': number += pow(16, i) * 7; i++; break; case '8': number += pow(16, i) * 8; i++; break; case '9': number += pow(16, i) * 9; i++; break; case 'a': case 'A': number += pow(16, i) * 10; i++; break; case 'b': case 'B': number += pow(16, i) * 11; i++; break; case 'c': case 'C': number += pow(16, i) * 12; i++; break; case 'd': case 'D': number += pow(16, i) * 13; i++; break; case 'e': case 'E': number += pow(16, i) * 14; i++; break; case 'f': case 'F': number += pow(16, i) * 15; i++; break; case '"': case ' ': case '\t': break; default: return 0; } str++; } return number; } int hasCaps() { #ifdef HAVE_LCAP cap_t caps; cap_value_t capval; cap_flag_value_t capflag; /* Recommended caps: cap_net_raw=eip */ caps = cap_get_proc(); if (caps == NULL) { SPINE_LOG(("ERROR: cap_get_proc failed.")); return FALSE; } /* check if cap_net_raw is in effective set */ if (cap_get_flag(caps, CAP_NET_RAW, CAP_EFFECTIVE, &capflag)) { SPINE_LOG(("ERROR: cap_get_flag for CAP_NET_RAW failed. ICMP ping will not work as non-root user.")); return FALSE; } if (capflag != CAP_SET) { SPINE_LOG(("ERROR: Capability CAP_NET_RAW is not set. ICMP ping will not work as non-root user.")); return FALSE; } SPINE_LOG_DEBUG(("DEBUG: Capability CAP_NET_RAW is set.")); cap_free(caps); return TRUE; #else return FALSE; #endif } void checkAsRoot() { #ifndef __CYGWIN__ #ifdef SOLAR_PRIV priv_set_t *privset; char *p; /* Get the basic set */ privset = priv_str_to_set("basic", ",", NULL); if (privset == NULL) { die("ERROR: Could not get basic privset from priv_str_to_set()."); } else { p = priv_set_to_str(privset, ',', 0); SPINE_LOG_DEBUG(("DEBUG: Basic privset is: '%s'.", p != NULL ? p : "Unknown")); } /* Add priviledge to send/receive ICMP packets */ if (priv_addset(privset, PRIV_NET_ICMPACCESS) < 0) { SPINE_LOG_DEBUG(("WARNING: Addition of PRIV_NET_ICMPACCESS to privset failed: '%s'.", strerror(errno))); } /* Compute the set of privileges that are never needed */ priv_inverse(privset); /* Remove the set of unneeded privs from Permitted (and by * implication from Effective) */ if (setppriv(PRIV_OFF, PRIV_PERMITTED, privset) < 0) { SPINE_LOG_DEBUG(("WARNING: Dropping privileges from PRIV_PERMITTED failed: '%s'.", strerror(errno))); } /* Remove unneeded priv set from Limit to be safe */ if (setppriv(PRIV_OFF, PRIV_LIMIT, privset) < 0) { SPINE_LOG_DEBUG(("WARNING: Dropping privileges from PRIV_LIMIT failed: '%s'.", strerror(errno))); } boolean_t pe = priv_ineffect(PRIV_NET_ICMPACCESS); SPINE_LOG_DEBUG(("DEBUG: Privilege PRIV_NET_ICMPACCESS is: '%s'.", pe != 0 ? "Enabled" : "Disabled")); set.icmp_avail = pe; /* Free the privset */ priv_freeset(privset); free(p); #else if (hasCaps() != TRUE) { SPINE_LOG_DEBUG(("DEBUG: Spine running as %d UID, %d EUID", getuid(), geteuid())); int ret = seteuid(0); if (ret != 0) { SPINE_LOG_DEBUG(("WARNING: Spine NOT able to set effective UID to 0")); } if (geteuid() != 0) { SPINE_LOG_DEBUG(("WARNING: Spine NOT running as root. This is required if using ICMP. Please run \"chown root:root spine;chmod u+s spine\" to resolve.")); set.icmp_avail = FALSE; } else { SPINE_LOG_DEBUG(("DEBUG: Spine is running as root.")); set.icmp_avail = TRUE; if (seteuid(getuid()) == -1) { SPINE_LOG_DEBUG(("WARNING: Spine unable to drop from root to local user.")); } } } else { SPINE_LOG_DEBUG(("DEBUG: Spine has cap_net_raw capability.")); set.icmp_avail = TRUE; } SPINE_LOG_DEBUG(("DEBUG: Spine has %sgot ICMP", set.icmp_avail?"":"not ")); #endif #endif } /*! \fn int get_cacti_version(MYSQL *psql, int mode, const char *setting) * \brief Returns the version of Cacti as a decimal * * Given a pointer to a database get the version of Cacti and convert * to an integer. * * \return the cacti version * */ int get_cacti_version(MYSQL *psql, int mode) { char qstring[256]; char *retval; MYSQL_RES *result; MYSQL_ROW mysql_row; int i; int major, minor, point; int cacti_version; assert(psql != 0); sprintf(qstring, "SELECT cacti FROM version LIMIT 1"); result = db_query(psql, mode, qstring); if (result != 0) { if (mysql_num_rows(result) > 0) { mysql_row = mysql_fetch_row(result); if (mysql_row != NULL) { retval = strdup(mysql_row[0]); db_free_result(result); if (STRIMATCH(retval, "new_install")) { return 0; } else { sscanf(retval, "%d.%d.%d", &major, &minor, &point); cacti_version = (major * 1000) + (minor * 100) + (point * 1); return cacti_version; } }else{ return 0; } }else{ db_free_result(result); return 0; } }else{ return 0; } } cacti-spine-1.2.30/sql.c0000664000175000017500000004450314770060157014000 0ustar markvmarkv/* ex: set tabstop=4 shiftwidth=4 autoindent: +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 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, implimentation details) | +-------------------------------------------------------------------------+ | - Cacti - http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ #include "common.h" #include "spine.h" /*! \fn int db_insert(MYSQL *mysql, int type, const char *query) * \brief inserts a row or rows in a database table. * \param mysql the database connection object * \param type the database to connect to local or remote * \param query the database query to execute * * Unless the SQL_readonly boolean is set to TRUE, the function will execute * the SQL statement specified in the query variable. * * \return TRUE if successful, or FALSE if not. * */ int db_insert(MYSQL *mysql, int type, const char *query) { int error; int error_count = 0; char query_frag[LRG_BUFSIZE]; /* save a fragment just in case */ memset(query_frag, 0, LRG_BUFSIZE); snprintf(query_frag, LRG_BUFSIZE, "%s", query); /* show the sql query */ SPINE_LOG_DEVDBG(("DEVDBG: SQL:%s", query_frag)); while(1) { if (set.SQL_readonly == FALSE) { if (mysql_query(mysql, query)) { error = mysql_errno(mysql); if (error == 2013 || error == 2006) { if (errno != EINTR) { db_reconnect(mysql, error, "db_insert"); error_count++; if (error_count > 30) { die("FATAL: Too many Reconnect Attempts!"); } continue; } else { usleep(50000); continue; } } if ((error == 1213) || (error == 1205)) { usleep(50000); error_count++; if (error_count > 30) { SPINE_LOG(("ERROR: Too many Lock/Deadlock errors occurred!, SQL Fragment:'%s'", query_frag)); return FALSE; } continue; } else { SPINE_LOG(("ERROR: SQL Failed! Error:'%i', Message:'%s', SQL Fragment:'%s'", error, mysql_error(mysql), query_frag)); return FALSE; } } else { return TRUE; } } else { return TRUE; } } } int db_reconnect(MYSQL *mysql, int error, char *function) { unsigned long mysql_thread = 0; char query[100]; mysql_thread = mysql_thread_id(mysql); mysql_ping(mysql); if (mysql_thread_id(mysql) != mysql_thread) { SPINE_LOG(("WARNING: Connection Broken in Function %s with Error %i. Reconnect successful.", function, error)); snprintf(query, 100, "KILL %lu;", mysql_thread); mysql_query(mysql, query); mysql_query(mysql, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'NO_ZERO_DATE', ''))"); mysql_query(mysql, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'NO_ZERO_IN_DATE', ''))"); mysql_query(mysql, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY', ''))"); mysql_query(mysql, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'NO_AUTO_VALUE_ON_ZERO', ''))"); mysql_query(mysql, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'TRADITIONAL', ''))"); mysql_query(mysql, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'STRICT_ALL_TABLES', ''))"); sleep(1); return TRUE; } else { SPINE_LOG(("WARNING: Connection Broken with Error %i. Reconnect failed.", error)); return FALSE; } } /*! \fn MYSQL_RES *db_query(MYSQL *mysql, int type, const char *query) * \brief executes a query and returns a pointer to the result set. * \param mysql the database connection object * \param query the database query to execute * * This function will execute the SQL statement specified in the query variable. * * \return MYSQL_RES a MySQL result structure * */ MYSQL_RES *db_query(MYSQL *mysql, int type, const char *query) { MYSQL_RES *mysql_res = 0; int error = 0; int error_count = 0; char query_frag[LRG_BUFSIZE]; /* save a fragment just in case */ memset(query_frag, 0, LRG_BUFSIZE); snprintf(query_frag, LRG_BUFSIZE, "%s", query); /* show the sql query */ SPINE_LOG_DEVDBG(("DEVDBG: SQL:%s", query_frag)); while (1) { if (mysql_query(mysql, query)) { error = mysql_errno(mysql); if (error == 2013 || error == 2006) { if (errno != EINTR) { db_reconnect(mysql, error, "db_query"); error_count++; if (error_count > 30) { die("FATAL: Too many Reconnect Attempts!"); } continue; } else { usleep(50000); continue; } } if (error == 1213 || error == 1205) { usleep(50000); error_count++; if (error_count > 30) { SPINE_LOG(("FATAL: Too many Lock/Deadlock errors occured!, SQL Fragment:'%s'", query_frag)); exit(1); } continue; } else { SPINE_LOG(("FATAL: Database Error:'%i', Message:'%s'", error, mysql_error(mysql))); SPINE_LOG(("ERROR: The Query Was:'%s'", query)); exit(1); } } else { mysql_res = mysql_store_result(mysql); break; } } return mysql_res; } /*! \fn void db_connect(char *database, MYSQL *mysql) * \brief opens a connection to a MySQL databse. * \param database a string pointer to the database name * \param mysql a pointer to a mysql database connection object * * This function will attempt to open a connection to a MySQL database and then * return the connection object to the calling function. If the database connection * fails more than 20 times, the function will fail and Spine will terminate. * */ void db_connect(int type, MYSQL *mysql) { int tries; int attempts; int timeout; int rtimeout; int wtimeout; int options_error; int success; int error = 0; bool reconnect; MYSQL *connect_error; char *hostname = NULL; char *socket = NULL; struct stat socket_stat; static int connections = 0; /* see if the hostname variable is a file reference. If so, * and if it is a socket file, setup mysql to use it. */ if (set.poller_id > 1) { if (type == LOCAL) { STRDUP_OR_DIE(hostname, set.db_host, "db_host") if (stat(hostname, &socket_stat) == 0) { if (socket_stat.st_mode & S_IFSOCK) { socket = strdup (set.db_host); hostname = NULL; } } else if ((socket = strstr(hostname,":"))) { *socket++ = 0x0; } } else { STRDUP_OR_DIE(hostname, set.rdb_host, "rdb_host") } } else { STRDUP_OR_DIE(hostname, set.db_host, "db_host") if (stat(hostname, &socket_stat) == 0) { if (socket_stat.st_mode & S_IFSOCK) { socket = strdup (set.db_host); hostname = NULL; } } else if ((socket = strstr(hostname,":"))) { *socket++ = 0x0; } } /* initialalize variables */ tries = 2; success = FALSE; timeout = 5; rtimeout = 30; wtimeout = 30; reconnect = 1; attempts = 1; mysql_init(mysql); if (mysql == NULL) { printf("FATAL: Database unable to allocate memory and therefore can not connect\n"); exit(1); } MYSQL_SET_OPTION(MYSQL_OPT_READ_TIMEOUT, (int *)&rtimeout, "read timeout"); MYSQL_SET_OPTION(MYSQL_OPT_WRITE_TIMEOUT, (int *)&wtimeout, "write timeout"); MYSQL_SET_OPTION(MYSQL_OPT_CONNECT_TIMEOUT, (int *)&timeout, "general timeout"); #if defined(MARIADB_BASE_VERSION) || (MYSQL_VERSION_ID < 80034 && MYSQL_VERSION_ID >= 50013) MYSQL_SET_OPTION(MYSQL_OPT_RECONNECT, &reconnect, "reconnect"); #endif #ifdef HAS_MYSQL_OPT_RETRY_COUNT MYSQL_SET_OPTION(MYSQL_OPT_RETRY_COUNT, &tries, "retry count"); #endif /* set SSL options if available */ #ifdef HAS_MYSQL_OPT_SSL_KEY char *ssl_key = NULL; char *ssl_ca = NULL; char *ssl_cert = NULL; if (type == REMOTE) { STRDUP_OR_DIE(ssl_key, set.rdb_ssl_key, "rdb_ssl_key"); STRDUP_OR_DIE(ssl_ca, set.rdb_ssl_ca, "rdb_ssl_ca"); STRDUP_OR_DIE(ssl_cert, set.rdb_ssl_cert, "rdb_ssl_cert"); } else { STRDUP_OR_DIE(ssl_key, set.db_ssl_key, "db_ssl_key"); STRDUP_OR_DIE(ssl_ca, set.db_ssl_ca, "db_ssl_ca"); STRDUP_OR_DIE(ssl_cert, set.db_ssl_cert, "db_ssl_cert"); } if (strlen(ssl_key)) MYSQL_SET_OPTION(MYSQL_OPT_SSL_KEY, ssl_key, "ssl key"); if (strlen(ssl_ca)) MYSQL_SET_OPTION(MYSQL_OPT_SSL_CA, ssl_ca, "ssl ca"); if (strlen(ssl_cert)) MYSQL_SET_OPTION(MYSQL_OPT_SSL_CERT, ssl_cert, "ssl cert"); #endif while (tries > 0) { tries--; if (set.poller_id > 1) { if (type == LOCAL) { connect_error = mysql_real_connect(mysql, hostname, set.db_user, set.db_pass, set.db_db, set.db_port, socket, 0); } else { connect_error = mysql_real_connect(mysql, hostname, set.rdb_user, set.rdb_pass, set.rdb_db, set.rdb_port, socket, 0); } } else { connect_error = mysql_real_connect(mysql, hostname, set.db_user, set.db_pass, set.db_db, set.db_port, socket, 0); } if (!connect_error) { error = mysql_errno(mysql); if ((error == 2002 || error == 2003 || error == 2006 || error == 2013) && errno == EINTR) { usleep(5000); tries++; success = FALSE; } else if (error == 2002) { printf("Database: Connection Failed: Attempt:'%u', Error:'%u', Message:'%s'\n", attempts, mysql_errno(mysql), mysql_error(mysql)); sleep(1); success = FALSE; } else if (error != 1049 && error != 2005 && error != 1045) { printf("Database: Connection Failed: Error:'%u', Message:'%s'\n", error, mysql_error(mysql)); success = FALSE; usleep(50000); } else { tries = 0; success = FALSE; } } else { tries = 0; success = TRUE; break; } attempts++; } if (hostname != NULL) { free(hostname); } #ifdef HAS_MYSQL_OPT_SSL_KEY if (ssl_key != NULL) { free(ssl_key); } if (ssl_ca != NULL) { free(ssl_ca); } if (ssl_cert != NULL) { free(ssl_cert); } #endif if (!success){ printf("FATAL: Connection Failed, Error:'%i', Message:'%s'\n", error, mysql_error(mysql)); exit(1); } SPINE_LOG_DEBUG(("DEBUG: Total Connections made %i", connections)); connections++; } /*! \fn void db_disconnect(MYSQL *mysql) * \brief closes connection to MySQL database * \param mysql the database connection object * */ void db_disconnect(MYSQL *mysql) { if (mysql != NULL) { mysql_close(mysql); } } /*! \fn void db_create_connection_pool(int type) * \brief Creates a connection pool for spine * \param type the connection type, LOCAL or REMOTE * */ void db_create_connection_pool(int type) { int id; if (type == LOCAL) { SPINE_LOG_DEBUG(("DEBUG: Creating Local Connection Pool of %i threads.", set.threads)); for(id = 0; id < set.threads; id++) { SPINE_LOG_DEBUG(("DEBUG: Creating Local Connection %i.", id)); db_connect(type, &db_pool_local[id].mysql); db_insert(&db_pool_local[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'NO_ZERO_DATE', ''))"); db_insert(&db_pool_local[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'NO_ZERO_IN_DATE', ''))"); db_insert(&db_pool_local[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY', ''))"); db_insert(&db_pool_local[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'NO_AUTO_VALUE_ON_ZERO', ''))"); db_insert(&db_pool_local[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'TRADITIONAL', ''))"); db_insert(&db_pool_local[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'STRICT_ALL_TABLES', ''))"); db_insert(&db_pool_local[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'STRICT_TRANS_TABLES', ''))"); db_pool_local[id].free = TRUE; db_pool_local[id].id = id; } } else { SPINE_LOG_DEBUG(("DEBUG: Creating Remote Connection Pool of %i threads.", set.threads)); for(id = 0; id < set.threads; id++) { SPINE_LOG_DEBUG(("DEBUG: Creating Remote Connection %i.", id)); db_connect(type, &db_pool_remote[id].mysql); db_insert(&db_pool_remote[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'NO_ZERO_DATE', ''))"); db_insert(&db_pool_remote[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'NO_ZERO_IN_DATE', ''))"); db_insert(&db_pool_remote[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY', ''))"); db_insert(&db_pool_remote[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'NO_AUTO_VALUE_ON_ZERO', ''))"); db_insert(&db_pool_remote[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'TRADITIONAL', ''))"); db_insert(&db_pool_remote[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'STRICT_ALL_TABLES', ''))"); db_insert(&db_pool_remote[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'STRICT_TRANS_TABLES', ''))"); db_pool_remote[id].free = TRUE; db_pool_remote[id].id = id; } } } /*! \fn void db_close_connection_pool(int type) * \brief Closes a connection pool for spine * \param type the connection type, LOCAL or REMOTE * */ void db_close_connection_pool(int type) { int id; if (type == LOCAL) { for(id = 0; id < set.threads; id++) { SPINE_LOG_DEBUG(("DEBUG: Closing Local Connection Pool ID %i", id)); db_disconnect(&db_pool_local[id].mysql); } free(db_pool_local); } else { for(id = 0; id < set.threads; id++) { SPINE_LOG_DEBUG(("DEBUG: Closing Remote Connection Pool ID %i", id)); db_disconnect(&db_pool_remote[id].mysql); } free(db_pool_remote); } } /*! \fn pool_t db_get_connection(int type) * \brief returns a free mysql connection from the pool * \param type the connection type, LOCAL or REMOTE * */ pool_t *db_get_connection(int type) { int id; thread_mutex_lock(LOCK_POOL); if (type == LOCAL) { SPINE_LOG_DEBUG(("DEBUG: Traversing Local Connection Pool for free connection.")); for (id = 0; id < set.threads; id++) { SPINE_LOG_DEBUG(("DEBUG: Checking Local Pool ID %i.", id)); if (db_pool_local[id].free == TRUE) { SPINE_LOG_DEBUG(("DEBUG: Allocating Local Pool ID %i.", id)); db_pool_local[id].free = FALSE; thread_mutex_unlock(LOCK_POOL); return &db_pool_local[id]; } } } else { SPINE_LOG_DEBUG(("DEBUG: Traversing Remote Connection Pool for free connection.")); for (id = 0; id < set.threads; id++) { SPINE_LOG_DEBUG(("DEBUG: Checking Remote Pool ID %i.", id)); if (db_pool_remote[id].free == TRUE) { SPINE_LOG_DEBUG(("DEBUG: Allocating Remote Pool ID %i.", id)); db_pool_remote[id].free = FALSE; thread_mutex_unlock(LOCK_POOL); return &db_pool_remote[id]; } } } SPINE_LOG(("FATAL: Connection Pool Fatal Error.")); thread_mutex_unlock(LOCK_POOL); return NULL; } /*! \fn voi db_release_connection(int id) * \brief marks a database connection as free * \param id the connection id * */ void db_release_connection(int type, int id) { thread_mutex_lock(LOCK_POOL); if (type == LOCAL) { SPINE_LOG_DEBUG(("DEBUG: Freeing Local Pool ID %i", id)); db_pool_local[id].free = TRUE; } else { SPINE_LOG_DEBUG(("DEBUG: Freeing Remote Pool ID %i", id)); db_pool_remote[id].free = TRUE; } thread_mutex_unlock(LOCK_POOL); } /*! \fn int append_hostrange(char *obuf, const char *colname, const config_t *set) * \brief appends a host range to a sql select statement * \param obuf the sql select statment to have the host range appended * \param colname the sql column name that will have the host range checked * \param set global runtime settings * * Several places in the code need to limit the range of hosts to * those with a certain ID range, but only if those range values * are actually nonzero. * * This appends the SQL clause if necessary, returning the # of * characters added to the buffer. Else return 0. * * \return the number of characters added to the end of the character buffer * */ int append_hostrange(char *obuf, const char *colname) { if (HOSTID_DEFINED(set.start_host_id) && HOSTID_DEFINED(set.end_host_id)) { return sprintf(obuf, " AND %s BETWEEN %d AND %d", colname, set.start_host_id, set.end_host_id); } else { return 0; } } /*! \fn void db_escape(MYSQL *mysql, char *output, int max_size, const char *input) * \brief Escapse a text string to make it safe for mysql insert/updates * \param mysql the connection object * \param output a pointer to the output string * \param a pointer to the input string * * A simple implementation of the mysql_real_escape_string that one * day should be portable. * * \return void * */ void db_escape(MYSQL *mysql, char *output, int max_size, const char *input) { if (input == NULL) return; char input_trimmed[DBL_BUFSIZE]; int max_escaped_input_size = (strlen(input) * 2) + 1; if (max_escaped_input_size > max_size) { snprintf(input_trimmed, (max_size / 2) - 1, "%s", input); } else { snprintf(input_trimmed, max_size, "%s", input); } mysql_real_escape_string(mysql, output, input_trimmed, strlen(input_trimmed)); } void db_free_result(MYSQL_RES *result) { mysql_free_result(result); } cacti-spine-1.2.30/Makefile.am0000664000175000017500000000363514770060157015072 0ustar markvmarkv# +-------------------------------------------------------------------------+ # | Copyright (C) 2004-2024 The Cacti Group | # | | # | 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. | # +-------------------------------------------------------------------------+ # | Cacti: The Complete RRDtool-based Graphing Solution | # +-------------------------------------------------------------------------+ # | This code is designed, written, and maintained by the Cacti Group. See | # | about.php and/or the AUTHORS file for specific developer information. | # +-------------------------------------------------------------------------+ # | http://www.cacti.net/ | # +-------------------------------------------------------------------------+ AUTOMAKE_OPTIONS = foreign ACLOCAL_AMFLAGS = -I m4 spine_SOURCES = sql.c spine.c util.c snmp.c locks.c poller.c nft_popen.c php.c ping.c keywords.c error.c configdir = $(sysconfdir) config_DATA = spine.conf.dist bin_PROGRAMS = spine man_MANS = spine.1 spine.1: $(bin_PROGRAMS) $(HELP2MAN) --output=$@ --name='Data Collector for Cacti' --no-info --version-option='--version' ./spine cacti-spine-1.2.30/INSTALL0000664000175000017500000001125114770060157014060 0ustar markvmarkvSpine is a high speed poller replacement for cmd.php. It is almost 100% compatible with the legacy cmd.php processor and provides much more flexibility, speed and concurrency than cmd.php. Make sure that you have the proper development environment to compile Spine. This includes compilers, header files and things such as libtool. If you have questions please consult the forums and/or online documentation. Development =========== DEVELOP branch should generally be considered UNSTABLE, use with caution! ----------------------------------------------------------------------------- Unix Installation ================= These instructions assume the default install location for spine of /usr/local/spine. If you choose to use another prefix, make sure you update the commands as required for that new path. To compile and install Spine using MySQL versions 5.5 or higher please do the following: 1. Run the bootstrap process to automatically create the configure script ./bootstrap 2. Run the configure process to detect what is available on the system ./configure 3. Build spine make 4. Optionally, install spine to the default location (/usr/local/spine/bin/) but do note that if you manually copy to another folder, change the paths below to reflect the correct folder you want spine to run from: make install chown root:root /usr/local/spine/bin/spine chmod u+s /usr/local/spine/bin/spine To compile and install Spine using MySQL versions previous to 5.5 please add the additional --with-reentrant option to the ./configure command above but please be aware that Cacti no longer officially supports MySQL prior to 5.5. Windows Installation ==================== CYGWIN Prerequisite ------------------- 1. Download Cygwin for Window from https://www.cygwin.com/ 2. Install Cygwin by executing the downloaded setup program 3. Select _Install from Internet_ 4. Select Root Directory: _C:\cygwin_ 5. Select a mirror which is close to your location 6. Once on the package selection section make sure to select the following (TIP: use the search!): * autoconf * automake * dos2unix * gcc-core * gcc-debuginfo * gzip * help2man * libmysqlclient * libmysqlclient-devel * libtool * m4 * make * net-snmp-devel * openssl-devel * wget 7. Wait for installation to complete, coffee time! 8. Move the cygwin setup to the C:\cygwin\ folder for future usage. Compile Spine ------------- 1. Open Cygwin shell prompt (C:\Cygwin\cygwin.bat) and brace yourself to use unix commands on Windows. 2. Download the Spine source to the current directory: http://www.cacti.net/spine_download.php 3. Extract Spine into C:\Cygwin\usr\src\: tar xzvf cacti-spine-*.tar.gz 4. Change into the Spine directory: cd /usr/src/cacti-spine-* 5. Run bootstrap to prepare Spine for compilation: ./bootstrap 6. Follow the instruction which bootstrap outputs. 7. Update the spine.conf file for your installation of Cacti. You can optionally move it to a better location if you choose to do so, make sure to copy the spine.conf as well. 8. Ensure that Spine runs well by running with: /usr/local/spine/spine -R -S -V 3 9. Update Cacti 'Paths' Setting to point to the Spine binary and update the 'Poller Type' to Spine. For the spine binary on Windows x64, and using default locations, that would be: C:\cygwin64\usr\local\spine\bin\spine.exe 10. If all is good Spine will be run from the poller in place of cmd.php. -------------------------------------------------------------------------------------- Known Issues ============ 1. On Windows, Microsoft does not support a TCP Socket send timeout. Therefore, if you are using TCP ping on Windows, spine will not perform a second or subsequent retries to connect and the host will be assumed down on the first failure. If this is a problem it is suggested to use another Availability/Reachability method, or moving to Linux/UNIX. 2. Spine takes quite a few MySQL connections. The number of connections is calculated as follows: * main poller take one connection * all threads take one connection each * all script servers take one connection each Therefore, if you have 4 processes, with 10 threads each, and 5 script servers each your spine will take approximately: total connections = 4 * ( 1 + 10 + 5 ) = 64 3. On older MySQL versions, different libraries had to be used to make MySQL thread safe. MySQL versions 5.0 and 5.1 require this flag. If you are using these version of MySQL, you must use the --with-reentrant configure flag. ----------------------------------------------- Copyright (c) 2004-2024 - The Cacti Group, Inc. cacti-spine-1.2.30/poller.c0000664000175000017500000030022614770060157014473 0ustar markvmarkv/* ex: set tabstop=4 shiftwidth=4 autoindent: +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 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, implimentation details) | +-------------------------------------------------------------------------+ | - Cacti - http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ #include "common.h" #include "spine.h" void child_cleanup(void *arg) { poller_thread_t poller_details = *(poller_thread_t*) arg; SPINE_LOG_DEVDBG(("DEBUG: Device[%i] HT[%i] The Device Thread has cleaned up.", poller_details.host_id, poller_details.host_thread)); child_cleanup_thread(arg); } void child_cleanup_thread(void *arg) { sem_post(&available_threads); int a_threads_value; sem_getvalue(&available_threads, &a_threads_value); SPINE_LOG_DEVDBG(("DEBUG: Available Threads is %i (%i outstanding)", a_threads_value, set.threads - a_threads_value)); } void child_cleanup_script(void *arg) { sem_post(&available_scripts); int a_scripts_value; sem_getvalue(&available_scripts, &a_scripts_value); SPINE_LOG_DEVDBG(("DEBUG: Available Scripts is %i (%i outstanding)", a_scripts_value, MAX_SIMULTANEOUS_SCRIPTS - a_scripts_value)); } /*! \fn void *child(void *arg) * \brief function is called via the fork command and initiates a poll of a host * \param arg a pointer to an integer point to the host_id to be polled * * This function will call the primary Spine polling function to poll a host * and then reduce the number of active threads by one so that the next host * can be polled. * */ void *child(void *arg) { pthread_cleanup_push(child_cleanup, arg); int device_counter; int host_id; int host_thread; int host_threads; int host_data_ids; int host_errors; double host_time_double; char host_time[SMALL_BUFSIZE]; extern poller_thread_t** details; host_errors = 0; poller_thread_t poller_details = *(poller_thread_t*) arg; device_counter = poller_details.device_counter; host_id = poller_details.host_id; host_thread = poller_details.host_thread; host_threads = poller_details.host_threads; host_data_ids = poller_details.host_data_ids; host_time_double = poller_details.host_time_double; snprintf(host_time, SMALL_BUFSIZE, "%s", poller_details.host_time); thread_mutex_unlock(LOCK_HOST_TIME); /* Allows main thread to proceed with creation of other threads */ sem_post(poller_details.thread_init_sem); if (is_debug_device(host_id)) { SPINE_LOG(("DEBUG: Device[%i] HT[%i] In Poller, About to Start Polling", host_id, host_thread)); } else { SPINE_LOG_DEBUG(("DEBUG: Device[%i] HT[%i] In Poller, About to Start Polling", host_id, host_thread)); } poll_host(device_counter, host_id, host_thread, host_threads, host_data_ids, host_time, &host_errors, host_time_double); pthread_cleanup_pop(1); /* end the thread */ pthread_exit(0); exit(0); } /*! \fn void poll_host(int device_counter, int host_id, int host_thread, int host_threads, int host_data_ids, char *host_time, int *host_errors, double host_time_double) * \brief core Spine function that polls a host * \param host_id integer value for the host_id from the hosts table in Cacti * * This function is core to Spine. It will take a host_id and then poll it. * * Prior to the poll, the system will ping the host to verifiy that it is up. * In addition, the system will check to see if any reindexing of data query's * is required. * * If reindexing is required, the Cacti poller.php function will spawn that * reindexing process. * * In the case of hosts that require reindexing because of a sysUptime * rollback, Spine will store an unknown (NaN) value for all objects to prevent * spikes in the graphs. * * With regard to snmp calls, if the host has multiple snmp agents running * Spine will re-initialize the snmp session and poll under those new ports * as the host poller_items table dictates. * */ void poll_host(int device_counter, int host_id, int host_thread, int host_threads, int host_data_ids, char *host_time, int *host_errors, double host_time_double) { char query1[BUFSIZE]; char query2[BIG_BUFSIZE]; char *query3 = NULL; char query4[BUFSIZE]; char query5[BUFSIZE]; char query6[BUFSIZE]; char query8[BUFSIZE]; char query9[BUFSIZE]; char query10[BUFSIZE]; char query11[BUFSIZE]; char *query12 = NULL; char posuffix[BUFSIZE]; int query8_len = 0; int query11_len = 0; int posuffix_len = 0; char sysUptime[BUFSIZE]; char result_string[RESULTS_BUFFER+SMALL_BUFSIZE]; int result_length; char temp_result[RESULTS_BUFFER]; int errors = 0; int *buf_errors; int *buf_size; char *error_string; int error_len = 0; int num_rows; int assert_fail = FALSE; int reindex_err = FALSE; int spike_kill = FALSE; int rows_processed = 0; int i = 0; int j = 0; int k = 0; int num_oids = 0; int snmp_poller_items = 0; size_t out_buffer; int php_process; char *poll_result = NULL; char update_sql[BIG_BUFSIZE]; char temp_poll_result[BUFSIZE]; char temp_arg1[BUFSIZE]; char limits[SMALL_BUFSIZE]; int last_snmp_version = 0; int last_snmp_port = 0; char last_snmp_community[50]; char last_snmp_username[50]; char last_snmp_password[50]; char last_snmp_auth_protocol[7]; char last_snmp_priv_passphrase[200]; char last_snmp_priv_protocol[8]; char last_snmp_context[65]; char last_snmp_engine_id[30]; double poll_time = get_time_as_double(); double thread_start = 0; double thread_end = 0; /* reindex shortcuts to speed polling */ int previous_assert_failure = FALSE; int last_data_query_id = 0; int perform_assert = TRUE; int new_buffer = TRUE; int ignore_sysinfo = TRUE; int buf_length = 0; extern poller_thread_t** details; pool_t *local_cnn; pool_t *remote_cnn; reindex_t *reindex = NULL; host_t *host = NULL; ping_t *ping = NULL; name_t *name = NULL; target_t *poller_items = NULL; snmp_oids_t *snmp_oids = NULL; error_string = malloc(DBL_BUFSIZE); buf_size = malloc(sizeof(int)); buf_errors = malloc(sizeof(int)); *buf_size = 0; *buf_errors = 0; MYSQL mysql; MYSQL mysqlr; MYSQL mysqlt; MYSQL_RES *result; MYSQL_ROW row; //db_connect(LOCAL, &mysql); local_cnn = db_get_connection(LOCAL); mysql = local_cnn->mysql; if (set.poller_id > 1 && set.mode == REMOTE_ONLINE) { remote_cnn = db_get_connection(REMOTE); mysqlr = remote_cnn->mysql; } /* allocate host and ping structures with appropriate values */ if (!(host = (host_t *) malloc(sizeof(host_t)))) { die("ERROR: Fatal malloc error: poller.c host struct!"); } /* set zeros */ memset(host, 0, sizeof(host_t)); if (!(ping = (ping_t *) malloc(sizeof(ping_t)))) { die("ERROR: Fatal malloc error: poller.c ping struct!"); } /* set zeros */ memset(ping, 0, sizeof(ping_t)); if (!(reindex = (reindex_t *) malloc(sizeof(reindex_t)))) { die("ERROR: Fatal malloc error: poller.c reindex poll!"); } memset(reindex, 0, sizeof(reindex_t)); /* determine the SQL limits using the poller instructions */ if (host_data_ids > 0) { snprintf(limits, SMALL_BUFSIZE, "LIMIT %i, %i", host_data_ids * (host_thread - 1), host_data_ids); } else { limits[0] = '\0'; } /* single polling interval query for items */ if (set.poller_id == 0) { if (set.total_snmp_ports == 1) { snprintf(query1, BUFSIZE, "SELECT SQL_NO_CACHE action, hostname, snmp_community, " "snmp_version, snmp_username, snmp_password, " "rrd_name, rrd_path, arg1, arg2, arg3, local_data_id, " "rrd_num, snmp_port, snmp_timeout, " "snmp_auth_protocol, snmp_priv_passphrase, snmp_priv_protocol, snmp_context, snmp_engine_id " " FROM poller_item" " WHERE host_id = %i" " AND deleted = '' %s", host_id, limits); } else { snprintf(query1, BUFSIZE, "SELECT SQL_NO_CACHE action, hostname, snmp_community, " "snmp_version, snmp_username, snmp_password, " "rrd_name, rrd_path, arg1, arg2, arg3, local_data_id, " "rrd_num, snmp_port, snmp_timeout, " "snmp_auth_protocol, snmp_priv_passphrase, snmp_priv_protocol, snmp_context, snmp_engine_id " " FROM poller_item" " WHERE host_id = %i" " AND deleted = ''" " ORDER BY snmp_port %s", host_id, limits); } /* host structure for uptime checks */ snprintf(query2, BIG_BUFSIZE, "SELECT SQL_NO_CACHE id, hostname, snmp_community, snmp_version, " "snmp_username, snmp_password, snmp_auth_protocol, " "snmp_priv_passphrase, snmp_priv_protocol, snmp_context, snmp_engine_id, snmp_port, snmp_timeout, max_oids, " "availability_method, ping_method, ping_port, ping_timeout, ping_retries, " "status, status_event_count, UNIX_TIMESTAMP(status_fail_date), " "UNIX_TIMESTAMP(status_rec_date), status_last_error, " "min_time, max_time, cur_time, avg_time, " "total_polls, failed_polls, availability, snmp_sysUpTimeInstance, snmp_sysDescr, snmp_sysObjectID, " "snmp_sysContact, snmp_sysName, snmp_sysLocation" " FROM host" " WHERE id = %i" " AND deleted = ''", host_id); /* data query structure for reindex detection */ snprintf(query4, BUFSIZE, "SELECT SQL_NO_CACHE data_query_id, action, op, assert_value, arg1" " FROM poller_reindex" " WHERE host_id = %i", host_id); /* multiple polling interval query for items */ if (set.active_profiles != 1) { if (set.total_snmp_ports == 1) { snprintf(query5, BUFSIZE, "SELECT SQL_NO_CACHE action, hostname, snmp_community, " "snmp_version, snmp_username, snmp_password, " "rrd_name, rrd_path, arg1, arg2, arg3, local_data_id, " "rrd_num, snmp_port, snmp_timeout, " "snmp_auth_protocol, snmp_priv_passphrase, snmp_priv_protocol, snmp_context, snmp_engine_id " " FROM poller_item" " WHERE host_id = %i" " AND rrd_next_step <= 0" " %s", host_id, limits); } else { snprintf(query5, BUFSIZE, "SELECT SQL_NO_CACHE action, hostname, snmp_community, " "snmp_version, snmp_username, snmp_password, " "rrd_name, rrd_path, arg1, arg2, arg3, local_data_id, " "rrd_num, snmp_port, snmp_timeout, " "snmp_auth_protocol, snmp_priv_passphrase, snmp_priv_protocol, snmp_context, snmp_engine_id " " FROM poller_item" " WHERE host_id = %i" " AND rrd_next_step <= 0" " ORDER BY snmp_port %s", host_id, limits); } } else { if (set.total_snmp_ports == 1) { snprintf(query5, BUFSIZE, "SELECT SQL_NO_CACHE action, hostname, snmp_community, " "snmp_version, snmp_username, snmp_password, " "rrd_name, rrd_path, arg1, arg2, arg3, local_data_id, " "rrd_num, snmp_port, snmp_timeout, " "snmp_auth_protocol, snmp_priv_passphrase, snmp_priv_protocol, snmp_context, snmp_engine_id " " FROM poller_item" " WHERE host_id = %i" " %s", host_id, limits); } else { snprintf(query5, BUFSIZE, "SELECT SQL_NO_CACHE action, hostname, snmp_community, " "snmp_version, snmp_username, snmp_password, " "rrd_name, rrd_path, arg1, arg2, arg3, local_data_id, " "rrd_num, snmp_port, snmp_timeout, " "snmp_auth_protocol, snmp_priv_passphrase, snmp_priv_protocol, snmp_context, snmp_engine_id " " FROM poller_item" " WHERE host_id = %i" " ORDER BY snmp_port %s", host_id, limits); } } /* query to setup the next polling interval in cacti */ snprintf(query6, BUFSIZE, "UPDATE poller_item" " SET rrd_next_step = IF(rrd_step = %i, 0, IF(rrd_next_step - %i < 0, rrd_step - %i, rrd_next_step - %i))" " WHERE host_id = %i", set.poller_interval, set.poller_interval, set.poller_interval, set.poller_interval, host_id); /* query to add output records to the poller output table */ snprintf(query8, BUFSIZE, "INSERT INTO poller_output" " (local_data_id, rrd_name, time, output) VALUES"); /* query suffix to add rows to the poller output table */ if (set.dbonupdate == 0) { snprintf(posuffix, BUFSIZE, " ON DUPLICATE KEY UPDATE output=VALUES(output)"); } else { snprintf(posuffix, BUFSIZE, " AS rs ON DUPLICATE KEY UPDATE output=rs.output"); } /* number of agent's count for single polling interval */ snprintf(query9, BUFSIZE, "SELECT SQL_NO_CACHE snmp_port, count(snmp_port)" " FROM poller_item" " WHERE host_id = %i" " GROUP BY snmp_port %s", host_id, limits); /* number of agent's count for multiple polling intervals */ if (set.active_profiles != 1) { snprintf(query10, BUFSIZE, "SELECT SQL_NO_CACHE snmp_port, count(snmp_port)" " FROM poller_item" " WHERE host_id = %i" " AND rrd_next_step <= 0" " GROUP BY snmp_port %s", host_id, limits); } else { snprintf(query10, BUFSIZE, "SELECT SQL_NO_CACHE snmp_port, count(snmp_port)" " FROM poller_item" " WHERE host_id = %i" " GROUP BY snmp_port %s", host_id, limits); } } else { if (set.total_snmp_ports == 1) { snprintf(query1, BUFSIZE, "SELECT SQL_NO_CACHE action, hostname, snmp_community, " "snmp_version, snmp_username, snmp_password, " "rrd_name, rrd_path, arg1, arg2, arg3, local_data_id, " "rrd_num, snmp_port, snmp_timeout, " "snmp_auth_protocol, snmp_priv_passphrase, snmp_priv_protocol, snmp_context, snmp_engine_id " " FROM poller_item" " WHERE host_id = %i" " AND poller_id=%i %s", host_id, set.poller_id, limits); } else { snprintf(query1, BUFSIZE, "SELECT SQL_NO_CACHE action, hostname, snmp_community, " "snmp_version, snmp_username, snmp_password, " "rrd_name, rrd_path, arg1, arg2, arg3, local_data_id, " "rrd_num, snmp_port, snmp_timeout, " "snmp_auth_protocol, snmp_priv_passphrase, snmp_priv_protocol, snmp_context, snmp_engine_id " " FROM poller_item" " WHERE host_id = %i" " AND poller_id=%i" " ORDER BY snmp_port %s", host_id, set.poller_id, limits); } /* host structure for uptime checks */ snprintf(query2, BIG_BUFSIZE, "SELECT SQL_NO_CACHE id, hostname, snmp_community, snmp_version, " "snmp_username, snmp_password, snmp_auth_protocol, " "snmp_priv_passphrase, snmp_priv_protocol, snmp_context, snmp_engine_id, snmp_port, snmp_timeout, max_oids, " "availability_method, ping_method, ping_port, ping_timeout, ping_retries, " "status, status_event_count, UNIX_TIMESTAMP(status_fail_date), " "UNIX_TIMESTAMP(status_rec_date), status_last_error, " "min_time, max_time, cur_time, avg_time, " "total_polls, failed_polls, availability, snmp_sysUpTimeInstance, snmp_sysDescr, snmp_sysObjectID, " "snmp_sysContact, snmp_sysName, snmp_sysLocation" " FROM host" " WHERE id = %i" " AND deleted = ''", host_id); /* data query structure for reindex detection */ snprintf(query4, BUFSIZE, "SELECT SQL_NO_CACHE data_query_id, action, op, assert_value, arg1" " FROM poller_reindex" " WHERE host_id = %i", host_id); /* multiple polling interval query for items */ if (set.active_profiles != 1) { if (set.total_snmp_ports == 1) { snprintf(query5, BUFSIZE, "SELECT SQL_NO_CACHE action, hostname, snmp_community, " "snmp_version, snmp_username, snmp_password, " "rrd_name, rrd_path, arg1, arg2, arg3, local_data_id, " "rrd_num, snmp_port, snmp_timeout, " "snmp_auth_protocol, snmp_priv_passphrase, snmp_priv_protocol, snmp_context, snmp_engine_id " " FROM poller_item" " WHERE host_id = %i" " AND rrd_next_step <= 0" " AND poller_id = %i %s", host_id, set.poller_id, limits); } else { snprintf(query5, BUFSIZE, "SELECT SQL_NO_CACHE action, hostname, snmp_community, " "snmp_version, snmp_username, snmp_password, " "rrd_name, rrd_path, arg1, arg2, arg3, local_data_id, " "rrd_num, snmp_port, snmp_timeout, " "snmp_auth_protocol, snmp_priv_passphrase, snmp_priv_protocol, snmp_context, snmp_engine_id " " FROM poller_item" " WHERE host_id = %i" " AND rrd_next_step <= 0" " AND poller_id = %i" " ORDER BY snmp_port %s", host_id, set.poller_id, limits); } } else { if (set.total_snmp_ports == 1) { snprintf(query5, BUFSIZE, "SELECT SQL_NO_CACHE action, hostname, snmp_community, " "snmp_version, snmp_username, snmp_password, " "rrd_name, rrd_path, arg1, arg2, arg3, local_data_id, " "rrd_num, snmp_port, snmp_timeout, " "snmp_auth_protocol, snmp_priv_passphrase, snmp_priv_protocol, snmp_context, snmp_engine_id " " FROM poller_item" " WHERE host_id = %i" " AND poller_id = %i %s", host_id, set.poller_id, limits); } else { snprintf(query5, BUFSIZE, "SELECT SQL_NO_CACHE action, hostname, snmp_community, " "snmp_version, snmp_username, snmp_password, " "rrd_name, rrd_path, arg1, arg2, arg3, local_data_id, " "rrd_num, snmp_port, snmp_timeout, " "snmp_auth_protocol, snmp_priv_passphrase, snmp_priv_protocol, snmp_context, snmp_engine_id " " FROM poller_item" " WHERE host_id = %i" " AND poller_id = %i" " ORDER BY snmp_port %s", host_id, set.poller_id, limits); } } /* query to setup the next polling interval in cacti */ snprintf(query6, BUFSIZE, "UPDATE poller_item" " SET rrd_next_step = IF(rrd_step = %i, 0, IF(rrd_next_step - %i < 0, rrd_step - %i, rrd_next_step - %i))" " WHERE host_id = %i" " AND poller_id = %i", set.poller_interval, set.poller_interval, set.poller_interval, set.poller_interval, host_id, set.poller_id); /* query to add output records to the poller output table */ snprintf(query8, BUFSIZE, "INSERT INTO poller_output" " (local_data_id, rrd_name, time, output) VALUES"); /* query suffix to add rows to the poller output table */ snprintf(posuffix, BUFSIZE, " ON DUPLICATE KEY UPDATE output=VALUES(output)"); /* number of agent's count for single polling interval */ snprintf(query9, BUFSIZE, "SELECT SQL_NO_CACHE snmp_port, count(snmp_port)" " FROM poller_item" " WHERE host_id = %i" " AND poller_id = %i" " GROUP BY snmp_port %s", host_id, set.poller_id, limits); /* number of agent's count for multiple polling intervals */ if (set.active_profiles != 1) { snprintf(query10, BUFSIZE, "SELECT SQL_NO_CACHE snmp_port, count(snmp_port)" " FROM poller_item" " WHERE host_id = %i" " AND rrd_next_step <= 0" " AND poller_id = %i" " GROUP BY snmp_port %s", host_id, set.poller_id, limits); } else { snprintf(query10, BUFSIZE, "SELECT SQL_NO_CACHE snmp_port, count(snmp_port)" " FROM poller_item" " WHERE host_id = %i" " AND poller_id = %i" " GROUP BY snmp_port %s", host_id, set.poller_id, limits); } } /* query to add output records to the poller output table */ snprintf(query11, BUFSIZE, "INSERT INTO poller_output_boost" " (local_data_id, rrd_name, time, output) VALUES"); query8_len = strlen(query8); query11_len = strlen(query11); posuffix_len = strlen(posuffix); /* initialize the ping structure variables */ snprintf(ping->ping_status, 50, "down"); snprintf(ping->ping_response, SMALL_BUFSIZE, "Ping not performed due to setting."); snprintf(ping->snmp_status, 50, "down"); snprintf(ping->snmp_response, SMALL_BUFSIZE, "SNMP not performed due to setting or ping result"); /* if the host is a real host. Note host_id=0 is not host based data source */ if (host_id) { /* get data about this host */ if ((result = db_query(&mysql, LOCAL, query2)) != 0) { num_rows = mysql_num_rows(result); if (num_rows != 1) { mysql_free_result(result); if (local_cnn != NULL) { db_release_connection(LOCAL, local_cnn->id); } else { SPINE_LOG(("WARNING: Device[%i] HT[%i] Trying to close uninitialized local connection.", host_id, host_thread)); } if (set.poller_id > 1 && set.mode == REMOTE_ONLINE) { if (remote_cnn != NULL) { db_release_connection(REMOTE, remote_cnn->id); } else { SPINE_LOG(("WARNING: Device[%i] HT[%i] Trying to close uninitialized remote connection.", host_id, host_thread)); } } SPINE_FREE(host); SPINE_FREE(reindex); SPINE_FREE(ping); SPINE_FREE(error_string); SPINE_FREE(buf_size); SPINE_FREE(buf_errors); return; } /* fetch the result */ row = mysql_fetch_row(result); if (row) { /* initialize variables first */ host->id = 0; // 0 host->hostname[0] = '\0'; // 1 host->snmp_session = NULL; // - host->snmp_community[0] = '\0'; // 2 host->snmp_version = 1; // 3 host->snmp_username[0] = '\0'; // 4 host->snmp_password[0] = '\0'; // 5 host->snmp_auth_protocol[0] = '\0'; // 6 host->snmp_priv_passphrase[0] = '\0'; // 7 host->snmp_priv_protocol[0] = '\0'; // 8 host->snmp_context[0] = '\0'; // 9 host->snmp_engine_id[0] = '\0'; // 10 host->snmp_port = 161; // 11 host->snmp_timeout = 500; // 12 host->snmp_retries = set.snmp_retries; // - host->max_oids = 10; // 13 host->availability_method = 0; // 14 host->ping_method = 0; // 15 host->ping_port = 23; // 16 host->ping_timeout = 500; // 17 host->ping_retries = 2; // 18 host->status = HOST_UP; // 19 host->status_event_count = 0; // 20 host->status_fail_date[0] = '\0'; // 21 host->status_rec_date[0] = '\0'; // 22 host->status_last_error[0] = '\0'; // 23 host->min_time = 0; // 24 host->max_time = 0; // 25 host->cur_time = 0; // 26 host->avg_time = 0; // 27 host->total_polls = 0; // 28 host->failed_polls = 0; // 29 host->availability = 100; // 30 host->snmp_sysUpTimeInstance = 0; // 31 host->snmp_sysDescr[0] = '\0'; // 32 host->snmp_sysObjectID[0] = '\0'; // 33 host->snmp_sysContact[0] = '\0'; // 34 host->snmp_sysName[0] = '\0'; // 35 host->snmp_sysLocation[0] = '\0'; // 36 /* populate host structure */ host->ignore_host = FALSE; if (row[0] != NULL) host->id = atoi(row[0]); if (row[1] != NULL) { name = get_namebyhost(row[1], NULL); STRNCOPY(host->hostname, name->hostname); host->ping_port = name->port; SPINE_FREE(name); } if (row[2] != NULL) STRNCOPY(host->snmp_community, row[2]); if (row[3] != NULL) host->snmp_version = atoi(row[3]); if (row[4] != NULL) STRNCOPY(host->snmp_username, row[4]); if (row[5] != NULL) STRNCOPY(host->snmp_password, row[5]); if (row[6] != NULL) STRNCOPY(host->snmp_auth_protocol, row[6]); if (row[7] != NULL) STRNCOPY(host->snmp_priv_passphrase, row[7]); if (row[8] != NULL) STRNCOPY(host->snmp_priv_protocol, row[8]); if (row[9] != NULL) STRNCOPY(host->snmp_context, row[9]); if (row[10] != NULL) STRNCOPY(host->snmp_engine_id, row[10]); if (row[11] != NULL) host->snmp_port = atoi(row[11]); if (row[12] != NULL) host->snmp_timeout = atoi(row[12]); if (row[13] != NULL) host->max_oids = atoi(row[13]); if (row[14] != NULL) host->availability_method = atoi(row[14]); if (row[15] != NULL) host->ping_method = atoi(row[15]); if (row[16] != NULL) host->ping_port = atoi(row[16]); if (row[17] != NULL) host->ping_timeout = atoi(row[17]); if (row[18] != NULL) host->ping_retries = atoi(row[18]); if (row[19] != NULL) host->status = atoi(row[19]); if (row[20] != NULL) host->status_event_count = atoi(row[20]); if (row[21] != NULL) STRNCOPY(host->status_fail_date, row[21]); if (row[22] != NULL) STRNCOPY(host->status_rec_date, row[22]); if (row[23] != NULL) STRNCOPY(host->status_last_error, row[23]); if (row[24] != NULL) host->min_time = atof(row[24]); if (row[25] != NULL) host->max_time = atof(row[25]); if (row[26] != NULL) host->cur_time = atof(row[26]); if (row[27] != NULL) host->avg_time = atof(row[27]); if (row[28] != NULL) host->total_polls = atoi(row[28]); if (row[29] != NULL) host->failed_polls = atoi(row[29]); if (row[30] != NULL) host->availability = atof(row[30]); if (row[31] != NULL) host->snmp_sysUpTimeInstance=atoll(row[31]); if (row[32] != NULL) db_escape(&mysql, host->snmp_sysDescr, sizeof(host->snmp_sysDescr), row[32]); if (row[33] != NULL) db_escape(&mysql, host->snmp_sysObjectID, sizeof(host->snmp_sysObjectID), row[33]); if (row[34] != NULL) db_escape(&mysql, host->snmp_sysContact, sizeof(host->snmp_sysContact), row[34]); if (row[35] != NULL) db_escape(&mysql, host->snmp_sysName, sizeof(host->snmp_sysName), row[35]); if (row[36] != NULL) db_escape(&mysql, host->snmp_sysLocation, sizeof(host->snmp_sysLocation), row[36]); /* correct max_oid bounds issues */ if ((host->max_oids == 0) || (host->max_oids > 100)) { SPINE_LOG(("Device[%i] HT[%i] WARNING: Max OIDS is out of range with value of '%i'. Resetting to default of 5", host_id, host_thread, host->max_oids)); host->max_oids = 5; } /* free the host result */ mysql_free_result(result); if (((host->snmp_version >= 1) && (host->snmp_version <= 2) && (strlen(host->snmp_community) > 0)) || (host->snmp_version == 3)) { host->snmp_session = snmp_host_init(host->id, host->hostname, host->snmp_version, host->snmp_community, host->snmp_username, host->snmp_password, host->snmp_auth_protocol, host->snmp_priv_passphrase, host->snmp_priv_protocol, host->snmp_context, host->snmp_engine_id, host->snmp_port, host->snmp_timeout); } else { host->snmp_session = NULL; } /* perform a check to see if the host is alive by polling it's SysDesc * if the host down from an snmp perspective, don't poll it. * function sets the ignore_host bit */ if ((host->availability_method == AVAIL_SNMP) && (strlen(host->snmp_community) == 0) && (host->snmp_version < 3)) { host->ignore_host = FALSE; update_host_status(HOST_UP, host, ping, host->availability_method); if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] HT[%i] No host availability check possible for '%s'", host->id, host_thread, host->hostname)); } else { SPINE_LOG_MEDIUM(("Device[%i] HT[%i] No host availability check possible for '%s'", host->id, host_thread, host->hostname)); } } else { if (ping_host(host, ping) == HOST_UP) { host->ignore_host = FALSE; if (host_thread == 1) { update_host_status(HOST_UP, host, ping, host->availability_method); if ((host->availability_method != AVAIL_PING) && (host->availability_method != AVAIL_NONE)) { if (host->snmp_session != NULL && set.mibs) { get_system_information(host, &mysql, 1); ignore_sysinfo = FALSE; } } } } else { host->ignore_host = TRUE; if (host_thread == 1) { update_host_status(HOST_DOWN, host, ping, host->availability_method); } } } /* update host table */ if (host_thread == 1) { if (!ignore_sysinfo) { if (host->ignore_host != TRUE) { snprintf(update_sql, BIG_BUFSIZE, "UPDATE host " "SET status='%i', status_event_count='%i', status_fail_date=FROM_UNIXTIME(%s)," " status_rec_date=FROM_UNIXTIME(%s), status_last_error='%s', min_time='%f'," " max_time='%f', cur_time='%f', avg_time='%f', total_polls='%i'," " failed_polls='%i', availability='%.4f', snmp_sysDescr='%s', " " snmp_sysObjectID='%s', snmp_sysUpTimeInstance='%llu', " " snmp_sysContact='%s', snmp_sysName='%s', snmp_sysLocation='%s' " "WHERE id='%i'", host->status, host->status_event_count, host->status_fail_date, host->status_rec_date, host->status_last_error, host->min_time, host->max_time, host->cur_time, host->avg_time, host->total_polls, host->failed_polls, host->availability, host->snmp_sysDescr, host->snmp_sysObjectID, host->snmp_sysUpTimeInstance, host->snmp_sysContact, host->snmp_sysName, host->snmp_sysLocation, host->id); } else { snprintf(update_sql, BIG_BUFSIZE, "UPDATE host " "SET status='%i', status_event_count='%i', status_fail_date=FROM_UNIXTIME(%s)," " status_rec_date=FROM_UNIXTIME(%s), status_last_error='%s', min_time='%f'," " max_time='%f', cur_time='%f', avg_time='%f', total_polls='%i'," " failed_polls='%i', availability='%.4f' " "WHERE id='%i'", host->status, host->status_event_count, host->status_fail_date, host->status_rec_date, host->status_last_error, host->min_time, host->max_time, host->cur_time, host->avg_time, host->total_polls, host->failed_polls, host->availability, host->id); } } else { snprintf(update_sql, BIG_BUFSIZE, "UPDATE host " "SET status='%i', status_event_count='%i', status_fail_date=FROM_UNIXTIME(%s)," " status_rec_date=FROM_UNIXTIME(%s), status_last_error='%s', min_time='%f'," " max_time='%f', cur_time='%f', avg_time='%f', total_polls='%i'," " failed_polls='%i', availability='%.4f' " "WHERE id='%i'", host->status, host->status_event_count, host->status_fail_date, host->status_rec_date, host->status_last_error, host->min_time, host->max_time, host->cur_time, host->avg_time, host->total_polls, host->failed_polls, host->availability, host->id); } db_insert(&mysql, LOCAL, update_sql); } } else { SPINE_LOG(("Device[%i] HT[%i] ERROR: MySQL Returned a Null Device Result", host->id, host_thread)); num_rows = 0; host->ignore_host = TRUE; } } else { num_rows = 0; host->ignore_host = TRUE; } } else { host->id = 0; host->max_oids = 1; host->snmp_session = NULL; host->ignore_host = FALSE; } if (set.ping_only) { SPINE_FREE(host); SPINE_FREE(reindex); SPINE_FREE(ping); SPINE_FREE(error_string); SPINE_FREE(buf_size); SPINE_FREE(buf_errors); if (local_cnn != NULL) { db_release_connection(LOCAL, local_cnn->id); } else { SPINE_LOG(("WARNING: Device[%i] HT[%i] Trying to close uninitialized local connection.", host_id, host_thread)); } if (set.poller_id > 1 && set.mode == REMOTE_ONLINE) { if (remote_cnn != NULL) { db_release_connection(REMOTE, remote_cnn->id); } else { SPINE_LOG(("WARNING: Device[%i] HT[%i] Trying to close uninitialized remote connection.", host_id, host_thread)); } } mysql_thread_end(); return; } /* do the reindex check for this host if not script based */ if ((!host->ignore_host) && (host_id)) { if ((result = db_query(&mysql, LOCAL, query4)) != 0) { num_rows = mysql_num_rows(result); if (num_rows > 0) { if (is_debug_device(host->id)) { SPINE_LOG(("DEBUG: Device[%i] HT[%i] RECACHE: Processing %i items in the auto reindex cache for '%s'", host->id, host_thread, num_rows, host->hostname)); } else { SPINE_LOG_DEBUG(("DEBUG: Device[%i] HT[%i] RECACHE: Processing %i items in the auto reindex cache for '%s'", host->id, host_thread, num_rows, host->hostname)); } // Cache uptime in case we need it again sysUptime[0] = '\0'; while ((row = mysql_fetch_row(result))) { assert_fail = FALSE; reindex_err = FALSE; /* initialize the reindex struction */ reindex->data_query_id = 0; reindex->action = -1; reindex->op[0] = '\0'; reindex->assert_value[0] = '\0'; reindex->arg1[0] = '\0'; if (row[0] != NULL) reindex->data_query_id = atoi(row[0]); if (row[1] != NULL) reindex->action = atoi(row[1]); if (row[2] != NULL) snprintf(reindex->op, sizeof(reindex->op), "%s", row[2]); if (row[3] != NULL) snprintf(reindex->assert_value, sizeof(reindex->assert_value), "%s", row[3]); if (row[4] != NULL) snprintf(reindex->arg1, sizeof(reindex->arg1), "%s", row[4]); /* shortcut assertion checks if a data query reindex has already been queued */ if ((last_data_query_id == reindex->data_query_id) && (!previous_assert_failure)) { perform_assert = TRUE; } else if (last_data_query_id != reindex->data_query_id) { last_data_query_id = reindex->data_query_id; perform_assert = TRUE; previous_assert_failure = FALSE; } else { perform_assert = FALSE; } poll_result = NULL; if (perform_assert) { switch(reindex->action) { case POLLER_ACTION_SNMP: /* snmp */ /* if there is no snmp session, don't probe */ if (host->snmp_session == NULL) { reindex_err = TRUE; } /* check to see if you are checking uptime */ if (!reindex_err) { if ((strstr(reindex->arg1, ".1.3.6.1.2.1.1.3.0") || strstr(reindex->arg1, ".1.3.6.1.6.3.10.2.1.3.0")) && strlen(sysUptime) > 0) { if (!(poll_result = (char *) malloc(BUFSIZE))) { die("ERROR: Fatal malloc error: poller.c poll_result"); } poll_result[0] = '\0'; snprintf(poll_result, BUFSIZE, "%s", sysUptime); } else if (strstr(reindex->arg1, ".1.3.6.1.2.1.1.3.0")) { // Ensure uptime is empty to start with snprintf(sysUptime, BUFSIZE, ""); // Check the legacy poll result first poll_result = snmp_get(host, reindex->arg1); if (poll_result && is_numeric(poll_result)) { snprintf(sysUptime, BUFSIZE, "%s", poll_result); } if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] HT[%i] DQ[%i] Legacy Uptime Result: %s, Is Numeric: %d", host->id, host_thread, reindex->data_query_id, poll_result, is_numeric(poll_result) )); } else { SPINE_LOG_MEDIUM(("Device[%i] HT[%i] DQ[%i] Legacy Uptime Result: %s, Is Numeric: %d", host->id, host_thread, reindex->data_query_id, poll_result, is_numeric(poll_result) )); } SPINE_FREE(poll_result); // Check the modern uptimeInsance second poll_result = snmp_get_base(host, ".1.3.6.1.6.3.10.2.1.3.0", false); if (poll_result && is_numeric(poll_result)) { snprintf(sysUptime, BUFSIZE, "%llu", atoll(poll_result) * 100); } // Use the primed uptime to repopulate the poll_result // This ensures whichever response was valid gets used snprintf(poll_result, BUFSIZE, "%s", sysUptime); if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] HT[%i] DQ[%i] Extended Uptime Result: %s, Is Numeric: %d", host->id, host_thread, reindex->data_query_id, poll_result, is_numeric(poll_result) )); } else { SPINE_LOG_MEDIUM(("Device[%i] HT[%i] DQ[%i] Extended Uptime Result: %s, Is Numeric: %d", host->id, host_thread, reindex->data_query_id, poll_result, is_numeric(poll_result) )); } } else { poll_result = snmp_get(host, reindex->arg1); } if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] HT[%i] DQ[%i] RECACHE OID: %s, (assert: %s %s output: %s)", host->id, host_thread, reindex->data_query_id, reindex->arg1, reindex->assert_value, reindex->op, poll_result)); } else { SPINE_LOG_MEDIUM(("Device[%i] HT[%i] DQ[%i] RECACHE OID: %s, (assert: %s %s output: %s)", host->id, host_thread, reindex->data_query_id, reindex->arg1, reindex->assert_value, reindex->op, poll_result)); } } else { SPINE_LOG(("WARNING: Device[%i] HT[%i] DQ[%i] Reindex Check FAILED: No SNMP Session. If not an SNMP host, don't use Uptime Goes Backwards!", host->id, host_thread, reindex->data_query_id)); } break; case POLLER_ACTION_SCRIPT: /* script (popen) */ poll_result = trim(exec_poll(host, reindex->arg1, reindex->data_query_id, "DQ")); if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] HT[%i] DQ[%i] RECACHE CMD: %s, output: %s", host->id, host_thread, reindex->data_query_id, reindex->arg1, poll_result)); } else { SPINE_LOG_MEDIUM(("Device[%i] HT[%i] DQ[%i] RECACHE CMD: %s, output: %s", host->id, host_thread, reindex->data_query_id, reindex->arg1, poll_result)); } break; case POLLER_ACTION_PHP_SCRIPT_SERVER: /* script (php script server) */ php_process = php_get_process(); poll_result = trim(php_cmd(reindex->arg1, php_process)); if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] HT[%i] DQ[%i] RECACHE SERVER: %s, output: %s", host->id, host_thread, reindex->data_query_id, reindex->arg1, poll_result)); } else { SPINE_LOG_MEDIUM(("Device[%i] HT[%i] DQ[%i] RECACHE SERVER: %s, output: %s", host->id, host_thread, reindex->data_query_id, reindex->arg1, poll_result)); } break; case POLLER_ACTION_SNMP_COUNT: /* snmp; count items */ if (!(poll_result = (char *) malloc(BUFSIZE))) { die("ERROR: Fatal malloc error: poller.c poll_result"); } poll_result[0] = '\0'; snprintf(poll_result, BUFSIZE, "%d", snmp_count(host, reindex->arg1)); if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] HT[%i] DQ[%i] RECACHE OID COUNT: %s, output: %s", host->id, host_thread, reindex->data_query_id, reindex->arg1, poll_result)); } else { SPINE_LOG_MEDIUM(("Device[%i] HT[%i] DQ[%i] RECACHE OID COUNT: %s, output: %s", host->id, host_thread, reindex->data_query_id, reindex->arg1, poll_result)); } break; case POLLER_ACTION_SCRIPT_COUNT: /* script (popen); count items by counting line feeds */ if (!(poll_result = (char *) malloc(BUFSIZE))) { die("ERROR: Fatal malloc error: poller.c poll_result"); } poll_result[0] = '\0'; snprintf(poll_result, BUFSIZE, "%d", char_count(exec_poll(host, reindex->arg1, reindex->data_query_id, "DQ"), '\n')); if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] HT[%i] DQ[%i] RECACHE CMD COUNT: %s, output: %s", host->id, host_thread, reindex->data_query_id, reindex->arg1, poll_result)); } else { SPINE_LOG_MEDIUM(("Device[%i] HT[%i] DQ[%i] RECACHE CMD COUNT: %s, output: %s", host->id, host_thread, reindex->data_query_id, reindex->arg1, poll_result)); } break; case POLLER_ACTION_PHP_SCRIPT_SERVER_COUNT: /* script (php script server); count number of lines */ if (!(poll_result = (char *) malloc(BUFSIZE))) { die("ERROR: Fatal malloc error: poller.c poll_result"); } poll_result[0] = '\0'; php_process = php_get_process(); sprintf(poll_result, "%d", char_count(php_cmd(reindex->arg1, php_process), '\n')); if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] HT[%i] DQ[%i] RECACHE SERVER COUNT: %s, output: %s", host->id, host_thread, reindex->data_query_id, reindex->arg1, poll_result)); } else { SPINE_LOG_MEDIUM(("Device[%i] HT[%i] DQ[%i] RECACHE SERVER COUNT: %s, output: %s", host->id, host_thread, reindex->data_query_id, reindex->arg1, poll_result)); } break; default: SPINE_LOG(("Device[%i] HT[%i] ERROR: Unknown Assert Action!", host->id, host_thread)); } if (!reindex_err) { if (!(query3 = (char *)malloc(LRG_BUFSIZE))) { die("ERROR: Fatal malloc error: poller.c reindex insert!"); } query3[0] = '\0'; /* assume ok if host is up and result wasn't obtained */ if (poll_result == NULL || (IS_UNDEFINED(poll_result)) || (STRIMATCH(poll_result, "No Such Instance"))) { if (is_debug_device(host->id) || set.spine_log_level == 2) { SPINE_LOG(("Device[%i] HT[%i] DQ[%i] RECACHE ASSERT FAILED: '%s=%s'", host->id, host_thread, reindex->data_query_id, reindex->assert_value, poll_result)); } assert_fail = FALSE; } else if ((!strcmp(reindex->op, "=")) && (strcmp(reindex->assert_value, poll_result))) { if (is_debug_device(host->id) || set.spine_log_level == 2) { SPINE_LOG(("Device[%i] HT[%i] DQ[%i] RECACHE ASSERT FAILED: '%s=%s'", host->id, host_thread, reindex->data_query_id, reindex->assert_value, poll_result)); } else { if (set.spine_log_level == 1) { errors++; } SPINE_LOG(("Device[%i] HT[%i] DQ[%i] RECACHE ASSERT FAILED: '%s=%s'", host->id, host_thread, reindex->data_query_id, reindex->assert_value, poll_result)); } if (host_thread == 1) { snprintf(query3, LRG_BUFSIZE, "REPLACE INTO poller_command (poller_id, time, action,command) values (%i, NOW(), %i, '%i:%i')", set.poller_id, POLLER_COMMAND_REINDEX, host->id, reindex->data_query_id); if (set.poller_id > 1 && set.mode == REMOTE_ONLINE) { db_insert(&mysqlr, REMOTE, query3); } else { db_insert(&mysql, LOCAL, query3); } /* set zeros */ memset(query3, 0, buf_length); } assert_fail = TRUE; previous_assert_failure = TRUE; } else if ((!strcmp(reindex->op, ">")) && (atoll(reindex->assert_value) < atoll(poll_result))) { if (is_debug_device(host->id) || set.spine_log_level == 2) { SPINE_LOG(("Device[%i] HT[%i] DQ[%i] RECACHE ASSERT FAILED: '%s>%s'", host->id, host_thread, reindex->data_query_id, reindex->assert_value, poll_result)); } else { if (set.spine_log_level == 1) { errors++; } SPINE_LOG(("Device[%i] HT[%i] DQ[%i] RECACHE ASSERT FAILED: '%s>%s'", host->id, host_thread, reindex->data_query_id, reindex->assert_value, poll_result)); } if (host_thread == 1) { snprintf(query3, LRG_BUFSIZE, "REPLACE INTO poller_command (poller_id, time, action, command) ValueS (%i, NOW(), %i, '%i:%i')", set.poller_id, POLLER_COMMAND_REINDEX, host->id, reindex->data_query_id); if (set.poller_id > 1 && set.mode == REMOTE_ONLINE) { db_insert(&mysqlr, REMOTE, query3); } else { db_insert(&mysql, LOCAL, query3); } /* set zeros */ memset(query3, 0, buf_length); } assert_fail = TRUE; previous_assert_failure = TRUE; /* if uptime is set to '0' don't fail out */ } else if (strcmp(reindex->assert_value, "0")) { if ((!strcmp(reindex->op, "<")) && (atoll(reindex->assert_value) > atoll(poll_result))) { if (is_debug_device(host->id) || set.spine_log_level == 2) { SPINE_LOG(("Device[%i] HT[%i] DQ[%i] RECACHE ASSERT FAILED: '%s<%s'", host->id, host_thread, reindex->data_query_id, reindex->assert_value, poll_result)); } else { if (set.spine_log_level == 1) { errors++; } SPINE_LOG(("Device[%i] HT[%i] DQ[%i] RECACHE ASSERT FAILED: '%s<%s'", host->id, host_thread, reindex->data_query_id, reindex->assert_value, poll_result)); } if (host_thread == 1) { snprintf(query3, LRG_BUFSIZE, "REPLACE INTO poller_command (poller_id, time, action, command) VALUES (%i, NOW(), %i, '%i:%i')", set.poller_id, POLLER_COMMAND_REINDEX, host->id, reindex->data_query_id); if (set.poller_id > 1 && set.mode == REMOTE_ONLINE) { db_insert(&mysqlr, REMOTE, query3); } else { db_insert(&mysql, LOCAL, query3); } /* set zeros */ memset(query3, 0, buf_length); } assert_fail = TRUE; previous_assert_failure = TRUE; } } /* update 'poller_reindex' with the correct information if: * 1) the assert fails * 2) the OP code is > or < meaning the current value could have changed without causing * the assert to fail */ if ((assert_fail) || (!strcmp(reindex->op, ">")) || (!strcmp(reindex->op, "<"))) { if (host_thread == 1) { db_escape(&mysql, temp_poll_result, sizeof(temp_poll_result), poll_result); db_escape(&mysql, temp_arg1, sizeof(temp_arg1), reindex->arg1); snprintf(query3, LRG_BUFSIZE, "UPDATE poller_reindex SET assert_value='%s' WHERE host_id='%i' AND data_query_id='%i' AND arg1='%s'", temp_poll_result, host_id, reindex->data_query_id, temp_arg1); db_insert(&mysql, LOCAL, query3); /* set zeros */ memset(query3, 0, buf_length); } if ((assert_fail) && ((!strcmp(reindex->op, "<")) || (!strcmp(reindex->arg1,".1.3.6.1.2.1.1.3.0") && !strcmp(reindex->arg1, ".1.3.6.1.6.3.10.2.1.3.0")))) { spike_kill = TRUE; if (is_debug_device(host->id) || set.spine_log_level == 2) { SPINE_LOG(("Device[%i] HT[%i] NOTICE: Spike Kill in Effect for '%s'", host_id, host_thread, host->hostname)); } else { if (set.spine_log_level == 1) { errors++; } SPINE_LOG_MEDIUM(("Device[%i] HT[%i] NOTICE: Spike Kill in Effect for '%s'", host_id, host_thread, host->hostname)); } } } SPINE_FREE(query3); SPINE_FREE(poll_result); } } } } else { if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] HT[%i] Device has no information for recache.", host->id, host_thread)); } else { SPINE_LOG_HIGH(("Device[%i] HT[%i] Device has no information for recache.", host->id, host_thread)); } } /* free the host result */ mysql_free_result(result); } else { SPINE_LOG(("Device[%i] HT[%i] ERROR: RECACHE Query Returned Null Result!", host->id, host_thread)); } /* close the host snmp session, we will create again momentarily */ if (host->snmp_session != NULL) { snmp_host_cleanup(host->snmp_session); host->snmp_session = NULL; } } /* calculate the number of poller items to poll this cycle */ num_rows = 0; if (set.poller_interval == 0) { /* get the poller items */ if ((result = db_query(&mysql, LOCAL, query1)) != 0) { num_rows = mysql_num_rows(result); } else { SPINE_LOG(("Device[%i] HT[%i] ERROR: Unable to Retrieve Rows due to Null Result!", host->id, host_thread)); } } else { /* get the poller items */ if ((result = db_query(&mysql, LOCAL, query5)) != 0) { num_rows = mysql_num_rows(result); } else { SPINE_LOG(("Device[%i] HT[%i] ERROR: Unable to Retrieve Rows due to Null Result!", host->id, host_thread)); } } if (num_rows > 0) { /* retreive each hosts polling items from poller cache and load into array */ poller_items = (target_t *) calloc(num_rows, sizeof(target_t)); i = 0; while ((row = mysql_fetch_row(result))) { /* initialize monitored object */ poller_items[i].target_id = 0; poller_items[i].action = -1; poller_items[i].hostname[0] = '\0'; poller_items[i].snmp_community[0] = '\0'; poller_items[i].snmp_version = 1; poller_items[i].snmp_username[0] = '\0'; poller_items[i].snmp_password[0] = '\0'; poller_items[i].snmp_auth_protocol[0] = '\0'; poller_items[i].snmp_priv_passphrase[0] = '\0'; poller_items[i].snmp_priv_protocol[0] = '\0'; poller_items[i].snmp_context[0] = '\0'; poller_items[i].snmp_engine_id[0] = '\0'; poller_items[i].snmp_port = 161; poller_items[i].snmp_timeout = 500; poller_items[i].rrd_name[0] = '\0'; poller_items[i].rrd_path[0] = '\0'; poller_items[i].arg1[0] = '\0'; poller_items[i].arg2[0] = '\0'; poller_items[i].arg3[0] = '\0'; poller_items[i].local_data_id = 0; poller_items[i].rrd_num = 0; if (row[0] != NULL) poller_items[i].action = atoi(row[0]); if (row[1] != NULL) snprintf(poller_items[i].hostname, sizeof(poller_items[i].hostname), "%s", row[1]); if (row[2] != NULL) snprintf(poller_items[i].snmp_community, sizeof(poller_items[i].snmp_community), "%s", row[2]); if (row[3] != NULL) poller_items[i].snmp_version = atoi(row[3]); if (row[4] != NULL) snprintf(poller_items[i].snmp_username, sizeof(poller_items[i].snmp_username), "%s", row[4]); if (row[5] != NULL) snprintf(poller_items[i].snmp_password, sizeof(poller_items[i].snmp_password), "%s", row[5]); if (row[6] != NULL) snprintf(poller_items[i].rrd_name, sizeof(poller_items[i].rrd_name), "%s", row[6]); if (row[7] != NULL) snprintf(poller_items[i].rrd_path, sizeof(poller_items[i].rrd_path), "%s", row[7]); if (row[8] != NULL) snprintf(poller_items[i].arg1, sizeof(poller_items[i].arg1), "%s", row[8]); if (row[9] != NULL) snprintf(poller_items[i].arg2, sizeof(poller_items[i].arg2), "%s", row[9]); if (row[10] != NULL) snprintf(poller_items[i].arg3, sizeof(poller_items[i].arg3), "%s", row[10]); if (row[11] != NULL) poller_items[i].local_data_id = atoi(row[11]); if (row[12] != NULL) poller_items[i].rrd_num = atoi(row[12]); if (row[13] != NULL) poller_items[i].snmp_port = atoi(row[13]); if (row[14] != NULL) poller_items[i].snmp_timeout = atoi(row[14]); if (row[15] != NULL) snprintf(poller_items[i].snmp_auth_protocol, sizeof(poller_items[i].snmp_auth_protocol), "%s", row[15]); if (row[16] != NULL) snprintf(poller_items[i].snmp_priv_passphrase, sizeof(poller_items[i].snmp_priv_passphrase), "%s", row[16]); if (row[17] != NULL) snprintf(poller_items[i].snmp_priv_protocol, sizeof(poller_items[i].snmp_priv_protocol), "%s", row[17]); if (row[18] != NULL) snprintf(poller_items[i].snmp_context, sizeof(poller_items[i].snmp_context), "%s", row[18]); if (row[19] != NULL) snprintf(poller_items[i].snmp_engine_id, sizeof(poller_items[i].snmp_engine_id), "%s", row[19]); SET_UNDEFINED(poller_items[i].result); if (poller_items[i].action == POLLER_ACTION_SNMP) { snmp_poller_items++; } i++; } /* free the mysql result */ mysql_free_result(result); /* create an array for snmp oids */ snmp_oids = (snmp_oids_t *) calloc(host->max_oids, sizeof(snmp_oids_t)); /* initialize all the memory to insure we don't get issues */ memset(snmp_oids, 0, sizeof(snmp_oids_t)*host->max_oids); /* log an informative message */ if (is_debug_device(host_id)) { SPINE_LOG(("Device[%i] HT[%i] NOTE: There are '%i' Polling Items for this Device", host_id, host_thread, num_rows)); } else { SPINE_LOG_MEDIUM(("Device[%i] HT[%i] NOTE: There are '%i' Polling Items for this Device", host_id, host_thread, num_rows)); } i = 0; k = 0; while ((i < num_rows) && (!host->ignore_host)) { thread_start = get_time_as_double(); switch(poller_items[i].action) { case POLLER_ACTION_SNMP: /* raw SNMP poll */ /* initialize or reinitialize snmp as required */ if (k == 0) { last_snmp_port = poller_items[i].snmp_port; last_snmp_version = poller_items[i].snmp_version; STRNCOPY(last_snmp_community, poller_items[i].snmp_community); STRNCOPY(last_snmp_username, poller_items[i].snmp_username); STRNCOPY(last_snmp_password, poller_items[i].snmp_password); STRNCOPY(last_snmp_auth_protocol, poller_items[i].snmp_auth_protocol); STRNCOPY(last_snmp_priv_passphrase, poller_items[i].snmp_priv_passphrase); STRNCOPY(last_snmp_priv_protocol, poller_items[i].snmp_priv_protocol); STRNCOPY(last_snmp_context, poller_items[i].snmp_context); STRNCOPY(last_snmp_engine_id, poller_items[i].snmp_engine_id); host->snmp_session = snmp_host_init(host->id, poller_items[i].hostname, poller_items[i].snmp_version, poller_items[i].snmp_community, poller_items[i].snmp_username, poller_items[i].snmp_password, poller_items[i].snmp_auth_protocol, poller_items[i].snmp_priv_passphrase, poller_items[i].snmp_priv_protocol, poller_items[i].snmp_context, poller_items[i].snmp_engine_id, poller_items[i].snmp_port, poller_items[i].snmp_timeout); k++; } /* catch snmp initialization issues */ if (host->snmp_session == NULL) { host->ignore_host = TRUE; break; } /* some snmp data changed from poller item to poller item. therefore, poll host and store data */ if ((last_snmp_port != poller_items[i].snmp_port) || (last_snmp_version != poller_items[i].snmp_version) || (poller_items[i].snmp_version < 3 && (!STRMATCH(last_snmp_community, poller_items[i].snmp_community))) || (poller_items[i].snmp_version > 2 && ((!STRMATCH(last_snmp_username, poller_items[i].snmp_username)) || (!STRMATCH(last_snmp_password, poller_items[i].snmp_password)) || (!STRMATCH(last_snmp_auth_protocol, poller_items[i].snmp_auth_protocol)) || (!STRMATCH(last_snmp_priv_passphrase, poller_items[i].snmp_priv_passphrase)) || (!STRMATCH(last_snmp_priv_protocol, poller_items[i].snmp_priv_protocol)) || (!STRMATCH(last_snmp_context, poller_items[i].snmp_context)) || (!STRMATCH(last_snmp_engine_id, poller_items[i].snmp_engine_id))))) { if (num_oids > 0) { snmp_get_multi(host, poller_items, snmp_oids, num_oids); for (j = 0; j < num_oids; j++) { if (host->ignore_host) { SPINE_LOG(("Device[%i] HT[%i] DS[%i] WARNING: SNMP timeout detected [%i ms], ignoring host '%s'", host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, host->snmp_timeout, host->hostname)); SET_UNDEFINED(snmp_oids[j].result); } else if (IS_UNDEFINED(snmp_oids[j].result)) { if (set.spine_log_level == 2) { SPINE_LOG(("WARNING: Invalid Response, Device[%i] HT[%i] DS[%i] SNMP: v%i: %s, dsname: %s, oid: %s, value: %s", host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, host->snmp_version, host->hostname, poller_items[snmp_oids[j].array_position].rrd_name, poller_items[snmp_oids[j].array_position].arg1, snmp_oids[j].result)); } else if (set.spine_log_level == 1) { errors++; buffer_output_errors(error_string, buf_size, buf_errors, host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, false); } /* continue */ } else if ((is_numeric(snmp_oids[j].result)) || (is_multipart_output(snmp_oids[j].result))) { /* continue */ } else if (is_hexadecimal(snmp_oids[j].result, TRUE)) { snprintf(snmp_oids[j].result, RESULTS_BUFFER, "%lld", hex2dec(snmp_oids[j].result)); } else if ((STRIMATCH(snmp_oids[j].result, "U")) || (STRIMATCH(snmp_oids[j].result, "Nan"))) { if (set.spine_log_level == 2) { SPINE_LOG(("WARNING: Invalid Response, Device[%i] HT[%i] DS[%i] SNMP: v%i: %s, dsname: %s, oid: %s, value: %s", host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, host->snmp_version, host->hostname, poller_items[snmp_oids[j].array_position].rrd_name, poller_items[snmp_oids[j].array_position].arg1, snmp_oids[j].result)); } else if (set.spine_log_level == 1) { errors++; buffer_output_errors(error_string, buf_size, buf_errors, host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, false); } /* is valid output, continue */ } else { /* remove double or single quotes from string */ snprintf(temp_result, RESULTS_BUFFER, "%s", strip_alpha(trim(snmp_oids[j].result))); snprintf(snmp_oids[j].result , RESULTS_BUFFER, "%s", temp_result); /* detect erroneous non-numeric result */ if (!validate_result(snmp_oids[j].result)) { if (set.spine_log_level == 2) { SPINE_LOG(("WARNING: Invalid Response, Device[%i] HT[%i] DS[%i] SNMP: v%i: %s, dsname: %s, oid: %s, value: %s", host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, host->snmp_version, host->hostname, poller_items[snmp_oids[j].array_position].rrd_name, poller_items[snmp_oids[j].array_position].arg1, snmp_oids[j].result)); } else if (set.spine_log_level == 1) { errors++; buffer_output_errors(error_string, buf_size, buf_errors, host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, false); } SET_UNDEFINED(snmp_oids[j].result); } } snprintf(poller_items[snmp_oids[j].array_position].result, RESULTS_BUFFER, "%s", snmp_oids[j].result); thread_end = get_time_as_double(); if (is_debug_device(host_id)) { SPINE_LOG(("Device[%i] HT[%i] DS[%i] TT[%.2f] SNMP: v%i: %s, dsname: %s, oid: %s, value: %s", host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, (thread_end - thread_start) * 1000, host->snmp_version, host->hostname, poller_items[snmp_oids[j].array_position].rrd_name, poller_items[snmp_oids[j].array_position].arg1, poller_items[snmp_oids[j].array_position].result)); } else { SPINE_LOG_MEDIUM(("Device[%i] HT[%i] DS[%i] TT[%.2f] SNMP: v%i: %s, dsname: %s, oid: %s, value: %s", host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, (thread_end - thread_start) * 1000, host->snmp_version, host->hostname, poller_items[snmp_oids[j].array_position].rrd_name, poller_items[snmp_oids[j].array_position].arg1, poller_items[snmp_oids[j].array_position].result)); } } /* reset num_snmps */ num_oids = 0; /* initialize all the memory to insure we don't get issues */ memset(snmp_oids, 0, sizeof(snmp_oids_t)*host->max_oids); } if (host->snmp_session != NULL) { snmp_host_cleanup(host->snmp_session); host->snmp_session = NULL; } host->snmp_session = snmp_host_init(host->id, poller_items[i].hostname, poller_items[i].snmp_version, poller_items[i].snmp_community, poller_items[i].snmp_username, poller_items[i].snmp_password, poller_items[i].snmp_auth_protocol, poller_items[i].snmp_priv_passphrase, poller_items[i].snmp_priv_protocol, poller_items[i].snmp_context, poller_items[i].snmp_engine_id, poller_items[i].snmp_port, poller_items[i].snmp_timeout); last_snmp_port = poller_items[i].snmp_port; last_snmp_version = poller_items[i].snmp_version; STRNCOPY(last_snmp_community, poller_items[i].snmp_community); STRNCOPY(last_snmp_username, poller_items[i].snmp_username); STRNCOPY(last_snmp_password, poller_items[i].snmp_password); STRNCOPY(last_snmp_auth_protocol, poller_items[i].snmp_auth_protocol); STRNCOPY(last_snmp_priv_passphrase, poller_items[i].snmp_priv_passphrase); STRNCOPY(last_snmp_priv_protocol, poller_items[i].snmp_priv_protocol); STRNCOPY(last_snmp_context, poller_items[i].snmp_context); STRNCOPY(last_snmp_engine_id, poller_items[i].snmp_engine_id); } if (num_oids >= host->max_oids) { snmp_get_multi(host, poller_items, snmp_oids, num_oids); for (j = 0; j < num_oids; j++) { if (host->ignore_host) { SPINE_LOG(("Device[%i] HT[%i] DS[%i] WARNING: SNMP timeout detected [%i ms], ignoring host '%s'", host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, host->snmp_timeout, host->hostname)); SET_UNDEFINED(snmp_oids[j].result); } else if (IS_UNDEFINED(snmp_oids[j].result)) { if (set.spine_log_level == 2) { SPINE_LOG(("WARNING: Invalid Response, Device[%i] HT[%i] DS[%i] SNMP: v%i: %s, dsname: %s, oid: %s, value: %s", host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, host->snmp_version, host->hostname, poller_items[snmp_oids[j].array_position].rrd_name, poller_items[snmp_oids[j].array_position].arg1, snmp_oids[j].result)); } else if (set.spine_log_level == 1) { errors++; buffer_output_errors(error_string, buf_size, buf_errors, host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, false); } /* continue */ } else if ((is_numeric(snmp_oids[j].result)) || (is_multipart_output(snmp_oids[j].result))) { /* continue */ } else if (is_hexadecimal(snmp_oids[j].result, TRUE)) { snprintf(snmp_oids[j].result, RESULTS_BUFFER, "%lld", hex2dec(snmp_oids[j].result)); } else if ((STRIMATCH(snmp_oids[j].result, "U")) || (STRIMATCH(snmp_oids[j].result, "Nan"))) { if (set.spine_log_level == 2) { SPINE_LOG(("WARNING: Invalid Response, Device[%i] HT[%i] DS[%i] SNMP: v%i: %s, dsname: %s, oid: %s, value: %s", host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, host->snmp_version, host->hostname, poller_items[snmp_oids[j].array_position].rrd_name, poller_items[snmp_oids[j].array_position].arg1, snmp_oids[j].result)); } else if (set.spine_log_level == 1) { errors++; buffer_output_errors(error_string, buf_size, buf_errors, host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, false); } /* is valid output, continue */ } else { /* remove double or single quotes from string */ snprintf(temp_result, RESULTS_BUFFER, "%s", strip_alpha(trim(snmp_oids[j].result))); snprintf(snmp_oids[j].result , RESULTS_BUFFER, "%s", temp_result); /* detect erroneous non-numeric result */ if (!validate_result(snmp_oids[j].result)) { if (set.spine_log_level == 2) { SPINE_LOG(("WARNING: Invalid Response, Device[%i] HT[%i] DS[%i] SNMP: v%i: %s, dsname: %s, oid: %s, value: %s", host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, host->snmp_version, host->hostname, poller_items[snmp_oids[j].array_position].rrd_name, poller_items[snmp_oids[j].array_position].arg1, snmp_oids[j].result)); } else if (set.spine_log_level == 1) { errors++; buffer_output_errors(error_string, buf_size, buf_errors, host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, false); } SET_UNDEFINED(snmp_oids[j].result); } } snprintf(poller_items[snmp_oids[j].array_position].result, RESULTS_BUFFER, "%s", snmp_oids[j].result); thread_end = get_time_as_double(); if (is_debug_device(host_id)) { SPINE_LOG(("Device[%i] HT[%i] DS[%i] TT[%.2f] SNMP: v%i: %s, dsname: %s, oid: %s, value: %s", host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, (thread_end - thread_start) * 1000, host->snmp_version, host->hostname, poller_items[snmp_oids[j].array_position].rrd_name, poller_items[snmp_oids[j].array_position].arg1, poller_items[snmp_oids[j].array_position].result)); } else { SPINE_LOG_MEDIUM(("Device[%i] HT[%i] DS[%i] TT[%.2f] SNMP: v%i: %s, dsname: %s, oid: %s, value: %s", host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, (thread_end - thread_start) * 1000, host->snmp_version, host->hostname, poller_items[snmp_oids[j].array_position].rrd_name, poller_items[snmp_oids[j].array_position].arg1, poller_items[snmp_oids[j].array_position].result)); } if (!IS_UNDEFINED(poller_items[snmp_oids[j].array_position].result)) { /* insert a NaN in place of the actual value if the snmp agent restarts */ if ((spike_kill) && (!strstr(poller_items[snmp_oids[j].array_position].result,":"))) { SET_UNDEFINED(poller_items[snmp_oids[j].array_position].result); } } } /* reset num_snmps */ num_oids = 0; /* initialize all the memory to insure we don't get issues */ memset(snmp_oids, 0, sizeof(snmp_oids_t)*host->max_oids); } snprintf(snmp_oids[num_oids].oid, sizeof(snmp_oids[num_oids].oid), "%s", poller_items[i].arg1); snmp_oids[num_oids].array_position = i; num_oids++; break; case POLLER_ACTION_SCRIPT: /* execute script file */ poll_result = exec_poll(host, poller_items[i].arg1, poller_items[i].local_data_id, "DS"); /* process the result */ if (IS_UNDEFINED(poll_result)) { SET_UNDEFINED(poller_items[i].result); if (set.spine_log_level == 2) { SPINE_LOG(("WARNING: Invalid Response, Device[%i] HT[%i] DS[%i] SCRIPT: %s, output: %s", host_id, host_thread, poller_items[i].local_data_id, poller_items[i].arg1, poller_items[i].result)); } else if (set.spine_log_level == 1) { errors++; buffer_output_errors(error_string, buf_size, buf_errors, host_id, host_thread, poller_items[i].local_data_id, false); } } else if ((is_numeric(poll_result)) || (is_multipart_output(trim(poll_result)))) { snprintf(poller_items[i].result, RESULTS_BUFFER, "%s", poll_result); } else if (is_hexadecimal(poll_result, TRUE)) { snprintf(poller_items[i].result, RESULTS_BUFFER, "%lld", hex2dec(poll_result)); } else { /* remove double or single quotes from string */ snprintf(temp_result, RESULTS_BUFFER, "%s", strip_alpha(trim(poll_result))); snprintf(poller_items[i].result , RESULTS_BUFFER, "%s", temp_result); /* detect erroneous result. can be non-numeric */ if (!validate_result(poller_items[i].result)) { if (set.spine_log_level == 2) { SPINE_LOG(("WARNING: Invalid Response, Device[%i] HT[%i] DS[%i] SCRIPT: %s, output: %s", host_id, host_thread, poller_items[i].local_data_id, poller_items[i].arg1, poller_items[i].result)); } else if (set.spine_log_level == 1) { errors++; buffer_output_errors(error_string, buf_size, buf_errors, host_id, host_thread, poller_items[i].local_data_id, false); } SET_UNDEFINED(poller_items[i].result); } } SPINE_FREE(poll_result); thread_end = get_time_as_double(); if (is_debug_device(host_id)) { SPINE_LOG(("Device[%i] HT[%i] DS[%i] TT[%.2f] SCRIPT: %s, output: %s", host_id, host_thread, poller_items[i].local_data_id, (thread_end - thread_start) * 1000, poller_items[i].arg1, poller_items[i].result)); } else { SPINE_LOG_MEDIUM(("Device[%i] HT[%i] DS[%i] TT[%.2f] SCRIPT: %s, output: %s", host_id, host_thread, poller_items[i].local_data_id, (thread_end - thread_start) * 1000, poller_items[i].arg1, poller_items[i].result)); } if (!IS_UNDEFINED(poller_items[i].result)) { /* insert a NaN in place of the actual value if the snmp agent restarts */ if ((spike_kill) && (!strstr(poller_items[i].result,":"))) { SET_UNDEFINED(poller_items[i].result); } } break; case POLLER_ACTION_PHP_SCRIPT_SERVER: /* execute script server */ php_process = php_get_process(); poll_result = php_cmd(poller_items[i].arg1, php_process); /* process the output */ if (IS_UNDEFINED(poll_result)) { SET_UNDEFINED(poller_items[i].result); if (set.spine_log_level == 2) { SPINE_LOG(("WARNING: Invalid Response, Device[%i] HT[%i] DS[%i] SCRIPT: %s, output: %s", host_id, host_thread, poller_items[i].local_data_id, poller_items[i].arg1, poller_items[i].result)); } else if (set.spine_log_level == 1) { errors++; buffer_output_errors(error_string, buf_size, buf_errors, host_id, host_thread, poller_items[i].local_data_id, false); } } else if ((is_numeric(poll_result)) || (is_multipart_output(trim(poll_result)))) { snprintf(poller_items[i].result, RESULTS_BUFFER, "%s", poll_result); } else if (is_hexadecimal(poll_result, TRUE)) { snprintf(poller_items[i].result, RESULTS_BUFFER, "%lld", hex2dec(poll_result)); } else { /* remove double or single quotes from string */ snprintf(temp_result, RESULTS_BUFFER, "%s", strip_alpha(trim(poll_result))); snprintf(poller_items[i].result , RESULTS_BUFFER, "%s", temp_result); /* detect erroneous result. can be non-numeric */ if (!validate_result(poller_items[i].result)) { if (set.spine_log_level == 2) { SPINE_LOG(("WARNING: Invalid Response, Device[%i] HT[%i] DS[%i] SCRIPT: %s, output: %s", host_id, host_thread, poller_items[i].local_data_id, poller_items[i].arg1, poller_items[i].result)); } else if (set.spine_log_level == 1) { errors++; buffer_output_errors(error_string, buf_size, buf_errors, host_id, host_thread, poller_items[i].local_data_id, false); } SET_UNDEFINED(poller_items[i].result); } } SPINE_FREE(poll_result); thread_end = get_time_as_double(); if (is_debug_device(host_id)) { SPINE_LOG(("Device[%i] HT[%i] DS[%i] TT[%.2f] SS[%i] SERVER: %s, output: %s", host_id, host_thread, poller_items[i].local_data_id, (thread_end - thread_start) * 1000, php_process, poller_items[i].arg1, poller_items[i].result)); } else { SPINE_LOG_MEDIUM(("Device[%i] HT[%i] DS[%i] TT[%.2f] SS[%i] SERVER: %s, output: %s", host_id, host_thread, poller_items[i].local_data_id, (thread_end - thread_start) * 1000, php_process, poller_items[i].arg1, poller_items[i].result)); } if (IS_UNDEFINED(poller_items[i].result)) { /* insert a NaN in place of the actual value if the snmp agent restarts */ if ((spike_kill) && (!STRIMATCH(poller_items[i].result,":"))) { SET_UNDEFINED(poller_items[i].result); } } break; default: /* unknown action, generate error */ SPINE_LOG(("Device[%i] HT[%i] DS[%i] ERROR: Unknown Poller Action: %s", host_id, host_thread, poller_items[i].local_data_id, poller_items[i].arg1)); break; } i++; rows_processed++; } /* process last multi-get request if applicable */ if (num_oids > 0) { snmp_get_multi(host, poller_items, snmp_oids, num_oids); for (j = 0; j < num_oids; j++) { if (host->ignore_host) { SPINE_LOG(("Device[%i] HT[%i] DS[%i] WARNING: SNMP timeout detected [%i ms], ignoring host '%s'", host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, host->snmp_timeout, host->hostname)); SET_UNDEFINED(snmp_oids[j].result); } else if (IS_UNDEFINED(snmp_oids[j].result)) { if (set.spine_log_level == 2) { SPINE_LOG(("WARNING: Invalid Response, Device[%i] HT[%i] DS[%i] SNMP: v%i: %s, dsname: %s, oid: %s, value: %s", host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, host->snmp_version, host->hostname, poller_items[snmp_oids[j].array_position].rrd_name, poller_items[snmp_oids[j].array_position].arg1, snmp_oids[j].result)); } else if (set.spine_log_level == 1) { errors++; buffer_output_errors(error_string, buf_size, buf_errors, host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, false); } /* continue */ } else if ((is_numeric(snmp_oids[j].result)) || (is_multipart_output(snmp_oids[j].result))) { /* continue */ } else if (is_hexadecimal(snmp_oids[j].result, TRUE)) { snprintf(snmp_oids[j].result, RESULTS_BUFFER, "%lld", hex2dec(snmp_oids[j].result)); } else if ((STRIMATCH(snmp_oids[j].result, "U")) || (STRIMATCH(snmp_oids[j].result, "Nan"))) { if (set.spine_log_level == 2) { SPINE_LOG(("WARNING: Invalid Response, Device[%i] HT[%i] DS[%i] SNMP: v%i: %s, dsname: %s, oid: %s, value: %s", host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, host->snmp_version, host->hostname, poller_items[snmp_oids[j].array_position].rrd_name, poller_items[snmp_oids[j].array_position].arg1, snmp_oids[j].result)); } else if (set.spine_log_level == 1) { errors++; buffer_output_errors(error_string, buf_size, buf_errors, host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, false); } /* is valid output, continue */ } else { /* remove double or single quotes from string */ snprintf(temp_result, RESULTS_BUFFER, "%s", strip_alpha(trim(snmp_oids[j].result))); snprintf(snmp_oids[j].result , RESULTS_BUFFER, "%s", temp_result); /* detect erroneous non-numeric result */ if (!validate_result(snmp_oids[j].result)) { if (set.spine_log_level == 2) { SPINE_LOG(("WARNING: Invalid Response, Device[%i] HT[%i] DS[%i] SNMP: v%i: %s, dsname: %s, oid: %s, value: %s", host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, host->snmp_version, host->hostname, poller_items[snmp_oids[j].array_position].rrd_name, poller_items[snmp_oids[j].array_position].arg1, snmp_oids[j].result)); } else if (set.spine_log_level == 1) { errors++; buffer_output_errors(error_string, buf_size, buf_errors, host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, false); } SET_UNDEFINED(snmp_oids[j].result); } } snprintf(poller_items[snmp_oids[j].array_position].result, RESULTS_BUFFER, "%s", snmp_oids[j].result); thread_end = get_time_as_double(); if (is_debug_device(host_id)) { SPINE_LOG(("Device[%i] HT[%i] DS[%i] TT[%.2f] SNMP: v%i: %s, dsname: %s, oid: %s, value: %s", host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, (thread_end - thread_start) * 1000, host->snmp_version, host->hostname, poller_items[snmp_oids[j].array_position].rrd_name, poller_items[snmp_oids[j].array_position].arg1, poller_items[snmp_oids[j].array_position].result)); } else { SPINE_LOG_MEDIUM(("Device[%i] HT[%i] DS[%i] TT[%.2f] SNMP: v%i: %s, dsname: %s, oid: %s, value: %s", host_id, host_thread, poller_items[snmp_oids[j].array_position].local_data_id, (thread_end - thread_start) * 1000, host->snmp_version, host->hostname, poller_items[snmp_oids[j].array_position].rrd_name, poller_items[snmp_oids[j].array_position].arg1, poller_items[snmp_oids[j].array_position].result)); } if (!IS_UNDEFINED(poller_items[snmp_oids[j].array_position].result)) { /* insert a NaN in place of the actual value if the snmp agent restarts */ if ((spike_kill) && (!strstr(poller_items[snmp_oids[j].array_position].result,":"))) { SET_UNDEFINED(poller_items[snmp_oids[j].array_position].result); } } } } buf_length = MAX_MYSQL_BUF_SIZE+RESULTS_BUFFER; /* insert the query results into the database */ if (!(query3 = (char *)malloc(buf_length))) { die("ERROR: Fatal malloc error: poller.c query3 output buffer!"); } /* set zeros */ memset(query3, 0, buf_length); /* append data */ strncat(query3, query8, query8_len); out_buffer = strlen(query3); if (set.boost_redirect && set.boost_enabled) { /* insert the query results into the database */ if (!(query12 = (char *)malloc(buf_length))) { die("ERROR: Fatal malloc error: poller.c query12 boost output buffer!"); } /* set zeros */ memset(query12, 0, buf_length); /* append data */ strncat(query12, query11, query11_len); } int mode; if (set.poller_id > 1 && set.mode == REMOTE_ONLINE) { SPINE_LOG_DEBUG(("DEBUG: Setting up writes to remote database")); mysqlt = mysqlr; mode = REMOTE; } else { SPINE_LOG_DEBUG(("DEBUG: Setting up writes to local database")); mysqlt = mysql; mode = LOCAL; } i = 0; while (i < rows_processed) { snprintf(result_string, RESULTS_BUFFER+SMALL_BUFSIZE, " (%i, '%s', FROM_UNIXTIME(%s), '%s')", poller_items[i].local_data_id, poller_items[i].rrd_name, host_time, poller_items[i].result); result_length = strlen(result_string); /* if the next element to the buffer will overflow it, write to the database */ if ((out_buffer + result_length) >= MAX_MYSQL_BUF_SIZE) { /* append the suffix */ strncat(query3, posuffix, posuffix_len); /* insert the record */ db_insert(&mysqlt, mode, query3); /* re-initialize the query buffer */ memset(query3, 0, MAX_MYSQL_BUF_SIZE+RESULTS_BUFFER); strncat(query3, query8, query8_len); /* insert the record for boost */ if (set.boost_redirect && set.boost_enabled) { /* append the suffix */ strncat(query12, posuffix, posuffix_len); db_insert(&mysqlt, mode, query12); memset(query12, 0, MAX_MYSQL_BUF_SIZE+RESULTS_BUFFER); strncat(query12, query11, query11_len); } /* reset the output buffer length */ out_buffer = strlen(query3); /* set binary, let the system know we are a new buffer */ new_buffer = TRUE; } /* if this is our first pass, or we just outputted to the database, need to change the delimeter */ if (new_buffer) { result_string[0] = ' '; } else { result_string[0] = ','; } strncat(query3, result_string, result_length); if (set.boost_redirect && set.boost_enabled) { strncat(query12, result_string, result_length); } out_buffer = out_buffer + strlen(result_string); new_buffer = FALSE; i++; } /* perform the last insert if there is data to process */ if (out_buffer > strlen(query8)) { /* append the suffix */ strncat(query3, posuffix, posuffix_len); /* insert records into database */ db_insert(&mysqlt, mode, query3); /* insert the record for boost */ if (set.boost_redirect && set.boost_enabled) { /* append the suffix */ strncat(query12, posuffix, posuffix_len); db_insert(&mysqlt, mode, query12); } } /* cleanup memory and prepare for function exit */ if (host->snmp_session != NULL) { snmp_host_cleanup(host->snmp_session); host->snmp_session = NULL; } SPINE_FREE(query3); if (set.boost_redirect && set.boost_enabled) { SPINE_FREE(query12); } SPINE_FREE(poller_items); SPINE_FREE(snmp_oids); } else { /* free the mysql result */ mysql_free_result(result); } SPINE_FREE(host); SPINE_FREE(reindex); SPINE_FREE(ping); /* update poller_items table for next polling interval */ if (host_thread == host_threads && set.active_profiles != 1) { SPINE_LOG_MEDIUM(("Device[%i] HT[%i] Updating Poller Items for Next Poll", host_id, host_thread)); db_query(&mysql, LOCAL, query6); } /* record the polling time for the device */ poll_time = get_time_as_double() - poll_time; if (is_debug_device(host_id)) { SPINE_LOG(("Device[%i] HT[%i] Total Time: %0.2g Seconds", host_id, host_thread, poll_time)); } else { SPINE_LOG_MEDIUM(("Device[%i] HT[%i] Total Time: %0.2g Seconds", host_id, host_thread, poll_time)); } /* record the total time for the host */ thread_mutex_lock(LOCK_THDET); details[device_counter]->threads_complete++; if (details[device_counter]->threads_complete == details[device_counter]->host_threads) { details[device_counter]->complete = TRUE; poll_time = get_time_as_double(); query1[0] = '\0'; snprintf(query1, BUFSIZE, "UPDATE host SET polling_time = %.3f - %.3f WHERE id = %i", poll_time, host_time_double, host_id); db_query(&mysql, LOCAL, query1); } thread_mutex_unlock(LOCK_THDET); if (local_cnn != NULL) { db_release_connection(LOCAL, local_cnn->id); } else { SPINE_LOG(("WARNING: Device[%i] HT[%i] Trying to close uninitialized local connection.", host_id, host_thread)); } if (set.poller_id > 1 && set.mode == REMOTE_ONLINE) { if (remote_cnn != NULL) { db_release_connection(REMOTE, remote_cnn->id); } else { SPINE_LOG(("WARNING: Device[%i] HT[%i] Trying to close uninitialized remote connection.", host_id, host_thread)); } } mysql_thread_end(); if (is_debug_device(host_id)) { SPINE_LOG(("DEBUG: Device[%i] HT[%i] DEBUG: HOST COMPLETE: About to Exit Device Polling Thread Function", host_id, host_thread)); } else { SPINE_LOG_DEBUG(("DEBUG: Device[%i] HT[%i] DEBUG: HOST COMPLETE: About to Exit Device Polling Thread Function", host_id, host_thread)); } buffer_output_errors(error_string, buf_size, buf_errors, host_id, host_thread, 0, true); SPINE_FREE(error_string); SPINE_FREE(buf_size); SPINE_FREE(buf_errors); *host_errors = errors; } /*! \fn void buffer_output_errors(local_data_id) { * \brief buffers output errors and pushes those errors to standard * output as required. * \param char* buffer - pointer to the output buffer * \param int device_id - the device id * \param int thread id - the device thread * \param int local_data_id - the local data id * \param boolean flush - flush any part of buffer */ void buffer_output_errors(char *error_string, int *buf_size, int *buf_errors, int device_id, int thread_id, int local_data_id, bool flush) { int error_len; char tbuffer[SMALL_BUFSIZE]; if (flush && *buf_errors > 0) { SPINE_LOG(("WARNING: Invalid Response(s), Errors[%i] Device[%i] Thread[%i] DS[%s]", *buf_errors, device_id, thread_id, error_string)); } else if (!flush) { snprintf(tbuffer, SMALL_BUFSIZE, *buf_errors > 0 ? ", %i" : "%i", local_data_id); error_len = strlen(tbuffer); if (*buf_size + error_len >= DBL_BUFSIZE) { SPINE_LOG(("WARNING: Invalid Response(s), Errors[%i] Device[%i] Thread[%i] DS[%s]", *buf_errors, device_id, thread_id, error_string)); *buf_errors = 1; *buf_size = snprintf(error_string, DBL_BUFSIZE, "%i", local_data_id); } else { (*buf_errors)++; snprintf(error_string + *buf_size, DBL_BUFSIZE, "%s", tbuffer); *buf_size += error_len; } } } /*! \fn int is_multipart_output(char *result) * \brief validates the output syntax is a valid name value pair syntax * \param result the value to be checked for legality * * This function will poll a specific host using the script pointed to by * the command variable. * * \return TRUE if the result is valid, otherwise FALSE. * */ int is_multipart_output(char *result) { int space_cnt = 0; int delim_cnt = 0; int i; /* check the easy cases first */ if (result) { /* it must have delimiters */ if ((strstr(result, ":")) || (strstr(result, "!"))) { if (!strstr(result, " ")) { return TRUE; } else { const int len = strlen(result); for (i=0; iid)); if (set.mibs || system) { if (is_debug_device(host->id)) { SPINE_LOG(("Device[%d] Updating Full System Information Table", host->id)); } else { SPINE_LOG_MEDIUM(("Device[%d] Updating Full System Information Table", host->id)); } SPINE_LOG_DEVDBG(("DEVDBG: Device[%d] poll_result = snmp_get(host, '.1.3.6.1.2.1.1.1.0');", host->id)); poll_result = snmp_get(host, ".1.3.6.1.2.1.1.1.0"); SPINE_LOG_DEVDBG(("DEVDBG: Device[%d] poll_result = snmp_get(host, '.1.3.6.1.2.1.1.1.0'); [complete]", host->id)); if (poll_result) { db_escape(mysql, host->snmp_sysDescr, sizeof(host->snmp_sysDescr), poll_result); SPINE_FREE(poll_result); } SPINE_LOG_DEVDBG(("DEVDBG: Device[%d] poll_result = snmp_get(host, '.1.3.6.1.2.1.1.2.0');", host->id)); poll_result = snmp_get(host, ".1.3.6.1.2.1.1.2.0"); SPINE_LOG_DEVDBG(("DEVDBG: Device[%d] poll_result = snmp_get(host, '.1.3.6.1.2.1.1.2.0'); [complete]", host->id)); if (poll_result) { db_escape(mysql, host->snmp_sysObjectID, sizeof(host->snmp_sysObjectID), poll_result); SPINE_FREE(poll_result); } // Get the legacy system uptime instance first SPINE_LOG_DEVDBG(("DEVDBG: Device[%d] poll_result = snmp_get(host, '.1.3.6.1.2.1.1.3.0');", host->id)); poll_result = snmp_get(host, ".1.3.6.1.2.1.1.3.0"); SPINE_LOG_DEVDBG(("DEVDGB: Device[%d] poll_result = snmp_get(host, '.1.3.6.1.2.1.1.3.0'); [complete]", host->id)); if (poll_result && is_numeric(poll_result)) { host->snmp_sysUpTimeInstance = atoll(poll_result); SPINE_FREE(poll_result); // Attempt to get the more modern version SPINE_LOG_DEVDBG(("DEVDBG: Device[%d] poll_result = snmp_get(host, '.1.3.6.1.6.3.10.2.1.3.0');", host->id)); poll_result = snmp_get_base(host, ".1.3.6.1.6.3.10.2.1.3.0", false); SPINE_LOG_DEVDBG(("DEVDGB: Device[%d] poll_result = snmp_get(host, '.1.3.6.1.6.3.10.2.1.3.0'); [complete]", host->id)); if (poll_result && is_numeric(poll_result)) { host->snmp_sysUpTimeInstance = atoll(poll_result) * 100; snprintf(poll_result, BUFSIZE, "%llu", host->snmp_sysUpTimeInstance); } SPINE_FREE(poll_result); } SPINE_LOG_DEVDBG(("DEVDBG: Device [%d] poll_result = snmp_get(host, '.1.3.6.1.2.1.1.4.0');", host->id)); poll_result = snmp_get(host, ".1.3.6.1.2.1.1.4.0"); SPINE_LOG_DEVDBG(("DEVDBG: Device [%d] poll_result = snmp_get(host, '.1.3.6.1.2.1.1.4.0'); [complete]", host->id)); if (poll_result) { db_escape(mysql, host->snmp_sysContact, sizeof(host->snmp_sysContact), poll_result); SPINE_FREE(poll_result); } SPINE_LOG_DEVDBG(("DEVDBG: Device [%d] poll_result = snmp_get(host, '.1.3.6.1.2.1.1.5.0');", host->id)); poll_result = snmp_get(host, ".1.3.6.1.2.1.1.5.0"); SPINE_LOG_DEVDBG(("DEVDBG: Device [%d] poll_result = snmp_get(host, '.1.3.6.1.2.1.1.5.0'); [complete]", host->id)); if (poll_result) { db_escape(mysql, host->snmp_sysName, sizeof(host->snmp_sysName), poll_result); SPINE_FREE(poll_result); } SPINE_LOG_DEVDBG(("DEVDBG: Device [%d] poll_result = snmp_get(host, '.1.3.6.1.2.1.1.6.0');", host->id)); poll_result = snmp_get(host, ".1.3.6.1.2.1.1.6.0"); SPINE_LOG_DEVDBG(("DEVDBG: Device [%d] poll_result = snmp_get(host, '.1.3.6.1.2.1.1.6.0'); [complete]", host->id)); if (poll_result) { db_escape(mysql, host->snmp_sysLocation, sizeof(host->snmp_sysLocation), poll_result); SPINE_FREE(poll_result); } } else { if (is_debug_device(host->id)) { SPINE_LOG(("Device[%d] Updating Short System Information Table", host->id)); } else { SPINE_LOG_MEDIUM(("Device[%d] Updating Short System Information Table", host->id)); } // Get the legacy system uptime instance first SPINE_LOG_DEVDBG(("DEVDBG: Device[%d] poll_result = snmp_get(host, '.1.3.6.1.2.1.1.3.0');", host->id)); poll_result = snmp_get(host, ".1.3.6.1.2.1.1.3.0"); SPINE_LOG_DEVDBG(("DEVDGB: Device[%d] poll_result = snmp_get(host, '.1.3.6.1.2.1.1.3.0'); [complete]", host->id)); if (poll_result && is_numeric(poll_result)) { host->snmp_sysUpTimeInstance = atoll(poll_result); SPINE_FREE(poll_result); // Attempt to get the more modern version SPINE_LOG_DEVDBG(("DEVDBG: Device[%d] poll_result = snmp_get(host, '.1.3.6.1.6.3.10.2.1.3.0');", host->id)); poll_result = snmp_get(host, ".1.3.6.1.6.3.10.2.1.3.0"); SPINE_LOG_DEVDBG(("DEVDGB: Device[%d] poll_result = snmp_get(host, '.1.3.6.1.6.3.10.2.1.3.0'); [complete]", host->id)); if (poll_result && is_numeric(poll_result)) { host->snmp_sysUpTimeInstance = atoll(poll_result) * 100; snprintf(poll_result, BUFSIZE, "%llu", host->snmp_sysUpTimeInstance); } SPINE_FREE(poll_result); } } } /*! \fn int validate_result(char *result) * \brief validates the output from the polling action is valid * \param result the value to be checked for legality * * This function will poll a specific host using the script pointed to by * the command variable. * * \return TRUE if the result is valid, otherwise FALSE. * */ int validate_result(char *result) { /* check the easy cases first */ if (result) { if (is_numeric(result)) { return TRUE; } else { if (is_multipart_output(trim(result))) { return TRUE; } else { return FALSE; } } } return FALSE; } /*! \fn char *exec_poll(host_t *current_host, char *command, int id, char *type) * \brief polls a host using a script * \param current_host a pointer to the current host structure * \param command the command to be executed * \param id either the local_data_id or the data_query_id * * This function will poll a specific host using the script pointed to by * the command variable. * * \return a pointer to a character buffer containing the result. * */ char *exec_poll(host_t *current_host, char *command, int id, char *type) { int cmd_fd; int pid; int close_fd = TRUE; #ifdef USING_TPOPEN FILE *fd; #endif int bytes_read; fd_set fds; double begin_time = 0; double end_time = 0; double script_timeout; double remaining_usec = 0; struct timeval timeout; char *proc_command; char *result_string; /* compensate for back slashes in arguments */ #if defined(__CYGWIN__) proc_command = add_slashes(command); #else proc_command = command; #endif if (!(result_string = (char *) malloc(RESULTS_BUFFER))) { die("ERROR: Fatal malloc error: poller.c exec_poll!"); } /* set zeros */ memset(result_string, 0, RESULTS_BUFFER); /* set script timeout as double */ script_timeout = set.script_timeout; /* establish timeout of 25 seconds for pipe response */ timeout.tv_sec = set.script_timeout; timeout.tv_usec = 0; /* don't run too many scripts, operating systems do not like that. */ int retries = 0; int sem_err = 0; int needs_cleanup = 0; /* used for checking executable status */ char executable[BUFSIZE]; char *saveptr = NULL; char *token; pthread_cleanup_push(child_cleanup_script, NULL); // use the script server timeout value, allow for 50% leeway while (++retries < (set.script_timeout * 15)) { sem_err = sem_trywait(&available_scripts); if (sem_err == 0) { break; } else if (sem_err == EAGAIN || sem_err == EWOULDBLOCK) { if (is_debug_device(current_host->id)) { SPINE_LOG(("DEBUG: Device[%i]: Pausing as unable to obtain a script execution lock", current_host->id)); } else { SPINE_LOG_DEVDBG(("DEBUG: Device[%i]: Pausing as unable to obtain a script execution lock", current_host->id)); } } else { if (is_debug_device(current_host->id)) { SPINE_LOG(("DEBUG: Device[%i]: Pausing as error %d whilst obtaining a script execution lock", current_host->id, sem_err)); } else { SPINE_LOG_DEVDBG(("DEBUG: Device[%i]: Pausing as error %d whilst obtaining a script execution lock", current_host->id, sem_err)); } } usleep(10000); } if (sem_err) { SPINE_LOG(("ERROR: Device[%i]: Failed to obtain a script execution lock within 30 seconds", current_host->id)); } else { /* Mark for cleanup */ needs_cleanup = 1; /* record start time */ begin_time = get_time_as_double(); /* peel the executable from the command */ saveptr = proc_command; sprintf(executable, "%s", proc_command); token = strtok_r(executable, " ", &saveptr); /* cheesy little hack to add /usr/bin/ if its not included */ if (strstr(executable, "/") == NULL) { saveptr = proc_command; sprintf(executable, "/usr/bin/%s", proc_command); token = strtok_r(executable, " ", &saveptr); } SPINE_LOG_DEBUG(("The executable is '%s' in \'%s\'", executable, proc_command)); if (access(executable, X_OK | F_OK) != -1) { #ifdef USING_TPOPEN fd = popen((char *)proc_command, "r"); cmd_fd = fileno(fd); if (is_debug_device(current_host->id)) { SPINE_LOG(("DEBUG: Device[%i] DEBUG: The POPEN returned the following File Descriptor %i", current_host->id, cmd_fd)); } else { SPINE_LOG_DEBUG(("DEBUG: Device[%i] DEBUG: The POPEN returned the following File Descriptor %i", current_host->id, cmd_fd)); } #else cmd_fd = nft_popen((char *)proc_command, "r"); if (is_debug_device(current_host->id)) { SPINE_LOG(("DEBUG: Device[%i] DEBUG: The NIFTY POPEN returned the following File Descriptor %i", current_host->id, cmd_fd)); } else { SPINE_LOG_DEBUG(("DEBUG: Device[%i] DEBUG: The NIFTY POPEN returned the following File Descriptor %i", current_host->id, cmd_fd)); } #endif if (cmd_fd > 0) { retry: /* Initialize File Descriptors to Review for Input/Output */ FD_ZERO(&fds); FD_SET(cmd_fd, &fds); /* wait x seconds for pipe response */ switch (select(FD_SETSIZE, &fds, NULL, NULL, &timeout)) { case -1: switch (errno) { case EBADF: SPINE_LOG(("Device[%i] ERROR: One or more of the file descriptor sets specified a file descriptor that is not a valid open file descriptor.", current_host->id)); SET_UNDEFINED(result_string); close_fd = FALSE; break; case EINTR: #ifndef SOLAR_THREAD /* take a moment */ usleep(2000); #endif /* record end time */ end_time = get_time_as_double(); /* re-establish new timeout value */ timeout.tv_sec = rint(floor(script_timeout-(end_time-begin_time))); remaining_usec = set.script_timeout - timeout.tv_sec - (end_time - begin_time); if (remaining_usec > 0) { timeout.tv_usec = rint(remaining_usec * 1000000); } else { timeout.tv_usec = 0; } timeout.tv_sec = rint(floor(script_timeout-(end_time-begin_time))); timeout.tv_usec = rint((script_timeout-(end_time-begin_time)-timeout.tv_sec)*1000000); if (timeout.tv_sec + timeout.tv_usec > 0) { goto retry; } else { SPINE_LOG(("WARNING: A script timed out while processing EINTR's.")); SET_UNDEFINED(result_string); close_fd = FALSE; } break; case EINVAL: SPINE_LOG(("Device[%i] ERROR: Possible invalid timeout specified in select() statement.", current_host->id)); SET_UNDEFINED(result_string); close_fd = FALSE; break; default: SPINE_LOG(("Device[%i] ERROR: The script/command select() failed", current_host->id)); SET_UNDEFINED(result_string); close_fd = FALSE; break; } break; case 0: #ifdef USING_TPOPEN SPINE_LOG_MEDIUM(("Device[%i] ERROR: The POPEN timed out", current_host->id)); close_fd = FALSE; #else SPINE_LOG_MEDIUM(("Device[%i] ERROR: The NIFTY POPEN timed out", current_host->id)); pid = nft_pchild(cmd_fd); kill(pid, SIGKILL); #endif SET_UNDEFINED(result_string); break; default: /* get only one line of output, we will ignore the rest */ bytes_read = read(cmd_fd, result_string, RESULTS_BUFFER-1); if (bytes_read > 0) { result_string[bytes_read] = '\0'; } else { if (STRIMATCH(type,"DS")) { SPINE_LOG(("Device[%i] DS[%i] ERROR: Empty result [%s]: '%s'", current_host->id, id, current_host->hostname, command)); } else { SPINE_LOG(("Device[%i] DQ[%i] ERROR: Empty result [%s]: '%s'", current_host->id, id, current_host->hostname, command)); } SET_UNDEFINED(result_string); } } /* close pipe */ #ifdef USING_TPOPEN /* we leave the old fd open if it timed out. It will have to exit on it's own */ if (close_fd) { pclose(fd); } #else nft_pclose(cmd_fd); #endif } else { SPINE_LOG(("Device[%i] ERROR: Problem executing POPEN [%s]: '%s'", current_host->id, current_host->hostname, command)); SET_UNDEFINED(result_string); } } else { SPINE_LOG(("Device[%i] ERROR: Problem executing POPEN. File '%s' does not exist or is not executable.", current_host->id, command)); SET_UNDEFINED(result_string); } #if defined(__CYGWIN__) SPINE_FREE(proc_command); #endif } /* reduce the active script count */ pthread_cleanup_pop(needs_cleanup); return result_string; } cacti-spine-1.2.30/locks.h0000664000175000017500000000502014770060157014310 0ustar markvmarkv/* ex: set tabstop=4 shiftwidth=4 autoindent: +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 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, implimentation details) | +-------------------------------------------------------------------------+ | - Cacti - http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ extern void init_mutexes(void); extern void thread_mutex_lock(int mutex); extern void thread_mutex_unlock(int mutex); extern int thread_mutex_trylock(int mutex); extern pthread_cond_t* get_cond(int lock); extern pthread_mutex_t* get_lock(int lock); extern pthread_once_t* get_attr(int locko); cacti-spine-1.2.30/snmp.h0000664000175000017500000000621214770060157014156 0ustar markvmarkv/* ex: set tabstop=4 shiftwidth=4 autoindent: +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 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, implimentation details) | +-------------------------------------------------------------------------+ | - Cacti - http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ extern void snmp_spine_init(void); extern void snmp_spine_close(void); extern void *snmp_host_init(int host_id, char *hostname, int snmp_version, char *snmp_community, char *snmp_username, char *snmp_password, char *snmp_auth_protocol, char *snmp_priv_passphrase, char *snmp_priv_protocol, char *snmp_context, char *snmp_engine_id, int snmp_port, int snmp_timeout); extern void snmp_host_cleanup(void *snmp_session); extern char *snmp_get_base(host_t *current_host, char *snmp_oid, bool should_fail); extern char *snmp_get(host_t *current_host, char *snmp_oid); extern char *snmp_getnext(host_t *current_host, char *snmp_oid); extern int snmp_count(host_t *current_host, char *snmp_oid); extern void snmp_get_multi(host_t *current_host, target_t *poller_items, snmp_oids_t *snmp_oids, int num_oids); extern void snmp_snprint_value(char *obuf, size_t buf_len, const oid *objid, size_t objidlen, struct variable_list *variable); cacti-spine-1.2.30/copyright_year.sh0000664000175000017500000000765214770060157016425 0ustar markvmarkv#!/usr/bin/env bash # +-------------------------------------------------------------------------+ # | Copyright (C) 2004-2024 The Cacti Group | # | | # | 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. | # +-------------------------------------------------------------------------+ # | Cacti: The Complete RRDTool-based Graphing Solution | # +-------------------------------------------------------------------------+ # | This code is designed, written, and maintained by the Cacti Group. See | # | about.php and/or the AUTHORS file for specific developer information. | # +-------------------------------------------------------------------------+ # | http://www.cacti.net/ | # +-------------------------------------------------------------------------+ update_copyright() { local file=$1 file=${file/$SCRIPT_BASE/} printf -v line "%60s" "$file" if [[ -z "$ERRORS_ONLY" ]]; then echo -n "$line" line= fi old_reg="20[0-9][0-9][ ]*-[ ]*20[0-9][0-9]" old_data=$(grep -c -e "$old_reg" "$1" 2>/dev/null) new_reg="2004-$YEAR" result=$? if [[ $old_data -eq 0 ]]; then old_reg="(Copyright.*) 20[0-9][0-9] " old_data=$(grep -c -e "$old_reg" "$1" 2>/dev/null) new_reg="\1 2004-$YEAR" result=$? fi if [[ $old_data -gt 0 ]]; then old_data=$(grep -e "$old_reg" "$1" 2>/dev/null) new_data=$(echo "$old_data" | sed -r s/"$old_reg"/"$new_reg"/g) if [[ "$old_data" == "$new_data" ]]; then if [[ -z "$ERRORS_ONLY" ]]; then echo "$line Skipping Copyright Data" fi else echo "$line Updating Copyright Data" printf "%60s %s\n" "==============================" "====================" printf "%60s %s\n" "$old_data" "=>" printf "%60s %s\n" "$new_data" "" sed -i -r s/"$old_reg"/"$new_reg"/g $1 printf "%60s %s\n" "==============================" "====================" fi else echo "$line Copyright not found!" SCRIPT_ERR=1 fi } SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) SCRIPT_BASE=$(realpath "${SCRIPT_DIR}/")/ BAD_FOLDERS="\.git include/vendor \*\*/vendor include/fa cache include/js scripts" SCRIPT_EXCLUSION= for f in $BAD_FOLDERS; do SCRIPT_EXCLUSION="$SCRIPT_EXCLUSION -not -path ${SCRIPT_BASE}$f/\* " done SCRIPT_ERR=0 YEAR=$(date +"%Y") EXT="" # "sh sql php js md conf c h ac dist" ERRORS_ONLY=1 while [ -n "$1" ]; do case $1 in "--help") echo "NOTE: Checks all Cacti pages for this years copyright" echo "" echo "usage: copyright_year.sh [-a]" echo "" ;; "-E" | "-e") shift EXT="$1" ;; "-A" | "-a") ERRORS_ONLY= echo "Searching..." ;; *) ;; esac shift done # ---------------------------------------------- # PHP / JS / MD Files # ---------------------------------------------- SCRIPT_INCLUSION= SCRIPT_SEPARATOR= for ext in $EXT; do if [ -n "$SCRIPT_INCLUSION" ]; then SCRIPT_SEPARATOR="-o " fi SCRIPT_INCLUSION="$SCRIPT_INCLUSION $SCRIPT_SEPARATOR-name \*.$ext" done if [[ -n "$SCRIPT_INCLUSION" ]]; then SCRIPT_INCLUSION="\( $SCRIPT_INCLUSION \)" fi SCRIPT_CMD="find ${SCRIPT_BASE} -type f $SCRIPT_INCLUSION $SCRIPT_EXCLUSION -print0" bash -c "$SCRIPT_CMD" | while IFS= read -r -d '' file; do update_copyright "${file}" done cacti-spine-1.2.30/snmp.c0000664000175000017500000010220514770060157014150 0ustar markvmarkv/* ex: set tabstop=4 shiftwidth=4 autoindent: +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 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, implimentation details) | +-------------------------------------------------------------------------+ | - Cacti - http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ #include "common.h" #include "spine.h" /* resolve problems in debian */ #ifndef NETSNMP_DS_LIB_DONT_PERSIST_STATE #define NETSNMP_DS_LIB_DONT_PERSIST_STATE 32 #endif #define OIDSIZE(p) (sizeof(p)/sizeof(oid)) /*! \fn void snmp_spine_init() * \brief wrapper function for init_snmp * * Initializes snmp for the given application ID * */ void snmp_spine_init(void) { /* Only do numeric output */ #ifdef NETSNMP_DS_LIB_PRINT_NUMERIC_ENUM netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_NUMERIC_ENUM, 1); #endif /* Prevent update of the snmpapp.conf file */ #ifdef NETSNMP_DS_LIB_DONT_PERSIST_STATE netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_PERSIST_STATE, 1); #endif /* Prevent update of the snmpapp.conf file */ #ifdef NETSNMP_DS_LIB_DISABLE_PERSISTENT_LOAD netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DISABLE_PERSISTENT_LOAD, 1); #endif #ifdef NETSNMP_DS_LIB_DONT_PRINT_UNITS netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_PRINT_UNITS, 1); #endif setenv("MIBS", "", 1); netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_QUICK_PRINT, 1); netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_QUICKE_PRINT, 1); netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_BARE_VALUE, 1); netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_NUMERIC_TIMETICKS, 1); /* don't check the range of the OID */ netsnmp_ds_toggle_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_CHECK_RANGE); #if defined(VERIFY_PACKAGE_VERSION) && defined(PACKAGE_VERSION) /* check that the headers we compiled with match the library we linked with */ SPINE_LOG_DEBUG(("DEBUG: SNMP Header Version is %s", PACKAGE_VERSION)); SPINE_LOG_DEBUG(("DEBUG: SNMP Library Version is %s", netsnmp_get_version())); if (STRMATCH(PACKAGE_VERSION,netsnmp_get_version())) { init_snmp("spine"); } else { /* report the error and quit spine */ die("ERROR: SNMP Library Version Mismatch (%s vs %s)",PACKAGE_VERSION,netsnmp_get_version()); } #else SPINE_LOG_DEBUG(("DEBUG: Issues with SNMP Header Version information, assuming old version of Net-SNMP.")); init_snmp("spine"); #endif } /*! \fn void snmp_spine_close() * \brief wrapper function for the snmp_shutdown function * * Closes the snmp api for the given application ID * */ void snmp_spine_close(void) { snmp_shutdown("spine"); } /*! \fn void *snmp_host_init(int host_id, char *hostname, int snmp_version, * char *snmp_community, char *snmp_username, char *snmp_password, * char *snmp_auth_protocol, char *snmp_priv_passphrase, char *snmp_priv_protocol, * char *snmp_context, char *snmp_engine_id, int snmp_port, int snmp_timeout) * \brief initializes an snmp_session object for a Spine host * * This function will initialize NET-SNMP for the Spine host * in question. * */ void *snmp_host_init(int host_id, char *hostname, int snmp_version, char *snmp_community, char *snmp_username, char *snmp_password, char *snmp_auth_protocol, char *snmp_priv_passphrase, char *snmp_priv_protocol, char *snmp_context, char *snmp_engine_id, int snmp_port, int snmp_timeout) { void *sessp = NULL; struct snmp_session session; char hostnameport[BUFSIZE]; /* initialize SNMP */ snmp_sess_init(&session); /* Bind to snmp_clientaddr if specified */ size_t len = strlen(set.snmp_clientaddr); if (len > 0 && len <= SMALL_BUFSIZE) { #if SNMP_LOCALNAME == 1 session.localname = strdup(set.snmp_clientaddr); #endif } /* Prevent update of the snmpapp.conf file */ #ifdef NETSNMP_DS_LIB_DONT_PERSIST_STATE netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_PERSIST_STATE, 1); #endif /* Prevent update of the snmpapp.conf file */ #ifdef NETSNMP_DS_LIB_DISABLE_PERSISTENT_LOAD netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DISABLE_PERSISTENT_LOAD, 1); #endif #ifdef NETSNMP_DS_LIB_DONT_PRINT_UNITS netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_PRINT_UNITS, 1); #endif netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_QUICK_PRINT, 1); netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_QUICKE_PRINT, 1); netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_PRINT_BARE_VALUE, 1); netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_NUMERIC_TIMETICKS, 1); session.securityEngineID = 0; session.securityEngineIDLen = 0; session.securityName = 0; session.securityNameLen = 0; session.contextEngineID = 0; session.contextEngineIDLen = 0; session.contextName = 0; session.contextNameLen = 0; session.contextEngineID = 0; session.contextEngineIDLen = 0; /* verify snmp version is accurate */ if (snmp_version == 2) { session.version = SNMP_VERSION_2c; session.securityModel = SNMP_SEC_MODEL_SNMPv2c; } else if (snmp_version == 1) { session.version = SNMP_VERSION_1; session.securityModel = SNMP_SEC_MODEL_SNMPv1; } else if (snmp_version == 3) { session.version = SNMP_VERSION_3; session.securityModel = USM_SEC_MODEL_NUMBER; } else { SPINE_LOG(("Device[%i] ERROR: SNMP Version Error for Device '%s'", host_id, hostname)); return 0; } snprintf(hostnameport, BUFSIZE, "%s:%i", hostname, snmp_port); session.peername = hostnameport; session.retries = set.snmp_retries; session.timeout = (snmp_timeout * 1000); /* net-snmp likes microseconds */ SPINE_LOG_HIGH(("Device[%i] INFO: SNMP Device '%s' has a timeout of %ld (%d), with %d retries", host_id, hostnameport, session.timeout, snmp_timeout, session.retries)); if ((snmp_version == 2) || (snmp_version == 1)) { session.community = (unsigned char*) snmp_community; session.community_len = strlen(snmp_community); } else { session.securityName = snmp_username; session.securityNameLen = strlen(session.securityName); if (snmp_context && strlen(snmp_context)) { session.contextName = snmp_context; session.contextNameLen = strlen(session.contextName); } if (snmp_engine_id && strlen(snmp_engine_id)) { session.contextEngineID = (unsigned char*) snmp_engine_id; session.contextEngineIDLen = strlen(snmp_engine_id); } session.securityAuthKeyLen = USM_AUTH_KU_LEN; /* set the authentication protocol */ if (strcmp(snmp_auth_protocol, "MD5") == 0) { #ifndef NETSNMP_DISABLE_MD5 /* set the authentication method to MD5 */ session.securityAuthProto = snmp_duplicate_objid(usmHMACMD5AuthProtocol, USM_AUTH_PROTO_MD5_LEN); session.securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN; #else SPINE_LOG(("SNMP: Error MD5 is no longer supported on this system.")); return 0; #endif } else if (strcmp(snmp_auth_protocol, "SHA") == 0) { session.securityAuthProto = snmp_duplicate_objid(usmHMACSHA1AuthProtocol, USM_AUTH_PROTO_SHA_LEN); session.securityAuthProtoLen = USM_AUTH_PROTO_SHA_LEN; } else if (strcmp(snmp_auth_protocol, "SHA224") == 0) { #if defined(HAVE_EVP_SHA224) && defined(NETSNMP_USMAUTH_HMAC128SHA224) session.securityAuthProto = snmp_duplicate_objid(usmHMAC128SHA224AuthProtocol, OID_LENGTH(usmHMAC128SHA224AuthProtocol)); session.securityAuthProtoLen = OID_LENGTH(usmHMAC128SHA224AuthProtocol); #else SPINE_LOG(("SNMP: Error SHA224 is not supported on this system. Upgrade the Net-SNMP API to 5.8+")); return 0; #endif } else if (strcmp(snmp_auth_protocol, "SHA256") == 0) { #if defined(HAVE_EVP_SHA224) && defined(NETSNMP_USMAUTH_HMAC192SHA256) session.securityAuthProto = snmp_duplicate_objid(usmHMAC192SHA256AuthProtocol, OID_LENGTH(usmHMAC192SHA256AuthProtocol)); session.securityAuthProtoLen = OID_LENGTH(usmHMAC192SHA256AuthProtocol); #else SPINE_LOG(("SNMP: Error SHA256 is not supported on this system. Upgrade the Net-SNMP API to 5.8+")); return 0; #endif } else if (strcmp(snmp_auth_protocol, "SHA384") == 0) { #if defined(HAVE_EVP_SHA384) && defined(NETSNMP_USMAUTH_HMAC256SHA384) session.securityAuthProto = snmp_duplicate_objid(usmHMAC256SHA384AuthProtocol, OID_LENGTH(usmHMAC256SHA384AuthProtocol)); session.securityAuthProtoLen = USM_HMAC256SHA384_AUTH_LEN; #else SPINE_LOG(("SNMP: Error SHA384 is not supported on this system. Upgrade the Net-SNMP API to 5.8+")); return 0; #endif } else if (strcmp(snmp_auth_protocol, "SHA512") == 0) { #if defined(HAVE_EVP_SHA384) && defined(NETSNMP_USMAUTH_HMAC384SHA512) session.securityAuthProto = snmp_duplicate_objid(usmHMAC384SHA512AuthProtocol, OID_LENGTH(usmHMAC384SHA512AuthProtocol)); session.securityAuthProtoLen = USM_HMAC384SHA512_AUTH_LEN; #else SPINE_LOG(("SNMP: Error SHA512 is not supported on this system. Upgrade the Net-SNMP API to 5.8+")); return 0; #endif } if (strlen(snmp_password)) { /* set the authentication key to the hashed version. The password must me at least 8 char */ if (generate_Ku(session.securityAuthProto, session.securityAuthProtoLen, (u_char *) snmp_password, strlen(snmp_password), session.securityAuthKey, &session.securityAuthKeyLen) != SNMPERR_SUCCESS) { SPINE_LOG(("SNMP: Error generating SNMPv3 Ku from authentication pass phrase.")); } } /* set the privacy protocol to none */ if (strcmp(snmp_priv_protocol, "[None]") == 0 || (strlen(snmp_priv_passphrase) == 0)) { session.securityPrivProto = snmp_duplicate_objid(usmNoPrivProtocol, OID_LENGTH(usmNoPrivProtocol)); session.securityPrivProtoLen = OID_LENGTH(usmNoPrivProtocol); session.securityPrivKeyLen = USM_PRIV_KU_LEN; /* set the security level to authenticate, but not encrypted */ if (strlen(snmp_password)) { session.securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV; } else { session.securityLevel = SNMP_SEC_LEVEL_NOAUTH; } } else { if (strcmp(snmp_priv_protocol, "DES") == 0) { #if defined(USM_PRIV_PROTO_DES_LEN) && !defined(NETSNMP_DISABLE_DES) session.securityPrivProto = snmp_duplicate_objid(usmDESPrivProtocol, USM_PRIV_PROTO_DES_LEN); session.securityPrivProtoLen = USM_PRIV_PROTO_DES_LEN; session.securityPrivKeyLen = USM_PRIV_KU_LEN; session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; #else SPINE_LOG(("SNMP: Error DES is no longer supported on this system")); return 0; #endif } else if (strcmp(snmp_priv_protocol, "AES") == 0) { #if defined(USM_PRIV_PROTO_AES_LEN) session.securityPrivProto = snmp_duplicate_objid(usmAESPrivProtocol, USM_PRIV_PROTO_AES_LEN); session.securityPrivProtoLen = USM_PRIV_PROTO_AES_LEN; session.securityPrivKeyLen = USM_PRIV_KU_LEN; session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; #else SPINE_LOG(("SNMP: Error AES is not supported in the Net-SNMP API, upgrade the Net-SNMP libraries.")); return 0; #endif } else if (strcmp(snmp_priv_protocol, "AES128") == 0) { #if defined(USM_PRIV_PROTO_AES_LEN) session.securityPrivProto = snmp_duplicate_objid(usmAESPrivProtocol, USM_PRIV_PROTO_AES_LEN); session.securityPrivProtoLen = USM_PRIV_PROTO_AES_LEN; session.securityPrivKeyLen = USM_PRIV_KU_LEN; session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; #else session.securityPrivProto = snmp_duplicate_objid(usmAES128PrivProtocol, OID_LENGTH(usmAES128PrivProtocol)); session.securityPrivProtoLen = OID_LENGTH(usmAES128PrivProtocol); session.securityPrivKeyLen = USM_PRIV_KU_LEN; session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; #endif } else if(strcmp(snmp_priv_protocol, "AES192") == 0) { #if defined(NETSNMP_DRAFT_BLUMENTHAL_AES_04) && defined(USM_CREATE_USER_PRIV_AES192) session.securityPrivProto = snmp_duplicate_objid(usmAES192PrivProtocol, OID_LENGTH(usmAES192PrivProtocol)); session.securityPrivProtoLen = OID_LENGTH(usmAES192PrivProtocol); session.securityPrivKeyLen = SNMP_TRANS_PRIVLEN_AES192; session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; #else SPINE_LOG(("SNMP: Error AES-192 is not supported in the Net-SNMP API, upgrade the Net-SNMP libraries.")); return 0; #endif } else if(strcmp(snmp_priv_protocol, "AES256") == 0) { #if defined(NETSNMP_DRAFT_BLUMENTHAL_AES_04) && defined(USM_CREATE_USER_PRIV_AES256) session.securityPrivProto = snmp_duplicate_objid(usmAES256PrivProtocol, OID_LENGTH(usmAES256PrivProtocol)); session.securityPrivProtoLen = OID_LENGTH(usmAES256PrivProtocol); session.securityPrivKeyLen = SNMP_TRANS_PRIVLEN_AES256; session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; #else SPINE_LOG(("SNMP: Error AES-256 is not supported in the Net-SNMP API, upgrade the Net-SNMP libraries.")); return 0; #endif } else if(strcmp(snmp_priv_protocol, "AES192C") == 0) { #if defined(NETSNMP_DRAFT_BLUMENTHAL_AES_04) && defined(USM_CREATE_USER_PRIV_AES192_CISCO) session.securityPrivProto = snmp_duplicate_objid(usmAES192CiscoPrivProtocol, OID_LENGTH(usmAES192CiscoPrivProtocol)); session.securityPrivProtoLen = OID_LENGTH(usmAES192CiscoPrivProtocol); session.securityPrivKeyLen = SNMP_TRANS_PRIVLEN_AES192; session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; #else SPINE_LOG(("SNMP: Error AES192C is not supported in the Net-SNMP API, upgrade the Net-SNMP libraries.")); return 0; #endif } else if(strcmp(snmp_priv_protocol, "AES256C") == 0) { #if defined(NETSNMP_DRAFT_BLUMENTHAL_AES_04) && defined(USM_CREATE_USER_PRIV_AES256_CISCO) session.securityPrivProto = snmp_duplicate_objid(usmAES256CiscoPrivProtocol, OID_LENGTH(usmAES256CiscoPrivProtocol)); session.securityPrivProtoLen = OID_LENGTH(usmAES256CiscoPrivProtocol); session.securityPrivKeyLen = SNMP_TRANS_PRIVLEN_AES256; session.securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; #else SPINE_LOG(("SNMP: Error AES256C is not supported in the Net-SNMP API, upgrade the Net-SNMP libraries.")); return 0; #endif } /* set the privacy key to the hashed version. */ SPINE_LOG_MEDIUM(("SNMP: Using privacy protocol(len): %s(%d)", snmp_priv_protocol, session.securityPrivKeyLen)); if (generate_Ku(session.securityAuthProto, session.securityAuthProtoLen, (u_char *) snmp_priv_passphrase, strlen(snmp_priv_passphrase), session.securityPrivKey, &session.securityPrivKeyLen) != SNMPERR_SUCCESS) { SPINE_LOG(("SNMP: Error generating SNMPv3 Ku from privacy pass phrase.")); return 0; } } } /* open SNMP Session */ thread_mutex_lock(LOCK_SNMP); sessp = snmp_sess_open(&session); thread_mutex_unlock(LOCK_SNMP); if (!sessp) { if (is_debug_device(host_id)) { SPINE_LOG(("ERROR: Problem initializing SNMP session '%s'", hostname)); } else { SPINE_LOG_MEDIUM(("ERROR: Problem initializing SNMP session '%s'", hostname)); } } return sessp; } /*! \fn void snmp_host_cleanup(void *snmp_session) * \brief closes an established snmp session * * This function performs cleanup of the snmp sessions once polling is completed * for a host. * */ void snmp_host_cleanup(void *snmp_session) { if (snmp_session != NULL) { snmp_sess_close(snmp_session); } } /*! \fn char *snmp_get_base(host_t *current_host, char *snmp_oid, bool should_fail) * \brief performs a single snmp_get for a specific snmp OID * * This function will poll a specific snmp OID for a host. The host snmp * session must already be established. * * \return returns the character representaton of the snmp OID, or "U" if * unsuccessful. * */ char *snmp_get_base(host_t *current_host, char *snmp_oid, bool should_fail) { struct snmp_pdu *pdu = NULL; struct snmp_pdu *response = NULL; struct variable_list *vars = NULL; size_t anOID_len = MAX_OID_LEN; oid anOID[MAX_OID_LEN]; int status; char *result_string; char temp_result[RESULTS_BUFFER]; if (!(result_string = (char *) malloc(RESULTS_BUFFER))) { die("ERROR: Fatal malloc error: snmp.c snmp_get!"); } result_string[0] = '\0'; if (current_host->ignore_host) { SPINE_LOG_HIGH(("WARNING: Skipped oid '%s' for Device[%d] as host ignore flag is active", snmp_oid, current_host->id)); SET_UNDEFINED(result_string); return result_string; } status = STAT_DESCRIP_ERROR; if (current_host->snmp_session != NULL) { anOID_len = MAX_OID_LEN; SPINE_LOG_DEVDBG(("Device[%i] DEBUG: snmp_pdu_create(%s)", current_host->id, snmp_oid)); pdu = snmp_pdu_create(SNMP_MSG_GET); SPINE_LOG_DEVDBG(("Device[%i] DEBUG: snmp_pdu_create(%s) [complete]", current_host->id, snmp_oid)); if (pdu != NULL) { SPINE_LOG_DEVDBG(("Device[%i] DEBUG: snmp_parse_oid(%s)", current_host->id, snmp_oid)); if (!snmp_parse_oid(snmp_oid, anOID, &anOID_len)) { SPINE_LOG_DEVDBG(("Device[%i] DEBUG: snmp_parse_oid(%s) [complete]", current_host->id, snmp_oid)); SPINE_LOG(("Device[%i] ERROR: SNMP Get Problems parsing SNMP OID %s", current_host->id, snmp_oid)); SET_UNDEFINED(result_string); return result_string; } else { SPINE_LOG_DEVDBG(("Device[%i] DEBUG: snmp_parse_oid(%s) [complete]", current_host->id, snmp_oid)); SPINE_LOG_DEVDBG(("Device[%i] DEBUG: snmp_add_null_var(%s)", current_host->id, snmp_oid)); snmp_add_null_var(pdu, anOID, anOID_len); SPINE_LOG_DEVDBG(("Device[%i] DEBUG: snmp_add_null_var(%s) [complete]", current_host->id, snmp_oid)); } /* poll host */ SPINE_LOG_DEVDBG(("Device[%i] DEBUG: snmp_sess_sync_response(%s)", current_host->id, snmp_oid)); status = snmp_sess_synch_response(current_host->snmp_session, pdu, &response); SPINE_LOG_DEVDBG(("Device[%i] DEBUG: snmp_sess_sync_response(%s) [complete]", current_host->id, snmp_oid)); } /* add status to host structure */ current_host->snmp_status = status; /* liftoff, successful poll, process it!! */ if (status == STAT_DESCRIP_ERROR) { SPINE_LOG(("ERROR: Unable to create SNMP PDU")); SET_UNDEFINED(result_string); status = STAT_ERROR; response = NULL; } else if (status == STAT_SUCCESS) { if (response == NULL) { SPINE_LOG(("ERROR: An internal Net-Snmp error condition detected in Cacti snmp_get")); SET_UNDEFINED(result_string); status = STAT_ERROR; } else if (response->errstat == SNMP_ERR_NOERROR && response->variables != NULL && response->variables->name != NULL) { vars = response->variables; if (vars->type == SNMP_NOSUCHOBJECT) { SET_UNDEFINED(result_string); status = STAT_ERROR; SPINE_LOG_HIGH(("ERROR: No such Object for oid '%s' for Device[%d] with Status[%d]", snmp_oid, current_host->id, status)); } else if (vars->type == SNMP_NOSUCHINSTANCE) { SET_UNDEFINED(result_string); status = STAT_ERROR; // We will ignore the new OID error if (!strstr(snmp_oid, ".1.3.6.1.6.3.10.2.1.3.0")) { SPINE_LOG_HIGH(("WARNING: No such Instance for oid '%s' for Device[%d] with Status[%d]", snmp_oid, current_host->id, status)); } else { SPINE_LOG_DEBUG(("NOTE: Legacy SNMP agent found! No per second Uptime oid found '%s' for Device[%d] with Status[%d]", snmp_oid, current_host->id, status)); } } else if (vars->type == SNMP_ENDOFMIBVIEW) { SET_UNDEFINED(result_string); status = STAT_ERROR; SPINE_LOG_HIGH(("ERROR: End of Mib for oid '%s' for Device[%d] with Status[%d]", snmp_oid, current_host->id, status)); } else { snprint_value(temp_result, RESULTS_BUFFER, vars->name, vars->name_length, vars); snprintf(result_string, RESULTS_BUFFER, "%s", trim(temp_result)); } } else { SPINE_LOG_HIGH(("ERROR: Failed to get oid '%s' for Device[%d] with Response[%ld]", snmp_oid, current_host->id, response->errstat)); } } else if (response != NULL && response->variables != NULL) { vars = response->variables; if (vars->type == SNMP_NOSUCHOBJECT) { SET_UNDEFINED(result_string); status = STAT_ERROR; SPINE_LOG_HIGH(("ERROR: No such Object for oid '%s' for Device[%d] with Status[%d]", snmp_oid, current_host->id, status)); } else if (vars->type == SNMP_NOSUCHINSTANCE) { SET_UNDEFINED(result_string); status = STAT_ERROR; // We will ignore the new OID error if (!strstr(snmp_oid, ".1.3.6.1.6.3.10.2.1.3.0")) { SPINE_LOG_HIGH(("WARNING: No such Instance for oid '%s' for Device[%d] with Status[%d]", snmp_oid, current_host->id, status)); } else { SPINE_LOG_DEBUG(("NOTE: Per second level uptime oid missing oid '%s' for Device[%d] with Status[%d]", snmp_oid, current_host->id, status)); } } else if (vars->type == SNMP_ENDOFMIBVIEW) { SET_UNDEFINED(result_string); status = STAT_ERROR; SPINE_LOG_HIGH(("ERROR: End of Mib for oid '%s' for Device[%d] with Status[%d]", snmp_oid, current_host->id, status)); } else { SET_UNDEFINED(result_string); status = STAT_ERROR; SPINE_LOG_HIGH(("ERROR: Unknown error getting oid '%s' for Device[%d] with Status[%d]", snmp_oid, current_host->id, status)); } } else if (status == STAT_TIMEOUT) { SPINE_LOG_HIGH(("ERROR: Timeout getting oid '%s' for Device[%d] with Status[%d]", snmp_oid, current_host->id, status)); } else { SPINE_LOG_HIGH(("ERROR: Unknown error getting oid '%s' for Device[%d] with Status[%d]", snmp_oid, current_host->id, status)); } if (response != NULL && status != STAT_DESCRIP_ERROR) { snmp_free_pdu(response); response = NULL; } } if (status != STAT_SUCCESS && should_fail) { current_host->ignore_host = TRUE; SET_UNDEFINED(result_string); } return result_string; } char *snmp_get(host_t *current_host, char *snmp_oid) { return snmp_get_base(current_host, snmp_oid, true); } /*! \fn char *snmp_getnext(host_t *current_host, char *snmp_oid) * \brief performs a single snmp_getnext for a specific snmp OID * * This function will poll a specific snmp OID for a host. The host snmp * session must already be established. * * \return returns the character representaton of the snmp OID, or "U" if * unsuccessful. * */ char *snmp_getnext(host_t *current_host, char *snmp_oid) { struct snmp_pdu *pdu = NULL; struct snmp_pdu *response = NULL; struct variable_list *vars = NULL; size_t anOID_len = MAX_OID_LEN; oid anOID[MAX_OID_LEN]; int status; char *result_string; char temp_result[RESULTS_BUFFER]; if (!(result_string = (char *) malloc(RESULTS_BUFFER))) { die("ERROR: Fatal malloc error: snmp.c snmp_get!"); } result_string[0] = '\0'; status = STAT_DESCRIP_ERROR; if (current_host->snmp_session != NULL) { anOID_len = MAX_OID_LEN; pdu = snmp_pdu_create(SNMP_MSG_GETNEXT); if (!snmp_parse_oid(snmp_oid, anOID, &anOID_len)) { SPINE_LOG(("Device[%i] ERROR: SNMP Getnext Problems parsing SNMP OID %s", current_host->id, snmp_oid)); SET_UNDEFINED(result_string); return result_string; } else { snmp_add_null_var(pdu, anOID, anOID_len); } /* poll host */ status = snmp_sess_synch_response(current_host->snmp_session, pdu, &response); /* add status to host structure */ current_host->snmp_status = status; /* liftoff, successful poll, process it!! */ if (status == STAT_SUCCESS) { if (response == NULL) { SPINE_LOG(("ERROR: An internal Net-Snmp error condition detected in Cacti snmp_get")); SET_UNDEFINED(result_string); status = STAT_ERROR; } else { if (response->errstat == SNMP_ERR_NOERROR) { vars = response->variables; if (vars != NULL) { snprint_value(temp_result, RESULTS_BUFFER, vars->name, vars->name_length, vars); snprint_asciistring(result_string, RESULTS_BUFFER, (unsigned char *)temp_result, strlen(temp_result)); } else { SET_UNDEFINED(result_string); status = STAT_ERROR; } } } } if (response) { snmp_free_pdu(response); response = NULL; } } else { status = STAT_DESCRIP_ERROR; } if (status != STAT_SUCCESS) { current_host->ignore_host = TRUE; SET_UNDEFINED(result_string); } return result_string; } /*! \fn char *snmp_count(host_t *current_host, char *snmp_oid) * \brief counts entries of snmp table specified by a specific snmp OID * * This function will poll a specific snmp OID for a host. The host snmp * session must already be established. * * \return returns count of table entries * */ int snmp_count(host_t *current_host, char *snmp_oid) { struct snmp_pdu *pdu = NULL; struct snmp_pdu *response = NULL; struct variable_list *vars = NULL; size_t anOID_len = MAX_OID_LEN; size_t rootlen = MAX_OID_LEN; oid anOID[MAX_OID_LEN]; oid root[MAX_OID_LEN]; int status; int ok = 1; int error_occurred = 0; int count = 0; status = STAT_DESCRIP_ERROR; if (is_debug_device(current_host->id)) { SPINE_LOG(("DEBUG: walk starts at OID %s", snmp_oid)); } else { SPINE_LOG_DEBUG(("DEBUG: walk starts at OID %s", snmp_oid)); } if (current_host->snmp_session != NULL) { rootlen = MAX_OID_LEN; /* parse input parm to an array for use with snmp functions */ if (!snmp_parse_oid(snmp_oid, root, &rootlen)) { SPINE_LOG(("Device[%i] ERROR: SNMP Count Problems parsing SNMP OID %s", current_host->id, snmp_oid)); return count; } memmove(anOID, root, rootlen * sizeof(oid)); anOID_len = rootlen; while (ok && !error_occurred) { /* create PDU for GETNEXT request */ pdu = snmp_pdu_create(SNMP_MSG_GETNEXT); snmp_add_null_var(pdu, anOID, anOID_len); /* do the request, use thread safe call */ status = snmp_sess_synch_response(current_host->snmp_session, pdu, &response); /* add status to host structure */ current_host->snmp_status = status; //SPINE_LOG_DEBUG(("TRACE: Status %i Response %i", status, response->errstat)); if (status == STAT_SUCCESS) { if (response->errstat == SNMP_ERR_NOERROR) { /* check resulting variables */ for (vars = response->variables; vars; vars = vars->next_variable) { if ((vars->name_length < rootlen) || (memcmp(root, vars->name, rootlen * sizeof(oid)) != 0)) { /* next OID is not part of snmptable */ ok = 0; continue; } count++; /* END OF MIB or NO SUCH OBJECT or NO SUCH INSTANCE */ if ((vars->type != SNMP_ENDOFMIBVIEW) && (vars->type != SNMP_NOSUCHOBJECT) && (vars->type != SNMP_NOSUCHINSTANCE)) { /* valid data, so perform a compare */ if (snmp_oid_compare(anOID, anOID_len, vars->name, vars->name_length) >= 0) { SPINE_LOG(("ERROR: OID not increasing")); ok = 0; error_occurred = 1; } /* prepare next turn */ memmove((char *) anOID, (char *) vars->name, vars->name_length * sizeof(oid)); anOID_len = vars->name_length; } else { /* abnormal end of loop */ ok = 0; } } } else { SPINE_LOG(("ERROR: An internal Net-Snmp error condition detected in Cacti snmp_count")); } } else if (status == STAT_TIMEOUT) { SPINE_LOG(("ERROR: Timeout detected in Cacti snmp_count")); ok = 0; error_occurred = 1; } else { /* status == STAT_ERROR */ SPINE_LOG(("ERROR: An internal Net-Snmp error condition detected in Cacti snmp_count (STAT_ERROR)")); ok = 0; error_occurred = 1; } if (response) { snmp_free_pdu(response); } } } else { status = STAT_DESCRIP_ERROR; } if (status != STAT_SUCCESS) { current_host->ignore_host = TRUE; } return count; } /*! \fn void snmp_snprint_value(char *obuf, size_t buf_len, const oid *objid, size_t objidlen, struct variable_list *variable) * * \brief replacement for the buggy net-snmp.org snprint_value function * * This function format an output buffer with the correct string representation * of an snmp OID result fetched with snmp_get_multi. The buffer pointed to by * the function is modified. * */ void snmp_snprint_value(char *obuf, size_t buf_len, const oid *objid, size_t objidlen, struct variable_list *variable) { u_char *buf = NULL; size_t out_len = 0; if (buf_len > 0) { if ((buf = (u_char *) calloc(buf_len, 1)) != 0) { sprint_realloc_by_type(&buf, &buf_len, &out_len, 0, variable, NULL, NULL, NULL); snprintf(obuf, buf_len, "%s", buf); } else { SET_UNDEFINED(obuf); } free(buf); } else { SET_UNDEFINED(obuf); } } /*! \fn char *snmp_get_multi(host_t *current_host, target_t *poller_items, snmp_oids_t *snmp_oids, int num_oids) * \brief performs multiple OID snmp_get's in a single network call * * This function will a group of snmp OID's for a host. The host snmp * session must already be established. The function will modify elements of * the snmp_oids array with the results from the snmp api call. * */ void snmp_get_multi(host_t *current_host, target_t *poller_items, snmp_oids_t *snmp_oids, int num_oids) { struct snmp_pdu *pdu = NULL; struct snmp_pdu *response = NULL; struct variable_list *vars = NULL; int status; int i; int array_count; int index_count; char temp_result[RESULTS_BUFFER]; struct nameStruct { oid name[MAX_OID_LEN]; size_t name_len; } *name, *namep; /* load up oids */ namep = name = (struct nameStruct *) calloc(num_oids, sizeof(*name)); pdu = snmp_pdu_create(SNMP_MSG_GET); for (i = 0; i < num_oids; i++) { namep->name_len = MAX_OID_LEN; if (!snmp_parse_oid(snmp_oids[i].oid, namep->name, &namep->name_len)) { SPINE_LOG(("Device[%i] DS[%i] ERROR: Problems parsing Multi SNMP OID! (oid: %s), Set MAX_OIDS to 1 for this host to isolate bad OID", current_host->id, poller_items[snmp_oids[i].array_position].local_data_id, snmp_oids[i].oid)); /* Mark this OID as "bad" */ SET_UNDEFINED(snmp_oids[i].result); } else { snmp_add_null_var(pdu, namep->name, namep->name_len); } namep++; } status = STAT_DESCRIP_ERROR; /* execute the multi-get request */ retry: status = snmp_sess_synch_response(current_host->snmp_session, pdu, &response); /* add status to host structure */ current_host->snmp_status = status; /* liftoff, successful poll, process it!! */ if (status == STAT_SUCCESS) { if (response == NULL) { SPINE_LOG(("ERROR: An internal Net-Snmp error condition detected in Cacti snmp_get_multi")); status = STAT_ERROR; } else { if (response->errstat == SNMP_ERR_NOERROR) { vars = response->variables; for (i = 0; i < num_oids && vars; i++) { if (!IS_UNDEFINED(snmp_oids[i].result)) { snprint_value(temp_result, RESULTS_BUFFER, vars->name, vars->name_length, vars); snprintf(snmp_oids[i].result, RESULTS_BUFFER, "%s", trim(temp_result)); vars = vars->next_variable; } } } else { if (response->errindex != 0) { index_count = 1; array_count = 0; /* Find our index against errindex */ while (array_count < num_oids) { if (IS_UNDEFINED(snmp_oids[array_count].result) ) { array_count++; } else { /* if we have found our error, exit */ if (index_count == response->errindex) { SET_UNDEFINED(snmp_oids[array_count].result); break; } array_count++; index_count++; } } /* remove the invalid OID from the PDU */ pdu = snmp_fix_pdu(response, SNMP_MSG_GET); /* free the previous response */ snmp_free_pdu(response); response = NULL; if (pdu != NULL) { /* retry the request */ goto retry; } else { /* all OID's errored out so exit cleanly */ status = STAT_SUCCESS; } } } } } if (status == STAT_TIMEOUT) { current_host->ignore_host = 1; for (i = 0; i < num_oids; i++) { SET_UNDEFINED(snmp_oids[i].result); } } if (response != NULL) { snmp_free_pdu(response); } free(name); } cacti-spine-1.2.30/nft_popen.c0000664000175000017500000003002214770060157015160 0ustar markvmarkv/* +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 The Cacti Group | | | | 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. | +-------------------------------------------------------------------------+ | Cacti: The Complete RRDtool-based Graphing Solution | +-------------------------------------------------------------------------+ | This code is designed, written, and maintained by the Cacti Group. See | | about.php and/or the AUTHORS file for specific developer information. | +-------------------------------------------------------------------------+ | http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ /******************************************************************************* ex: set tabstop=4 shiftwidth=4 autoindent: * (C) Xenadyne Inc. 2002. All Rights Reserved * * Permission to use, copy, modify and distribute this software for * any purpose and without fee is hereby granted, provided that the * above copyright notice appears in all copies. Also note the * University of California copyright below. * * XENADYNE INC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL XENADYNE BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM THE * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * File: nft_popen.c * * Description: A thread-safe replacement for popen()/pclose(). * * This is a thread-safe variant of popen that does unbuffered IO, to * avoid running afoul of Solaris's inability to fdopen when fd > 255. * ******************************************************************************* */ /* * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software written by Ken Arnold and * published in UNIX Review, Vol. 6, No. 8. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "common.h" #include "spine.h" /* An instance of this struct is created for each popen() fd. */ static struct pid { struct pid *next; int fd; pid_t pid; } * PidList; /* Serialize access to PidList. */ static pthread_mutex_t ListMutex = PTHREAD_MUTEX_INITIALIZER; static void close_cleanup(void *); /*! ------------------------------------------------------------------------------ * * nft_popen * * The nft_popen() function forks a command in a child process, and returns * a pipe that is connected to the child's standard input and output. It is * like the standard popen() call, except that it does not dfopen() the pipe * file descriptor in order to return a stdio FILE *. This is useful if you * wish to use select()- or poll()-driven IO. * * The mode argument is defined as in standard popen(). * * On success, returns a file descriptor, or -1 on error. * On failure, returns -1, with errno set to one of: * EINVAL The mode argument is incorrect. * EMFILE pipe() failed. * ENFILE pipe() failed. * ENOMEM malloc() failed. * EAGAIN fork() failed. * *------------------------------------------------------------------------------ */ int nft_popen(const char * command, const char * type) { struct pid *cur; struct pid *p; int pdes[2]; int fd, pid, twoway; char *argv[4]; int cancel_state; extern char **environ; int retry_count = 0; /* On platforms where pipe() is bidirectional, * "r+" gives two-way communication. */ if (strchr(type, '+')) { twoway = 1; type = "r+"; }else { twoway = 0; if ((*type != 'r' && *type != 'w') || type[1]) { errno = EINVAL; return -1; } } if (pipe(pdes) < 0) return -1; /* Disable thread cancellation from this point forward. */ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state); if ((cur = malloc(sizeof(struct pid))) == NULL) { (void)close(pdes[0]); (void)close(pdes[1]); pthread_setcancelstate(cancel_state, NULL); return -1; } argv[0] = "sh"; argv[1] = "-c"; argv[2] = (char *)command; argv[3] = NULL; /* Lock the list mutex prior to forking, to ensure that * the child process sees PidList in a consistent list state. */ pthread_mutex_lock(&ListMutex); /* Fork. */ retry: switch (pid = vfork()) { case -1: /* Error. */ switch (errno) { case EAGAIN: if (retry_count < 3) { retry_count++; #ifndef SOLAR_THREAD /* take a moment */ usleep(50000); #endif goto retry; }else{ SPINE_LOG(("ERROR: SCRIPT: Cound not fork. Out of Resources nft_popen.c")); } case ENOMEM: if (retry_count < 3) { retry_count++; #ifndef SOLAR_THREAD /* take a moment */ usleep(50000); #endif goto retry; }else{ SPINE_LOG(("ERROR: SCRIPT Cound not fork. Out of Memory nft_popen.c")); } default: SPINE_LOG(("ERROR: SCRIPT Cound not fork. Unknown Reason nft_popen.c")); } (void)close(pdes[0]); (void)close(pdes[1]); pthread_mutex_unlock(&ListMutex); pthread_setcancelstate(cancel_state, NULL); return -1; /* NOTREACHED */ case 0: /* Child. */ if (*type == 'r') { /* The dup2() to STDIN_FILENO is repeated to avoid * writing to pdes[1], which might corrupt the * parent's copy. This isn't good enough in * general, since the _exit() is no return, so * the compiler is free to corrupt all the local * variables. */ (void)close(pdes[0]); if (pdes[1] != STDOUT_FILENO) { (void)dup2(pdes[1], STDOUT_FILENO); (void)close(pdes[1]); if (twoway) (void)dup2(STDOUT_FILENO, STDIN_FILENO); }else if (twoway && (pdes[1] != STDIN_FILENO)) (void)dup2(pdes[1], STDIN_FILENO); }else { if (pdes[0] != STDIN_FILENO) { (void)dup2(pdes[0], STDIN_FILENO); (void)close(pdes[0]); } (void)close(pdes[1]); } /* Close all the other pipes in the child process. * Posix.2 requires this, tho I don't know why. */ for (p = PidList; p; p = p->next) (void)close(p->fd); /* Execute the command. */ #if defined(__CYGWIN__) if (set.cygwinshloc == 0) { execve("sh.exe", argv, environ); }else{ execve("/bin/sh", argv, environ); } #else execve("/bin/sh", argv, environ); #endif _exit(127); /* NOTREACHED */ } /* Parent. */ if (*type == 'r') { fd = pdes[0]; (void)close(pdes[1]); }else { fd = pdes[1]; (void)close(pdes[0]); } /* Link into list of file descriptors. */ cur->fd = fd; cur->pid = pid; cur->next = PidList; PidList = cur; /* Unlock the mutex, and restore caller's cancellation state. */ pthread_mutex_unlock(&ListMutex); pthread_setcancelstate(cancel_state, NULL); return fd; } /*! ------------------------------------------------------------------------------ * * nft_pchild * * Get the pid of the child process for an fd created by ntf_popen(). * * On success, the pid of the child process is returned. * On failure, nft_pchild() returns -1, with errno set to: * * EBADF The fd is not an active nft_popen() file descriptor. * *------------------------------------------------------------------------------ */ int nft_pchild(int fd) { struct pid *cur; pid_t pid = 0; /* Find the appropriate file descriptor. */ pthread_mutex_lock(&ListMutex); for (cur = PidList; cur; cur = cur->next) if (cur->fd == fd) { pid = cur->pid; break; } pthread_mutex_unlock(&ListMutex); if (cur == NULL) { errno = EBADF; return -1; } return pid; } /*! ------------------------------------------------------------------------------ * * nft_pclose * * Close the pipe and wait for the status of the child process. * * On success, the exit status of the child process is returned. * On failure, nft_pclose() returns -1, with errno set to: * * EBADF The fd is not an active popen() file descriptor. * ECHILD The waitpid() call failed. * * This call is cancellable. * *------------------------------------------------------------------------------ */ int nft_pclose(int fd) { struct pid *cur; int pstat; pid_t pid; /* Find the appropriate file descriptor. */ pthread_mutex_lock(&ListMutex); for (cur = PidList; cur; cur = cur->next) if (cur->fd == fd) break; pthread_mutex_unlock(&ListMutex); if (cur == NULL) { errno = EBADF; return -1; } /* The close and waitpid calls below are cancellation points. * We want to ensure that the fd is closed and the PidList * entry freed despite cancellation, so push a cleanup handler. */ pthread_cleanup_push(close_cleanup, cur); /* end the process nicely and then forcefully */ (void)close(fd); cur->fd = -1; /* Prevent the fd being closed twice. */ do { pid = waitpid(cur->pid, &pstat, 0); } while (pid == -1 && errno == EINTR); pthread_cleanup_pop(1); /* Execute the cleanup handler. */ return (pid == -1 ? -1 : pstat); } /*! ------------------------------------------------------------------------------ * close_cleanup - close the pipe and free the pidlist entry. *------------------------------------------------------------------------------ */ static void close_cleanup(void * arg) { struct pid * cur = arg; struct pid * prev; /* Close the pipe fd if necessary. */ if (cur->fd >= 0) { (void)close(cur->fd); } /* Remove the entry from the linked list. */ pthread_mutex_lock(&ListMutex); if (PidList == cur) { PidList = cur->next; }else{ for (prev = PidList; prev; prev = prev->next) if (prev->next == cur) { prev->next = cur->next; break; } assert(prev != NULL); /* Search should not fail */ } pthread_mutex_unlock(&ListMutex); free(cur); } cacti-spine-1.2.30/locks.c0000664000175000017500000002776114770060157014323 0ustar markvmarkv/* ex: set tabstop=4 shiftwidth=4 autoindent: +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 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, implimentation details) | +-------------------------------------------------------------------------+ | - Cacti - http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ #include "common.h" #include "spine.h" /* * each lock requires a handful of parts: a mutex, an init structure, and an * init helper function. We are NOT allowed to use these in an array (doesn * not work with pthreads), so we are stuck setting these up individually. * This macro defines these helpers in a single step. */ #define DEFINE_SPINE_LOCK(name) \ static pthread_mutex_t name ## _lock; \ static pthread_once_t name ## _lock_o = PTHREAD_ONCE_INIT; \ static pthread_cond_t name ## _cond = PTHREAD_COND_INITIALIZER; \ static void init_ ## name ## _lock(void) { \ pthread_mutex_init(&name ## _lock, PTHREAD_MUTEXATTR_DEFAULT); \ } DEFINE_SPINE_LOCK(snmp) DEFINE_SPINE_LOCK(seteuid) DEFINE_SPINE_LOCK(ghbn) DEFINE_SPINE_LOCK(pool) DEFINE_SPINE_LOCK(syslog) DEFINE_SPINE_LOCK(php) DEFINE_SPINE_LOCK(php_proc_0) DEFINE_SPINE_LOCK(php_proc_1) DEFINE_SPINE_LOCK(php_proc_2) DEFINE_SPINE_LOCK(php_proc_3) DEFINE_SPINE_LOCK(php_proc_4) DEFINE_SPINE_LOCK(php_proc_5) DEFINE_SPINE_LOCK(php_proc_6) DEFINE_SPINE_LOCK(php_proc_7) DEFINE_SPINE_LOCK(php_proc_8) DEFINE_SPINE_LOCK(php_proc_9) DEFINE_SPINE_LOCK(php_proc_10) DEFINE_SPINE_LOCK(php_proc_11) DEFINE_SPINE_LOCK(php_proc_12) DEFINE_SPINE_LOCK(php_proc_13) DEFINE_SPINE_LOCK(php_proc_14) DEFINE_SPINE_LOCK(thdet) DEFINE_SPINE_LOCK(host_time) void init_mutexes() { pthread_once((pthread_once_t*) get_attr(LOCK_SNMP_O), init_snmp_lock); pthread_once((pthread_once_t*) get_attr(LOCK_SETEUID_O), init_seteuid_lock); pthread_once((pthread_once_t*) get_attr(LOCK_GHBN_O), init_ghbn_lock); pthread_once((pthread_once_t*) get_attr(LOCK_POOL_O), init_pool_lock); pthread_once((pthread_once_t*) get_attr(LOCK_SYSLOG_O), init_syslog_lock); pthread_once((pthread_once_t*) get_attr(LOCK_PHP_O), init_php_lock); pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_0_O), init_php_proc_0_lock); pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_1_O), init_php_proc_1_lock); pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_2_O), init_php_proc_2_lock); pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_3_O), init_php_proc_3_lock); pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_4_O), init_php_proc_4_lock); pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_5_O), init_php_proc_5_lock); pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_6_O), init_php_proc_6_lock); pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_7_O), init_php_proc_7_lock); pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_8_O), init_php_proc_8_lock); pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_9_O), init_php_proc_9_lock); pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_10_O), init_php_proc_10_lock); pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_11_O), init_php_proc_11_lock); pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_12_O), init_php_proc_12_lock); pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_13_O), init_php_proc_13_lock); pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_14_O), init_php_proc_14_lock); pthread_once((pthread_once_t*) get_attr(LOCK_THDET_O), init_thdet_lock); pthread_once((pthread_once_t*) get_attr(LOCK_HOST_TIME_O), init_host_time_lock); } const char* get_name(int lock) { switch (lock) { case LOCK_SNMP: return "snmp"; case LOCK_SETEUID: return "seteuid"; case LOCK_GHBN: return "ghbn"; case LOCK_POOL: return "pool"; case LOCK_SYSLOG: return "syslog"; case LOCK_PHP: return "php"; case LOCK_PHP_PROC_0: return "php_proc_0"; case LOCK_PHP_PROC_1: return "php_proc_1"; case LOCK_PHP_PROC_2: return "php_proc_2"; case LOCK_PHP_PROC_3: return "php_proc_3"; case LOCK_PHP_PROC_4: return "php_proc_4"; case LOCK_PHP_PROC_5: return "php_proc_5"; case LOCK_PHP_PROC_6: return "php_proc_6"; case LOCK_PHP_PROC_7: return "php_proc_7"; case LOCK_PHP_PROC_8: return "php_proc_8"; case LOCK_PHP_PROC_9: return "php_proc_9"; case LOCK_PHP_PROC_10: return "php_proc_10"; case LOCK_PHP_PROC_11: return "php_proc_11"; case LOCK_PHP_PROC_12: return "php_proc_12"; case LOCK_PHP_PROC_13: return "php_proc_13"; case LOCK_PHP_PROC_14: return "php_proc_14"; case LOCK_THDET: return "thdet"; case LOCK_HOST_TIME: return "host_time"; } return "Unknown lock"; } pthread_cond_t* get_cond(int lock) { pthread_cond_t *ret_val = NULL; switch (lock) { case LOCK_SNMP: ret_val = &snmp_cond; break; case LOCK_SETEUID: ret_val = &seteuid_cond; break; case LOCK_GHBN: ret_val = &ghbn_cond; break; case LOCK_POOL: ret_val = &pool_cond; break; case LOCK_SYSLOG: ret_val = &syslog_cond; break; case LOCK_PHP: ret_val = &php_cond; break; case LOCK_PHP_PROC_0: ret_val = &php_proc_0_cond; break; case LOCK_PHP_PROC_1: ret_val = &php_proc_1_cond; break; case LOCK_PHP_PROC_2: ret_val = &php_proc_2_cond; break; case LOCK_PHP_PROC_3: ret_val = &php_proc_3_cond; break; case LOCK_PHP_PROC_4: ret_val = &php_proc_4_cond; break; case LOCK_PHP_PROC_5: ret_val = &php_proc_5_cond; break; case LOCK_PHP_PROC_6: ret_val = &php_proc_6_cond; break; case LOCK_PHP_PROC_7: ret_val = &php_proc_7_cond; break; case LOCK_PHP_PROC_8: ret_val = &php_proc_8_cond; break; case LOCK_PHP_PROC_9: ret_val = &php_proc_9_cond; break; case LOCK_PHP_PROC_10: ret_val = &php_proc_10_cond; break; case LOCK_PHP_PROC_11: ret_val = &php_proc_11_cond; break; case LOCK_PHP_PROC_12: ret_val = &php_proc_12_cond; break; case LOCK_PHP_PROC_13: ret_val = &php_proc_13_cond; break; case LOCK_PHP_PROC_14: ret_val = &php_proc_14_cond; break; case LOCK_THDET: ret_val = &thdet_cond; break; case LOCK_HOST_TIME: ret_val = &host_time_cond; break; } SPINE_LOG_DEVDBG(("LOCKS: [RET] Returning cond for %s", get_name(lock))); return ret_val; } pthread_mutex_t* get_lock(int lock) { pthread_mutex_t *ret_val = NULL; switch (lock) { case LOCK_SNMP: ret_val = &snmp_lock; break; case LOCK_SETEUID: ret_val = &seteuid_lock; break; case LOCK_GHBN: ret_val = &ghbn_lock; break; case LOCK_POOL: ret_val = &pool_lock; break; case LOCK_SYSLOG: ret_val = &syslog_lock; break; case LOCK_PHP: ret_val = &php_lock; break; case LOCK_PHP_PROC_0: ret_val = &php_proc_0_lock; break; case LOCK_PHP_PROC_1: ret_val = &php_proc_1_lock; break; case LOCK_PHP_PROC_2: ret_val = &php_proc_2_lock; break; case LOCK_PHP_PROC_3: ret_val = &php_proc_3_lock; break; case LOCK_PHP_PROC_4: ret_val = &php_proc_4_lock; break; case LOCK_PHP_PROC_5: ret_val = &php_proc_5_lock; break; case LOCK_PHP_PROC_6: ret_val = &php_proc_6_lock; break; case LOCK_PHP_PROC_7: ret_val = &php_proc_7_lock; break; case LOCK_PHP_PROC_8: ret_val = &php_proc_8_lock; break; case LOCK_PHP_PROC_9: ret_val = &php_proc_9_lock; break; case LOCK_PHP_PROC_10: ret_val = &php_proc_10_lock; break; case LOCK_PHP_PROC_11: ret_val = &php_proc_11_lock; break; case LOCK_PHP_PROC_12: ret_val = &php_proc_12_lock; break; case LOCK_PHP_PROC_13: ret_val = &php_proc_13_lock; break; case LOCK_PHP_PROC_14: ret_val = &php_proc_14_lock; break; case LOCK_THDET: ret_val = &thdet_lock; break; case LOCK_HOST_TIME: ret_val = &host_time_lock; break; } SPINE_LOG_DEVDBG(("LOCKS: [RET] Returning lock for %s", get_name(lock))); return ret_val; } pthread_once_t* get_attr(int locko) { pthread_once_t *ret_val = NULL; switch (locko) { case LOCK_SNMP_O: ret_val = &snmp_lock_o; break; case LOCK_SETEUID_O: ret_val = &seteuid_lock_o; break; case LOCK_GHBN_O: ret_val = &ghbn_lock_o; break; case LOCK_POOL_O: ret_val = &pool_lock_o; break; case LOCK_SYSLOG_O: ret_val = &syslog_lock_o; break; case LOCK_PHP_O: ret_val = &php_lock_o; break; case LOCK_PHP_PROC_0_O: ret_val = &php_proc_0_lock_o; break; case LOCK_PHP_PROC_1_O: ret_val = &php_proc_1_lock_o; break; case LOCK_PHP_PROC_2_O: ret_val = &php_proc_2_lock_o; break; case LOCK_PHP_PROC_3_O: ret_val = &php_proc_3_lock_o; break; case LOCK_PHP_PROC_4_O: ret_val = &php_proc_4_lock_o; break; case LOCK_PHP_PROC_5_O: ret_val = &php_proc_5_lock_o; break; case LOCK_PHP_PROC_6_O: ret_val = &php_proc_6_lock_o; break; case LOCK_PHP_PROC_7_O: ret_val = &php_proc_7_lock_o; break; case LOCK_PHP_PROC_8_O: ret_val = &php_proc_8_lock_o; break; case LOCK_PHP_PROC_9_O: ret_val = &php_proc_9_lock_o; break; case LOCK_PHP_PROC_10_O: ret_val = &php_proc_10_lock_o; break; case LOCK_PHP_PROC_11_O: ret_val = &php_proc_11_lock_o; break; case LOCK_PHP_PROC_12_O: ret_val = &php_proc_12_lock_o; break; case LOCK_PHP_PROC_13_O: ret_val = &php_proc_13_lock_o; break; case LOCK_PHP_PROC_14_O: ret_val = &php_proc_14_lock_o; break; case LOCK_THDET_O: ret_val = &thdet_lock_o; break; case LOCK_HOST_TIME_O: ret_val = &host_time_lock_o; break; } SPINE_LOG_DEVDBG(("LOCKS: [RET] Returning attr for %s", get_name(locko))); return ret_val; } void thread_mutex_lock(int mutex) { SPINE_LOG_DEVDBG(("LOCKS: [START] Mutex lock for %s", get_name(mutex))); pthread_mutex_lock(get_lock(mutex)); SPINE_LOG_DEVDBG(("LOCKS: [END] Mutex lock for %s", get_name(mutex))); } void thread_mutex_unlock(int mutex) { SPINE_LOG_DEVDBG(("LOCKS: [START] Mutex unlock for %s", get_name(mutex))); pthread_mutex_unlock(get_lock(mutex)); SPINE_LOG_DEVDBG(("LOCKS: [END] Mutex unlock for %s", get_name(mutex))); } int thread_mutex_trylock(int mutex) { SPINE_LOG_DEVDBG(("LOCKS: [START] Mutex try lock for %s", get_name(mutex))); int ret_val = pthread_mutex_trylock(get_lock(mutex)); SPINE_LOG_DEVDBG(("LOCKS: [END] Mutex try lock for %s, result = %d", get_name(mutex), ret_val)); return ret_val; } cacti-spine-1.2.30/LICENSE0000664000175000017500000006351014770060157014041 0ustar markvmarkv GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library 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 library 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 Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! cacti-spine-1.2.30/ping.h0000664000175000017500000001241214770060157014135 0ustar markvmarkv/* ex: set tabstop=4 shiftwidth=4 autoindent: +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 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, implimentation details) | +-------------------------------------------------------------------------+ | - Cacti - http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ #ifndef ICMP_ECHOREPLY #define ICMP_ECHOREPLY 0 /* Echo Reply */ #endif #ifndef MSG_WAITALL #define MSG_WAITALL 0x100 #endif #ifdef __CYGWIN__ struct icmp_ra_addr { u_int32_t ira_addr; u_int32_t ira_preference; }; struct iphdr { #if __BYTE_ORDER == __LITTLE_ENDIAN unsigned int ihl:4; unsigned int version:4; #elif __BYTE_ORDER == __BIG_ENDIAN unsigned int version:4; unsigned int ihl:4; #else # error "Please fix " #endif u_int8_t tos; u_int16_t tot_len; u_int16_t id; u_int16_t frag_off; u_int8_t ttl; u_int8_t protocol; u_int16_t check; u_int32_t saddr; u_int32_t daddr; /*The options start here. */ }; struct icmp { u_int8_t icmp_type; /* type of message, see below */ u_int8_t icmp_code; /* type sub code */ u_int16_t icmp_cksum; /* ones complement checksum of struct */ union { u_char ih_pptr; /* ICMP_PARAMPROB */ struct in_addr ih_gwaddr; /* gateway address */ struct ih_idseq /* echo datagram */ { u_int16_t icd_id; u_int16_t icd_seq; } ih_idseq; u_int32_t ih_void; /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */ struct ih_pmtu { u_int16_t ipm_void; u_int16_t ipm_nextmtu; } ih_pmtu; struct ih_rtradv { u_int8_t irt_num_addrs; u_int8_t irt_wpa; u_int16_t irt_lifetime; } ih_rtradv; } icmp_hun; #define icmp_pptr icmp_hun.ih_pptr #define icmp_gwaddr icmp_hun.ih_gwaddr #define icmp_id icmp_hun.ih_idseq.icd_id #define icmp_seq icmp_hun.ih_idseq.icd_seq #define icmp_void icmp_hun.ih_void #define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void #define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu #define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs #define icmp_wpa icmp_hun.ih_rtradv.irt_wpa #define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime union { struct { u_int32_t its_otime; u_int32_t its_rtime; u_int32_t its_ttime; } id_ts; struct { struct ip idi_ip; /* options and then 64 bits of data */ } id_ip; struct icmp_ra_addr id_radv; u_int32_t id_mask; u_int8_t id_data[1]; } icmp_dun; #define icmp_otime icmp_dun.id_ts.its_otime #define icmp_rtime icmp_dun.id_ts.its_rtime #define icmp_ttime icmp_dun.id_ts.its_ttime #define icmp_ip icmp_dun.id_ip.idi_ip #define icmp_radv icmp_dun.id_radv #define icmp_mask icmp_dun.id_mask #define icmp_data icmp_dun.id_data }; #endif /* Host availability functions */ extern int ping_host(host_t *host, ping_t *ping); extern int ping_snmp(host_t *host, ping_t *ping); extern int ping_icmp(host_t *host, ping_t *ping); extern int ping_udp(host_t *host, ping_t *ping); extern int ping_tcp(host_t *host, ping_t *ping); extern name_t *get_namebyhost(char *hostname, name_t *name); extern void update_host_status(int status, host_t *host, ping_t *ping, int availability_method); extern int init_sockaddr(struct sockaddr_in *name, const char *hostname, unsigned short int port); extern int get_address_type(host_t *host); extern unsigned short int get_checksum(void* buf, int len); cacti-spine-1.2.30/php.h0000664000175000017500000000472614770060157014000 0ustar markvmarkv/* ex: set tabstop=4 shiftwidth=4 autoindent: +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 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, implimentation details) | +-------------------------------------------------------------------------+ | - Cacti - http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ extern char *php_cmd(const char *php_command, int php_process); extern char *php_readpipe(int php_process, char *command); extern int php_init(int php_process); extern void php_close(int php_process); extern int php_get_process(void); cacti-spine-1.2.30/ping.c0000664000175000017500000013120314770060157014130 0ustar markvmarkv/* ex: set tabstop=4 shiftwidth=4 autoindent: +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 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, implimentation details) | +-------------------------------------------------------------------------+ | - Cacti - http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ #include "common.h" #include "spine.h" /*! \fn int ping_host(host_t *host, ping_t *ping) * \brief ping a host to determine if it is reachable for polling * \param host a pointer to the current host structure * \param ping a pointer to the current hosts ping structure * * This function pings a host using the method specified within the system * configuration and then returns the host status to the calling function. * * \return HOST_UP if the host is reachable, HOST_DOWN otherwise. */ int ping_host(host_t *host, ping_t *ping) { int ping_result; int snmp_result; double start_time; double end_time; /* snmp pinging has been selected at a minimum */ ping_result = 0; snmp_result = 0; /* icmp/tcp/udp ping test */ if ((host->availability_method == AVAIL_SNMP_AND_PING) || (host->availability_method == AVAIL_PING) || (host->availability_method == AVAIL_SNMP_OR_PING)) { if (host->ping_method == PING_ICMP) { if (set.icmp_avail == FALSE) { SPINE_LOG(("Device[%i] DEBUG Falling back to UDP Ping Due to SetUID Issues", host->id)); host->ping_method = PING_UDP; } } if (!strstr(host->hostname, "localhost")) { if (get_address_type(host) == 1) { if (host->ping_method == PING_ICMP) { ping_result = ping_icmp(host, ping); } else if (host->ping_method == PING_UDP) { ping_result = ping_udp(host, ping); } else if (host->ping_method == PING_TCP || host->ping_method == PING_TCP_CLOSED) { ping_result = ping_tcp(host, ping); } } else if (host->availability_method == AVAIL_PING) { snprintf(ping->ping_status, 50, "0.000"); snprintf(ping->ping_response, SMALL_BUFSIZE, "PING: Device is Unknown or is IPV6. Please use the SNMP ping options only."); ping_result = HOST_DOWN; } } else { snprintf(ping->ping_status, 50, "0.000"); snprintf(ping->ping_response, SMALL_BUFSIZE, "PING: Device does not require ping."); ping_result = HOST_UP; } } /* snmp test */ if ((host->availability_method == AVAIL_SNMP) || (host->availability_method == AVAIL_SNMP_GET_SYSDESC) || (host->availability_method == AVAIL_SNMP_GET_NEXT) || (host->availability_method == AVAIL_SNMP_AND_PING) || (host->availability_method == AVAIL_SNMP_OR_PING)) { /* If we are in AND mode and already have a failed ping result, we don't need SNMP */ if ((ping_result == HOST_DOWN) && (host->availability_method == AVAIL_SNMP_AND_PING)) { snmp_result = ping_result; } else { /* Lets assume the host is up because if we are in OR mode then we have already * pinged the host successfully, or some when silly people have not entered an * snmp_community under v1/2, we assume that this was successfully anyway */ snmp_result = HOST_UP; if ((host->availability_method != AVAIL_SNMP_OR_PING) && ((strlen(host->snmp_community) > 0) || (host->snmp_version >= 3))) { start_time = get_time_as_double(); snmp_result = ping_snmp(host, ping); end_time = get_time_as_double(); if (snmp_result == HOST_UP) { if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] INFO: SNMP Device Alive, Time:%.4f ms", host->id, end_time - start_time)); } else { SPINE_LOG_MEDIUM(("Device[%i] INFO: SNMP Device Alive, Time:%.4f ms", host->id, end_time - start_time)); } } else { if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] INFO: SNMP Device Down, Time:%.4f ms", host->id, end_time - start_time)); } else { SPINE_LOG_MEDIUM(("Device[%i] INFO: SNMP Device Down, Time:%.4f ms", host->id, end_time - start_time)); } } } } } switch (host->availability_method) { case AVAIL_SNMP_AND_PING: return ((ping_result == HOST_UP) && (snmp_result == HOST_UP)) ? HOST_UP : HOST_DOWN; case AVAIL_SNMP_OR_PING: return ((ping_result == HOST_UP) || (snmp_result == HOST_UP)) ? HOST_UP : HOST_DOWN; case AVAIL_SNMP: case AVAIL_SNMP_GET_NEXT: case AVAIL_SNMP_GET_SYSDESC: return (snmp_result == HOST_UP) ? HOST_UP : HOST_DOWN; case AVAIL_PING: return (ping_result == HOST_UP) ? HOST_UP : HOST_DOWN; case AVAIL_NONE: return HOST_UP; default: return HOST_DOWN; } } /*! \fn int ping_snmp(host_t *host, ping_t *ping) * \brief ping a host using snmp sysUptime * \param host a pointer to the current host structure * \param ping a pointer to the current hosts ping structure * * This function pings a host using snmp. It polls sysUptime by default. * It will modify the ping structure to include the specifics of the ping results. * * \return HOST_UP if the host is reachable, HOST_DOWN otherwise. * */ int ping_snmp(host_t *host, ping_t *ping) { char *poll_result = NULL; char *oid; double begin_time, end_time, total_time; double one_thousand = 1000.00; if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] DEBUG: Entering SNMP Ping", host->id)); } else { SPINE_LOG_DEBUG(("DEBUG: Device[%i] Entering SNMP Ping", host->id)); } if (host->snmp_session) { if (strlen(host->snmp_community) != 0 || host->snmp_version == 3) { /* by default, we look at sysUptime */ if (host->availability_method == AVAIL_SNMP_GET_NEXT) { oid = strdup(".1.3"); } else if (host->availability_method == AVAIL_SNMP_GET_SYSDESC) { oid = strdup(".1.3.6.1.2.1.1.1.0"); } else { oid = strdup(".1.3.6.1.2.1.1.3.0"); } if (oid == NULL) die("ERROR: malloc(): strdup() oid ping.c failed"); /* record start time */ begin_time = get_time_as_double(); if (host->availability_method == AVAIL_SNMP_GET_NEXT) { poll_result = snmp_getnext(host, oid); } else { poll_result = snmp_get(host, oid); } /* record end time */ end_time = get_time_as_double(); SPINE_FREE(oid); total_time = (end_time - begin_time) * one_thousand; /* do positive test cases first */ if (host->snmp_status == SNMPERR_UNKNOWN_OBJID) { snprintf(ping->snmp_response, SMALL_BUFSIZE, "Device responded to SNMP"); snprintf(ping->snmp_status, 50, "%.5f", total_time); SPINE_FREE(poll_result); return HOST_UP; } else if (host->snmp_status != SNMPERR_SUCCESS) { if (is_debug_device(host->id)) { if (host->snmp_status == STAT_TIMEOUT) { SPINE_LOG(("Device[%i] SNMP Ping Timeout", host->id)); } else { SPINE_LOG(("Device[%i] SNMP Ping Unknown Error", host->id)); } } else { if (host->snmp_status == STAT_TIMEOUT) { SPINE_LOG_HIGH(("Device[%i] SNMP Ping Timeout", host->id)); } else { SPINE_LOG_HIGH(("Device[%i] SNMP Ping Unknown Error", host->id)); } } snprintf(ping->snmp_response, SMALL_BUFSIZE, "Device did not respond to SNMP"); SPINE_FREE(poll_result); return HOST_DOWN; } else { snprintf(ping->snmp_response, SMALL_BUFSIZE, "Device responded to SNMP"); snprintf(ping->snmp_status, 50, "%.5f", total_time); SPINE_FREE(poll_result); return HOST_UP; } } else { snprintf(ping->snmp_status, 50, "0.00"); snprintf(ping->snmp_response, SMALL_BUFSIZE, "Device does not require SNMP"); return HOST_UP; } } else { snprintf(ping->snmp_status, 50, "0.00"); snprintf(ping->snmp_response, SMALL_BUFSIZE, "Invalid SNMP Session"); return HOST_DOWN; } } /*! \fn int ping_icmp(host_t *host, ping_t *ping) * \brief ping a host using an ICMP packet * \param host a pointer to the current host structure * \param ping a pointer to the current hosts ping structure * * This function pings a host using ICMP. The ICMP packet contains a marker * to the "Cacti" application so that firewall's can be configured to allow. * It will modify the ping structure to include the specifics of the ping results. * * \return HOST_UP if the host is reachable, HOST_DOWN otherwise. * */ int ping_icmp(host_t *host, ping_t *ping) { int icmp_socket; double begin_time, end_time, total_time; double host_timeout; double one_thousand = 1000.00; struct timeval timeout; struct sockaddr_in recvname; struct sockaddr_in fromname; char socket_reply[BUFSIZE]; int retry_count; char *cacti_msg = "cacti-monitoring-system\0"; int packet_len; socklen_t fromlen; ssize_t return_code; fd_set socket_fds; static unsigned int seq = 0; struct icmp *icmp; struct ip *ip; struct icmp *pkt; unsigned char *packet; if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] DEBUG: Entering ICMP Ping", host->id)); } else { SPINE_LOG_DEBUG(("DEBUG: Device[%i] Entering ICMP Ping", host->id)); } /* get ICMP socket */ retry_count = 0; while (TRUE) { #if !(defined(__CYGWIN__) && !defined(SOLAR_PRIV)) if (hasCaps() != TRUE) { thread_mutex_lock(LOCK_SETEUID); if (seteuid(0) == -1) { SPINE_LOG_DEBUG(("WARNING: Spine unable to obtain root privileges.")); } } #endif if ((icmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == -1) { usleep(500000); retry_count++; if (retry_count > 4) { snprintf(ping->ping_response, SMALL_BUFSIZE, "ICMP: Ping unable to create ICMP Socket"); snprintf(ping->ping_status, 50, "down"); #if !(defined(__CYGWIN__) && !defined(SOLAR_PRIV)) if (hasCaps() != TRUE) { if (seteuid(getuid()) == -1) { SPINE_LOG_DEBUG(("WARNING: Spine unable to drop from root to local user.")); } thread_mutex_unlock(LOCK_SETEUID); } #endif return HOST_DOWN; break; } } else { break; } } #if !(defined(__CYGWIN__) && !defined(SOLAR_PRIV)) if (hasCaps() != TRUE) { if (seteuid(getuid()) == -1) { SPINE_LOG_DEBUG(("WARNING: Spine unable to drop from root to local user.")); } thread_mutex_unlock(LOCK_SETEUID); } #endif /* convert the host timeout to a double precision number in seconds */ host_timeout = host->ping_timeout; /* allocate the packet in memory */ packet_len = ICMP_HDR_SIZE + strlen(cacti_msg); if (!(packet = malloc(packet_len))) { die("ERROR: Fatal malloc error: ping.c ping_icmp!"); } memset(packet, 0, packet_len); /* set the memory of the ping address */ memset(&fromname, 0, sizeof(struct sockaddr_in)); memset(&recvname, 0, sizeof(struct sockaddr_in)); icmp = (struct icmp*) packet; icmp->icmp_type = ICMP_ECHO; icmp->icmp_code = 0; icmp->icmp_id = getpid() & 0xFFFF; /* lock set/get the sequence and unlock */ thread_mutex_lock(LOCK_GHBN); icmp->icmp_seq = seq++; thread_mutex_unlock(LOCK_GHBN); icmp->icmp_cksum = 0; memcpy(packet+ICMP_HDR_SIZE, cacti_msg, strlen(cacti_msg)); icmp->icmp_cksum = get_checksum(packet, packet_len); /* hostname must be nonblank */ if ((strlen(host->hostname) != 0) && (icmp_socket != -1)) { /* initialize variables */ snprintf(ping->ping_status, 50, "down"); snprintf(ping->ping_response, SMALL_BUFSIZE, "default"); /* get address of hostname */ if (init_sockaddr(&fromname, host->hostname, 7)) { retry_count = 0; total_time = 0; begin_time = get_time_as_double(); /* initialize file descriptor to review for input/output */ FD_ZERO(&socket_fds); FD_SET(icmp_socket,&socket_fds); while (1) { if (retry_count > host->ping_retries) { snprintf(ping->ping_response, SMALL_BUFSIZE, "ICMP: Ping timed out"); snprintf(ping->ping_status, 50, "down"); free(packet); close(icmp_socket); return HOST_DOWN; } if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] DEBUG: Attempting to ping %s, seq %d (Retry %d of %d)", host->id, host->hostname, icmp->icmp_seq, retry_count, host->ping_retries)); } else { SPINE_LOG_DEBUG(("DEBUG: Device[%i] Attempting to ping %s, seq %d (Retry %d of %d)", host->id, host->hostname, icmp->icmp_seq, retry_count, host->ping_retries)); } /* decrement the timeout value by the total time */ timeout.tv_sec = rint((host_timeout - total_time) / 1000); timeout.tv_usec = ((int) (host_timeout - total_time) % 1000) * 1000; /* set the socket send and receive timeout */ setsockopt(icmp_socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)); setsockopt(icmp_socket, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout)); /* send packet to destination */ return_code = sendto(icmp_socket, packet, packet_len, 0, (struct sockaddr *) &fromname, sizeof(fromname)); fromlen = sizeof(fromname); /* wait for a response on the socket */ keep_listening: return_code = select(FD_SETSIZE, &socket_fds, NULL, NULL, &timeout); /* record end time */ end_time = get_time_as_double(); /* caculate total time */ total_time = (end_time - begin_time) * one_thousand; if (total_time < host_timeout) { #if !(defined(__CYGWIN__)) return_code = recvfrom(icmp_socket, socket_reply, BUFSIZE, MSG_WAITALL, (struct sockaddr *) &recvname, &fromlen); #else return_code = recvfrom(icmp_socket, socket_reply, BUFSIZE, MSG_PEEK, (struct sockaddr *) &recvname, &fromlen); #endif if (return_code < 0) { if (errno == EINTR) { /* call was interrupted by some system event */ if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] DEBUG: Received EINTR", host->id)); } else { SPINE_LOG_DEBUG(("DEBUG: Device[%i] Received EINTR", host->id)); } goto keep_listening; } } else { ip = (struct ip *) socket_reply; pkt = (struct icmp *) (socket_reply + (ip->ip_hl << 2)); if (fromname.sin_addr.s_addr == recvname.sin_addr.s_addr) { if (pkt->icmp_type == ICMP_ECHOREPLY) { if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] INFO: ICMP Device Alive, Try Count:%i, Time:%.4f ms", host->id, retry_count+1, (total_time))); } else { SPINE_LOG_MEDIUM(("Device[%i] INFO: ICMP Device Alive, Try Count:%i, Time:%.4f ms", host->id, retry_count+1, (total_time))); } snprintf(ping->ping_response, SMALL_BUFSIZE, "ICMP: Device is Alive"); snprintf(ping->ping_status, 50, "%.5f", total_time); free(packet); #if !(defined(__CYGWIN__) && !defined(SOLAR_PRIV)) if (hasCaps() != TRUE) { thread_mutex_lock(LOCK_SETEUID); if (seteuid(0) == -1) { SPINE_LOG_DEBUG(("WARNING: Spine unable to obtain root privileges.")); } } #endif close(icmp_socket); #if !(defined(__CYGWIN__) && !defined(SOLAR_PRIV)) if (hasCaps() != TRUE) { if (seteuid(getuid()) == -1) { SPINE_LOG_DEBUG(("WARNING: Spine unable to drop from root to local user.")); } thread_mutex_unlock(LOCK_SETEUID); } #endif return HOST_UP; } else { /* received a response other than an echo reply */ if (total_time > host_timeout) { retry_count++; total_time = 0; } continue; } } else { /* another host responded */ goto keep_listening; } } } else { if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] DEBUG: Exceeded Device Timeout, Retrying", host->id)); } else { SPINE_LOG_DEBUG(("DEBUG: Device[%i] Exceeded Device Timeout, Retrying", host->id)); } } total_time = 0; retry_count++; #ifndef SOLAR_THREAD usleep(1000); #endif } } else { snprintf(ping->ping_response, SMALL_BUFSIZE, "ICMP: Destination hostname invalid"); snprintf(ping->ping_status, 50, "down"); free(packet); #if !(defined(__CYGWIN__) && !defined(SOLAR_PRIV)) if (hasCaps() != TRUE) { thread_mutex_lock(LOCK_SETEUID); if (seteuid(0) == -1) { SPINE_LOG_DEBUG(("WARNING: Spine unable to obtain root privileges.")); } } #endif close(icmp_socket); #if !(defined(__CYGWIN__) && !defined(SOLAR_PRIV)) if (hasCaps() != TRUE) { if (seteuid(getuid()) == -1) { SPINE_LOG_DEBUG(("WARNING: Spine unable to drop from root to local user.")); } thread_mutex_unlock(LOCK_SETEUID); } #endif return HOST_DOWN; } } else { snprintf(ping->ping_response, SMALL_BUFSIZE, "ICMP: Destination address not specified"); snprintf(ping->ping_status, 50, "down"); free(packet); if (icmp_socket != -1) { #if !(defined(__CYGWIN__) && !defined(SOLAR_PRIV)) if (hasCaps() != TRUE) { thread_mutex_lock(LOCK_SETEUID); if (seteuid(0) == -1) { SPINE_LOG_DEBUG(("WARNING: Spine unable to obtain root privileges.")); } } #endif close(icmp_socket); #if !(defined(__CYGWIN__) && !defined(SOLAR_PRIV)) if (hasCaps() != TRUE) { if (seteuid(getuid()) == -1) { SPINE_LOG_DEBUG(("WARNING: Spine unable to drop from root to local user.")); } thread_mutex_unlock(LOCK_SETEUID); } #endif } return HOST_DOWN; } } /*! \fn int ping_udp(host_t *host, ping_t *ping) * \brief ping a host using an UDP datagram * \param host a pointer to the current host structure * \param ping a pointer to the current hosts ping structure * * This function pings a host using UDP. The UDP datagram contains a marker * to the "Cacti" application so that firewall's can be configured to allow. * It will modify the ping structure to include the specifics of the ping results. * * \return HOST_UP if the host is reachable, HOST_DOWN otherwise. * */ int ping_udp(host_t *host, ping_t *ping) { double begin_time, end_time, total_time; double host_timeout; double one_thousand = 1000.00; struct timeval timeout; int udp_socket; struct sockaddr_in servername; char socket_reply[BUFSIZE]; int retry_count; char request[BUFSIZE]; int request_len; int return_code; fd_set socket_fds; if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] DEBUG: Entering UDP Ping", host->id)); } else { SPINE_LOG_DEBUG(("DEBUG: Device[%i] Entering UDP Ping", host->id)); } /* set total time */ total_time = 0; begin_time = get_time_as_double(); /* convert the host timeout to a double precision number in seconds */ host_timeout = host->ping_timeout; /* initilize the socket */ udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); /* hostname must be nonblank */ if ((strlen(host->hostname) != 0) && (udp_socket != -1)) { /* initialize variables */ snprintf(ping->ping_status, 50, "down"); snprintf(ping->ping_response, SMALL_BUFSIZE, "default"); /* get address of hostname */ if (init_sockaddr(&servername, host->hostname, host->ping_port)) { if (connect(udp_socket, (struct sockaddr *) &servername, sizeof(servername)) < 0) { snprintf(ping->ping_status, 50, "down"); snprintf(ping->ping_response, SMALL_BUFSIZE, "UDP: Cannot connect to host"); close(udp_socket); return HOST_DOWN; } /* format packet */ snprintf(request, BUFSIZE, "cacti-monitoring-system"); /* the actual test data */ request_len = strlen(request); retry_count = 0; /* initialize file descriptor to review for input/output */ FD_ZERO(&socket_fds); FD_SET(udp_socket,&socket_fds); while (1) { if (retry_count > host->ping_retries) { snprintf(ping->ping_response, SMALL_BUFSIZE, "UDP: Ping timed out"); snprintf(ping->ping_status, 50, "down"); close(udp_socket); return HOST_DOWN; } /* record start time */ if (total_time == 0) { /* establish timeout value */ timeout.tv_sec = rint(host_timeout / 1000); timeout.tv_usec = rint((int) host_timeout % 1000) * 1000; /* set the socket send and receive timeout */ setsockopt(udp_socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)); setsockopt(udp_socket, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout)); } else { /* decrement the timeout value by the total time */ timeout.tv_sec = rint((host_timeout - total_time) / 1000); timeout.tv_usec = ((int) (host_timeout - total_time) % 1000) * 1000; /* set the socket send and receive timeout */ setsockopt(udp_socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)); setsockopt(udp_socket, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout)); } /* send packet to destination */ send(udp_socket, request, request_len, 0); /* wait for a response on the socket */ wait_more: return_code = select(FD_SETSIZE, &socket_fds, NULL, NULL, &timeout); /* record end time */ end_time = get_time_as_double(); /* caculate total time */ total_time = (end_time - begin_time) * one_thousand; /* check to see which socket talked */ if (return_code > 0) { if (FD_ISSET(udp_socket, &socket_fds)) { return_code = read(udp_socket, socket_reply, BUFSIZE); if (return_code == -1 && (errno == EHOSTUNREACH || errno == ECONNRESET || errno == ECONNREFUSED)) { if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] INFO: UDP Device Alive, Try Count:%i, Time:%.4f ms", host->id, retry_count+1, (total_time))); } else { SPINE_LOG_MEDIUM(("Device[%i] INFO: UDP Device Alive, Try Count:%i, Time:%.4f ms", host->id, retry_count+1, (total_time))); } snprintf(ping->ping_response, SMALL_BUFSIZE, "UDP: Device is Alive"); snprintf(ping->ping_status, 50, "%.5f", total_time); close(udp_socket); return HOST_UP; } } } else if (return_code == -1) { if (errno == EINTR) { /* interrupted, try again */ usleep(10000); goto wait_more; } else { snprintf(ping->ping_response, SMALL_BUFSIZE, "UDP: Device is Down"); snprintf(ping->ping_status, 50, "%.5f", total_time); close(udp_socket); return HOST_DOWN; } } else { /* timeout */ } if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] DEBUG: UDP Timeout, Try Count:%i, Time:%.4f ms", host->id, retry_count+1, (total_time))); } else { SPINE_LOG_DEBUG(("DEBUG: Device[%i] UDP Timeout, Try Count:%i, Time:%.4f ms", host->id, retry_count+1, (total_time))); } retry_count++; #ifndef SOLAR_THREAD usleep(1000); #endif } } else { snprintf(ping->ping_response, SMALL_BUFSIZE, "UDP: Destination hostname invalid"); snprintf(ping->ping_status, 50, "down"); close(udp_socket); return HOST_DOWN; } } else { snprintf(ping->ping_response, SMALL_BUFSIZE, "UDP: Destination address invalid or unable to create socket"); snprintf(ping->ping_status, 50, "down"); if (udp_socket != -1) close(udp_socket); return HOST_DOWN; } } /*! \fn int ping_tcp(host_t *host, ping_t *ping) * \brief ping a host using an TCP syn * \param host a pointer to the current host structure * \param ping a pointer to the current hosts ping structure * * This function pings a host using TCP. The TCP socket contains a marker * to the "Cacti" application so that firewall's can be configured to allow. * It will modify the ping structure to include the specifics of the ping results. * * \return HOST_UP if the host is reachable, HOST_DOWN otherwise. * */ int ping_tcp(host_t *host, ping_t *ping) { double begin_time, end_time, total_time; double host_timeout; double one_thousand = 1000.00; struct timeval timeout; int tcp_socket; struct sockaddr_in servername; int retry_count; int return_code; if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] DEBUG: Entering TCP Ping", host->id)); } else { SPINE_LOG_DEBUG(("DEBUG: Device[%i] Entering TCP Ping", host->id)); } /* convert the host timeout to a double precision number in seconds */ host_timeout = host->ping_timeout; /* initilize the socket */ tcp_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); /* initialize total time */ total_time = 0; /* initialize begin time */ begin_time = get_time_as_double(); /* hostname must be nonblank */ if ((strlen(host->hostname) != 0) && (tcp_socket != -1)) { /* initialize variables */ snprintf(ping->ping_status, 50, "down"); snprintf(ping->ping_response, SMALL_BUFSIZE, "default"); /* get address of hostname */ if (init_sockaddr(&servername, host->hostname, host->ping_port)) { /* first attempt a connect */ retry_count = 0; while (1) { /* establish timeout value */ timeout.tv_sec = rint(host_timeout / 1000); timeout.tv_usec = ((int) host_timeout % 1000) * 1000; /* set the socket send and receive timeout */ setsockopt(tcp_socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout)); setsockopt(tcp_socket, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout)); /* make the connection */ return_code = connect(tcp_socket, (struct sockaddr *) &servername, sizeof(servername)); /* record end time */ end_time = get_time_as_double(); /* caculate total time */ total_time = (end_time - begin_time) * one_thousand; if ((return_code == -1 && errno == ECONNREFUSED && host->ping_method == PING_TCP_CLOSED) || return_code == 0) { if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] INFO: TCP Device Alive, Try Count:%i, Time:%.4f ms", host->id, retry_count+1, (total_time))); } else { SPINE_LOG_MEDIUM(("Device[%i] INFO: TCP Device Alive, Try Count:%i, Time:%.4f ms", host->id, retry_count+1, (total_time))); } snprintf(ping->ping_response, SMALL_BUFSIZE, "TCP: Device is Alive"); snprintf(ping->ping_status, 50, "%.5f", total_time); close(tcp_socket); return HOST_UP; } else { #if defined(__CYGWIN__) snprintf(ping->ping_status, 50, "down"); snprintf(ping->ping_response, SMALL_BUFSIZE, "TCP: Cannot connect to host"); close(tcp_socket); return HOST_DOWN; #else if (retry_count > host->ping_retries) { snprintf(ping->ping_status, 50, "down"); snprintf(ping->ping_response, SMALL_BUFSIZE, "TCP: Cannot connect to host"); close(tcp_socket); return HOST_DOWN; } else { retry_count++; } #endif } } } else { snprintf(ping->ping_response, SMALL_BUFSIZE, "TCP: Destination hostname invalid"); snprintf(ping->ping_status, 50, "down"); close(tcp_socket); return HOST_DOWN; } } else { snprintf(ping->ping_response, SMALL_BUFSIZE, "TCP: Destination address invalid or unable to create socket"); snprintf(ping->ping_status, 50, "down"); if (tcp_socket != -1) close(tcp_socket); return HOST_DOWN; } } /*! \fn int get_address_type(host_t *host) * \brief determines using getaddrinfo the iptype and returns the iptype * * \return 1 - IPv4, 2 - IPv6, 0 - Unknown */ int get_address_type(host_t *host) { struct addrinfo hints, *res, *res_list; char addrstr[255]; void *ptr = NULL; int addr_found = FALSE; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; int error; if ((error = getaddrinfo(host->hostname, NULL, &hints, &res_list)) != 0) { SPINE_LOG(("WARNING: Unable to determine address info for %s (%s)", host->hostname, gai_strerror(error))); return SPINE_NONE; } for (res = res_list; res != NULL; res = res->ai_next) { inet_ntop(res->ai_family, res->ai_addr->sa_data, addrstr, 100); switch(res->ai_family) { case AF_INET: ptr = &((struct sockaddr_in *) res->ai_addr)->sin_addr; addr_found = TRUE; break; case AF_INET6: ptr = &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr; addr_found = TRUE; break; } inet_ntop(res->ai_family, ptr, addrstr, 100); SPINE_LOG_HIGH(("Device[%d] IPv%d address %s (%s)", host->id, res->ai_family == PF_INET6 ? 6:4, addrstr, res->ai_canonname)); if (res->ai_family != PF_INET6) { freeaddrinfo(res_list); return SPINE_IPV4; } } freeaddrinfo(res_list); if (addr_found) { return SPINE_IPV6; } else { return SPINE_NONE; } } /*! \fn int init_sockaddr(struct sockaddr_in *name, const char *hostname, unsigned short int port) * \brief converts a hostname to an internet address * * \return TRUE if successful, FALSE otherwise. * */ int init_sockaddr(struct sockaddr_in *name, const char *hostname, unsigned short int port) { struct addrinfo hints, *hostinfo; int rv, retry_count; // Initialize the hints structure memset(&hints, 0, sizeof hints); hints.ai_family = AF_INET; hints.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; retry_count = 0; rv = 0; while (TRUE) { rv = getaddrinfo(hostname, NULL, &hints, &hostinfo); if (rv == 0) { break; } else { switch (rv) { case EAI_AGAIN: if (retry_count < 3) { SPINE_LOG(("WARNING: EAGAIN received resolving after 3 retryies for host %s (%s)", hostname, gai_strerror(rv))); if (hostinfo != NULL) { freeaddrinfo(hostinfo); } retry_count++; usleep(50000); continue; } else { SPINE_LOG(("WARNING: Error resolving after 3 retryies for host %s (%s)", hostname, gai_strerror(rv))); if (hostinfo != NULL) { freeaddrinfo(hostinfo); } return FALSE; } break; case EAI_FAIL: SPINE_LOG(("WARNING: DNS Server reported permanent error for host %s (%s)", hostname, gai_strerror(rv))); if (hostinfo != NULL) { freeaddrinfo(hostinfo); } return FALSE; break; case EAI_MEMORY: SPINE_LOG(("WARNING: Out of memory trying to resolve host %s (%s)", hostname, gai_strerror(rv))); if (hostinfo != NULL) { freeaddrinfo(hostinfo); } return FALSE; break; default: SPINE_LOG(("WARNING: Unknown error while resolving host %s (%s)", hostname, gai_strerror(rv))); if (hostinfo != NULL) { freeaddrinfo(hostinfo); } return FALSE; break; } } } if (hostinfo == NULL) { SPINE_LOG(("WARNING: Unknown host %s", hostname)); return FALSE; } else { // Copy socket details name->sin_family = hostinfo->ai_family; name->sin_addr = ((struct sockaddr_in *)hostinfo->ai_addr)->sin_addr; name->sin_port = htons(port); // Free results var freeaddrinfo(hostinfo); return TRUE; } } /*! \fn name_t *get_namebyhost(char *hostname, name_t *name) * \brief splits the hostname into method, name and port * * \return name_t containing a trimmed hostname, port, and optional method * */ name_t *get_namebyhost(char *hostname, name_t *name) { if (name == NULL) { SPINE_LOG_DEBUG(("DEBUG: get_namebyhost(%s) - Allocating name_t", hostname)); if (!(name = (name_t *) malloc(sizeof(name_t)))) { die("ERROR: Fatal malloc error: ping.c get_namebyhost->name"); } memset(name, '\0', sizeof(name_t)); } int tokens = 0; char *stack = NULL; char *token = NULL; if (!(stack = (char *) malloc(strlen(hostname)+1))) { die("ERROR: Fatal malloc error: ping.c get_namebyhost->stack"); } memset(stack, '\0', strlen(hostname)+1); strncopy(stack, hostname, strlen(stack)); token = strtok(stack, ":"); if (token == NULL) { SPINE_LOG_DEBUG(("DEBUG: get_namebyhost(%s) - No delimiter, assume full hostname", hostname)); strncopy(name->hostname, hostname, SMALL_BUFSIZE); } while (token != NULL && tokens <= 3) { tokens++; SPINE_LOG_DEBUG(("DEBUG: get_namebyhost(%s) - Token #%i - %s", hostname, tokens, token)); if (tokens == 1) { if (strlen(token) && token[0] == '[') { SPINE_LOG_DEBUG(("DEBUG: get_namebyhost(%s) - Have TCPv6 method", hostname)); strncpy(name->hostname, hostname, sizeof(name->hostname)); break; } else if (strlen(token) == 3) { if (strncasecmp(token, "TCP", 3)) { SPINE_LOG_DEBUG(("DEBUG: get_namebyhost(%s) - Have TCPv4 method", hostname)); name->method = 1; } else if (strncasecmp(hostname, "UDP", 3)) { SPINE_LOG_DEBUG(("DEBUG: get_namebyhost(%s) - Have UDPv4 method", hostname)); name->method = 2; } else { SPINE_LOG_DEBUG(("DEBUG: get_namebyhost(%s) - No matching method for 3 chars: %s", hostname, token)); // assume we have had a method tokens++; } } else if (strlen(token) == 4) { if (strncasecmp(token, "TCP6", 3)) { SPINE_LOG_DEBUG(("DEBUG: get_namebyhost(%s) - Have TCPv6 method", hostname)); name->method = 3; } else if (strncasecmp(hostname, "UDP6", 3)) { SPINE_LOG_DEBUG(("DEBUG: get_namebyhost(%s) - Have UDPv6 method", hostname)); name->method = 4; } else { SPINE_LOG_DEBUG(("DEBUG: get_namebyhost(%s) - No matching method for 4 chars: %s", hostname, token)); // assume we have had a method tokens++; } } else { SPINE_LOG_DEBUG(("DEBUG: get_hostbyname(%s) - No matching method for %li chars: %s", hostname, strlen(token), token)); // assume we have had a method tokens++; } } if (tokens == 2) { SPINE_LOG_DEBUG(("DEBUG: get_namebyhost(%s) - Setting hostname: %s", hostname, token)); strncpy(name->hostname, token, sizeof(name->hostname)); name->hostname[strlen(token)] = '\0'; } if (tokens == 3 && strlen(token)) { SPINE_LOG_DEBUG(("DEBUG: get_namebyhost(%s) - Setting port: %s", hostname, token)); name->port = atoi(token); } if (tokens > 3) { SPINE_LOG_DEBUG(("DEBUG: get_namebyhost(%s) - Unexpected token: %i", hostname, tokens)); } token = strtok(NULL, ":"); } if (stack != NULL) { free(stack); stack = NULL; } return name; } /*! \fn unsigned short int get_checksum(void* buf, int len) * \brief calculates a 16bit checksum of a packet buffer * \param buf the input buffer to calculate the checksum of * \param len the size of the input buffer * * \return 16bit checksum of an input buffer of size len. * */ unsigned short int get_checksum(void* buf, int len) { int nleft = len; int32_t sum = 0; unsigned short int answer; unsigned short int* w = (unsigned short int*)buf; unsigned short int odd_byte = 0; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { *(unsigned char*)(&odd_byte) = *(unsigned char*)w; sum += odd_byte; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); answer = ~sum; /* truncate to 16 bits */ return answer; } /*! \fn void update_host_status(int status, host_t *host, ping_t *ping, int availability_method) * \brief update the host table in Cacti with the result of the ping of the host. * \param status the current poll status of the host, either HOST_UP, or HOST_DOWN * \param host a pointer to the current host structure * \param ping a pointer to the current hosts ping structure * \param availability_method the method that was used to poll the host * * This function will determine if the host is UP, DOWN, or RECOVERING based upon * the ping result and it's current status. It will update the Cacti database * with the calculated status. * */ void update_host_status(int status, host_t *host, ping_t *ping, int availability_method) { int issue_log_message = FALSE; double ping_time; double hundred_percent = 100.00; char current_date[40]; snprintf(current_date, 40, "%lu", time(NULL)); /* host is down */ if (status == HOST_DOWN) { /* update total polls, failed polls and availability */ host->failed_polls = host->failed_polls + 1; host->total_polls = host->total_polls + 1; host->availability = hundred_percent * (host->total_polls - host->failed_polls) / host->total_polls; /*determine the error message to display */ switch (availability_method) { case AVAIL_SNMP_OR_PING: case AVAIL_SNMP_AND_PING: if (strlen(host->snmp_community) == 0 && host->snmp_version < 3) { snprintf(host->status_last_error, BUFSIZE, "%s", ping->ping_response); } else { snprintf(host->status_last_error, BUFSIZE, "%s, %s", ping->snmp_response, ping->ping_response); } break; case AVAIL_SNMP: if (strlen(host->snmp_community) == 0 && host->snmp_version < 3) { snprintf(host->status_last_error, BUFSIZE, "%s", "Device does not require SNMP"); } else { snprintf(host->status_last_error, BUFSIZE, "%s", ping->snmp_response); } break; default: snprintf(host->status_last_error, BUFSIZE, "%s", ping->ping_response); } /* determine if to send an alert and update remainder of statistics */ if (host->status == HOST_UP) { /* increment the event failure count */ host->status_event_count++; /* if it's time to issue an error message, indicate so */ if (host->status_event_count >= set.ping_failure_count) { /* host is now down, flag it that way */ host->status = HOST_DOWN; issue_log_message = TRUE; /* update the failure date only if the failure count is 1 */ if (set.ping_failure_count == 1) { snprintf(host->status_fail_date, 40, "%s", current_date); } } else { /* host down for the first time, set event date */ if (host->status_event_count == 1) { snprintf(host->status_fail_date, 40, "%s", current_date); } } } else if (host->status == HOST_RECOVERING) { /* host is recovering, put back in failed state */ host->status_event_count = 1; host->status = HOST_DOWN; } else if (host->status == HOST_UNKNOWN) { /* host was unknown and now is down */ host->status = HOST_DOWN; host->status_event_count = 0; } else { host->status_event_count++; } } else { /* host is up!! */ /* update total polls and availability */ host->total_polls = host->total_polls + 1; host->availability = hundred_percent * (host->total_polls - host->failed_polls) / host->total_polls; /* determine the ping statistic to set and do so */ if (availability_method == AVAIL_SNMP_AND_PING) { if (strlen(host->snmp_community) == 0 && host->snmp_version < 3) { ping_time = atof(ping->ping_status); } else { /* calculate the average of the two times */ ping_time = (atof(ping->snmp_status) + atof(ping->ping_status)) / 2; } } else if (availability_method == AVAIL_SNMP) { if (strlen(host->snmp_community) == 0 && host->snmp_version < 3) { ping_time = 0.000; } else { ping_time = atof(ping->snmp_status); } } else if (availability_method == AVAIL_NONE) { ping_time = 0.000; } else { ping_time = atof(ping->ping_status); } /* update times as required */ host->cur_time = ping_time; /* maximum time */ if (ping_time > host->max_time) host->max_time = ping_time; /* minimum time */ if (ping_time < host->min_time) host->min_time = ping_time; /* average time */ host->avg_time = (((host->total_polls-1-host->failed_polls) * host->avg_time) + ping_time) / (host->total_polls-host->failed_polls); /* the host was down, now it's recovering */ if ((host->status == HOST_DOWN) || (host->status == HOST_RECOVERING)) { /* just up, change to recovering */ if (host->status == HOST_DOWN) { host->status = HOST_RECOVERING; host->status_event_count = 1; } else { host->status_event_count++; } /* if it's time to issue a recovery message, indicate so */ if (host->status_event_count >= set.ping_recovery_count) { /* host is up, flag it that way */ host->status = HOST_UP; issue_log_message = TRUE; /* update the recovery date only if the recovery count is 1 */ if (set.ping_recovery_count == 1) { snprintf(host->status_rec_date, 40, "%s", current_date); } /* reset the event counter */ host->status_event_count = 0; } else { /* host recovering for the first time, set event date */ if (host->status_event_count == 1) { snprintf(host->status_rec_date, 40, "%s", current_date); } } } else if (host->status_event_count > 0) { /* host was unknown and now is up */ host->status = HOST_UP; host->status_event_count = 0; } else { /* host was unknown and now is up */ host->status = HOST_UP; host->status_event_count = 0; } } /* if the user wants a flood of information then flood them */ if (set.log_level >= POLLER_VERBOSITY_HIGH) { if ((host->status == HOST_UP) || (host->status == HOST_RECOVERING)) { /* log ping result if we are to use a ping for reachability testing */ if (availability_method == AVAIL_SNMP_AND_PING) { if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] PING Result: %s", host->id, ping->ping_response)); SPINE_LOG(("Device[%i] SNMP Result: %s", host->id, ping->snmp_response)); } else { SPINE_LOG_HIGH(("Device[%i] PING Result: %s", host->id, ping->ping_response)); SPINE_LOG_HIGH(("Device[%i] SNMP Result: %s", host->id, ping->snmp_response)); } } else if (availability_method == AVAIL_SNMP_OR_PING) { if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] PING Result: %s", host->id, ping->ping_response)); SPINE_LOG(("Device[%i] SNMP Result: %s", host->id, ping->snmp_response)); } else { SPINE_LOG_HIGH(("Device[%i] PING Result: %s", host->id, ping->ping_response)); SPINE_LOG_HIGH(("Device[%i] SNMP Result: %s", host->id, ping->snmp_response)); } } else if (availability_method == AVAIL_SNMP) { if ((strlen(host->snmp_community) == 0) && (host->snmp_version < 3)) { if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] SNMP Result: Device does not require SNMP", host->id)); } else { SPINE_LOG_HIGH(("Device[%i] SNMP Result: Device does not require SNMP", host->id)); } } else { if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] SNMP Result: %s", host->id, ping->snmp_response)); } else { SPINE_LOG_HIGH(("Device[%i] SNMP Result: %s", host->id, ping->snmp_response)); } } } else if (availability_method == AVAIL_NONE) { if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] No Device Availability Method Selected", host->id)); } else { SPINE_LOG_HIGH(("Device[%i] No Device Availability Method Selected", host->id)); } } else { if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] PING: Result %s", host->id, ping->ping_response)); } else { SPINE_LOG_HIGH(("Device[%i] PING: Result %s", host->id, ping->ping_response)); } } } else { if (availability_method == AVAIL_SNMP_AND_PING) { if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] PING Result: %s", host->id, ping->ping_response)); SPINE_LOG(("Device[%i] SNMP Result: %s", host->id, ping->snmp_response)); } else { SPINE_LOG_HIGH(("Device[%i] PING Result: %s", host->id, ping->ping_response)); SPINE_LOG_HIGH(("Device[%i] SNMP Result: %s", host->id, ping->snmp_response)); } } else if (availability_method == AVAIL_SNMP) { if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] SNMP Result: %s", host->id, ping->snmp_response)); } else { SPINE_LOG_HIGH(("Device[%i] SNMP Result: %s", host->id, ping->snmp_response)); } } else if (availability_method == AVAIL_NONE) { if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] No Device Availability Method Selected", host->id)); } else { SPINE_LOG_HIGH(("Device[%i] No Device Availability Method Selected", host->id)); } } else { if (is_debug_device(host->id)) { SPINE_LOG(("Device[%i] PING Result: %s", host->id, ping->ping_response)); } else { SPINE_LOG_HIGH(("Device[%i] PING Result: %s", host->id, ping->ping_response)); } } } } /* if there is supposed to be an event generated, do it */ if (issue_log_message) { if (host->status == HOST_DOWN) { SPINE_LOG(("Device[%i] Hostname[%s] ERROR: HOST EVENT: Device is DOWN Message: %s", host->id, host->hostname, host->status_last_error)); } else { SPINE_LOG(("Device[%i] Hostname[%s] NOTICE: HOST EVENT: Device Returned from DOWN State", host->id, host->hostname)); } } } cacti-spine-1.2.30/keywords.c0000664000175000017500000001550214770060157015045 0ustar markvmarkv/* ex: set tabstop=4 shiftwidth=4 autoindent: +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 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, implimentation details) | +-------------------------------------------------------------------------+ | - Cacti - http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ /*! * * This module provides keyword-lookup support for various spine * objects. The idea is that we can do a two-way translation: given * a token, return a printable name for it, and to take a word from * the user and return the numeric internal value. * * The center of the module is the table of keywords which map in * both directions word<-->value. Lookups are case insensitive, and * both direction * */ #include "common.h" #include "spine.h" struct keyword { const char *word; int value; }; /*! Log Level Structure * * Structure that helps map either an integer value to a text logging level or * vice versa. * */ static const struct keyword log_level[] = { { "NONE", POLLER_VERBOSITY_NONE }, { "LOW", POLLER_VERBOSITY_LOW }, { "MEDIUM", POLLER_VERBOSITY_MEDIUM }, { "HIGH", POLLER_VERBOSITY_HIGH }, { "DEBUG", POLLER_VERBOSITY_DEBUG }, { 0, 0 } /* ENDMARKER */ }; /*! Log Destination Structure * * Structure that helps map either an integer value to a text logging destination * or vice versa. * */ static const struct keyword logdest[] = { { "FILE", LOGDEST_FILE }, { "SYSLOG", LOGDEST_SYSLOG }, { "BOTH", LOGDEST_BOTH }, { "STDOUT", LOGDEST_STDOUT }, { 0, 0 } /* ENDMARKER */ }; /*! Poller Action Structure * * Structure that helps map either an integer value to a text poller action * or vice versa. * */ static const struct keyword actions[] = { { "SNMP", POLLER_ACTION_SNMP }, { "SCRIPT", POLLER_ACTION_SCRIPT }, { "PHPSCRIPT", POLLER_ACTION_PHP_SCRIPT_SERVER }, { "SNMP_CT", POLLER_ACTION_SNMP_COUNT }, { "SCRIPT_CT", POLLER_ACTION_SCRIPT_COUNT }, { "PHPSCRIPT_CT", POLLER_ACTION_PHP_SCRIPT_SERVER_COUNT }, { 0, 0 } /* ENDMARKER */ }; /*! \fn find_keyword_by_word(const struct keyword *tbl, const char *word, int dflt) * \brief takes a generic word and returns either TRUE or FALSE * \param tbl the table that contains the translation from text to boolean * \param word the word to compare against the table for the result * \param dflt the default value to be returned if the string can not be found * * Given a table of keywords and a user's word, look that word up in the * table and return the value associted with it. If the word is not found, * return the user-provide default value. * * The default-value parameter can be used for either the actual default * value of the parameter being searched for (say, LOGDEST_BOTH), or * a didn't-find-it value (say, -1) which the caller can key off of. * * NOTE: if the given word is all digits, it's parsed as a number and * returned numerically. * * \return TRUE, FALSE, or dflt depending on results of search * */ static int find_keyword_by_word(const struct keyword *tbl, const char *word, int dflt) { assert(tbl != 0); assert(word != 0); if (all_digits(word)) { return atoi(word); } for (; tbl->word; tbl++) { if (STRIMATCH(word, tbl->word)) { return tbl->value; } } return dflt; } /*! \fn static const char *find_keyword_by_value(const struct keyword *tbl, int value, const char *dflt) * \brief searches a table for text string based upon a numeric input value * \param tbl the table that contains the translation from text to boolean * \param word the word to compare against the table for the result * \param dflt the default value to be returned if the string can not be found * * Given a keyword table and a numeric value, find the printable word * associated with it. The *first* value found is returned (in case more * than one word maps to the same value), and if it's not found, the * user's default value is returned. * * The dflt value is allowed to be NULL. * * \return a string pointer to that matches the search criteria, or dflt * */ static const char *find_keyword_by_value(const struct keyword *tbl, int value, const char *dflt) { assert(tbl != 0); for (; tbl->word; tbl++ ) { if (tbl->value == value) { return tbl->word; } } return dflt; } const char *printable_log_level(int token) { return find_keyword_by_value(log_level, token, "-unknown-"); } int parse_log_level(const char *word, int dflt) { return find_keyword_by_word(log_level, word, dflt); } const char *printable_logdest(int token) { return find_keyword_by_value(logdest, token, "-unknown-"); } int parse_logdest(const char *word, int dflt) { return find_keyword_by_word(logdest, word, dflt); } const char *printable_action(int token) { return find_keyword_by_value(actions, token, "-unknown-"); } int parse_action(const char *word, int dflt) { return find_keyword_by_word(actions, word, dflt); } cacti-spine-1.2.30/nft_popen.h0000664000175000017500000000773314770060157015202 0ustar markvmarkv/* +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 The Cacti Group | | | | 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. | +-------------------------------------------------------------------------+ | Cacti: The Complete RRDtool-based Graphing Solution | +-------------------------------------------------------------------------+ | This code is designed, written, and maintained by the Cacti Group. See | | about.php and/or the AUTHORS file for specific developer information. | +-------------------------------------------------------------------------+ | http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ /****************************************************************************** ex: set tabstop=4 shiftwidth=4 autoindent: * * (C) Copyright Xenadyne, Inc. 2002 All rights reserved. * * Permission to use, copy, modify and distribute this software for * any purpose and without fee is hereby granted, provided that the * above copyright notice appears in all copies. * * XENADYNE INC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL XENADYNE BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM THE * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * File: nft_popen.h * * PURPOSE * * Thread-safe substitute for popen() that doesn't use stdio streams. * ****************************************************************************** */ /*! * The nft_popen() function forks a command in a child process, and returns * a pipe that is connected to the child's standard input and output. It is * like the standard popen() call, except that it returns the file descriptor, * instead of a stdio stream created by fdopen(). The file descriptor can be * used with select() or poll(), or the caller can use fdopen() if a stdio * FILE* is preferable. * * The mode argument is defined as in standard popen(). * * On success, returns a file descriptor, or -1 on error. * On failure, returns -1, with errno set to one of: * EINVAL The mode argument is incorrect. * EMFILE pipe() failed. * ENFILE pipe() failed. * ENOMEM malloc() failed. * EAGAIN fork() failed. */ extern int nft_popen(const char * command, const char * mode); /*! * nft_pchild * * Get the pid of the child process for an fd created by ntf_popen(). * * On success, the pid of the child process is returned. * On failure, nft_pchild() returns -1, with errno set to: * * EBADF The fd is not an active nft_popen() file descriptor. */ extern int nft_pchild(int fd); /*! * nft_pclose * * Close the pipe and wait for the status of the child process. * * On success, the exit status of the child process is returned. * On failure, nft_pclose() returns -1, with errno set to: * * EBADF The fd is not an active popen() file descriptor. * ECHILD waitpid() failed. */ extern int nft_pclose(int fd); cacti-spine-1.2.30/spine.conf.dist0000664000175000017500000000700114770060157015754 0ustar markvmarkv# +-------------------------------------------------------------------------+ # | Copyright (C) 2004-2024 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 General Public License for more details. | # +-------------------------------------------------------------------------+ # | 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, implimentation details) | # +-------------------------------------------------------------------------+ # | Settings | # +-------------------------------------------------------------------------+ # | DB_Host 'localhost' or socket file for UNIX/Linux | # | IP Address for Windows | # | DB_Database Database name, typically 'cacti' | # | DB_Port The database port to use | # | DB_User The user to access the database, typically 'cactiuser' | # | DB_Pass The password for the Cacti user | # | SNMP_Clientaddr Bind SNMP to a specific address for sites that use | # | higher security levels | # | Cacti_Log Optional path to the Cacti log file | # +-------------------------------------------------------------------------+ # | Settings for Remote Polling | # +-------------------------------------------------------------------------+ # | RDB_Host The remote database hostname. | # | RDB_Database The remote database name, typically 'cacti' | # | RDB_Port The remote database port to use | # | RDB_User The remote database user, typically 'cactiuser' | # | RDB_Pass The remote database password. | # +-------------------------------------------------------------------------+ DB_Host localhost DB_Database cacti DB_User cactiuser DB_Pass cactiuser DB_Port 3306 #DB_UseSSL 0 #DB_SSL_Key #DB_SSL_Cert #DB_SSL_CA #Cacti_Log /var/www/html/cacti/log/cacti.log #RDB_Host localhost #RDB_Database cacti #RDB_User cactiuser #RDB_Pass cactiuser #RDB_Port 3306 #RDB_UseSSL 0 #RDB_SSL_Key #RDB_SSL_Cert #RDB_SSL_CA cacti-spine-1.2.30/spine.c0000664000175000017500000011006114770060157014310 0ustar markvmarkv/* ex: set tabstop=4 shiftwidth=4 autoindent: +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 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, implimentation details) | +-------------------------------------------------------------------------+ | - Cacti - http://www.cacti.net/ | +-------------------------------------------------------------------------+ * * COMMAND-LINE PARAMETERS * * -h | --help * -v | --version * * Show a brief help listing, then exit. * * -C | --conf=F * * Provide the name of the Spine configuration file, which contains * the parameters for connecting to the database. In the absence of * this, it looks [WHERE?] * * -f | --first=ID * * Start polling with device (else starts at the beginning) * * -l | --last=ID * * Stop polling after device (else ends with the last one) * * -m | --mibs * * Collect all system mibs this pass * * -N | --mode=online|offline|recovery * * For remote pollers, the polling mode. The default is 'online' * * -H | --hostlist="hostid1,hostid2,hostid3,...,hostidn" * * Override the expected first host, last host behavior with a list of hostids. * * -O | --option=setting:value * * Override a DB-provided value from the settings table in the DB. * * -C | -conf=FILE * * Specify the location of the Spine configuration file. * * -R | --readonly * * This processing is readonly with respect to the database: it's * meant only for developer testing. * * -S | --stdout * * All logging goes to the standard output * * -V | --verbosity=V * * Set the debug logging verbosity to . Can be 1..5 or * NONE/LOW/MEDIUM/HIGH/DEBUG (case insensitive). * * The First/Last device IDs are all relative to the "hosts" table in the * Cacti database, and this mechanism allows us to split up the polling * duties across multiple "spine" instances: each one gets a subset of * the polling range. * * For compatibility with poller.php, we also accept the first and last * device IDs as standalone parameters on the command line. */ #include "common.h" #include "spine.h" /* Global Variables */ int entries = 0; int num_hosts = 0; sem_t available_threads; sem_t available_scripts; double start_time; double total_time; config_t set; php_t *php_processes = 0; char config_paths[CONFIG_PATHS][BUFSIZE]; int *debug_devices; pool_t *db_pool_local; pool_t *db_pool_remote; poller_thread_t** details = NULL; static char *getarg(char *opt, char ***pargv); static void display_help(int only_version); void poller_push_data_to_main(); #ifdef HAVE_LCAP /* This patch is adapted (copied) patch for ntpd from Jarno Huuskonen and * Pekka Savola that was adapted (copied) from a patch by Chris Wings to drop * root for xntpd. */ void drop_root(uid_t server_uid, gid_t server_gid) { cap_t caps; if (prctl(PR_SET_KEEPCAPS, 1)) { SPINE_LOG_HIGH(("prctl(PR_SET_KEEPCAPS, 1) failed")); exit(1); } if (setgroups(0, NULL) == -1) { SPINE_LOG_HIGH(("setgroups failed.")); exit(1); } if (setegid(server_gid) == -1 || seteuid(server_uid) == -1) { SPINE_LOG_HIGH(("setegid/seteuid to uid=%d/gid=%d failed.", server_uid, server_gid)); exit(1); } caps = cap_from_text("cap_net_raw=eip"); if (caps == NULL) { SPINE_LOG_HIGH(("cap_from_text failed.")); exit(1); } if (cap_set_proc(caps) == -1) { SPINE_LOG_HIGH(("cap_set_proc failed.")); exit(1); } /* Try to free the memory from cap_from_text */ cap_free( caps ); if ( setregid(server_gid, server_gid) == -1 || setreuid(server_uid, server_uid) == -1 ) { SPINE_LOG_HIGH(("setregid/setreuid to uid=%d/gid=%d failed.", server_uid, server_gid)); exit(1); } SPINE_LOG_LOW(("running as uid(%d)/gid(%d) euid(%d)/egid(%d) with cap_net_raw=eip.", getuid(), getgid(), geteuid(), getegid())); } #endif /* HAVE_LCAP */ /*! \fn main(int argc, char *argv[]) * \brief The Spine program entry point * \param argc The number of arguments passed to the function plus one (+1) * \param argv An array of the command line arguments * * The Spine entry point. This function performs the following tasks. * 1) Processes command line input parameters * 2) Processes the Spine configuration file to obtain database access information * 3) Process runtime parameters from the settings table * 4) Initialize the runtime threads and mutexes for the threaded environment * 5) Initialize Net-SNMP, MySQL, and the PHP Script Server (if required) * 6) Spawns X threads in order to process hosts * 7) Loop until either all hosts have been processed or until the poller runtime * has been exceeded * 8) Close database and free variables * 9) Log poller process statistics if required * 10) Exit * * Note: Command line runtime parameters override any database settings. * * \return 0 if SUCCESS, or -1 if FAILED * */ int main(int argc, char *argv[]) { char *conf_file = NULL; double begin_time, end_time, cur_time; int num_rows = 0; int device_counter = 0; int valid_conf_file = FALSE; char querybuf[MEGA_BUFSIZE], *qp = querybuf; char *host_time = NULL; double host_time_double = 0; int items_per_thread = 0; int device_threads; sem_t thread_init_sem; int a_threads_value; struct timespec until_spec; start_time = get_time_as_double(); total_time = 0; #ifdef HAVE_LCAP if (geteuid() == 0) { drop_root(getuid(), getgid()); } #endif /* HAVE_LCAP */ pthread_t* threads = NULL; poller_thread_t* poller_details = NULL; pthread_attr_t attr; int* ids = NULL; int mode = REMOTE; MYSQL mysql; MYSQL mysqlr; MYSQL_RES *result = NULL; MYSQL_RES *tresult = NULL; MYSQL_ROW mysql_row; int canexit = FALSE; int host_id = 0; int i; int thread_status = 0; int total_items = 0; int change_host = TRUE; int current_thread; int threads_final = 0; int threads_missing = -1; int threads_count; /* we must initilize snmp in the main thread */ struct snmp_session session; UNUSED_PARAMETER(argc); /* we operate strictly with argv */ /* install the spine signal handler */ install_spine_signal_handler(); /* establish php processes and initialize space */ php_processes = (php_t*) calloc(MAX_PHP_SERVERS, sizeof(php_t)); for (i = 0; i < MAX_PHP_SERVERS; i++) { php_processes[i].php_state = PHP_BUSY; } /* create the array of debug devices */ debug_devices = calloc(100, sizeof(int)); /* initialize icmp_avail */ set.icmp_avail = TRUE; /* initialize number of threads */ set.threads = 1; set.threads_set = FALSE; /* detect and compensate for stdin/stderr ttys */ if (!isatty(fileno(stdout))) { set.stdout_notty = TRUE; } else { set.stdout_notty = FALSE; } if (!isatty(fileno(stderr))) { set.stderr_notty = TRUE; } else { set.stderr_notty = FALSE; } /* set start time for cacti */ begin_time = get_time_as_double(); /* set default verbosity */ set.log_level = POLLER_VERBOSITY_LOW; /* set default log separator */ set.log_datetime_separator = GDC_DEFAULT; /* set default log format */ set.log_datetime_format = GD_DEFAULT; /* set the default exit code */ set.exit_code = 0; set.exit_size = 0; /* get static defaults for system */ config_defaults(); /*! ---------------------------------------------------------------- * PROCESS COMMAND LINE * * Run through the list of ARGV words looking for parameters we * know about. Most have two flavors (-C + --conf), and many * themselves take a parameter. * * These parameters can be structured in two ways: * * --conf=FILE both parts in one argv[] string * --conf FILE two separate argv[] strings * * We set "arg" to point to "--conf", and "opt" to point to FILE. * The helper routine * * In each loop we set "arg" to next argv[] string, then look * to see if it has an equal sign. If so, we split it in half * and point to the option separately. * * NOTE: most direction to the program is given with dash-type * parameters, but we also allow standalone numeric device IDs * in "first last" format: this is how poller.php calls this * program. */ /* initialize some global variables */ set.poller_id = 1; set.start_host_id = -1; set.end_host_id = -1; set.host_id_list[0] = '\0'; set.php_initialized = FALSE; set.logfile_processed = FALSE; set.parent_fork = SPINE_PARENT; set.mode = REMOTE_ONLINE; set.has_device_0 = FALSE; for (argv++; *argv; argv++) { char *arg = *argv; char *opt = strchr(arg, '='); /* pick off the =VALUE part */ if (opt) *opt++ = '\0'; if (STRMATCH(arg, "-f") || STRMATCH(arg, "--first")) { if (HOSTID_DEFINED(set.start_host_id)) { die("ERROR: %s can only be used once", arg); } set.start_host_id = atoi(opt = getarg(opt, &argv)); if (!HOSTID_DEFINED(set.start_host_id)) { die("ERROR: '%s=%s' is invalid first-host ID", arg, opt); } } else if (STRMATCH(arg, "-l") || STRIMATCH(arg, "--last")) { if (HOSTID_DEFINED(set.end_host_id)) { die("ERROR: %s can only be used once", arg); } set.end_host_id = atoi(opt = getarg(opt, &argv)); if (!HOSTID_DEFINED(set.end_host_id)) { die("ERROR: '%s=%s' is invalid last-host ID", arg, opt); } } else if (STRMATCH(arg, "-p") || STRIMATCH(arg, "--poller")) { set.poller_id = atoi(getarg(opt, &argv)); } else if (STRMATCH(arg, "-t") || STRIMATCH(arg, "--threads")) { set.threads = atoi(getarg(opt, &argv)); set.threads_set = TRUE; } else if (STRMATCH(arg, "-P") || STRIMATCH(arg, "--pingonly")) { set.ping_only = TRUE; } else if (STRMATCH(arg, "-N") || STRIMATCH(arg, "--mode")) { if (STRIMATCH(getarg(opt, &argv), "online")) { set.mode = REMOTE_ONLINE; } else if (STRIMATCH(getarg(opt, &argv), "offline")) { set.mode = REMOTE_OFFLINE; } else if (STRIMATCH(getarg(opt, &argv), "recovery")) { set.mode = REMOTE_RECOVERY; } else { die("ERROR: invalid polling mode '%s' specified", opt); } } else if (STRMATCH(arg, "-H") || STRIMATCH(arg, "--hostlist")) { snprintf(set.host_id_list, BIG_BUFSIZE, "%s", getarg(opt, &argv)); } else if (STRMATCH(arg, "-M") || STRMATCH(arg, "--mibs")) { set.mibs = 1; } else if (STRMATCH(arg, "-h") || STRMATCH(arg, "--help")) { display_help(FALSE); exit(EXIT_SUCCESS); } else if (STRMATCH(arg, "-v") || STRMATCH(arg, "--version")) { display_help(TRUE); exit(EXIT_SUCCESS); } else if (STRMATCH(arg, "-O") || STRIMATCH(arg, "--option")) { char *setting = getarg(opt, &argv); char *value = strchr(setting, ':'); if (*value) { *value++ = '\0'; } else { die("ERROR: -O requires setting:value"); } set_option(setting, value); } else if (STRMATCH(arg, "-R") || STRMATCH(arg, "--readonly") || STRMATCH(arg, "--read-only")) { set.SQL_readonly = TRUE; } else if (STRMATCH(arg, "-C") || STRMATCH(arg, "--conf")) { conf_file = strdup(getarg(opt, &argv)); } else if (STRMATCH(arg, "-S") || STRMATCH(arg, "--stdout")) { set_option("log_destination", "STDOUT"); } else if (STRMATCH(arg, "-L") || STRMATCH(arg, "--log")) { set_option("log_destination", getarg(opt, &argv)); } else if (STRMATCH(arg, "-V") || STRMATCH(arg, "--verbosity")) { set_option("log_verbosity", getarg(opt, &argv)); } else if (!HOSTID_DEFINED(set.start_host_id) && all_digits(arg)) { set.start_host_id = atoi(arg); } else if (!HOSTID_DEFINED(set.end_host_id) && all_digits(arg)) { set.end_host_id = atoi(arg); } else { die("ERROR: %s is an unknown command-line parameter", arg); } } if (set.ping_only) { set.mibs = 0; } /* we attempt to support scripts better in cygwin */ #if defined(__CYGWIN__) setenv("CYGWIN", "nodosfilewarning", 1); if (file_exists("./sh.exe")) { set.cygwinshloc = 0; if (set.log_level == POLLER_VERBOSITY_DEBUG) { printf("The Shell Command Exists in the current directory\n"); } } else { set.cygwinshloc = 1; if (set.log_level == POLLER_VERBOSITY_DEBUG) { printf("The Shell Command Exists in the /bin directory\n"); } } #endif /* we require either both the first and last hosts, or niether host */ if ((HOSTID_DEFINED(set.start_host_id) != HOSTID_DEFINED(set.end_host_id)) && (!strlen(set.host_id_list))) { die("ERROR: must provide both -f/-l, a hostlist (-H/--hostlist), or neither"); } if (set.start_host_id > set.end_host_id) { die("ERROR: Invalid row spec; first host_id must be less than the second"); } /* read configuration file to establish local environment */ if (conf_file) { if ((read_spine_config(conf_file)) < 0) { die("ERROR: Could not read config file: %s", conf_file); } else { valid_conf_file = TRUE; } } else { if (!(conf_file = calloc(CONFIG_PATHS, DBL_BUFSIZE))) { die("ERROR: Fatal malloc error: spine.c conf_file!"); } for (i=0; i= 0) { valid_conf_file = TRUE; break; } if (i == CONFIG_PATHS-1) { snprintf(conf_file, DBL_BUFSIZE, "%s%s", config_paths[0], DEFAULT_CONF_FILE); } } } if (valid_conf_file) { /* read settings table from the database to further establish environment */ read_config_options(); } else { die("FATAL: Unable to read configuration file!"); } /* set the poller interval for those who use less than 5 minute intervals */ if (set.poller_interval == 0) { set.poller_interval = 300; } /* tokenize the debug devices */ if (strlen(set.selective_device_debug)) { SPINE_LOG_DEBUG(("DEBUG: Selective Debug Devices %s", set.selective_device_debug)); int i = 0; char *token = strtok(set.selective_device_debug, ","); while(token) { debug_devices[i] = atoi(token); debug_devices[i+1] = '\0'; token = strtok(NULL, ","); i++; } } else { debug_devices[0] = '\0'; } /* connect for main loop */ db_connect(LOCAL, &mysql); /* setup local connection pool for hosts */ db_pool_local = (pool_t *) calloc(set.threads, sizeof(pool_t)); db_create_connection_pool(LOCAL); if (set.poller_id > 1 && set.mode == REMOTE_ONLINE) { db_connect(REMOTE, &mysqlr); mode = REMOTE; /* setup remote connection pool for hosts */ db_pool_remote = (pool_t *) calloc(set.threads, sizeof(pool_t)); db_create_connection_pool(REMOTE); } else { mode = LOCAL; } /* check for device 0 items */ result = db_query(&mysql, LOCAL, "SELECT * FROM (SELECT COUNT(*) AS items FROM poller_item WHERE host_id = 0 AND poller_id = 1) AS rs WHERE rs.items > 0"); if (mysql_num_rows(result)) { set.has_device_0 = TRUE; } db_free_result(result); /* Since MySQL 5.7 the sql_mode defaults are too strict for cacti */ db_insert(&mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'NO_ZERO_DATE', ''))"); db_insert(&mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY', ''))"); if (set.log_level == POLLER_VERBOSITY_DEBUG) { SPINE_LOG_DEBUG(("DEBUG: Version %s starting", VERSION)); if (set.poller_id > 1) { if (mode == REMOTE) { SPINE_LOG_DEBUG(("DEBUG: Sending entries to remote database in 'online' mode")); } else { SPINE_LOG_DEBUG(("DEBUG: Sending entries to local database in 'offline', or 'recovery' mode")); } } } else { if (!set.stdout_notty) { printf("Version %s starting\n", VERSION); if (set.poller_id > 1) { if (mode == REMOTE) { printf("Sending entries to remote database in 'online' mode\n"); } else { printf("Sending entries to local database in 'offline', or 'recovery' mode\n"); } } } } if (set.has_device_0) { SPINE_LOG_MEDIUM(("Device 0 Poller Items found. Ensure that these entries are accurate")); } else { SPINE_LOG_MEDIUM(("No Device 0 Poller Items found.")); } /* see if mysql is thread safe */ if (mysql_thread_safe()) { if (set.log_level == POLLER_VERBOSITY_DEBUG) { SPINE_LOG(("DEBUG: MySQL is Thread Safe!")); } } else { SPINE_LOG(("WARNING: MySQL is NOT Thread Safe!")); } /* test for asroot permissions for ICMP */ checkAsRoot(); /* initialize SNMP */ SPINE_LOG_DEBUG(("DEBUG: Initializing Net-SNMP API")); snmp_spine_init(); /* initialize PHP if required */ SPINE_LOG_DEBUG(("DEBUG: Initializing PHP Script Server(s)")); /* tell spine that it is parent, and set the poller id */ set.parent_fork = SPINE_PARENT; /* initialize the script server */ if (set.php_required && !set.ping_only) { php_init(PHP_INIT); set.php_initialized = TRUE; set.php_current_server = 0; } /* determine if the poller_id field exists in the host table */ result = db_query(&mysql, LOCAL, "SHOW COLUMNS FROM host LIKE 'poller_id'"); if (mysql_num_rows(result)) { set.poller_id_exists = TRUE; } else { set.poller_id_exists = FALSE; if (set.poller_id > 0) { SPINE_LOG(("WARNING: PollerID > 0, but 'host' table does NOT contain the poller_id column!!")); } } db_free_result(result); /* obtain the list of hosts to poll */ qp += sprintf(qp, "SELECT SQL_NO_CACHE id, device_threads, picount, picount/device_threads AS tppi FROM host LEFT JOIN (SELECT host_id, COUNT(*) AS picount FROM poller_item GROUP BY host_id) AS pi ON host.id = pi.host_id"); qp += sprintf(qp, " WHERE disabled = ''"); if (!strlen(set.host_id_list)) { qp += append_hostrange(qp, "id"); /* AND id BETWEEN a AND b */ } else { qp += sprintf(qp, " AND id IN(%s)", set.host_id_list); } if (set.poller_id_exists) { qp += sprintf(qp, " AND host.poller_id = %i", set.poller_id); } qp += sprintf(qp, " ORDER BY picount DESC"); result = db_query(&mysql, LOCAL, querybuf); if (set.poller_id == 1) { if (set.has_device_0) { num_rows = mysql_num_rows(result) + 1; /* pollerid 1 takes care of non host based data sources */ } else { num_rows = mysql_num_rows(result); /* pollerid 1 takes care of non host based data sources */ } } else { num_rows = mysql_num_rows(result); } if (!(threads = (pthread_t *)malloc(num_rows * sizeof(pthread_t)))) { die("ERROR: Fatal malloc error: spine.c threads!"); } if (!(details = (poller_thread_t **)malloc(num_rows * sizeof(poller_thread_t*)))) { die("ERROR: Fatal malloc error: spine.c details!"); } if (!(ids = (int *)malloc(num_rows * sizeof(int)))) { die("ERROR: Fatal malloc error: spine.c host id's!"); } if (!(host_time = (char *) malloc(SMALL_BUFSIZE))) { die("ERROR: Fatal malloc error: util.c host_time"); } memset(host_time, 0, SMALL_BUFSIZE); /* initialize winsock library on Windows */ SOCK_STARTUP; /* mark the spine process as started */ if (!set.ping_only) { snprintf(querybuf, BIG_BUFSIZE, "INSERT INTO poller_time (poller_id, pid, start_time, end_time) VALUES (%i, %i, NOW(), '0000-00-00 00:00:00')", set.poller_id, getpid()); if (mode == REMOTE) { db_insert(&mysqlr, REMOTE, querybuf); } else { db_insert(&mysql, LOCAL, querybuf); } } /* initialize threads and mutexes */ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); init_mutexes(); /* initialize available_threads semaphore */ sem_init(&available_threads, 0, set.threads); /* initialize available_scripts semaphore */ sem_init(&available_scripts, 0, MAX_SIMULTANEOUS_SCRIPTS); /* initialize thread initialization semaphore */ sem_init(&thread_init_sem, 0, 1); /* specify the point of timeout for timedwait semaphores */ until_spec.tv_sec = (time_t)(set.poller_interval + begin_time - 0.2); until_spec.tv_nsec = 0; sem_getvalue(&available_threads, &a_threads_value); SPINE_LOG_HIGH(("DEBUG: Initial Value of Available Threads is %i (%i outstanding)", a_threads_value, set.threads - a_threads_value)); /* tell fork processes that they are now active */ set.parent_fork = SPINE_FORK; /* initialize the threading code */ device_threads = 1; current_thread = 0; /* poller 1 always polls host 0 but only if it exists */ if (set.poller_id == 1 && set.has_device_0 == TRUE) { host_id = 0; change_host = FALSE; } else { change_host = TRUE; } /** * We must initilize the first snmp session * in the main thread to initilize the mib files * and other structures. After which it's snmp * is thread safe in threads */ snmp_sess_init(&session); /* loop through devices until done */ while (canexit == FALSE && device_counter < num_rows) { if (change_host) { mysql_row = mysql_fetch_row(result); host_id = atoi(mysql_row[0]); device_threads = atoi(mysql_row[1]); current_thread = 1; if (device_threads < 1) { device_threads = 1; } } else { current_thread++; } /* adjust device threads in cases where the host does not have sufficient data sources */ if (!set.ping_only) { if (set.active_profiles != 1) { snprintf(querybuf, BIG_BUFSIZE, "SELECT SQL_NO_CACHE COUNT(local_data_id) FROM poller_item WHERE host_id=%i AND rrd_next_step <=0", host_id); } else { snprintf(querybuf, BIG_BUFSIZE, "SELECT SQL_NO_CACHE COUNT(local_data_id) FROM poller_item WHERE host_id=%i", host_id); } tresult = db_query(&mysql, LOCAL, querybuf); mysql_row = mysql_fetch_row(tresult); total_items = atoi(mysql_row[0]); db_free_result(tresult); if (total_items && total_items < device_threads) { device_threads = total_items; } } else { device_threads = 1; } change_host = (current_thread >= device_threads) ? TRUE : FALSE; /* determine how many items will be polled per thread */ if (device_threads > 1) { if (current_thread == 1) { if (set.active_profiles != 1) { snprintf(querybuf, BIG_BUFSIZE, "SELECT SQL_NO_CACHE CEIL(COUNT(local_data_id)/%i) FROM poller_item WHERE host_id=%i AND rrd_next_step <=0", device_threads, host_id); } else { snprintf(querybuf, BIG_BUFSIZE, "SELECT SQL_NO_CACHE CEIL(COUNT(local_data_id)/%i) FROM poller_item WHERE host_id=%i", device_threads, host_id); } tresult = db_query(&mysql, LOCAL, querybuf); mysql_row = mysql_fetch_row(tresult); items_per_thread = atoi(mysql_row[0]); db_free_result(tresult); sprintf(host_time, "%lu", (unsigned long) time(NULL)); host_time_double = get_time_as_double(); } else if (host_time_double == 0 || host_time == 0 || host_time == NULL) { sprintf(host_time, "%lu", (unsigned long) time(NULL)); host_time_double = get_time_as_double(); } } else { snprintf(querybuf, BIG_BUFSIZE, "SELECT SQL_NO_CACHE COUNT(local_data_id) FROM poller_item WHERE host_id=%i AND rrd_next_step <=0", host_id); tresult = db_query(&mysql, LOCAL, querybuf); mysql_row = mysql_fetch_row(tresult); items_per_thread = atoi(mysql_row[0]); db_free_result(tresult); sprintf(host_time, "%lu", (unsigned long) time(NULL)); host_time_double = get_time_as_double(); } /* populate the thread structure */ if (!(poller_details = (poller_thread_t *)malloc(sizeof(poller_thread_t)))) { die("ERROR: Fatal malloc error: spine.c poller_details!"); } poller_details->device_counter = device_counter; poller_details->host_id = host_id; poller_details->host_thread = current_thread; poller_details->host_threads = device_threads; poller_details->host_data_ids = items_per_thread; snprintf(poller_details->host_time, 40, "%s", host_time); poller_details->host_time_double = host_time_double; poller_details->thread_init_sem = &thread_init_sem; poller_details->complete = FALSE; poller_details->threads_complete = 0; thread_mutex_lock(LOCK_THDET); details[device_counter] = poller_details; thread_mutex_unlock(LOCK_THDET); /* dev note - errno was never primed at this point in previous version of code */ int wait_retries = 0; int loop_count = 0; double progress_time = 0; unsigned int sem_err = 0; int spine_timeout = FALSE; while (TRUE) { sem_err = sem_trywait(&available_threads); if (sem_err == 0) { // Acquired a thread break; } else if (sem_err == EINTR) { // Interrupted by signal handler } else if (sem_err == EDEADLK) { SPINE_LOG_DEVDBG(("WARNING: Device[%i] HT[%i] would have deadlocked acquiring Available Thread Lock", host_id, current_thread)); } else if (sem_err == EAGAIN) { // Keep trying } loop_count++; if (loop_count == 10) { progress_time = get_time_as_double() - start_time; if (progress_time + 1 > set.poller_interval) { SPINE_LOG(("ERROR: Device[%i] HT[%i] polling timed out while acquiring Available Thread Lock", host_id, current_thread)); spine_timeout = TRUE; break; } loop_count = 0; } usleep(10000); total_time = get_time_as_double(); if (total_time - start_time > set.poller_interval) { SPINE_LOG(("ERROR: Device[%i] HT[%i] Spine Timed Out While Processing Devices External", host_id, current_thread)); spine_timeout = TRUE; canexit = TRUE; break; } } loop_count = 0; while (!spine_timeout) { sem_err = sem_trywait(&thread_init_sem); if (sem_err == 0) { // Acquired a thread break; } else if (sem_err == EINTR) { // Interrupted by signal handler } else if (sem_err == EDEADLK) { SPINE_LOG_DEVDBG(("WARNING: Device[%i] HT[%i] would have deadlocked acquiring Thread Initialization Lock", host_id, current_thread)); } else if (sem_err == EAGAIN) { // Keep trying } else { SPINE_LOG_DEVDBG(("WARNING: Device[%i] HT[%i] errored with %d while acquiring Thread Initialization Lock", host_id, current_thread, sem_err)); } if (loop_count == 10) { progress_time = get_time_as_double() - start_time; if (progress_time + 1 > set.poller_interval) { SPINE_LOG(("ERROR: Device[%i] HT[%i] polling timed out while acquiring Thread Init Lock", host_id, current_thread)); spine_timeout = TRUE; break; } loop_count = 0; } usleep(10000); total_time = get_time_as_double(); if (total_time - start_time > set.poller_interval) { SPINE_LOG(("ERROR: Device[%i] HT[%i] Spine Timed Out While Processing Devices Internal", host_id, current_thread)); spine_timeout = TRUE; canexit = TRUE; break; } } if (!spine_timeout) { /* create child process */ thread_retry: thread_mutex_lock(LOCK_HOST_TIME); thread_status = pthread_create(&threads[device_counter], &attr, child, poller_details); if (thread_status == 0) { SPINE_LOG_DEBUG(("DEBUG: Device[%i] Valid Thread to be Created (%ld)", poller_details->host_id, (unsigned long int)threads[device_counter])); if (change_host) { device_counter++; } sem_getvalue(&available_threads, &a_threads_value); SPINE_LOG_HIGH(("DEBUG: Device[%i] Available Threads is %i (%i outstanding)", poller_details->host_id, a_threads_value, set.threads - a_threads_value)); sem_post(&thread_init_sem); SPINE_LOG_DEVDBG(("DEBUG: DTS: device = %d, host_id = %d, host_thread = %d," " host_threads = %d, host_data_ids = %d, complete = %d", device_counter-1, poller_details->host_id, poller_details->host_thread, poller_details->host_threads, poller_details->host_data_ids, poller_details->complete)); } else if (thread_status == EAGAIN) { usleep(10000); goto thread_retry; } else if (thread_status == EINVAL) { SPINE_LOG(("ERROR: The Thread Attribute is Not Initialized")); } /* Restore thread initialization semaphore if thread creation failed */ if (thread_status) { sem_post(&thread_init_sem); } } } sem_getvalue(&available_threads, &a_threads_value); /* wait for all threads to 'complete' * using the mutex here as the semaphore will * show zero before the children are done */ while (a_threads_value < set.threads) { cur_time = get_time_as_double(); if (cur_time - begin_time > set.poller_interval) { SPINE_LOG(("ERROR: Polling timed out while waiting for %d Threads to End", set.threads - a_threads_value)); break; } SPINE_LOG_HIGH(("NOTE: Polling sleeping while waiting for %d Threads to End", set.threads - a_threads_value)); usleep(500000); sem_getvalue(&available_threads, &a_threads_value); } threads_final = set.threads - a_threads_value; SPINE_LOG_HIGH(("The final count of Threads is %i", threads_final)); if (!set.ping_only) { thread_mutex_lock(LOCK_THDET); for (threads_count = 0; threads_count < num_rows; threads_count++) { poller_thread_t* det = details[threads_count]; if (threads_missing == -1 && det == NULL) { threads_missing = threads_count; } if (det != NULL) { // && !det->complete) { SPINE_LOG_HIGH(("INFO: Device[%i] Thread %scomplete and %d to %d sources", det->host_id, det->complete ? "":"in", det->host_data_ids * (det->host_thread - 1), det->host_data_ids * (det->host_thread))); SPINE_LOG_DEVDBG(("DEBUG: DTF: device = %d, host_id = %d, host_thread = %d," " host_threads = %d, host_data_ids = %d, complete = %d", threads_count, det->host_id, det->host_thread, det->host_threads, det->host_data_ids, det->complete)); } } thread_mutex_unlock(LOCK_THDET); } if (threads_missing > -1) { SPINE_LOG(("WARNING: There were %d threads which did not run", num_rows - threads_missing)); } /* tell Spine that it is now parent */ set.parent_fork = SPINE_PARENT; /* push data back to the main server */ if (set.poller_id > 1 && set.mode == REMOTE_ONLINE && !set.SQL_readonly) { poller_push_data_to_main(); } /* update the db for |data_time| on graphs */ if (!set.ping_only) { if (set.poller_id == 1) { db_insert(&mysql, LOCAL, "REPLACE INTO settings (name,value) VALUES ('date',NOW())"); } snprintf(querybuf, BIG_BUFSIZE, "UPDATE poller_time SET end_time=NOW() WHERE poller_id=%i AND pid=%i", set.poller_id, getpid()); if (mode == REMOTE) { db_insert(&mysqlr, REMOTE, querybuf); } else { db_insert(&mysql, LOCAL, querybuf); } } if (db_pool_local) { db_close_connection_pool(LOCAL); } if (db_pool_remote) { db_close_connection_pool(REMOTE); } /* cleanup and exit program */ pthread_attr_destroy(&attr); SPINE_LOG_DEBUG(("DEBUG: Thread Cleanup Complete")); /* close the php script server */ if (set.php_required && !set.ping_only) { php_close(PHP_INIT); } SPINE_LOG_DEBUG(("DEBUG: PHP Script Server Pipes Closed")); /* free malloc'd variables */ for (i = 0; i < num_rows; i++) { if (details[i] != NULL) { SPINE_FREE(details[i]); } } SPINE_FREE(details); SPINE_FREE(threads); SPINE_FREE(ids); SPINE_FREE(conf_file); SPINE_FREE(debug_devices); SPINE_FREE(host_time); SPINE_LOG_DEBUG(("DEBUG: Allocated Variable Memory Freed")); /* close mysql */ db_free_result(result); db_disconnect(&mysql); if (set.poller_id > 1 && set.mode == REMOTE_ONLINE) { db_disconnect(&mysqlr); } SPINE_LOG_DEBUG(("DEBUG: MYSQL Free & Close Completed")); /* close snmp */ snmp_spine_close(); SPINE_LOG_DEBUG(("DEBUG: Net-SNMP Close Completed")); /* finally add some statistics to the log and exit */ end_time = get_time_as_double(); if (set.log_level >= POLLER_VERBOSITY_MEDIUM) { SPINE_LOG(("Time: %.4f s, Threads: %i, Devices: %i", (end_time - begin_time), set.threads, num_rows)); } else { /* provide output if running from command line */ if (!set.stdout_notty) { fprintf(stdout, "Time: %.4f s, Threads: %i, Devices: %i\n", (end_time - begin_time), set.threads, num_rows); } } /* uninstall the spine signal handler */ uninstall_spine_signal_handler(); /* clueanup winsock library on Windows */ SOCK_CLEANUP; exit(EXIT_SUCCESS); } /*! \fn static void display_help() * \brief Display Spine usage information to the caller. * * Display the help listing: the first line is created at runtime with * the version information, and the rest is strictly static text which * is dumped literally. * */ static void display_help(int only_version) { static const char *const *p; static const char * const helptext[] = { "Usage: spine [options] [[firstid lastid] || [-H/--hostlist='hostid1,hostid2,...,hostidn']]", "", "Options:", " -h/--help Show this brief help listing", " -f/--first=X Start polling with host id X", " -l/--last=X End polling with host id X", " -H/--hostlist=X Poll the list of host ids, separated by comma's", " -p/--poller=X Set the poller id to X", " -t/--threads=X Override the database threads setting.", " -C/--conf=F Read spine configuration from file F", " -O/--option=S:V Override DB settings 'set' with value 'V'", " -M/--mibs Refresh the device System Mib data", " -N/--mode=online For remote pollers, the operating mode.", " Options include: online, offline, recovery.", " The default is 'online'.", " -R/--readonly Spine will not write output to the DB", " -S/--stdout Logging is performed to standard output", " -P/--pingonly Ping device and update device status only", " -V/--verbosity=V Set logging verbosity to ", "", "Either both of --first/--last must be provided, a valid hostlist must be provided.", "In their absence, all hosts are processed.", "", "Without the --conf parameter, spine searches for its spine.conf", "file in the usual places.", "", "Verbosity is one of NONE/LOW/MEDIUM/HIGH/DEBUG or 1..5", "", "Runtime options are read from the 'settings' table in the Cacti", "database, but they can be overridden with the --option=S:V", "parameter.", "", "Spine is distributed under the Terms of the GNU Lesser", "General Public License Version 2.1. (http://www.gnu.org/licenses/lgpl.txt)", "For more information, see http://www.cacti.net", 0 /* ENDMARKER */ }; printf("SPINE %s Copyright 2004-2024 by The Cacti Group\n", VERSION); if (only_version == FALSE) { printf("\n"); for (p = helptext; *p; p++) { puts(*p); /* automatically adds a newline */ } } } /*! \fn static char *getarg(char *opt, char ***pargv) * \brief A function to parse calling parameters * * This is a helper for the main arg-processing loop: we work with * options which are either of the form "-X=FOO" or "-X FOO"; we * want an easy way to handle either one. * * The idea is that if the parameter has an = sign, we use the rest * of that same argv[X] string, otherwise we have to get the *next* * argv[X] string. But it's an error if an option-requiring param * is at the end of the list with no argument to follow. * * The option name could be of the form "-C" or "--conf", but we * grab it from the existing argv[] so we can report it well. * * \return character pointer to the argument * */ static char *getarg(char *opt, char ***pargv) { const char *const optname = **pargv; /* option already set? */ if (opt) return opt; /* advance to next argv[] and try that one */ if ((opt = *++(*pargv)) != 0) return opt; die("ERROR: option %s requires a parameter", optname); } cacti-spine-1.2.30/CHANGELOG0000664000175000017500000007134214770060157014250 0ustar markvmarkvThe Cacti Group | spine 1.2.30 -no changes 1.2.29 -no changes 1.2.28 -issue#350: When using Ping or SNMP Uptime, host is not always detected properly -issue#351: Add Ping Method where connection refused assumes host is up 1.2.27 -issue#349: Removal of AES Privacy Protocol was pre-mature 1.2.26 -issue#315: Errors cam be reported when uptime OID is not present -issue#317: MySQL reconnect option is depreciated -issue#321: Spine does not check a host with no poller items -issue#323: Poller may report the wrong number of devices polled -feature#329: Allow users to override the threads setting at the command line -feature#331: Allow spine to run in ping-only mode 1.2.25 -issue#234: Spine should see if script to be executed is executable -issue#291: Enhance number recognition -issue#294: When polling devices, sort by larger number of items first -issue#298: Log format may be corrupted when timeout occurs -issue#303: Compile warning appears due to GCC flag on RHEL7/RHEL8 -issue#304: Downed device detection only checks one of the two uptime OIDs -issue#305: Compile error appears due to execinfo.h on FreeBSD -issue#306: Bootstrap shell script contains some PHP cruft -issue#308: Padding is not always removed from the start of non-numeric strings -issue#311: Improve SNMP result handling for non-numeric results -issue#312: Further improve SNMP result handling for non-numeric results -issue#313: Remove check for the max_oids column which has been present since Cacti v1.0 -issue#314: Minimize Sorting when fetching poller records for maximum performance -feature#234: Spine should see if script to be executed is executable 1.2.24 -issue#296: When ignoring older OIDs, segmentation faults can be caused 1.2.23 -issue#287: Unexpected reindexing occurs when using uptime as the reindex method -issue#279: spine does not compile with SQL TLS deactivated -issue#298: Odd Cacti log format issue when script server timeout happened -feature#277: Spine should prevent the script server from connecting to remote when offline -feature#292: Improve Script Server Timeout Logging -feature#293: Add SQL_NO_CACHE to Spine Queries 1.2.22 -issue#265: Periodically spine crashes with sigabort due to MariaDB API buffer overruns -issue#267: FATAL: Spine Encountered a Segmentation Fault -issue#269: When polling time is exceed, spine does not always exit as expected -issue#270: Spine logging at `-V 5` includes an extra line feed -issue#271: Incorrect SNMP responses can cause spine to crash -issue#272: Properly handle devices that timeout responding to the Extended Uptime -issue#274: MariaDB can cause spine to abort prematurely despite error handling -feature#273: Spine should log the error time when exiting via signal 1.2.21 -issue#258: Disable DES if Net-SNMP doesn't have it 1.2.20 -issue#233: Add support for newer versions of MySQL/MariaDB -issue#238: When checking for uptime of device, don't assume a non-responce is always fatal -issue#240: When compiling, newer systems may complain about obsolete macros -issue#241: When querying devices, long descriptions can become truncated -issue#243: When compiling, FreeBSD may complain about missing backtrace functions -issue#244: When building queries, commands may become truncated -issue#247: When compiling with SNMPv3 support, SHA256 may incorrectly appear to be not supported -feature#252: Improve spine performance when only one snmp agent port is in use 1.2.19 -issue#172: Issues with polling loop may skip some datasources -issue#209: Ping no longer works due to hostname changes -issue#216: RRD steps are not always calculated correctly -issue#220: Unable to build when DES no longer supported -issue#221: IPv6 devices are not properly parsed -issue#226: Reduce a number of compiler warnings -issue#227: Compiler warnings due to lack of return in thread_mutex_trylock -issue#237: Spine will not look at non-timetics uptime when sysUpTimeInstance overflows -feature#235: Improve performance of Cacti poller on heavily loaded systems 1.2.18 -issue#207: Missing time parameter on FROM_UNIXTIME function 1.2.17 -issue#178: When building spine, errors with Percona libraries may appear by tersmitten -issue#184: When building spine, make process can report problems with backtrace -issue#186: When opening the database, spine can unexpectedly crash -issue#187: On large systems, too many connections can cause firewalls to assume a DDoS attack -issue#192: Spine does not properly use the mysql reconnect option -issue#193: When performing certain re-index operations, spine can unexpectedly crash -issue#194: When logging to standard output, log messaging is inconsistent -issue#195: When using newer gcc versions, spine is unable to compile properly -issue#197: Remote data collectors push too much data to main when performing diagnostics -issue#200: Make spine more responsive when remote connection is down -issue#202: MySQL SSL Key capability check does not work -issue#203: Backtracing support checks do not work -issue#204: MySQL Retry Count capability check always fails -feature: Make spine immune to DST changes -feature#181: Parse hostnames from tcp[6]:hostname:port [not fully implemented] -feature#188: On large systems, more concurrent script servers are needed 1.2.16 -issue: Some developer debug log messages falsely labeled as WARNINGS -issue#164: Remove the need of the dos2unix program -issue#171: Spine experiencing MySQL socket error 2002 under load -issue#175: Under heavy load MySQL/MariaDB return 2006 and 2013 errors on query -feature: Add backtrace output to stderr for signals -feature#176: Add Data Source turnaround time to debug output 1.2.15 -issue#166: Special characters may not always be ignored properly -issue#168: Correct issues with linking under OpenBSD 1.2.14 -issue#162: Spine not updating rrd_next_step 1.2.13 -issue#156: Compile on Cygwin is failing due to icmp6 headers missing 1.2.12 -issue#155: Failed host lookup causes spine to crash 1.2.11 -issue#122: Unable to compile spine on OpenBSD -issue#129: Repeated warnings due to 'Recache Event Detected for Device' -issue#134: No error is recorded to log file when database is incorrect -issue#144: MySQL retry logic is not working as expected -issue#149: When using IPv6 address, segmentation fault can occur due to incorrectly using IPv4 ping -issue#150: When polling results are null, segmentation errors may occur -issue#151: When collecting data, spine should be stripping alpha characters from output 1.2.10 -feature: release to match Cacti release 1.2.9 -issue#124: Improve logging of snmp multiple OID get function -issue#129: PCOMMAND Device[XXXX] WARNING: Recache Event Detected for Device -issue#130: Segmentation Fault triggered by a lot of errors -issue#131: Replace function gethostbyname with thread safe getaddrinfo -issue#139: removed strip_alpha for "Wrong-Type" SNMP-Answer 1.2.8 -issue#119: When calling bootstrap, an invalid error claiming config.h.in is missing can appear on first compile 1.2.7 -issue#113: When compiling under CentOS, 'm4' directory appears to be missing -issue#115: Perform proper initialization and teardown of sockets under Windows -issue#117: Spine configuration example shows remote database prefix for local database values 1.2.6 -issue#101: MySQL 8 has retired 'my_bool' type -issue#104: Spine should report which threads are outstanding -issue#105: Spine should include 'Process[]' in standard logging -issue#107: Log Buffer is too small to handle some output -issue#108: Once an SNMP timeout is reached, snmp_get does not honor include_host -issue#109: Initial declarations inside for loop cause compile errors -issue#110: When compiling, 'directive output may be truncated' messages can appear -issue#112: INSTALL instructions have typo in bootstrap command 1.2.5 -issue: Improve detection of MySQL vs. MariaDB client library requirement -issue#98: Checking type in poller.c causes compiler error 1.2.4 -issue#92: Add Data Source ID to "Empty result' error messages -issue#94: Increase default 'results_buffer' size to 2048 and MySQL max buffer to 131072 bytes 1.2.3 -issue#76: Spine unhandled exception signal -issue: More recent versions of MariaDB require different client library 1.2.2 -feature: release to match Cacti release 1.2.1 -feature: release to match Cacti release 1.2.0 -feature: Allow threads to be set a Data Collector level -issue#50: make fails on Debian 7 attempting to locate clock_gettime -issue#53: Improved escaping when updating database records -issue#54: Spine should always log if ICMP fails -issue#58: When updating snmp_sysLocation, text can become truncated -issue#63: Automatically reduce device threads when number of data sources does not require it 1.1.38 -feature: release to match Cacti release 1.1.37 -issue: Extra long snmp_sysObjectID's generate SQL errors 1.1.36 -issue#49: Re-Index update query failed due to lack of escaping 1.1.35 -issue#48: Availability check none triggers snmp system check that causes device to be ignored 1.1.34 -feature: release to match Cacti release 1.1.33 -feature: release to match Cacti release 1.1.32 -feature: release to match Cacti release 1.1.31 -issue#15: Spine cause CPU usage up to 100% -issue#32: Support noAuthNoPriv in Spine -issue#34: Re-introduce check for relocatable Net-SNMP and development files -issue#35: SNMP OID range checks cause some snmpget's to fail -issue#38: SNMP Ping timeouts not reported properly -issue#39: Spine should use same logging format as Cacti -issue#41: Spine cause CPU usage up to 100% -issue#42: Boost table keeps populating with boost disabled 1.1.30 -feature: release to match Cacti release 1.1.29 -issue#39: Have spine match cacti_log function 1.1.28 -feature: release to match Cacti release 1.1.27 -feature: release to match Cacti release 1.1.26 -feature: release to match Cacti release 1.1.25 -feature: release to match Cacti release 1.1.24 -feature: release to match Cacti release 1.1.23 -feature: release to match Cacti release 1.1.22 -feature: release to match Cacti release 1.1.21 -feature: release to match Cacti release 1.1.20 -issue#28: UDP and TCP ping issues 1.1.19 -feature: release to match Cacti release 1.1.18 -issue: Sort devices by polling time to allow long running devices to go first 1.1.17 -issue: Increase logging level for POPEN timeouts 1.1.16 -feature: release to match Cacti release 1.1.15 -feature: release to match Cacti release 1.1.14 -feature: release to match Cacti release 1.1.13 -issue: Script timeout should start after semaphore releases -issue: Don't include line breaks in Syslog 1.1.12 -feature: release to match Cacti release 1.1.11 -feature: release to match Cacti release 1.1.10 -feature: release to match Cacti release 1.1.9 -feature: release to match Cacti release 1.1.8 -issue#21: Errors when snmp_sysLocation contains a single quote 1.1.7 -feature: release to match Cacti release 1.1.6 -issue#15: Spine can cause high CPU utilization due to semaphore implementation -issue#11: Spine reports SNMP timeouts in some cases when gathering multiple OID in one request 1.1.4 -feature: release to match Cacti release 1.1.3 -feature: release to match Cacti release 1.1.2 -issue#13: Spine does not compile with --enable-lcap -issue#16: Spine not handling debug devices correctly 1.1.1 -feature: release to match Cacti release 1.1.0 -feature: release to match Cacti release 1.0.6 -feature: release to match Cacti release 1.0.5 -issue#7: remove database name not respected 1.0.4 -feature: release to match Cacti release 1.0.3 -issue#6: Spine accepts arbitrary polling modes 1.0.2 -feature: release to match Cacti release 1.0.1 -bug:With enough make jobs, help2man will fail if help2man is run before spine has been built 1.0.0 -feature: Track per host polling time -feature: Collect System Mib Information -feature: Place collection timing into host table -feature: Support snmpContextEngineID -feature: Change all references of Host to Device -bug:0001415: spine does not handle log_perror -bug#0002220: spine/cacti patch to allow SNMP_polling decices' hostnames like user@host:port -bug#0002234: Wrong alphanumeric value got from Spine SNMP poller -bug#0002651: False Positive Down status with snmpv3 -bug#0002674: Segmentation fault in Spine during polling host 0.8.8h -bug:0002659: wrong information about spine setup for icmps -bug: Removed requirement for building man page, deferred to 1.0 0.8.8g -bug:0002380: please build man page for spine -bug:0002610: Spine is trying to use mysql results after data being freed -bug:0002639: Spine does not respect snmp_retries from the database -bug:0002640: Spine crashes randomly while polling using multiple threads on one device -bug:0002549: Spine: hasCaps() always returns FALSE -bug:0002638: Spine: ping_udp() uses uninitialised socket timeout -bug:0002654: On RHEL 6.5, spine ./configure builds Makefile without crypto support 0.8.8f -bug:0002604: cacti-spine, configure script ignores LDFLAGS in certain situations 0.8.8e -bug#0002185: Multiple memory leaks found in spine -bug#0002398: Buffer overflow -bug#0002447: Building spine on Debian Jessie (testing) fails -bug#0002536: Spine segfaults when 18k+ hosts are to be pulled -bug#0002576: Call to a non-existent column of the database in poller.c -bug#0002578: spine doesn't compile on ubuntu 12.04 -bug#0002585: Spine: buffer overflow in logging when RESULTS_BUFFER>LOGSIZE can SEGV -bug#0002588: Spine "is_numeric" incorrectly returns TRUE for some hexadecimal values 0.8.8d -feature: add snmp system information gathering 0.8.8c -bug: remove obsolete reference causing pre-compile issues 0.8.8b -bug: set appropriate mysql 5.5+ timeouts 0.8.8a -bug#0002204: spine corrupted double-linked list error -bug#0002206: On multi-architecture Debian platforms spine fails to build -bug#0002205: Typo, twice, in spine.c: absense -> absence 0.8.8 -bug#0002185: Multiple memory leaks found in spine 0.8.7i -bug: Rework the way ping works, due to bad impact of changes in 0.8.7h -bug: ICMP ping not working on windows due to cygwin change -bug: Linux capabilities reported as on regardless of state -feature: Add a reconnect capability when the mysql server goes away 0.8.7h -bug#0001669: Problems with getting data from script using SPINE on Windows Server 2003 x32 -bug#0001829: Wrong string numerical value got from Spine SNMP poller -bug#0001899: spine treats undef replies as 0 -bug#0001968: Spine Error with Cisco FWSM firewalls -bug#0001974: PRIV_PROC_EXEC is required for Spine on Solaris 10 -bug#0002059: Spine doing too many (80+) connections when polling -bug: Net-snmp API issues cause spine crashes with some SNMP agents -bug: Host list not properly initialized -bug: Mutex locking issues cause deadlocks in Windows -bug: Escape windows type back slashes in scripts -bug: Use safer Non Net-SNMP string parsing calls for snmpget and snmpgetnext -bug: be smart for SNMP V3 and use AuthNoPriv when privacy passphrase is empty -bug: Do not fail on insert for a duplicate record -bug: Script server results with special characters can cause segfaults -feature#0001852: Another 10% speedup in spine -feature#0001878: Add Linux Capabilities to Spine -feature: Add additional ping methods to allow more precise snmp pings -feature: Clean up directory, update bootstrap 0.8.7g -feature: Multi threaded host polling 0.8.7f -bug#0001465: Don't check micro version when comparing installed net-snmp version to version used when building -bug#0001497: Prevent constant reindexes if snmp agent continually returns uptime of 0 -bug#0001510: Speedup spine resorting hosts by estimated polling time -bug#0001519: spine enhancement for parallel collection of data at host level -bug#0001541: Spine snmp bulk get incorrect assumes that the server SNMP Agent returns variables in same order -bug#0001551: Spine compile problem on AIX 5 -bug#0001563: Spine Compilation Problem on sql.c -bug#0001604: HEX Counter values enclosed in quotes not recognized as HEX -bug#0001657: Spine output shows drop of latest bit from hex value -bug#0001653: Transient ICMP Socket create failures causing spine to die -bug: When performing reindex checks, spine does not trim quotes from string values resulting in reindex faiures -bug: Add new option to enable Net-SNMP library vs. header versions -bug: ICMP fails on some platforms -bug: MySQL client auto reconnect default in MySQL 5.0.71 causes spine to crash -bug: Inconsistent behavior when the poller interval has not been set -bug: Invalid use of FDSETSIZE can cause issue with Script Server -bug: When performing reindex checks, spine does not trim quotes from string values resulting in reindex faiures -feature#0001629: Implement the snmp clientaddr option to bind spine to a specific local ip address 0.8.7e -bug: Not all configuration paths are searched for spine.conf -bug: If host has MAX OID's set to 0, timeouts occur -bug: Properly detect full Cygwin installs by finding sh.exe -bug: Don't perform TCP Ping retries in Windows. You cannot set socket send timeouts in Windows 0.8.7d -bug#0001310: Feature request for poller.php- single host polling -bug#0001313: Spine takes an excessive amount of time timing out failed tcp pings -bug#0001358: (cacti + spine) svn. Using snmp v3 constantly get "value: U", whereas snmpget returns correct value -bug#0001379: Ping.c might send more then retry_count icmp packages -bug#0001381: configure script distributed with cacti-spine-0.8.7c is broken -bug#0001420: PCOMMAND: Poller[0] Host[80] WARNING: Recache Event Detected for Host -bug: A host that for some reason can not initialize snmp can cause a segfault -bug: When using nifty popen, when the script times out, attempt to kill it -bug: Under some platforms tcp and udp ping unresponsive -bug: Availability messaging incorrect for snmpv3 hosts -bug: TCP Pings that receive a connection refused are good pings -bug: Avoid segmentation faults when Reindex table issues are found -bug: When performing snmp_get's, dont assume no error means non-null response -bug: Some snmp agents don't getnext on '.1' and need another oid '.1.3' -bug: Net-snmp versioning breaks snmpV3 -bug: Use system level snmp_retries variable instead of 3 -feature: Add one additional debug message to the poller code to track data sources polled -feature: Add more config paths for packagers -feature: Allow specification of an optional hostlist instead of first and last hostid's -feature: Integrate more tightly with boost by allowing writing directly to the boost output table -feature: Complete implementation poller_id field for people using a poller_id for polling. 0.8.7c -bug#0001022: strip_quotes incorrectly parsing script output -bug#0001097: Spine: Patch to allow unknown values including "Nan" and "U" without warnings -bug#0001101: spine hangs and times out -bug#0001130: ERROR: Spine Timed Out While Processing Hosts Internal -bug#0001147: spine doesn't trim white space from data template OIDs -bug#0001152: Running spine w/o setuid-root on Solaris -bug#0001186: clarify INSTALL documentation. -bug#0001231: Compiling Spine on Solaris 10 -bug#0001260: Deadlock in ping.c when resolving bad hostnames -bug#0001271: hex string to decimal conversion -bug#0001277: Spine reporting partial result or 'U' for SNMP value -bug: Correct a buffer overrun issue in poller.c -bug: If the script server goes away due to a syntax problem, it will not restart -bug: If the snmp session is not created spine times out -bug: If your snmp context changes between poller items, the context would not always change -feature: Use threadsafe gethostbyname_r -feature: Improve mysql logging -feature: Allow overriding the default results buffer size -feature: Allow overriding the default max mysql buffer (insert) size -feature: Allow overriding the maximum simultaneous scripts setting 0.8.7a -bug#0001054: Cactid/Spine installation instructions are missing dependencies -bug#0001056: Spine/Cactid configure reports incorrect URL for bug reporting -bug#0001071: When using Multiget OID's in Spine Errored OID's Cause Alignment Issues -bug#0001072: When the Last OID of a group is 'U', the host is repored down and remainder of OID's not polled -bug#0001097: Add patch to allow unknown values -bug#0001101: Fix ICMP hang while in a timed out state 0.8.7 -bug: Fix configure issues with hybrid 64 bit OS' -feature: Rename to spine -feature: Update copyright -feature: Add tab stops and autoindent to source code -feature: Make spike detection more generic -feature: Add snmpv3 support including contexts 0.8.6j -bug#0000879/0000872: cactid doesn't always report error messages before dying -bug#0000856: Slight documentation change -bug#0000861: Downed Host Detection on non-SNMP devices wrong -bug#0000886: SNMP Ping to Utilize The snmpgetnext instead of snmpget -bug#0000889: Use configure script's sysconfdir variable for placement of cactid.conf -bug#0000929: Spine with setuid root falls back to UDP after first host -bug#0000939: cactid use custom hostname, snmp_community,snmp_username and snmp_password in datasource -bug#0000940: Increasing BUFSIZE in cactid.h will still not allow longer output from scripts -bug#0000951: Spine SNMP polling does not strip enum values -bug#0000960: cactid truncating responses from scripts to 511 characters -bug#0000987: cactid can't use a socket unix -bug: By default, Cygwin can not handle large quantities of running scripts, therefore, limit to 20 -bug: Increase the number of file descriptors in Cygwin to 512, 64 does not work for large systems. -bug: If the system is out of resources when trying to fork(), retry three more times -bug: Use vfork instead of fork. It's more efficient with resources. -bug: Spine Script Server would not work with cgi version of php. Now it does. -bug: Changes in SNMP Community from poller item to poller item were not being considered. -bug: Restore meaningful notices when running from command line -feature: Allow users to use the traditional popen instead of nifty if required. -feature: Properly support INNODB database engine in MySQL 0.8.6i -bug: Only output as much as MySQL can handle in 1 insert -bug: Faulty Script Server shutdown was causing race condition -bug: Allow ICMP or UDP ping to work if host begins with either "TCP:" or "UDP:" -bug: Change assert logging to HIGH to more closely match cmd.php -bug: Allow Spine to make on 64bit x86 Platforms - thanks to Reuben Farrelly -bug: Changing syntax from "insert into to replace into" for reindex inserts -bug: Don't perform a reindex a check for a data query if you already have to reindex -bug: Make sure to log FATAL to stderr -bug: Do not re-index a host if the re-index value is a "No Such Instance" result. -bug#0000682: Allow storing of each hosts polling start time -bug#0000691: Spine fails with custom snmp port -bug#0000699: Tick counts not being detected -bug#0000706: Spine didn't start PHP scripts when host didn't answer SNMP (but icmp is ok) -bug#0000710: cactid.conf doesn't allow for no mysql password -bug#0000716: Don't attempt to save output for a host if there is none. -bug#0000717: Allow numeric double results to start with '+', '-', '.' -bug#0000724: cactid truncate script command when reindex -bug#0000736: Solaris 8 : Alarm Clock message -bug#0000745: cactid: search path for mysql and snmp does not include /usr/pkg -bug#0000748: cactid doesn't handle special charectors -bug#0000749: Spine 0.8.6g won't accept special characters from data template -bug#0000750: poller doesn't check for snmp if udp ping fails -bug#0000752: Problem with OID truncation with cactid -bug: add kstat checking for Net-SNMP v5.3.x -bug: get more intelegent. move cactid.conf.dist to /etc during install -feature: add Net-SNMP Library vs. Header check prior to execution -feature: enabled signal handling in cactid 0.8.6h -feature: Not released. Syncing with Cacti distribution again. 0.8.6g -bug#0000609: console "error" messages should go to stderr instead of stdout -bug#0000618/0000613: correct invalid assignment of session.community in snmp.c -bug#0000629: correct issues with invalid unknown reaults when reducing MAX OID get size -bug: correct issue where spike prevention logic was not being activated properly -bug: correct issue where host reindex was not operating for uptime goes backwards reindex method -bug: if sysUptime is queried for a host, only do it once -bug: certain SNMP string responses where not being handled properly due to net-snmp bug -bug: increase the allowable size of the logfile entry -bug: change behavior of availability so that if SNMP and ICMP/UDP are selected, only one has to succeed -bug: cleaned up implementation of multi SNMP OID get error checking logic -bug: don't attempt to allocate a zero item array when a host has no polling items -bug: correct string values returning as blank in *nix environments -bug: correct issue where invalid SNMP OID results would cascade to subseqent SNMP OID calls for a host -optimization: change logging logic to cleanup code and add varadic formatting -optimization: complete audit of code for portability complements of Steve @ http://www.unixwiz.net -optimization: documentation, documentation, documentation... -feature: added command line argument handling including database overrides complements of Steve @ http://www.unixwiz.net -feature: added multiple script server process feature to current version -feature: added support for MySQL 5.x -feature: added more verbose MySQL DEBUG logic 0.8.6f-1 -bug: Two memory leak fixes in poller.c -bug: Typo in the README-WINDOWS install file 0.8.6f -bug: allow a host to have more than 1 SNMP port defined for those odd devices that have them -bug: corrected an issue that caused infrequent segfaults when a host refused a ping request -bug: increased max_threads from 30 to 100 -bug: delayed killing php script server process by .5 seconds to allow MySQL connections to close -bug: place thread locks around unsafe functions to prevent possible segfaults -feature: implemented snmpv3 AuthNoPriv to align with Cacti 0.8.6 user interface -feature: improved performance of poller by the reduction of MySQL insert calls -feature: improved poller performance by grouping snmp get requests in a single call 0.8.6e -bug:#0000470: added -v, --version, -h, --help to command line options to make more GNU friendly -bug:#0000425: added ability to specify a socket file in *nix environments -bug: correct issues converting back slashes to forward slashes in arguments -bug: stop cactid from segfaulting when the database contains orphaned data source records -bug: re-implemented thread safe operations for non-linux systems -bug: allow cactid to poll from non-standard snmp port -bug: optimized some string handling routines -bug: changed snmp availability check to sysUptime from sysDesc -bug: limit the number of concurrent threads to 30 -bug: remove legacy cactid.conf file processing code -bug: add fault detection logic to mysql query calls to prevent segfaults -bug: cleaned up some unused variables -bug: add ability to detect errors and gracefully shut down php script_server -bug: remove trailing blanks and alpha characters from results -bug: clean up some string handling on script server results -bug: stop DEADLOCKS by not prematurely releasing mutex locks -bug: changed licensing to LGPL to accomodate linked libraries such as OpenSSL -bug: corrected issues where snprintf was taking 1 too many characters -bug: remove unneccessary Windows MACRO's from net-snmp initialization -feature: added seteuid functionality for unix and ICMP ping -feature: restart the script server after a timeout to prevent unexpected results -feature: only run the php script server if you have script server elements to process -feature: disable MIB loading to speed the launch of the net-snmp api -feature: change TREAD_SLEEP duration depending on number of processes to reduce CPU load -feature: allow the user to specify a script and script server timeout value from the user interface -feature: added polling for non-host specific data sources -feature: added data source logging to provide easier problem resolution -feature: added cmd.php like spike suppression -feature: added support for experimental multiple poller interval handling 0.8.6d -bug: increased timers for popen and script server to 20 seconds -bug: corrected segfault when polling a device with a null community string -bug: re-included ifdef for mysql thread protection for mysql 3.x 0.8.6c -bug: fixed integer/float conversion problem. -bug: increased the default script timeout from 5 to 10 seconds -bug: changed stdout format to reduce clutter on screen -bug: removed cygwin workaround that was causing SNMP instability -bug: increased both BUFSIZE and LOGSIZE to allow for larger output corner cases -bug: added mutex locking around MySQL calls and eliminated one unlock without a lock -feature: added connection timeout and retry logic -feature: added database port option in cactid.conf file. New parameter is DB_Port -feature: added some compiler directives for better compatibility with cygwin 0.8.6b -bug: fixed issue that prevented multi ds item RRD?s from updating -bug: fixed invalid string length calculation that was truncating output over 4 characters -bug: small log file reformatting ----------------------------------------------- Copyright (c) 2004-2024 - The Cacti Group, Inc. cacti-spine-1.2.30/sql.h0000664000175000017500000000614114770060157014001 0ustar markvmarkv/* ex: set tabstop=4 shiftwidth=4 autoindent: +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 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, implimentation details) | +-------------------------------------------------------------------------+ | - Cacti - http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ extern int db_insert(MYSQL *mysql, int type, const char *query); extern MYSQL_RES *db_query(MYSQL *mysql, int type, const char *query); extern void db_connect(int type, MYSQL *mysql); extern void db_disconnect(MYSQL *mysql); extern void db_escape(MYSQL *mysql, char *output, int max_size, const char *input); extern void db_free_result(MYSQL_RES *result); extern void db_create_connection_pool(int type); extern void db_close_connection_pool(int type); extern pool_t *db_get_connection(int type); extern void db_release_connection(int type, int id); extern int db_reconnect(MYSQL *mysql, int error, char *location); extern int append_hostrange(char *obuf, const char *colname); #define MYSQL_SET_OPTION(opt, value, desc) \ {\ options_error = mysql_options(mysql, opt, value); \ if (options_error < 0) {\ die("FATAL: MySQL options unable to set %s option", desc);\ }\ }\ cacti-spine-1.2.30/keywords.h0000664000175000017500000000504514770060157015053 0ustar markvmarkv/* ex: set tabstop=4 shiftwidth=4 autoindent: +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 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, implimentation details) | +-------------------------------------------------------------------------+ | - Cacti - http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ extern const char *printable_log_level(int token); extern int parse_log_level(const char *word, int dflt); extern const char *printable_logdest(int token); extern int parse_logdest(const char *word, int dflt); extern const char *printable_action(int token); extern int parse_action(const char *word, int dflt); cacti-spine-1.2.30/error.c0000664000175000017500000001370214770060157014327 0ustar markvmarkv/* ex: set tabstop=4 shiftwidth=4 autoindent: +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 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, implimentation details) | +-------------------------------------------------------------------------+ | - Cacti - http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ /* These functions handle simple singal handling functions for Spine. It was written to handle specifically issues with the Solaris threading model in version 2.8. */ #include "common.h" #include "spine.h" /*! \fn static void spine_signal_handler(int spine_signal) * \brief interupts the os default signal handler as appropriate. * */ static void spine_signal_handler(int spine_signal) { signal(spine_signal, SIG_DFL); set.exit_code = spine_signal; /* variables for time display */ time_t nowbin; struct tm now_time; struct tm *now_ptr; /* get time for poller_output table */ nowbin = time(&nowbin); localtime_r(&nowbin,&now_time); now_ptr = &now_time; char *log_fmt = get_date_format(); char logtime[50]; strftime(logtime, 50, log_fmt, now_ptr); switch (spine_signal) { case SIGABRT: fprintf(stderr, "%s FATAL: Spine Interrupted by Abort Signal\n", logtime); break; case SIGINT: fprintf(stderr, "%s FATAL: Spine Interrupted by Console Operator\n", logtime); break; case SIGSEGV: fprintf(stderr, "%s FATAL: Spine Encountered a Segmentation Fault\n", logtime); #ifdef HAVE_EXECINFO_H int row = 0; char **exit_strings = NULL; fprintf(stderr, "Generating backtrace...%ld line(s)...\n", set.exit_size); if (set.exit_size) { set.exit_size = backtrace(set.exit_stack, set.exit_size); exit_strings = backtrace_symbols(set.exit_stack, set.exit_size); if (exit_strings) { for (row = 0; row < set.exit_size; row++) { fprintf(stderr, "%3d: %s\n", row, exit_strings[row]); } free(exit_strings); } } #endif exit(1); break; case SIGBUS: fprintf(stderr, "%s FATAL: Spine Encountered a Bus Error\n", logtime); break; case SIGFPE: fprintf(stderr, "%s FATAL: Spine Encountered a Floating Point Exception\n", logtime); break; case SIGQUIT: fprintf(stderr, "%s FATAL: Spine Encountered a Keyboard Quit Command\n", logtime); break; case SIGPIPE: fprintf(stderr, "%s FATAL: Spine Encountered a Broken Pipe\n", logtime); break; default: fprintf(stderr, "%s FATAL: Spine Encountered An Unhandled Exception Signal Number: '%d'\n", logtime, spine_signal); break; } } static int spine_fatal_signals[] = { SIGINT, SIGPIPE, SIGSEGV, SIGBUS, SIGFPE, SIGQUIT, SIGSYS, SIGABRT, 0 }; /*! \fn void install_spine_signal_handler(void) * \brief installs the spine signal handler to stop certain calls from * abending Spine. * */ void install_spine_signal_handler(void) { /* Set a handler for any fatal signal not already handled */ int i; struct sigaction sa; void (*ohandler)(int); for (i=0; spine_fatal_signals[i]; ++i) { sigaction(spine_fatal_signals[i], NULL, &sa); if (sa.sa_handler == SIG_DFL) { sa.sa_handler = spine_signal_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sigaction(spine_fatal_signals[i], &sa, NULL); } } for (i=0; spine_fatal_signals[i]; ++i) { ohandler = signal(spine_fatal_signals[i], spine_signal_handler); if (ohandler != SIG_DFL) { signal(spine_fatal_signals[i], ohandler); } } return; } /*! \fn void uninstall_spine_signal_handler(void) * \brief uninstalls the spine signal handler. * */ void uninstall_spine_signal_handler(void) { /* Remove a handler for any fatal signal handled */ int i; struct sigaction sa; void (*ohandler)(int); for (i=0; spine_fatal_signals[i]; ++i) { sigaction(spine_fatal_signals[i], NULL, &sa); if (sa.sa_handler == spine_signal_handler) { sa.sa_handler = SIG_DFL; sigaction(spine_fatal_signals[i], &sa, NULL); } } for ( i=0; spine_fatal_signals[i]; ++i ) { ohandler = signal(spine_fatal_signals[i], SIG_DFL); if (ohandler != spine_signal_handler) { signal(spine_fatal_signals[i], ohandler); } } } cacti-spine-1.2.30/Makefile.in0000664000175000017500000007744014770060157015110 0ustar markvmarkv# Makefile.in generated by automake 1.13.4 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # # +-------------------------------------------------------------------------+ # | Copyright (C) 2004-2024 The Cacti Group | # | | # | 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. | # +-------------------------------------------------------------------------+ # | Cacti: The Complete RRDtool-based Graphing Solution | # +-------------------------------------------------------------------------+ # | This code is designed, written, and maintained by the Cacti Group. See | # | about.php and/or the AUTHORS file for specific developer information. | # +-------------------------------------------------------------------------+ # | http://www.cacti.net/ | # +-------------------------------------------------------------------------+ VPATH = @srcdir@ am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ bin_PROGRAMS = spine$(EXEEXT) subdir = . DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/configure $(am__configure_deps) \ $(top_srcdir)/config/config.h.in $(top_srcdir)/config/depcomp \ INSTALL config/config.guess config/config.sub config/depcomp \ config/install-sh config/missing config/ltmain.sh \ $(top_srcdir)/config/config.guess \ $(top_srcdir)/config/config.sub \ $(top_srcdir)/config/install-sh $(top_srcdir)/config/ltmain.sh \ $(top_srcdir)/config/missing ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" \ "$(DESTDIR)$(configdir)" PROGRAMS = $(bin_PROGRAMS) am_spine_OBJECTS = sql.$(OBJEXT) spine.$(OBJEXT) util.$(OBJEXT) \ snmp.$(OBJEXT) locks.$(OBJEXT) poller.$(OBJEXT) \ nft_popen.$(OBJEXT) php.$(OBJEXT) ping.$(OBJEXT) \ keywords.$(OBJEXT) error.$(OBJEXT) spine_OBJECTS = $(am_spine_OBJECTS) spine_LDADD = $(LDADD) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/config depcomp = $(SHELL) $(top_srcdir)/config/depcomp am__depfiles_maybe = depfiles am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(spine_SOURCES) DIST_SOURCES = $(spine_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } man1dir = $(mandir)/man1 NROFF = nroff MANS = $(man_MANS) DATA = $(config_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags CSCOPE = cscope AM_RECURSIVE_TARGETS = cscope DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) am__remove_distdir = \ if test -d "$(distdir)"; then \ find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ && rm -rf "$(distdir)" \ || { sleep 5 && rm -rf "$(distdir)"; }; \ else :; fi am__post_remove_distdir = $(am__remove_distdir) DIST_ARCHIVES = $(distdir).tar.gz GZIP_ENV = --best DIST_TARGETS = dist-gzip distuninstallcheck_listfiles = find . -type f -print am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' distcleancheck_listfiles = find . -type f -print ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ HELP2MAN = @HELP2MAN@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_aux_dir = @ac_aux_dir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign ACLOCAL_AMFLAGS = -I m4 spine_SOURCES = sql.c spine.c util.c snmp.c locks.c poller.c nft_popen.c php.c ping.c keywords.c error.c configdir = $(sysconfdir) config_DATA = spine.conf.dist man_MANS = spine.1 all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj am--refresh: Makefile @: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign Makefile .PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck $(top_srcdir)/configure: $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) $(ACLOCAL_M4): $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): config/config.h: config/stamp-h1 @if test ! -f $@; then rm -f config/stamp-h1; else :; fi @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) config/stamp-h1; else :; fi config/stamp-h1: $(top_srcdir)/config/config.h.in $(top_builddir)/config.status @rm -f config/stamp-h1 cd $(top_builddir) && $(SHELL) ./config.status config/config.h $(top_srcdir)/config/config.h.in: $(am__configure_deps) ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) rm -f config/stamp-h1 touch $@ distclean-hdr: -rm -f config/config.h config/stamp-h1 install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list spine$(EXEEXT): $(spine_OBJECTS) $(spine_DEPENDENCIES) $(EXTRA_spine_DEPENDENCIES) @rm -f spine$(EXEEXT) $(AM_V_CCLD)$(LINK) $(spine_OBJECTS) $(spine_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keywords.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/locks.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nft_popen.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/php.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ping.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/poller.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/spine.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sql.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@ .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs distclean-libtool: -rm -f libtool config.lt install-man1: $(man_MANS) @$(NORMAL_INSTALL) @list1=''; \ list2='$(man_MANS)'; \ test -n "$(man1dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.1[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ done; } uninstall-man1: @$(NORMAL_UNINSTALL) @list=''; test -n "$(man1dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ sed -n '/\.1[a-z]*$$/p'; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir) install-configDATA: $(config_DATA) @$(NORMAL_INSTALL) @list='$(config_DATA)'; test -n "$(configdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(configdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(configdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(configdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(configdir)" || exit $$?; \ done uninstall-configDATA: @$(NORMAL_UNINSTALL) @list='$(config_DATA)'; test -n "$(configdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(configdir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscope: cscope.files test ! -s cscope.files \ || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) clean-cscope: -rm -f cscope.files cscope.files: clean-cscope cscopelist cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -rm -f cscope.out cscope.in.out cscope.po.out cscope.files distdir: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done -test -n "$(am__skip_mode_fix)" \ || find "$(distdir)" -type d ! -perm -755 \ -exec chmod u+rwx,go+rx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz $(am__post_remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 $(am__post_remove_distdir) dist-lzip: distdir tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz $(am__post_remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz $(am__post_remove_distdir) dist-tarZ: distdir tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__post_remove_distdir) dist-shar: distdir shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz $(am__post_remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) $(am__post_remove_distdir) dist dist-all: $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' $(am__post_remove_distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another # tarfile. distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lz*) \ lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ esac chmod -R a-w $(distdir) chmod u+w $(distdir) mkdir $(distdir)/_build $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build \ && ../configure --srcdir=.. --prefix="$$dc_install_base" \ $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ && $(MAKE) $(AM_MAKEFLAGS) uninstall \ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ distuninstallcheck \ && chmod -R a-w "$$dc_install_base" \ && ({ \ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ } || { rm -rf "$$dc_destdir"; exit 1; }) \ && rm -rf "$$dc_destdir" \ && $(MAKE) $(AM_MAKEFLAGS) dist \ && rm -rf $(DIST_ARCHIVES) \ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ && cd "$$am__cwd" \ || exit 1 $(am__post_remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' distuninstallcheck: @test -n '$(distuninstallcheck_dir)' || { \ echo 'ERROR: trying to run $@ with an empty' \ '$$(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ $(am__cd) '$(distuninstallcheck_dir)' || { \ echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left after uninstall:" ; \ if test -n "$(DESTDIR)"; then \ echo " (check DESTDIR support)"; \ fi ; \ $(distuninstallcheck_listfiles) ; \ exit 1; } >&2 distcleancheck: distclean @if test '$(srcdir)' = . ; then \ echo "ERROR: distcleancheck can only run from a VPATH build" ; \ exit 1 ; \ fi @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left in build directory after distclean:" ; \ $(distcleancheck_listfiles) ; \ exit 1; } >&2 check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(MANS) $(DATA) installdirs: for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(configdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf ./$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-hdr distclean-libtool distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-configDATA install-man install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-binPROGRAMS install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-man1 install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache -rm -rf ./$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-binPROGRAMS uninstall-configDATA uninstall-man uninstall-man: uninstall-man1 .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am am--refresh check check-am clean \ clean-binPROGRAMS clean-cscope clean-generic clean-libtool \ cscope cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \ dist-gzip dist-lzip dist-shar dist-tarZ dist-xz dist-zip \ distcheck distclean distclean-compile distclean-generic \ distclean-hdr distclean-libtool distclean-tags distcleancheck \ distdir distuninstallcheck dvi dvi-am html html-am info \ info-am install install-am install-binPROGRAMS \ install-configDATA install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-man1 install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-binPROGRAMS uninstall-configDATA \ uninstall-man uninstall-man1 spine.1: $(bin_PROGRAMS) $(HELP2MAN) --output=$@ --name='Data Collector for Cacti' --no-info --version-option='--version' ./spine # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: cacti-spine-1.2.30/error.h0000664000175000017500000000464714770060157014344 0ustar markvmarkv/* ex: set tabstop=4 shiftwidth=4 autoindent: +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 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, implimentation details) | +-------------------------------------------------------------------------+ | - Cacti - http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ extern void install_spine_signal_handler(void); extern void uninstall_spine_signal_handler(void); #ifdef HAVE_EXECINFO_H extern char ** backtrace_symbols(); extern int backtrace(); #endif cacti-spine-1.2.30/php.c0000664000175000017500000004400314770060157013763 0ustar markvmarkv/* ex: set tabstop=4 shiftwidth=4 autoindent: +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 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, implimentation details) | +-------------------------------------------------------------------------+ | - Cacti - http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ #include "common.h" #include "spine.h" /*! \fn char *php_cmd(const char *php_command, int php_process) * \brief calls the script server and executes a script command * \param php_command the formatted php script server command * \param php_process the php script server process to call * * This function is called directly by the spine poller when a script server * request has been initiated for a host. It will place the PHP Script Server * command on it's output pipe and then wait the pre-defined timeout period for * a response on the PHP Script Servers output pipe. * * \return pointer to the string results. Must be freed by the parent. * */ char *php_cmd(const char *php_command, int php_process) { char *result_string; char command[BUFSIZE]; ssize_t bytes; int retries = 0; assert(php_command != 0); /* pad command with CR-LF */ snprintf(command, BUFSIZE, "%s\r\n", php_command); /* place lock around mutex */ switch (php_process) { case 0: thread_mutex_lock(LOCK_PHP_PROC_0); break; case 1: thread_mutex_lock(LOCK_PHP_PROC_1); break; case 2: thread_mutex_lock(LOCK_PHP_PROC_2); break; case 3: thread_mutex_lock(LOCK_PHP_PROC_3); break; case 4: thread_mutex_lock(LOCK_PHP_PROC_4); break; case 5: thread_mutex_lock(LOCK_PHP_PROC_5); break; case 6: thread_mutex_lock(LOCK_PHP_PROC_6); break; case 7: thread_mutex_lock(LOCK_PHP_PROC_7); break; case 8: thread_mutex_lock(LOCK_PHP_PROC_8); break; case 9: thread_mutex_lock(LOCK_PHP_PROC_9); break; case 10: thread_mutex_lock(LOCK_PHP_PROC_10); break; case 11: thread_mutex_lock(LOCK_PHP_PROC_11); break; case 12: thread_mutex_lock(LOCK_PHP_PROC_12); break; case 13: thread_mutex_lock(LOCK_PHP_PROC_13); break; case 14: thread_mutex_lock(LOCK_PHP_PROC_14); break; } /* send command to the script server */ retry: bytes = write(php_processes[php_process].php_write_fd, command, strlen(command)); /* if write status is <= 0 then the script server may be hung */ if (bytes <= 0) { result_string = strdup("U"); SPINE_LOG(("ERROR: SS[%i] PHP Script Server communications lost sending Command[%s]. Restarting PHP Script Server", php_process, command)); php_close(php_process); php_init(php_process); /* increment and retry a few times on the next item */ retries++; if (retries < 3) { goto retry; } } else { /* read the result from the php_command */ result_string = php_readpipe(php_process, command); /* check for a null */ if (!strlen(result_string)) { SET_UNDEFINED(result_string); } } /* unlock around php process */ switch (php_process) { case 0: thread_mutex_unlock(LOCK_PHP_PROC_0); break; case 1: thread_mutex_unlock(LOCK_PHP_PROC_1); break; case 2: thread_mutex_unlock(LOCK_PHP_PROC_2); break; case 3: thread_mutex_unlock(LOCK_PHP_PROC_3); break; case 4: thread_mutex_unlock(LOCK_PHP_PROC_4); break; case 5: thread_mutex_unlock(LOCK_PHP_PROC_5); break; case 6: thread_mutex_unlock(LOCK_PHP_PROC_6); break; case 7: thread_mutex_unlock(LOCK_PHP_PROC_7); break; case 8: thread_mutex_unlock(LOCK_PHP_PROC_8); break; case 9: thread_mutex_unlock(LOCK_PHP_PROC_9); break; case 10: thread_mutex_unlock(LOCK_PHP_PROC_10); break; case 11: thread_mutex_unlock(LOCK_PHP_PROC_11); break; case 12: thread_mutex_unlock(LOCK_PHP_PROC_12); break; case 13: thread_mutex_unlock(LOCK_PHP_PROC_13); break; case 14: thread_mutex_unlock(LOCK_PHP_PROC_14); break; } return result_string; } /*! \fn in php_get_process() * \brief returns the next php script server process to utilize * * This very simple function simply returns the next PHP Script Server * process id to poll using a round robin algorithm. * * \return the integer number of the next script server to use * */ int php_get_process(void) { int i; thread_mutex_lock(LOCK_PHP); if (set.php_current_server >= set.php_servers) { set.php_current_server = 0; } i = set.php_current_server; set.php_current_server++; thread_mutex_unlock(LOCK_PHP); return i; } /*! \fn char *php_readpipe(int php_process, char *command) * \brief read a line from a PHP Script Server process * \param php_process the PHP Script Server process to obtain output from * * This function will read the output pipe from the PHP Script Server process * and return that string to the Spine thread requesting the output. If for * some reason the PHP Script Server process does not respond in time, it will * be closed using the php_close function, then restarted. * * \return a string pointer to the PHP Script Server response */ char *php_readpipe(int php_process, char *command) { fd_set fds; struct timeval timeout; double begin_time = 0; double end_time = 0; double remaining_usec = 0; char *result_string; int i; char *cp; char *bptr; if (!(result_string = (char *)malloc(RESULTS_BUFFER))) { die("ERROR: Fatal malloc error: php.c php_readpipe!"); } result_string[0] = '\0'; /* record start time */ begin_time = get_time_as_double(); /* establish timeout value for the PHP script server to respond */ timeout.tv_sec = set.script_timeout; timeout.tv_usec = 0; /* check to see which pipe talked and take action * should only be the READ pipe */ retry: /* initialize file descriptors to review for input/output */ FD_ZERO(&fds); FD_SET(php_processes[php_process].php_read_fd,&fds); switch (select(php_processes[php_process].php_read_fd+1, &fds, NULL, NULL, &timeout)) { case -1: switch (errno) { case EBADF: SPINE_LOG(("ERROR: SS[%i] An invalid file descriptor was given in one of the sets.", php_process)); break; case EINTR: #ifndef SOLAR_THREAD /* take a moment */ usleep(2000); #endif /* record end time */ end_time = get_time_as_double(); /* re-establish new timeout value */ timeout.tv_sec = rint(floor(set.script_timeout-(end_time-begin_time))); remaining_usec = set.script_timeout - timeout.tv_sec - (end_time - begin_time); if (remaining_usec > 0) { timeout.tv_usec = rint(remaining_usec * 1000000); } else { timeout.tv_usec = 0; } if (timeout.tv_sec + timeout.tv_usec > 0) { goto retry; } else { SPINE_LOG(("WARNING: SS[%i] The Script Server script timed out while processing EINTR's.", php_process)); } break; case EINVAL: SPINE_LOG(("ERROR: SS[%i] N is negative or the value contained within timeout is invalid.", php_process)); break; case ENOMEM: SPINE_LOG(("ERROR: SS[%i] Select was unable to allocate memory for internal tables.", php_process)); break; default: SPINE_LOG(("ERROR: SS[%i] Unknown fatal select() error", php_process)); break; } SET_UNDEFINED(result_string); /* kill script server because it is misbehaving */ php_close(php_process); php_init(php_process); break; case 0: /* record end time */ end_time = get_time_as_double(); SPINE_LOG(("WARNING: SS[%i] The PHP Script Server did not respond in time for Timeout[%0.2f], Command[%s] and will therefore be restarted", php_process, end_time - begin_time, command)); SET_UNDEFINED(result_string); /* kill script server because it is misbehaving */ php_close(php_process); php_init(php_process); break; default: if (FD_ISSET(php_processes[php_process].php_read_fd, &fds)) { bptr = result_string; while (1) { i = read(php_processes[php_process].php_read_fd, bptr, RESULTS_BUFFER-(bptr-result_string)); if (i <= 0) { SET_UNDEFINED(result_string); break; } bptr += i; *bptr = '\0'; /* make what we've got into a string */ if ((cp = strstr(result_string,"\n")) != 0) { break; } if (bptr >= result_string+BUFSIZE) { SPINE_LOG(("ERROR: SS[%i] The Script Server result was longer than the acceptable range", php_process)); SET_UNDEFINED(result_string); } } } else { SPINE_LOG(("ERROR: SS[%i] The FD was not set as expected", php_process)); SET_UNDEFINED(result_string); } php_processes[php_process].php_state = PHP_READY; } return result_string; } /*! \fn int php_init(int php_process) * \brief initialize either a specific PHP Script Server or all of them. * \param php_process the process number to start or PHP_INIT * * This function will either start an individual PHP Script Server process * or all of them if the input parameter is the PHP_INIT constant. The function * will check the status of the process to verify that it is ready to process * scripts as well. * * \return TRUE if the PHP Script Server is know running or FALSE otherwise */ int php_init(int php_process) { int cacti2php_pdes[2]; int php2cacti_pdes[2]; pid_t pid; char poller_id[TINY_BUFSIZE]; char mode[TINY_BUFSIZE]; char *argv[7]; int cancel_state; char *result_string = 0; int num_processes; int i; int retry_count = 0; char *command = strdup("INIT"); /* special code to start all PHP Servers */ if (php_process == PHP_INIT) { num_processes = set.php_servers; } else { num_processes = 1; } for (i=0; i < num_processes; i++) { SPINE_LOG_DEBUG(("DEBUG: SS[%i] PHP Script Server Routine Starting", i)); /* create the output pipes from Spine to php*/ if (pipe(cacti2php_pdes) < 0) { SPINE_LOG(("ERROR: SS[%i] Could not allocate php server pipes", i)); return FALSE; } /* create the input pipes from php to Spine */ if (pipe(php2cacti_pdes) < 0) { SPINE_LOG(("ERROR: SS[%i] Could not allocate php server pipes", i)); return FALSE; } /* disable thread cancellation from this point forward. */ pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state); /* establish arguments for script server execution */ if (set.cacti_version <= 1222) { argv[0] = set.path_php; argv[1] = "-q"; argv[2] = set.path_php_server; argv[3] = "spine"; snprintf(poller_id, TINY_BUFSIZE, "%d", set.poller_id); argv[4] = poller_id; argv[5] = NULL; } else if (set.poller_id > 1) { argv[0] = set.path_php; argv[1] = "-q"; argv[2] = set.path_php_server; argv[3] = "--environ=spine"; snprintf(poller_id, TINY_BUFSIZE, "--poller=%d", set.poller_id); argv[4] = poller_id; if (set.mode == REMOTE_ONLINE) { snprintf(mode, TINY_BUFSIZE, "--mode=online"); } else { snprintf(mode, TINY_BUFSIZE, "--mode=offline"); } argv[5] = mode; argv[6] = NULL; } else { argv[0] = set.path_php; argv[1] = "-q"; argv[2] = set.path_php_server; argv[3] = "--environ=spine"; snprintf(poller_id, TINY_BUFSIZE, "--poller=%d", set.poller_id); argv[4] = poller_id; argv[5] = NULL; } /* fork a child process */ SPINE_LOG_DEBUG(("DEBUG: SS[%i] PHP Script Server About to FORK Child Process", i)); retry: pid = vfork(); /* check the pid status and process as required */ switch (pid) { case -1: /* ERROR: Could not fork() */ switch (errno) { case EAGAIN: if (retry_count < 3) { retry_count++; #ifndef SOLAR_THREAD /* take a moment */ usleep(50000); #endif goto retry; } else { SPINE_LOG(("ERROR: SS[%i] Could not fork PHP Script Server Out of Resources", i)); } case ENOMEM: if (retry_count < 3) { retry_count++; #ifndef SOLAR_THREAD /* take a moment */ usleep(50000); #endif goto retry; } else { SPINE_LOG(("ERROR: SS[%i] Could not fork PHP Script Server Out of Memory", i)); } default: SPINE_LOG(("ERROR: SS[%i] Could not fork PHP Script Server Unknown Reason", i)); } close(php2cacti_pdes[0]); close(php2cacti_pdes[1]); close(cacti2php_pdes[0]); close(cacti2php_pdes[1]); SPINE_LOG(("ERROR: SS[%i] Could not fork PHP Script Server", i)); pthread_setcancelstate(cancel_state, NULL); return FALSE; /* NOTREACHED */ case 0: /* SUCCESS: I am now the child */ /* set the standard input/output channels of the new process. */ dup2(cacti2php_pdes[0], STDIN_FILENO); dup2(php2cacti_pdes[1], STDOUT_FILENO); /* close unneeded Pipes */ (void)close(php2cacti_pdes[0]); (void)close(php2cacti_pdes[1]); (void)close(cacti2php_pdes[0]); (void)close(cacti2php_pdes[1]); /* start the php script server process */ execv(argv[0], argv); _exit(127); /* NOTREACHED */ default: /* I am the parent process */ SPINE_LOG_DEBUG(("DEBUG: SS[%i] PHP Script Server Child FORK Success", i)); } /* Parent */ /* close unneeded pipes */ close(cacti2php_pdes[0]); close(php2cacti_pdes[1]); if (php_process == PHP_INIT) { php_processes[i].php_pid = pid; php_processes[i].php_write_fd = cacti2php_pdes[1]; php_processes[i].php_read_fd = php2cacti_pdes[0]; } else { php_processes[php_process].php_pid = pid; php_processes[php_process].php_write_fd = cacti2php_pdes[1]; php_processes[php_process].php_read_fd = php2cacti_pdes[0]; } /* restore caller's cancellation state. */ pthread_setcancelstate(cancel_state, NULL); /* check pipe to insure startup took place */ if (php_process == PHP_INIT) { result_string = php_readpipe(i, command); } else { result_string = php_readpipe(php_process, command); } if (strstr(result_string, "Started")) { if (php_process == PHP_INIT) { SPINE_LOG_DEBUG(("DEBUG: SS[%i] Confirmed PHP Script Server running using readfd[%i], writefd[%i]", i, php2cacti_pdes[0], cacti2php_pdes[1])); php_processes[i].php_state = PHP_READY; } else { SPINE_LOG_DEBUG(("DEBUG: SS[%i] Confirmed PHP Script Server running using readfd[%i], writefd[%i]", php_process, php2cacti_pdes[0], cacti2php_pdes[1])); php_processes[php_process].php_state = PHP_READY; } } else { if (php_process == PHP_INIT) { SPINE_LOG(("ERROR: SS[%i] Script Server did not start properly return message was: '%s'", i, result_string)); php_processes[i].php_state = PHP_BUSY; } else { SPINE_LOG(("ERROR: SS[%i] Script Server did not start properly return message was: '%s'", php_process, result_string)); php_processes[php_process].php_state = PHP_BUSY; } } free(result_string); } return TRUE; } /*! \fn void php_close(int php_process) * \brief close the php script server process * \param php_process the process to close or PHP_INIT * * This function will take an input parameter of either a specially coded * PHP_INIT parameter or an integer stating the process number. With that * information is will close and/or terminate the child PHP Script Server * process and then return to the calling function. * * TODO: Make ending of the child process not be reliant on SIG_TERM in cases * where the child process is hung for one reason or another. * */ void php_close(int php_process) { int i; int num_processes; ssize_t bytes; if (php_process == PHP_INIT) { num_processes = set.php_servers; } else { num_processes = 1; } for(i = 0; i < num_processes; i++) { php_t *phpp; SPINE_LOG_DEBUG(("DEBUG: SS[%i] Script Server Shutdown Started", i)); /* tell the script server to close */ if (php_process == PHP_INIT) { phpp = &php_processes[i]; } else { phpp = &php_processes[php_process]; } /* If we still have a valid write pipe, tell PHP to close down * by sending a "quit" message, then closing the input channel * so it gets an EOF. * * Then we wait a moment before actually killing it to allow for * a clean shutdown. */ if (phpp->php_write_fd >= 0) { static const char quit[] = "quit\r\n"; bytes = write(phpp->php_write_fd, quit, strlen(quit)); close(phpp->php_write_fd); phpp->php_write_fd = -1; /* wait before killing php */ #ifndef SOLAR_THREAD usleep(50000); /* 50 msec */ #endif } /* only try to kill the process if the PID looks valid. * Trying to kill a negative number is bad news (it's * a process group leader), and PID 1 is "init". */ if (phpp->php_pid > 1) { /* end the php script server process */ kill(phpp->php_pid, SIGTERM); /* reset this PID variable? */ } /* close file descriptors */ close(phpp->php_read_fd); phpp->php_read_fd = -1; } } cacti-spine-1.2.30/configure.ac0000664000175000017500000004126614770060157015326 0ustar markvmarkv# +-------------------------------------------------------------------------+ # | Copyright (C) 2004-2024 The Cacti Group | # | | # | 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. | # +-------------------------------------------------------------------------+ # | Cacti: The Complete RRDtool-based Graphing Solution | # +-------------------------------------------------------------------------+ # | This code is designed, written, and maintained by the Cacti Group. See | # | about.php and/or the AUTHORS file for specific developer information. | # +-------------------------------------------------------------------------+ # | http://www.cacti.net/ | # +-------------------------------------------------------------------------+ AC_PREREQ([2.63]) AC_INIT([Spine Poller],[1.2.30],[http://www.cacti.net/issues.php]) AC_CONFIG_AUX_DIR(config) AC_SUBST(ac_aux_dir) AC_CANONICAL_HOST AC_CONFIG_SRCDIR(spine.c) AC_PREFIX_DEFAULT(/usr/local/spine) AC_LANG(C) AC_PROG_CC AM_INIT_AUTOMAKE([foreign]) AC_CONFIG_HEADERS(config/config.h) # static libraries AC_ARG_WITH(static, AS_HELP_STRING([--with-static],[Build using static libraries ]), [CFLAGS="-static $CFLAGS"] ) AC_CONFIG_MACRO_DIR([m4]) # mysql AC_ARG_WITH(mysql, AS_HELP_STRING([--with-mysql],[MySQL base directory [[/usr/local/mysql]] ]), [MYSQL_DIR=$withval] ) # snmp AC_ARG_WITH(snmp, AS_HELP_STRING([--with-snmp],[SNMP base directory [[/usr/(local/)include]] ]), [SNMP_DIR=$withval] ) # reentrant MYSQL_REENTRANT=0 AC_ARG_WITH(reentrant, AS_HELP_STRING([--with-reentrant],[If using MySQL versions less than 5.5 you must choose this option ]), [MYSQL_REENTRANT=1] ) # if host_alias is empty, ac_cv_host_alias may still have the info if test -z "$host_alias"; then host_alias=$ac_cv_host_alias fi # Platform-specific tweaks ShLib="so" case $host_alias in *sparc-sun-solaris2.8) CPPFLAGS="$CPPFLAGS -D_POSIX_PTHREAD_SEMANTICS" AC_DEFINE(SOLAR_THREAD, 1, [Correct issue around Solaris threading model]);; *solaris*) CPPFLAGS="$CPPFLAGS -D_POSIX_PTHREAD_SEMANTICS";; *freebsd*) LIBS="$LIBS -pthread -lexecinfo" AC_DEFINE(HAVE_LIBPTHREAD, 1);; *darwin*) ShLib="dylib";; *) LIBS="-lpthread -lssl $LIBS" esac # Checks for programs. AC_PROG_AWK AC_PROG_CC AC_PROG_CPP AC_PROG_INSTALL AC_PROG_LN_S LT_INIT AC_MSG_CHECKING([whether to enable -Wall]) AC_ARG_ENABLE(warnings, [ --enable-warnings Enable -Wall if using gcc.], [if test -n "$GCC"; then AC_MSG_RESULT(adding -Wall to CFLAGS.) CFLAGS="$CFLAGS -Wall" fi ], AC_MSG_RESULT(no) ) AC_PATH_PROG(HELP2MAN, help2man, false // No help2man //) AC_CHECK_PROG([HELP2MAN], [help2man], [help2man]) AM_CONDITIONAL([HAVE_HELP2MAN], [test x$HELP2MAN = xhelp2man]) # Checks for libraries. AC_CHECK_LIB(socket, socket) AC_CHECK_LIB(m, floor) AC_CHECK_LIB(dl, dlclose) AC_CHECK_LIB(pthread, pthread_exit) # Some builds of MySQL require libz - try to detect AC_CHECK_LIB(z, deflate) AC_CHECK_LIB(kstat, kstat_close) AC_CHECK_LIB(crypto, CRYPTO_realloc) # minor adjustments for debian AC_SEARCH_LIBS([clock_gettime], [rt pthread]) # Checks for header files. AC_CHECK_HEADERS(sys/socket.h sys/select.h sys/wait.h sys/time.h) AC_CHECK_HEADERS(assert.h ctype.h errno.h signal.h math.h malloc.h netdb.h) AC_CHECK_HEADERS(signal.h stdarg.h stdio.h syslog.h) AC_CHECK_HEADERS( netinet/in_systm.h netinet/in.h netinet/ip.h netinet/ip_icmp.h, [], [], [#ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_NETINET_IN_SYSTM_H #include #endif #ifdef HAVE_NETINET_IP_H #include #endif] ) # Checks for typedefs, structures, and compiler characteristics. AC_CHECK_TYPES([unsigned long long, long long]) AC_C_CONST AC_TYPE_SIZE_T AC_HEADER_TIME AC_STRUCT_TM # Checks for library functions. AC_DIAGNOSE([obsolete],[your code may safely assume C89 semantics that RETSIGTYPE is void. Remove this warning and the `AC_CACHE_CHECK' when you adjust the code.])dnl AC_CACHE_CHECK([return type of signal handlers],[ac_cv_type_signal],[AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([#include #include ], [return *(signal (0, 0)) (0) == 1;])], [ac_cv_type_signal=int], [ac_cv_type_signal=void])]) AC_DEFINE_UNQUOTED([RETSIGTYPE],[$ac_cv_type_signal],[Define as the return type of signal handlers (`int' or `void').]) AC_CHECK_FUNCS(malloc calloc gettimeofday strerror strtoll) # ****************** Solaris Privileges Check *********************** # Check if usage of Solaris privileges support is possible AC_CHECK_HEADER(priv.h, [FOUND_PRIV_H=yes], [FOUND_PRIV_H=no]) # If we should use the Solaris privileges support AC_MSG_CHECKING(whether we are using Solaris privileges) AC_ARG_ENABLE(solaris-priv, [ --enable-solaris-priv Enable support for the Solaris process privilege model (default: disabled)], [ ENABLED_SOL_PRIV=$enableval ], [ ENABLED_SOL_PRIV=no ] ) if test x$ENABLED_SOL_PRIV != xno; then if test x$FOUND_PRIV_H != xno; then AC_MSG_RESULT([yes]) AC_DEFINE([SOLAR_PRIV], [1], [If Support for Solaris privileges should be enabled] ) else AC_MSG_RESULT([no]) fi else AC_MSG_RESULT([no]) fi # ****************** Linux Capabilities Check *********************** CAPLOC="sys/capability.h" for file in sys/capability.h;do test -f /usr/include/$file && CAPLOC=$file && break done AC_CHECK_HEADER($CAPLOC, [FOUND_SYS_CAPABILITY_H=yes], [FOUND_SYS_CAPABILITY_H=no]) # If we should use the Linux Capabilities support AC_MSG_CHECKING(whether we are using Linux Capabilities) AC_ARG_ENABLE(lcap, [ --enable-lcap Enable support for the Linux Capabilities (default: disabled)], [ ENABLED_LCAP=$enableval ], [ ENABLED_LCAP=no ] ) if test x$ENABLED_LCAP != xno; then if test x$FOUND_SYS_CAPABILITY_H != xno; then AC_MSG_RESULT([yes]) AC_CHECK_LIB(cap, cap_init, [ LIBS="-lcap $LIBS" AC_DEFINE(HAVE_LCAP, 1, Linux Capabilities) HAVE_LCAP=yes ], [ AC_MSG_RESULT(Cannot find Linux Capabilities library(cap)...) HAVE_LCAP=no ] ) else AC_MSG_RESULT([no]) fi else AC_MSG_RESULT([no]) fi # ****************** MySQL Checks *********************** AC_DEFUN([MYSQL_LIB_CHK], [ str="$1/libmysqlclient.*" for j in `echo $str`; do if test -r $j; then MYSQL_LIB_DIR=$1 break 2 fi done ] ) # Determine MySQL installation paths MYSQL_SUB_DIR="include include/mysql include/mariadb mysql"; for i in $MYSQL_DIR /usr /usr/local /opt /opt/mysql /usr/pkg /usr/local/mysql; do for d in $MYSQL_SUB_DIR; do if [[ -f $i/$d/mysql.h ]]; then MYSQL_INC_DIR=$i/$d break; fi done if [[ ! -z $MYSQL_INC_DIR ]]; then break; fi # test -f $i/include/mysql.h && MYSQL_INC_DIR=$i/include && break # test -f $i/include/mysql/mysql.h && MYSQL_INC_DIR=$i/include/mysql && break # test -f $i/include/mariadb/mysql.h && MYSQL_INC_DIR=$i/include/mariadb && break # test -f $i/mysql/include/mysql.h && MYSQL_INC_DIR=$i/mysql/include && break done if test -z "$MYSQL_INC_DIR"; then if test "x$MYSQL_DIR" != "x"; then AC_MSG_ERROR(Cannot find MySQL header files under $MYSQL_DIR) else AC_MSG_ERROR(Cannot find MySQL headers. Use --with-mysql= to specify non-default path.) fi fi for i in $MYSQL_DIR /usr /usr/local /opt /opt/mysql /usr/pkg /usr/local/mysql; do MYSQL_LIB_CHK($i/lib64) MYSQL_LIB_CHK($i/lib64/mysql) MYSQL_LIB_CHK($i/lib/x86_64-linux-gnu) MYSQL_LIB_CHK($i/lib/x86_64-linux-gnu/mysql) MYSQL_LIB_CHK($i/lib) MYSQL_LIB_CHK($i/lib/mysql) done if test -n "$MYSQL_LIB_DIR" ; then LDFLAGS="-L$MYSQL_LIB_DIR $LDFLAGS" fi CFLAGS="-I$MYSQL_INC_DIR $CFLAGS" unamestr=$(uname) if test $unamestr == 'OpenBSD'; then AC_CHECK_LIB(mysqlclient, mysql_init, [ LIBS="-lmysqlclient -lexecinfo -lm $LIBS" AC_DEFINE(HAVE_MYSQL, 1, MySQL Client API) HAVE_MYSQL=yes ], [ HAVE_MYSQL=no ] ) else AC_CHECK_LIB(mysqlclient, mysql_init, [ LIBS="-lmysqlclient -lm -ldl $LIBS" AC_DEFINE(HAVE_MYSQL, 1, MySQL Client API) HAVE_MYSQL=yes ], [ HAVE_MYSQL=no ] ) fi if test $MYSQL_REENTRANT = 1 ; then LIBS="-lmysqlclient_r -lm -ldl $LIBS" else if test -f $MYSQL_LIB_DIR/libmysqlclient_r.a -o -f $MYSQL_LIB_DIR/libmysqlclient_r.$ShLib; then LIBS="-lmysqlclient_r -lm -ldl $LIBS" else if test -f $MYSQL_LIB_DIR/libmysqlclient_r.a -o -f $MYSQL_LIB_DIR/libmysqlclient_r.$ShLib ; then LIBS="-lmysqlclient_r -lm -ldl $LIBS" else if test "$HAVE_MYSQL" == "yes"; then if test $unamestr == 'OpenBSD'; then LIBS="-lmysqlclient -lm $LIBS" else LIBS="-lmysqlclient -lm -ldl $LIBS" fi else if test -f $MYSQL_LIB_DIR/libperconaserverclient.a -o -f $MYSQL_LIB_DIR/libperconaserverclient.$ShLib; then LIBS="-lperconaserverclient -lm -ldl $LIBS" else LIBS="-lmariadbclient -lm -ldl $LIBS" fi fi fi fi fi # ****************** Net-SNMP Checks *********************** if test "x$SNMP_DIR" != "x"; then for i in / /net-snmp /include/net-snmp; do test -f $SNMP_DIR/$i/net-snmp-config.h && SNMP_INCDIR=$SNMP_DIR$i && break done # Accomodate 64-Bit Libraries test -f $SNMP_DIR/lib64/libnetsnmp.a -o -f $SNMP_DIR/lib64/libnetsnmp.$ShLib && SNMP_LIBDIR=$SNMP_DIR/lib64 if test -z "$SNMP_LIBDIR"; then # Accomodate 32-Bit Libraries test -f $SNMP_DIR/lib/libnetsnmp.a -o -f $SNMP_DIR/lib/libnetsnmp.$ShLib && SNMP_LIBDIR=$SNMP_DIR/lib fi else for i in /usr /usr/local /usr/include /usr/pkg/include /usr/local/include /opt /opt/net-snmp /opt/snmp; do test -f $i/snmp.h && SNMP_INCDIR=$i && break test -f $i/include/net-snmp/net-snmp-config.h && SNMP_INCDIR=$i/include/net-snmp && break test -f $i/net-snmp/net-snmp-config.h && SNMP_INCDIR=$i/net-snmp && break test -f $i/net-snmp/include/net-snmp-config.h && SNMP_INCDIR=$i/net-snmp/include && break test -f $i/snmp/snmp.h && SNMP_INCDIR=$i/snmp && break test -f $i/snmp/include/net-snmp/net-snmp-config.h && SNMP_INCDIR=$i/snmp/include/net-snmp && break done # Accomodate 64-Bit Libraries for i in /usr /usr/local /usr/pkg /usr/snmp /opt /opt/net-snmp /opt/snmp /usr/local/snmp; do test -f $i/lib64/libnetsnmp.a -o -f $i/lib64/libnetsnmp.$ShLib && SNMP_LIBDIR=$i/lib64 && break done # Only check for 32 Bit libraries if the 64 bit are not found if test -z "$SNMP_LIBDIR"; then # Accomodate 32-Bit Libraries for i in /usr /usr/local /usr/pkg /usr/snmp /opt /opt/net-snmp /opt/snmp /usr/local/snmp; do test -f $i/lib/libnetsnmp.a -o -f $i/lib/libnetsnmp.$ShLib && SNMP_LIBDIR=$i/lib && break done fi fi if test -z "$SNMP_INCDIR"; then if test "x$SNMP_DIR" != "x";then AC_MSG_ERROR(Cannot find SNMP header files under $SNMP_DIR) else AC_MSG_ERROR(Cannot find SNMP headers. Use --with-snmp= to specify non-default path.) fi fi if test -n "$SNMP_LIBDIR" ; then LDFLAGS="-L$SNMP_LIBDIR $LDFLAGS" fi if test -n "$SNMP_INCDIR" ; then CFLAGS="-I$SNMP_INCDIR -I$SNMP_INCDIR/.. $CFLAGS" fi # Net-SNMP includes v3 support and insists on crypto unless compiled --without-openssl AC_MSG_CHECKING([if Net-SNMP needs crypto support]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], [[exit(NETSNMP_USE_OPENSSL != 1);]])],[ AC_MSG_RESULT(yes) SNMP_SSL=yes ],[AC_MSG_RESULT(no) ]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #include #include #include ]], [[struct snmp_session session; snmp_sess_init(&session); session.localname = strdup("hello")]])],[havelocalname=1],[havelocalname=0 ]) AC_DEFINE_UNQUOTED(SNMP_LOCALNAME, $havelocalname, If snmp localname session structure member exists) AC_CHECK_LIB(netsnmp, snmp_timeout) # ****************** Spine Result Buffer Check *********************** # Check for the default spine output buffer size results_buffer=2048 AC_ARG_WITH(results-buffer, AS_HELP_STRING([--with-results-buffer=N],[The size of the spine results buffer (default=2048)]), [results_buffer=$withval] ) AC_DEFINE_UNQUOTED(RESULTS_BUFFER, $results_buffer, The size of the spine result buffer) AC_MSG_RESULT(checking for the spine results buffer size... $results_buffer bytes) # ****************** Maximum Simultaneous Scripts *********************** # Check for the most scripts that can be active at one time per spine process max_scripts=20 AC_ARG_WITH(max-scripts, AS_HELP_STRING([--with-max-scripts=N],[The maximum simultaneous spine scripts that can run (default=20)]), [max_scripts=$withval] ) AC_DEFINE_UNQUOTED(MAX_SIMULTANEOUS_SCRIPTS, $max_scripts, The maximum number of simultaneous running scripts) AC_MSG_RESULT(checking for the maximum simultaneous spine scripts... $max_scripts) # ****************** Maximum MySQL Buffer Size *********************** # Check for the most scripts that can be active at one time per spine process max_mysql_buffer=131072 AC_ARG_WITH(max-mysql-buffer, AS_HELP_STRING([--with-max-mysql-buffer=N],[The maximum SQL insert size allowed (default=131072)]), [max_mysql_buffer=$withval] ) AC_DEFINE_UNQUOTED(MAX_MYSQL_BUF_SIZE, $max_mysql_buffer, The maximum MySQL buffer size to insert) AC_MSG_RESULT(checking for the maximum MySQL buffer size... $max_mysql_buffer) # ****************** Traditional Popen Check *********************** # If we should use the system popen or nifty popen AC_MSG_CHECKING(whether we are using traditional popen) AC_ARG_ENABLE(popen, [ --enable-popen Enable the traditional popen implementation of nifty popen (default: disabled)], [ ENABLED_TPOPEN=$enableval ], [ ENABLED_TPOPEN=no ] ) if test "$ENABLED_TPOPEN" = "yes"; then AC_MSG_RESULT([yes]) AC_DEFINE(USING_TPOPEN, 1, If traditional popen should be enabled by default) else AC_MSG_RESULT([no]) fi # ****************** Force Net-SNMP Version Checks *********************** # If we should use the system popen or nifty popen AC_MSG_CHECKING(whether to verify net-snmp library vs header versions) AC_ARG_ENABLE(strict-snmp, [ --enable-strict-snmp Enable checking of Net-SNMP library vs header versions (default: disabled)], [ ENABLED_SNMP_VERSION=$enableval ], [ ENABLED_SNMP_VERSION=no ] ) if test "$ENABLED_SNMP_VERSION" = "yes"; then AC_MSG_RESULT([yes]) AC_DEFINE(VERIFY_PACKAGE_VERSION, 1, If we are going to force Net-SNMP library and header versons to be the same) else AC_MSG_RESULT([no]) fi # See if we can support backtracing AC_MSG_CHECKING([if we can support backtracing]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ void *array[10]; size_t size; // get void*'s for all entries on the stack size = backtrace(array, 10); if (size) { exit(0); } else { exit(1); } ]])],[ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_EXECINFO_H,1,[Do we have backtracing capabilities?]) ],[AC_MSG_RESULT(no) ]) # See if we can support backtracing AC_MSG_CHECKING([if we can support mysql/mariadb retry count]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include "$MYSQL_INC_DIR/mysql.h" ]], [[ if (MYSQL_OPT_RETRY_COUNT) { exit(0); } else { exit(1); } ]])],[ AC_MSG_RESULT(yes) AC_DEFINE(HAS_MYSQL_OPT_RETRY_COUNT,1,[Do we have mysql/maraidb retry count capabilities?]) ],[AC_MSG_RESULT(no) ]) # See if we can support backtracing AC_MSG_CHECKING([if we can support mysql/mariadb ssl keys]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include "$MYSQL_INC_DIR/mysql.h" ]], [[ if (MYSQL_OPT_SSL_KEY) { exit(0); } else { exit(1); } ]])],[ AC_MSG_RESULT(yes) AC_DEFINE(HAS_MYSQL_OPT_SSL_KEY,1,[Do we have mysql/maraidb ssl keys capabilities?]) ],[AC_MSG_RESULT(no) ]) AC_CONFIG_FILES([Makefile]) AC_OUTPUT cacti-spine-1.2.30/README.md0000664000175000017500000001016614770060157014312 0ustar markvmarkv# Spine: a poller for Cacti Spine is a high speed poller replacement for `cmd.php`. It is almost 100% compatible with the legacy cmd.php processor and provides much more flexibility, speed and concurrency than `cmd.php`. Make sure that you have the proper development environment to compile Spine. This includes compilers, header files and things such as libtool. If you have questions please consult the forums and/or online documentation. ----------------------------------------------------------------------------- ## Unix Installation These instructions assume the default install location for spine of `/usr/local/spine`. If you choose to use another prefix, make sure you update the commands as required for that new path. To compile and install Spine using MySQL versions 5.5 or higher please do the following: ```shell ./bootstrap ./configure make make install chown root:root /usr/local/spine/bin/spine chmod u+s /usr/local/spine/bin/spine ``` To compile and install Spine using MySQL versions previous to 5.5 please do the following: ```shell ./bootstrap ./configure --with-reentrant make make install chown root:root /usr/local/spine/bin/spine chmod +s /usr/local/spine/bin/spine ``` ## Windows Installation ### CYGWIN Prerequisite 1. Download Cygwin for Window from [https://www.cygwin.com/](https://www.cygwin.com/) 2. Install Cygwin by executing the downloaded setup program 3. Select _Install from Internet_ 4. Select Root Directory: _C:\cygwin_ 5. Select a mirror which is close to your location 6. Once on the package selection section make sure to select the following (TIP: use the search!): * autoconf * automake * dos2unix * gcc-core * gzip * help2man * inetutils-src * libmysqlclient * libmariadb-devel * libssl-devel * libtool * m4 * make * net-snmp-devel * openssl-devel * wget 7. Wait for installation to complete, coffee time! 8. Move the cygwin setup to the C:\cygwin\ folder for future usage. ### Compile Spine 1. Open Cygwin shell prompt (C:\Cygwin\cygwin.bat) and brace yourself to use unix commands on Windows. 2. Download the Spine source to the current directory: [http://www.cacti.net/spine_download.php](http://www.cacti.net/spine_download.php) 3. Extract Spine into C:\Cygwin\usr\src\: `tar xzvf cacti-spine-*.tar.gz` 4. Change into the Spine directory: `cd /usr/src/cacti-spine-*` 5. Run bootstrap to prepare Spine for compilation: `./bootstrap` 6. Follow the instruction which bootstrap outputs. 7. Update the spine.conf file for your installation of Cacti. You can optionally move it to a better location if you choose to do so, make sure to copy the spine.conf as well. 8. Ensure that Spine runs well by running with `/usr/local/spine/spine -R -S -V 3` 9. Update Cacti `Paths` Setting to point to the Spine binary and update the `Poller Type` to Spine. For the spine binary on Windows x64, and using default locations, that would be `C:\cygwin64\usr\local\spine\bin\spine.exe` 10. If all is good Spine will be run from the poller in place of cmd.php. ## Known Issues 1. On Windows, Microsoft does not support a TCP Socket send timeout. Therefore, if you are using TCP ping on Windows, spine will not perform a second or subsequent retries to connect and the host will be assumed down on the first failure. If this is a problem it is suggested to use another Availability/Reachability method, or moving to Linux/UNIX. 2. Spine takes quite a few MySQL connections. The number of connections is calculated as follows: (1 for main poller + 1 per each thread + 1 per each script server) Therefore, if you have 4 processes, with 10 threads each, and 5 script servers each your spine will take approximately: `total connections = 4 * ( 1 + 10 + 5 ) = 64` 3. On older MySQL versions, different libraries had to be used to make MySQL thread safe. MySQL versions 5.0 and 5.1 require this flag. If you are using these version of MySQL, you must use the --with-reentrant configure flag. ----------------------------------------------------------------------------- Copyright (c) 2004-2024 - The Cacti Group, Inc. cacti-spine-1.2.30/common.h0000664000175000017500000001134114770060157014470 0ustar markvmarkv/* ex: set tabstop=4 shiftwidth=4 autoindent: +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 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, implimentation details) | +-------------------------------------------------------------------------+ | - Cacti - http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ #ifndef SPINE_COMMON_H #define SPINE_COMMON_H 1 #ifdef __CYGWIN__ /* We use a Unix API, so pretend it's not Windows */ #undef WIN #undef WIN32 #undef _WIN #undef _WIN32 #undef _WIN64 #undef __WIN__ #undef __WIN32__ #define HAVE_ERRNO_AS_DEFINE /* Cygwin supports only 64 open file descriptors, let's increase it a bit. */ #define FD_SETSIZE 512 #endif /* __CYGWIN__ */ #define _THREAD_SAFE #define _PTHREADS #define _P __P #ifndef _REENTRANT #define _REENTRANT #endif #ifndef _LIBC_REENTRANT #define _LIBC_REENTRANT #endif #define PTHREAD_MUTEXATTR_DEFAULT ((pthread_mutexattr_t *) 0) #include "config/config.h" #if STDC_HEADERS # include # include #elif HAVE_STRINGS_H # include #endif /*STDC_HEADERS*/ #if HAVE_UNISTD_H # include # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_STDINT_H # include #endif #if HAVE_NETINET_IN_H # include # include # include # include #ifndef __CYGWIN__ # include #endif # include #endif #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #ifndef HAVE_LIBPTHREAD # define HAVE_LIBPTHREAD 0 #else # include #endif #ifdef SOLAR_PRIV # include #endif #undef PACKAGE_NAME #undef PACKAGE_VERSION #undef PACKAGE_BUGREPORT #undef PACKAGE_STRING #undef PACKAGE_TARNAME #include #include #include #include #include #include #include //#include #include #include #include #include #include #include #include #ifdef HAVE_LCAP # include # include # include #endif #endif /* SPINE_COMMON_H */ cacti-spine-1.2.30/util.h0000664000175000017500000001045414770060157014161 0ustar markvmarkv/* ex: set tabstop=4 shiftwidth=4 autoindent:* +-------------------------------------------------------------------------+ | Copyright (C) 2004-2024 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, implimentation details) | +-------------------------------------------------------------------------+ | - Cacti - http://www.cacti.net/ | +-------------------------------------------------------------------------+ */ /* cacti config reading functions */ extern void read_config_options(void); extern int read_spine_config(char *file); extern void config_defaults(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_numeric(char *string); extern int is_ipaddress(const char *string); extern int all_digits(const char *str); extern int is_hexadecimal(const char * str, const short ignore_special); /* determine if a device is a debug device */ extern int is_debug_device(int device_id); /* string and file functions */ extern char *add_slashes(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 char *trim(char *str); extern char *rtrim(char *str); extern char *ltrim(char *str); extern char *reverse(char *str); extern int strpos(char *haystack, char *needle) ; extern int char_count(const char *str, int chr); /* custom hex2dec that returns a string instead of a number */ unsigned long long hex2dec(char *str); /* macro to copy string to string with an ending null */ #define STRNCOPY(dst, src) strncopy((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 int hasCaps(); /* see if we can do things as root */ extern void checkAsRoot(); /* log format */ extern char *get_date_format(); /* start time for spine */ extern double start_time; /* the version of Cacti as a decimal */ int get_cacti_version(MYSQL *psql, int mode); cacti-spine-1.2.30/bootstrap0000775000175000017500000001021714770060157014773 0ustar markvmarkv#!/bin/sh # +-------------------------------------------------------------------------+ # | Copyright (C) 2004-2024 The Cacti Group | # | | # | 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. | # +-------------------------------------------------------------------------+ # | Cacti: The Complete RRDtool-based Graphing Solution | # +-------------------------------------------------------------------------+ # | This code is designed, written, and maintained by the Cacti Group. See | # | about.php and/or the AUTHORS file for specific developer information. | # +-------------------------------------------------------------------------+ # | http://www.cacti.net/ | # +-------------------------------------------------------------------------+ # # ---------------------------------------------------------- # Name: bootstrap # # Function: build spine from scratch # # Description: This script will take a vanilla Spine source # package and attempt to compile it. It will # attempt to handle nasty things like dos2unix # issues in all files and searching for the # presence of required modules. # # It is not a replacement for the auto tools, # but simply a supplement. # # ---------------------------------------------------------- # Help function display_help () { echo "--------------------------------------------------------------" echo "Spine bootstrap script" echo " Attempts to configure spine based on a 'normal' system. If you" echo " install things in non-common locations you may have to use" echo " the install instructions to build." echo "--------------------------------------------------------------" echo } # Check for parameters if [ "${1}" = "--help" -o "${1}" = "-h" ]; then display_help exit 0 fi echo "INFO: Starting Spine build process" # Remove software build specific directories echo "INFO: Removing cache directories" rm -rf autom4te.cache .deps # Make sure all files are unix formatted files which dos2unix > /dev/null 2>&1 if [ $? -eq 0 ]; then for e in $(echo "ac am c h in md mdlrc rb sh yml"); do echo "INFO: Ensuring UNIX format for *.$e" find . -type f -name \*.$e -exec dos2unix --d2u \{\} \; > /dev/null 2>&1 done fi # Prepare a build state echo "INFO: Running auto-tools to verify buildability" aclocal --install libtoolize autoheader automake --add-missing autoreconf --force --install [ $? -ne 0 ] && echo "ERROR: 'autoreconf' exited with errors" && exit -1 # Provide some meaningful notes echo "INFO: Spine bootstrap process completed" echo "" echo " These instructions assume the default install location for spine" echo " of /usr/local/spine. If you choose to use another prefix, make" echo " sure you update the commands as required for that new path." echo "" echo " To compile and install Spine using MySQL versions 5.5 or higher" echo " please do the following:" echo "" echo " ./configure" echo " make" echo " make install" echo " chown root:root /usr/local/spine/bin/spine" echo " chmod +s /usr/local/spine/bin/spine" echo "" echo " To compile and install Spine using MySQL versions previous to 5.5" echo " please do the following:" echo "" echo " ./configure --with-reentrant" echo " make" echo " make install" echo " chown root:root /usr/local/spine/bin/spine" echo " chmod +s /usr/local/spine/bin/spine" echo "" exit 0