You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

335 lines
7.1 KiB

  1. #!/bin/sh
  2. #
  3. #
  4. # This is a "library" of sorts for use by the other FRR shell scripts. It
  5. # has most of the daemon start/stop logic, but expects the following shell
  6. # functions/commands to be provided by the "calling" script:
  7. #
  8. # log_success_msg
  9. # log_warning_msg
  10. # log_failure_msg
  11. #
  12. # (coincidentally, these are LSB standard functions.)
  13. #
  14. # Sourcing this file in a shell script will load FRR config variables but
  15. # not perform any action. Note there is an "exit 1" if the main config
  16. # file does not exist.
  17. #
  18. # This script should be installed in /usr/sbin/frrcommon.sh
  19. PATH=/bin:/usr/bin:/sbin:/usr/sbin
  20. D_PATH="/usr/sbin" # /usr/lib/frr
  21. C_PATH="/etc/frr" # /etc/frr
  22. V_PATH="/var/run/frr" # /var/run/frr
  23. VTYSH="/usr/bin/vtysh" # /usr/bin/vtysh
  24. FRR_USER="network" # frr
  25. FRR_GROUP="network" # frr
  26. FRR_VTY_GROUP="" # frrvty
  27. # ORDER MATTERS FOR $DAEMONS!
  28. # - keep zebra first
  29. # - watchfrr does NOT belong in this list
  30. DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd"
  31. RELOAD_SCRIPT="$D_PATH/frr-reload.py"
  32. #
  33. # general helpers
  34. #
  35. debug() {
  36. [ -n "$watchfrr_debug" ] || return 0
  37. printf '%s %s(%s):' "`date +%Y-%m-%dT%H:%M:%S.%N`" "$0" $$ >&2
  38. # this is to show how arguments are split regarding whitespace & co.
  39. # (e.g. for use with `debug "message" "$@"`)
  40. while [ $# -gt 0 ]; do
  41. printf ' "%s"' "$1" >&2
  42. shift
  43. done
  44. printf '\n' >&2
  45. }
  46. chownfrr() {
  47. [ -n "$FRR_USER" ] && chown "$FRR_USER" "$1"
  48. [ -n "$FRR_GROUP" ] && chgrp "$FRR_GROUP" "$1"
  49. }
  50. vtysh_b () {
  51. [ "$1" = "watchfrr" ] && return 0
  52. [ -r "$C_PATH/frr.conf" ] || return 0
  53. if [ -n "$1" ]; then
  54. "$VTYSH" -b -n -d "$1"
  55. else
  56. "$VTYSH" -b -n
  57. fi
  58. }
  59. daemon_inst() {
  60. # note this sets global variables ($dmninst, $daemon, $inst)
  61. dmninst="$1"
  62. daemon="${dmninst%-*}"
  63. inst=""
  64. [ "$daemon" != "$dmninst" ] && inst="${dmninst#*-}"
  65. }
  66. daemon_list() {
  67. # note $1 and $2 specify names for global variables to be set
  68. local enabled disabled evar dvar
  69. enabled=""
  70. disabled=""
  71. evar="$1"
  72. dvar="$2"
  73. for daemon in $DAEMONS; do
  74. eval cfg=\$$daemon
  75. eval inst=\$${daemon}_instances
  76. [ "$daemon" = zebra -o "$daemon" = staticd ] && cfg=yes
  77. if [ -n "$cfg" -a "$cfg" != "no" -a "$cfg" != "0" ]; then
  78. if ! daemon_prep "$daemon" "$inst"; then
  79. continue
  80. fi
  81. debug "$daemon enabled"
  82. enabled="$enabled $daemon"
  83. if [ -n "$inst" ]; then
  84. debug "$daemon multi-instance $inst"
  85. oldifs="${IFS}"
  86. IFS="${IFS},"
  87. for i in $inst; do
  88. enabled="$enabled $daemon-$i"
  89. done
  90. IFS="${oldifs}"
  91. fi
  92. else
  93. debug "$daemon disabled"
  94. disabled="$disabled $daemon"
  95. fi
  96. done
  97. enabled="${enabled# }"
  98. disabled="${disabled# }"
  99. [ -z "$evar" ] && echo "$enabled"
  100. [ -n "$evar" ] && eval $evar="\"$enabled\""
  101. [ -n "$dvar" ] && eval $dvar="\"$disabled\""
  102. }
  103. #
  104. # individual daemon management
  105. #
  106. daemon_prep() {
  107. local daemon inst cfg
  108. daemon="$1"
  109. inst="$2"
  110. [ "$daemon" = "watchfrr" ] && return 0
  111. [ -x "$D_PATH/$daemon" ] || {
  112. log_failure_msg "cannot start $daemon${inst:+ (instance $inst)}: daemon binary not installed"
  113. return 1
  114. }
  115. [ -r "$C_PATH/frr.conf" ] && return 0
  116. cfg="$C_PATH/$daemon${inst:+-$inst}.conf"
  117. if [ ! -r "$cfg" ]; then
  118. touch "$cfg"
  119. chownfrr "$cfg"
  120. fi
  121. return 0
  122. }
  123. daemon_start() {
  124. local dmninst daemon inst args instopt wrap bin
  125. daemon_inst "$1"
  126. ulimit -n $MAX_FDS > /dev/null 2> /dev/null
  127. daemon_prep "$daemon" "$inst" || return 1
  128. if test ! -d "$V_PATH"; then
  129. mkdir -p "$V_PATH"
  130. chown $FRR_USER "$V_PATH"
  131. fi
  132. eval wrap="\$${daemon}_wrap"
  133. bin="$D_PATH/$daemon"
  134. instopt="${inst:+-n $inst}"
  135. eval args="\$${daemon}_options"
  136. if eval "$all_wrap $wrap $bin -d $instopt $args"; then
  137. log_success_msg "Started $dmninst"
  138. vtysh_b "$daemon"
  139. else
  140. log_failure_msg "Failed to start $dmninst!"
  141. fi
  142. }
  143. daemon_stop() {
  144. local dmninst daemon inst pidfile vtyfile pid cnt fail
  145. daemon_inst "$1"
  146. pidfile="$V_PATH/$daemon${inst:+-$inst}.pid"
  147. vtyfile="$V_PATH/$daemon${inst:+-$inst}.vty"
  148. [ -r "$pidfile" ] || fail="pid file not found"
  149. [ -z "$fail" ] && pid="`cat \"$pidfile\"`"
  150. [ -z "$fail" -a -z "$pid" ] && fail="pid file is empty"
  151. [ -n "$fail" ] || kill -0 "$pid" 2>/dev/null || fail="pid $pid not running"
  152. if [ -n "$fail" ]; then
  153. log_failure_msg "Cannot stop $dmninst: $fail"
  154. return 1
  155. fi
  156. debug "kill -2 $pid"
  157. kill -2 "$pid"
  158. cnt=1200
  159. while kill -0 "$pid" 2>/dev/null; do
  160. sleep 1
  161. [ $(( cnt -= 1 )) -gt 0 ] || break
  162. done
  163. if kill -0 "$pid" 2>/dev/null; then
  164. log_failure_msg "Failed to stop $dmninst, pid $pid still running"
  165. still_running=1
  166. return 1
  167. else
  168. log_success_msg "Stopped $dmninst"
  169. rm -f "$pidfile"
  170. return 0
  171. fi
  172. }
  173. daemon_status() {
  174. local dmninst daemon inst pidfile pid fail
  175. daemon_inst "$1"
  176. pidfile="$V_PATH/$daemon${inst:+-$inst}.pid"
  177. [ -r "$pidfile" ] || return 3
  178. pid="`cat \"$pidfile\"`"
  179. [ -z "$pid" ] && return 1
  180. kill -0 "$pid" 2>/dev/null || return 1
  181. return 0
  182. }
  183. print_status() {
  184. daemon_status "$1"
  185. rv=$?
  186. if [ "$rv" -eq 0 ]; then
  187. log_success_msg "Status of $1: running"
  188. else
  189. log_failure_msg "Status of $1: FAILED"
  190. fi
  191. return $rv
  192. }
  193. #
  194. # all-daemon commands
  195. #
  196. all_start() {
  197. daemon_list daemons
  198. for dmninst in $daemons; do
  199. daemon_start "$dmninst"
  200. done
  201. }
  202. all_stop() {
  203. local pids reversed
  204. daemon_list daemons disabled
  205. [ "$1" = "--reallyall" ] && daemons="$daemons $disabled"
  206. reversed=""
  207. for dmninst in $daemons; do
  208. reversed="$dmninst $reversed"
  209. done
  210. for dmninst in $reversed; do
  211. daemon_stop "$dmninst" &
  212. pids="$pids $!"
  213. done
  214. for pid in $pids; do
  215. wait $pid
  216. done
  217. }
  218. all_status() {
  219. local fail
  220. daemon_list daemons
  221. fail=0
  222. for dmninst in $daemons; do
  223. print_status "$dmninst" || fail=1
  224. done
  225. return $fail
  226. }
  227. #
  228. # config sourcing
  229. #
  230. load_old_config() {
  231. oldcfg="$1"
  232. [ -r "$oldcfg" ] || return 0
  233. [ -s "$oldcfg" ] || return 0
  234. grep -v '^[[:blank:]]*\(#\|$\)' "$oldcfg" > /dev/null || return 0
  235. log_warning_msg "Reading deprecated $oldcfg. Please move its settings to $C_PATH/daemons and remove it."
  236. # save off settings from daemons for the OR below
  237. for dmn in $DAEMONS; do eval "_new_$dmn=\${$dmn:-no}"; done
  238. . "$oldcfg"
  239. # OR together the daemon enabling options between config files
  240. for dmn in $DAEMONS; do eval "test \$_new_$dmn != no && $dmn=\$_new_$dmn; unset _new_$dmn"; done
  241. }
  242. [ -r "$C_PATH/daemons" ] || {
  243. log_failure_msg "cannot run $@: $C_PATH/daemons does not exist"
  244. exit 1
  245. }
  246. . "$C_PATH/daemons"
  247. load_old_config "$C_PATH/daemons.conf"
  248. load_old_config "/etc/default/frr"
  249. load_old_config "/etc/sysconfig/frr"
  250. if { declare -p watchfrr_options 2>/dev/null || true; } | grep -q '^declare \-a'; then
  251. log_warning_msg "watchfrr_options contains a bash array value." \
  252. "The configured value is intentionally ignored since it is likely wrong." \
  253. "Please remove or fix the setting."
  254. unset watchfrr_options
  255. fi
  256. #
  257. # other defaults and dispatch
  258. #
  259. frrcommon_main() {
  260. local cmd
  261. debug "frrcommon_main" "$@"
  262. cmd="$1"
  263. shift
  264. if [ "$1" = "all" -o -z "$1" ]; then
  265. case "$cmd" in
  266. start) all_start;;
  267. stop) all_stop;;
  268. restart)
  269. all_stop
  270. all_start
  271. ;;
  272. *) $cmd "$@";;
  273. esac
  274. else
  275. case "$cmd" in
  276. start) daemon_start "$@";;
  277. stop) daemon_stop "$@";;
  278. restart)
  279. daemon_stop "$@"
  280. daemon_start "$@"
  281. ;;
  282. *) $cmd "$@";;
  283. esac
  284. fi
  285. }