1# $OpenBSD: rc.subr,v 1.136 2021/02/27 09:28:04 ajacoutot Exp $ 2# 3# Copyright (c) 2010, 2011, 2014-2021 Antoine Jacoutot <ajacoutot@openbsd.org> 4# Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org> 5# Copyright (c) 2010, 2011, 2014 Robert Nagy <robert@openbsd.org> 6# 7# Permission to use, copy, modify, and distribute this software for any 8# purpose with or without fee is hereby granted, provided that the above 9# copyright notice and this permission notice appear in all copies. 10# 11# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 19_rc_actions="start stop restart reload check" 20readonly _rc_actions 21 22_rc_err() { 23 [ -n "${1}" ] && echo "${1}" 1>&2 24 [ -n "${2}" ] && exit "${2}" || exit 1 25} 26 27_rc_not_supported() { 28 local _a _enotsup _what=${1} 29 for _a in ${_rc_actions}; do 30 [ "${_what}" == "restart" ] && _what="stop" 31 if [ "${_what}" == "${_a}" ]; then 32 eval _enotsup=\${rc_${_what}} 33 break 34 fi 35 done 36 [ X"${_enotsup}" == X"NO" ] 37} 38 39_rc_usage() { 40 local _a _allsup 41 for _a in ${_rc_actions}; do 42 _rc_not_supported ${_a} || _allsup="${_allsup:+$_allsup|}${_a}" 43 done 44 _rc_err "usage: $0 [-df] ${_allsup}" 45} 46 47_rc_write_runfile() { 48 [ -d ${_RC_RUNDIR} ] || mkdir -p ${_RC_RUNDIR} && 49 cat >${_RC_RUNFILE} <<EOF 50daemon_class=${daemon_class} 51daemon_flags=${daemon_flags} 52daemon_logger=${daemon_logger} 53daemon_rtable=${daemon_rtable} 54daemon_timeout=${daemon_timeout} 55daemon_user=${daemon_user} 56pexp=${pexp} 57EOF 58} 59 60_rc_rm_runfile() { 61 rm -f ${_RC_RUNFILE} 62} 63 64_rc_check_name() { 65 [[ $1 == +([_[:alpha:]])+(|[_[:alnum:]]) ]] 66} 67 68_rc_do() { 69 if [ -n "${_RC_DEBUG}" ]; then 70 echo "doing $@" && "$@" 71 else 72 "$@" >/dev/null 2>&1 73 fi 74} 75 76_rc_exit() { 77 local _pfix 78 [ -z "${INRC}" -o X"$1" != X"ok" ] && _pfix="($1)" 79 echo ${INRC:+'-n'} "${_pfix}" 80 [ X"$1" = X"ok" ] && exit 0 || exit 1 81} 82 83_rc_alarm() 84{ 85 trap - ALRM 86 kill -ALRM ${_TIMERSUB} 2>/dev/null # timer may not be running anymore 87 kill $! 2>/dev/null # kill last job if it's running 88} 89 90_rc_wait() { 91 local _i=0 92 if [ X"$1" = X"start" ]; then # prevent hanging the boot sequence 93 trap "_rc_alarm" ALRM 94 while [ $_i -lt ${daemon_timeout} ]; do 95 if _rc_do rc_check; then 96 [ X"${rc_bg}" = X"YES" ] || [ -z "$$" ] && break 97 fi 98 sleep 1 99 _i=$((_i+1)) 100 done & wait 101 pkill -ALRM -P $$ 102 return 103 fi 104 while [ $_i -lt ${daemon_timeout} ]; do 105 case "$1" in 106 reload) 107 _rc_do rc_check && return 0 ;; 108 stop) 109 _rc_do rc_check || return 0 ;; 110 *) 111 break ;; 112 esac 113 sleep 1 114 _i=$((_i+1)) 115 done 116 return 1 117} 118 119_rc_quirks() { 120 # special care needed for spamlogd to avoid starting it up and failing 121 # all the time 122 if [ X"${spamd_flags}" = X"NO" -o X"${spamd_black}" != X"NO" ]; then 123 spamlogd_flags=NO 124 fi 125 126 # special care needed for pflogd to avoid starting it up and failing 127 # if pf is not enabled 128 if [ X"${pf}" = X"NO" ]; then 129 pflogd_flags=NO 130 fi 131 132 # special care needed if nfs_server=YES to startup nfsd and mountd with 133 # sane default flags 134 if [ X"${nfs_server}" = X"YES" ]; then 135 [ X"${nfsd_flags}" = X"NO" ] && nfsd_flags="-tun 4" 136 [ X"${mountd_flags}" = X"NO" ] && mountd_flags= 137 fi 138} 139 140_rc_parse_conf() { 141 typeset -l _key 142 local _l _rcfile _val 143 set -A _allowed_keys -- \ 144 accounting amd_master check_quotas ipsec library_aslr \ 145 multicast nfs_server pexp pf pkg_scripts shlib_dirs spamd_black 146 147 [ $# -gt 0 ] || set -- /etc/rc.conf /etc/rc.conf.local 148 for _rcfile; do 149 [[ -f $_rcfile ]] || continue 150 while IFS=' ' read -r _l; do 151 [[ $_l == [!#=]*=* ]] || continue 152 _key=${_l%%*([[:blank:]])=*} 153 [[ $_key == *_@(flags|logger|rtable|timeout|user) ]] || 154 [[ " ${_allowed_keys[*]} " == *" $_key "* ]] || 155 continue 156 [[ $_key == "" ]] && continue 157 _val=${_l##*([!=])=*([[:blank:]])} 158 _val=${_val%%#*} 159 _val=${_val%%*([[:blank:]])} 160 # remove leading and trailing quotes (backwards compat) 161 [[ $_val == @(\"*\"|\'*\') ]] && 162 _val=${_val#?} _val=${_val%?} 163 eval "${_key}=\${_val}" 164 done < $_rcfile 165 done 166 167 _rc_do _rc_quirks 168} 169 170# return if we only want internal functions 171[ -n "${FUNCS_ONLY}" ] && return 172 173rc_start() { 174 ${rcexec} "${daemon_logger:+set -o pipefail; }${daemon} ${daemon_flags}${daemon_logger:+ 2>&1 | 175 logger -ip ${daemon_logger} -t ${_name}}" 176} 177 178rc_check() { 179 pgrep -T "${daemon_rtable}" -q -xf "${pexp}" 180} 181 182rc_reload() { 183 pkill -HUP -T "${daemon_rtable}" -xf "${pexp}" 184} 185 186rc_stop() { 187 pkill -T "${daemon_rtable}" -xf "${pexp}" 188} 189 190rc_cmd() { 191 local _to _n _ret 192 193 [ -n "${1}" ] && echo "${_rc_actions}" | grep -qw -- ${1} || _rc_usage 194 195 [ "$(id -u)" -eq 0 ] || 196 [ X"${rc_usercheck}" != X"NO" -a X"$1" = "Xcheck" ] || 197 _rc_err "$0: need root privileges" 198 199 if _rc_not_supported $1; then 200 [ -n "${INRC}" ] && exit 1 201 _rc_err "$0: $1 is not supported" 202 fi 203 204 [ -n "${_RC_DEBUG}" ] || _n="-n" 205 206 [[ ${1} == start ]] || _rc_do _rc_parse_conf ${_RC_RUNFILE} 207 208 case "$1" in 209 check) 210 echo $_n "${INRC:+ }${_name}" 211 _rc_do rc_check && _rc_exit ok 212 _rc_exit failed 213 ;; 214 start) 215 if [ X"${daemon_flags}" = X"NO" ]; then 216 _rc_err "$0: need -f to force $1 since ${_name}_flags=NO" 217 fi 218 [ -z "${INRC}" ] && _rc_do rc_check && exit 0 219 echo $_n "${INRC:+ }${_name}" 220 while true; do # no real loop, only needed to break 221 if type rc_pre >/dev/null; then 222 _rc_do rc_pre || break 223 fi 224 _rc_do _rc_wait start & _TIMERSUB=$! 225 trap "_rc_alarm" ALRM 226 _rc_do rc_start; _ret=$? 227 kill -ALRM ${_TIMERSUB} 228 wait ${_TIMERSUB} 2>/dev/null # don't print Alarm clock 229 [[ "${_ret}" == 142 ]] && [ X"${rc_bg}" != X"YES" ] && 230 _to="timeout" 231 # XXX for unknown reason, rc_check can fail (e.g. redis) 232 # while it just succeeded in _rc_wait; the check is 233 # needed to cope with failing daemons returning 0 234 #[[ "${_ret}" == @(0|142) ]] && _rc_do rc_check || break 235 [[ "${_ret}" == @(0|142) ]] || break 236 _rc_do _rc_write_runfile 237 _rc_exit ${_to:=ok} 238 done 239 # handle failure 240 type rc_post >/dev/null && _rc_do rc_post 241 _rc_do _rc_rm_runfile 242 _rc_exit failed 243 ;; 244 stop) 245 _rc_do rc_check || exit 0 246 echo $_n "${INRC:+ }${_name}" 247 _rc_do rc_stop || _rc_exit failed 248 _rc_do _rc_wait stop || _rc_exit failed 249 if type rc_post >/dev/null; then 250 _rc_do rc_post || _rc_exit failed 251 fi 252 _rc_do _rc_rm_runfile 253 _rc_exit ok 254 ;; 255 reload) 256 echo $_n "${INRC:+ }${_name}" 257 _rc_do rc_check && _rc_do rc_reload || _rc_exit failed 258 _rc_do _rc_wait reload || _rc_exit failed 259 _rc_exit ok 260 ;; 261 restart) 262 $0 ${_RC_DEBUG} ${_RC_FORCE} stop && 263 $0 ${_RC_DEBUG} ${_RC_FORCE} start 264 ;; 265 *) 266 _rc_usage 267 ;; 268 esac 269} 270 271_name=${0##*/} 272_rc_check_name "${_name}" || _rc_err "invalid rc.d script name: ${_name}" 273 274[ -n "${daemon}" ] || _rc_err "$0: daemon is not set" 275 276unset _RC_DEBUG _RC_FORCE 277while getopts "df" c; do 278 case "$c" in 279 d) _RC_DEBUG=-d;; 280 f) _RC_FORCE=-f;; 281 *) _rc_usage;; 282 esac 283done 284shift $((OPTIND-1)) 285 286_RC_RUNDIR=/var/run/rc.d 287_RC_RUNFILE=${_RC_RUNDIR}/${_name} 288 289# parse /etc/rc.conf{.local} for the daemon variables 290_rc_do _rc_parse_conf 291 292eval _rcflags=\${${_name}_flags} 293eval _rclogger=\${${_name}_logger} 294eval _rcrtable=\${${_name}_rtable} 295eval _rctimeout=\${${_name}_timeout} 296eval _rcuser=\${${_name}_user} 297 298# set default values; duplicated in rcctl(8) 299getcap -f /etc/login.conf ${_name} 1>/dev/null 2>&1 && daemon_class=${_name} || 300 daemon_class=daemon 301[ -z "${daemon_rtable}" ] && daemon_rtable=0 302[ -z "${daemon_timeout}" ] && daemon_timeout=30 303[ -z "${daemon_user}" ] && daemon_user=root 304 305# use flags from the rc.d script if daemon is not enabled 306[ -n "${_RC_FORCE}" -o "$1" != "start" ] && [ X"${_rcflags}" = X"NO" ] && 307 unset _rcflags 308 309[ -n "${_rcflags}" ] && daemon_flags=${_rcflags} 310[ -n "${_rclogger}" ] && daemon_logger=${_rclogger} 311[ -n "${_rcrtable}" ] && daemon_rtable=${_rcrtable} 312[ -n "${_rctimeout}" ] && daemon_timeout=${_rctimeout} 313[ -n "${_rcuser}" ] && daemon_user=${_rcuser} 314 315if [ -n "${_RC_DEBUG}" ]; then 316 echo -n "${_name}_flags " 317 [ -n "${_rcflags}" ] || echo -n "empty, using default " 318 echo ">${daemon_flags}<" 319fi 320 321readonly daemon_class 322unset _rcflags _rclogger _rcrtable _rctimeout _rcuser 323# the shell will strip the quotes from daemon_flags when starting a daemon; 324# make sure pexp matches the process (i.e. doesn't include the quotes) 325pexp="$(eval echo ${daemon}${daemon_flags:+ ${daemon_flags}})" 326rcexec="su -fl -c ${daemon_class} -s /bin/sh ${daemon_user} -c" 327[ "${daemon_rtable}" -eq "$(id -R)" ] || 328 rcexec="route -T ${daemon_rtable} exec ${rcexec}" 329