#!/bin/zsh
# vim: ft=zsh noet ts=4 sts=4 sw=4
zmodload zsh/stat
: ${SVDIR:=/run/service}
zparseopts -D \
h=H -help=H \
-all-up=ALL_UP \
-all-down=ALL_DOWN \
-new-down=NEW_DOWN \
-lock+:=LOCK \
f+:=F -filter+:=F
if [[ -n $H ]]; then cat <<END
usage: ${0:t} [options] [logfile]
options:
-h --help this help
-f --filter PATTERN consider only services of given pattern
--lock TIMEOUT perform locking on ZSVDIR
--all-up turn all (filtered) services on
--all-down turn all (filtered) services off
--new-down turn all (filtered) services that haven't been
initialized yet off
END
exit 0
fi
if (( $#ALL_UP + $#ALL_DOWN + $#NEW_DOWN > 1 )); then
printf '%s\n' "Only one of following options is allowed:" \
" --all-up --all-down --new-down"
exit 2
fi
if [[ -n $1 ]]; then
exec >>$1 2>&1
shift
zmodload zsh/datetime zsh/system
print -r - "$(strftime '%Y-%m-%d %H:%M:%S' $EPOCHSECONDS) started: ${(qqq)0}" "${(qqq)@} (pid: $$)"
PS4='+%B${SECONDS} ${sysparams[pid]} %N:%i>%b '
set -x
fi
service_patterns=()
for pat in $F; do
case $pat in
(-f);;
(--filter);;
(*) service_patterns+=( $pat );;
esac
done
typeset -A remove
autoload -Uz zsv_config
zsv_config
zsv_parse
if (($#LOCK)); then
LOCK_TIMEOUT=$LOCK[-1]
if ! [[ $LOCK_TIMEOUT =~ '^[0-9]+$' ]]; then
printf 'Incorrect lock timeout: "%s"\n' $LOCK_TIMEOUT
exit 2
fi
exec 3> ${ZSVDIR}.lock
if ! flock -w $LOCK_TIMEOUT 3; then
exit 3
fi
fi
for d in $ZSVDIR/*(N/); do
[[ $d == $ZSVDIR/_* ]] && continue
if (($#service_patterns)); then
for pat in $service_patterns; do
if [[ $d:t == $~pat ]]; then
remove[$d:t]=$d
break
fi
done
else
remove[$d:t]=$d
fi
done
sv_mkdir=${SVDIR%%/}
typeset -gA svdir_stat
zstat -L -H svdir_stat $sv_mkdir && \
while (( $svdir_stat[mode] >> 12 == 10 )); do # symlink
sv_mkdir=$svdir_stat[link]
zstat -L -H svdir_stat $sv_mkdir || break
done
mkdir -p $sv_mkdir || exit $?
for name in $svtab; do
unset "remove[$name]"
if (($#service_patterns)); then
pat_matches=0
for pat in $service_patterns; do
if [[ $name == $~pat ]]; then
pat_matches=1
break
fi
done
(($pat_matches)) || continue
unset pat pat_matches
fi
zsv_eval $ZSVDIR/$name
zsv_dir=$zsv_svdir/$name
if (($#NEW_DOWN)); then
if ! [[ -d $ZSVDIR/_ovr/$name ]]; then
mkdir -p $zsv_dir $ZSVDIR/_ovr/$name
touch $ZSVDIR/_ovr/$name/down
fi
else
mkdir -p $zsv_dir $ZSVDIR/_ovr/$name
if (($#ALL_UP)); then
[[ -e $ZSVDIR/_ovr/$name/down ]] && rm $ZSVDIR/_ovr/$name/down
elif (($#ALL_DOWN)); then
touch $ZSVDIR/_ovr/$name/down
fi
fi
# flags
if (( ${flags[M]} )); then # Manual
touch $zsv_dir/down
else
# check if the service should be up
if [[ ! -e $ZSVDIR/_ovr/$name/down ]] && \
$handler[cond] && ! [[ -f ${SVDIR}/down ]] ; then
rm -f $zsv_dir/down
sv up $zsv_dir
else
touch $zsv_dir/down
sv down $zsv_dir
fi
fi
# run
for f in run check finish control/{u,d,o,p,c,h,a,i,q,1,2,t,k,x}; do
if zsv_has_handler $f:t; then
[[ -L $zsv_dir/$f ]] && rm $zsv_dir/$f
mkdir -p $zsv_dir/$f:h
ln -s ${commands[zsv.exec]} $zsv_dir/$f
else
rm -f $zsv_dir/$f
fi
done
# log
if [[ $handler[log] != '-' ]]; then
mkdir -p $zsv_dir/log
[[ -L $zsv_dir/log/run ]] && rm $zsv_dir/log/run
ln -s ${commands[zsv.exec]} $zsv_dir/log/run
elif [[ -e $zsv_dir/log ]]; then
rm -rf $zsv_dir/log
fi
# make symlink
[[ -L ${SVDIR}/$name ]] && rm ${SVDIR}/$name
ln -s ../zsv/$name ${SVDIR}
done
# for name in $svtab; do
# [[ -L ${SVDIR}/$name ]] && rm ${SVDIR}/$name
# ln -s ../zsv/$name ${SVDIR}
# done
if (($#remove)); then
rm -f ${SVDIR}/${^remove:t}
sv_exit=( $^remove/supervise/control(N) )
(($#sv_exit)) && sv exit $sv_exit:h:h
rm -rf $remove
fi
broken=( ${SVDIR}/*(-@N) )
(($#broken)) && rm -f $broken
# make runsvdir re-read the directories
killall --signal CONT --user ${USER:-root} runsvdir &>/dev/null
touch ${SVDIR}/.zsv