#!/bin/sh
# Copyright (c) 2013 Pavel I Volkov
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# file location: /usr/local/libexec/carpcontrol.sh
# version 1.5
# file example: /usr/local/etc/devd/carp.conf
# notify 0 {
# match "system" "IFNET";
# match "subsystem" "carp*";
# match "type" "LINK_UP";
# action "/usr/local/libexec/carpcontrol.sh $type $subsystem";
# };
#
# notify 0 {
# match "system" "IFNET";
# match "subsystem" "carp*";
# match "type" "LINK_DOWN";
# action "/usr/local/libexec/carpcontrol.sh $type $subsystem";
# };
# file example: /etc/fstab
# ...
# /dev/hast/volume /hast/volume ufs rw,noauto 0 0
# ...
# file example: /etc/rc.conf.local
# ...
# hastd_enable="YES"
# carpcontrol_services="samba apache22"
# if [ -d "/dev/hast" ]; then # master mode
# samba_enable="YES"
# apache22_enable="YES"
# else # backup mode
# samba_enable="NO"
# apache22_enable="NO"
# fi
# ...
MY=`basename $0 .sh`
PID=$$
EVENT=$1 # Event type
IF="$2" # The network interface
PIDf="/var/run/${MY}.pid" # PID file for background process
. /etc/rc.subr
load_rc_config ${MY}
carp_type() { ifconfig ${IF} | sed -E '1,3d;s/^.*(INIT|MASTER|BACKUP).*$/\1/'; }
get_fstab() { awk -v p1=$1 '/\/dev\/hast\//{print $p1}' /etc/fstab; }
hast_role() { local _ret
hastctl role "${1}" all; _ret=$?
[ $_ret -ne 0 ] \
&& logger -p daemon.err -t "${MY}[${PID}]" "hastd unable to switch role to ${1} (${_ret})" \
|| logger -p daemon.notice -t "${MY}[${PID}]" "hastd switched to ${1} (${_ret})"
return $_ret
}
serv_ctl() { local _i _st
for _i in ${carpcontrol_services}; do
logger -p daemon.notice -t "${MY}[${PID}]" "attempt to change the status of a service ${_i} to ${1}"
service $_i onestatus > /dev/null 2>&1; _st=$?
case ${1} in
*start) [ $_st -ne 0 ] && service $_i ${1} ;;
*stop) [ $_st -eq 0 ] && service $_i ${1} ;;
esac
done
}
hast_init() { local _i
serv_ctl stop # services stop
# umount all hast volumes
sync
[ -d /dev/hast ] && for _i in `ls -1 /dev/hast/*`; do umount -f "${_i}"; done
[ -e /var/run/hastctl ] && hast_role init # HAST(init)
}
wait_hast_bg() { local _i _j _k PID
sleep 0.25; [ -e "${PIDf}" ] && PID=`cat ${PIDf}` || PID=$$
logger -p daemon.notice -t "${MY}[${PID}]" "bg start from [$$] process"
until hastctl status all > /dev/null 2>&1 # wait hast daemon
do
sleep 0.25
logger -p daemon.notice -t "${MY}[${PID}]" "wait hastd"
done
for _i in `jot 12`; do # wait until 3 seconds
case `carp_type` in
BACKUP) break ;;
*) sleep 0.25 ;;
esac
done
for _i in `jot 12`; do # wait until 3 second
case `carp_type` in
BACKUP) # backup mode
hast_init
# HAST(secondary)
hast_role secondary
break
;;
MASTER) # master mode
hast_init
# HAST(primary)
hast_role primary
[ $? -ne 0 ] && break
# mount all hast volumes from /etc/fstab
for _j in `get_fstab 2`; do [ -d "$_j" ] || mkdir -p "$_j"; done
for _j in `get_fstab 1`; do
for _k in `jot 40`; do sleep 0.25; [ -e $_j ] && break; done # wait until 10 second
fsck -p -y -t ufs $_j
logger -p daemon.notice -t "${MY}[${PID}]" "trying mount ${_j}"
mount $_j
[ $? -ne 0 ] && break 2
logger -p daemon.notice -t "${MY}[${PID}]" "${_j} mounted"
done
serv_ctl start # services start
break
;;
*) # other mode
sleep 0.25
;;
esac
done
rm -f "${PIDf}" # remove PID of the background process
logger -p daemon.notice -t "${MY}[${PID}]" "bg stop from $$ process"
}
case "${EVENT}" in
"LINK_UP"|"LINK_DOWN") # Carrier status changed to UP or DOWN
logger -p daemon.notice -t "${MY}[${PID}]" "${IF} ${EVENT}"
if [ -e "${PIDf}" ]; then # PID exist
if [ pgrep -qF "${PIDf}" ]; then # process don't exist
rm -f "${PIDf}"
else # process exist
logger -p daemon.err -t "${MY}[${PID}]" "background process already running"
exit 1
fi
fi
# because it is necessary to wait hast daemon at startup
wait_hast_bg > /dev/null 2>&1 &
echo $! > "${PIDf}" # create PID for background process
;;
*) logger -p daemon.err -t "${MY}[${PID}]" "unknown event ${EVENT} for interface ${IF}" ;;
esac
exit 0