rc.subr revision 1.12
1# $NetBSD: rc.subr,v 1.12 2000/04/19 04:26:22 simonb Exp $ 2# 3# Copyright (c) 1997-2000 The NetBSD Foundation, Inc. 4# All rights reserved. 5# 6# This code is derived from software contributed to The NetBSD Foundation 7# by Luke Mewburn. 8# 9# Redistribution and use in source and binary forms, with or without 10# modification, are permitted provided that the following conditions 11# are met: 12# 1. Redistributions of source code must retain the above copyright 13# notice, this list of conditions and the following disclaimer. 14# 2. Redistributions in binary form must reproduce the above copyright 15# notice, this list of conditions and the following disclaimer in the 16# documentation and/or other materials provided with the distribution. 17# 3. All advertising materials mentioning features or use of this software 18# must display the following acknowledgement: 19# This product includes software developed by the NetBSD 20# Foundation, Inc. and its contributors. 21# 4. Neither the name of The NetBSD Foundation nor the names of its 22# contributors may be used to endorse or promote products derived 23# from this software without specific prior written permission. 24# 25# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 26# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 29# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35# POSSIBILITY OF SUCH DAMAGE. 36# 37# rc.subr 38# functions used by various rc scripts 39# 40 41# 42# functions 43# --------- 44 45# 46# checkyesno var 47# Test $1 variable, and warn if not set to YES or NO. 48# Return 0 if it's "yes" (et al), nonzero otherwise. 49# 50checkyesno() 51{ 52 eval _value=\$${1} 53 case $_value in 54 55 # "yes", "true", "on", or "1" 56 [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) 57 return 0 58 ;; 59 60 # "no", "false", "off", or "0" 61 [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0) 62 return 1 63 ;; 64 *) 65 warn "\$${1} is not set properly." 66 return 1 67 ;; 68 esac 69} 70 71# 72# mount_critical_filesystems 73# Go through the list of critical filesystems, checking each one 74# to see if it is mounted, and if it is not, mounting it. 75# 76mount_critical_filesystems() 77{ 78 if [ $1 = local ]; then 79 _fslist=$critical_filesystems_beforenet 80 else 81 _fslist=$critical_filesystems 82 fi 83 for fs in $_fslist; do 84 mount | ( 85 ismounted=no 86 while read what _on on _type type; do 87 if [ $on = $fs ]; then 88 ismounted=yes 89 fi 90 done 91 if [ $ismounted = no ]; then 92 mount $fs >/dev/null 2>&1 93 fi 94 ) 95 done 96} 97 98# 99# check_pidfile pidfile procname 100# Parses the first line of pidfile for a pid, and ensures 101# that the process is running and matches procname. 102# Prints the matching pid upon success, nothing otherwise. 103# 104check_pidfile() 105{ 106 _pidfile=$1 107 _procname=$2 108 if [ -z "$_pidfile" -o -z "$_procname" ]; then 109 err 3 'USAGE: check_pidfile pidfile procname' 110 fi 111 if [ ! -f $_pidfile ]; then 112 return 113 fi 114 read _pid _junk < $_pidfile 115 if [ -z "$_pid" ]; then 116 return 117 fi 118 _procnamebn=`basename $_procname` 119 ps -p $_pid -o 'pid,command' | while read _npid _arg0 _argv; do 120 if [ "$_npid" = "PID" ]; then 121 continue 122 fi 123 if [ "$_arg0" = "$_procname" -o "$_arg0" = "$_procnamebn" \ 124 -o "$_arg0" = "${_procnamebn}:" ]; then 125 echo $_npid 126 return 127 fi 128 done 129} 130 131# 132# check_process procname 133# Ensures that a process (or processes) named procname is running. 134# Prints a list of matching pids. 135# 136check_process() 137{ 138 _procname=$1 139 if [ -z "$_procname" ]; then 140 err 3 'USAGE: check_process procname' 141 fi 142 _procnamebn=`basename $_procname` 143 _pref= 144 ps -ax -o 'pid,command' | while read _npid _arg0 _argv; do 145 if [ "$_npid" = "PID" ]; then 146 continue 147 fi 148 if [ "$_arg0" = "$_procname" -o "$_arg0" = "$_procnamebn" \ 149 -o "$_arg0" = "${_procnamebn}:" ]; then 150 echo -n "$_pref$_npid" 151 _pref=" " 152 fi 153 done 154} 155 156# 157# run_rc_command arg [supported_args] 158# Scan supported_args (which has "start stop restart rcvar status" 159# prepended) for arg. 160# If there's a match, run ${arg}_cmd or the default command (see below). 161# 162# If arg has a given prefix, then: 163# prefix operation 164# ------ --------- 165# fast Skip the pid check. 166# force Set ${rcvar} to YES. 167# 168# The following globals are used: 169# name needed function 170# ---- ------ -------- 171# name y Name of script. 172# command n Full path to command. 173# Not needed if ${arg}_cmd is set for 174# each keyword. 175# command_args n Optional args/shell directives for command. 176# pidfile n If set, use check_pidfile $pidfile, else if 177# $command is set, use check_process $command. 178# ${rcvar} n If the default command is being run, this is 179# checked with checkyesno to determine if 180# the action should be run. 181# If this variable isn't set, ${name} is checked 182# instead. 183# ${name}_flags n Arguments to call ${command} with. 184# NOTE: if $flags is set (e.g, from the parent 185# environment), it overrides this. 186# ${_arg}_cmd n If set, use this as the action when invoked; 187# $_arg is available to the action to use. 188# Otherwise, use default command (see below) 189# NOTE: checkyesno ${rcvar} is NOT performed 190# for ${_arg}_cmd; use ${_arg}_precmd to 191# do this. 192# ${_arg}_precmd n If set, run just before performing the main 193# action in the default command (i.e, after 194# checking for required bits and process 195# (non)existance). 196# If this completes with a non-zero exit code, 197# don't run ${_arg}_cmd. 198# required_dirs n If set, check for the existence of the given 199# directories before running the default 200# (re)start command. 201# required_files n If set, check for the readability of the given 202# files before running the default (re)start 203# command. 204# required_vars n If set, perform checkyesno on each of the 205# listed variables before running the default 206# (re)start command. 207# 208# Default commands for a given arg: 209# arg default 210# --- ------- 211# status Show if ${command} is running, etc. 212# start if !running && checkyesno ${rcvar} 213# ${command} 214# stop if ${pidfile} 215# kill $sig_stop `check_pidfile $pidfile` 216# else 217# kill $sig_stop `check_process $command` 218# $sig_stop defaults to TERM. 219# reload As stop, except use $sig_reload instead. 220# $sig_reload defaults to HUP. 221# restart Run `stop' then `start'. 222# 223run_rc_command() 224{ 225 if ! checkyesno rc_configured; then 226 err 1 '/etc/rc.conf is not configured' 227 fi 228 229 _arg=$1 230 shift 231 _ckvar=${rcvar:-$name} 232 if [ -z "$_ckvar" ]; then 233 err 3 'neither $rcvar or $name is set.' 234 fi 235 236 if [ ! -z "$rc_fastboot" ]; then 237 if checkyesno rc_fastboot; then 238 _arg=fast$_arg 239 fi 240 fi 241 242 case "$_arg" in 243 fast*) 244 _arg=${_arg#fast} 245 _rc_fast_run=YES 246 ;; 247 force*) 248 _arg=${_arg#force} 249 eval ${_ckvar}=YES 250 ;; 251 esac 252 253 _keywords="start stop restart rcvar $*" 254 _pidcmd= 255 if [ -z "$_rc_fast_run" ]; then 256 if [ -n "$pidfile" ]; then 257 _pidcmd='_pid=`check_pidfile '$pidfile' '$command'`' 258 elif [ -n "$command" ]; then 259 _pidcmd='_pid=`check_process '$command'`' 260 fi 261 if [ -n "$_pidcmd" ]; then 262 _keywords="${_keywords} status" 263 fi 264 fi 265 266 if [ -z "$_arg" ]; then 267 rc_usage "$_keywords" 268 fi 269 270 if [ -n "$flags" ]; then 271 _flags=$flags 272 else 273 eval _flags=\$${name}_flags 274 fi 275 276 eval $_pidcmd 277 278 for _elem in $_keywords; do 279 if [ "$_elem" != "$_arg" ]; then 280 continue 281 fi 282 283 eval _cmd=\$${_arg}_cmd 284 eval _precmd=\$${_arg}_precmd 285 if [ -n "$_cmd" ]; then 286 eval $_precmd || return 1 287 eval $_cmd 288 return 0 289 fi 290 291 case "$_arg" in 292 293 status) 294 if [ -n "$_pid" ]; then 295 echo "${name} is running as pid $_pid." 296 else 297 echo "${name} is not running." 298 fi 299 ;; 300 301 start) 302 if [ -n "$_pid" ]; then 303 echo "${name} already running? (pid=$_pid)." 304 exit 1 305 fi 306 307 if ! checkyesno ${_ckvar} || [ ! -x $command ]; then 308 return 0 309 fi 310 311 for _f in $required_vars; do 312 if ! checkyesno $_f; then 313 warn \ 314 "\$${_f} is not set; ${name} not started." 315 return 1 316 fi 317 done 318 for _f in $required_dirs; do 319 if [ ! -d "${_f}/." ]; then 320 warn \ 321 "${_f} is not a directory; ${name} not started." 322 return 1 323 fi 324 done 325 for _f in $required_files; do 326 if [ ! -r "${_f}" ]; then 327 warn \ 328 "${_f} is not readable; ${name} not started." 329 return 1 330 fi 331 done 332 333 eval $_precmd || return 1 334 echo "Starting ${name}." 335 eval $command $_flags $command_args 336 ;; 337 338 stop) 339 if [ -z "$_pid" ]; then 340 if checkyesno ${_ckvar}; then 341 if [ -n "$pidfile" ]; then 342 echo \ 343 "${name} not running? (check $pidfile)." 344 else 345 echo "${name} not running?" 346 fi 347 exit 1 348 fi 349 return 0 350 fi 351 352 eval $_precmd || return 1 353 echo "Stopping ${name}." 354 kill -${sig_stop:-TERM} $_pid 355 ;; 356 357 reload) 358 if [ -z "$_pid" ]; then 359 if checkyesno ${_ckvar}; then 360 if [ -n "$pidfile" ]; then 361 echo \ 362 "${name} not running? (check $pidfile)." 363 else 364 echo "${name} not running?" 365 fi 366 exit 1 367 fi 368 return 0 369 fi 370 echo "Reloading ${name} config files." 371 eval $_precmd || return 1 372 kill -${sig_reload:-HUP} $_pid 373 ;; 374 375 restart) 376 if ! checkyesno ${_ckvar}; then 377 return 0 378 fi 379 eval $_precmd || return 1 380 ( $0 stop ) 381 sleep 1 382 $0 start 383 384 ;; 385 386 rcvar) 387 echo "# $name" 388 if checkyesno ${_ckvar}; then 389 echo "\$${_ckvar}=YES" 390 else 391 echo "\$${_ckvar}=NO" 392 fi 393 ;; 394 395 *) 396 rc_usage "$_keywords" 397 ;; 398 399 esac 400 return 0 401 done 402 403 echo 1>&2 "$0: unknown directive '$_arg'." 404 rc_usage "$_keywords" 405 exit 1 406} 407 408# 409# run_rc_script file arg 410# Start the script `file' with `arg', and correctly handle the 411# return value from the script. If `file' ends with `.sh', it's 412# sourced into the current environment. Otherwise it's run as 413# a child process. 414# Note: because `.sh' files are sourced into the current environment 415# run_rc_command shouldn't be used because its difficult to ensure 416# that the global variable state before and after the sourcing of 417# the .sh file won't adversely affect other scripts. 418# 419run_rc_script() 420{ 421 _file=$1 422 _arg=$2 423 if [ -z "$_file" -o -z "$_arg" ]; then 424 err 3 'USAGE: run_rc_script file arg' 425 fi 426 427 _narg=$_arg 428 case "$_narg" in 429 fast*) 430 _narg=${_narg#fast} 431 ;; 432 force*) 433 _narg=${_narg#force} 434 ;; 435 esac 436 eval ${_narg}_precmd="" 437 eval ${_narg}_cmd="" 438 439 case "$_file" in 440 *.sh) # run in current shell 441 set $_arg ; . $_file 442 ;; 443 *) # run in subshell 444 ( set $_arg ; . $_file ) 445 ;; 446 esac 447} 448 449# 450# rc_usage commands 451# Print a usage string for $0, with `commands' being a list of 452# valid commands. 453# 454rc_usage() 455{ 456 echo -n 1>&2 "Usage: $0 [fast|force](" 457 458 _sep= 459 for _elem in $*; do 460 echo -n 1>&2 "$_sep$_elem" 461 _sep="|" 462 done 463 echo 1>&2 ")" 464 exit 1 465} 466 467# 468# err exitval message 469# Display message to stderr and log to the syslog, and exit with exitval. 470# 471err() 472{ 473 exitval=$1 474 shift 475 476 logger "$0: ERROR $*" 477 echo 1>&2 "$0: ERROR $*" 478 exit $exitval 479} 480 481# 482# warn message 483# Display message to stderr and log to the syslog. 484# 485warn() 486{ 487 logger "$0: WARNING $*" 488 echo 1>&2 "$0: WARNING $*" 489} 490