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