diff options
Diffstat (limited to 'tools/frrcommon.sh.in')
-rwxr-xr-x | tools/frrcommon.sh.in | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/tools/frrcommon.sh.in b/tools/frrcommon.sh.in new file mode 100755 index 0000000..00b63a7 --- /dev/null +++ b/tools/frrcommon.sh.in @@ -0,0 +1,385 @@ +#!/bin/bash +# +# This is a "library" of sorts for use by the other FRR shell scripts. It +# has most of the daemon start/stop logic, but expects the following shell +# functions/commands to be provided by the "calling" script: +# +# log_success_msg +# log_warning_msg +# log_failure_msg +# +# (coincidentally, these are LSB standard functions.) +# +# Sourcing this file in a shell script will load FRR config variables but +# not perform any action. Note there is an "exit 1" if the main config +# file does not exist. +# +# This script should be installed in @CFG_SBIN@/frrcommon.sh + +# FRR_PATHSPACE is passed in from watchfrr +suffix="${FRR_PATHSPACE:+/${FRR_PATHSPACE}}" +nsopt="${FRR_PATHSPACE:+-N ${FRR_PATHSPACE}}" + +PATH=/bin:/usr/bin:/sbin:/usr/sbin +D_PATH="@CFG_SBIN@" # /usr/lib/frr +C_PATH="@CFG_SYSCONF@${suffix}" # /etc/frr +V_PATH="@CFG_STATE@${suffix}" # /var/run/frr +B_PATH="@CFG_BIN@" +VTYSH="@vtysh_bin@" # /usr/bin/vtysh +FRR_USER="@enable_user@" # frr +FRR_GROUP="@enable_group@" # frr +FRR_VTY_GROUP="@enable_vty_group@" # frrvty +FRR_CONFIG_MODE="@enable_configfile_mask@" # 0600 +FRR_DEFAULT_PROFILE="@DFLT_NAME@" # traditional / datacenter + +# ORDER MATTERS FOR $DAEMONS! +# - keep zebra first +# - watchfrr does NOT belong in this list + +DAEMONS="zebra mgmtd bgpd ripd ripngd ospfd ospf6d isisd babeld pimd pim6d ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd pathd" +RELOAD_SCRIPT="$D_PATH/frr-reload.py" + +# +# general helpers +# + +is_user_root () { + if [[ ! -z $FRR_NO_ROOT && "${FRR_NO_ROOT}" == "yes" ]]; then + return 0 + fi + + [ "${EUID:-$(id -u)}" -eq 0 ] || { + log_failure_msg "Only users having EUID=0 can start/stop daemons" + return 1 + } +} + +debug() { + [ -n "$watchfrr_debug" ] || return 0 + + printf '%s %s(%s):' "$(date +%Y-%m-%dT%H:%M:%S.%N)" "$0" $$ >&2 + # this is to show how arguments are split regarding whitespace & co. + # (e.g. for use with `debug "message" "$@"`) + while [ $# -gt 0 ]; do + printf ' "%s"' "$1" >&2 + shift + done + printf '\n' >&2 +} + +vtysh_b () { + [ "$1" = "watchfrr" ] && return 0 + if [ ! -r "$C_PATH/frr.conf" ]; then + log_warning_msg "$C_PATH/frr.conf does not exist; skipping config apply" + return 0 + fi + + cmd="$VTYSH $nsopt -b" + [ -n "$1" ] && cmd="${cmd} -d $1" + + log_success_msg "Sending config with '$cmd'" + eval "$cmd" +} + +daemon_inst() { + # note this sets global variables ($dmninst, $daemon, $inst) + dmninst="$1" + daemon="${dmninst%-*}" + inst="" + [ "$daemon" != "$dmninst" ] && inst="${dmninst#*-}" +} + +daemon_list() { + # note $1 and $2 specify names for global variables to be set + local enabled disabled evar dvar + enabled="" + disabled="" + evar="$1" + dvar="$2" + + for daemon in $DAEMONS; do + eval cfg=\$$daemon + eval inst=\$${daemon}_instances + [ "$daemon" = zebra -o "$daemon" = staticd -o "$daemon" = mgmtd ] && cfg=yes + if [ -n "$cfg" -a "$cfg" != "no" -a "$cfg" != "0" ]; then + if ! daemon_prep "$daemon" "$inst"; then + continue + fi + debug "$daemon enabled" + + if [ -n "$inst" ]; then + debug "$daemon multi-instance $inst" + oldifs="${IFS}" + IFS="${IFS}," + for i in $inst; do + enabled="$enabled $daemon-$i" + done + IFS="${oldifs}" + else + enabled="$enabled $daemon" + fi + else + debug "$daemon disabled" + disabled="$disabled $daemon" + fi + done + + enabled="${enabled# }" + disabled="${disabled# }" + [ -z "$evar" ] && echo "$enabled" + [ -n "$evar" ] && eval $evar="\"$enabled\"" + [ -n "$dvar" ] && eval $dvar="\"$disabled\"" +} + +# +# individual daemon management +# + +daemon_prep() { + local daemon inst cfg + daemon="$1" + inst="$2" + [ "$daemon" = "watchfrr" ] && return 0 + [ -x "$D_PATH/$daemon" ] || { + log_failure_msg "cannot start $daemon${inst:+ (instance $inst)}: daemon binary not installed" + return 1 + } + [ -r "$C_PATH/frr.conf" ] && return 0 + + cfg="$C_PATH/$daemon${inst:+-$inst}.conf" + if [ ! -r "$cfg" ]; then + install -g "$FRR_GROUP" -o "$FRR_USER" -m "$FRR_CONFIG_MODE" /dev/null "$cfg" + fi + return 0 +} + +daemon_start() { + local dmninst daemon inst args instopt wrap bin + + is_user_root || exit 1 + + all=false + [ "$1" = "--all" ] && { all=true; shift; } + + daemon_inst "$1" + + [ "$MAX_FDS" != "" ] && ulimit -n "$MAX_FDS" > /dev/null 2> /dev/null + daemon_prep "$daemon" "$inst" || return 1 + if test ! -d "$V_PATH"; then + install -g "$FRR_GROUP" -o "$FRR_USER" -m "$FRR_CONFIG_MODE" -d "$V_PATH" + chmod gu+x "${V_PATH}" + fi + + eval wrap="\$${daemon}_wrap" + bin="$D_PATH/$daemon" + instopt="${inst:+-n $inst}" + eval args="\$${daemon}_options" + + cmd="$all_wrap $wrap $bin $nsopt -d $frr_global_options $instopt $args" + log_success_msg "Starting $daemon with command: '$cmd'" + if eval "$cmd"; then + log_success_msg "Started $dmninst" + if $all; then + debug "Skipping startup of vtysh until all have started" + else + vtysh_b "$daemon" + fi + else + log_failure_msg "Failed to start $dmninst!" + fi +} + +daemon_stop() { + local dmninst daemon inst pidfile vtyfile pid cnt fail + daemon_inst "$1" + + is_user_root || exit 1 + + all=false + [ "$2" = "--reallyall" ] && all=true + + pidfile="$V_PATH/$daemon${inst:+-$inst}.pid" + vtyfile="$V_PATH/$daemon${inst:+-$inst}.vty" + + [ -r "$pidfile" ] || fail="pid file not found" + $all && [ -n "$fail" ] && return 0 + [ -z "$fail" ] && pid="$(cat "$pidfile")" + [ -z "$fail" -a -z "$pid" ] && fail="pid file is empty" + [ -n "$fail" ] || kill -0 "$pid" 2>/dev/null || fail="pid $pid not running" + + if [ -n "$fail" ]; then + [ "$2" = "--quiet" ] || log_failure_msg "Cannot stop $dmninst: $fail" + return 1 + fi + + debug "kill -2 $pid" + kill -2 "$pid" + cnt=1200 + while kill -0 "$pid" 2>/dev/null; do + sleep .1 + [ $(( cnt -= 1 )) -gt 0 ] || break + done + if kill -0 "$pid" 2>/dev/null; then + [ "$2" = "--quiet" ] || log_failure_msg "Failed to stop $dmninst, pid $pid still running" + still_running=1 + return 1 + else + [ "$2" = "--quiet" ] || log_success_msg "Stopped $dmninst" + rm -f "$pidfile" + return 0 + fi +} + +daemon_status() { + local dmninst daemon inst pidfile pid fail + daemon_inst "$1" + + pidfile="$V_PATH/$daemon${inst:+-$inst}.pid" + + [ -r "$pidfile" ] || return 3 + pid="$(cat "$pidfile")" + [ -z "$pid" ] && return 1 + kill -0 "$pid" 2>/dev/null || return 1 + return 0 +} + +print_status() { + daemon_status "$1" + rv=$? + if [ "$rv" -eq 0 ]; then + log_success_msg "Status of $1: running" + else + log_failure_msg "Status of $1: FAILED" + fi + return $rv +} + +# +# all-daemon commands +# + +all_start() { + daemon_list daemons + for dmninst in $daemons; do + daemon_start --all "$dmninst" + done + vtysh_b +} + +all_stop() { + local pids reversed + + daemon_list enabled_daemons disabled_daemons + [ "$1" = "--reallyall" ] && enabled_daemons="$enabled_daemons $disabled_daemons" + + reversed="" + for dmninst in $enabled_daemons; do + reversed="$dmninst $reversed" + done + + for dmninst in $reversed; do + daemon_stop "$dmninst" "$1" & + pids="$pids $!" + done + for pid in $pids; do + wait $pid + done +} + +all_status() { + local fail + + daemon_list daemons + fail=0 + for dmninst in $daemons; do + print_status "$dmninst" || fail=1 + done + return $fail +} + +# +# config sourcing +# + +load_old_config() { + oldcfg="$1" + [ -r "$oldcfg" ] || return 0 + [ -s "$oldcfg" ] || return 0 + grep -v '^[[:blank:]]*\(#\|$\)' "$oldcfg" > /dev/null || return 0 + + log_warning_msg "Reading deprecated $oldcfg. Please move its settings to $C_PATH/daemons and remove it." + + # save off settings from daemons for the OR below + for dmn in $DAEMONS; do eval "_new_$dmn=\${$dmn:-no}"; done + + . "$oldcfg" + + # OR together the daemon enabling options between config files + for dmn in $DAEMONS; do eval "test \$_new_$dmn != no && $dmn=\$_new_$dmn; unset _new_$dmn"; done +} + +[ -r "$C_PATH/daemons" ] || { + log_failure_msg "cannot run $@: $C_PATH/daemons does not exist" + exit 1 +} +. "$C_PATH/daemons" + +if [ -z "$FRR_PATHSPACE" ]; then + load_old_config "$C_PATH/daemons.conf" + load_old_config "/etc/default/frr" + load_old_config "/etc/sysconfig/frr" +fi + +if { declare -p watchfrr_options 2>/dev/null || true; } | grep -q '^declare -a'; then + log_warning_msg "watchfrr_options contains a bash array value." \ + "The configured value is intentionally ignored since it is likely wrong." \ + "Please remove or fix the setting." + unset watchfrr_options +fi + +if test -z "$frr_profile"; then + # try to autodetect config profile + if test -d /etc/cumulus; then + frr_profile=datacenter + # elif test ...; then + # -- add your distro/system here + elif test -n "$FRR_DEFAULT_PROFILE"; then + frr_profile="$FRR_DEFAULT_PROFILE" + fi +fi +test -n "$frr_profile" && frr_global_options="$frr_global_options -F $frr_profile" + +# +# other defaults and dispatch +# + +frrcommon_main() { + local cmd + + debug "frrcommon_main" "$@" + + cmd="$1" + shift + + if [ "$1" = "all" ] || [ -z "$1" ]; then + case "$cmd" in + start) all_start;; + stop) all_stop;; + restart) + all_stop --quiet + all_start + ;; + *) $cmd "$@";; + esac + else + case "$cmd" in + start) daemon_start "$@";; + stop) daemon_stop "$@";; + restart) + daemon_stop "$@" + daemon_start "$@" + ;; + *) $cmd "$@";; + esac + fi +} |