├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .mdl_style.rb ├── .mdlrc ├── CHANGELOG ├── INSTALL ├── LICENSE ├── Makefile.am ├── Makefile.in ├── README.md ├── bootstrap ├── common.h ├── configure.ac ├── copyright_year.sh ├── debug ├── error.c ├── error.h ├── keywords.c ├── keywords.h ├── locks.c ├── locks.h ├── nft_popen.c ├── nft_popen.h ├── package ├── php.c ├── php.h ├── ping.c ├── ping.h ├── poller.c ├── poller.h ├── snmp.c ├── snmp.h ├── spine.c ├── spine.conf.dist ├── spine.h ├── sql.c ├── sql.h ├── util.c └── util.h /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 14 | **Describe the bug** 15 | A clear and concise description of what the bug is. 16 | 17 | **To Reproduce** 18 | Steps to reproduce the behavior: 19 | 1. Go to '...' 20 | 2. Click on '....' 21 | 3. Scroll down to '....' 22 | 4. See error 23 | 24 | **Expected behavior** 25 | A clear and concise description of what you expected to happen. 26 | 27 | **Screenshots** 28 | If applicable, add screenshots to help explain your problem. 29 | 30 | **Server (please complete the following information):** 31 | - OS: [e.g. CentOS 7] 32 | - Version [e.g. 22] 33 | 34 | **Compiling (please complete the following information):** 35 | 36 | - compiler: [e.g. clang or gcc 5.4.0] 37 | - autoconf: [e.g. autoconf 2.69] 38 | - glibc: [e.g. 2.23] 39 | - source: [e.g. release or github] 40 | 41 | *Note: you can find glibc version by running `ldd --version`* 42 | 43 | *Note: if source is github, please include last commit reference* 44 | 45 | **Additional context** 46 | Add any other context about the problem here including logs and command line output 47 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 14 | **Is your feature request related to a problem? Please describe.** 15 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 16 | 17 | **Describe the solution you'd like** 18 | A clear and concise description of what you want to happen. 19 | 20 | **Describe alternatives you've considered** 21 | A clear and concise description of any alternative solutions or features you've considered. 22 | 23 | **Additional context** 24 | Add any other context or screenshots about the feature request here. 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # +-------------------------------------------------------------------------+ 2 | # | Copyright (C) 2004-2024 The Cacti Group | 3 | # | | 4 | # | This program is free software; you can redistribute it and/or | 5 | # | modify it under the terms of the GNU General Public License | 6 | # | as published by the Free Software Foundation; either version 2 | 7 | # | of the License, or (at your option) any later version. | 8 | # | | 9 | # | This program is distributed in the hope that it will be useful, | 10 | # | but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | # | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 12 | # | GNU General Public License for more details. | 13 | # +-------------------------------------------------------------------------+ 14 | # | Cacti: The Complete RRDtool-based Graphing Solution | 15 | # +-------------------------------------------------------------------------+ 16 | # | This code is designed, written, and maintained by the Cacti Group. See | 17 | # | about.php and/or the AUTHORS file for specific developer information. | 18 | # +-------------------------------------------------------------------------+ 19 | # | http://www.cacti.net/ | 20 | # +-------------------------------------------------------------------------+ 21 | 22 | .deps 23 | Makefile 24 | Makefile.in 25 | configure 26 | config.* 27 | autom4te.cache/ 28 | config/ 29 | libtool 30 | spine 31 | spine.1 32 | *.o 33 | *.m4 34 | -------------------------------------------------------------------------------- /.mdl_style.rb: -------------------------------------------------------------------------------- 1 | # +-------------------------------------------------------------------------+ 2 | # | Copyright (C) 2004-2024 The Cacti Group | 3 | # | | 4 | # | This program is free software; you can redistribute it and/or | 5 | # | modify it under the terms of the GNU General Public License | 6 | # | as published by the Free Software Foundation; either version 2 | 7 | # | of the License, or (at your option) any later version. | 8 | # | | 9 | # | This program is distributed in the hope that it will be useful, | 10 | # | but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | # | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 12 | # | GNU General Public License for more details. | 13 | # +-------------------------------------------------------------------------+ 14 | # | Cacti: The Complete RRDtool-based Graphing Solution | 15 | # +-------------------------------------------------------------------------+ 16 | # | This code is designed, written, and maintained by the Cacti Group. See | 17 | # | about.php and/or the AUTHORS file for specific developer information. | 18 | # +-------------------------------------------------------------------------+ 19 | # | http://www.cacti.net/ | 20 | # +-------------------------------------------------------------------------+ 21 | 22 | # customize style guide 23 | all 24 | rule "MD013", code_blocks: false, tables: false 25 | rule "MD029", style: "ordered" 26 | rule "MD046", style: "fenced" 27 | 28 | # Lesser rules 29 | exclude_rule "MD010" # hard tabs 30 | exclude_rule "MD013" # line length 31 | 32 | # Rule Exclusions 33 | exclude_rule "MD001" # Headers are useful in other ways 34 | exclude_rule "MD024" # Headers with same name are useful, but break link labeling (Rework needed on affected files before enabling this rule) 35 | exclude_rule "MD046" # seems broken 36 | -------------------------------------------------------------------------------- /.mdlrc: -------------------------------------------------------------------------------- 1 | # +-------------------------------------------------------------------------+ 2 | # | Copyright (C) 2004-2023 The Cacti Group | 3 | # | | 4 | # | This program is free software; you can redistribute it and/or | 5 | # | modify it under the terms of the GNU General Public License | 6 | # | as published by the Free Software Foundation; either version 2 | 7 | # | of the License, or (at your option) any later version. | 8 | # | | 9 | # | This program is distributed in the hope that it will be useful, | 10 | # | but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | # | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 12 | # | GNU General Public License for more details. | 13 | # +-------------------------------------------------------------------------+ 14 | # | Cacti: The Complete RRDtool-based Graphing Solution | 15 | # +-------------------------------------------------------------------------+ 16 | # | This code is designed, written, and maintained by the Cacti Group. See | 17 | # | about.php and/or the AUTHORS file for specific developer information. | 18 | # +-------------------------------------------------------------------------+ 19 | # | http://www.cacti.net/ | 20 | # +-------------------------------------------------------------------------+ 21 | 22 | # mdl cli configuration 23 | style ".mdl_style.rb" 24 | verbose false 25 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Spine is a high speed poller replacement for cmd.php. It is almost 100% 2 | compatible with the legacy cmd.php processor and provides much more 3 | flexibility, speed and concurrency than cmd.php. 4 | 5 | Make sure that you have the proper development environment to compile Spine. 6 | This includes compilers, header files and things such as libtool. If you 7 | have questions please consult the forums and/or online documentation. 8 | 9 | Development 10 | =========== 11 | 12 | DEVELOP branch should generally be considered UNSTABLE, use with caution! 13 | 14 | ----------------------------------------------------------------------------- 15 | 16 | Unix Installation 17 | ================= 18 | 19 | These instructions assume the default install location for spine 20 | of /usr/local/spine. If you choose to use another prefix, make 21 | sure you update the commands as required for that new path. 22 | 23 | To compile and install Spine using MySQL versions 5.5 or higher 24 | please do the following: 25 | 26 | 1. Run the bootstrap process to automatically create the configure script 27 | 28 | ./bootstrap 29 | 30 | 2. Run the configure process to detect what is available on the system 31 | 32 | ./configure 33 | 34 | 3. Build spine 35 | 36 | make 37 | 38 | 4. Optionally, install spine to the default location (/usr/local/spine/bin/) 39 | but do note that if you manually copy to another folder, change the paths 40 | below to reflect the correct folder you want spine to run from: 41 | 42 | make install 43 | chown root:root /usr/local/spine/bin/spine 44 | chmod u+s /usr/local/spine/bin/spine 45 | 46 | To compile and install Spine using MySQL versions previous to 5.5 please add 47 | the additional --with-reentrant option to the ./configure command above but 48 | please be aware that Cacti no longer officially supports MySQL prior to 5.5. 49 | 50 | Windows Installation 51 | ==================== 52 | 53 | CYGWIN Prerequisite 54 | ------------------- 55 | 56 | 1. Download Cygwin for Window from https://www.cygwin.com/ 57 | 58 | 2. Install Cygwin by executing the downloaded setup program 59 | 60 | 3. Select _Install from Internet_ 61 | 62 | 4. Select Root Directory: _C:\cygwin_ 63 | 64 | 5. Select a mirror which is close to your location 65 | 66 | 6. Once on the package selection section make sure to select the following (TIP: use the search!): 67 | 68 | * autoconf 69 | * automake 70 | * dos2unix 71 | * gcc-core 72 | * gcc-debuginfo 73 | * gzip 74 | * help2man 75 | * libmysqlclient 76 | * libmariadb-devel 77 | * libtool 78 | * m4 79 | * make 80 | * net-snmp-devel 81 | * libssl-devel 82 | * wget 83 | 84 | 7. Wait for installation to complete, coffee time! 85 | 86 | 8. Move the cygwin setup to the C:\cygwin\ folder for future usage. 87 | 88 | Compile Spine 89 | ------------- 90 | 91 | 1. Open Cygwin shell prompt (C:\Cygwin\cygwin.bat) and brace yourself to use unix commands on Windows. 92 | 93 | 2. Download the Spine source to the current directory: 94 | http://www.cacti.net/spine_download.php 95 | 96 | 3. Extract Spine into C:\Cygwin\usr\src\: 97 | tar xzvf cacti-spine-*.tar.gz 98 | 99 | 4. Change into the Spine directory: 100 | cd /usr/src/cacti-spine-* 101 | 102 | 5. Run bootstrap to prepare Spine for compilation: 103 | ./bootstrap 104 | 105 | 6. Follow the instruction which bootstrap outputs. 106 | 107 | 7. Update the spine.conf file for your installation of Cacti. You can optionally 108 | move it to a better location if you choose to do so, make sure to copy the 109 | spine.conf as well. 110 | 111 | 8. Ensure that Spine runs well by running with: 112 | /usr/local/spine/spine -R -S -V 3 113 | 114 | 9. Update Cacti 'Paths' Setting to point to the Spine binary and update the 115 | 'Poller Type' to Spine. For the spine binary on Windows x64, and using default 116 | locations, that would be: 117 | C:\cygwin64\usr\local\spine\bin\spine.exe 118 | 119 | 10. If all is good Spine will be run from the poller in place of cmd.php. 120 | 121 | -------------------------------------------------------------------------------------- 122 | 123 | Known Issues 124 | ============ 125 | 1. On Windows, Microsoft does not support a TCP Socket send timeout. Therefore, 126 | if you are using TCP ping on Windows, spine will not perform a second or subsequent 127 | retries to connect and the host will be assumed down on the first failure. 128 | 129 | If this is a problem it is suggested to use another Availability/Reachability 130 | method, or moving to Linux/UNIX. 131 | 132 | 2. Spine takes quite a few MySQL connections. The number of connections is calculated 133 | as follows: 134 | 135 | * main poller take one connection 136 | * all threads take one connection each 137 | * all script servers take one connection each 138 | 139 | Therefore, if you have 4 processes, with 10 threads each, and 5 script servers each 140 | your spine will take approximately: 141 | 142 | total connections = 4 * ( 1 + 10 + 5 ) = 64 143 | 144 | 3. On older MySQL versions, different libraries had to be used to make MySQL thread 145 | safe. MySQL versions 5.0 and 5.1 require this flag. If you are using these version 146 | of MySQL, you must use the --with-reentrant configure flag. 147 | 148 | 149 | ----------------------------------------------- 150 | Copyright (c) 2004-2024 - The Cacti Group, Inc. 151 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # +-------------------------------------------------------------------------+ 2 | # | Copyright (C) 2004-2024 The Cacti Group | 3 | # | | 4 | # | This program is free software; you can redistribute it and/or | 5 | # | modify it under the terms of the GNU General Public License | 6 | # | as published by the Free Software Foundation; either version 2 | 7 | # | of the License, or (at your option) any later version. | 8 | # | | 9 | # | This program is distributed in the hope that it will be useful, | 10 | # | but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | # | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 12 | # | GNU General Public License for more details. | 13 | # +-------------------------------------------------------------------------+ 14 | # | Cacti: The Complete RRDtool-based Graphing Solution | 15 | # +-------------------------------------------------------------------------+ 16 | # | This code is designed, written, and maintained by the Cacti Group. See | 17 | # | about.php and/or the AUTHORS file for specific developer information. | 18 | # +-------------------------------------------------------------------------+ 19 | # | http://www.cacti.net/ | 20 | # +-------------------------------------------------------------------------+ 21 | 22 | AUTOMAKE_OPTIONS = foreign 23 | ACLOCAL_AMFLAGS = -I m4 24 | 25 | 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 26 | 27 | configdir = $(sysconfdir) 28 | config_DATA = spine.conf.dist 29 | 30 | bin_PROGRAMS = spine 31 | 32 | man_MANS = spine.1 33 | 34 | spine.1: $(bin_PROGRAMS) 35 | $(HELP2MAN) --output=$@ --name='Data Collector for Cacti' --no-info --version-option='--version' ./spine 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spine: a poller for Cacti 2 | 3 | Spine is a high speed poller replacement for `cmd.php`. It is almost 100% 4 | compatible with the legacy cmd.php processor and provides much more flexibility, 5 | speed and concurrency than `cmd.php`. 6 | 7 | Make sure that you have the proper development environment to compile Spine. 8 | This includes compilers, header files and things such as libtool. If you have 9 | questions please consult the forums and/or online documentation. 10 | 11 | ----------------------------------------------------------------------------- 12 | 13 | ## Unix Installation 14 | 15 | These instructions assume the default install location for spine of 16 | `/usr/local/spine`. If you choose to use another prefix, make sure you update 17 | the commands as required for that new path. 18 | 19 | To compile and install Spine using MySQL versions 5.5 or higher please do the 20 | following: 21 | 22 | ```shell 23 | ./bootstrap 24 | ./configure 25 | make 26 | make install 27 | chown root:root /usr/local/spine/bin/spine 28 | chmod u+s /usr/local/spine/bin/spine 29 | ``` 30 | 31 | To compile and install Spine using MySQL versions previous to 5.5 please do the 32 | following: 33 | 34 | ```shell 35 | ./bootstrap 36 | ./configure --with-reentrant 37 | make 38 | make install 39 | chown root:root /usr/local/spine/bin/spine 40 | chmod +s /usr/local/spine/bin/spine 41 | ``` 42 | 43 | ## Windows Installation 44 | 45 | ### CYGWIN Prerequisite 46 | 47 | 1. Download Cygwin for Window from [https://www.cygwin.com/](https://www.cygwin.com/) 48 | 49 | 2. Install Cygwin by executing the downloaded setup program 50 | 51 | 3. Select _Install from Internet_ 52 | 53 | 4. Select Root Directory: _C:\cygwin_ 54 | 55 | 5. Select a mirror which is close to your location 56 | 57 | 6. Once on the package selection section make sure to select the following (TIP: 58 | use the search!): 59 | 60 | * autoconf 61 | * automake 62 | * dos2unix 63 | * gcc-core 64 | * gzip 65 | * help2man 66 | * inetutils-src 67 | * libmysqlclient 68 | * libmariadb-devel 69 | * libssl-devel 70 | * libtool 71 | * m4 72 | * make 73 | * net-snmp-devel 74 | * openssl-devel 75 | * wget 76 | 77 | 7. Wait for installation to complete, coffee time! 78 | 79 | 8. Move the cygwin setup to the C:\cygwin\ folder for future usage. 80 | 81 | ### Compile Spine 82 | 83 | 1. Open Cygwin shell prompt (C:\Cygwin\cygwin.bat) and brace yourself to use 84 | unix commands on Windows. 85 | 86 | 2. Download the Spine source to the current directory: 87 | 88 | [http://www.cacti.net/spine_download.php](http://www.cacti.net/spine_download.php) 89 | 90 | 3. Extract Spine into C:\Cygwin\usr\src\: 91 | 92 | `tar xzvf cacti-spine-*.tar.gz` 93 | 94 | 4. Change into the Spine directory: 95 | 96 | `cd /usr/src/cacti-spine-*` 97 | 98 | 5. Run bootstrap to prepare Spine for compilation: 99 | 100 | `./bootstrap` 101 | 102 | 6. Follow the instruction which bootstrap outputs. 103 | 104 | 7. Update the spine.conf file for your installation of Cacti. You can optionally 105 | move it to a better location if you choose to do so, make sure to copy the 106 | spine.conf as well. 107 | 108 | 8. Ensure that Spine runs well by running with `/usr/local/spine/spine -R -S -V 3` 109 | 110 | 9. Update Cacti `Paths` Setting to point to the Spine binary and update the 111 | `Poller Type` to Spine. For the spine binary on Windows x64, and using default 112 | locations, that would be `C:\cygwin64\usr\local\spine\bin\spine.exe` 113 | 114 | 10. If all is good Spine will be run from the poller in place of cmd.php. 115 | 116 | ## Known Issues 117 | 118 | 1. On Windows, Microsoft does not support a TCP Socket send timeout. Therefore, 119 | if you are using TCP ping on Windows, spine will not perform a second or 120 | subsequent retries to connect and the host will be assumed down on the first 121 | failure. 122 | 123 | If this is a problem it is suggested to use another Availability/Reachability 124 | method, or moving to Linux/UNIX. 125 | 126 | 2. Spine takes quite a few MySQL connections. The number of connections is 127 | calculated as follows: (1 for main poller + 1 per each thread + 1 per each 128 | script server) 129 | 130 | Therefore, if you have 4 processes, with 10 threads each, and 5 script 131 | servers each your spine will take approximately: 132 | 133 | `total connections = 4 * ( 1 + 10 + 5 ) = 64` 134 | 135 | 3. On older MySQL versions, different libraries had to be used to make MySQL 136 | thread safe. MySQL versions 5.0 and 5.1 require this flag. If you are using 137 | these version of MySQL, you must use the --with-reentrant configure flag. 138 | 139 | ----------------------------------------------------------------------------- 140 | Copyright (c) 2004-2024 - The Cacti Group, Inc. 141 | -------------------------------------------------------------------------------- /bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # +-------------------------------------------------------------------------+ 3 | # | Copyright (C) 2004-2024 The Cacti Group | 4 | # | | 5 | # | This program is free software; you can redistribute it and/or | 6 | # | modify it under the terms of the GNU General Public License | 7 | # | as published by the Free Software Foundation; either version 2 | 8 | # | of the License, or (at your option) any later version. | 9 | # | | 10 | # | This program is distributed in the hope that it will be useful, | 11 | # | but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | # | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | # | GNU General Public License for more details. | 14 | # +-------------------------------------------------------------------------+ 15 | # | Cacti: The Complete RRDtool-based Graphing Solution | 16 | # +-------------------------------------------------------------------------+ 17 | # | This code is designed, written, and maintained by the Cacti Group. See | 18 | # | about.php and/or the AUTHORS file for specific developer information. | 19 | # +-------------------------------------------------------------------------+ 20 | # | http://www.cacti.net/ | 21 | # +-------------------------------------------------------------------------+ 22 | 23 | # 24 | # ---------------------------------------------------------- 25 | # Name: bootstrap 26 | # 27 | # Function: build spine from scratch 28 | # 29 | # Description: This script will take a vanilla Spine source 30 | # package and attempt to compile it. It will 31 | # attempt to handle nasty things like dos2unix 32 | # issues in all files and searching for the 33 | # presence of required modules. 34 | # 35 | # It is not a replacement for the auto tools, 36 | # but simply a supplement. 37 | # 38 | # ---------------------------------------------------------- 39 | 40 | # Help function 41 | display_help () { 42 | echo "--------------------------------------------------------------" 43 | echo "Spine bootstrap script" 44 | echo " Attempts to configure spine based on a 'normal' system. If you" 45 | echo " install things in non-common locations you may have to use" 46 | echo " the install instructions to build." 47 | echo "--------------------------------------------------------------" 48 | echo 49 | } 50 | 51 | # Check for parameters 52 | if [ "${1}" = "--help" -o "${1}" = "-h" ]; then 53 | display_help 54 | exit 0 55 | fi 56 | 57 | echo "INFO: Starting Spine build process" 58 | 59 | # Remove software build specific directories 60 | echo "INFO: Removing cache directories" 61 | rm -rf autom4te.cache .deps 62 | 63 | # Make sure all files are unix formatted files 64 | which dos2unix > /dev/null 2>&1 65 | if [ $? -eq 0 ]; then 66 | for e in $(echo "ac am c h in md mdlrc rb sh yml"); do 67 | echo "INFO: Ensuring UNIX format for *.$e" 68 | find . -type f -name \*.$e -exec dos2unix --d2u \{\} \; > /dev/null 2>&1 69 | done 70 | fi 71 | 72 | # Prepare a build state 73 | echo "INFO: Running auto-tools to verify buildability" 74 | aclocal --install 75 | libtoolize 76 | autoheader 77 | automake --add-missing 78 | autoreconf --force --install 79 | [ $? -ne 0 ] && echo "ERROR: 'autoreconf' exited with errors" && exit -1 80 | 81 | 82 | # Provide some meaningful notes 83 | echo "INFO: Spine bootstrap process completed" 84 | echo "" 85 | echo " These instructions assume the default install location for spine" 86 | echo " of /usr/local/spine. If you choose to use another prefix, make" 87 | echo " sure you update the commands as required for that new path." 88 | echo "" 89 | echo " To compile and install Spine using MySQL or MariaDB" 90 | echo " please do the following:" 91 | echo "" 92 | echo " ./configure" 93 | echo " make" 94 | echo " make install" 95 | echo " chown root:root /usr/local/spine/bin/spine" 96 | echo " chmod +s /usr/local/spine/bin/spine" 97 | echo "" 98 | 99 | exit 0 100 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | /* 2 | ex: set tabstop=4 shiftwidth=4 autoindent: 3 | +-------------------------------------------------------------------------+ 4 | | Copyright (C) 2004-2024 The Cacti Group | 5 | | | 6 | | This program is free software; you can redistribute it and/or | 7 | | modify it under the terms of the GNU Lesser General Public | 8 | | License as published by the Free Software Foundation; either | 9 | | version 2.1 of the License, or (at your option) any later version. | 10 | | | 11 | | This program is distributed in the hope that it will be useful, | 12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | | GNU Lesser General Public License for more details. | 15 | | | 16 | | You should have received a copy of the GNU Lesser General Public | 17 | | License along with this library; if not, write to the Free Software | 18 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 19 | | 02110-1301, USA | 20 | | | 21 | +-------------------------------------------------------------------------+ 22 | | spine: a backend data gatherer for cacti | 23 | +-------------------------------------------------------------------------+ 24 | | This poller would not have been possible without: | 25 | | - Larry Adams (current development and enhancements) | 26 | | - Rivo Nurges (rrd support, mysql poller cache, misc functions) | 27 | | - RTG (core poller code, pthreads, snmp, autoconf examples) | 28 | | - Brady Alleman/Doug Warner (threading ideas, implementation details) | 29 | +-------------------------------------------------------------------------+ 30 | | - Cacti - http://www.cacti.net/ | 31 | +-------------------------------------------------------------------------+ 32 | */ 33 | 34 | #ifndef SPINE_COMMON_H 35 | #define SPINE_COMMON_H 1 36 | 37 | #ifdef __CYGWIN__ 38 | /* We use a Unix API, so pretend it's not Windows */ 39 | #undef WIN 40 | #undef WIN32 41 | #undef _WIN 42 | #undef _WIN32 43 | #undef _WIN64 44 | #undef __WIN__ 45 | #undef __WIN32__ 46 | #define HAVE_ERRNO_AS_DEFINE 47 | 48 | /* Cygwin supports only 64 open file descriptors, let's increase it a bit. */ 49 | #define FD_SETSIZE 512 50 | #endif /* __CYGWIN__ */ 51 | 52 | #define _THREAD_SAFE 53 | #define _PTHREADS 54 | #define _P __P 55 | 56 | #ifndef _REENTRANT 57 | #define _REENTRANT 58 | #endif 59 | 60 | #ifndef _LIBC_REENTRANT 61 | #define _LIBC_REENTRANT 62 | #endif 63 | 64 | #define PTHREAD_MUTEXATTR_DEFAULT ((pthread_mutexattr_t *) 0) 65 | 66 | #include "config/config.h" 67 | 68 | #if STDC_HEADERS 69 | # include 70 | # include 71 | #elif HAVE_STRINGS_H 72 | # include 73 | #endif /*STDC_HEADERS*/ 74 | 75 | #if HAVE_UNISTD_H 76 | # include 77 | # include 78 | #endif 79 | 80 | #include 81 | #include 82 | #include 83 | #include 84 | #include 85 | #include 86 | #include 87 | #include 88 | #include 89 | #include 90 | #include 91 | #include 92 | #include 93 | #include 94 | #include 95 | #include 96 | #include 97 | 98 | #if HAVE_STDINT_H 99 | # include 100 | #endif 101 | 102 | #if HAVE_NETINET_IN_H 103 | # include 104 | # include 105 | # include 106 | # include 107 | #ifndef __CYGWIN__ 108 | # include 109 | #endif 110 | # include 111 | #endif 112 | 113 | #if TIME_WITH_SYS_TIME 114 | # include 115 | # include 116 | #else 117 | # if HAVE_SYS_TIME_H 118 | # include 119 | # else 120 | # include 121 | # endif 122 | #endif 123 | 124 | #ifndef HAVE_LIBPTHREAD 125 | # define HAVE_LIBPTHREAD 0 126 | #else 127 | # include 128 | #endif 129 | 130 | #ifdef SOLAR_PRIV 131 | # include 132 | #endif 133 | 134 | #undef PACKAGE_NAME 135 | #undef PACKAGE_VERSION 136 | #undef PACKAGE_BUGREPORT 137 | #undef PACKAGE_STRING 138 | #undef PACKAGE_TARNAME 139 | #include 140 | #include 141 | #include 142 | #include 143 | #include 144 | #include 145 | #include 146 | //#include 147 | #include 148 | 149 | #include 150 | #include 151 | #include 152 | #include 153 | #include 154 | #include 155 | 156 | #ifdef HAVE_LCAP 157 | # include 158 | # include 159 | # include 160 | #endif 161 | 162 | #endif /* SPINE_COMMON_H */ 163 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # +-------------------------------------------------------------------------+ 2 | # | Copyright (C) 2004-2024 The Cacti Group | 3 | # | | 4 | # | This program is free software; you can redistribute it and/or | 5 | # | modify it under the terms of the GNU General Public License | 6 | # | as published by the Free Software Foundation; either version 2 | 7 | # | of the License, or (at your option) any later version. | 8 | # | | 9 | # | This program is distributed in the hope that it will be useful, | 10 | # | but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | # | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 12 | # | GNU General Public License for more details. | 13 | # +-------------------------------------------------------------------------+ 14 | # | Cacti: The Complete RRDtool-based Graphing Solution | 15 | # +-------------------------------------------------------------------------+ 16 | # | This code is designed, written, and maintained by the Cacti Group. See | 17 | # | about.php and/or the AUTHORS file for specific developer information. | 18 | # +-------------------------------------------------------------------------+ 19 | # | http://www.cacti.net/ | 20 | # +-------------------------------------------------------------------------+ 21 | 22 | AC_PREREQ([2.63]) 23 | AC_INIT([Spine Poller],[1.3.0],[http://www.cacti.net/issues.php]) 24 | 25 | AC_CONFIG_AUX_DIR(config) 26 | AC_SUBST(ac_aux_dir) 27 | 28 | AC_CANONICAL_HOST 29 | AC_CONFIG_SRCDIR(spine.c) 30 | AC_PREFIX_DEFAULT(/usr/local/spine) 31 | AC_LANG(C) 32 | AC_PROG_CC 33 | 34 | AM_INIT_AUTOMAKE([foreign]) 35 | AC_CONFIG_HEADERS(config/config.h) 36 | 37 | # static libraries 38 | AC_ARG_WITH(static, 39 | AS_HELP_STRING([--with-static],[Build using static libraries 40 | ]), 41 | [CFLAGS="-static $CFLAGS"] 42 | ) 43 | 44 | AC_CONFIG_MACRO_DIR([m4]) 45 | 46 | # mysql 47 | AC_ARG_WITH(mysql, 48 | AS_HELP_STRING([--with-mysql],[MySQL base directory [[/usr/local/mysql]] 49 | ]), 50 | [MYSQL_DIR=$withval] 51 | ) 52 | 53 | # snmp 54 | AC_ARG_WITH(snmp, 55 | AS_HELP_STRING([--with-snmp],[SNMP base directory [[/usr/(local/)include]] 56 | ]), 57 | [SNMP_DIR=$withval] 58 | ) 59 | 60 | # if host_alias is empty, ac_cv_host_alias may still have the info 61 | if test -z "$host_alias"; then 62 | host_alias=$ac_cv_host_alias 63 | fi 64 | 65 | # Platform-specific tweaks 66 | ShLib="so" 67 | 68 | case $host_alias in 69 | *sparc-sun-solaris2.8) 70 | CPPFLAGS="$CPPFLAGS -D_POSIX_PTHREAD_SEMANTICS" 71 | AC_DEFINE(SOLAR_THREAD, 1, [Correct issue around Solaris threading model]);; 72 | *solaris*) 73 | CPPFLAGS="$CPPFLAGS -D_POSIX_PTHREAD_SEMANTICS";; 74 | *freebsd*) 75 | LIBS="$LIBS -pthread -lexecinfo" 76 | AC_DEFINE(HAVE_LIBPTHREAD, 1);; 77 | *darwin*) 78 | ShLib="dylib";; 79 | *) 80 | LIBS="-lpthread -lssl $LIBS" 81 | esac 82 | 83 | # Checks for programs. 84 | AC_PROG_AWK 85 | AC_PROG_CC 86 | AC_PROG_CPP 87 | AC_PROG_INSTALL 88 | AC_PROG_LN_S 89 | LT_INIT 90 | 91 | AC_MSG_CHECKING([whether to enable -Wall]) 92 | AC_ARG_ENABLE(warnings, 93 | [ --enable-warnings Enable -Wall if using gcc.], 94 | [if test -n "$GCC"; then 95 | AC_MSG_RESULT(adding -Wall to CFLAGS.) 96 | CFLAGS="$CFLAGS -Wall" 97 | fi 98 | ], 99 | AC_MSG_RESULT(no) 100 | ) 101 | 102 | AC_PATH_PROG(HELP2MAN, help2man, false // No help2man //) 103 | AC_CHECK_PROG([HELP2MAN], [help2man], [help2man]) 104 | AM_CONDITIONAL([HAVE_HELP2MAN], [test x$HELP2MAN = xhelp2man]) 105 | 106 | # Checks for libraries. 107 | AC_CHECK_LIB(socket, socket) 108 | AC_CHECK_LIB(m, floor) 109 | AC_CHECK_LIB(dl, dlclose) 110 | AC_CHECK_LIB(pthread, pthread_exit) 111 | 112 | # Some builds of MySQL require libz - try to detect 113 | AC_CHECK_LIB(z, deflate) 114 | AC_CHECK_LIB(kstat, kstat_close) 115 | AC_CHECK_LIB(crypto, CRYPTO_realloc) 116 | 117 | # minor adjustments for debian 118 | AC_SEARCH_LIBS([clock_gettime], [rt pthread]) 119 | 120 | # Checks for header files. 121 | AC_CHECK_HEADERS(sys/socket.h sys/select.h sys/wait.h sys/time.h) 122 | AC_CHECK_HEADERS(assert.h ctype.h errno.h signal.h math.h malloc.h netdb.h) 123 | AC_CHECK_HEADERS(signal.h stdarg.h stdio.h syslog.h) 124 | AC_CHECK_HEADERS( 125 | netinet/in_systm.h netinet/in.h netinet/ip.h netinet/ip_icmp.h, 126 | [], 127 | [], 128 | [#ifdef HAVE_SYS_TYPES_H 129 | #include 130 | #endif 131 | #ifdef HAVE_NETINET_IN_H 132 | #include 133 | #endif 134 | #ifdef HAVE_NETINET_IN_SYSTM_H 135 | #include 136 | #endif 137 | #ifdef HAVE_NETINET_IP_H 138 | #include 139 | #endif] 140 | ) 141 | 142 | # Checks for typedefs, structures, and compiler characteristics. 143 | AC_CHECK_TYPES([unsigned long long, long long]) 144 | AC_C_CONST 145 | AC_TYPE_SIZE_T 146 | AC_HEADER_TIME 147 | AC_STRUCT_TM 148 | 149 | # Checks for library functions. 150 | AC_DIAGNOSE([obsolete],[your code may safely assume C89 semantics that RETSIGTYPE is void. 151 | Remove this warning and the `AC_CACHE_CHECK' when you adjust the code.])dnl 152 | AC_CACHE_CHECK([return type of signal handlers],[ac_cv_type_signal],[AC_COMPILE_IFELSE( 153 | [AC_LANG_PROGRAM([#include 154 | #include 155 | ], 156 | [return *(signal (0, 0)) (0) == 1;])], 157 | [ac_cv_type_signal=int], 158 | [ac_cv_type_signal=void])]) 159 | AC_DEFINE_UNQUOTED([RETSIGTYPE],[$ac_cv_type_signal],[Define as the return type of signal handlers 160 | (`int' or `void').]) 161 | 162 | AC_CHECK_FUNCS(malloc calloc gettimeofday strerror strtoll) 163 | 164 | # ****************** Solaris Privileges Check *********************** 165 | 166 | # Check if usage of Solaris privileges support is possible 167 | AC_CHECK_HEADER(priv.h, [FOUND_PRIV_H=yes], [FOUND_PRIV_H=no]) 168 | 169 | # If we should use the Solaris privileges support 170 | AC_MSG_CHECKING(whether we are using Solaris privileges) 171 | AC_ARG_ENABLE(solaris-priv, 172 | [ --enable-solaris-priv Enable support for the Solaris process privilege model (default: disabled)], 173 | [ ENABLED_SOL_PRIV=$enableval ], 174 | [ ENABLED_SOL_PRIV=no ] 175 | ) 176 | if test x$ENABLED_SOL_PRIV != xno; then 177 | if test x$FOUND_PRIV_H != xno; then 178 | AC_MSG_RESULT([yes]) 179 | AC_DEFINE([SOLAR_PRIV], [1], 180 | [If Support for Solaris privileges should be enabled] 181 | ) 182 | else 183 | AC_MSG_RESULT([no]) 184 | fi 185 | else 186 | AC_MSG_RESULT([no]) 187 | fi 188 | 189 | # ****************** Linux Capabilities Check *********************** 190 | CAPLOC="sys/capability.h" 191 | for file in sys/capability.h;do 192 | test -f /usr/include/$file && CAPLOC=$file && break 193 | done 194 | 195 | AC_CHECK_HEADER($CAPLOC, [FOUND_SYS_CAPABILITY_H=yes], 196 | [FOUND_SYS_CAPABILITY_H=no]) 197 | 198 | # If we should use the Linux Capabilities support 199 | AC_MSG_CHECKING(whether we are using Linux Capabilities) 200 | AC_ARG_ENABLE(lcap, 201 | [ --enable-lcap Enable support for the Linux Capabilities (default: disabled)], 202 | [ ENABLED_LCAP=$enableval ], 203 | [ ENABLED_LCAP=no ] 204 | ) 205 | 206 | if test x$ENABLED_LCAP != xno; then 207 | if test x$FOUND_SYS_CAPABILITY_H != xno; then 208 | AC_MSG_RESULT([yes]) 209 | AC_CHECK_LIB(cap, cap_init, 210 | [ LIBS="-lcap $LIBS" 211 | AC_DEFINE(HAVE_LCAP, 1, Linux Capabilities) 212 | HAVE_LCAP=yes ], 213 | [ AC_MSG_RESULT(Cannot find Linux Capabilities library(cap)...) 214 | HAVE_LCAP=no ] 215 | ) 216 | else 217 | AC_MSG_RESULT([no]) 218 | fi 219 | else 220 | AC_MSG_RESULT([no]) 221 | fi 222 | 223 | # ****************** MySQL Checks *********************** 224 | AC_DEFUN([MYSQL_LIB_CHK], 225 | [ str="$1/libmysqlclient.*" 226 | for j in `echo $str`; do 227 | if test -r $j; then 228 | MYSQL_LIB_DIR=$1 229 | break 2 230 | fi 231 | done 232 | ] 233 | ) 234 | 235 | # Determine MySQL installation paths 236 | MYSQL_SUB_DIR="include include/mysql include/mariadb mysql"; 237 | for i in $MYSQL_DIR /usr /usr/local /opt /opt/mysql /usr/pkg /usr/local/mysql; do 238 | for d in $MYSQL_SUB_DIR; do 239 | if [[ -f $i/$d/mysql.h ]]; then 240 | MYSQL_INC_DIR=$i/$d 241 | break; 242 | fi 243 | done 244 | 245 | if [[ ! -z $MYSQL_INC_DIR ]]; then 246 | break; 247 | fi 248 | # test -f $i/include/mysql.h && MYSQL_INC_DIR=$i/include && break 249 | # test -f $i/include/mysql/mysql.h && MYSQL_INC_DIR=$i/include/mysql && break 250 | # test -f $i/include/mariadb/mysql.h && MYSQL_INC_DIR=$i/include/mariadb && break 251 | # test -f $i/mysql/include/mysql.h && MYSQL_INC_DIR=$i/mysql/include && break 252 | done 253 | 254 | if test -z "$MYSQL_INC_DIR"; then 255 | if test "x$MYSQL_DIR" != "x"; then 256 | AC_MSG_ERROR(Cannot find MySQL header files under $MYSQL_DIR) 257 | else 258 | AC_MSG_ERROR(Cannot find MySQL headers. Use --with-mysql= to specify non-default path.) 259 | fi 260 | fi 261 | 262 | for i in $MYSQL_DIR /usr /usr/local /opt /opt/mysql /usr/pkg /usr/local/mysql; do 263 | MYSQL_LIB_CHK($i/lib64) 264 | MYSQL_LIB_CHK($i/lib64/mysql) 265 | MYSQL_LIB_CHK($i/lib/x86_64-linux-gnu) 266 | MYSQL_LIB_CHK($i/lib/x86_64-linux-gnu/mysql) 267 | MYSQL_LIB_CHK($i/lib) 268 | MYSQL_LIB_CHK($i/lib/mysql) 269 | done 270 | 271 | if test -n "$MYSQL_LIB_DIR" ; then 272 | LDFLAGS="-L$MYSQL_LIB_DIR $LDFLAGS" 273 | fi 274 | CFLAGS="-I$MYSQL_INC_DIR $CFLAGS" 275 | 276 | unamestr=$(uname) 277 | if test $unamestr = 'OpenBSD'; then 278 | AC_CHECK_LIB(mysqlclient, mysql_init, 279 | [ LIBS="-lmysqlclient -lexecinfo -lm $LIBS" 280 | AC_DEFINE(HAVE_MYSQL, 1, MySQL Client API) 281 | HAVE_MYSQL=yes ], 282 | [ HAVE_MYSQL=no ] 283 | ) 284 | else 285 | AC_CHECK_LIB(mysqlclient, mysql_init, 286 | [ LIBS="-lmysqlclient -lm -ldl $LIBS" 287 | AC_DEFINE(HAVE_MYSQL, 1, MySQL Client API) 288 | HAVE_MYSQL=yes ], 289 | [ HAVE_MYSQL=no ] 290 | ) 291 | fi 292 | 293 | if test -f $MYSQL_LIB_DIR/libmysqlclient_r.a -o -f $MYSQL_LIB_DIR/libmysqlclient_r.$ShLib; then 294 | LIBS="-lmysqlclient_r -lm -ldl $LIBS" 295 | else 296 | if test -f $MYSQL_LIB_DIR/libmysqlclient_r.a -o -f $MYSQL_LIB_DIR/libmysqlclient_r.$ShLib ; then 297 | LIBS="-lmysqlclient_r -lm -ldl $LIBS" 298 | else 299 | if test "$HAVE_MYSQL" = "yes"; then 300 | if test $unamestr = 'OpenBSD'; then 301 | LIBS="-lmysqlclient -lm $LIBS" 302 | else 303 | LIBS="-lmysqlclient -lm -ldl $LIBS" 304 | fi 305 | else 306 | if test -f $MYSQL_LIB_DIR/libperconaserverclient.a -o -f $MYSQL_LIB_DIR/libperconaserverclient.$ShLib; then 307 | LIBS="-lperconaserverclient -lm -ldl $LIBS" 308 | else 309 | LIBS="-lmariadbclient -lm -ldl $LIBS" 310 | fi 311 | fi 312 | fi 313 | fi 314 | 315 | # ****************** Net-SNMP Checks *********************** 316 | if test "x$SNMP_DIR" != "x"; then 317 | for i in / /net-snmp /include/net-snmp; do 318 | test -f $SNMP_DIR/$i/net-snmp-config.h && SNMP_INCDIR=$SNMP_DIR$i && break 319 | done 320 | 321 | # Accommodate 64-Bit Libraries 322 | test -f $SNMP_DIR/lib64/libnetsnmp.a -o -f $SNMP_DIR/lib64/libnetsnmp.$ShLib && SNMP_LIBDIR=$SNMP_DIR/lib64 323 | 324 | if test -z "$SNMP_LIBDIR"; then 325 | # Accommodate 32-Bit Libraries 326 | test -f $SNMP_DIR/lib/libnetsnmp.a -o -f $SNMP_DIR/lib/libnetsnmp.$ShLib && SNMP_LIBDIR=$SNMP_DIR/lib 327 | fi 328 | else 329 | for i in /usr /usr/local /usr/include /usr/pkg/include /usr/local/include /opt /opt/net-snmp /opt/snmp; do 330 | test -f $i/snmp.h && SNMP_INCDIR=$i && break 331 | test -f $i/include/net-snmp/net-snmp-config.h && SNMP_INCDIR=$i/include/net-snmp && break 332 | test -f $i/net-snmp/net-snmp-config.h && SNMP_INCDIR=$i/net-snmp && break 333 | test -f $i/net-snmp/include/net-snmp-config.h && SNMP_INCDIR=$i/net-snmp/include && break 334 | test -f $i/snmp/snmp.h && SNMP_INCDIR=$i/snmp && break 335 | test -f $i/snmp/include/net-snmp/net-snmp-config.h && SNMP_INCDIR=$i/snmp/include/net-snmp && break 336 | done 337 | 338 | # Accommodate 64-Bit Libraries 339 | for i in /usr /usr/local /usr/pkg /usr/snmp /opt /opt/net-snmp /opt/snmp /usr/local/snmp; do 340 | test -f $i/lib64/libnetsnmp.a -o -f $i/lib64/libnetsnmp.$ShLib && SNMP_LIBDIR=$i/lib64 && break 341 | done 342 | 343 | # Only check for 32 Bit libraries if the 64 bit are not found 344 | if test -z "$SNMP_LIBDIR"; then 345 | # Accommodate 32-Bit Libraries 346 | for i in /usr /usr/local /usr/pkg /usr/snmp /opt /opt/net-snmp /opt/snmp /usr/local/snmp; do 347 | test -f $i/lib/libnetsnmp.a -o -f $i/lib/libnetsnmp.$ShLib && SNMP_LIBDIR=$i/lib && break 348 | done 349 | fi 350 | fi 351 | 352 | if test -z "$SNMP_INCDIR"; then 353 | if test "x$SNMP_DIR" != "x";then 354 | AC_MSG_ERROR(Cannot find SNMP header files under $SNMP_DIR) 355 | else 356 | AC_MSG_ERROR(Cannot find SNMP headers. Use --with-snmp= to specify non-default path.) 357 | fi 358 | fi 359 | 360 | if test -n "$SNMP_LIBDIR" ; then 361 | LDFLAGS="-L$SNMP_LIBDIR $LDFLAGS" 362 | fi 363 | 364 | if test -n "$SNMP_INCDIR" ; then 365 | CFLAGS="-I$SNMP_INCDIR -I$SNMP_INCDIR/.. $CFLAGS" 366 | fi 367 | 368 | # Net-SNMP includes v3 support and insists on crypto unless compiled --without-openssl 369 | AC_MSG_CHECKING([if Net-SNMP needs crypto support]) 370 | AC_TRY_COMPILE([#include ], [return NETSNMP_USE_OPENSSL != 1;], 371 | [ AC_MSG_RESULT(yes) 372 | SNMP_SSL=yes 373 | ],[AC_MSG_RESULT(no) 374 | ]) 375 | 376 | AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include 377 | #include 378 | #include 379 | #include 380 | #include ]], [[struct snmp_session session; snmp_sess_init(&session); session.localname = strdup("hello")]])],[havelocalname=1],[havelocalname=0 381 | ]) 382 | AC_DEFINE_UNQUOTED(SNMP_LOCALNAME, $havelocalname, If snmp localname session structure member exists) 383 | 384 | AC_CHECK_LIB(netsnmp, snmp_timeout) 385 | 386 | # ****************** Spine Result Buffer Check *********************** 387 | # Check for the default spine output buffer size 388 | results_buffer=2048 389 | AC_ARG_WITH(results-buffer, 390 | AS_HELP_STRING([--with-results-buffer=N],[The size of the spine results buffer (default=2048)]), 391 | [results_buffer=$withval] 392 | ) 393 | AC_DEFINE_UNQUOTED(RESULTS_BUFFER, $results_buffer, The size of the spine result buffer) 394 | AC_MSG_RESULT(checking for the spine results buffer size... $results_buffer bytes) 395 | 396 | # ****************** Maximum Simultaneous Scripts *********************** 397 | # Check for the most scripts that can be active at one time per spine process 398 | max_scripts=20 399 | AC_ARG_WITH(max-scripts, 400 | AS_HELP_STRING([--with-max-scripts=N],[The maximum simultaneous spine scripts that can run (default=20)]), 401 | [max_scripts=$withval] 402 | ) 403 | AC_DEFINE_UNQUOTED(MAX_SIMULTANEOUS_SCRIPTS, $max_scripts, The maximum number of simultaneous running scripts) 404 | AC_MSG_RESULT(checking for the maximum simultaneous spine scripts... $max_scripts) 405 | 406 | # ****************** Maximum MySQL Buffer Size *********************** 407 | # Check for the most scripts that can be active at one time per spine process 408 | max_mysql_buffer=131072 409 | AC_ARG_WITH(max-mysql-buffer, 410 | AS_HELP_STRING([--with-max-mysql-buffer=N],[The maximum SQL insert size allowed (default=131072)]), 411 | [max_mysql_buffer=$withval] 412 | ) 413 | AC_DEFINE_UNQUOTED(MAX_MYSQL_BUF_SIZE, $max_mysql_buffer, The maximum MySQL buffer size to insert) 414 | AC_MSG_RESULT(checking for the maximum MySQL buffer size... $max_mysql_buffer) 415 | 416 | # ****************** Traditional Popen Check *********************** 417 | # If we should use the system popen or nifty popen 418 | AC_MSG_CHECKING(whether we are using traditional popen) 419 | AC_ARG_ENABLE(popen, 420 | [ --enable-popen Enable the traditional popen implementation of nifty popen (default: disabled)], 421 | [ ENABLED_TPOPEN=$enableval ], 422 | [ ENABLED_TPOPEN=no ] 423 | ) 424 | if test "$ENABLED_TPOPEN" = "yes"; then 425 | AC_MSG_RESULT([yes]) 426 | AC_DEFINE(USING_TPOPEN, 1, If traditional popen should be enabled by default) 427 | else 428 | AC_MSG_RESULT([no]) 429 | fi 430 | 431 | # ****************** Force Net-SNMP Version Checks *********************** 432 | # If we should use the system popen or nifty popen 433 | AC_MSG_CHECKING(whether to verify net-snmp library vs header versions) 434 | AC_ARG_ENABLE(strict-snmp, 435 | [ --enable-strict-snmp Enable checking of Net-SNMP library vs header versions (default: disabled)], 436 | [ ENABLED_SNMP_VERSION=$enableval ], 437 | [ ENABLED_SNMP_VERSION=no ] 438 | ) 439 | if test "$ENABLED_SNMP_VERSION" = "yes"; then 440 | AC_MSG_RESULT([yes]) 441 | AC_DEFINE(VERIFY_PACKAGE_VERSION, 1, If we are going to force Net-SNMP library and header versions to be the same) 442 | else 443 | AC_MSG_RESULT([no]) 444 | fi 445 | 446 | # See if we can support backtracing 447 | AC_MSG_CHECKING([if we can support backtracing]) 448 | AC_LINK_IFELSE([AC_LANG_PROGRAM([[ 449 | #include 450 | #include 451 | ]], [[ 452 | void *array[10]; 453 | size_t size; 454 | 455 | // get void*'s for all entries on the stack 456 | size = backtrace(array, 10); 457 | if (size) { 458 | exit(0); 459 | } else { 460 | exit(1); 461 | } 462 | ]])],[ AC_MSG_RESULT(yes) 463 | AC_DEFINE(HAVE_EXECINFO_H,1,[Do we have backtracing capabilities?]) 464 | ],[AC_MSG_RESULT(no) 465 | ]) 466 | 467 | # See if we can support backtracing 468 | AC_MSG_CHECKING([if we can support mysql/mariadb retry count]) 469 | AC_LINK_IFELSE([AC_LANG_PROGRAM([[ 470 | #include 471 | #include "$MYSQL_INC_DIR/mysql.h" 472 | ]], [[ 473 | if (MYSQL_OPT_RETRY_COUNT) { 474 | exit(0); 475 | } else { 476 | exit(1); 477 | } 478 | ]])],[ AC_MSG_RESULT(yes) 479 | AC_DEFINE(HAS_MYSQL_OPT_RETRY_COUNT,1,[Do we have mysql/maraidb retry count capabilities?]) 480 | ],[AC_MSG_RESULT(no) 481 | ]) 482 | 483 | # See if we can support backtracing 484 | AC_MSG_CHECKING([if we can support mysql/mariadb ssl keys]) 485 | AC_LINK_IFELSE([AC_LANG_PROGRAM([[ 486 | #include 487 | #include "$MYSQL_INC_DIR/mysql.h" 488 | ]], [[ 489 | if (MYSQL_OPT_SSL_KEY) { 490 | exit(0); 491 | } else { 492 | exit(1); 493 | } 494 | ]])],[ AC_MSG_RESULT(yes) 495 | AC_DEFINE(HAS_MYSQL_OPT_SSL_KEY,1,[Do we have mysql/maraidb ssl keys capabilities?]) 496 | ],[AC_MSG_RESULT(no) 497 | ]) 498 | 499 | AC_CONFIG_FILES([Makefile]) 500 | AC_OUTPUT 501 | -------------------------------------------------------------------------------- /copyright_year.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # +-------------------------------------------------------------------------+ 3 | # | Copyright (C) 2004-2024 The Cacti Group | 4 | # | | 5 | # | This program is free software; you can redistribute it and/or | 6 | # | modify it under the terms of the GNU General Public License | 7 | # | as published by the Free Software Foundation; either version 2 | 8 | # | of the License, or (at your option) any later version. | 9 | # | | 10 | # | This program is distributed in the hope that it will be useful, | 11 | # | but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | # | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | # | GNU General Public License for more details. | 14 | # +-------------------------------------------------------------------------+ 15 | # | Cacti: The Complete RRDTool-based Graphing Solution | 16 | # +-------------------------------------------------------------------------+ 17 | # | This code is designed, written, and maintained by the Cacti Group. See | 18 | # | about.php and/or the AUTHORS file for specific developer information. | 19 | # +-------------------------------------------------------------------------+ 20 | # | http://www.cacti.net/ | 21 | # +-------------------------------------------------------------------------+ 22 | 23 | update_copyright() { 24 | local file=$1 25 | file=${file/$SCRIPT_BASE/} 26 | printf -v line "%60s" "$file" 27 | if [[ -z "$ERRORS_ONLY" ]]; then 28 | echo -n "$line" 29 | line= 30 | fi 31 | 32 | old_reg="20[0-9][0-9][ ]*-[ ]*20[0-9][0-9]" 33 | old_data=$(grep -c -e "$old_reg" "$1" 2>/dev/null) 34 | new_reg="2004-$YEAR" 35 | result=$? 36 | 37 | if [[ $old_data -eq 0 ]]; then 38 | old_reg="(Copyright.*) 20[0-9][0-9] " 39 | old_data=$(grep -c -e "$old_reg" "$1" 2>/dev/null) 40 | new_reg="\1 2004-$YEAR" 41 | result=$? 42 | fi 43 | 44 | if [[ $old_data -gt 0 ]]; then 45 | old_data=$(grep -e "$old_reg" "$1" 2>/dev/null) 46 | new_data=$(echo "$old_data" | sed -r s/"$old_reg"/"$new_reg"/g) 47 | if [[ "$old_data" == "$new_data" ]]; then 48 | if [[ -z "$ERRORS_ONLY" ]]; then 49 | echo "$line Skipping Copyright Data" 50 | fi 51 | else 52 | echo "$line Updating Copyright Data" 53 | printf "%60s %s\n" "==============================" "====================" 54 | printf "%60s %s\n" "$old_data" "=>" 55 | printf "%60s %s\n" "$new_data" "" 56 | sed -i -r s/"$old_reg"/"$new_reg"/g $1 57 | printf "%60s %s\n" "==============================" "====================" 58 | fi 59 | else 60 | echo "$line Copyright not found!" 61 | SCRIPT_ERR=1 62 | fi 63 | } 64 | 65 | SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) 66 | SCRIPT_BASE=$(realpath "${SCRIPT_DIR}/")/ 67 | 68 | BAD_FOLDERS="\.git include/vendor \*\*/vendor include/fa cache include/js scripts" 69 | SCRIPT_EXCLUSION= 70 | for f in $BAD_FOLDERS; do 71 | SCRIPT_EXCLUSION="$SCRIPT_EXCLUSION -not -path ${SCRIPT_BASE}$f/\* " 72 | done 73 | 74 | SCRIPT_ERR=0 75 | YEAR=$(date +"%Y") 76 | EXT="" # "sh sql php js md conf c h ac dist" 77 | ERRORS_ONLY=1 78 | while [ -n "$1" ]; do 79 | case $1 in 80 | "--help") 81 | echo "NOTE: Checks all Cacti pages for this years copyright" 82 | echo "" 83 | echo "usage: copyright_year.sh [-a]" 84 | echo "" 85 | ;; 86 | "-E" | "-e") 87 | shift 88 | EXT="$1" 89 | ;; 90 | "-A" | "-a") 91 | ERRORS_ONLY= 92 | echo "Searching..." 93 | ;; 94 | *) ;; 95 | 96 | esac 97 | shift 98 | done 99 | 100 | # ---------------------------------------------- 101 | # PHP / JS / MD Files 102 | # ---------------------------------------------- 103 | SCRIPT_INCLUSION= 104 | SCRIPT_SEPARATOR= 105 | for ext in $EXT; do 106 | if [ -n "$SCRIPT_INCLUSION" ]; then 107 | SCRIPT_SEPARATOR="-o " 108 | fi 109 | SCRIPT_INCLUSION="$SCRIPT_INCLUSION $SCRIPT_SEPARATOR-name \*.$ext" 110 | done 111 | 112 | if [[ -n "$SCRIPT_INCLUSION" ]]; then 113 | SCRIPT_INCLUSION="\( $SCRIPT_INCLUSION \)" 114 | fi 115 | 116 | SCRIPT_CMD="find ${SCRIPT_BASE} -type f $SCRIPT_INCLUSION $SCRIPT_EXCLUSION -print0" 117 | bash -c "$SCRIPT_CMD" | while IFS= read -r -d '' file; do 118 | update_copyright "${file}" 119 | done 120 | -------------------------------------------------------------------------------- /debug: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # +-------------------------------------------------------------------------+ 4 | # | Copyright (C) 2004-2024 The Cacti Group | 5 | # | | 6 | # | This program is free software; you can redistribute it and/or | 7 | # | modify it under the terms of the GNU General Public License | 8 | # | as published by the Free Software Foundation; either version 2 | 9 | # | of the License, or (at your option) any later version. | 10 | # | | 11 | # | This program is distributed in the hope that it will be useful, | 12 | # | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | # | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | # | GNU General Public License for more details. | 15 | # +-------------------------------------------------------------------------+ 16 | # | Cacti: The Complete RRDtool-based Graphing Solution | 17 | # +-------------------------------------------------------------------------+ 18 | # | This code is designed, written, and maintained by the Cacti Group. See | 19 | # | about.php and/or the AUTHORS file for specific developer information. | 20 | # +-------------------------------------------------------------------------+ 21 | # | http://www.cacti.net/ | 22 | # +-------------------------------------------------------------------------+ 23 | 24 | if [[ -z $SPINE_CONFIG ]]; then 25 | export SPINE_CONFIG="/etc/spine.conf"; 26 | fi 27 | 28 | make 29 | 30 | if [[ $? -eq 0 ]]; then 31 | echo 32 | echo ------ 33 | echo Debugging using SPINE_CONFIG = $SPINE_CONFIG 34 | echo 35 | echo 36 | gdb -quiet -ex run --args ./spine -R -V 6 -C $SPINE_CONFIG 37 | fi 38 | -------------------------------------------------------------------------------- /error.c: -------------------------------------------------------------------------------- 1 | /* 2 | ex: set tabstop=4 shiftwidth=4 autoindent: 3 | +-------------------------------------------------------------------------+ 4 | | Copyright (C) 2004-2024 The Cacti Group | 5 | | | 6 | | This program is free software; you can redistribute it and/or | 7 | | modify it under the terms of the GNU Lesser General Public | 8 | | License as published by the Free Software Foundation; either | 9 | | version 2.1 of the License, or (at your option) any later version. | 10 | | | 11 | | This program is distributed in the hope that it will be useful, | 12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | | GNU Lesser General Public License for more details. | 15 | | | 16 | | You should have received a copy of the GNU Lesser General Public | 17 | | License along with this library; if not, write to the Free Software | 18 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 19 | | 02110-1301, USA | 20 | | | 21 | +-------------------------------------------------------------------------+ 22 | | spine: a backend data gatherer for cacti | 23 | +-------------------------------------------------------------------------+ 24 | | This poller would not have been possible without: | 25 | | - Larry Adams (current development and enhancements) | 26 | | - Rivo Nurges (rrd support, mysql poller cache, misc functions) | 27 | | - RTG (core poller code, pthreads, snmp, autoconf examples) | 28 | | - Brady Alleman/Doug Warner (threading ideas, implementation details) | 29 | +-------------------------------------------------------------------------+ 30 | | - Cacti - http://www.cacti.net/ | 31 | +-------------------------------------------------------------------------+ 32 | */ 33 | 34 | /* These functions handle simple signal handling functions for Spine. It was 35 | written to handle specifically issues with the Solaris threading model in 36 | version 2.8. 37 | */ 38 | 39 | #include "common.h" 40 | #include "spine.h" 41 | 42 | /*! \fn static void spine_signal_handler(int spine_signal) 43 | * \brief interrupts the os default signal handler as appropriate. 44 | * 45 | */ 46 | static void spine_signal_handler(int spine_signal) { 47 | signal(spine_signal, SIG_DFL); 48 | 49 | set.exit_code = spine_signal; 50 | 51 | /* variables for time display */ 52 | time_t nowbin; 53 | struct tm now_time; 54 | struct tm *now_ptr; 55 | 56 | /* get time for poller_output table */ 57 | nowbin = time(&nowbin); 58 | 59 | localtime_r(&nowbin,&now_time); 60 | now_ptr = &now_time; 61 | 62 | char *log_fmt = get_date_format(); 63 | char logtime[50]; 64 | 65 | strftime(logtime, 50, log_fmt, now_ptr); 66 | 67 | switch (spine_signal) { 68 | case SIGABRT: 69 | fprintf(stderr, "%s FATAL: Spine Interrupted by Abort Signal\n", logtime); 70 | break; 71 | case SIGINT: 72 | fprintf(stderr, "%s FATAL: Spine Interrupted by Console Operator\n", logtime); 73 | break; 74 | case SIGSEGV: 75 | fprintf(stderr, "%s FATAL: Spine Encountered a Segmentation Fault\n", logtime); 76 | 77 | #ifdef HAVE_EXECINFO_H 78 | int row = 0; 79 | char **exit_strings = NULL; 80 | 81 | fprintf(stderr, "Generating backtrace...%ld line(s)...\n", set.exit_size); 82 | 83 | if (set.exit_size) { 84 | set.exit_size = backtrace(set.exit_stack, set.exit_size); 85 | exit_strings = backtrace_symbols(set.exit_stack, set.exit_size); 86 | 87 | if (exit_strings) { 88 | for (row = 0; row < set.exit_size; row++) { 89 | fprintf(stderr, "%3d: %s\n", row, exit_strings[row]); 90 | } 91 | 92 | free(exit_strings); 93 | } 94 | } 95 | #endif 96 | 97 | exit(1); 98 | 99 | break; 100 | case SIGBUS: 101 | fprintf(stderr, "%s FATAL: Spine Encountered a Bus Error\n", logtime); 102 | break; 103 | case SIGFPE: 104 | fprintf(stderr, "%s FATAL: Spine Encountered a Floating Point Exception\n", logtime); 105 | break; 106 | case SIGQUIT: 107 | fprintf(stderr, "%s FATAL: Spine Encountered a Keyboard Quit Command\n", logtime); 108 | break; 109 | case SIGPIPE: 110 | fprintf(stderr, "%s FATAL: Spine Encountered a Broken Pipe\n", logtime); 111 | break; 112 | default: 113 | fprintf(stderr, "%s FATAL: Spine Encountered An Unhandled Exception Signal Number: '%d'\n", logtime, spine_signal); 114 | break; 115 | } 116 | } 117 | 118 | static int spine_fatal_signals[] = { 119 | SIGINT, 120 | SIGPIPE, 121 | SIGSEGV, 122 | SIGBUS, 123 | SIGFPE, 124 | SIGQUIT, 125 | SIGSYS, 126 | SIGABRT, 127 | 0 128 | }; 129 | 130 | /*! \fn void install_spine_signal_handler(void) 131 | * \brief installs the spine signal handler to stop certain calls from 132 | * abending Spine. 133 | * 134 | */ 135 | void install_spine_signal_handler(void) { 136 | /* Set a handler for any fatal signal not already handled */ 137 | int i; 138 | struct sigaction sa; 139 | void (*ohandler)(int); 140 | 141 | for (i=0; spine_fatal_signals[i]; ++i) { 142 | sigaction(spine_fatal_signals[i], NULL, &sa); 143 | if (sa.sa_handler == SIG_DFL) { 144 | sa.sa_handler = spine_signal_handler; 145 | sigemptyset(&sa.sa_mask); 146 | sa.sa_flags = SA_RESTART; 147 | sigaction(spine_fatal_signals[i], &sa, NULL); 148 | } 149 | } 150 | 151 | for (i=0; spine_fatal_signals[i]; ++i) { 152 | ohandler = signal(spine_fatal_signals[i], spine_signal_handler); 153 | if (ohandler != SIG_DFL) { 154 | signal(spine_fatal_signals[i], ohandler); 155 | } 156 | } 157 | 158 | return; 159 | } 160 | 161 | /*! \fn void uninstall_spine_signal_handler(void) 162 | * \brief uninstalls the spine signal handler. 163 | * 164 | */ 165 | void uninstall_spine_signal_handler(void) { 166 | /* Remove a handler for any fatal signal handled */ 167 | int i; 168 | struct sigaction sa; 169 | void (*ohandler)(int); 170 | 171 | for (i=0; spine_fatal_signals[i]; ++i) { 172 | sigaction(spine_fatal_signals[i], NULL, &sa); 173 | if (sa.sa_handler == spine_signal_handler) { 174 | sa.sa_handler = SIG_DFL; 175 | sigaction(spine_fatal_signals[i], &sa, NULL); 176 | } 177 | } 178 | 179 | for ( i=0; spine_fatal_signals[i]; ++i ) { 180 | ohandler = signal(spine_fatal_signals[i], SIG_DFL); 181 | if (ohandler != spine_signal_handler) { 182 | signal(spine_fatal_signals[i], ohandler); 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /error.h: -------------------------------------------------------------------------------- 1 | /* 2 | ex: set tabstop=4 shiftwidth=4 autoindent: 3 | +-------------------------------------------------------------------------+ 4 | | Copyright (C) 2004-2024 The Cacti Group | 5 | | | 6 | | This program is free software; you can redistribute it and/or | 7 | | modify it under the terms of the GNU Lesser General Public | 8 | | License as published by the Free Software Foundation; either | 9 | | version 2.1 of the License, or (at your option) any later version. | 10 | | | 11 | | This program is distributed in the hope that it will be useful, | 12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | | GNU Lesser General Public License for more details. | 15 | | | 16 | | You should have received a copy of the GNU Lesser General Public | 17 | | License along with this library; if not, write to the Free Software | 18 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 19 | | 02110-1301, USA | 20 | | | 21 | +-------------------------------------------------------------------------+ 22 | | spine: a backend data gatherer for cacti | 23 | +-------------------------------------------------------------------------+ 24 | | This poller would not have been possible without: | 25 | | - Larry Adams (current development and enhancements) | 26 | | - Rivo Nurges (rrd support, mysql poller cache, misc functions) | 27 | | - RTG (core poller code, pthreads, snmp, autoconf examples) | 28 | | - Brady Alleman/Doug Warner (threading ideas, implementation details) | 29 | +-------------------------------------------------------------------------+ 30 | | - Cacti - http://www.cacti.net/ | 31 | +-------------------------------------------------------------------------+ 32 | */ 33 | 34 | extern void install_spine_signal_handler(void); 35 | extern void uninstall_spine_signal_handler(void); 36 | #ifdef HAVE_EXECINFO_H 37 | extern char ** backtrace_symbols(); 38 | extern int backtrace(); 39 | #endif 40 | -------------------------------------------------------------------------------- /keywords.c: -------------------------------------------------------------------------------- 1 | /* 2 | ex: set tabstop=4 shiftwidth=4 autoindent: 3 | +-------------------------------------------------------------------------+ 4 | | Copyright (C) 2004-2024 The Cacti Group | 5 | | | 6 | | This program is free software; you can redistribute it and/or | 7 | | modify it under the terms of the GNU Lesser General Public | 8 | | License as published by the Free Software Foundation; either | 9 | | version 2.1 of the License, or (at your option) any later version. | 10 | | | 11 | | This program is distributed in the hope that it will be useful, | 12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | | GNU Lesser General Public License for more details. | 15 | | | 16 | | You should have received a copy of the GNU Lesser General Public | 17 | | License along with this library; if not, write to the Free Software | 18 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 19 | | 02110-1301, USA | 20 | | | 21 | +-------------------------------------------------------------------------+ 22 | | spine: a backend data gatherer for cacti | 23 | +-------------------------------------------------------------------------+ 24 | | This poller would not have been possible without: | 25 | | - Larry Adams (current development and enhancements) | 26 | | - Rivo Nurges (rrd support, mysql poller cache, misc functions) | 27 | | - RTG (core poller code, pthreads, snmp, autoconf examples) | 28 | | - Brady Alleman/Doug Warner (threading ideas, implementation details) | 29 | +-------------------------------------------------------------------------+ 30 | | - Cacti - http://www.cacti.net/ | 31 | +-------------------------------------------------------------------------+ 32 | */ 33 | /*! 34 | * 35 | * This module provides keyword-lookup support for various spine 36 | * objects. The idea is that we can do a two-way translation: given 37 | * a token, return a printable name for it, and to take a word from 38 | * the user and return the numeric internal value. 39 | * 40 | * The center of the module is the table of keywords which map in 41 | * both directions word<-->value. Lookups are case insensitive, and 42 | * both direction 43 | * 44 | */ 45 | 46 | #include "common.h" 47 | #include "spine.h" 48 | 49 | struct keyword { 50 | const char *word; 51 | int value; 52 | }; 53 | 54 | /*! Log Level Structure 55 | * 56 | * Structure that helps map either an integer value to a text logging level or 57 | * vice versa. 58 | * 59 | */ 60 | static const struct keyword log_level[] = { 61 | { "NONE", POLLER_VERBOSITY_NONE }, 62 | { "LOW", POLLER_VERBOSITY_LOW }, 63 | { "MEDIUM", POLLER_VERBOSITY_MEDIUM }, 64 | { "HIGH", POLLER_VERBOSITY_HIGH }, 65 | { "DEBUG", POLLER_VERBOSITY_DEBUG }, 66 | 67 | { 0, 0 } /* ENDMARKER */ 68 | }; 69 | 70 | /*! Log Destination Structure 71 | * 72 | * Structure that helps map either an integer value to a text logging destination 73 | * or vice versa. 74 | * 75 | */ 76 | static const struct keyword logdest[] = { 77 | { "FILE", LOGDEST_FILE }, 78 | { "SYSLOG", LOGDEST_SYSLOG }, 79 | { "BOTH", LOGDEST_BOTH }, 80 | { "STDOUT", LOGDEST_STDOUT }, 81 | 82 | { 0, 0 } /* ENDMARKER */ 83 | }; 84 | 85 | /*! Poller Action Structure 86 | * 87 | * Structure that helps map either an integer value to a text poller action 88 | * or vice versa. 89 | * 90 | */ 91 | static const struct keyword actions[] = { 92 | { "SNMP", POLLER_ACTION_SNMP }, 93 | { "SCRIPT", POLLER_ACTION_SCRIPT }, 94 | { "PHPSCRIPT", POLLER_ACTION_PHP_SCRIPT_SERVER }, 95 | { "SNMP_CT", POLLER_ACTION_SNMP_COUNT }, 96 | { "SCRIPT_CT", POLLER_ACTION_SCRIPT_COUNT }, 97 | { "PHPSCRIPT_CT", POLLER_ACTION_PHP_SCRIPT_SERVER_COUNT }, 98 | 99 | { 0, 0 } /* ENDMARKER */ 100 | }; 101 | 102 | /*! \fn find_keyword_by_word(const struct keyword *tbl, const char *word, int dflt) 103 | * \brief takes a generic word and returns either TRUE or FALSE 104 | * \param tbl the table that contains the translation from text to boolean 105 | * \param word the word to compare against the table for the result 106 | * \param dflt the default value to be returned if the string can not be found 107 | * 108 | * Given a table of keywords and a user's word, look that word up in the 109 | * table and return the value associated with it. If the word is not found, 110 | * return the user-provide default value. 111 | * 112 | * The default-value parameter can be used for either the actual default 113 | * value of the parameter being searched for (say, LOGDEST_BOTH), or 114 | * a didn't-find-it value (say, -1) which the caller can key off of. 115 | * 116 | * NOTE: if the given word is all digits, it's parsed as a number and 117 | * returned numerically. 118 | * 119 | * \return TRUE, FALSE, or dflt depending on results of search 120 | * 121 | */ 122 | static int find_keyword_by_word(const struct keyword *tbl, const char *word, int dflt) 123 | { 124 | assert(tbl != 0); 125 | assert(word != 0); 126 | 127 | if (all_digits(word)) { 128 | return atoi(word); 129 | } 130 | 131 | for (; tbl->word; tbl++) { 132 | if (STRIMATCH(word, tbl->word)) { 133 | return tbl->value; 134 | } 135 | } 136 | 137 | return dflt; 138 | } 139 | 140 | /*! \fn static const char *find_keyword_by_value(const struct keyword *tbl, int value, const char *dflt) 141 | * \brief searches a table for text string based upon a numeric input value 142 | * \param tbl the table that contains the translation from text to boolean 143 | * \param word the word to compare against the table for the result 144 | * \param dflt the default value to be returned if the string can not be found 145 | * 146 | * Given a keyword table and a numeric value, find the printable word 147 | * associated with it. The *first* value found is returned (in case more 148 | * than one word maps to the same value), and if it's not found, the 149 | * user's default value is returned. 150 | * 151 | * The dflt value is allowed to be NULL. 152 | * 153 | * \return a string pointer to that matches the search criteria, or dflt 154 | * 155 | */ 156 | static const char *find_keyword_by_value(const struct keyword *tbl, int value, const char *dflt) { 157 | assert(tbl != 0); 158 | 159 | for (; tbl->word; tbl++ ) { 160 | if (tbl->value == value) { 161 | return tbl->word; 162 | } 163 | } 164 | 165 | return dflt; 166 | } 167 | 168 | const char *printable_log_level(int token) { 169 | return find_keyword_by_value(log_level, token, "-unknown-"); 170 | } 171 | 172 | int parse_log_level(const char *word, int dflt) { 173 | return find_keyword_by_word(log_level, word, dflt); 174 | } 175 | 176 | const char *printable_logdest(int token) { 177 | return find_keyword_by_value(logdest, token, "-unknown-"); 178 | } 179 | 180 | int parse_logdest(const char *word, int dflt) { 181 | return find_keyword_by_word(logdest, word, dflt); 182 | } 183 | 184 | const char *printable_action(int token) { 185 | return find_keyword_by_value(actions, token, "-unknown-"); 186 | } 187 | 188 | int parse_action(const char *word, int dflt) { 189 | return find_keyword_by_word(actions, word, dflt); 190 | } 191 | -------------------------------------------------------------------------------- /keywords.h: -------------------------------------------------------------------------------- 1 | /* 2 | ex: set tabstop=4 shiftwidth=4 autoindent: 3 | +-------------------------------------------------------------------------+ 4 | | Copyright (C) 2004-2024 The Cacti Group | 5 | | | 6 | | This program is free software; you can redistribute it and/or | 7 | | modify it under the terms of the GNU Lesser General Public | 8 | | License as published by the Free Software Foundation; either | 9 | | version 2.1 of the License, or (at your option) any later version. | 10 | | | 11 | | This program is distributed in the hope that it will be useful, | 12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | | GNU Lesser General Public License for more details. | 15 | | | 16 | | You should have received a copy of the GNU Lesser General Public | 17 | | License along with this library; if not, write to the Free Software | 18 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 19 | | 02110-1301, USA | 20 | | | 21 | +-------------------------------------------------------------------------+ 22 | | spine: a backend data gatherer for cacti | 23 | +-------------------------------------------------------------------------+ 24 | | This poller would not have been possible without: | 25 | | - Larry Adams (current development and enhancements) | 26 | | - Rivo Nurges (rrd support, mysql poller cache, misc functions) | 27 | | - RTG (core poller code, pthreads, snmp, autoconf examples) | 28 | | - Brady Alleman/Doug Warner (threading ideas, implementation details) | 29 | +-------------------------------------------------------------------------+ 30 | | - Cacti - http://www.cacti.net/ | 31 | +-------------------------------------------------------------------------+ 32 | */ 33 | 34 | extern const char *printable_log_level(int token); 35 | extern int parse_log_level(const char *word, int dflt); 36 | 37 | extern const char *printable_logdest(int token); 38 | extern int parse_logdest(const char *word, int dflt); 39 | 40 | extern const char *printable_action(int token); 41 | extern int parse_action(const char *word, int dflt); 42 | 43 | -------------------------------------------------------------------------------- /locks.c: -------------------------------------------------------------------------------- 1 | /* 2 | ex: set tabstop=4 shiftwidth=4 autoindent: 3 | +-------------------------------------------------------------------------+ 4 | | Copyright (C) 2004-2024 The Cacti Group | 5 | | | 6 | | This program is free software; you can redistribute it and/or | 7 | | modify it under the terms of the GNU Lesser General Public | 8 | | License as published by the Free Software Foundation; either | 9 | | version 2.1 of the License, or (at your option) any later version. | 10 | | | 11 | | This program is distributed in the hope that it will be useful, | 12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | | GNU Lesser General Public License for more details. | 15 | | | 16 | | You should have received a copy of the GNU Lesser General Public | 17 | | License along with this library; if not, write to the Free Software | 18 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 19 | | 02110-1301, USA | 20 | | | 21 | +-------------------------------------------------------------------------+ 22 | | spine: a backend data gatherer for cacti | 23 | +-------------------------------------------------------------------------+ 24 | | This poller would not have been possible without: | 25 | | - Larry Adams (current development and enhancements) | 26 | | - Rivo Nurges (rrd support, mysql poller cache, misc functions) | 27 | | - RTG (core poller code, pthreads, snmp, autoconf examples) | 28 | | - Brady Alleman/Doug Warner (threading ideas, implementation details) | 29 | +-------------------------------------------------------------------------+ 30 | | - Cacti - http://www.cacti.net/ | 31 | +-------------------------------------------------------------------------+ 32 | */ 33 | 34 | #include "common.h" 35 | #include "spine.h" 36 | 37 | /* 38 | * each lock requires a handful of parts: a mutex, an init structure, and an 39 | * init helper function. We are NOT allowed to use these in an array (doesn 40 | * not work with pthreads), so we are stuck setting these up individually. 41 | * This macro defines these helpers in a single step. 42 | */ 43 | 44 | #define DEFINE_SPINE_LOCK(name) \ 45 | static pthread_mutex_t name ## _lock; \ 46 | static pthread_once_t name ## _lock_o = PTHREAD_ONCE_INIT; \ 47 | static pthread_cond_t name ## _cond = PTHREAD_COND_INITIALIZER; \ 48 | static void init_ ## name ## _lock(void) { \ 49 | pthread_mutex_init(&name ## _lock, PTHREAD_MUTEXATTR_DEFAULT); \ 50 | } 51 | 52 | DEFINE_SPINE_LOCK(snmp) 53 | DEFINE_SPINE_LOCK(seteuid) 54 | DEFINE_SPINE_LOCK(ghbn) 55 | DEFINE_SPINE_LOCK(pool) 56 | DEFINE_SPINE_LOCK(syslog) 57 | DEFINE_SPINE_LOCK(php) 58 | DEFINE_SPINE_LOCK(php_proc_0) 59 | DEFINE_SPINE_LOCK(php_proc_1) 60 | DEFINE_SPINE_LOCK(php_proc_2) 61 | DEFINE_SPINE_LOCK(php_proc_3) 62 | DEFINE_SPINE_LOCK(php_proc_4) 63 | DEFINE_SPINE_LOCK(php_proc_5) 64 | DEFINE_SPINE_LOCK(php_proc_6) 65 | DEFINE_SPINE_LOCK(php_proc_7) 66 | DEFINE_SPINE_LOCK(php_proc_8) 67 | DEFINE_SPINE_LOCK(php_proc_9) 68 | DEFINE_SPINE_LOCK(php_proc_10) 69 | DEFINE_SPINE_LOCK(php_proc_11) 70 | DEFINE_SPINE_LOCK(php_proc_12) 71 | DEFINE_SPINE_LOCK(php_proc_13) 72 | DEFINE_SPINE_LOCK(php_proc_14) 73 | DEFINE_SPINE_LOCK(thdet) 74 | DEFINE_SPINE_LOCK(host_time) 75 | 76 | void init_mutexes() { 77 | pthread_once((pthread_once_t*) get_attr(LOCK_SNMP_O), init_snmp_lock); 78 | pthread_once((pthread_once_t*) get_attr(LOCK_SETEUID_O), init_seteuid_lock); 79 | pthread_once((pthread_once_t*) get_attr(LOCK_GHBN_O), init_ghbn_lock); 80 | pthread_once((pthread_once_t*) get_attr(LOCK_POOL_O), init_pool_lock); 81 | pthread_once((pthread_once_t*) get_attr(LOCK_SYSLOG_O), init_syslog_lock); 82 | pthread_once((pthread_once_t*) get_attr(LOCK_PHP_O), init_php_lock); 83 | pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_0_O), init_php_proc_0_lock); 84 | pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_1_O), init_php_proc_1_lock); 85 | pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_2_O), init_php_proc_2_lock); 86 | pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_3_O), init_php_proc_3_lock); 87 | pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_4_O), init_php_proc_4_lock); 88 | pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_5_O), init_php_proc_5_lock); 89 | pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_6_O), init_php_proc_6_lock); 90 | pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_7_O), init_php_proc_7_lock); 91 | pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_8_O), init_php_proc_8_lock); 92 | pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_9_O), init_php_proc_9_lock); 93 | pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_10_O), init_php_proc_10_lock); 94 | pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_11_O), init_php_proc_11_lock); 95 | pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_12_O), init_php_proc_12_lock); 96 | pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_13_O), init_php_proc_13_lock); 97 | pthread_once((pthread_once_t*) get_attr(LOCK_PHP_PROC_14_O), init_php_proc_14_lock); 98 | pthread_once((pthread_once_t*) get_attr(LOCK_THDET_O), init_thdet_lock); 99 | pthread_once((pthread_once_t*) get_attr(LOCK_HOST_TIME_O), init_host_time_lock); 100 | } 101 | 102 | const char* get_name(int lock) { 103 | switch (lock) { 104 | case LOCK_SNMP: return "snmp"; 105 | case LOCK_SETEUID: return "seteuid"; 106 | case LOCK_GHBN: return "ghbn"; 107 | case LOCK_POOL: return "pool"; 108 | case LOCK_SYSLOG: return "syslog"; 109 | case LOCK_PHP: return "php"; 110 | case LOCK_PHP_PROC_0: return "php_proc_0"; 111 | case LOCK_PHP_PROC_1: return "php_proc_1"; 112 | case LOCK_PHP_PROC_2: return "php_proc_2"; 113 | case LOCK_PHP_PROC_3: return "php_proc_3"; 114 | case LOCK_PHP_PROC_4: return "php_proc_4"; 115 | case LOCK_PHP_PROC_5: return "php_proc_5"; 116 | case LOCK_PHP_PROC_6: return "php_proc_6"; 117 | case LOCK_PHP_PROC_7: return "php_proc_7"; 118 | case LOCK_PHP_PROC_8: return "php_proc_8"; 119 | case LOCK_PHP_PROC_9: return "php_proc_9"; 120 | case LOCK_PHP_PROC_10: return "php_proc_10"; 121 | case LOCK_PHP_PROC_11: return "php_proc_11"; 122 | case LOCK_PHP_PROC_12: return "php_proc_12"; 123 | case LOCK_PHP_PROC_13: return "php_proc_13"; 124 | case LOCK_PHP_PROC_14: return "php_proc_14"; 125 | case LOCK_THDET: return "thdet"; 126 | case LOCK_HOST_TIME: return "host_time"; 127 | } 128 | 129 | return "Unknown lock"; 130 | } 131 | 132 | pthread_cond_t* get_cond(int lock) { 133 | pthread_cond_t *ret_val = NULL; 134 | 135 | switch (lock) { 136 | case LOCK_SNMP: ret_val = &snmp_cond; break; 137 | case LOCK_SETEUID: ret_val = &seteuid_cond; break; 138 | case LOCK_GHBN: ret_val = &ghbn_cond; break; 139 | case LOCK_POOL: ret_val = &pool_cond; break; 140 | case LOCK_SYSLOG: ret_val = &syslog_cond; break; 141 | case LOCK_PHP: ret_val = &php_cond; break; 142 | case LOCK_PHP_PROC_0: ret_val = &php_proc_0_cond; break; 143 | case LOCK_PHP_PROC_1: ret_val = &php_proc_1_cond; break; 144 | case LOCK_PHP_PROC_2: ret_val = &php_proc_2_cond; break; 145 | case LOCK_PHP_PROC_3: ret_val = &php_proc_3_cond; break; 146 | case LOCK_PHP_PROC_4: ret_val = &php_proc_4_cond; break; 147 | case LOCK_PHP_PROC_5: ret_val = &php_proc_5_cond; break; 148 | case LOCK_PHP_PROC_6: ret_val = &php_proc_6_cond; break; 149 | case LOCK_PHP_PROC_7: ret_val = &php_proc_7_cond; break; 150 | case LOCK_PHP_PROC_8: ret_val = &php_proc_8_cond; break; 151 | case LOCK_PHP_PROC_9: ret_val = &php_proc_9_cond; break; 152 | case LOCK_PHP_PROC_10: ret_val = &php_proc_10_cond; break; 153 | case LOCK_PHP_PROC_11: ret_val = &php_proc_11_cond; break; 154 | case LOCK_PHP_PROC_12: ret_val = &php_proc_12_cond; break; 155 | case LOCK_PHP_PROC_13: ret_val = &php_proc_13_cond; break; 156 | case LOCK_PHP_PROC_14: ret_val = &php_proc_14_cond; break; 157 | case LOCK_THDET: ret_val = &thdet_cond; break; 158 | case LOCK_HOST_TIME: ret_val = &host_time_cond; break; 159 | } 160 | 161 | SPINE_LOG_DEVDBG(("LOCKS: [RET] Returning cond for %s", get_name(lock))); 162 | 163 | return ret_val; 164 | } 165 | 166 | pthread_mutex_t* get_lock(int lock) { 167 | pthread_mutex_t *ret_val = NULL; 168 | 169 | switch (lock) { 170 | case LOCK_SNMP: ret_val = &snmp_lock; break; 171 | case LOCK_SETEUID: ret_val = &seteuid_lock; break; 172 | case LOCK_GHBN: ret_val = &ghbn_lock; break; 173 | case LOCK_POOL: ret_val = &pool_lock; break; 174 | case LOCK_SYSLOG: ret_val = &syslog_lock; break; 175 | case LOCK_PHP: ret_val = &php_lock; break; 176 | case LOCK_PHP_PROC_0: ret_val = &php_proc_0_lock; break; 177 | case LOCK_PHP_PROC_1: ret_val = &php_proc_1_lock; break; 178 | case LOCK_PHP_PROC_2: ret_val = &php_proc_2_lock; break; 179 | case LOCK_PHP_PROC_3: ret_val = &php_proc_3_lock; break; 180 | case LOCK_PHP_PROC_4: ret_val = &php_proc_4_lock; break; 181 | case LOCK_PHP_PROC_5: ret_val = &php_proc_5_lock; break; 182 | case LOCK_PHP_PROC_6: ret_val = &php_proc_6_lock; break; 183 | case LOCK_PHP_PROC_7: ret_val = &php_proc_7_lock; break; 184 | case LOCK_PHP_PROC_8: ret_val = &php_proc_8_lock; break; 185 | case LOCK_PHP_PROC_9: ret_val = &php_proc_9_lock; break; 186 | case LOCK_PHP_PROC_10: ret_val = &php_proc_10_lock; break; 187 | case LOCK_PHP_PROC_11: ret_val = &php_proc_11_lock; break; 188 | case LOCK_PHP_PROC_12: ret_val = &php_proc_12_lock; break; 189 | case LOCK_PHP_PROC_13: ret_val = &php_proc_13_lock; break; 190 | case LOCK_PHP_PROC_14: ret_val = &php_proc_14_lock; break; 191 | case LOCK_THDET: ret_val = &thdet_lock; break; 192 | case LOCK_HOST_TIME: ret_val = &host_time_lock; break; 193 | } 194 | 195 | SPINE_LOG_DEVDBG(("LOCKS: [RET] Returning lock for %s", get_name(lock))); 196 | 197 | return ret_val; 198 | } 199 | 200 | pthread_once_t* get_attr(int locko) { 201 | pthread_once_t *ret_val = NULL; 202 | 203 | switch (locko) { 204 | case LOCK_SNMP_O: ret_val = &snmp_lock_o; break; 205 | case LOCK_SETEUID_O: ret_val = &seteuid_lock_o; break; 206 | case LOCK_GHBN_O: ret_val = &ghbn_lock_o; break; 207 | case LOCK_POOL_O: ret_val = &pool_lock_o; break; 208 | case LOCK_SYSLOG_O: ret_val = &syslog_lock_o; break; 209 | case LOCK_PHP_O: ret_val = &php_lock_o; break; 210 | case LOCK_PHP_PROC_0_O: ret_val = &php_proc_0_lock_o; break; 211 | case LOCK_PHP_PROC_1_O: ret_val = &php_proc_1_lock_o; break; 212 | case LOCK_PHP_PROC_2_O: ret_val = &php_proc_2_lock_o; break; 213 | case LOCK_PHP_PROC_3_O: ret_val = &php_proc_3_lock_o; break; 214 | case LOCK_PHP_PROC_4_O: ret_val = &php_proc_4_lock_o; break; 215 | case LOCK_PHP_PROC_5_O: ret_val = &php_proc_5_lock_o; break; 216 | case LOCK_PHP_PROC_6_O: ret_val = &php_proc_6_lock_o; break; 217 | case LOCK_PHP_PROC_7_O: ret_val = &php_proc_7_lock_o; break; 218 | case LOCK_PHP_PROC_8_O: ret_val = &php_proc_8_lock_o; break; 219 | case LOCK_PHP_PROC_9_O: ret_val = &php_proc_9_lock_o; break; 220 | case LOCK_PHP_PROC_10_O: ret_val = &php_proc_10_lock_o; break; 221 | case LOCK_PHP_PROC_11_O: ret_val = &php_proc_11_lock_o; break; 222 | case LOCK_PHP_PROC_12_O: ret_val = &php_proc_12_lock_o; break; 223 | case LOCK_PHP_PROC_13_O: ret_val = &php_proc_13_lock_o; break; 224 | case LOCK_PHP_PROC_14_O: ret_val = &php_proc_14_lock_o; break; 225 | case LOCK_THDET_O: ret_val = &thdet_lock_o; break; 226 | case LOCK_HOST_TIME_O: ret_val = &host_time_lock_o; break; 227 | } 228 | 229 | SPINE_LOG_DEVDBG(("LOCKS: [RET] Returning attr for %s", get_name(locko))); 230 | 231 | return ret_val; 232 | } 233 | 234 | void thread_mutex_lock(int mutex) { 235 | SPINE_LOG_DEVDBG(("LOCKS: [START] Mutex lock for %s", get_name(mutex))); 236 | pthread_mutex_lock(get_lock(mutex)); 237 | SPINE_LOG_DEVDBG(("LOCKS: [END] Mutex lock for %s", get_name(mutex))); 238 | } 239 | 240 | void thread_mutex_unlock(int mutex) { 241 | SPINE_LOG_DEVDBG(("LOCKS: [START] Mutex unlock for %s", get_name(mutex))); 242 | pthread_mutex_unlock(get_lock(mutex)); 243 | SPINE_LOG_DEVDBG(("LOCKS: [END] Mutex unlock for %s", get_name(mutex))); 244 | } 245 | 246 | int thread_mutex_trylock(int mutex) { 247 | SPINE_LOG_DEVDBG(("LOCKS: [START] Mutex try lock for %s", get_name(mutex))); 248 | int ret_val = pthread_mutex_trylock(get_lock(mutex)); 249 | SPINE_LOG_DEVDBG(("LOCKS: [END] Mutex try lock for %s, result = %d", get_name(mutex), ret_val)); 250 | return ret_val; 251 | } 252 | -------------------------------------------------------------------------------- /locks.h: -------------------------------------------------------------------------------- 1 | /* 2 | ex: set tabstop=4 shiftwidth=4 autoindent: 3 | +-------------------------------------------------------------------------+ 4 | | Copyright (C) 2004-2024 The Cacti Group | 5 | | | 6 | | This program is free software; you can redistribute it and/or | 7 | | modify it under the terms of the GNU Lesser General Public | 8 | | License as published by the Free Software Foundation; either | 9 | | version 2.1 of the License, or (at your option) any later version. | 10 | | | 11 | | This program is distributed in the hope that it will be useful, | 12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | | GNU Lesser General Public License for more details. | 15 | | | 16 | | You should have received a copy of the GNU Lesser General Public | 17 | | License along with this library; if not, write to the Free Software | 18 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 19 | | 02110-1301, USA | 20 | | | 21 | +-------------------------------------------------------------------------+ 22 | | spine: a backend data gatherer for cacti | 23 | +-------------------------------------------------------------------------+ 24 | | This poller would not have been possible without: | 25 | | - Larry Adams (current development and enhancements) | 26 | | - Rivo Nurges (rrd support, mysql poller cache, misc functions) | 27 | | - RTG (core poller code, pthreads, snmp, autoconf examples) | 28 | | - Brady Alleman/Doug Warner (threading ideas, implementation details) | 29 | +-------------------------------------------------------------------------+ 30 | | - Cacti - http://www.cacti.net/ | 31 | +-------------------------------------------------------------------------+ 32 | */ 33 | 34 | extern void init_mutexes(void); 35 | extern void thread_mutex_lock(int mutex); 36 | extern void thread_mutex_unlock(int mutex); 37 | extern int thread_mutex_trylock(int mutex); 38 | extern pthread_cond_t* get_cond(int lock); 39 | extern pthread_mutex_t* get_lock(int lock); 40 | extern pthread_once_t* get_attr(int locko); 41 | -------------------------------------------------------------------------------- /nft_popen.c: -------------------------------------------------------------------------------- 1 | /* 2 | +-------------------------------------------------------------------------+ 3 | | Copyright (C) 2004-2024 The Cacti Group | 4 | | | 5 | | This program is free software; you can redistribute it and/or | 6 | | modify it under the terms of the GNU General Public License | 7 | | as published by the Free Software Foundation; either version 2 | 8 | | of the License, or (at your option) any later version. | 9 | | | 10 | | This program is distributed in the hope that it will be useful, | 11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | | GNU General Public License for more details. | 14 | +-------------------------------------------------------------------------+ 15 | | Cacti: The Complete RRDtool-based Graphing Solution | 16 | +-------------------------------------------------------------------------+ 17 | | This code is designed, written, and maintained by the Cacti Group. See | 18 | | about.php and/or the AUTHORS file for specific developer information. | 19 | +-------------------------------------------------------------------------+ 20 | | http://www.cacti.net/ | 21 | +-------------------------------------------------------------------------+ 22 | */ 23 | 24 | /******************************************************************************* 25 | ex: set tabstop=4 shiftwidth=4 autoindent: 26 | * (C) Xenadyne Inc. 2002. All Rights Reserved 27 | * 28 | * Permission to use, copy, modify and distribute this software for 29 | * any purpose and without fee is hereby granted, provided that the 30 | * above copyright notice appears in all copies. Also note the 31 | * University of California copyright below. 32 | * 33 | * XENADYNE INC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 34 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. 35 | * IN NO EVENT SHALL XENADYNE BE LIABLE FOR ANY SPECIAL, INDIRECT OR 36 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM THE 37 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 38 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 39 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 40 | * 41 | * File: nft_popen.c 42 | * 43 | * Description: A thread-safe replacement for popen()/pclose(). 44 | * 45 | * This is a thread-safe variant of popen that does unbuffered IO, to 46 | * avoid running afoul of Solaris's inability to fdopen when fd > 255. 47 | * 48 | ******************************************************************************* 49 | */ 50 | 51 | /* 52 | * Copyright (c) 1988, 1993 53 | * The Regents of the University of California. All rights reserved. 54 | * 55 | * This code is derived from software written by Ken Arnold and 56 | * published in UNIX Review, Vol. 6, No. 8. 57 | * 58 | * Redistribution and use in source and binary forms, with or without 59 | * modification, are permitted provided that the following conditions 60 | * are met: 61 | * 1. Redistributions of source code must retain the above copyright 62 | * notice, this list of conditions and the following disclaimer. 63 | * 2. Redistributions in binary form must reproduce the above copyright 64 | * notice, this list of conditions and the following disclaimer in the 65 | * documentation and/or other materials provided with the distribution. 66 | * 3. All advertising materials mentioning features or use of this software 67 | * must display the following acknowledgement: 68 | * This product includes software developed by the University of 69 | * California, Berkeley and its contributors. 70 | * 4. Neither the name of the University nor the names of its contributors 71 | * may be used to endorse or promote products derived from this software 72 | * without specific prior written permission. 73 | * 74 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 75 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 76 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 77 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 78 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 79 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 80 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 81 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 82 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 83 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 84 | * SUCH DAMAGE. 85 | */ 86 | 87 | #include "common.h" 88 | #include "spine.h" 89 | 90 | /* An instance of this struct is created for each popen() fd. */ 91 | static struct pid 92 | { 93 | struct pid *next; 94 | int fd; 95 | pid_t pid; 96 | } * PidList; 97 | 98 | /* Serialize access to PidList. */ 99 | static pthread_mutex_t ListMutex = PTHREAD_MUTEX_INITIALIZER; 100 | 101 | static void close_cleanup(void *); 102 | 103 | /*! ------------------------------------------------------------------------------ 104 | * 105 | * nft_popen 106 | * 107 | * The nft_popen() function forks a command in a child process, and returns 108 | * a pipe that is connected to the child's standard input and output. It is 109 | * like the standard popen() call, except that it does not dfopen() the pipe 110 | * file descriptor in order to return a stdio FILE *. This is useful if you 111 | * wish to use select()- or poll()-driven IO. 112 | * 113 | * The mode argument is defined as in standard popen(). 114 | * 115 | * On success, returns a file descriptor, or -1 on error. 116 | * On failure, returns -1, with errno set to one of: 117 | * EINVAL The mode argument is incorrect. 118 | * EMFILE pipe() failed. 119 | * ENFILE pipe() failed. 120 | * ENOMEM malloc() failed. 121 | * EAGAIN fork() failed. 122 | * 123 | *------------------------------------------------------------------------------ 124 | */ 125 | int nft_popen(const char * command, const char * type) { 126 | struct pid *cur; 127 | struct pid *p; 128 | int pdes[2]; 129 | int fd, pid, twoway; 130 | char *argv[4]; 131 | int cancel_state; 132 | extern char **environ; 133 | int retry_count = 0; 134 | 135 | /* On platforms where pipe() is bidirectional, 136 | * "r+" gives two-way communication. 137 | */ 138 | if (strchr(type, '+')) { 139 | twoway = 1; 140 | type = "r+"; 141 | }else { 142 | twoway = 0; 143 | if ((*type != 'r' && *type != 'w') || type[1]) { 144 | errno = EINVAL; 145 | return -1; 146 | } 147 | } 148 | 149 | if (pipe(pdes) < 0) 150 | return -1; 151 | 152 | /* Disable thread cancellation from this point forward. */ 153 | pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state); 154 | 155 | if ((cur = malloc(sizeof(struct pid))) == NULL) { 156 | (void)close(pdes[0]); 157 | (void)close(pdes[1]); 158 | pthread_setcancelstate(cancel_state, NULL); 159 | return -1; 160 | } 161 | 162 | argv[0] = "sh"; 163 | argv[1] = "-c"; 164 | argv[2] = (char *)command; 165 | argv[3] = NULL; 166 | 167 | /* Lock the list mutex prior to forking, to ensure that 168 | * the child process sees PidList in a consistent list state. 169 | */ 170 | pthread_mutex_lock(&ListMutex); 171 | 172 | /* Fork. */ 173 | retry: 174 | switch (pid = vfork()) { 175 | case -1: /* Error. */ 176 | switch (errno) { 177 | case EAGAIN: 178 | if (retry_count < 3) { 179 | retry_count++; 180 | #ifndef SOLAR_THREAD 181 | /* take a moment */ 182 | usleep(50000); 183 | #endif 184 | goto retry; 185 | }else{ 186 | SPINE_LOG(("ERROR: SCRIPT: Could not fork. Out of Resources nft_popen.c")); 187 | } 188 | case ENOMEM: 189 | if (retry_count < 3) { 190 | retry_count++; 191 | #ifndef SOLAR_THREAD 192 | /* take a moment */ 193 | usleep(50000); 194 | #endif 195 | goto retry; 196 | }else{ 197 | SPINE_LOG(("ERROR: SCRIPT Could not fork. Out of Memory nft_popen.c")); 198 | } 199 | default: 200 | SPINE_LOG(("ERROR: SCRIPT Could not fork. Unknown Reason nft_popen.c")); 201 | } 202 | 203 | (void)close(pdes[0]); 204 | (void)close(pdes[1]); 205 | pthread_mutex_unlock(&ListMutex); 206 | pthread_setcancelstate(cancel_state, NULL); 207 | 208 | return -1; 209 | /* NOTREACHED */ 210 | case 0: /* Child. */ 211 | if (*type == 'r') { 212 | /* The dup2() to STDIN_FILENO is repeated to avoid 213 | * writing to pdes[1], which might corrupt the 214 | * parent's copy. This isn't good enough in 215 | * general, since the _exit() is no return, so 216 | * the compiler is free to corrupt all the local 217 | * variables. 218 | */ 219 | (void)close(pdes[0]); 220 | if (pdes[1] != STDOUT_FILENO) { 221 | (void)dup2(pdes[1], STDOUT_FILENO); 222 | (void)close(pdes[1]); 223 | if (twoway) 224 | (void)dup2(STDOUT_FILENO, STDIN_FILENO); 225 | }else if (twoway && (pdes[1] != STDIN_FILENO)) 226 | (void)dup2(pdes[1], STDIN_FILENO); 227 | }else { 228 | if (pdes[0] != STDIN_FILENO) { 229 | (void)dup2(pdes[0], STDIN_FILENO); 230 | (void)close(pdes[0]); 231 | } 232 | (void)close(pdes[1]); 233 | } 234 | 235 | /* Close all the other pipes in the child process. 236 | * Posix.2 requires this, tho I don't know why. 237 | */ 238 | for (p = PidList; p; p = p->next) 239 | (void)close(p->fd); 240 | 241 | /* Execute the command. */ 242 | #if defined(__CYGWIN__) 243 | if (set.cygwinshloc == 0) { 244 | execve("sh.exe", argv, environ); 245 | }else{ 246 | execve("/bin/sh", argv, environ); 247 | } 248 | #else 249 | execve("/bin/sh", argv, environ); 250 | #endif 251 | _exit(127); 252 | /* NOTREACHED */ 253 | } 254 | 255 | /* Parent. */ 256 | if (*type == 'r') { 257 | fd = pdes[0]; 258 | (void)close(pdes[1]); 259 | }else { 260 | fd = pdes[1]; 261 | (void)close(pdes[0]); 262 | } 263 | 264 | /* Link into list of file descriptors. */ 265 | cur->fd = fd; 266 | cur->pid = pid; 267 | cur->next = PidList; 268 | PidList = cur; 269 | 270 | /* Unlock the mutex, and restore caller's cancellation state. */ 271 | pthread_mutex_unlock(&ListMutex); 272 | pthread_setcancelstate(cancel_state, NULL); 273 | 274 | return fd; 275 | } 276 | 277 | /*! ------------------------------------------------------------------------------ 278 | * 279 | * nft_pchild 280 | * 281 | * Get the pid of the child process for an fd created by ntf_popen(). 282 | * 283 | * On success, the pid of the child process is returned. 284 | * On failure, nft_pchild() returns -1, with errno set to: 285 | * 286 | * EBADF The fd is not an active nft_popen() file descriptor. 287 | * 288 | *------------------------------------------------------------------------------ 289 | */ 290 | int nft_pchild(int fd) { 291 | struct pid *cur; 292 | pid_t pid = 0; 293 | 294 | /* Find the appropriate file descriptor. */ 295 | pthread_mutex_lock(&ListMutex); 296 | for (cur = PidList; cur; cur = cur->next) 297 | if (cur->fd == fd) { 298 | pid = cur->pid; 299 | break; 300 | } 301 | 302 | pthread_mutex_unlock(&ListMutex); 303 | 304 | if (cur == NULL) { 305 | errno = EBADF; 306 | return -1; 307 | } 308 | 309 | return pid; 310 | } 311 | 312 | /*! ------------------------------------------------------------------------------ 313 | * 314 | * nft_pclose 315 | * 316 | * Close the pipe and wait for the status of the child process. 317 | * 318 | * On success, the exit status of the child process is returned. 319 | * On failure, nft_pclose() returns -1, with errno set to: 320 | * 321 | * EBADF The fd is not an active popen() file descriptor. 322 | * ECHILD The waitpid() call failed. 323 | * 324 | * This call is cancellable. 325 | * 326 | *------------------------------------------------------------------------------ 327 | */ 328 | int 329 | nft_pclose(int fd) 330 | { 331 | struct pid *cur; 332 | int pstat; 333 | pid_t pid; 334 | 335 | /* Find the appropriate file descriptor. */ 336 | pthread_mutex_lock(&ListMutex); 337 | 338 | for (cur = PidList; cur; cur = cur->next) 339 | if (cur->fd == fd) break; 340 | 341 | pthread_mutex_unlock(&ListMutex); 342 | 343 | if (cur == NULL) { 344 | errno = EBADF; 345 | return -1; 346 | } 347 | 348 | /* The close and waitpid calls below are cancellation points. 349 | * We want to ensure that the fd is closed and the PidList 350 | * entry freed despite cancellation, so push a cleanup handler. 351 | */ 352 | pthread_cleanup_push(close_cleanup, cur); 353 | 354 | /* end the process nicely and then forcefully */ 355 | (void)close(fd); 356 | 357 | cur->fd = -1; /* Prevent the fd being closed twice. */ 358 | 359 | do { pid = waitpid(cur->pid, &pstat, 0); 360 | } while (pid == -1 && errno == EINTR); 361 | 362 | pthread_cleanup_pop(1); /* Execute the cleanup handler. */ 363 | 364 | return (pid == -1 ? -1 : pstat); 365 | } 366 | 367 | /*! ------------------------------------------------------------------------------ 368 | * close_cleanup - close the pipe and free the pidlist entry. 369 | *------------------------------------------------------------------------------ 370 | */ 371 | static void 372 | close_cleanup(void * arg) 373 | { 374 | struct pid * cur = arg; 375 | struct pid * prev; 376 | 377 | /* Close the pipe fd if necessary. */ 378 | if (cur->fd >= 0) { 379 | (void)close(cur->fd); 380 | } 381 | 382 | /* Remove the entry from the linked list. */ 383 | pthread_mutex_lock(&ListMutex); 384 | 385 | if (PidList == cur) { 386 | PidList = cur->next; 387 | }else{ 388 | for (prev = PidList; prev; prev = prev->next) 389 | if (prev->next == cur) { 390 | prev->next = cur->next; 391 | break; 392 | } 393 | 394 | assert(prev != NULL); /* Search should not fail */ 395 | } 396 | 397 | pthread_mutex_unlock(&ListMutex); 398 | 399 | free(cur); 400 | } 401 | 402 | -------------------------------------------------------------------------------- /nft_popen.h: -------------------------------------------------------------------------------- 1 | /* 2 | +-------------------------------------------------------------------------+ 3 | | Copyright (C) 2004-2024 The Cacti Group | 4 | | | 5 | | This program is free software; you can redistribute it and/or | 6 | | modify it under the terms of the GNU General Public License | 7 | | as published by the Free Software Foundation; either version 2 | 8 | | of the License, or (at your option) any later version. | 9 | | | 10 | | This program is distributed in the hope that it will be useful, | 11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 | | GNU General Public License for more details. | 14 | +-------------------------------------------------------------------------+ 15 | | Cacti: The Complete RRDtool-based Graphing Solution | 16 | +-------------------------------------------------------------------------+ 17 | | This code is designed, written, and maintained by the Cacti Group. See | 18 | | about.php and/or the AUTHORS file for specific developer information. | 19 | +-------------------------------------------------------------------------+ 20 | | http://www.cacti.net/ | 21 | +-------------------------------------------------------------------------+ 22 | */ 23 | 24 | /****************************************************************************** 25 | ex: set tabstop=4 shiftwidth=4 autoindent: 26 | * 27 | * (C) Copyright Xenadyne, Inc. 2002 All rights reserved. 28 | * 29 | * Permission to use, copy, modify and distribute this software for 30 | * any purpose and without fee is hereby granted, provided that the 31 | * above copyright notice appears in all copies. 32 | * 33 | * XENADYNE INC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 34 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. 35 | * IN NO EVENT SHALL XENADYNE BE LIABLE FOR ANY SPECIAL, INDIRECT OR 36 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM THE 37 | * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 38 | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 39 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 40 | * 41 | * File: nft_popen.h 42 | * 43 | * PURPOSE 44 | * 45 | * Thread-safe substitute for popen() that doesn't use stdio streams. 46 | * 47 | ****************************************************************************** 48 | */ 49 | 50 | /*! 51 | * The nft_popen() function forks a command in a child process, and returns 52 | * a pipe that is connected to the child's standard input and output. It is 53 | * like the standard popen() call, except that it returns the file descriptor, 54 | * instead of a stdio stream created by fdopen(). The file descriptor can be 55 | * used with select() or poll(), or the caller can use fdopen() if a stdio 56 | * FILE* is preferable. 57 | * 58 | * The mode argument is defined as in standard popen(). 59 | * 60 | * On success, returns a file descriptor, or -1 on error. 61 | * On failure, returns -1, with errno set to one of: 62 | * EINVAL The mode argument is incorrect. 63 | * EMFILE pipe() failed. 64 | * ENFILE pipe() failed. 65 | * ENOMEM malloc() failed. 66 | * EAGAIN fork() failed. 67 | */ 68 | extern int nft_popen(const char * command, const char * mode); 69 | 70 | /*! 71 | * nft_pchild 72 | * 73 | * Get the pid of the child process for an fd created by ntf_popen(). 74 | * 75 | * On success, the pid of the child process is returned. 76 | * On failure, nft_pchild() returns -1, with errno set to: 77 | * 78 | * EBADF The fd is not an active nft_popen() file descriptor. 79 | */ 80 | extern int nft_pchild(int fd); 81 | 82 | /*! 83 | * nft_pclose 84 | * 85 | * Close the pipe and wait for the status of the child process. 86 | * 87 | * On success, the exit status of the child process is returned. 88 | * On failure, nft_pclose() returns -1, with errno set to: 89 | * 90 | * EBADF The fd is not an active popen() file descriptor. 91 | * ECHILD waitpid() failed. 92 | */ 93 | extern int nft_pclose(int fd); 94 | -------------------------------------------------------------------------------- /package: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | " 36 | echo "" 37 | echo " - Designated version for build (required)" 38 | echo "" 39 | } 40 | 41 | # Sanity checks 42 | [ ! -e configure.ac ] && echo "ERROR: Your current working directory must be the SVN check out of Spine" && exit -1 43 | 44 | if [ "${1}x" = "--helpx" -o "${1}x" = "-hx" ]; then 45 | display_help 46 | exit 0 47 | fi 48 | 49 | if [ -z "${1}" ]; then 50 | echo "" 51 | echo "ERROR: Invalid syntax, missing required argument" 52 | echo "" 53 | display_help 54 | exit -1 55 | fi 56 | VERSION=${1} 57 | 58 | # Perform packaging 59 | echo "" 60 | echo "----------------------------------------------------------------------------" 61 | echo "Spine package builder" 62 | echo " Version: ${VERSION}" 63 | echo "----------------------------------------------------------------------------" 64 | 65 | # Clean up previous builds 66 | if [ -e ${TMP_DIR}/cacti-spine-${VERSION} ]; then 67 | echo "INFO: Removing previous build ${TMP_DIR}/cacti-spine-${VERSION}..." 68 | rm -Rf ${TMP_DIR}/cacti-spine-${VERSION} > /dev/null 2>&1 69 | [ $? -gt 1 ] && echo "ERROR: Unable to remove directory: ${TMP_DIR}/cacti-spine-${VERSION}" && exit -1 70 | fi 71 | if [ -e ${TMP_DIR}/cacti-spine-${VERSION}.tar.gz ]; then 72 | rm -Rf ${TMP_DIR}/cacti-spine-${VERSION}.tar.gz > /dev/null 2>&1 73 | [ $? -gt 1 ] && echo "ERROR: Unable to remove file: ${TMP_DIR}/cacti-spine-${VERSION}.tar.gz" && exit -1 74 | fi 75 | 76 | # Copy repository 77 | mkdir -p ${TMP_DIR}/cacti-spine-${VERSION} > /dev/null 2>&1 78 | tar -cf - --exclude 'package' --exclude '.svn' --exclude '.travis.yml' * | (cd ${TMP_DIR}/cacti-spine-${VERSION}; tar -xf -) 79 | [ $? -gt 0 ] && echo "ERROR: Unable to repository to ${TMP_DIR}/cacti-spine-${VERSION}" && exit -1 80 | 81 | # Change working directory 82 | pushd ${TMP_DIR}/cacti-spine-${VERSION} > /dev/null 2>&1 83 | 84 | # Get version from source files, warn if different than defined for build 85 | SRC_VERSION=`cat configure.ac | grep AC_INIT | awk -F, '{print $2}' | sed 's/ //g'` 86 | if [ "${SRC_VERSION}" != "${VERSION}" ]; then 87 | echo "WARNING: Build version and source version are not the same"; 88 | echo "WARNING: Build Version: ${VERSION}" 89 | echo "WARNING: Source Version: ${SRC_VERSION}" 90 | fi 91 | 92 | # Call bootstrap 93 | echo "INFO: call bootstrap..." 94 | ./bootstrap 95 | 96 | # Check working directory 97 | cd ${TMP_DIR}/ 98 | 99 | # Package it 100 | echo "INFO: Packaging..." 101 | tar -zcf cacti-spine-${VERSION}.tar.gz cacti-spine-${VERSION} 102 | [ $? -gt 1 ] && echo "ERROR: Unable to package" && exit -1 103 | 104 | # Change working directory 105 | popd > /dev/null 2>&1 106 | 107 | # Clean up 108 | echo "INFO: Cleaning up build directory..." 109 | rm -rf ${TMP_DIR}/cacti-spine-${VERSION} > /dev/null 2>&1 110 | 111 | # Display file locations 112 | echo "INFO: Completed..." 113 | echo "" 114 | echo "Package file: ${TMP_DIR}/cacti-spine-${VERSION}.tar.gz" 115 | echo "" 116 | 117 | exit 0 118 | 119 | -------------------------------------------------------------------------------- /php.c: -------------------------------------------------------------------------------- 1 | /* 2 | ex: set tabstop=4 shiftwidth=4 autoindent: 3 | +-------------------------------------------------------------------------+ 4 | | Copyright (C) 2004-2024 The Cacti Group | 5 | | | 6 | | This program is free software; you can redistribute it and/or | 7 | | modify it under the terms of the GNU Lesser General Public | 8 | | License as published by the Free Software Foundation; either | 9 | | version 2.1 of the License, or (at your option) any later version. | 10 | | | 11 | | This program is distributed in the hope that it will be useful, | 12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | | GNU Lesser General Public License for more details. | 15 | | | 16 | | You should have received a copy of the GNU Lesser General Public | 17 | | License along with this library; if not, write to the Free Software | 18 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 19 | | 02110-1301, USA | 20 | | | 21 | +-------------------------------------------------------------------------+ 22 | | spine: a backend data gatherer for cacti | 23 | +-------------------------------------------------------------------------+ 24 | | This poller would not have been possible without: | 25 | | - Larry Adams (current development and enhancements) | 26 | | - Rivo Nurges (rrd support, mysql poller cache, misc functions) | 27 | | - RTG (core poller code, pthreads, snmp, autoconf examples) | 28 | | - Brady Alleman/Doug Warner (threading ideas, implementation details) | 29 | +-------------------------------------------------------------------------+ 30 | | - Cacti - http://www.cacti.net/ | 31 | +-------------------------------------------------------------------------+ 32 | */ 33 | 34 | #include "common.h" 35 | #include "spine.h" 36 | 37 | /*! \fn char *php_cmd(const char *php_command, int php_process) 38 | * \brief calls the script server and executes a script command 39 | * \param php_command the formatted php script server command 40 | * \param php_process the php script server process to call 41 | * 42 | * This function is called directly by the spine poller when a script server 43 | * request has been initiated for a host. It will place the PHP Script Server 44 | * command on it's output pipe and then wait the pre-defined timeout period for 45 | * a response on the PHP Script Servers output pipe. 46 | * 47 | * \return pointer to the string results. Must be freed by the parent. 48 | * 49 | */ 50 | char *php_cmd(const char *php_command, int php_process) { 51 | char *result_string; 52 | char command[BUFSIZE]; 53 | ssize_t bytes; 54 | int retries = 0; 55 | 56 | assert(php_command != 0); 57 | 58 | /* pad command with CR-LF */ 59 | snprintf(command, BUFSIZE, "%s\r\n", php_command); 60 | 61 | /* place lock around mutex */ 62 | switch (php_process) { 63 | case 0: thread_mutex_lock(LOCK_PHP_PROC_0); break; 64 | case 1: thread_mutex_lock(LOCK_PHP_PROC_1); break; 65 | case 2: thread_mutex_lock(LOCK_PHP_PROC_2); break; 66 | case 3: thread_mutex_lock(LOCK_PHP_PROC_3); break; 67 | case 4: thread_mutex_lock(LOCK_PHP_PROC_4); break; 68 | case 5: thread_mutex_lock(LOCK_PHP_PROC_5); break; 69 | case 6: thread_mutex_lock(LOCK_PHP_PROC_6); break; 70 | case 7: thread_mutex_lock(LOCK_PHP_PROC_7); break; 71 | case 8: thread_mutex_lock(LOCK_PHP_PROC_8); break; 72 | case 9: thread_mutex_lock(LOCK_PHP_PROC_9); break; 73 | case 10: thread_mutex_lock(LOCK_PHP_PROC_10); break; 74 | case 11: thread_mutex_lock(LOCK_PHP_PROC_11); break; 75 | case 12: thread_mutex_lock(LOCK_PHP_PROC_12); break; 76 | case 13: thread_mutex_lock(LOCK_PHP_PROC_13); break; 77 | case 14: thread_mutex_lock(LOCK_PHP_PROC_14); break; 78 | } 79 | 80 | /* send command to the script server */ 81 | retry: 82 | bytes = write(php_processes[php_process].php_write_fd, command, strlen(command)); 83 | 84 | /* if write status is <= 0 then the script server may be hung */ 85 | if (bytes <= 0) { 86 | result_string = strdup("U"); 87 | SPINE_LOG(("ERROR: SS[%i] PHP Script Server communications lost sending Command[%s]. Restarting PHP Script Server", php_process, command)); 88 | 89 | php_close(php_process); 90 | php_init(php_process); 91 | /* increment and retry a few times on the next item */ 92 | retries++; 93 | if (retries < 3) { 94 | goto retry; 95 | } 96 | } else { 97 | /* read the result from the php_command */ 98 | result_string = php_readpipe(php_process, command); 99 | 100 | /* check for a null */ 101 | if (!strlen(result_string)) { 102 | SET_UNDEFINED(result_string); 103 | } 104 | } 105 | 106 | /* unlock around php process */ 107 | switch (php_process) { 108 | case 0: thread_mutex_unlock(LOCK_PHP_PROC_0); break; 109 | case 1: thread_mutex_unlock(LOCK_PHP_PROC_1); break; 110 | case 2: thread_mutex_unlock(LOCK_PHP_PROC_2); break; 111 | case 3: thread_mutex_unlock(LOCK_PHP_PROC_3); break; 112 | case 4: thread_mutex_unlock(LOCK_PHP_PROC_4); break; 113 | case 5: thread_mutex_unlock(LOCK_PHP_PROC_5); break; 114 | case 6: thread_mutex_unlock(LOCK_PHP_PROC_6); break; 115 | case 7: thread_mutex_unlock(LOCK_PHP_PROC_7); break; 116 | case 8: thread_mutex_unlock(LOCK_PHP_PROC_8); break; 117 | case 9: thread_mutex_unlock(LOCK_PHP_PROC_9); break; 118 | case 10: thread_mutex_unlock(LOCK_PHP_PROC_10); break; 119 | case 11: thread_mutex_unlock(LOCK_PHP_PROC_11); break; 120 | case 12: thread_mutex_unlock(LOCK_PHP_PROC_12); break; 121 | case 13: thread_mutex_unlock(LOCK_PHP_PROC_13); break; 122 | case 14: thread_mutex_unlock(LOCK_PHP_PROC_14); break; 123 | } 124 | 125 | return result_string; 126 | } 127 | 128 | /*! \fn in php_get_process() 129 | * \brief returns the next php script server process to utilize 130 | * 131 | * This very simple function simply returns the next PHP Script Server 132 | * process id to poll using a round robin algorithm. 133 | * 134 | * \return the integer number of the next script server to use 135 | * 136 | */ 137 | int php_get_process(void) { 138 | int i; 139 | 140 | thread_mutex_lock(LOCK_PHP); 141 | if (set.php_current_server >= set.php_servers) { 142 | set.php_current_server = 0; 143 | } 144 | i = set.php_current_server; 145 | set.php_current_server++; 146 | thread_mutex_unlock(LOCK_PHP); 147 | 148 | return i; 149 | } 150 | 151 | /*! \fn char *php_readpipe(int php_process, char *command) 152 | * \brief read a line from a PHP Script Server process 153 | * \param php_process the PHP Script Server process to obtain output from 154 | * 155 | * This function will read the output pipe from the PHP Script Server process 156 | * and return that string to the Spine thread requesting the output. If for 157 | * some reason the PHP Script Server process does not respond in time, it will 158 | * be closed using the php_close function, then restarted. 159 | * 160 | * \return a string pointer to the PHP Script Server response 161 | */ 162 | char *php_readpipe(int php_process, char *command) { 163 | fd_set fds; 164 | struct timeval timeout; 165 | double begin_time = 0; 166 | double end_time = 0; 167 | double remaining_usec = 0; 168 | char *result_string; 169 | 170 | int i; 171 | char *cp; 172 | char *bptr; 173 | 174 | if (!(result_string = (char *)malloc(RESULTS_BUFFER))) { 175 | die("ERROR: Fatal malloc error: php.c php_readpipe!"); 176 | } 177 | result_string[0] = '\0'; 178 | 179 | /* record start time */ 180 | begin_time = get_time_as_double(); 181 | 182 | /* establish timeout value for the PHP script server to respond */ 183 | timeout.tv_sec = set.script_timeout; 184 | timeout.tv_usec = 0; 185 | 186 | /* check to see which pipe talked and take action 187 | * should only be the READ pipe */ 188 | retry: 189 | 190 | /* initialize file descriptors to review for input/output */ 191 | FD_ZERO(&fds); 192 | FD_SET(php_processes[php_process].php_read_fd,&fds); 193 | 194 | switch (select(php_processes[php_process].php_read_fd+1, &fds, NULL, NULL, &timeout)) { 195 | case -1: 196 | switch (errno) { 197 | case EBADF: 198 | SPINE_LOG(("ERROR: SS[%i] An invalid file descriptor was given in one of the sets.", php_process)); 199 | break; 200 | case EINTR: 201 | #ifndef SOLAR_THREAD 202 | /* take a moment */ 203 | usleep(2000); 204 | #endif 205 | 206 | /* record end time */ 207 | end_time = get_time_as_double(); 208 | 209 | /* re-establish new timeout value */ 210 | timeout.tv_sec = rint(floor(set.script_timeout-(end_time-begin_time))); 211 | remaining_usec = set.script_timeout - timeout.tv_sec - (end_time - begin_time); 212 | 213 | if (remaining_usec > 0) { 214 | timeout.tv_usec = rint(remaining_usec * 1000000); 215 | } else { 216 | timeout.tv_usec = 0; 217 | } 218 | 219 | if (timeout.tv_sec + timeout.tv_usec > 0) { 220 | goto retry; 221 | } else { 222 | SPINE_LOG(("WARNING: SS[%i] The Script Server script timed out while processing EINTR's.", php_process)); 223 | } 224 | 225 | break; 226 | case EINVAL: 227 | SPINE_LOG(("ERROR: SS[%i] N is negative or the value contained within timeout is invalid.", php_process)); 228 | break; 229 | case ENOMEM: 230 | SPINE_LOG(("ERROR: SS[%i] Select was unable to allocate memory for internal tables.", php_process)); 231 | break; 232 | default: 233 | SPINE_LOG(("ERROR: SS[%i] Unknown fatal select() error", php_process)); 234 | break; 235 | } 236 | 237 | SET_UNDEFINED(result_string); 238 | 239 | /* kill script server because it is misbehaving */ 240 | php_close(php_process); 241 | php_init(php_process); 242 | break; 243 | case 0: 244 | /* record end time */ 245 | end_time = get_time_as_double(); 246 | 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)); 247 | SET_UNDEFINED(result_string); 248 | 249 | /* kill script server because it is misbehaving */ 250 | php_close(php_process); 251 | php_init(php_process); 252 | break; 253 | default: 254 | if (FD_ISSET(php_processes[php_process].php_read_fd, &fds)) { 255 | bptr = result_string; 256 | 257 | while (1) { 258 | i = read(php_processes[php_process].php_read_fd, bptr, RESULTS_BUFFER-(bptr-result_string)); 259 | 260 | if (i <= 0) { 261 | SET_UNDEFINED(result_string); 262 | break; 263 | } 264 | 265 | bptr += i; 266 | *bptr = '\0'; /* make what we've got into a string */ 267 | 268 | if ((cp = strstr(result_string,"\n")) != 0) { 269 | break; 270 | } 271 | 272 | if (bptr >= result_string+BUFSIZE) { 273 | SPINE_LOG(("ERROR: SS[%i] The Script Server result was longer than the acceptable range", php_process)); 274 | SET_UNDEFINED(result_string); 275 | } 276 | } 277 | } else { 278 | SPINE_LOG(("ERROR: SS[%i] The FD was not set as expected", php_process)); 279 | SET_UNDEFINED(result_string); 280 | } 281 | 282 | php_processes[php_process].php_state = PHP_READY; 283 | } 284 | 285 | return result_string; 286 | } 287 | 288 | /*! \fn int php_init(int php_process) 289 | * \brief initialize either a specific PHP Script Server or all of them. 290 | * \param php_process the process number to start or PHP_INIT 291 | * 292 | * This function will either start an individual PHP Script Server process 293 | * or all of them if the input parameter is the PHP_INIT constant. The function 294 | * will check the status of the process to verify that it is ready to process 295 | * scripts as well. 296 | * 297 | * \return TRUE if the PHP Script Server is know running or FALSE otherwise 298 | */ 299 | int php_init(int php_process) { 300 | int cacti2php_pdes[2]; 301 | int php2cacti_pdes[2]; 302 | pid_t pid; 303 | char poller_id[TINY_BUFSIZE]; 304 | char mode[TINY_BUFSIZE]; 305 | char *argv[7]; 306 | int cancel_state; 307 | char *result_string = 0; 308 | int num_processes; 309 | int i; 310 | int retry_count = 0; 311 | char *command = strdup("INIT"); 312 | 313 | /* special code to start all PHP Servers */ 314 | if (php_process == PHP_INIT) { 315 | num_processes = set.php_servers; 316 | } else { 317 | num_processes = 1; 318 | } 319 | 320 | for (i=0; i < num_processes; i++) { 321 | SPINE_LOG_DEBUG(("DEBUG: SS[%i] PHP Script Server Routine Starting", i)); 322 | 323 | /* create the output pipes from Spine to php*/ 324 | if (pipe(cacti2php_pdes) < 0) { 325 | SPINE_LOG(("ERROR: SS[%i] Could not allocate php server pipes", i)); 326 | return FALSE; 327 | } 328 | 329 | /* create the input pipes from php to Spine */ 330 | if (pipe(php2cacti_pdes) < 0) { 331 | SPINE_LOG(("ERROR: SS[%i] Could not allocate php server pipes", i)); 332 | return FALSE; 333 | } 334 | 335 | /* disable thread cancellation from this point forward. */ 336 | pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state); 337 | 338 | /* establish arguments for script server execution */ 339 | if (set.cacti_version <= 1222) { 340 | argv[0] = set.path_php; 341 | argv[1] = "-q"; 342 | argv[2] = set.path_php_server; 343 | argv[3] = "spine"; 344 | snprintf(poller_id, TINY_BUFSIZE, "%d", set.poller_id); 345 | argv[4] = poller_id; 346 | argv[5] = NULL; 347 | } else if (set.poller_id > 1) { 348 | argv[0] = set.path_php; 349 | argv[1] = "-q"; 350 | argv[2] = set.path_php_server; 351 | argv[3] = "--environ=spine"; 352 | 353 | snprintf(poller_id, TINY_BUFSIZE, "--poller=%d", set.poller_id); 354 | argv[4] = poller_id; 355 | 356 | if (set.mode == REMOTE_ONLINE) { 357 | snprintf(mode, TINY_BUFSIZE, "--mode=online"); 358 | } else { 359 | snprintf(mode, TINY_BUFSIZE, "--mode=offline"); 360 | } 361 | argv[5] = mode; 362 | 363 | argv[6] = NULL; 364 | } else { 365 | argv[0] = set.path_php; 366 | argv[1] = "-q"; 367 | argv[2] = set.path_php_server; 368 | argv[3] = "--environ=spine"; 369 | snprintf(poller_id, TINY_BUFSIZE, "--poller=%d", set.poller_id); 370 | argv[4] = poller_id; 371 | 372 | argv[5] = NULL; 373 | } 374 | 375 | /* fork a child process */ 376 | SPINE_LOG_DEBUG(("DEBUG: SS[%i] PHP Script Server About to FORK Child Process", i)); 377 | 378 | retry: 379 | 380 | pid = vfork(); 381 | 382 | /* check the pid status and process as required */ 383 | switch (pid) { 384 | case -1: /* ERROR: Could not fork() */ 385 | switch (errno) { 386 | case EAGAIN: 387 | if (retry_count < 3) { 388 | retry_count++; 389 | #ifndef SOLAR_THREAD 390 | /* take a moment */ 391 | usleep(50000); 392 | #endif 393 | goto retry; 394 | } else { 395 | SPINE_LOG(("ERROR: SS[%i] Could not fork PHP Script Server Out of Resources", i)); 396 | } 397 | case ENOMEM: 398 | if (retry_count < 3) { 399 | retry_count++; 400 | #ifndef SOLAR_THREAD 401 | /* take a moment */ 402 | usleep(50000); 403 | #endif 404 | goto retry; 405 | } else { 406 | SPINE_LOG(("ERROR: SS[%i] Could not fork PHP Script Server Out of Memory", i)); 407 | } 408 | default: 409 | SPINE_LOG(("ERROR: SS[%i] Could not fork PHP Script Server Unknown Reason", i)); 410 | } 411 | 412 | close(php2cacti_pdes[0]); 413 | close(php2cacti_pdes[1]); 414 | close(cacti2php_pdes[0]); 415 | close(cacti2php_pdes[1]); 416 | 417 | SPINE_LOG(("ERROR: SS[%i] Could not fork PHP Script Server", i)); 418 | pthread_setcancelstate(cancel_state, NULL); 419 | 420 | return FALSE; 421 | /* NOTREACHED */ 422 | case 0: /* SUCCESS: I am now the child */ 423 | /* set the standard input/output channels of the new process. */ 424 | dup2(cacti2php_pdes[0], STDIN_FILENO); 425 | dup2(php2cacti_pdes[1], STDOUT_FILENO); 426 | 427 | /* close unneeded Pipes */ 428 | (void)close(php2cacti_pdes[0]); 429 | (void)close(php2cacti_pdes[1]); 430 | (void)close(cacti2php_pdes[0]); 431 | (void)close(cacti2php_pdes[1]); 432 | 433 | /* start the php script server process */ 434 | execv(argv[0], argv); 435 | _exit(127); 436 | /* NOTREACHED */ 437 | default: /* I am the parent process */ 438 | SPINE_LOG_DEBUG(("DEBUG: SS[%i] PHP Script Server Child FORK Success", i)); 439 | } 440 | 441 | /* Parent */ 442 | /* close unneeded pipes */ 443 | close(cacti2php_pdes[0]); 444 | close(php2cacti_pdes[1]); 445 | 446 | if (php_process == PHP_INIT) { 447 | php_processes[i].php_pid = pid; 448 | php_processes[i].php_write_fd = cacti2php_pdes[1]; 449 | php_processes[i].php_read_fd = php2cacti_pdes[0]; 450 | } else { 451 | php_processes[php_process].php_pid = pid; 452 | php_processes[php_process].php_write_fd = cacti2php_pdes[1]; 453 | php_processes[php_process].php_read_fd = php2cacti_pdes[0]; 454 | } 455 | 456 | /* restore caller's cancellation state. */ 457 | pthread_setcancelstate(cancel_state, NULL); 458 | 459 | /* check pipe to insure startup took place */ 460 | if (php_process == PHP_INIT) { 461 | result_string = php_readpipe(i, command); 462 | } else { 463 | result_string = php_readpipe(php_process, command); 464 | } 465 | 466 | if (strstr(result_string, "Started")) { 467 | if (php_process == PHP_INIT) { 468 | SPINE_LOG_DEBUG(("DEBUG: SS[%i] Confirmed PHP Script Server running using readfd[%i], writefd[%i]", i, php2cacti_pdes[0], cacti2php_pdes[1])); 469 | 470 | php_processes[i].php_state = PHP_READY; 471 | } else { 472 | SPINE_LOG_DEBUG(("DEBUG: SS[%i] Confirmed PHP Script Server running using readfd[%i], writefd[%i]", php_process, php2cacti_pdes[0], cacti2php_pdes[1])); 473 | 474 | php_processes[php_process].php_state = PHP_READY; 475 | } 476 | } else { 477 | if (php_process == PHP_INIT) { 478 | SPINE_LOG(("ERROR: SS[%i] Script Server did not start properly return message was: '%s'", i, result_string)); 479 | 480 | php_processes[i].php_state = PHP_BUSY; 481 | } else { 482 | SPINE_LOG(("ERROR: SS[%i] Script Server did not start properly return message was: '%s'", php_process, result_string)); 483 | 484 | php_processes[php_process].php_state = PHP_BUSY; 485 | } 486 | } 487 | 488 | free(result_string); 489 | } 490 | 491 | 492 | return TRUE; 493 | } 494 | 495 | /*! \fn void php_close(int php_process) 496 | * \brief close the php script server process 497 | * \param php_process the process to close or PHP_INIT 498 | * 499 | * This function will take an input parameter of either a specially coded 500 | * PHP_INIT parameter or an integer stating the process number. With that 501 | * information is will close and/or terminate the child PHP Script Server 502 | * process and then return to the calling function. 503 | * 504 | * TODO: Make ending of the child process not be reliant on SIG_TERM in cases 505 | * where the child process is hung for one reason or another. 506 | * 507 | */ 508 | void php_close(int php_process) { 509 | int i; 510 | int num_processes; 511 | ssize_t bytes; 512 | 513 | if (php_process == PHP_INIT) { 514 | num_processes = set.php_servers; 515 | } else { 516 | num_processes = 1; 517 | } 518 | 519 | for(i = 0; i < num_processes; i++) { 520 | php_t *phpp; 521 | 522 | SPINE_LOG_DEBUG(("DEBUG: SS[%i] Script Server Shutdown Started", i)); 523 | 524 | /* tell the script server to close */ 525 | if (php_process == PHP_INIT) { 526 | phpp = &php_processes[i]; 527 | } else { 528 | phpp = &php_processes[php_process]; 529 | } 530 | 531 | /* If we still have a valid write pipe, tell PHP to close down 532 | * by sending a "quit" message, then closing the input channel 533 | * so it gets an EOF. 534 | * 535 | * Then we wait a moment before actually killing it to allow for 536 | * a clean shutdown. 537 | */ 538 | if (phpp->php_write_fd >= 0) { 539 | static const char quit[] = "quit\r\n"; 540 | 541 | bytes = write(phpp->php_write_fd, quit, strlen(quit)); 542 | 543 | close(phpp->php_write_fd); 544 | phpp->php_write_fd = -1; 545 | 546 | /* wait before killing php */ 547 | #ifndef SOLAR_THREAD 548 | usleep(50000); /* 50 msec */ 549 | #endif 550 | } 551 | 552 | /* only try to kill the process if the PID looks valid. 553 | * Trying to kill a negative number is bad news (it's 554 | * a process group leader), and PID 1 is "init". 555 | */ 556 | if (phpp->php_pid > 1) { 557 | /* end the php script server process */ 558 | kill(phpp->php_pid, SIGTERM); 559 | 560 | /* reset this PID variable? */ 561 | } 562 | 563 | /* close file descriptors */ 564 | close(phpp->php_read_fd); 565 | phpp->php_read_fd = -1; 566 | } 567 | } 568 | -------------------------------------------------------------------------------- /php.h: -------------------------------------------------------------------------------- 1 | /* 2 | ex: set tabstop=4 shiftwidth=4 autoindent: 3 | +-------------------------------------------------------------------------+ 4 | | Copyright (C) 2004-2024 The Cacti Group | 5 | | | 6 | | This program is free software; you can redistribute it and/or | 7 | | modify it under the terms of the GNU Lesser General Public | 8 | | License as published by the Free Software Foundation; either | 9 | | version 2.1 of the License, or (at your option) any later version. | 10 | | | 11 | | This program is distributed in the hope that it will be useful, | 12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | | GNU Lesser General Public License for more details. | 15 | | | 16 | | You should have received a copy of the GNU Lesser General Public | 17 | | License along with this library; if not, write to the Free Software | 18 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 19 | | 02110-1301, USA | 20 | | | 21 | +-------------------------------------------------------------------------+ 22 | | spine: a backend data gatherer for cacti | 23 | +-------------------------------------------------------------------------+ 24 | | This poller would not have been possible without: | 25 | | - Larry Adams (current development and enhancements) | 26 | | - Rivo Nurges (rrd support, mysql poller cache, misc functions) | 27 | | - RTG (core poller code, pthreads, snmp, autoconf examples) | 28 | | - Brady Alleman/Doug Warner (threading ideas, implementation details) | 29 | +-------------------------------------------------------------------------+ 30 | | - Cacti - http://www.cacti.net/ | 31 | +-------------------------------------------------------------------------+ 32 | */ 33 | 34 | extern char *php_cmd(const char *php_command, int php_process); 35 | extern char *php_readpipe(int php_process, char *command); 36 | extern int php_init(int php_process); 37 | extern void php_close(int php_process); 38 | extern int php_get_process(void); 39 | -------------------------------------------------------------------------------- /ping.h: -------------------------------------------------------------------------------- 1 | /* 2 | ex: set tabstop=4 shiftwidth=4 autoindent: 3 | +-------------------------------------------------------------------------+ 4 | | Copyright (C) 2004-2024 The Cacti Group | 5 | | | 6 | | This program is free software; you can redistribute it and/or | 7 | | modify it under the terms of the GNU Lesser General Public | 8 | | License as published by the Free Software Foundation; either | 9 | | version 2.1 of the License, or (at your option) any later version. | 10 | | | 11 | | This program is distributed in the hope that it will be useful, | 12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | | GNU Lesser General Public License for more details. | 15 | | | 16 | | You should have received a copy of the GNU Lesser General Public | 17 | | License along with this library; if not, write to the Free Software | 18 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 19 | | 02110-1301, USA | 20 | | | 21 | +-------------------------------------------------------------------------+ 22 | | spine: a backend data gatherer for cacti | 23 | +-------------------------------------------------------------------------+ 24 | | This poller would not have been possible without: | 25 | | - Larry Adams (current development and enhancements) | 26 | | - Rivo Nurges (rrd support, mysql poller cache, misc functions) | 27 | | - RTG (core poller code, pthreads, snmp, autoconf examples) | 28 | | - Brady Alleman/Doug Warner (threading ideas, implementation details) | 29 | +-------------------------------------------------------------------------+ 30 | | - Cacti - http://www.cacti.net/ | 31 | +-------------------------------------------------------------------------+ 32 | */ 33 | 34 | #ifndef ICMP_ECHOREPLY 35 | #define ICMP_ECHOREPLY 0 /* Echo Reply */ 36 | #endif 37 | 38 | #ifndef MSG_WAITALL 39 | #define MSG_WAITALL 0x100 40 | #endif 41 | 42 | #ifdef __CYGWIN__ 43 | struct icmp_ra_addr 44 | { 45 | u_int32_t ira_addr; 46 | u_int32_t ira_preference; 47 | }; 48 | struct iphdr { 49 | #if __BYTE_ORDER == __LITTLE_ENDIAN 50 | unsigned int ihl:4; 51 | unsigned int version:4; 52 | #elif __BYTE_ORDER == __BIG_ENDIAN 53 | unsigned int version:4; 54 | unsigned int ihl:4; 55 | #else 56 | # error "Please fix " 57 | #endif 58 | u_int8_t tos; 59 | u_int16_t tot_len; 60 | u_int16_t id; 61 | u_int16_t frag_off; 62 | u_int8_t ttl; 63 | u_int8_t protocol; 64 | u_int16_t check; 65 | u_int32_t saddr; 66 | u_int32_t daddr; 67 | /*The options start here. */ 68 | }; 69 | struct icmp 70 | { 71 | u_int8_t icmp_type; /* type of message, see below */ 72 | u_int8_t icmp_code; /* type sub code */ 73 | u_int16_t icmp_cksum; /* ones complement checksum of struct */ 74 | union 75 | { 76 | u_char ih_pptr; /* ICMP_PARAMPROB */ 77 | struct in_addr ih_gwaddr; /* gateway address */ 78 | struct ih_idseq /* echo datagram */ 79 | { 80 | u_int16_t icd_id; 81 | u_int16_t icd_seq; 82 | } ih_idseq; 83 | u_int32_t ih_void; 84 | 85 | /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */ 86 | struct ih_pmtu 87 | { 88 | u_int16_t ipm_void; 89 | u_int16_t ipm_nextmtu; 90 | } ih_pmtu; 91 | 92 | struct ih_rtradv 93 | { 94 | u_int8_t irt_num_addrs; 95 | u_int8_t irt_wpa; 96 | u_int16_t irt_lifetime; 97 | } ih_rtradv; 98 | } icmp_hun; 99 | #define icmp_pptr icmp_hun.ih_pptr 100 | #define icmp_gwaddr icmp_hun.ih_gwaddr 101 | #define icmp_id icmp_hun.ih_idseq.icd_id 102 | #define icmp_seq icmp_hun.ih_idseq.icd_seq 103 | #define icmp_void icmp_hun.ih_void 104 | #define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void 105 | #define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu 106 | #define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs 107 | #define icmp_wpa icmp_hun.ih_rtradv.irt_wpa 108 | #define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime 109 | union 110 | { 111 | struct 112 | { 113 | u_int32_t its_otime; 114 | u_int32_t its_rtime; 115 | u_int32_t its_ttime; 116 | } id_ts; 117 | struct 118 | { 119 | struct ip idi_ip; 120 | /* options and then 64 bits of data */ 121 | } id_ip; 122 | struct icmp_ra_addr id_radv; 123 | u_int32_t id_mask; 124 | u_int8_t id_data[1]; 125 | } icmp_dun; 126 | #define icmp_otime icmp_dun.id_ts.its_otime 127 | #define icmp_rtime icmp_dun.id_ts.its_rtime 128 | #define icmp_ttime icmp_dun.id_ts.its_ttime 129 | #define icmp_ip icmp_dun.id_ip.idi_ip 130 | #define icmp_radv icmp_dun.id_radv 131 | #define icmp_mask icmp_dun.id_mask 132 | #define icmp_data icmp_dun.id_data 133 | }; 134 | #endif 135 | 136 | /* Host availability functions */ 137 | extern int ping_host(host_t *host, ping_t *ping); 138 | extern int ping_snmp(host_t *host, ping_t *ping); 139 | extern int ping_icmp(host_t *host, ping_t *ping); 140 | extern int ping_udp(host_t *host, ping_t *ping); 141 | extern int ping_tcp(host_t *host, ping_t *ping); 142 | extern name_t *get_namebyhost(char *hostname, name_t *name); 143 | extern void update_host_status(int status, host_t *host, ping_t *ping, int availability_method); 144 | extern int init_sockaddr(struct sockaddr_in *name, const char *hostname, unsigned short int port); 145 | extern int get_address_type(host_t *host); 146 | extern unsigned short int get_checksum(void* buf, int len); 147 | -------------------------------------------------------------------------------- /poller.h: -------------------------------------------------------------------------------- 1 | /* 2 | ex: set tabstop=4 shiftwidth=4 autoindent: 3 | +-------------------------------------------------------------------------+ 4 | | Copyright (C) 2004-2024 The Cacti Group | 5 | | | 6 | | This program is free software; you can redistribute it and/or | 7 | | modify it under the terms of the GNU Lesser General Public | 8 | | License as published by the Free Software Foundation; either | 9 | | version 2.1 of the License, or (at your option) any later version. | 10 | | | 11 | | This program is distributed in the hope that it will be useful, | 12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | | GNU Lesser General Public License for more details. | 15 | | | 16 | | You should have received a copy of the GNU Lesser General Public | 17 | | License along with this library; if not, write to the Free Software | 18 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 19 | | 02110-1301, USA | 20 | | | 21 | +-------------------------------------------------------------------------+ 22 | | spine: a backend data gatherer for cacti | 23 | +-------------------------------------------------------------------------+ 24 | | This poller would not have been possible without: | 25 | | - Larry Adams (current development and enhancements) | 26 | | - Rivo Nurges (rrd support, mysql poller cache, misc functions) | 27 | | - RTG (core poller code, pthreads, snmp, autoconf examples) | 28 | | - Brady Alleman/Doug Warner (threading ideas, implementation details) | 29 | +-------------------------------------------------------------------------+ 30 | | - Cacti - http://www.cacti.net/ | 31 | +-------------------------------------------------------------------------+ 32 | */ 33 | 34 | extern void *child(void *arg); 35 | extern void child_cleanup(void *arg); 36 | extern void child_cleanup_thread(void *arg); 37 | extern void child_cleanup_script(void *arg); 38 | 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); 39 | extern char *exec_poll(host_t *current_host, char *command, int id, char *type); 40 | extern void get_system_information(host_t *host, MYSQL *mysql, int system); 41 | extern int is_multipart_output(char *result); 42 | extern int validate_result(char *result); 43 | 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); 44 | -------------------------------------------------------------------------------- /snmp.h: -------------------------------------------------------------------------------- 1 | /* 2 | ex: set tabstop=4 shiftwidth=4 autoindent: 3 | +-------------------------------------------------------------------------+ 4 | | Copyright (C) 2004-2024 The Cacti Group | 5 | | | 6 | | This program is free software; you can redistribute it and/or | 7 | | modify it under the terms of the GNU Lesser General Public | 8 | | License as published by the Free Software Foundation; either | 9 | | version 2.1 of the License, or (at your option) any later version. | 10 | | | 11 | | This program is distributed in the hope that it will be useful, | 12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | | GNU Lesser General Public License for more details. | 15 | | | 16 | | You should have received a copy of the GNU Lesser General Public | 17 | | License along with this library; if not, write to the Free Software | 18 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 19 | | 02110-1301, USA | 20 | | | 21 | +-------------------------------------------------------------------------+ 22 | | spine: a backend data gatherer for cacti | 23 | +-------------------------------------------------------------------------+ 24 | | This poller would not have been possible without: | 25 | | - Larry Adams (current development and enhancements) | 26 | | - Rivo Nurges (rrd support, mysql poller cache, misc functions) | 27 | | - RTG (core poller code, pthreads, snmp, autoconf examples) | 28 | | - Brady Alleman/Doug Warner (threading ideas, implementation details) | 29 | +-------------------------------------------------------------------------+ 30 | | - Cacti - http://www.cacti.net/ | 31 | +-------------------------------------------------------------------------+ 32 | */ 33 | 34 | extern void snmp_spine_init(void); 35 | extern void snmp_spine_close(void); 36 | 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); 37 | extern void snmp_host_cleanup(void *snmp_session); 38 | extern char *snmp_get_base(host_t *current_host, char *snmp_oid, bool should_fail); 39 | extern char *snmp_get(host_t *current_host, char *snmp_oid); 40 | extern char *snmp_getnext(host_t *current_host, char *snmp_oid); 41 | extern int snmp_count(host_t *current_host, char *snmp_oid); 42 | extern void snmp_get_multi(host_t *current_host, target_t *poller_items, snmp_oids_t *snmp_oids, int num_oids); 43 | extern void snmp_snprint_value(char *obuf, size_t buf_len, const oid *objid, size_t objidlen, struct variable_list *variable); 44 | -------------------------------------------------------------------------------- /spine.conf.dist: -------------------------------------------------------------------------------- 1 | # +-------------------------------------------------------------------------+ 2 | # | Copyright (C) 2004-2024 The Cacti Group | 3 | # | | 4 | # | This program is free software; you can redistribute it and/or | 5 | # | modify it under the terms of the GNU Lesser General Public License | 6 | # | as published by the Free Software Foundation; either version 2.1 | 7 | # | of the License, or (at your option) any later version. | 8 | # | | 9 | # | This program is distributed in the hope that it will be useful, | 10 | # | but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | # | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 12 | # | GNU General Public License for more details. | 13 | # +-------------------------------------------------------------------------+ 14 | # | spine: a backend data gatherer for Cacti | 15 | # +-------------------------------------------------------------------------+ 16 | # | This poller would not have been possible without: | 17 | # | - Larry Adams (current development and enhancements) | 18 | # | - Rivo Nurges (rrd support, mysql poller cache, misc functions) | 19 | # | - RTG (core poller code, pthreads, snmp, autoconf examples) | 20 | # | - Brady Alleman/Doug Warner (threading ideas, implementation details) | 21 | # +-------------------------------------------------------------------------+ 22 | # | Settings | 23 | # +-------------------------------------------------------------------------+ 24 | # | DB_Host 'localhost' or socket file for UNIX/Linux | 25 | # | IP Address for Windows | 26 | # | DB_Database Database name, typically 'cacti' | 27 | # | DB_Port The database port to use | 28 | # | DB_User The user to access the database, typically 'cactiuser' | 29 | # | DB_Pass The password for the Cacti user | 30 | # | SNMP_Clientaddr Bind SNMP to a specific address for sites that use | 31 | # | higher security levels | 32 | # | Cacti_Log Optional path to the Cacti log file | 33 | # +-------------------------------------------------------------------------+ 34 | # | Settings for Remote Polling | 35 | # +-------------------------------------------------------------------------+ 36 | # | RDB_Host The remote database hostname. | 37 | # | RDB_Database The remote database name, typically 'cacti' | 38 | # | RDB_Port The remote database port to use | 39 | # | RDB_User The remote database user, typically 'cactiuser' | 40 | # | RDB_Pass The remote database password. | 41 | # +-------------------------------------------------------------------------+ 42 | 43 | DB_Host localhost 44 | DB_Database cacti 45 | DB_User cactiuser 46 | DB_Pass cactiuser 47 | DB_Port 3306 48 | #DB_UseSSL 0 49 | #DB_SSL_Key 50 | #DB_SSL_Cert 51 | #DB_SSL_CA 52 | #Cacti_Log /var/www/html/cacti/log/cacti.log 53 | 54 | #RDB_Host localhost 55 | #RDB_Database cacti 56 | #RDB_User cactiuser 57 | #RDB_Pass cactiuser 58 | #RDB_Port 3306 59 | #RDB_UseSSL 0 60 | #RDB_SSL_Key 61 | #RDB_SSL_Cert 62 | #RDB_SSL_CA 63 | 64 | -------------------------------------------------------------------------------- /spine.h: -------------------------------------------------------------------------------- 1 | /* 2 | ex: set tabstop=4 shiftwidth=4 autoindent: 3 | +-------------------------------------------------------------------------+ 4 | | Copyright (C) 2004-2024 The Cacti Group | 5 | | | 6 | | This program is free software; you can redistribute it and/or | 7 | | modify it under the terms of the GNU Lesser General Public | 8 | | License as published by the Free Software Foundation; either | 9 | | version 2.1 of the License, or (at your option) any later version. | 10 | | | 11 | | This program is distributed in the hope that it will be useful, | 12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | | GNU Lesser General Public License for more details. | 15 | | | 16 | | You should have received a copy of the GNU Lesser General Public | 17 | | License along with this library; if not, write to the Free Software | 18 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 19 | | 02110-1301, USA | 20 | | | 21 | +-------------------------------------------------------------------------+ 22 | | spine: a backend data gatherer for cacti | 23 | +-------------------------------------------------------------------------+ 24 | | This poller would not have been possible without: | 25 | | - Larry Adams (current development and enhancements) | 26 | | - Rivo Nurges (rrd support, mysql poller cache, misc functions) | 27 | | - RTG (core poller code, pthreads, snmp, autoconf examples) | 28 | | - Brady Alleman/Doug Warner (threading ideas, implementation details) | 29 | +-------------------------------------------------------------------------+ 30 | | - Cacti - http://www.cacti.net/ | 31 | +-------------------------------------------------------------------------+ 32 | */ 33 | 34 | #ifndef _SPINE_H_ 35 | #define _SPINE_H_ 36 | 37 | /* Defines */ 38 | #ifndef FALSE 39 | #define FALSE 0 40 | #endif 41 | #ifndef TRUE 42 | #define TRUE 1 43 | #endif 44 | 45 | #define LOCAL 0 46 | #define REMOTE 1 47 | 48 | #define SPINE_NONE 0 49 | #define SPINE_IPV4 1 50 | #define SPINE_IPV6 2 51 | 52 | #ifndef __GNUC__ 53 | # define __attribute__(x) /* NOTHING */ 54 | #endif 55 | 56 | /* Windows does not support stderr. Therefore, don't use it. */ 57 | #ifdef __CYGWIN__ 58 | #define DISABLE_STDERR 59 | #endif 60 | 61 | #ifdef HAS_EXECINFO_H 62 | #include 63 | #endif 64 | 65 | /* if a host is legal, return TRUE */ 66 | #define HOSTID_DEFINED(x) ((x) >= 0) 67 | 68 | /* warning-suppression macros 69 | * 70 | * There are times when we cannot avoid using a parameter or variable which 71 | * is not used, and these correctly generate compiler warnings. But when we 72 | * *know* that the variable is actually intended to be unused, we can use one 73 | * of these macros inside the function to suppress it. This has the effect 74 | * of suppressing the warning (a good thing), plus documenting to the reader 75 | * that this is intentional. 76 | * 77 | * Both do the same thing - they're just for different semantics. 78 | */ 79 | 80 | #define UNUSED_VARIABLE(p) (void)(p) 81 | #define UNUSED_PARAMETER(p) (void)(p) 82 | 83 | /* logging macros 84 | * 85 | * These all perform conditional logging based on the current runtime logging 86 | * level, and it relies on a bit of tricky (but entirely portable) preprocessor 87 | * techniques. 88 | * 89 | * Standard C does not support variadic macros (macros with a variable number 90 | * of parameters), and though GNU C does, it's not at all portable. So we instead 91 | * rely on the fact that putting parens around something turn multiple params 92 | * into one: 93 | * 94 | * SPINE_LOG_DEBUG(("n=%d string=%s foo=%f", n, string, foo)); 95 | * 96 | * This macros has *one* parameter: 97 | * 98 | * ("n=%d string=%s foo=%f", n, string, foo) 99 | * 100 | * and the parentheses are part of it. When we call this macro, we pass the 101 | * "single" parameter unadorned, so that 102 | * 103 | * spine_log args 104 | * 105 | * expands to 106 | * 107 | * spine_log ("n=%d string=%s foo=%f", n, string, foo) 108 | * 109 | * Voila: it's a normal printf-like call. 110 | * 111 | * The second part of this is the conditional test, and the obvious approach 112 | * of using an "if" statement is exceptionally bad form: there are all kinds 113 | * of pitfalls which arise in this case. Instead, we should try to use an 114 | * *expression*, which has none of these problems. 115 | * 116 | * The conditional tests are modelled after the assert() mechanism, which 117 | * checks the first parameter, and if it's true, it evaluates the second 118 | * parameter. If the test is not true, then the second part is *guaranteed* 119 | * not to be evaluated. 120 | * 121 | * The (void) prefix is to forestall compiler warnings about expressions 122 | * not being used. 123 | */ 124 | #define SPINE_LOG(format_and_args) (spine_log format_and_args) 125 | #define SPINE_LOG_LOW(format_and_args) (void)(set.log_level >= POLLER_VERBOSITY_LOW && spine_log format_and_args) 126 | #define SPINE_LOG_MEDIUM(format_and_args) (void)(set.log_level >= POLLER_VERBOSITY_MEDIUM && spine_log format_and_args) 127 | #define SPINE_LOG_HIGH(format_and_args) (void)(set.log_level >= POLLER_VERBOSITY_HIGH && spine_log format_and_args) 128 | #define SPINE_LOG_DEBUG(format_and_args) (void)(set.log_level >= POLLER_VERBOSITY_DEBUG && spine_log format_and_args) 129 | #define SPINE_LOG_DEVDBG(format_and_args) (void)(set.log_level >= POLLER_VERBOSITY_DEVDBG && spine_log format_and_args) 130 | 131 | /* general constants */ 132 | #define MAX_THREADS 100 133 | #define TINY_BUFSIZE 16 134 | #define SMALL_BUFSIZE 256 135 | #define MEDIUM_BUFSIZE 512 136 | #define BUFSIZE 1024 137 | #define DBL_BUFSIZE 2048 138 | #define LRG_BUFSIZE 8096 139 | #define BIG_BUFSIZE 65535 140 | #define MEGA_BUFSIZE 1024000 141 | #define HUGE_BUFSIZE 2048000 142 | #define LOGSIZE 65535 143 | #define LRG_LOGSIZE 1024000 144 | #define BITSINBYTE 8 145 | #define THIRTYTWO 4294967295ul 146 | #define SIXTYFOUR 18446744073709551615ul 147 | #define STAT_DESCRIP_ERROR 99 148 | #define SPINE_PARENT 1 149 | #define SPINE_FORK 0 150 | 151 | /* locations to search for the config file */ 152 | #define CONFIG_PATHS 4 153 | #define CONFIG_PATH_1 "" 154 | #define CONFIG_PATH_2 "/etc/" 155 | #define CONFIG_PATH_3 "/etc/cacti/" 156 | #define CONFIG_PATH_4 "../etc/" 157 | 158 | /* config file defaults */ 159 | #define DEFAULT_CONF_FILE "spine.conf" 160 | #define DEFAULT_THREADS 5 161 | #define DEFAULT_DB_HOST "localhost" 162 | #define DEFAULT_DB_DB "cacti" 163 | #define DEFAULT_DB_USER "cactiuser" 164 | #define DEFAULT_DB_PASS "cactiuser" 165 | #define DEFAULT_DB_PORT 3306 166 | #define DEFAULT_DB_PREG 0 167 | #define DEFAULT_LOGFILE "/var/www/html/cacti/log/cacti.log" 168 | #define DEFAULT_TIMEOUT 294000000 169 | 170 | /* threads constants */ 171 | #define LOCK_SNMP 0 172 | #define LOCK_SETEUID 2 173 | #define LOCK_GHBN 3 174 | #define LOCK_POOL 4 175 | #define LOCK_SYSLOG 5 176 | #define LOCK_PHP 6 177 | #define LOCK_PHP_PROC_0 7 178 | #define LOCK_PHP_PROC_1 8 179 | #define LOCK_PHP_PROC_2 9 180 | #define LOCK_PHP_PROC_3 10 181 | #define LOCK_PHP_PROC_4 11 182 | #define LOCK_PHP_PROC_5 12 183 | #define LOCK_PHP_PROC_6 13 184 | #define LOCK_PHP_PROC_7 14 185 | #define LOCK_PHP_PROC_8 15 186 | #define LOCK_PHP_PROC_9 16 187 | #define LOCK_PHP_PROC_10 17 188 | #define LOCK_PHP_PROC_11 18 189 | #define LOCK_PHP_PROC_12 19 190 | #define LOCK_PHP_PROC_13 20 191 | #define LOCK_PHP_PROC_14 21 192 | #define LOCK_THDET 40 193 | #define LOCK_HOST_TIME 41 194 | 195 | #define LOCK_SNMP_O 0 196 | #define LOCK_SETEUID_O 2 197 | #define LOCK_GHBN_O 3 198 | #define LOCK_POOL_O 4 199 | #define LOCK_SYSLOG_O 5 200 | #define LOCK_PHP_O 6 201 | #define LOCK_PHP_PROC_0_O 7 202 | #define LOCK_PHP_PROC_1_O 8 203 | #define LOCK_PHP_PROC_2_O 9 204 | #define LOCK_PHP_PROC_3_O 10 205 | #define LOCK_PHP_PROC_4_O 11 206 | #define LOCK_PHP_PROC_5_O 12 207 | #define LOCK_PHP_PROC_6_O 13 208 | #define LOCK_PHP_PROC_7_O 14 209 | #define LOCK_PHP_PROC_8_O 15 210 | #define LOCK_PHP_PROC_9_O 16 211 | #define LOCK_PHP_PROC_10_O 17 212 | #define LOCK_PHP_PROC_11_O 18 213 | #define LOCK_PHP_PROC_12_O 19 214 | #define LOCK_PHP_PROC_13_O 20 215 | #define LOCK_PHP_PROC_14_O 21 216 | #define LOCK_THDET_O 40 217 | #define LOCK_HOST_TIME_O 41 218 | 219 | /* poller actions */ 220 | #define POLLER_ACTION_SNMP 0 221 | #define POLLER_ACTION_SCRIPT 1 222 | #define POLLER_ACTION_PHP_SCRIPT_SERVER 2 223 | #define POLLER_ACTION_SNMP_COUNT 10 224 | #define POLLER_ACTION_SCRIPT_COUNT 11 225 | #define POLLER_ACTION_PHP_SCRIPT_SERVER_COUNT 12 226 | 227 | /* reindex constants */ 228 | #define POLLER_COMMAND_REINDEX 1 229 | 230 | /* log destinations */ 231 | #define LOGDEST_FILE 1 232 | #define LOGDEST_BOTH 2 233 | #define LOGDEST_SYSLOG 3 234 | #define LOGDEST_STDOUT 4 235 | 236 | #define IS_LOGGING_TO_FILE() ((set.log_destination) == LOGDEST_FILE || (set.log_destination) == LOGDEST_BOTH) 237 | #define IS_LOGGING_TO_SYSLOG() ((set.log_destination) == LOGDEST_SYSLOG || (set.log_destination) == LOGDEST_BOTH) 238 | #define IS_LOGGING_TO_STDOUT() ((set.log_destination) == LOGDEST_STDOUT ) 239 | 240 | #define SPINE_FREE(s) do { if (s) { free((void *)s); s = NULL; } } while(0) 241 | 242 | /* logging levels */ 243 | #define POLLER_VERBOSITY_NONE 1 244 | #define POLLER_VERBOSITY_LOW 2 245 | #define POLLER_VERBOSITY_MEDIUM 3 246 | #define POLLER_VERBOSITY_HIGH 4 247 | #define POLLER_VERBOSITY_DEBUG 5 248 | #define POLLER_VERBOSITY_DEVDBG 6 249 | 250 | /* logging separator constants */ 251 | #define GDC_MIN 0 252 | #define GDC_HYPHEN 0 253 | #define GDC_SLASH 1 254 | #define GDC_DOT 2 255 | #define GDC_MAX 2 256 | #define GDC_DEFAULT 1 257 | 258 | /* logging format constants */ 259 | #define GD_FMT_SIZE 21 260 | #define GD_MIN 0 261 | #define GD_MO_D_Y 0 262 | #define GD_MN_D_Y 1 263 | #define GD_D_MO_Y 2 264 | #define GD_D_MN_Y 3 265 | #define GD_Y_MO_D 4 266 | #define GD_Y_MN_D 5 267 | #define GD_MAX 5 268 | #define GD_DEFAULT 5 269 | 270 | /* host availability statics */ 271 | #define AVAIL_NONE 0 272 | #define AVAIL_SNMP_AND_PING 1 273 | #define AVAIL_SNMP 2 274 | #define AVAIL_PING 3 275 | #define AVAIL_SNMP_OR_PING 4 276 | #define AVAIL_SNMP_GET_SYSDESC 5 277 | #define AVAIL_SNMP_GET_NEXT 6 278 | #define AVAIL_STREAM 7 279 | 280 | #define PING_ICMP 1 281 | #define PING_UDP 2 282 | #define PING_TCP 3 283 | #define PING_SNMP 4 284 | #define PING_TCP_CLOSED 5 285 | 286 | #define HOST_UNKNOWN 0 287 | #define HOST_DOWN 1 288 | #define HOST_RECOVERING 2 289 | #define HOST_UP 3 290 | 291 | /* required for ICMP and UDP ping */ 292 | #define ICMP_ECHO 8 293 | #define ICMP_HDR_SIZE 8 294 | 295 | /* required for PHP Script Server */ 296 | #define MAX_PHP_SERVERS 15 297 | #define PHP_READY 0 298 | #define PHP_BUSY 1 299 | #define PHP_INIT 999 300 | #define PHP_ERROR 99 301 | 302 | /* required for validation of script results */ 303 | #define RESULT_INIT 0 304 | #define RESULT_ARGX 1 305 | #define RESULT_VALX 2 306 | #define RESULT_SEPARATOR 3 307 | #define RESULT_SPACE 4 308 | #define RESULT_ALPHA 5 309 | #define RESULT_DIGIT 6 310 | 311 | /* snmp session status */ 312 | #define SNMP_1 0 313 | #define SNMP_2c 1 314 | #define SNMP_3 3 315 | #define SNMP_NONE 4 316 | 317 | /* Constants for remote polling */ 318 | #define REMOTE_ONLINE 0 319 | #define REMOTE_OFFLINE 1 320 | #define REMOTE_RECOVERY 2 321 | 322 | /* These are used to perform string matches, returning TRUE/VALUE values. 323 | * For strcmp() this is not really that useful, but the case-insensitive 324 | * one has slight portability issues. Better to abstract them here. 325 | */ 326 | #define STRMATCH(a,b) (strcmp((a),(b)) == 0) 327 | #define STRIMATCH(a,b) (strcasecmp((a),(b)) == 0) 328 | 329 | /* When any kind of poller wants to set an undefined value; this particular 330 | * value used ('U') springs from the requirements of rrdupdate. We also 331 | * include the corresponding test macro which looks for the literal string 332 | * "U". This *could* use strcmp(), but this is more efficient. 333 | */ 334 | #define SET_UNDEFINED(buf) ( (buf)[0] = 'U', (buf)[1] = '\0' ) 335 | #define IS_UNDEFINED(buf) ( (buf)[0] == 'U' && (buf)[1] == '\0' ) 336 | 337 | /*! Config Structure 338 | * 339 | * This structure holds Spine database configuration information and/or override values 340 | * obtained via either accessing the database or reading the runtime options. In addition, 341 | * it contains runtime status information. 342 | * 343 | */ 344 | typedef struct config_struct { 345 | /* stdout, stderr TTY protection */ 346 | int stdout_notty; 347 | int stderr_notty; 348 | /* general configuration/runtime settings */ 349 | int poller_id; 350 | int poller_id_exists; 351 | int poller_interval; 352 | int parent_fork; 353 | int num_parent_processes; 354 | int script_timeout; 355 | int active_profiles; 356 | int total_snmp_ports; 357 | int threads; 358 | int threads_set; 359 | int logfile_processed; 360 | int boost_enabled; 361 | int boost_redirect; 362 | int cygwinshloc; 363 | /* debugging options */ 364 | int snmponly; 365 | int SQL_readonly; 366 | /* host range to be poller with this spine process */ 367 | int start_host_id; 368 | int end_host_id; 369 | char host_id_list[BIG_BUFSIZE]; 370 | int has_device_0; 371 | /* database connection information */ 372 | char db_host[SMALL_BUFSIZE]; 373 | char db_db[SMALL_BUFSIZE]; 374 | char db_user[SMALL_BUFSIZE]; 375 | char db_pass[SMALL_BUFSIZE]; 376 | int db_ssl; 377 | char db_ssl_key[BIG_BUFSIZE]; 378 | char db_ssl_cert[BIG_BUFSIZE]; 379 | char db_ssl_ca[BIG_BUFSIZE]; 380 | int d_b; 381 | unsigned int db_port; 382 | char dbversion[SMALL_BUFSIZE]; 383 | int dbonupdate; 384 | int cacti_version; 385 | /* path information */ 386 | char path_logfile[DBL_BUFSIZE]; 387 | char path_php[BUFSIZE]; 388 | char path_php_server[BUFSIZE]; 389 | /* logging options */ 390 | int log_level; 391 | int log_destination; 392 | int log_perror; 393 | int log_pwarn; 394 | int log_pstats; 395 | char selective_device_debug[SMALL_BUFSIZE]; 396 | int spine_log_level; 397 | int log_datetime_separator; 398 | int log_datetime_format; 399 | /* ping settings */ 400 | int icmp_avail; 401 | int availability_method; 402 | int ping_method; 403 | int ping_retries; 404 | int ping_timeout; 405 | int ping_failure_count; 406 | int ping_recovery_count; 407 | int ping_only; 408 | /* snmp options */ 409 | int snmp_max_get_size; 410 | int snmp_retries; 411 | char snmp_clientaddr[SMALL_BUFSIZE]; 412 | int mibs; 413 | /* PHP Script Server Options */ 414 | int php_required; 415 | int php_initialized; 416 | int php_servers; 417 | int php_current_server; 418 | /* Exit code if we need it */ 419 | int exit_code; 420 | size_t exit_size; 421 | void* exit_stack[10]; 422 | 423 | /* Remote polling mode */ 424 | int mode; 425 | /* remote database connection information */ 426 | char rdb_host[SMALL_BUFSIZE]; 427 | char rdb_db[SMALL_BUFSIZE]; 428 | char rdb_user[SMALL_BUFSIZE]; 429 | char rdb_pass[SMALL_BUFSIZE]; 430 | int rdb_ssl; 431 | char rdb_ssl_key[BIG_BUFSIZE]; 432 | char rdb_ssl_cert[BIG_BUFSIZE]; 433 | char rdb_ssl_ca[BIG_BUFSIZE]; 434 | unsigned int rdb_port; 435 | char rdbversion[SMALL_BUFSIZE]; 436 | int rdbonupdate; 437 | } config_t; 438 | 439 | /*! Target Structure 440 | * 441 | * This structure holds the contents of the Poller Items table and the results 442 | * of each polling action. 443 | * 444 | */ 445 | typedef struct target_struct { 446 | int target_id; 447 | char result[RESULTS_BUFFER]; 448 | int local_data_id; 449 | int action; 450 | char command[256]; 451 | char hostname[250]; 452 | char snmp_community[100]; 453 | int snmp_version; 454 | char snmp_username[50]; 455 | char snmp_password[50]; 456 | char snmp_auth_protocol[7]; 457 | char snmp_priv_passphrase[200]; 458 | char snmp_priv_protocol[8]; 459 | char snmp_context[65]; 460 | char snmp_engine_id[30]; 461 | int snmp_port; 462 | int snmp_timeout; 463 | int availability_method; 464 | int ping_method; 465 | int ping_port; 466 | int ping_timeout; 467 | int ping_retries; 468 | char rrd_name[30]; 469 | char rrd_path[255]; 470 | int rrd_num; 471 | char arg1[512]; 472 | char arg2[255]; 473 | char arg3[255]; 474 | } target_t; 475 | 476 | /*! SNMP OID's Structure 477 | * 478 | * This structure holds SNMP get results temporarily while polling is taking place. 479 | * 480 | */ 481 | typedef struct snmp_oids { 482 | int array_position; 483 | char oid[512]; 484 | char result[RESULTS_BUFFER]; 485 | } snmp_oids_t; 486 | 487 | /*! Poller Structure 488 | * 489 | * This structure holds thread polling instructions. 490 | * 491 | */ 492 | typedef struct poller_thread { 493 | int device_counter; 494 | int host_id; 495 | int host_thread; 496 | int host_threads; 497 | int host_data_ids; 498 | int threads_complete; 499 | int complete; 500 | char host_time[40]; 501 | double host_time_double; 502 | sem_t *thread_init_sem; 503 | } poller_thread_t; 504 | 505 | /*! PHP Script Server Structure 506 | * 507 | * This structure holds status and PID information for all the running 508 | * PHP Script Server processes. 509 | * 510 | */ 511 | typedef struct php_processes { 512 | int php_state; 513 | pid_t php_pid; 514 | int php_write_fd; 515 | int php_read_fd; 516 | } php_t; 517 | 518 | /*! Host Structure 519 | * 520 | * This structure holds host information from the host table and is used throughout 521 | * the application. 522 | * 523 | */ 524 | typedef struct host_struct { 525 | int id; 526 | char hostname[250]; 527 | char snmp_community[100]; 528 | int snmp_version; 529 | char snmp_username[50]; 530 | char snmp_password[50]; 531 | char snmp_auth_protocol[7]; 532 | char snmp_priv_passphrase[200]; 533 | char snmp_priv_protocol[8]; 534 | char snmp_context[65]; 535 | char snmp_engine_id[30]; 536 | int snmp_port; 537 | int snmp_timeout; 538 | int snmp_retries; 539 | char snmp_sysDescr[600]; 540 | char snmp_sysObjectID[160]; 541 | unsigned long long snmp_sysUpTimeInstance; 542 | char snmp_sysContact[300]; 543 | char snmp_sysName[300]; 544 | char snmp_sysLocation[600]; 545 | int max_oids; 546 | int availability_method; 547 | int ping_method; 548 | int ping_port; 549 | int ping_timeout; 550 | int ping_retries; 551 | int status; 552 | int status_event_count; 553 | char status_fail_date[40]; 554 | char status_rec_date[40]; 555 | char status_last_error[BUFSIZE]; 556 | double min_time; 557 | double max_time; 558 | double cur_time; 559 | double avg_time; 560 | int total_polls; 561 | int failed_polls; 562 | double availability; 563 | int ignore_host; 564 | void *snmp_session; 565 | int snmp_status; 566 | } host_t; 567 | 568 | /*! Host Reindex Structure 569 | * 570 | * This structure holds the results of the host re-index checks and values. 571 | * 572 | */ 573 | typedef struct host_reindex_struct { 574 | char op[4]; 575 | char assert_value[100]; 576 | char arg1[512]; 577 | int data_query_id; 578 | int action; 579 | } reindex_t; 580 | 581 | /*! Ping Result Structure 582 | * 583 | * This structure holds the results of a host ping. 584 | * 585 | */ 586 | typedef struct ping_results { 587 | char hostname[SMALL_BUFSIZE]; 588 | char ping_status[50]; 589 | char ping_response[SMALL_BUFSIZE]; 590 | char snmp_status[50]; 591 | char snmp_response[SMALL_BUFSIZE]; 592 | } ping_t; 593 | 594 | /*! Name Result Structure 595 | * 596 | * This structure holds the results of a name/port split 597 | * 598 | */ 599 | typedef struct name_port { 600 | // Method = 0 - default, 1 - tcp, 2 - udp 601 | char hostname[SMALL_BUFSIZE]; 602 | int method; 603 | int port; 604 | } name_t; 605 | 606 | /*! MySQL Connection Pool Structure 607 | * 608 | * This structure holds the mysql connection pool object. 609 | */ 610 | typedef struct db_connection { 611 | int id; 612 | int free; 613 | MYSQL mysql; 614 | } pool_t; 615 | 616 | /* Include all Standard Spine Headers */ 617 | #include "poller.h" 618 | #include "locks.h" 619 | #include "keywords.h" 620 | #include "snmp.h" 621 | #include "php.h" 622 | #include "ping.h" 623 | #include "sql.h" 624 | #include "util.h" 625 | #include "nft_popen.h" 626 | #include "error.h" 627 | 628 | /* Globals */ 629 | extern config_t set; 630 | extern php_t *php_processes; 631 | extern char start_datetime[20]; 632 | extern char config_paths[CONFIG_PATHS][BUFSIZE]; 633 | extern sem_t available_threads; 634 | extern sem_t available_scripts; 635 | extern pool_t *db_pool_remote; 636 | extern pool_t *db_pool_local; 637 | 638 | #endif /* not _SPINE_H_ */ 639 | -------------------------------------------------------------------------------- /sql.c: -------------------------------------------------------------------------------- 1 | /* 2 | ex: set tabstop=4 shiftwidth=4 autoindent: 3 | +-------------------------------------------------------------------------+ 4 | | Copyright (C) 2004-2024 The Cacti Group | 5 | | | 6 | | This program is free software; you can redistribute it and/or | 7 | | modify it under the terms of the GNU Lesser General Public | 8 | | License as published by the Free Software Foundation; either | 9 | | version 2.1 of the License, or (at your option) any later version. | 10 | | | 11 | | This program is distributed in the hope that it will be useful, | 12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | | GNU Lesser General Public License for more details. | 15 | | | 16 | | You should have received a copy of the GNU Lesser General Public | 17 | | License along with this library; if not, write to the Free Software | 18 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 19 | | 02110-1301, USA | 20 | | | 21 | +-------------------------------------------------------------------------+ 22 | | spine: a backend data gatherer for cacti | 23 | +-------------------------------------------------------------------------+ 24 | | This poller would not have been possible without: | 25 | | - Larry Adams (current development and enhancements) | 26 | | - Rivo Nurges (rrd support, mysql poller cache, misc functions) | 27 | | - RTG (core poller code, pthreads, snmp, autoconf examples) | 28 | | - Brady Alleman/Doug Warner (threading ideas, implementation details) | 29 | +-------------------------------------------------------------------------+ 30 | | - Cacti - http://www.cacti.net/ | 31 | +-------------------------------------------------------------------------+ 32 | */ 33 | 34 | #include "common.h" 35 | #include "spine.h" 36 | 37 | /*! \fn int db_insert(MYSQL *mysql, int type, const char *query) 38 | * \brief inserts a row or rows in a database table. 39 | * \param mysql the database connection object 40 | * \param type the database to connect to local or remote 41 | * \param query the database query to execute 42 | * 43 | * Unless the SQL_readonly boolean is set to TRUE, the function will execute 44 | * the SQL statement specified in the query variable. 45 | * 46 | * \return TRUE if successful, or FALSE if not. 47 | * 48 | */ 49 | int db_insert(MYSQL *mysql, int type, const char *query) { 50 | int error; 51 | int error_count = 0; 52 | char query_frag[LRG_BUFSIZE]; 53 | 54 | /* save a fragment just in case */ 55 | memset(query_frag, 0, LRG_BUFSIZE); 56 | snprintf(query_frag, LRG_BUFSIZE, "%s", query); 57 | 58 | /* show the sql query */ 59 | SPINE_LOG_DEVDBG(("DEVDBG: SQL:%s", query_frag)); 60 | 61 | while(1) { 62 | if (set.SQL_readonly == FALSE) { 63 | if (mysql_query(mysql, query)) { 64 | error = mysql_errno(mysql); 65 | 66 | if (error == 2013 || error == 2006) { 67 | if (errno != EINTR) { 68 | db_reconnect(mysql, error, "db_insert"); 69 | 70 | error_count++; 71 | 72 | if (error_count > 30) { 73 | die("FATAL: Too many Reconnect Attempts!"); 74 | } 75 | 76 | continue; 77 | } else { 78 | usleep(50000); 79 | continue; 80 | } 81 | } 82 | 83 | if ((error == 1213) || (error == 1205)) { 84 | usleep(50000); 85 | error_count++; 86 | 87 | if (error_count > 30) { 88 | SPINE_LOG(("ERROR: Too many Lock/Deadlock errors occurred!, SQL Fragment:'%s'", query_frag)); 89 | return FALSE; 90 | } 91 | 92 | continue; 93 | } else { 94 | SPINE_LOG(("ERROR: SQL Failed! Error:'%i', Message:'%s', SQL Fragment:'%s'", error, mysql_error(mysql), query_frag)); 95 | return FALSE; 96 | } 97 | } else { 98 | return TRUE; 99 | } 100 | } else { 101 | return TRUE; 102 | } 103 | } 104 | } 105 | 106 | int db_reconnect(MYSQL *mysql, int error, char *function) { 107 | unsigned long mysql_thread = 0; 108 | char query[100]; 109 | 110 | mysql_thread = mysql_thread_id(mysql); 111 | mysql_ping(mysql); 112 | 113 | if (mysql_thread_id(mysql) != mysql_thread) { 114 | SPINE_LOG(("WARNING: Connection Broken in Function %s with Error %i. Reconnect successful.", function, error)); 115 | snprintf(query, 100, "KILL %lu;", mysql_thread); 116 | mysql_query(mysql, query); 117 | mysql_query(mysql, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'NO_ZERO_DATE', ''))"); 118 | mysql_query(mysql, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'NO_ZERO_IN_DATE', ''))"); 119 | mysql_query(mysql, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY', ''))"); 120 | mysql_query(mysql, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'NO_AUTO_VALUE_ON_ZERO', ''))"); 121 | mysql_query(mysql, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'TRADITIONAL', ''))"); 122 | mysql_query(mysql, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'STRICT_ALL_TABLES', ''))"); 123 | 124 | sleep(1); 125 | 126 | return TRUE; 127 | } else { 128 | SPINE_LOG(("WARNING: Connection Broken with Error %i. Reconnect failed.", error)); 129 | return FALSE; 130 | } 131 | } 132 | 133 | /*! \fn MYSQL_RES *db_query(MYSQL *mysql, int type, const char *query) 134 | * \brief executes a query and returns a pointer to the result set. 135 | * \param mysql the database connection object 136 | * \param query the database query to execute 137 | * 138 | * This function will execute the SQL statement specified in the query variable. 139 | * 140 | * \return MYSQL_RES a MySQL result structure 141 | * 142 | */ 143 | MYSQL_RES *db_query(MYSQL *mysql, int type, const char *query) { 144 | MYSQL_RES *mysql_res = 0; 145 | 146 | int error = 0; 147 | int error_count = 0; 148 | 149 | char query_frag[LRG_BUFSIZE]; 150 | 151 | /* save a fragment just in case */ 152 | memset(query_frag, 0, LRG_BUFSIZE); 153 | snprintf(query_frag, LRG_BUFSIZE, "%s", query); 154 | 155 | /* show the sql query */ 156 | SPINE_LOG_DEVDBG(("DEVDBG: SQL:%s", query_frag)); 157 | 158 | while (1) { 159 | if (mysql_query(mysql, query)) { 160 | error = mysql_errno(mysql); 161 | 162 | if (error == 2013 || error == 2006) { 163 | if (errno != EINTR) { 164 | db_reconnect(mysql, error, "db_query"); 165 | 166 | error_count++; 167 | 168 | if (error_count > 30) { 169 | die("FATAL: Too many Reconnect Attempts!"); 170 | } 171 | 172 | continue; 173 | } else { 174 | usleep(50000); 175 | continue; 176 | } 177 | } 178 | 179 | if (error == 1213 || error == 1205) { 180 | usleep(50000); 181 | error_count++; 182 | 183 | if (error_count > 30) { 184 | SPINE_LOG(("FATAL: Too many Lock/Deadlock errors occurred!, SQL Fragment:'%s'", query_frag)); 185 | exit(1); 186 | } 187 | 188 | continue; 189 | } else { 190 | SPINE_LOG(("FATAL: Database Error:'%i', Message:'%s'", error, mysql_error(mysql))); 191 | SPINE_LOG(("ERROR: The Query Was:'%s'", query)); 192 | exit(1); 193 | } 194 | } else { 195 | mysql_res = mysql_store_result(mysql); 196 | 197 | break; 198 | } 199 | } 200 | 201 | return mysql_res; 202 | } 203 | 204 | /*! \fn void db_connect(char *database, MYSQL *mysql) 205 | * \brief opens a connection to a MySQL database. 206 | * \param database a string pointer to the database name 207 | * \param mysql a pointer to a mysql database connection object 208 | * 209 | * This function will attempt to open a connection to a MySQL database and then 210 | * return the connection object to the calling function. If the database connection 211 | * fails more than 20 times, the function will fail and Spine will terminate. 212 | * 213 | */ 214 | void db_connect(int type, MYSQL *mysql) { 215 | int tries; 216 | int attempts; 217 | int timeout; 218 | int rtimeout; 219 | int wtimeout; 220 | int options_error; 221 | int success; 222 | int error = 0; 223 | bool reconnect; 224 | MYSQL *connect_error; 225 | char *hostname = NULL; 226 | char *socket = NULL; 227 | struct stat socket_stat; 228 | static int connections = 0; 229 | 230 | /* see if the hostname variable is a file reference. If so, 231 | * and if it is a socket file, setup mysql to use it. 232 | */ 233 | if (set.poller_id > 1) { 234 | if (type == LOCAL) { 235 | STRDUP_OR_DIE(hostname, set.db_host, "db_host") 236 | 237 | if (stat(hostname, &socket_stat) == 0) { 238 | if (socket_stat.st_mode & S_IFSOCK) { 239 | socket = strdup (set.db_host); 240 | hostname = NULL; 241 | } 242 | } else if ((socket = strstr(hostname,":"))) { 243 | *socket++ = 0x0; 244 | } 245 | } else { 246 | STRDUP_OR_DIE(hostname, set.rdb_host, "rdb_host") 247 | } 248 | } else { 249 | STRDUP_OR_DIE(hostname, set.db_host, "db_host") 250 | 251 | if (stat(hostname, &socket_stat) == 0) { 252 | if (socket_stat.st_mode & S_IFSOCK) { 253 | socket = strdup (set.db_host); 254 | hostname = NULL; 255 | } 256 | } else if ((socket = strstr(hostname,":"))) { 257 | *socket++ = 0x0; 258 | } 259 | } 260 | 261 | /* initialalize variables */ 262 | tries = 2; 263 | success = FALSE; 264 | timeout = 5; 265 | rtimeout = 30; 266 | wtimeout = 30; 267 | reconnect = 1; 268 | attempts = 1; 269 | 270 | mysql_init(mysql); 271 | 272 | if (mysql == NULL) { 273 | printf("FATAL: Database unable to allocate memory and therefore can not connect\n"); 274 | exit(1); 275 | } 276 | 277 | MYSQL_SET_OPTION(MYSQL_OPT_READ_TIMEOUT, (int *)&rtimeout, "read timeout"); 278 | MYSQL_SET_OPTION(MYSQL_OPT_WRITE_TIMEOUT, (int *)&wtimeout, "write timeout"); 279 | MYSQL_SET_OPTION(MYSQL_OPT_CONNECT_TIMEOUT, (int *)&timeout, "general timeout"); 280 | 281 | #if defined(MARIADB_BASE_VERSION) || (MYSQL_VERSION_ID < 80034 && MYSQL_VERSION_ID >= 50013) 282 | MYSQL_SET_OPTION(MYSQL_OPT_RECONNECT, &reconnect, "reconnect"); 283 | #endif 284 | 285 | #ifdef HAS_MYSQL_OPT_RETRY_COUNT 286 | MYSQL_SET_OPTION(MYSQL_OPT_RETRY_COUNT, &tries, "retry count"); 287 | #endif 288 | 289 | /* set SSL options if available */ 290 | #ifdef HAS_MYSQL_OPT_SSL_KEY 291 | char *ssl_key = NULL; 292 | char *ssl_ca = NULL; 293 | char *ssl_cert = NULL; 294 | 295 | if (type == REMOTE) { 296 | STRDUP_OR_DIE(ssl_key, set.rdb_ssl_key, "rdb_ssl_key"); 297 | STRDUP_OR_DIE(ssl_ca, set.rdb_ssl_ca, "rdb_ssl_ca"); 298 | STRDUP_OR_DIE(ssl_cert, set.rdb_ssl_cert, "rdb_ssl_cert"); 299 | } else { 300 | STRDUP_OR_DIE(ssl_key, set.db_ssl_key, "db_ssl_key"); 301 | STRDUP_OR_DIE(ssl_ca, set.db_ssl_ca, "db_ssl_ca"); 302 | STRDUP_OR_DIE(ssl_cert, set.db_ssl_cert, "db_ssl_cert"); 303 | } 304 | 305 | if (strlen(ssl_key)) MYSQL_SET_OPTION(MYSQL_OPT_SSL_KEY, ssl_key, "ssl key"); 306 | if (strlen(ssl_ca)) MYSQL_SET_OPTION(MYSQL_OPT_SSL_CA, ssl_ca, "ssl ca"); 307 | if (strlen(ssl_cert)) MYSQL_SET_OPTION(MYSQL_OPT_SSL_CERT, ssl_cert, "ssl cert"); 308 | 309 | #endif 310 | 311 | while (tries > 0) { 312 | tries--; 313 | 314 | if (set.poller_id > 1) { 315 | if (type == LOCAL) { 316 | connect_error = mysql_real_connect(mysql, hostname, set.db_user, set.db_pass, set.db_db, set.db_port, socket, 0); 317 | } else { 318 | connect_error = mysql_real_connect(mysql, hostname, set.rdb_user, set.rdb_pass, set.rdb_db, set.rdb_port, socket, 0); 319 | } 320 | } else { 321 | connect_error = mysql_real_connect(mysql, hostname, set.db_user, set.db_pass, set.db_db, set.db_port, socket, 0); 322 | } 323 | 324 | if (!connect_error) { 325 | error = mysql_errno(mysql); 326 | 327 | if ((error == 2002 || error == 2003 || error == 2006 || error == 2013) && errno == EINTR) { 328 | usleep(5000); 329 | tries++; 330 | success = FALSE; 331 | } else if (error == 2002) { 332 | printf("Database: Connection Failed: Attempt:'%u', Error:'%u', Message:'%s'\n", attempts, mysql_errno(mysql), mysql_error(mysql)); 333 | sleep(1); 334 | success = FALSE; 335 | } else if (error != 1049 && error != 2005 && error != 1045) { 336 | printf("Database: Connection Failed: Error:'%u', Message:'%s'\n", error, mysql_error(mysql)); 337 | success = FALSE; 338 | usleep(50000); 339 | } else { 340 | tries = 0; 341 | success = FALSE; 342 | } 343 | } else { 344 | tries = 0; 345 | success = TRUE; 346 | break; 347 | } 348 | 349 | attempts++; 350 | } 351 | 352 | if (hostname != NULL) { 353 | free(hostname); 354 | } 355 | 356 | #ifdef HAS_MYSQL_OPT_SSL_KEY 357 | if (ssl_key != NULL) { 358 | free(ssl_key); 359 | } 360 | 361 | if (ssl_ca != NULL) { 362 | free(ssl_ca); 363 | } 364 | 365 | if (ssl_cert != NULL) { 366 | free(ssl_cert); 367 | } 368 | #endif 369 | 370 | if (!success){ 371 | printf("FATAL: Connection Failed, Error:'%i', Message:'%s'\n", error, mysql_error(mysql)); 372 | exit(1); 373 | } 374 | 375 | SPINE_LOG_DEBUG(("DEBUG: Total Connections made %i", connections)); 376 | 377 | connections++; 378 | } 379 | 380 | /*! \fn void db_disconnect(MYSQL *mysql) 381 | * \brief closes connection to MySQL database 382 | * \param mysql the database connection object 383 | * 384 | */ 385 | void db_disconnect(MYSQL *mysql) { 386 | if (mysql != NULL) { 387 | mysql_close(mysql); 388 | } 389 | } 390 | 391 | /*! \fn void db_create_connection_pool(int type) 392 | * \brief Creates a connection pool for spine 393 | * \param type the connection type, LOCAL or REMOTE 394 | * 395 | */ 396 | void db_create_connection_pool(int type) { 397 | int id; 398 | 399 | if (type == LOCAL) { 400 | SPINE_LOG_DEBUG(("DEBUG: Creating Local Connection Pool of %i threads.", set.threads)); 401 | 402 | for(id = 0; id < set.threads; id++) { 403 | SPINE_LOG_DEBUG(("DEBUG: Creating Local Connection %i.", id)); 404 | 405 | db_connect(type, &db_pool_local[id].mysql); 406 | 407 | db_insert(&db_pool_local[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'NO_ZERO_DATE', ''))"); 408 | db_insert(&db_pool_local[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'NO_ZERO_IN_DATE', ''))"); 409 | db_insert(&db_pool_local[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY', ''))"); 410 | db_insert(&db_pool_local[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'NO_AUTO_VALUE_ON_ZERO', ''))"); 411 | db_insert(&db_pool_local[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'TRADITIONAL', ''))"); 412 | db_insert(&db_pool_local[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'STRICT_ALL_TABLES', ''))"); 413 | db_insert(&db_pool_local[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'STRICT_TRANS_TABLES', ''))"); 414 | 415 | db_pool_local[id].free = TRUE; 416 | db_pool_local[id].id = id; 417 | } 418 | } else { 419 | SPINE_LOG_DEBUG(("DEBUG: Creating Remote Connection Pool of %i threads.", set.threads)); 420 | 421 | for(id = 0; id < set.threads; id++) { 422 | SPINE_LOG_DEBUG(("DEBUG: Creating Remote Connection %i.", id)); 423 | 424 | db_connect(type, &db_pool_remote[id].mysql); 425 | 426 | db_insert(&db_pool_remote[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'NO_ZERO_DATE', ''))"); 427 | db_insert(&db_pool_remote[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'NO_ZERO_IN_DATE', ''))"); 428 | db_insert(&db_pool_remote[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY', ''))"); 429 | db_insert(&db_pool_remote[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'NO_AUTO_VALUE_ON_ZERO', ''))"); 430 | db_insert(&db_pool_remote[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'TRADITIONAL', ''))"); 431 | db_insert(&db_pool_remote[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'STRICT_ALL_TABLES', ''))"); 432 | db_insert(&db_pool_remote[id].mysql, LOCAL, "SET SESSION sql_mode = (SELECT REPLACE(@@sql_mode,'STRICT_TRANS_TABLES', ''))"); 433 | 434 | db_pool_remote[id].free = TRUE; 435 | db_pool_remote[id].id = id; 436 | } 437 | } 438 | } 439 | 440 | /*! \fn void db_close_connection_pool(int type) 441 | * \brief Closes a connection pool for spine 442 | * \param type the connection type, LOCAL or REMOTE 443 | * 444 | */ 445 | void db_close_connection_pool(int type) { 446 | int id; 447 | 448 | if (type == LOCAL) { 449 | for(id = 0; id < set.threads; id++) { 450 | SPINE_LOG_DEBUG(("DEBUG: Closing Local Connection Pool ID %i", id)); 451 | db_disconnect(&db_pool_local[id].mysql); 452 | } 453 | 454 | free(db_pool_local); 455 | } else { 456 | for(id = 0; id < set.threads; id++) { 457 | SPINE_LOG_DEBUG(("DEBUG: Closing Remote Connection Pool ID %i", id)); 458 | db_disconnect(&db_pool_remote[id].mysql); 459 | } 460 | 461 | free(db_pool_remote); 462 | } 463 | } 464 | 465 | /*! \fn pool_t db_get_connection(int type) 466 | * \brief returns a free mysql connection from the pool 467 | * \param type the connection type, LOCAL or REMOTE 468 | * 469 | */ 470 | pool_t *db_get_connection(int type) { 471 | int id; 472 | 473 | thread_mutex_lock(LOCK_POOL); 474 | 475 | if (type == LOCAL) { 476 | SPINE_LOG_DEBUG(("DEBUG: Traversing Local Connection Pool for free connection.")); 477 | for (id = 0; id < set.threads; id++) { 478 | SPINE_LOG_DEBUG(("DEBUG: Checking Local Pool ID %i.", id)); 479 | if (db_pool_local[id].free == TRUE) { 480 | SPINE_LOG_DEBUG(("DEBUG: Allocating Local Pool ID %i.", id)); 481 | db_pool_local[id].free = FALSE; 482 | thread_mutex_unlock(LOCK_POOL); 483 | return &db_pool_local[id]; 484 | } 485 | } 486 | } else { 487 | SPINE_LOG_DEBUG(("DEBUG: Traversing Remote Connection Pool for free connection.")); 488 | for (id = 0; id < set.threads; id++) { 489 | SPINE_LOG_DEBUG(("DEBUG: Checking Remote Pool ID %i.", id)); 490 | if (db_pool_remote[id].free == TRUE) { 491 | SPINE_LOG_DEBUG(("DEBUG: Allocating Remote Pool ID %i.", id)); 492 | db_pool_remote[id].free = FALSE; 493 | thread_mutex_unlock(LOCK_POOL); 494 | return &db_pool_remote[id]; 495 | } 496 | } 497 | } 498 | 499 | SPINE_LOG(("FATAL: Connection Pool Fatal Error.")); 500 | 501 | thread_mutex_unlock(LOCK_POOL); 502 | 503 | return NULL; 504 | } 505 | 506 | /*! \fn voi db_release_connection(int id) 507 | * \brief marks a database connection as free 508 | * \param id the connection id 509 | * 510 | */ 511 | void db_release_connection(int type, int id) { 512 | thread_mutex_lock(LOCK_POOL); 513 | 514 | if (type == LOCAL) { 515 | SPINE_LOG_DEBUG(("DEBUG: Freeing Local Pool ID %i", id)); 516 | db_pool_local[id].free = TRUE; 517 | } else { 518 | SPINE_LOG_DEBUG(("DEBUG: Freeing Remote Pool ID %i", id)); 519 | db_pool_remote[id].free = TRUE; 520 | } 521 | 522 | thread_mutex_unlock(LOCK_POOL); 523 | } 524 | 525 | /*! \fn int append_hostrange(char *obuf, const char *colname, const config_t *set) 526 | * \brief appends a host range to a sql select statement 527 | * \param obuf the sql select statement to have the host range appended 528 | * \param colname the sql column name that will have the host range checked 529 | * \param set global runtime settings 530 | * 531 | * Several places in the code need to limit the range of hosts to 532 | * those with a certain ID range, but only if those range values 533 | * are actually nonzero. 534 | * 535 | * This appends the SQL clause if necessary, returning the # of 536 | * characters added to the buffer. Else return 0. 537 | * 538 | * \return the number of characters added to the end of the character buffer 539 | * 540 | */ 541 | int append_hostrange(char *obuf, const char *colname) { 542 | if (HOSTID_DEFINED(set.start_host_id) && HOSTID_DEFINED(set.end_host_id)) { 543 | return sprintf(obuf, " AND %s BETWEEN %d AND %d", 544 | colname, 545 | set.start_host_id, 546 | set.end_host_id); 547 | } else { 548 | return 0; 549 | } 550 | } 551 | 552 | /*! \fn void db_escape(MYSQL *mysql, char *output, int max_size, const char *input) 553 | * \brief Escapes a text string to make it safe for mysql insert/updates 554 | * \param mysql the connection object 555 | * \param output a pointer to the output string 556 | * \param a pointer to the input string 557 | * 558 | * A simple implementation of the mysql_real_escape_string that one 559 | * day should be portable. 560 | * 561 | * \return void 562 | * 563 | */ 564 | void db_escape(MYSQL *mysql, char *output, int max_size, const char *input) { 565 | if (input == NULL) return; 566 | 567 | char input_trimmed[DBL_BUFSIZE]; 568 | int max_escaped_input_size = (strlen(input) * 2) + 1; 569 | 570 | if (max_escaped_input_size > max_size) { 571 | snprintf(input_trimmed, (max_size / 2) - 1, "%s", input); 572 | } else { 573 | snprintf(input_trimmed, max_size, "%s", input); 574 | } 575 | 576 | mysql_real_escape_string(mysql, output, input_trimmed, strlen(input_trimmed)); 577 | } 578 | 579 | void db_free_result(MYSQL_RES *result) { 580 | mysql_free_result(result); 581 | } 582 | 583 | int db_column_exists(MYSQL *mysql, int type, const char *table, const char *column) { 584 | char query_frag[BUFSIZE]; 585 | MYSQL_RES *result; 586 | int exists; 587 | 588 | /* save a fragment just in case */ 589 | memset(query_frag, 0, BUFSIZE); 590 | snprintf(query_frag, BUFSIZE, "SHOW COLUMNS FROM `%s` LIKE '%s'", table, column); 591 | 592 | /* show the sql query */ 593 | SPINE_LOG_DEVDBG(("DEVDBG: db_column_exists('%s','%s'): %s", table, column, query_frag)); 594 | 595 | result = db_query(mysql, LOCAL, query_frag); 596 | if (mysql_num_rows(result)) { 597 | exists = TRUE; 598 | } else { 599 | exists = FALSE; 600 | } 601 | 602 | db_free_result(result); 603 | return exists; 604 | } -------------------------------------------------------------------------------- /sql.h: -------------------------------------------------------------------------------- 1 | /* 2 | ex: set tabstop=4 shiftwidth=4 autoindent: 3 | +-------------------------------------------------------------------------+ 4 | | Copyright (C) 2004-2024 The Cacti Group | 5 | | | 6 | | This program is free software; you can redistribute it and/or | 7 | | modify it under the terms of the GNU Lesser General Public | 8 | | License as published by the Free Software Foundation; either | 9 | | version 2.1 of the License, or (at your option) any later version. | 10 | | | 11 | | This program is distributed in the hope that it will be useful, | 12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | | GNU Lesser General Public License for more details. | 15 | | | 16 | | You should have received a copy of the GNU Lesser General Public | 17 | | License along with this library; if not, write to the Free Software | 18 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 19 | | 02110-1301, USA | 20 | | | 21 | +-------------------------------------------------------------------------+ 22 | | spine: a backend data gatherer for cacti | 23 | +-------------------------------------------------------------------------+ 24 | | This poller would not have been possible without: | 25 | | - Larry Adams (current development and enhancements) | 26 | | - Rivo Nurges (rrd support, mysql poller cache, misc functions) | 27 | | - RTG (core poller code, pthreads, snmp, autoconf examples) | 28 | | - Brady Alleman/Doug Warner (threading ideas, implementation details) | 29 | +-------------------------------------------------------------------------+ 30 | | - Cacti - http://www.cacti.net/ | 31 | +-------------------------------------------------------------------------+ 32 | */ 33 | 34 | extern int db_insert(MYSQL *mysql, int type, const char *query); 35 | extern MYSQL_RES *db_query(MYSQL *mysql, int type, const char *query); 36 | extern void db_connect(int type, MYSQL *mysql); 37 | extern void db_disconnect(MYSQL *mysql); 38 | extern void db_escape(MYSQL *mysql, char *output, int max_size, const char *input); 39 | extern void db_free_result(MYSQL_RES *result); 40 | extern void db_create_connection_pool(int type); 41 | extern void db_close_connection_pool(int type); 42 | extern pool_t *db_get_connection(int type); 43 | extern void db_release_connection(int type, int id); 44 | extern int db_reconnect(MYSQL *mysql, int error, char *location); 45 | extern int db_column_exists(MYSQL *mysql, int type, const char *table, const char *column); 46 | 47 | extern int append_hostrange(char *obuf, const char *colname); 48 | 49 | #define MYSQL_SET_OPTION(opt, value, desc) \ 50 | {\ 51 | options_error = mysql_options(mysql, opt, value); \ 52 | if (options_error < 0) {\ 53 | die("FATAL: MySQL options unable to set %s option", desc);\ 54 | }\ 55 | }\ 56 | 57 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | /* 2 | ex: set tabstop=4 shiftwidth=4 autoindent:* 3 | +-------------------------------------------------------------------------+ 4 | | Copyright (C) 2004-2024 The Cacti Group | 5 | | | 6 | | This program is free software; you can redistribute it and/or | 7 | | modify it under the terms of the GNU Lesser General Public | 8 | | License as published by the Free Software Foundation; either | 9 | | version 2.1 of the License, or (at your option) any later version. | 10 | | | 11 | | This program is distributed in the hope that it will be useful, | 12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | | GNU Lesser General Public License for more details. | 15 | | | 16 | | You should have received a copy of the GNU Lesser General Public | 17 | | License along with this library; if not, write to the Free Software | 18 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 19 | | 02110-1301, USA | 20 | | | 21 | +-------------------------------------------------------------------------+ 22 | | spine: a backend data gatherer for cacti | 23 | +-------------------------------------------------------------------------+ 24 | | This poller would not have been possible without: | 25 | | - Larry Adams (current development and enhancements) | 26 | | - Rivo Nurges (rrd support, mysql poller cache, misc functions) | 27 | | - RTG (core poller code, pthreads, snmp, autoconf examples) | 28 | | - Brady Alleman/Doug Warner (threading ideas, implementation details) | 29 | +-------------------------------------------------------------------------+ 30 | | - Cacti - http://www.cacti.net/ | 31 | +-------------------------------------------------------------------------+ 32 | */ 33 | 34 | /* cacti config reading functions */ 35 | extern void read_config_options(void); 36 | extern int read_spine_config(char *file); 37 | extern void config_defaults(void); 38 | 39 | /* cacti logging function */ 40 | extern int spine_log(const char *format, ...) 41 | __attribute__((format(printf, 1, 2))); 42 | 43 | extern void die(const char *format, ...) 44 | __attribute__((noreturn)) 45 | __attribute__((format(printf, 1, 2))); 46 | 47 | /* option processing function */ 48 | extern void set_option(const char *setting, const char *value); 49 | 50 | /* number validation functions */ 51 | extern int is_numeric(char *string); 52 | extern int is_ipaddress(const char *string); 53 | extern int all_digits(const char *str); 54 | extern int is_hexadecimal(const char * str, const short ignore_special); 55 | 56 | /* determine if a device is a debug device */ 57 | extern int is_debug_device(int device_id); 58 | 59 | /* string and file functions */ 60 | extern char *add_slashes(char *string); 61 | extern int file_exists(const char *filename); 62 | extern char *strip_alpha(char *string); 63 | extern char *strncopy(char *dst, const char *src, size_t n); 64 | extern char *trim(char *str); 65 | extern char *rtrim(char *str); 66 | extern char *ltrim(char *str); 67 | extern char *reverse(char *str); 68 | extern int strpos(char *haystack, char *needle) ; 69 | extern int char_count(const char *str, int chr); 70 | 71 | /* custom hex2dec that returns a string instead of a number */ 72 | unsigned long long hex2dec(char *str); 73 | 74 | /* custom regex replace to return a value if matches */ 75 | #define MAX_MATCHES 5 76 | #define REGEX_NUMBER "([-+]*)([0-9]*)([.][0-9]+)" 77 | char *regex_replace(const char *exp, const char *value); 78 | 79 | /* macro to copy string to string with an ending null */ 80 | #define STRNCOPY(dst, src) strncopy((dst), (src), sizeof(dst)) 81 | 82 | /* macro to duplicate string and die if fails */ 83 | #define STRDUP_OR_DIE(dst, src, reason) \ 84 | if ((dst = strdup(src)) == NULL) {\ 85 | die("FATAL: malloc() failed during strdup() for %s", reason);\ 86 | }\ 87 | 88 | 89 | /* get highres time as double */ 90 | extern double get_time_as_double(void); 91 | 92 | /* function to check to see if program has capability to use raw socket with 93 | out uid = 0 */ 94 | extern int hasCaps(); 95 | 96 | /* see if we can do things as root */ 97 | extern void checkAsRoot(); 98 | 99 | /* log format */ 100 | extern char *get_date_format(); 101 | 102 | /* start time for spine */ 103 | extern double start_time; 104 | 105 | /* the version of Cacti as a decimal */ 106 | int get_cacti_version(MYSQL *psql, int mode); 107 | --------------------------------------------------------------------------------