Implement the checks for required_* objects as two functions, one
to be run before precmd and the other after it.  They get the current
rc command as an argument so they can choose what requirement tests
to perform.  As of now, only "start" needs such tests.

Implement a new requirement variable, required_modules.  It can
list kernel modules that need to be loaded after start_precmd
indicated success.  Each name in the list can be just "file", or
"file:module", or "file~regex".  This will allow us to remove a lot
of duplicated code from rc.d scripts.

Perform the checks not only for the default start method, but for
any method.  This allows for more flexibility and fixes a few rc.d
scripts (namely newsyslog, pf, sendmail) that rely on a required_*
variable while providing a non-default start method.

To be able to call the new check_required* functions naturally,
remove lots of crufty duplicated code pieces from run_rc_command
and replace each of them by a call to the helper function providing
a single corrected instance of the respective code snippet.  Now
run_rc_command isn't as scary as it used to be, and it even appears
to have quite a nice logic that was obscured by the old crufty code.

In the default handler for restart, run start from a subshell to
protect global varibles, e.g., _postcmd, from modification by the
start handler.  This enables using restart_postcmd. [x]

PR:		conf/98734 [x]
Submitted by:	Rick van der Zwet <rick@wzoeterwoude.net> [x]
Reviewed by:	freebsd-rc (silence for an older version)
MFC after:	1 month
This commit is contained in:
Yaroslav Tykhiy 2006-12-27 13:15:33 +00:00
parent b3c71457fa
commit c89572720c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=165565

View File

