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.

397 lines
8.5 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. # FRR_PATHSPACE is passed in from watchfrr
  20. suffix="${FRR_PATHSPACE:+/${FRR_PATHSPACE}}"
  21. nsopt="${FRR_PATHSPACE:+-N ${FRR_PATHSPACE}}"
  22. PATH=/bin:/usr/bin:/sbin:/usr/sbin
  23. D_PATH="/usr/sbin" # /usr/lib/frr
  24. C_PATH="/etc/frr" # /etc/frr
  25. V_PATH="/var/run/frr" # /var/run/frr
  26. VTYSH="/usr/bin/vtysh" # /usr/bin/vtysh
  27. FRR_USER="network" # frr
  28. FRR_GROUP="network" # frr
  29. FRR_VTY_GROUP="" # frrvty
  30. FRR_CONFIG_MODE="0600" # 0600
  31. FRR_DEFAULT_PROFILE="traditional" # traditional / datacenter
  32. # ORDER MATTERS FOR $DAEMONS!
  33. # - keep zebra first
  34. # - watchfrr does NOT belong in this list
  35. DAEMONS="zebra bgpd ripd ripngd ospfd ospf6d isisd babeld pathd pimd ldpd nhrpd eigrpd sharpd pbrd staticd bfdd fabricd vrrpd"
  36. RELOAD_SCRIPT="$D_PATH/frr-reload.py"
  37. #
  38. # general helpers
  39. #
  40. debug() {
  41. [ -n "$watchfrr_debug" ] || return 0
  42. printf '%s %s(%s):' "`date +%Y-%m-%dT%H:%M:%S.%N`" "$0" $$ >&2
  43. # this is to show how arguments are split regarding whitespace & co.
  44. # (e.g. for use with `debug "message" "$@"`)
  45. while [ $# -gt 0 ]; do
  46. printf ' "%s"' "$1" >&2
  47. shift
  48. done
  49. printf '\n' >&2
  50. }
  51. chownfrr() {
  52. [ -n "$FRR_USER" ] && chown "$FRR_USER" "$1"
  53. [ -n "$FRR_GROUP" ] && chgrp "$FRR_GROUP" "$1"
  54. [ -n "$FRR_CONFIG_MODE" ] && chmod "$FRR_CONFIG_MODE" "$1"
  55. if [ -d "$1" ]; then
  56. chmod u+x "$1"
  57. fi
  58. }
  59. vtysh_b () {
  60. [ "$1" = "watchfrr" ] && return 0
  61. [ -r "$C_PATH/frr.conf" ] || return 0
  62. if [ -n "$1" ]; then
  63. "$VTYSH" `echo $nsopt` -b -d "$1"
  64. else
  65. "$VTYSH" `echo $nsopt` -b
  66. fi
  67. }
  68. daemon_inst() {
  69. # note this sets global variables ($dmninst, $daemon, $inst)
  70. dmninst="$1"
  71. daemon="${dmninst%-*}"
  72. inst=""
  73. [ "$daemon" != "$dmninst" ] && inst="${dmninst#*-}"
  74. }
  75. daemon_list() {
  76. # note $1 and $2 specify names for global variables to be set
  77. local enabled disabled evar dvar
  78. enabled=""
  79. disabled=""
  80. evar="$1"
  81. dvar="$2"
  82. for daemon in $DAEMONS; do
  83. eval cfg=\$$daemon
  84. eval inst=\$${daemon}_instances
  85. [ "$daemon" = zebra -o "$daemon" = staticd ] && cfg=yes
  86. if [ -n "$cfg" -a "$cfg" != "no" -a "$cfg" != "0" ]; then
  87. if ! daemon_prep "$daemon" "$inst"; then
  88. continue
  89. fi
  90. debug "$daemon enabled"
  91. # enabled="$enabled $daemon"
  92. if [ -n "$inst" ]; then
  93. debug "$daemon multi-instance $inst"
  94. oldifs="${IFS}"
  95. IFS="${IFS},"
  96. for i in $inst; do
  97. enabled="$enabled $daemon-$i"
  98. done
  99. IFS="${oldifs}"
  100. else
  101. enabled="$enabled $daemon"
  102. fi
  103. else
  104. debug "$daemon disabled"
  105. disabled="$disabled $daemon"
  106. fi
  107. done
  108. enabled="${enabled# }"
  109. disabled="${disabled# }"
  110. [ -z "$evar" ] && echo "$enabled"
  111. [ -n "$evar" ] && eval $evar="\"$enabled\""
  112. [ -n "$dvar" ] && eval $dvar="\"$disabled\""
  113. }
  114. all_daemon_list() {
  115. # note $1 specifies the name of a global variable to be set
  116. local enabled evar daemon inst oldifs i
  117. enabled=""
  118. evar="$1"
  119. for daemon in $DAEMONS; do
  120. eval inst=\$${daemon}_instances
  121. if [ -n "$inst" ]; then
  122. oldifs="${IFS}"
  123. IFS="${IFS},"
  124. for i in $inst; do
  125. enabled="$enabled $daemon-$i"
  126. done
  127. IFS="${oldifs}"
  128. else
  129. enabled="$enabled $daemon"
  130. fi
  131. done
  132. enabled="${enabled# }"
  133. [ -z "$evar" ] && echo "$enabled"
  134. [ -n "$evar" ] && eval $evar="\"$enabled\""
  135. }
  136. in_list() {
  137. local item i
  138. item="$1"
  139. shift 1
  140. for i in "$@"; do
  141. [ "$item" = "$i" ] && return 0
  142. done
  143. return 1
  144. }
  145. #
  146. # individual daemon management
  147. #
  148. daemon_prep() {
  149. local daemon inst cfg
  150. daemon="$1"
  151. inst="$2"
  152. [ "$daemon" = "watchfrr" ] && return 0
  153. [ -x "$D_PATH/$daemon" ] || {
  154. log_failure_msg "cannot start $daemon${inst:+ (instance $inst)}: daemon binary not installed"
  155. return 1
  156. }
  157. [ -r "$C_PATH/frr.conf" ] && return 0
  158. cfg="$C_PATH/$daemon${inst:+-$inst}.conf"
  159. if [ ! -r "$cfg" ]; then
  160. touch "$cfg"
  161. chownfrr "$cfg"
  162. fi
  163. return 0
  164. }
  165. daemon_start() {
  166. local dmninst daemon inst args instopt wrap bin
  167. daemon_inst "$1"
  168. ulimit -n $MAX_FDS > /dev/null 2> /dev/null
  169. daemon_prep "$daemon" "$inst" || return 1
  170. if test ! -d "$V_PATH"; then
  171. mkdir -p "$V_PATH"
  172. chown $FRR_USER "$V_PATH"
  173. fi
  174. eval wrap="\$${daemon}_wrap"
  175. bin="$D_PATH/$daemon"
  176. instopt="${inst:+-n $inst}"
  177. eval args="\$${daemon}_options"
  178. if eval "$all_wrap $wrap $bin $nsopt -d $frr_global_options $instopt $args"; then
  179. log_success_msg "Started $dmninst"
  180. vtysh_b "$daemon"
  181. else
  182. log_failure_msg "Failed to start $dmninst!"
  183. fi
  184. }
  185. daemon_stop() {
  186. local dmninst daemon inst pidfile vtyfile pid cnt fail
  187. daemon_inst "$1"
  188. pidfile="$V_PATH/$daemon${inst:+-$inst}.pid"
  189. vtyfile="$V_PATH/$daemon${inst:+-$inst}.vty"
  190. [ -r "$pidfile" ] || fail="pid file not found"
  191. [ -z "$fail" ] && pid="`cat \"$pidfile\"`"
  192. [ -z "$fail" -a -z "$pid" ] && fail="pid file is empty"
  193. [ -n "$fail" ] || kill -0 "$pid" 2>/dev/null || fail="pid $pid not running"
  194. if [ -n "$fail" ]; then
  195. log_failure_msg "Cannot stop $dmninst: $fail"
  196. return 1
  197. fi
  198. debug "kill -2 $pid"
  199. kill -2 "$pid"
  200. cnt=1200
  201. while kill -0 "$pid" 2>/dev/null; do
  202. sleep 1
  203. [ $(( cnt -= 1 )) -gt 0 ] || break
  204. done
  205. if kill -0 "$pid" 2>/dev/null; then
  206. log_failure_msg "Failed to stop $dmninst, pid $pid still running"
  207. still_running=1
  208. return 1
  209. else
  210. log_success_msg "Stopped $dmninst"
  211. rm -f "$pidfile"
  212. return 0
  213. fi
  214. }
  215. daemon_status() {
  216. local dmninst daemon inst pidfile pid fail
  217. daemon_inst "$1"
  218. pidfile="$V_PATH/$daemon${inst:+-$inst}.pid"
  219. [ -r "$pidfile" ] || return 3
  220. pid="`cat \"$pidfile\"`"
  221. [ -z "$pid" ] && return 1
  222. kill -0 "$pid" 2>/dev/null || return 1
  223. return 0
  224. }
  225. print_status() {
  226. daemon_status "$1"
  227. rv=$?
  228. if [ "$rv" -eq 0 ]; then
  229. log_success_msg "Status of $1: running"
  230. else
  231. log_failure_msg "Status of $1: FAILED"
  232. fi
  233. return $rv
  234. }
  235. #
  236. # all-daemon commands
  237. #
  238. all_start() {
  239. daemon_list daemons
  240. for dmninst in $daemons; do
  241. daemon_start "$dmninst"
  242. done
  243. }
  244. all_stop() {
  245. local pids reversed
  246. daemon_list daemons disabled
  247. [ "$1" = "--reallyall" ] && daemons="$daemons $disabled"
  248. reversed=""
  249. for dmninst in $daemons; do
  250. reversed="$dmninst $reversed"
  251. done
  252. for dmninst in $reversed; do
  253. daemon_stop "$dmninst" &
  254. pids="$pids $!"
  255. done
  256. for pid in $pids; do
  257. wait $pid
  258. done
  259. }
  260. all_status() {
  261. local fail
  262. daemon_list daemons
  263. fail=0
  264. for dmninst in $daemons; do
  265. print_status "$dmninst" || fail=1
  266. done
  267. return $fail
  268. }
  269. #
  270. # config sourcing
  271. #
  272. load_old_config() {
  273. oldcfg="$1"
  274. [ -r "$oldcfg" ] || return 0
  275. [ -s "$oldcfg" ] || return 0
  276. grep -v '^[[:blank:]]*\(#\|$\)' "$oldcfg" > /dev/null || return 0
  277. log_warning_msg "Reading deprecated $oldcfg. Please move its settings to $C_PATH/daemons and remove it."
  278. # save off settings from daemons for the OR below
  279. for dmn in $DAEMONS; do eval "_new_$dmn=\${$dmn:-no}"; done
  280. . "$oldcfg"
  281. # OR together the daemon enabling options between config files
  282. for dmn in $DAEMONS; do eval "test \$_new_$dmn != no && $dmn=\$_new_$dmn; unset _new_$dmn"; done
  283. }
  284. [ -r "$C_PATH/daemons" ] || {
  285. log_failure_msg "cannot run $@: $C_PATH/daemons does not exist"
  286. exit 1
  287. }
  288. . "$C_PATH/daemons"
  289. if [ -z "$FRR_PATHSPACE" ]; then
  290. load_old_config "$C_PATH/daemons.conf"
  291. load_old_config "/etc/default/frr"
  292. load_old_config "/etc/sysconfig/frr"
  293. fi
  294. if { declare -p watchfrr_options 2>/dev/null || true; } | grep -q '^declare \-a'; then
  295. log_warning_msg "watchfrr_options contains a bash array value." \
  296. "The configured value is intentionally ignored since it is likely wrong." \
  297. "Please remove or fix the setting."
  298. unset watchfrr_options
  299. fi
  300. if test -z "$frr_profile"; then
  301. # try to autodetect config profile
  302. if test -d /etc/cumulus; then
  303. frr_profile=datacenter
  304. # elif test ...; then
  305. # -- add your distro/system here
  306. elif test -n "$FRR_DEFAULT_PROFILE"; then
  307. frr_profile="$FRR_DEFAULT_PROFILE"
  308. fi
  309. fi
  310. test -n "$frr_profile" && frr_global_options="$frr_global_options -F $frr_profile"
  311. #
  312. # other defaults and dispatch
  313. #
  314. frrcommon_main() {
  315. local cmd
  316. debug "frrcommon_main" "$@"
  317. cmd="$1"
  318. shift
  319. if [ "$1" = "all" -o -z "$1" ]; then
  320. case "$cmd" in
  321. start) all_start;;
  322. stop) all_stop;;
  323. restart)
  324. all_stop
  325. all_start
  326. ;;
  327. *) $cmd "$@";;
  328. esac
  329. else
  330. case "$cmd" in
  331. start) daemon_start "$@";;
  332. stop) daemon_stop "$@";;
  333. restart)
  334. daemon_stop "$@"
  335. daemon_start "$@"
  336. ;;
  337. *) $cmd "$@";;
  338. esac
  339. fi
  340. }