#!/bin/bash # vim: tabstop=4 shiftwidth=4 softtabstop=4 expandtab # # Nagios plugin to check Postgresql streamin replication state # # Could be use on Master or on standby node # # Requirements: # # Some CLI tools: sudo, awk, sed, bc, psql and pg_lscluster # # On master node: Slave nodes must be able to connect with user from recovery.conf / # `postgresql.auto.conf` (or user specify using -U) to database with the same # name (or another specified with -D) as trust (or using password specified in # ~/.pgpass). This user must have SUPERUSER privilege (need to get replication # details). # # On standby node: PG_USER must be able to connect locally on the database with the same name # (or another specified with -D) as trust (or using password specified in # ~/.pgpass). # # Author: Benjamin Renard # Version: 2024.7.3 # Date: 2024-07-17 11:36:09 +0200 # Source: https://gitea.zionetrix.net/bn8/check_pg_streaming_replication # SPDX-License-Identifier: GPL-3.0-or-later # DEFAULT_PG_USER=postgres DEFAULT_PG_VERSION=9.1 DEFAULT_PG_MAIN=/var/lib/postgresql/$PG_VERSION/main DEFAULT_PG_PORT=5432 PG_USER="" PG_VERSION="" PG_MAIN="" PG_MASTER_USER="" PSQL_BIN=/usr/bin/psql PG_LSCLUSTER_BIN=/usr/bin/pg_lsclusters RECOVERY_CONF="" PG_DEFAULT_PORT="" PG_DEFAULT_APP_NAME=$( hostname ) PG_DB="" REPLAY_WARNING_DELAY=3 REPLAY_CRITICAL_DELAY=5 EXPECTED_SYNC_STATE=sync EXPECTED_MODE=auto DEBUG=0 function usage () { ERROR="$*" [[ -n "$ERROR" ]] && echo -e "$ERROR\n" cat << EOF Usage: $0 [-d] [-h] [options] -u pg_user Specify local Postgres user (Default: try to auto-detect or use $DEFAULT_PG_USER) -b psql_bin Specify psql binary path (Default: $PSQL_BIN) -B pg_lsclusters_bin Specify pg_lsclusters binary path (Default: $PG_LSCLUSTER_BIN) -V pg_version Specify Postgres version (Default: try to auto-detect or use $DEFAULT_PG_VERSION) -m pg_main Specify Postgres main directory path (Default: try to auto-detect or use $DEFAULT_PG_MAIN) -r recovery_conf Specify Postgres recovery configuration file path (Default: [PG_MAIN]/recovery.conf on PG <= 11, [PG_MAIN]/postgresql.auto.conf on PG >= 12) -U pg_master_user Specify Postgres user to use on master (Default: user from recovery.conf file) -p pg_port Specify default Postgres master TCP port (Default: same as local PostgreSQL port if detected or use $DEFAULT_PG_PORT) -D dbname Specify DB name on Postgres master/slave to connect on (Default: PG_USER, must match with .pgpass one is used) -w replay_warn_delay Specify the replay warning delay in second (Default: $REPLAY_WARNING_DELAY) -c replay_crit_delay Specify the replay critical delay in second (Default: $REPLAY_CRITICAL_DELAY) -e expected_sync_state The expected replication state ('sync' or 'async', default: $EXPECTED_SYNC_STATE) -E expected_mode The expected mode ('master', 'hot-standby' or 'auto', default: '$EXPECTED_MODE') -d Debug mode -h Show this message EOF [[ -n "$ERROR" ]] && exit 1 || exit 0 } while getopts "hu:b:B:V:m:r:U:p:D:w:c:e:E:d" OPTION; do case $OPTION in u) PG_USER=$OPTARG ;; b) PSQL_BIN=$OPTARG ;; B) PG_LSCLUSTER_BIN=$OPTARG ;; V) PG_VERSION=$OPTARG ;; m) PG_MAIN=$OPTARG ;; r) RECOVERY_CONF=$OPTARG ;; U) PG_MASTER_USER=$OPTARG ;; p) PG_DEFAULT_PORT=$OPTARG ;; D) PG_DB=$OPTARG ;; w) REPLAY_WARNING_DELAY=$OPTARG ;; c) REPLAY_CRITICAL_DELAY=$OPTARG ;; e) [[ "$OPTARG" != "sync" ]] && [[ "$OPTARG" != "async" ]] && \ usage "Invalid expected replication state '$OPTARG'." \ "Possible values: sync or async." EXPECTED_SYNC_STATE=$OPTARG ;; E) [[ "$OPTARG" != "master" ]] && [[ "$OPTARG" != "hot-standby" ]] && \ [[ "$OPTARG" != "auto" ]] && \ usage "Invalid expected mode '$OPTARG'. Possible values: master, hot-standby" \ "or auto." EXPECTED_MODE=$OPTARG ;; d) DEBUG=1 ;; h) usage ;; \?) echo -n "Unknown option" usage esac done function debug() { if [[ $DEBUG -eq 1 ]]; then >&2 echo -e "[DEBUG] $*" fi } debug "Starting options (before handling auto-detection/default values): PG_VERSION = $PG_VERSION PG_DB = $PG_DB PG_USER = $PG_USER PSQL_BIN = $PSQL_BIN PG_LSCLUSTER_BIN = $PG_LSCLUSTER_BIN PG_MAIN = $PG_MAIN RECOVERY_CONF = $RECOVERY_CONF PG_DEFAULT_PORT = $PG_DEFAULT_PORT PG_DEFAULT_APP_NAME = $PG_DEFAULT_APP_NAME REPLAY_WARNING_DELAY = $REPLAY_WARNING_DELAY REPLAY_CRITICAL_DELAY = $REPLAY_CRITICAL_DELAY EXPECTED_SYNC_STATE = $EXPECTED_SYNC_STATE EXPECTED_MODE = $EXPECTED_MODE " # Auto-detect PostgreSQL information using pg_lsclusters if [[ -x "$PG_LSCLUSTER_BIN" ]]; then PG_CLUSTER=$( $PG_LSCLUSTER_BIN -h 2>/dev/null | head -n1 ) if [[ -n "$PG_CLUSTER" ]]; then debug "pg_lsclusters output:\n\t$PG_CLUSTER" # Output example: # 9.6 main 5432 online,recovery postgres /var/lib/postgresql/9.6/main \ # /var/log/postgresql/postgresql-9.6-main.log # 13 main 5432 online,recovery,pacemaker postgres /var/lib/postgresql/13/main \ # /var/log/postgresql/postgresql-13-main.log [[ -z "$PG_VERSION" ]] && PG_VERSION=$( awk -F ' +' '{print $1}' <<< "$PG_CLUSTER" ) [[ -z "$PG_DEFAULT_PORT" ]] && \ PG_DEFAULT_PORT=$( awk -F ' +' '{print $3}' <<< "$PG_CLUSTER" ) [[ -z "$PG_USER" ]] && PG_USER=$( awk -F ' +' '{print $5}' <<< "$PG_CLUSTER" ) [[ -z "$PG_MAIN" ]] && PG_MAIN=$( awk -F ' +' '{print $6}' <<< "$PG_CLUSTER" ) fi else debug "pg_lsclusters not found ($PG_LSCLUSTER_BIN): parameters auto-detection disabled" fi # If auto-detection failed, use default values [[ -z "$PG_USER" ]] && PG_USER="$DEFAULT_PG_USER" [[ -z "$PG_VERSION" ]] && PG_VERSION="$DEFAULT_PG_VERSION" [[ -z "$PG_MAIN" ]] && PG_MAIN="$DEFAULT_PG_MAIN" [[ -z "$PG_DEFAULT_PORT" ]] && PG_DEFAULT_PORT="$DEFAULT_PG_PORT" # Check PG_USER [[ -z "$PG_USER" ]] && echo "UNKNOWN: Postgres user not specified" && exit 3 id "$PG_USER" > /dev/null 2>&1 || { echo "UNKNOWN: Invalid Postgres user ($PG_USER)"; exit 3; } # Check PSQL_BIN [[ ! -x "$PSQL_BIN" ]] && echo "UNKNOWN: Invalid psql bin path ($PSQL_BIN)" && exit 3 # Check PG_MAIN [[ ! -d "$PG_MAIN/" ]] && echo "UNKNOWN: Invalid Postgres main directory path ($PG_MAIN)" && exit 3 # Check RECOVERY_CONF if [[ -z "$RECOVERY_CONF" ]]; then if [[ $PG_VERSION -le 11 ]]; then RECOVERY_CONF_FILENAME="recovery.conf" else RECOVERY_CONF_FILENAME="postgresql.auto.conf" fi RECOVERY_CONF="$PG_MAIN/$RECOVERY_CONF_FILENAME" else RECOVERY_CONF_FILENAME=$( basename "$RECOVERY_CONF" ) fi # Check PG_DEFAULT_PORT [[ $( grep -c -E '^[0-9]*$' <<< "$PG_DEFAULT_PORT" ) -ne 1 ]] && \ echo "UNKNOWN: Postgres default master TCP port must be an integer." && exit 3 # If PG_DB is not provided with -D parameter, use PG_USER as default value [[ -z "$PG_DB" ]] && PG_DB="$PG_USER" function psql_get () { local sql="$*" debug "Exec 'sudo -u $PG_USER $PSQL_BIN -d \"$PG_DB\" -w -t -P format=unaligned <<< \"$sql\"" sudo -u "$PG_USER" "$PSQL_BIN" -d "$PG_DB" -w -t -P format=unaligned <<< "$sql" } function psql_master_get () { local sql="$*" debug "Exec 'sudo -u $PG_USER $PSQL_BIN -U $M_USER -h $M_HOST -w -p $M_PORT -d $PG_DB -t" \ "-P format=unaligned <<< \"$sql\"" sudo -u "$PG_USER" "$PSQL_BIN" \ -U "$M_USER" -h "$M_HOST" -w -p "$M_PORT" -d "$PG_DB" -t -P format=unaligned <<< "$sql" } debug "Running options: PG_VERSION = $PG_VERSION PG_DB = $PG_DB PG_USER = $PG_USER PSQL_BIN = $PSQL_BIN PG_LSCLUSTER_BIN = $PG_LSCLUSTER_BIN PG_MAIN = $PG_MAIN RECOVERY_CONF = $RECOVERY_CONF PG_DEFAULT_PORT = $PG_DEFAULT_PORT PG_DEFAULT_APP_NAME = $PG_DEFAULT_APP_NAME REPLAY_WARNING_DELAY = $REPLAY_WARNING_DELAY REPLAY_CRITICAL_DELAY = $REPLAY_CRITICAL_DELAY " # Set some stuff to PostgreSQL version if [[ $( bc -l <<< "$PG_VERSION < 10" ) -eq 1 ]]; then pg_last_wal_receive_lsn='pg_last_xlog_receive_location()' pg_last_wal_replay_lsn='pg_last_xlog_replay_location()' pg_current_wal_lsn='pg_current_xlog_location()' pg_wal_lsn_diff='pg_xlog_location_diff' sent_lsn='sent_location' write_lsn='write_location' else pg_last_wal_receive_lsn='pg_last_wal_receive_lsn()' pg_last_wal_replay_lsn='pg_last_wal_replay_lsn()' pg_current_wal_lsn='pg_current_wal_lsn()' pg_wal_lsn_diff='pg_wal_lsn_diff' sent_lsn='sent_lsn' write_lsn='write_lsn' fi # Postgres is running ? if [[ $DEBUG -eq 0 ]]; then psql_get '\q' 2> /dev/null is_running=$? else psql_get '\q' is_running=$? fi if [[ $is_running -ne 0 ]]; then echo "CRITICAL: Postgres is not running !" exit 2 fi debug "Postgres is running" RECOVERY_MODE=0 [[ "$( psql_get 'SELECT pg_is_in_recovery();' )" == "t" ]] && RECOVERY_MODE=1 if [[ "$EXPECTED_MODE" == "auto" ]]; then debug "Auto-detect mode" if [[ $RECOVERY_MODE -eq 1 ]]; then debug "Postgres is in recovery mode. Hot-standby mode." EXPECTED_MODE="hot-standby" elif [[ -f $RECOVERY_CONF ]] && \ [[ $( grep -cE '^\s*primary_conninfo' "$RECOVERY_CONF" ) -gt 0 ]]; then debug "File $RECOVERY_CONF_FILENAME found and contain primary_conninfo. Hot-standby mode." EXPECTED_MODE="hot-standby" else debug "Postgres not in recovery mode and file $RECOVERY_CONF_FILENAME not found " \ "(or does not contain primary_conninfo). Master mode." EXPECTED_MODE="master" fi fi if [[ "$EXPECTED_MODE" == "hot-standby" ]]; then # Check recovery mode if [[ $RECOVERY_MODE -ne 1 ]]; then echo "CRITICAL: Not in recovery mode while $RECOVERY_CONF_FILENAME file found !" exit 2 fi debug "Postgres is in recovery mode" # Get local current last received/replayed LSN LAST_RECEIVED_LSN=$( psql_get "SELECT $pg_last_wal_receive_lsn" ) debug "Last received LSN: $LAST_RECEIVED_LSN" LAST_REPLAYED_LSN=$( psql_get "SELECT $pg_last_wal_replay_lsn" ) debug "Last replayed LSN: $LAST_REPLAYED_LSN" # Get master connection information from primary_conninfo configuration parameter MASTER_CONN_INFOS=$( psql_get "SHOW primary_conninfo" ) if [[ -z "$MASTER_CONN_INFOS" ]]; then echo "UNKNOWN: Can't retrieve master connection information from primary_conninfo" \ "configuration parameter" exit 3 fi debug "Master connection information: $MASTER_CONN_INFOS" M_HOST=$( grep 'host=' <<< "$MASTER_CONN_INFOS" | sed 's/^.*host= *\([0-9a-zA-Z.-]\+\) *.*$/\1/' ) if [[ -z "$M_HOST" ]]; then echo "UNKNOWN: Can't retrieve master host from primary_conninfo configuration parameter" exit 3 fi debug "Master host: $M_HOST" M_PORT=$( grep 'port=' <<< "$MASTER_CONN_INFOS" | sed 's/^.*port= *\([0-9a-zA-Z.-]\+\) *.*$/\1/' ) if [[ -z "$M_PORT" ]]; then debug "Master port not specified, use default: $PG_DEFAULT_PORT" M_PORT=$PG_DEFAULT_PORT else debug "Master port: $M_PORT" fi if [[ -n "$PG_MASTER_USER" ]]; then debug "Master user provided by command-line, use it: $PG_MASTER_USER" M_USER="$PG_MASTER_USER" else M_USER=$( grep 'user=' <<< "$MASTER_CONN_INFOS" | sed 's/^.*user= *\([0-9a-zA-Z.-]\+\) *.*$/\1/' ) if [[ -z "$M_USER" ]]; then debug "Master user not specified, use default: $PG_USER" M_USER=$PG_USER else debug "Master user: $M_USER" fi fi M_APP_NAME=$( grep 'application_name=' <<< "$MASTER_CONN_INFOS" | sed "s/^.*application_name=[ \'\"]*\([^ \'\"]\+\)[ \'\"]*.*$/\1/" ) if [[ -z "$M_APP_NAME" ]]; then if [[ $PG_VERSION -ge 12 ]]; then debug "Master application name not specified, use cluster_name if defined" CLUSTER_NAME=$( psql_get "SELECT current_setting('cluster_name')" ) debug "Cluster name: $CLUSTER_NAME" if [[ -n "$CLUSTER_NAME" ]]; then M_APP_NAME=$CLUSTER_NAME else debug "Cluster name not defined, use default: $PG_DEFAULT_APP_NAME" M_APP_NAME=$PG_DEFAULT_APP_NAME fi else debug "Master application name not specified, use default: $PG_DEFAULT_APP_NAME" M_APP_NAME=$PG_DEFAULT_APP_NAME fi else debug "Master application name: $M_APP_NAME" fi # Check if master is configured for synchronous commit SYNC_MODE="$( psql_master_get "SELECT setting from pg_settings WHERE name = 'synchronous_commit';" )" debug "Master synchronous_commit=$SYNC_MODE" if [[ "$SYNC_MODE" == "on" ]] || [[ "$SYNC_MODE" == "remote_apply" ]]; then debug "Master is configured for synchronous commit" SYNCHRONOUS_COMMIT=1 else debug "Master is not configured for synchronous commit" SYNCHRONOUS_COMMIT=0 fi # Get current replication state information from master M_CUR_REPL_STATE_INFO="$( psql_master_get \ "SELECT state, sync_state, $sent_lsn AS sent_lsn, $write_lsn AS write_lsn" \ "FROM pg_stat_replication WHERE application_name='$M_APP_NAME';" )" if [[ -z "$M_CUR_REPL_STATE_INFO" ]]; then echo "UNKNOWN: Can't retrieve current replication state information from master server" exit 3 fi debug "Master current replication state:\n" \ "\tstate|sync_state|sent_lsn|write_lsn\n\t$M_CUR_REPL_STATE_INFO" M_CUR_STATE=$( cut -d'|' -f1 <<< "$M_CUR_REPL_STATE_INFO" ) debug "Master current state: $M_CUR_STATE" if [[ "$M_CUR_STATE" != "streaming" ]]; then echo "CRITICAL: this host is not in streaming state according to master host" \ "(current state = '$M_CUR_STATE')" exit 2 fi M_CUR_SYNC_STATE=$( cut -d'|' -f2 <<< "$M_CUR_REPL_STATE_INFO" ) debug "Master current sync state: $M_CUR_SYNC_STATE" if [[ "$M_CUR_SYNC_STATE" != "$EXPECTED_SYNC_STATE" ]]; then echo "CRITICAL: unexpected replication state '$M_CUR_SYNC_STATE'" \ "(expected state = '$EXPECTED_SYNC_STATE')" exit 2 fi M_CUR_SENT_LSN=$( cut -d'|' -f3 <<< "$M_CUR_REPL_STATE_INFO" ) M_CUR_WRITED_LSN=$( cut -d'|' -f4 <<< "$M_CUR_REPL_STATE_INFO" ) debug "Master current last sent/writed LSN: '$M_CUR_SENT_LSN' / '$M_CUR_WRITED_LSN'" # The last received LSN is the last replayed ? if [[ "$LAST_RECEIVED_LSN" != "$LAST_REPLAYED_LSN" ]]; then debug "/!\ The last received LSN is NOT the last replayed LSN" \ "('$M_CUR_LSN' / '$LAST_REPLAYED_LSN')" REPLAY_DELAY="$( psql_get 'SELECT EXTRACT(EPOCH FROM now() - pg_last_xact_replay_timestamp());' )" debug "Replay delay is $REPLAY_DELAY second(s)" if [[ $( bc -l <<< "$REPLAY_DELAY >= $REPLAY_CRITICAL_DELAY" ) -gt 0 ]]; then echo "CRITICAL: last received LSN is not the last replayed" \ "('$LAST_RECEIVED_LSN' / '$LAST_REPLAYED_LSN') and" \ "replay delay is $REPLAY_DELAY second(s)" exit 2 fi if [[ $( bc -l <<< "$REPLAY_DELAY >= $REPLAY_WARNING_DELAY" ) -gt 0 ]]; then echo "WARNING: last received LSN is not the last replay file" \ "('$LAST_RECEIVED_LSN' / '$LAST_REPLAYED_LSN') and" \ "replay delay is $REPLAY_DELAY second(s)" exit 1 fi debug "Replay delay is not worrying" fi debug "Last received LSN is the last replayed file" # The master last sent LSN is the last received (and synced) ? if [[ $SYNCHRONOUS_COMMIT -eq 1 ]] && [[ "$M_CUR_SENT_LSN" != "$LAST_RECEIVED_LSN" ]]; then LSN_DIFF=$( psql_master_get "SELECT $pg_wal_lsn_diff('$M_CUR_SENT_LSN', '$LAST_RECEIVED_LSN');" ) debug "LSN diff ('$M_CUR_SENT_LSN' vs '$LAST_RECEIVED_LSN'): $LSN_DIFF bytes" echo "WARNING: master last sent LSN is not already received (and synced to disk) by slave" \ "(diff: $LSN_DIFF bytes). May be we have some network delay or load on slave" echo "Master last sent LSN: $M_CUR_SENT_LSN" echo "Slave last received (and synced to disk) LSN: $LAST_RECEIVED_LSN" echo "Diff: $LSN_DIFF bytes" exit 1 elif [[ $SYNCHRONOUS_COMMIT -eq 0 ]] && [ "$M_CUR_SENT_LSN" != "$M_CUR_WRITED_LSN" ];then LSN_DIFF=$( psql_master_get "SELECT pg_wal_lsn_diff('$M_CUR_SENT_LSN', '$M_CUR_WRITED_LSN');" ) debug "LSN diff ('$M_CUR_SENT_LSN' vs '$M_CUR_WRITED_LSN'): $LSN_DIFF bytes" echo "WARNING: master last sent LSN is not already received by slave " \ "(diff: $LSN_DIFF bytes). May be we have some network delay or load on slave" echo "Master last sent LSN: $M_CUR_SENT_LSN" echo "Slave last received LSN: $M_CUR_WRITED_LSN" echo "Diff: $LSN_DIFF bytes" exit 1 fi echo "OK: Hot-standby server is up-to-date" echo "Replication state: $M_CUR_SYNC_STATE" echo "Last sent/writed LSN: '$M_CUR_SENT_LSN' / '$M_CUR_WRITED_LSN'" [[ "$LAST_RECEIVED_LSN" != "$LAST_REPLAYED_LSN" ]] && echo "Replay delay: ${REPLAY_DELAY}s" exit 0 elif [[ "$EXPECTED_MODE" == "master" ]]; then # Check recovery mode if [[ $RECOVERY_MODE -eq 1 ]]; then echo "CRITICAL: In recovery mode while expected mode is master!" exit 2 fi debug "Postgres is not in recovery mode" # Retrieve current lsn CURRENT_LSN=$( psql_get "SELECT $pg_current_wal_lsn" ) if [[ -z "$CURRENT_LSN" ]]; then echo "UNKNOWN: Fail to retrieve current LSN (Log Sequence Number)" exit 3 fi debug "Current LSN: $CURRENT_LSN" # Check if master is configured for synchronous commit SYNC_MODE="$( psql_get "SELECT setting from pg_settings WHERE name = 'synchronous_commit';" )" debug "synchronous_commit=$SYNC_MODE" if [[ "$SYNC_MODE" == "on" ]] || [[ "$SYNC_MODE" == "remote_apply" ]]; then debug "Master is configured for synchronous commit" SYNCHRONOUS_COMMIT=1 else debug "Master is not configured for synchronous commit" SYNCHRONOUS_COMMIT=0 fi # Check standby client STANDBY_CLIENTS=$( psql_get \ "SELECT application_name, client_addr, sent_lsn, write_lsn, state, sync_state, current_lag FROM ( SELECT application_name, client_addr, sent_lsn, write_lsn, state, sync_state, current_lag FROM ( SELECT application_name, client_addr, $sent_lsn AS sent_lsn, $write_lsn AS write_lsn, state, sync_state, $pg_wal_lsn_diff($pg_current_wal_lsn, $write_lsn) AS current_lag FROM pg_stat_replication ) AS s2 ) AS s1" ) if [[ -z "$STANDBY_CLIENTS" ]]; then echo "WARNING: no stand-by client connected" exit 1 fi debug "Stand-by client(s):\n\t${STANDBY_CLIENTS//$'\n'/\\n\\t}" STANDBY_CLIENTS_ROWS=() CURRENT_LSN_IS_LAST_SENT=1 for line in $STANDBY_CLIENTS; do NAME=$( cut -d '|' -f 1 <<< "$line" ) IP=$( cut -d '|' -f 2 <<< "$line" ) SENT_LSN=$( cut -d '|' -f 3 <<< "$line" ) WRITED_LSN=$( cut -d '|' -f 4 <<< "$line" ) STATE=$( cut -d '|' -f 5 <<< "$line" ) SYNC_STATE=$( cut -d '|' -f 6 <<< "$line" ) LAG=$( cut -d '|' -f 7 <<< "$line" ) STANDBY_CLIENTS_ROW="$NAME ($IP): $STATE/$SYNC_STATE" STANDBY_CLIENTS_ROW+=" (LSN: sent='$SENT_LSN' / writed='$WRITED_LSN', Lag: ${LAG}b" STANDBY_CLIENTS_ROWS+=( "$STANDBY_CLIENTS_ROW" ) if [[ $SYNCHRONOUS_COMMIT -eq 1 ]] && [[ "$SENT_LSN" != "$CURRENT_LSN" ]]; then CURRENT_LSN_IS_LAST_SENT=0 elif [[ $SYNCHRONOUS_COMMIT -eq 0 ]] && [[ "$SENT_LSN" != "$WRITED_LSN" ]]; then CURRENT_LSN_IS_LAST_SENT=0 fi done if [[ $CURRENT_LSN_IS_LAST_SENT -eq 1 ]]; then echo "OK: ${#STANDBY_CLIENTS_ROWS[@]} stand-by client(s) connected" EXIT_CODE=0 else echo "WARNING: current master LSN is not the last sent to stand-by client(s) connected." \ "May be we have some load ?" EXIT_CODE=1 fi echo "Current master LSN: $CURRENT_LSN" IFS=$'\n' echo "${STANDBY_CLIENTS_ROWS[*]}" exit $EXIT_CODE else echo "UNKNOWN - Invalid mode '$EXPECTED_MODE'" exit 3 fi