#!/bin/bash ################################################################### #Script Name : notify_fifo_state.sh #Description : notify script for keepalived First-In-First-Out #Author : Yncensus #Email : yncensus@outlook.com #Version : 1.3 (2023-10-19) #Attributions : https://github.com/acassen/keepalived/blob/master/doc/samples/sample_notify_fifo.sh ################################################################### # To use this script, copy it to directory /etc/keepalived/scripts # and add one or more of the following to the keepalived configuration # file in the global_defs section: # notify_fifo /etc/keepalived/scripts/notify_fifo # vrrp_notify_fifo /etc/keepalived/scripts/vrrp_notify_fifo # lvs_notify_fifo /etc/keepalived/scripts/lvs_notify_fifo # This script will then need to be executed, passing it the name of the # fifo. # # As an alternative to executing this script manually, add one or more of # the following in the global_defs section of the config: # notify_fifo_script /etc/keepalived/scripts/sample_notify_fifo.sh # vrrp_notify_fifo_script /etc/keepalived/scripts/sample_notify_fifo.sh # lvs_notify_fifo_script /etc/keepalived/scripts/sample_notify_fifo.sh # If run this way, Keepalived will terminate the script with SIGTERM when # it exits. DEBUG=false CREATED_FIFO=0 SHUTDOWN=0 TIMEOUT=10 LOGDIR="/var/log/keepalived" TIMESTAMP="date --iso-8601=seconds" STATEFILE="/run/keepalived/keepalived.state" PID_DIR="/run/keepalived" FIFO=$1 [[ -z $FIFO ]] && echo "$($TIMESTAMP): A FIFO name must be specified" >>$LOGDIR/debug.log && exit 1 LOG_FILE=$LOGDIR/${FIFO##*/}.log $DEBUG && echo "$($TIMESTAMP): [$PPID:$$] STARTING" >>$LOG_FILE touch $STATEFILE chmod g+r $STATEFILE PID_FILE=$PID_DIR/${FIFO##*/}.pid exiting() { # When this script exists, this function is always executed because # it is associated to the bash EXIT signal. echo "${FIFO##*/} not running" > $STATEFILE $DEBUG && echo "$($TIMESTAMP): [$PPID:$$] EXIT" >>$LOG_FILE [[ $CREATED_FIFO -eq 1 ]] && rm -f $FIFO flock -u $FD } reload_terminate() { exit 0 } stopping() { $DEBUG && echo "$($TIMESTAMP): [$PPID:$$] STOPPING" >>$LOG_FILE exit 0 } start_shutdown() { $DEBUG && echo "$($TIMESTAMP): [$PPID:$$] START_SHUTDOWN" >>$LOG_FILE SHUTDOWN=1 # When keepalived terminates, it sends a TERM signal to this script before # sending the fifo notifies. We catch the SIGTERM here, and after a short # delay send a SIGALRM to the main script process ( sleep 0.5 kill -ALRM $$ 2>/dev/null ) & } # handling SIGNALS trap stopping HUP INT QUIT USR1 USR2 PIPE ALRM trap reload_terminate QUIT trap start_shutdown TERM trap exiting EXIT # PID_FILE handling exec {FD}>>"$PID_FILE" if ! flock -e -n $FD; then # Send SIGQUIT signal to the previous instance of this script. # The previous script waits for its current executed command to end # before actually calling reload_terminate(). The TIMEOUT value must # take this constraint into account. OLD_PID=$(cat $PID_FILE) if ls -l /proc/$OLD_PID/fd | grep -qw "$(readlink -f "$PID_FILE")" then kill -QUIT $OLD_PID fi flock -e --timeout $TIMEOUT $FD || echo "$($TIMESTAMP): [$PPID:$$] Unable to create pid-file $PID_FILE" >>$LOG_FILE; exit 1 fi echo $$ >"$PID_FILE" # FIFO [file] handling if [[ ! -p $FIFO ]]; then mkfifo $FIFO if [[ $? -eq 0 ]]; then CREATED_FIFO=1 else echo "$($TIMESTAMP): [$PPID:$$] Unable to create fifo $FIFO" >>$LOG_FILE exit 1 fi fi # If keepalived terminates, the FIFO will be closed, so # read the FIFO in a loop. It keepalived hasn't opened the # FIFO, the script will be blocked until it has been opened. # When keepalived reloads, it sends the script a SIGTERM, and # then closes the FIFO. Since keepalived removes the FIFO, # creates a new one and runs the (possibly changed) FIFO script # again, we need to terminate if the FIFO is closed and we have # received a SIGTERM. # When keepalived stops it sends SIGTERM to the script and # afterwards send STOPPING messages, so we need to continue # reading the FIFO until it is closed. FIFO_INODE=$(stat -c "%i" $FIFO) [[ $? -ne 0 ]] && echo "$($TIMESTAMP): [$PPID:$$] FIFO $FIFO not accessible" >>$LOG_FILE && exit 1 # FIFO Logic while [[ $SHUTDOWN -eq 0 ]] do [[ ! -p $FIFO ]] && echo "$($TIMESTAMP): [$PPID:$$] FIFO $FIFO missing" >>$LOG_FILE && exit 1 exec <$FIFO [[ $? -ne 0 || $(stat -c "%i" $FIFO 2>/dev/null) -ne $FIFO_INODE ]] && break while read line; do set $line TYPE=$1 if [[ $TYPE = INSTANCE || $TYPE = GROUP ]]; then VRRP_INST=${2//\"/} STATE=$3 PRIORITY=$4 # Now take whatever action is required echo "$VRRP_INST: $STATE" > $STATEFILE $DEBUG && echo "$($TIMESTAMP): [$PPID:$$]" $TYPE $VRRP_INST $STATE $PRIORITY >>$LOG_FILE elif [[ $TYPE = VS ]]; then VS=$2 STATE=$3 # Now take whatever action is required $DEBUG && echo "$($TIMESTAMP): [$PPID:$$]" $TYPE $VS $STATE >>$LOG_FILE elif [[ $TYPE = RS ]]; then RS=$2 VS=$3 STATE=$4 # Now take whatever action is required $DEBUG && echo "$($TIMESTAMP): [$PPID:$$]" $TYPE $RS $VS $STATE >>$LOG_FILE else $DEBUG && echo "$($TIMESTAMP): [$PPID:$$]" $TYPE - unknown "($*)" >>$LOG_FILE fi done [[ $SHUTDOWN -eq 0 ]] && $DEBUG && echo "$($TIMESTAMP): [$PPID:$$] FIFO CLOSED" >>$LOG_FILE done stopping