diff --git a/README.md b/README.md index b0cf219..5160453 100644 --- a/README.md +++ b/README.md @@ -157,11 +157,11 @@ chmod +x /etc/zabbix/zabbix_php_fpm_discovery.sh chmod +x /etc/zabbix/zabbix_php_fpm_status.sh ``` -#### 1.3. Root previliges -Automatic detection of pools requires root previliges. You can achieve it using one of the methods below. +#### 1.3. Root privileges +Automatic detection of pools requires root privileges. You can achieve it using one of the methods below. -##### 1.3.1 Root previliges for Zabbix Agent -This method sets root previliges for Zabbix Agent, i.e. the Zabbix Agent will run under `root` user, as a result all user scripts will also have the root access rights. +##### 1.3.1 Root privileges for Zabbix Agent +This method sets root privileges for Zabbix Agent, i.e. the Zabbix Agent will run under `root` user, as a result all user scripts will also have the root access rights. Edit Zabbix agent configuration file `/etc/zabbix/zabbix_agentd.conf`, find `AllowRoot` option and enable it: @@ -179,8 +179,8 @@ Edit Zabbix agent configuration file `/etc/zabbix/zabbix_agentd.conf`, find `All AllowRoot=1 ``` -##### 1.3.2 Grant previliges to the PHP-FPM autodiscovery script only -If you don't want to run Zabbix Agent as root, then you can configure the previliges only to our script. In this case you need to have `sudo` installed: +##### 1.3.2 Grant privileges to the PHP-FPM auto discovery script only +If you don't want to run Zabbix Agent as root, then you can configure the privileges only to our script. In this case you need to have `sudo` installed: ```console apt-get install sudo @@ -305,15 +305,15 @@ If you use a custom status path, then configure it in the macros section of the The setup is finished, just wait a couple of minutes till Zabbix discovers all your pools and captures the data. # Testing and Troubleshooting -## Check autodiscovery -First test that autodiscovery of PHP-FPM pools works on your machine. Run the following command: +## Check auto discovery +First test that auto discovery of PHP-FPM pools works on your machine. Run the following command: ```console -bash /etc/zabbix/zabbix_php_fpm_discovery.sh +root@server:/etc/zabbix#bash /etc/zabbix/zabbix_php_fpm_discovery.sh ``` **Important:** please make sure that you use `bash` in the command above, not `sh` or other alternatives, otherwise you may get a script syntax error message. -The output should be a valid JSON with a list of pools and their sockets, something like below: +The output should be a valid JSON with a list of pools and their sockets, something like below (you may want to use [online JSON tool](https://jsonformatter.curiousconcept.com/) for pretty formatting of the response): ```json { @@ -334,17 +334,32 @@ The output should be a valid JSON with a list of pools and their sockets, someth } ``` -If this script does not display the list, then it will show you the list of utilities that are missing on your system and must be installed. We require the following utilities to be installed: +For further investigation you can run the script above with `debug` option to get more details, example: +```console +root@server:/etc/zabbix#bash /etc/zabbix/zabbix_php_fpm_discovery.sh debug +Debug mode enabled +Success: found socket /var/lib/php7.3-fpm/web1.sock for pool web1, raw process info: php-fpm7. 5094 web1 11u unix 0x00000000dd9ea858 0t0 104495372 /var/lib/php7.3-fpm/web1.sock type=STREAM +Success: found socket /var/lib/php7.3-fpm/web4.sock for pool web4, raw process info: php-fpm7. 5096 web4 11u unix 0x00000000562748dd 0t0 104495374 /var/lib/php7.3-fpm/web4.sock type=STREAM +Success: found socket /run/php/php7.3-fpm.sock for pool www, raw process info: php-fpm7. 5098 www-data 11u unix 0x00000000ef5ef2fb 0t0 104495376 /run/php/php7.3-fpm.sock type=STREAM +Resulting JSON data for Zabbix: +{"data":[{"{#POOLNAME}":"web1","{#POOLSOCKET}":"/var/lib/php7.3-fpm/web1.sock"},{"{#POOLNAME}":"web4","{#POOLSOCKET}":"/var/lib/php7.3-fpm/web4.sock"},{"{#POOLNAME}":"www","{#POOLSOCKET}":"/run/php/php7.3-fpm.sock"}]} +``` -- awk -- ps -- grep -- sort -- head -- lsof -- jq +Any warning or error messages will be displayed here. -If some pools are missing, then check that they do really exist and are running, for example, using command: +**Note:** having a warning messages does not necessarily mean that you have a error here, because different OS may provide data about processes differently. So, if you don't see any error messages here, then the script works fine. + +The script can show you the list of utilities that are missing on your system and must be installed. We require the following utilities to be installed: + +- `awk` +- `ps` +- `grep` +- `sort` +- `head` +- `lsof` +- `jq` + +If some pools are missing, then you can manually check that they do really exist and are running, for example, using command: ```console ps aux | grep "php-fpm" diff --git a/zabbix/zabbix_php_fpm_discovery.sh b/zabbix/zabbix_php_fpm_discovery.sh index c2f2b26..151e8fc 100644 --- a/zabbix/zabbix_php_fpm_discovery.sh +++ b/zabbix/zabbix_php_fpm_discovery.sh @@ -1,6 +1,7 @@ #!/bin/bash #Ramil Valitov ramilvalitov@gmail.com #https://github.com/rvalitov/zabbix-php-fpm +#This script scans local machine for active PHP-FPM pools and returns them as a list in JSON format S_PS=`type -P ps` S_GREP=`type -P grep` @@ -39,31 +40,107 @@ if [[ ! -f $S_JQ ]]; then exit 1 fi -mapfile -t PS_LIST < <( $S_PS ax | $S_GREP "php-fpm: pool " | $S_GREP -v grep ) +DEBUG_MODE="" +if [[ ! -z $1 ]] && [[ $1 == "debug" ]]; then + DEBUG_MODE="1" + echo "Debug mode enabled" +fi + +# Prints a string on screen. Works only if debug mode is enabled. +function PrintDebug(){ + if [[ ! -z $DEBUG_MODE ]] && [[ ! -z $1 ]]; then + echo $1 + fi +} + +mapfile -t PS_LIST < <( $S_PS ax | $S_GREP -F "php-fpm: pool " | $S_GREP -F -v "grep" ) POOL_LIST=`printf '%s\n' "${PS_LIST[@]}" | $S_AWK '{print $NF}' | $S_SORT -u` POOL_FIRST=0 -echo -n "{\"data\":[" +#We store the resulting JSON data for Zabbix in the following var: +RESULT_DATA="{\"data\":[" while IFS= read -r line do - POOL_PID=`printf '%s\n' "${PS_LIST[@]}" | $S_GREP "php-fpm: pool $line$" | $S_HEAD -1 | $S_AWK '{print $1}'` + POOL_PID=`printf '%s\n' "${PS_LIST[@]}" | $S_GREP -F -w "php-fpm: pool $line" | $S_HEAD -1 | $S_AWK '{print $1}'` if [[ ! -z $POOL_PID ]]; then - POOL_SOCKET=`$S_LSOF -p $POOL_PID 2>/dev/null | $S_GREP -e unix -e TCP | $S_HEAD -1 | $S_AWK '{print $9}'` #We search for socket or IP address and port #Socket example: #php-fpm7. 25897 root 9u unix 0x000000006509e31f 0t0 58381847 /run/php/php7.3-fpm.sock type=STREAM #IP example: - #php-fpm7. 1110 defualt 0u IPv4 15760 0t0 TCP localhost:8002 (LISTEN) - if [[ ! -z $POOL_SOCKET ]]; then - if [[ $POOL_FIRST == 1 ]]; then - echo -n "," + #php-fpm7. 1110 default 0u IPv4 15760 0t0 TCP localhost:8002 (LISTEN) + + #Check all matching processes, because we may face a redirect (or a symlink?), examples: + #php-fpm7. 1203 www-data 5u unix 0x000000006509e31f 0t0 15068771 type=STREAM + #php-fpm7. 6086 www-data 11u IPv6 21771 0t0 TCP *:9000 (LISTEN) + #php-fpm7. 1203 www-data 8u IPv4 15070917 0t0 TCP localhost.localdomain:23054->localhost.localdomain:postgresql (ESTABLISHED) + #More info at https://github.com/rvalitov/zabbix-php-fpm/issues/12 + + PrintDebug "Started analysis of pool $line, PID $POOL_PID" + #Extract only important information: + POOL_PARAMS_LIST=`$S_LSOF -p $POOL_PID 2>/dev/null | $S_GREP -w -e "unix" -e "TCP"` + FOUND_POOL="" + while IFS= read -r pool + do + if [[ ! -z $pool ]]; then + if [[ -z $FOUND_POOL ]]; then + PrintDebug "Checking process: $pool" + POOL_TYPE=`echo "${pool}" | $S_AWK '{print $5}'` + POOL_SOCKET=`echo "${pool}" | $S_AWK '{print $9}'` + if [[ ! -z $POOL_TYPE ]] && [[ ! -z $POOL_SOCKET ]]; then + if [[ $POOL_TYPE == "unix" ]]; then + #We have a socket here, test if it's actually a socket: + if [[ -S $POOL_SOCKET ]]; then + FOUND_POOL="1" + PrintDebug "Success: found socket $POOL_SOCKET" + else + PrintDebug "Error: specified socket $POOL_SOCKET is not valid" + fi + elif [[ $POOL_TYPE == "IPv4" ]] || [[ $POOL_TYPE == "IPv6" ]]; then + #We have a TCP connection here, check it: + CONNECTION_TYPE=`echo "${pool}" | $S_AWK '{print $8}'` + if [[ $CONNECTION_TYPE == "TCP" ]]; then + #The connection must have state LISTEN: + LISTEN=`echo ${pool} | $S_GREP -F -w "(LISTEN)"` + if [[ ! -z $LISTEN ]]; then + #Check and replace * to localhost if it's found. Asterisk means that the PHP listens on + #all interfaces. + POOL_SOCKET=`echo -n ${POOL_SOCKET/*:/localhost:}` + FOUND_POOL="1" + PrintDebug "Success: found TCP connection $POOL_SOCKET" + else + PrintDebug "Warning: expected connection state must be LISTEN, but it was not detected" + fi + else + PrintDebug "Warning: expected connection type is TCP, but found $CONNECTION_TYPE" + fi + else + PrintDebug "Unsupported type $POOL_TYPE, skipping" + fi + else + PrintDebug "Warning: pool type or socket is empty" + fi + else + PrintDebug "Pool already found, skipping process: $pool" + fi + else + PrintDebug "Error: failed to get process information. Probably insufficient privileges. Use sudo or run this script under root." fi - echo -n "{\"{#POOLNAME}\":" - echo -n "$line" | $S_JQ -aR . - echo -n ",\"{#POOLSOCKET}\":" - echo -n "$POOL_SOCKET" | $S_JQ -aR . - echo -n "}" + done <<< "$POOL_PARAMS_LIST" + + if [[ ! -z $FOUND_POOL ]]; then + JSON_POOL=`echo -n "$line" | $S_JQ -aR .` + JSON_SOCKET=`echo -n "$POOL_SOCKET" | $S_JQ -aR .` + if [[ $POOL_FIRST == 1 ]]; then + RESULT_DATA="$RESULT_DATA," + fi + RESULT_DATA="$RESULT_DATA{\"{#POOLNAME}\":$JSON_POOL,\"{#POOLSOCKET}\":$JSON_SOCKET}" POOL_FIRST=1 + else + PrintDebug "Error: failed to discover information for pool $line" fi + else + PrintDebug "Error: failed to find PID for pool $line" fi done <<< "$POOL_LIST" -echo -n "]}" +RESULT_DATA="$RESULT_DATA]}" +PrintDebug "Resulting JSON data for Zabbix:" +echo -n $RESULT_DATA diff --git a/zabbix/zabbix_php_fpm_status.sh b/zabbix/zabbix_php_fpm_status.sh index 7837398..cbf869e 100644 --- a/zabbix/zabbix_php_fpm_status.sh +++ b/zabbix/zabbix_php_fpm_status.sh @@ -1,13 +1,13 @@ #!/bin/bash #Ramil Valitov ramilvalitov@gmail.com #https://github.com/rvalitov/zabbix-php-fpm +#Script gets status of PHP-FPM pool S_FCGI=`type -P cgi-fcgi` S_GREP=`type -P grep` if [[ ! -f $S_FCGI ]]; then echo "Utility 'cgi-fcgi' not found. Please, install it first." - echo "In Debian you should install a package libfcgi0ldbl" exit 1 fi