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