@ -408,12 +408,25 @@ wait_for_pids()
# returned a zero exit code. # returned a zero exit code.
# #
# required_dirs n If set, check for the existence of the given # required_dirs n If set, check for the existence of the given
# directories before running the default # directories before running a (re)start command.
# (re)start command.
# #
# required_files n If set, check for the readability of the given # required_files n If set, check for the readability of the given
# files before running the default (re)start # files before running a (re)start command.
# command. #
# required_modules n If set, ensure the given kernel modules are
# loaded before running a (re)start command.
# The check and possible loads are actually
# done after start_precmd so that the modules
# aren't loaded in vain, should the precmd
# return a non-zero status to indicate a error.
# If a word in the list looks like "foo:bar",
# "foo" is the KLD file name and "bar" is the
# module name. If a word looks like "foo~bar",
# "foo" is the KLD file name and "bar" is a
# egrep(1) pattern matching the module name.
# Otherwise the module name is assumed to be
# the same as the KLD file name, which is most
# common. See load_kld().
# #
# required_vars n If set, perform checkyesno on each of the # required_vars n If set, perform checkyesno on each of the
# listed variables before running the default # listed variables before running the default
@ -562,49 +575,31 @@ run_rc_command()
if [ "$_elem" != "$rc_arg" ]; then if [ "$_elem" != "$rc_arg" ]; then
continue continue
fi fi
# if there's a custom ${XXX_cmd}, # if there's a custom ${XXX_cmd},
# run that instead of the default # run that instead of the default
# #
eval _cmd=\$${rc_arg}_cmd _precmd=\$${rc_arg}_precmd \ eval _cmd=\$${rc_arg}_cmd \
_postcmd=\$${rc_arg}_postcmd _precmd=\$${rc_arg}_precmd \
_postcmd=\$${rc_arg}_postcmd
if [ -n "$_cmd" ]; then if [ -n "$_cmd" ]; then
# if the precmd failed and force _run_rc_precmd || return 1
# isn't set, exit _run_rc_doit "$_cmd $rc_extra_args" || return 1
# _run_rc_postcmd
if [ -n "$_precmd" ]; then
debug "run_rc_command: evaluating ${_precmd}()."
eval $_precmd $rc_extra_args
_return=$?
[ $_return -ne 0 ] && [ -z "$rc_force" ] &&
return 1
fi
if [ -n "$_cmd" ]; then
debug "run_rc_command: evaluating ${_cmd}()."
eval $_cmd $rc_extra_args
_return=$?
[ $_return -ne 0 ] && [ -z "$rc_force" ] &&
return 1
fi
if [ -n "$_postcmd" ]; then
debug "run_rc_command: evaluating ${_postcmd}()."
eval $_postcmd $rc_extra_args
_return=$?
fi
return $_return return $_return
fi fi
case "$rc_arg" in # default operations... case "$rc_arg" in # default operations...
status) status)
_run_rc_precmd || return 1
if [ -n "$rc_pid" ]; then if [ -n "$rc_pid" ]; then
echo "${name} is running as pid $rc_pid." echo "${name} is running as pid $rc_pid."
else else
echo "${name} is not running." echo "${name} is not running."
return 1 return 1
fi fi
_run_rc_postcmd
;; ;;
start) start)
@ -618,44 +613,7 @@ run_rc_command()
return 1 return 1
fi fi
# check for required variables, _run_rc_precmd || return 1
# directories, and files
#
for _f in $required_vars; do
if ! checkyesno $_f; then
warn "\$${_f} is not enabled."
if [ -z "$rc_force" ]; then
return 1
fi
fi
done
for _f in $required_dirs; do
if [ ! -d "${_f}/." ]; then
warn "${_f} is not a directory."
if [ -z "$rc_force" ]; then
return 1
fi
fi
done
for _f in $required_files; do
if [ ! -r "${_f}" ]; then
warn "${_f} is not readable."
if [ -z "$rc_force" ]; then
return 1
fi
fi
done
# if the precmd failed and force
# isn't set, exit
#
if [ -n "${_precmd}" ]; then
debug "run_rc_command: evaluating ${_precmd}()."
eval $_precmd
_return=$?
[ $_return -ne 0 ] && [ -z "$rc_force" ] &&
return 1
fi
# setup the full command to run # setup the full command to run
# #
@ -680,106 +638,52 @@ $command $rc_flags $command_args"
fi fi
fi fi
# run the full command; # run the full command
# if the cmd failed and force
# isn't set, exit
# #
debug "run_rc_command: _doit: $_doit" _run_rc_doit "$_doit" || return 1
eval $_doit
_return=$?
[ $_return -ne 0 ] && [ -z "$rc_force" ] && return 1
# finally, run postcmd # finally, run postcmd
# #
if [ -n "${_postcmd}" ]; then _run_rc_postcmd
debug "run_rc_command: evaluating ${_postcmd}()."
eval $_postcmd
fi
;; ;;
stop) stop)
if [ -z "$rc_pid" ]; then if [ -z "$rc_pid" ]; then
[ -n "$rc_fast" ] && return 0 [ -n "$rc_fast" ] && return 0
if [ -n "$pidfile" ]; then _run_rc_notrunning
echo 1>&2 \
"${name} not running? (check $pidfile)."
else
echo 1>&2 "${name} not running?"
fi
return 1 return 1
fi fi
# if the precmd failed and force _run_rc_precmd || return 1
# isn't set, exit
#
if [ -n "$_precmd" ]; then
eval $_precmd
_return=$?
[ $_return -ne 0 ] && [ -z "$rc_force" ] &&
return 1
fi
# send the signal to stop # send the signal to stop
# #
echo "Stopping ${name}." echo "Stopping ${name}."
_doit="kill -${sig_stop:-TERM} $rc_pid" _doit=$(_run_rc_killcmd "${sig_stop:-TERM}")
if [ -n "$_user" ]; then _run_rc_doit "$_doit" || return 1
_doit="su -m $_user -c 'sh -c \"$_doit\"'"
fi
# if the stop cmd failed and force
# isn't set, exit
#
eval $_doit
_return=$?
[ $_return -ne 0 ] && [ -z "$rc_force" ] && return 1
# wait for the command to exit, # wait for the command to exit,
# and run postcmd. # and run postcmd.
wait_for_pids $rc_pid wait_for_pids $rc_pid
if [ -n "$_postcmd" ]; then
eval $_postcmd _run_rc_postcmd
_return=$?
fi
;; ;;
reload) reload)
if [ -z "$rc_pid" ]; then if [ -z "$rc_pid" ]; then
if [ -n "$pidfile" ]; then _run_rc_notrunning
echo 1>&2 \
"${name} not running? (check $pidfile)."
else
echo 1>&2 "${name} not running?"
fi
return 1 return 1
fi fi
echo "Reloading ${name} config files."
if [ -n "$_precmd" ]; then _run_rc_precmd || return 1
eval $_precmd
_return=$? _doit=$(_run_rc_killcmd "${sig_reload:-HUP}")
[ $_return -ne 0 ] && [ -z "$rc_force" ] && _run_rc_doit "$_doit" || return 1
return 1
fi _run_rc_postcmd
_doit="kill -${sig_reload:-HUP} $rc_pid"
if [ -n "$_user" ]; then
_doit="su -m $_user -c 'sh -c \"$_doit\"'"
fi
eval $_doit
_return=$?
[ $_return -ne 0 ] && [ -z "$rc_force" ] && return 1
if [ -n "$_postcmd" ]; then
eval $_postcmd
_return=$?
fi
;; ;;
restart) restart)
if [ -n "$_precmd" ]; then
eval $_precmd $rc_extra_args
_return=$?
[ $_return -ne 0 ] && [ -z "$rc_force" ] &&
return 1
fi
# prevent restart being called more # prevent restart being called more
# than once by any given script # than once by any given script
# #
@ -788,20 +692,23 @@ $command $rc_flags $command_args"
fi fi
_rc_restart_done=true _rc_restart_done=true
# run stop in a subshell to keep variables for start _run_rc_precmd || return 1
( run_rc_command ${_rc_prefix}stop $rc_extra_args )
run_rc_command ${_rc_prefix}start $rc_extra_args
if [ -n "$_postcmd" ]; then # run those in a subshell to keep global variables
eval $_postcmd $rc_extra_args ( run_rc_command ${_rc_prefix}stop $rc_extra_args )
_return=$? ( run_rc_command ${_rc_prefix}start $rc_extra_args )
fi _return=$?
[ $_return -ne 0 ] && [ -z "$rc_force" ] && return 1
_run_rc_postcmd
;; ;;
poll) poll)
_run_rc_precmd || return 1
if [ -n "$rc_pid" ]; then if [ -n "$rc_pid" ]; then
wait_for_pids $rc_pid wait_for_pids $rc_pid
fi fi
_run_rc_postcmd
;; ;;
rcvar) rcvar)
@ -828,6 +735,83 @@ $command $rc_flags $command_args"
# not reached # not reached
} }
#
# Helper functions for run_rc_command: common code.
# They use such global variables besides the exported rc_* ones:
#
# name R/W
# ------------------
# _precmd R
# _postcmd R
# _return W
#
_run_rc_precmd()
{
check_required_before "$rc_arg" || return 1
if [ -n "$_precmd" ]; then
debug "run_rc_command: ${rc_arg}_precmd: $_precmd $rc_extra_args"
eval "$_precmd $rc_extra_args"
_return=$?
# If precmd failed and force isn't set, request exit.
if [ $_return -ne 0 ] && [ -z "$rc_force" ]; then
return 1
fi
fi
check_required_after "$rc_arg" || return 1
return 0
}
_run_rc_postcmd()
{
if [ -n "$_postcmd" ]; then
debug "run_rc_command: ${rc_arg}_postcmd: $_postcmd $rc_extra_args"
eval "$_postcmd $rc_extra_args"
_return=$?
fi
return 0
}
_run_rc_doit()
{
debug "run_rc_command: doit: $*"
eval "$@"
_return=$?
# If command failed and force isn't set, request exit.
if [ $_return -ne 0 ] && [ -z "$rc_force" ]; then
return 1
fi
return 0
}
_run_rc_notrunning()
{
local _pidmsg
if [ -n "$pidfile" ]; then
_pidmsg=" (check $pidfile)."
else
_pidmsg=
fi
echo 1>&2 "${name} not running?${_pidmsg}"
}
_run_rc_killcmd()
{
local _cmd
_cmd="kill -$1 $rc_pid"
if [ -n "$_user" ]; then
_cmd="su -m ${_user} -c 'sh -c \"${_cmd}\"'"
fi
echo "$_cmd"
}
# #
# run_rc_script file arg # run_rc_script file arg
# Start the script `file' with `arg', and correctly handle the # Start the script `file' with `arg', and correctly handle the
@ -1486,6 +1470,74 @@ find_local_scripts_new () {
done done
} }
# check_required_{before|after} command
# Check for things required by the command before and after its precmd,
# respectively. The two separate functions are needed because some
# conditions should prevent precmd from being run while other things
# depend on precmd having already been run.
#
check_required_before()
{
local _f
case "$1" in
start)
for _f in $required_vars; do
if ! checkyesno $_f; then
warn "\$${_f} is not enabled."
if [ -z "$rc_force" ]; then
return 1
fi
fi
done
for _f in $required_dirs; do
if [ ! -d "${_f}/." ]; then
warn "${_f} is not a directory."
if [ -z "$rc_force" ]; then
return 1
fi
fi
done
for _f in $required_files; do
if [ ! -r "${_f}" ]; then
warn "${_f} is not readable."
if [ -z "$rc_force" ]; then
return 1
fi
fi
done
;;
esac
return 0
}
check_required_after()
{
local _f _args
case "$1" in
start)
for _f in $required_modules; do
case "${_f}" in
*~*) _args="-e ${_f#*~} ${_f%%~*}" ;;
*:*) _args="-m ${_f#*:} ${_f%%:*}" ;;
*) _args="${_f}" ;;
esac
if ! load_kld ${_args}; then
if [ -z "$rc_force" ]; then
return 1
fi
fi
done
;;
esac
return 0
}
fi fi
_rc_subr_loaded=: _rc_subr_loaded=: