#!/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 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 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 () { [ "${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 ] && 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" ] && [ "$2" != "--quiet" ]; then 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 log_failure_msg "Failed to stop $dmninst, pid $pid still running" still_running=1 return 1 else 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 need_zebra 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 # Stop zebra last, after trying to stop the other daemons for dmninst in $reversed; do if [ "$dmninst" = "zebra" ]; then need_zebra="yes" continue fi daemon_stop "$dmninst" "$1" & pids="$pids $!" done for pid in $pids; do wait $pid done if [ -n "$need_zebra" ]; then daemon_stop "zebra" fi } 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 }