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